SPFx 1.22 CI/CD — Upgrade pipeline to npm + Heft

SPFx 1.22 CI/CD — Upgrade pipeline to npm + Heft

Why change the pipeline

SPFx 1.22 uses the Rushstack Heft build rig. Calling npm scripts (which delegate to Heft) ensures your prebuild hooks (e.g., run-script tasks) execute before bundling or packaging. Direct gulp calls may bypass Heft or run out of sync with your build-phase tasks.

References:

Old pipeline (SPFx Toolkit default)

This is the typical pipeline the toolkit generates:

name: Deploy Solution react-copilot-retrieval-api
...
      - name: Run npm ci
        run: npm ci

      - name: Bundle & Package
        run: |
          gulp bundle --ship
          gulp package-solution --ship

      - name: CLI for Microsoft 365 Login
...

Upgraded GitHub Actions workflow

Switch the bundling/packaging step to npm scripts so Heft prebuild hooks run. The rest of the workflow stays the same.

name: Deploy Solution react-copilot-retrieval-api
on:
  push:
    branches:
      - main
  workflow_dispatch:
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    env:
      NodeVersion: 22.x
    steps:
      - name: Checkout
        uses: actions/checkout@v3.5.3

      - name: Use Node.js
        uses: actions/setup-node@v3.7.0
        with:
          node-version: ${{ env.NodeVersion }}

      - name: Install dependencies
        run: npm ci

      - name: Bundle (Heft) & Package (SPFx)
        run: |
          # Use npm scripts so npm hooks and Heft customizations run
          npm run build -- --production
          npm run package-solution -- --production

      - name: CLI for Microsoft 365 Login
        uses: pnp/action-cli-login@v2.2.4
        with:
          CERTIFICATE_ENCODED: ${{ secrets.CERTIFICATE_ENCODED }}
          CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
          APP_ID: ${{ secrets.APP_ID }}
          TENANT: ${{ secrets.TENANT_ID }}

      - name: CLI for Microsoft 365 Deploy App
        uses: pnp/action-cli-deploy@v4.0.0
        with:
          APP_FILE_PATH: sharepoint/solution/react-copilot-retrieval-api.sppkg
          SKIP_FEATURE_DEPLOYMENT: false
          OVERWRITE: true

This change replaces direct gulp calls with npm scripts that delegate to Heft. As a result, npm lifecycle hooks run reliably and any Heft pipeline modifications (e.g., run-script plugins or custom phases) are honored. It’s a clean and consistent way to ensure your prebuild logic executes before packaging.

npm lifecycle hooks (examples)

npm supports automatic lifecycle hooks that run before/after a named script. This is handy for ensuring setup/validation steps happen without duplicating commands in workflows.

{
  "scripts": {
    "build": "heft build",

    "prebuild": "node ./scripts/set-build-env.mjs",
    "postbuild": "node ./scripts/verify-artifacts.mjs",
  }
}
  • prebuild / postbuild: Run automatically when you call npm run build.
  • Benefit: Centralize orchestration in package.json so CI and local builds behave the same.

Run locally or in CI exactly the same way:

npm run build -- --production
npm run package-solution -- --production