CI/CD & Migration
GitHub Actions
Basic Setup
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm test
- run: pnpm buildWith Store Caching
- uses: pnpm/action-setup@v4
with:
version: 9
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- run: pnpm install --frozen-lockfileMonorepo - Build Changed Only
- name: Build changed packages
run: pnpm --filter "...[origin/main]" buildGitLab CI
image: node:20
variables:
PNPM_HOME: /root/.local/share/pnpm
PATH: $PNPM_HOME:$PATH
before_script:
- corepack enable
- corepack prepare pnpm@latest --activate
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .pnpm-store
install:
script:
- pnpm config set store-dir .pnpm-store
- pnpm install --frozen-lockfileDocker
Multi-Stage Build
FROM node:20-slim AS builder
RUN corepack enable
WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY packages/*/package.json ./packages/
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM node:20-slim AS runner
RUN corepack enable
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
COPY --from=builder /app/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod
CMD ["node", "dist/index.js"]Key CI Flags
--frozen-lockfile # Always use in CI
--prefer-offline # Use cached packages
--ignore-scripts # Faster (careful: some deps need scripts)Corepack
// package.json
{
"packageManager": "pnpm@9.0.0"
}# CI
- run: corepack enable
- run: pnpm install --frozen-lockfileMigration from npm
rm -rf node_modules package-lock.json
pnpm installMigration from Yarn
rm -rf node_modules yarn.lock
rm -rf .yarn .yarnrc.yml # Yarn Berry
pnpm installImport Lockfile
pnpm import # Creates pnpm-lock.yaml from existing lockfileHandling Phantom Dependencies
pnpm is strict - imports must be in package.json:
# Error: lodash not found (it's a transitive dep)
# Solution: add it explicitly
pnpm add lodashSymlink Issues
Some tools don't work with symlinks:
# .npmrc - Use npm-like flat structure
node-linker=hoistedOr hoist specific packages:
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*babel*Monorepo Migration
Create
pnpm-workspace.yaml:packages: - 'packages/*'Update internal deps to workspace protocol:
{ "@myorg/utils": "workspace:^" }Install:
rm -rf node_modules packages/*/node_modules pnpm install
Performance Tips
# .npmrc
prefer-offline=true
side-effects-cache=true
workspace-concurrency=4
onlyBuiltDependencies[]=esbuild
onlyBuiltDependencies[]=@swc/core# Build changed only
pnpm --filter "...[origin/main]" build
# Parallel workspace
pnpm -r --parallel run test
# Clean store periodically
pnpm store pruneScripts Migration
{
"scripts": {
// npm workspaces
"build:all": "npm run build --workspaces",
// pnpm
"build:all": "pnpm -r run build",
// npm specific workspace
"dev:app": "npm run dev -w packages/app",
// pnpm
"dev:app": "pnpm --filter @myorg/app run dev"
}
}