Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions .github/workflows/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,28 @@ jobs:
working-directory: ./services/api
strategy:
matrix:
node-version: [24.x]
node-version: [26.x]
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Install pnpm
uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5
with:
version: 11.5.2

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: yarn
cache-dependency-path: services/api/yarn.lock
cache: pnpm
cache-dependency-path: services/api/pnpm-lock.yaml

- name: Install Dependencies
run: yarn install
run: pnpm install --frozen-lockfile

- name: Run Lint
run: yarn lint . || true
run: pnpm lint . || true

- name: Run Tests
run: yarn test --silent
run: pnpm test --silent
21 changes: 14 additions & 7 deletions .github/workflows/web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,27 @@ jobs:
working-directory: ./services/web
strategy:
matrix:
node-version: [24.x]
node-version: [26.x]
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Install pnpm
uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5
with:
version: 11.5.2

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: yarn
cache-dependency-path: services/web/yarn.lock
cache: pnpm
cache-dependency-path: |
services/web/pnpm-lock.yaml
services/web/serve/pnpm-lock.yaml

- name: Install Dependencies
run: yarn install
run: pnpm install --frozen-lockfile

- name: Run Lint
run: yarn lint .
run: pnpm lint .
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ dist
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
*.pyc
*.code-workspace
tmp
Expand Down
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,45 @@ docker compose up

Open the dashboard at http://localhost:2200/ - Admin login credentials can be seen in the API output.

## Package Management

This repo uses [pnpm](https://pnpm.io/). Node is pinned via [Volta](https://volta.sh/);
pnpm is pinned via the `packageManager` field in each `package.json`. Install pnpm once with
`npm install -g pnpm` (Node 26 no longer bundles corepack). Each service is installed
independently from its own directory:

```bash
cd services/api && pnpm install # likewise services/web
```

### Supply-chain hardening

Each service's `pnpm-workspace.yaml` enables a few pnpm
[supply-chain](https://pnpm.io/supply-chain-security) protections:

- `minimumReleaseAge: 10080` — refuse to install any package version published less than
**7 days** ago (lets malware in a fresh release be caught before it lands here). A
too-new pin in the lockfile is rejected on install; re-resolve to pick a compliant
version.
- `blockExoticSubdeps: true` — block transitive dependencies that resolve from git/tarball
URLs instead of the registry.
- `allowBuilds:` — an explicit allowlist of the only dependencies permitted to run install
scripts (pnpm blocks all others by default). Add a package here if a build it needs is
being skipped.

### Git worktrees

Worktrees work out of the box — pnpm shares the global content-addressable store across all
worktrees via hardlinks, so each worktree's `pnpm install` is fast and disk-cheap.

pnpm also has a [global virtual store](https://pnpm.io/git-worktrees) that would additionally
share the `.pnpm` symlink farm across worktrees. It is **not enabled** here: it requires the
isolated (symlinked) `node_modules` layout, which conflicts with the `nodeLinker: hoisted` we
commit for self-contained Docker/CI builds (hoisted has no virtual store to share). Because
the committed `hoisted` wins over global pnpm config, turning the virtual store on would mean
dropping `hoisted` from `pnpm-workspace.yaml` — so we leave it off and rely on the shared
content store above, which already makes worktree installs fast and cheap.

### API Documentation

Full portal with examples:
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
context: ./services/api
args:
NODE_ENV: development
command: yarn start
command: scripts/start-dev
volumes:
- ./services/api/src:/service/src
- ./services/api/emails:/service/emails
Expand Down
3 changes: 1 addition & 2 deletions services/api/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ dist
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
env
*.pyc
*.code-workspace
Expand Down
27 changes: 17 additions & 10 deletions services/api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
FROM node:24.13.0-alpine
# Build stage: install production dependencies with pnpm.
FROM node:26.3.0-alpine AS deps

# Yarn will not install any package listed in devDependencies if the NODE_ENV
# environment variable is set to production. Use this flag to instruct Yarn to
# ignore NODE_ENV and take its production-or-not status from this flag instead.
# devDependencies (jest, eslint, etc.) are run on the host, not in the image.
ARG NODE_ENV=production

# Install pnpm (Node 26 no longer bundles corepack)
RUN npm install -g pnpm@11.5.2

WORKDIR /service

COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN --mount=type=cache,target=/root/.local/share/pnpm/store --mount=type=cache,target=/root/.cache/pnpm pnpm install --prod --frozen-lockfile

# Runtime stage: node + production node_modules + source only. No pnpm or npm
# caches, so the image stays small.
FROM node:26.3.0-alpine

# Note layers should be ordered from less to more likely to change.

# Update & install required packages
Expand All @@ -16,11 +27,7 @@ RUN mkdir -p /root/.config
# Set work directory
WORKDIR /service

# Install dependencies and store yarn cache
COPY package.json yarn.lock ./
RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn install --frozen-lockfile

# Copy app source
COPY --from=deps /service/node_modules ./node_modules
COPY . .

# Set your port
Expand All @@ -30,4 +37,4 @@ ENV PORT=2300
EXPOSE 2300

# Start production server
CMD ["yarn", "-s", "start:production"]
CMD ["node", "src/index.js"]
16 changes: 9 additions & 7 deletions services/api/Dockerfile.cli
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
FROM node:24.13.0-alpine
FROM node:26.3.0-alpine

# Yarn will not install any package listed in devDependencies if the NODE_ENV
# environment variable is set to production. Use this flag to instruct Yarn to
# ignore NODE_ENV and take its production-or-not status from this flag instead.
# Install production dependencies only; devDependencies (jest, eslint, etc.) are
# run on the host, not in the image.
ARG NODE_ENV=production

RUN apk add mongodb-tools;
Expand All @@ -12,15 +11,18 @@ RUN apk add mongodb-tools;
# Update & install required packages
RUN apk add --update bash curl;

# Install pnpm (Node 26 no longer bundles corepack)
RUN npm install -g pnpm@11.5.2

# Needed for Gcloud Storage resumable file uploads
RUN mkdir -p /root/.config

# Set work directory
WORKDIR /service

# Install dependencies and store yarn cache
COPY package.json yarn.lock ./
RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn install --frozen-lockfile
# Install dependencies and store pnpm cache
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN --mount=type=cache,target=/root/.local/share/pnpm/store --mount=type=cache,target=/root/.cache/pnpm pnpm install --prod --frozen-lockfile

# Copy app source
COPY . .
Expand Down
16 changes: 9 additions & 7 deletions services/api/Dockerfile.jobs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
FROM node:24.13.0-alpine
FROM node:26.3.0-alpine

# Yarn will not install any package listed in devDependencies if the NODE_ENV
# environment variable is set to production. Use this flag to instruct Yarn to
# ignore NODE_ENV and take its production-or-not status from this flag instead.
# Install production dependencies only; devDependencies (jest, eslint, etc.) are
# run on the host, not in the image.
ARG NODE_ENV=production

# Note layers should be ordered from less to more likely to change.
Expand Down Expand Up @@ -32,12 +31,15 @@ RUN wget -qO /usr/local/bin/yq \
# Needed for Gcloud Storage resumable file uploads
RUN mkdir -p /root/.config

# Install pnpm (Node 26 no longer bundles corepack)
RUN npm install -g pnpm@11.5.2

# Set work directory
WORKDIR /service

# Install dependencies and store yarn cache
COPY package.json yarn.lock ./
RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn install --frozen-lockfile
# Install dependencies and store pnpm cache
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN --mount=type=cache,target=/root/.local/share/pnpm/store --mount=type=cache,target=/root/.cache/pnpm pnpm install --prod --frozen-lockfile

# Copy app source
COPY . .
Expand Down
29 changes: 14 additions & 15 deletions services/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ curl -sSLf https://get.volta.sh | bash
Install dependencies: (will install correct Node.js version)

```
yarn install
pnpm install
```

## Bedrock Packages
Expand Down Expand Up @@ -81,15 +81,15 @@ Tests are written using Jest. Tests MUST follow these guidelines:
Run tests with:

```
yarn test
pnpm test
```

## Running in Development

Start the development server with auto-reload:

```
yarn start
pnpm start
```

This command will automatically populate MongoDB fixtures when and empty DB is found.
Expand Down Expand Up @@ -177,25 +177,25 @@ Fixtures are provided with the [@bedrockio/fixtures](https://github.com/bedrocki

Package scripts are the main way of interacting with fixtures:

- `yarn start` - When the development server starts, fixtures will be loaded if the database is empty.
- `yarn fixtures:reload` - Drops the database and reloads all fixtures.
- `yarn fixtures:load` - Loads fixtures if the database is empty. Fixtures are not auto-loaded in non-development
- `pnpm start` - When the development server starts, fixtures will be loaded if the database is empty.
- `pnpm fixtures:reload` - Drops the database and reloads all fixtures.
- `pnpm fixtures:load` - Loads fixtures if the database is empty. Fixtures are not auto-loaded in non-development
environments as multiple pods starting up at the same time can cause data duplication, so instead this script is used
to provision the data once through the CLI pod.
- `yarn fixtures:export` - Exports documents as a zip file in a format compatible with the `fixtures` directory.
- `pnpm fixtures:export` - Exports documents as a zip file in a format compatible with the `fixtures` directory.

Additionally, a function `importFixtures` manually imports fixtures for use with testing.

You can force reload fixtures with the command:

```
yarn fixtures:reload
pnpm fixtures:reload
```

_Note: In the staging environment this script can be run by obtaining a shell into the API CLI pod (see
[../../deployment](../../deployment/README.md))_

The `yarn fixtures:export` script exports documents as a zip file in a format compatible with the `fixtures` directory.
The `pnpm fixtures:export` script exports documents as a zip file in a format compatible with the `fixtures` directory.
This allows database changes to be "baked" in as fixtures. The following options are accepted:

- `--model(s)`: Comma separated list of model names to export (required).
Expand Down Expand Up @@ -485,11 +485,10 @@ optimized for each method.

##### I get warnings like `The punycode module is deprecated`. What is this?

This warning comes from later node versions and is usually deep in the dependency chain. Add this to your `package.json`
and re-run `yarn install` and it should fix it:
This warning comes from later node versions and is usually deep in the dependency chain. Add this to your
`pnpm-workspace.yaml` and re-run `pnpm install` and it should fix it:

```json
"resolutions": {
"whatwg-url": "14.1.0"
},
```yaml
overrides:
whatwg-url: 14.1.0
```
8 changes: 4 additions & 4 deletions services/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
"version": "0.0.1",
"license": "MIT",
"engines": {
"node": ">=20"
"node": ">=24"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not 26?

},
"packageManager": "pnpm@11.5.2",
"scripts": {
"start": "scripts/start-dev",
"debug": "MONGO_DEBUG=true yarn start",
"debug": "MONGO_DEBUG=true pnpm start",
"start:production": "node src/index",
"lint": "eslint",
"test": "jest",
Expand Down Expand Up @@ -71,7 +72,6 @@
"supertest": "^7.1.4"
},
"volta": {
"node": "24.12.0",
"yarn": "1.22.22"
"node": "26.3.0"
}
}
Loading