chore(release): v1.4.1 — atomic six-package republish with looser @pu… #10
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Release — atomic six-package npm publish with isolated SLSA Build L3 | |
| # provenance + SBOM generation + signed GitHub release. | |
| # | |
| # Trigger: tag v*.*.* on main. The release is atomic — any step | |
| # failure aborts before any `npm publish`. There is NO long-lived | |
| # NPM_TOKEN; npm trusted publishing via OIDC is the only auth path. | |
| # | |
| # Build, pack, and SBOM generation happen without `id-token: write`. | |
| # `.github/workflows/sign-and-publish.yml` is the reusable signing lane | |
| # that receives OIDC + attestation permissions. | |
| # | |
| # The six-package set is asserted by | |
| # packages/k8s-baseline/tests/release-readiness.test.ts: | |
| # - @hulumi/baseline | |
| # - @hulumi/policies | |
| # - @hulumi/drift | |
| # - @hulumi/k8s-baseline (added in runbook hulumi-operations-k8s-security M1) | |
| # - @hulumi/cloudflare-baseline | |
| # - @hulumi/platform-patterns | |
| name: release | |
| on: | |
| push: | |
| tags: | |
| - "v*.*.*" | |
| permissions: | |
| contents: read | |
| jobs: | |
| preflight: | |
| name: pre-flight (build + test + lint) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6 | |
| with: | |
| version: 9.12.0 | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 | |
| with: | |
| node-version: 20 | |
| cache: pnpm | |
| registry-url: https://registry.npmjs.org | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm run lint:exact-pin-guard | |
| - run: pnpm -r build | |
| - run: pnpm -r typecheck | |
| - run: pnpm -r test | |
| - run: pnpm -r lint | |
| - run: pnpm run lint:license-boundary | |
| - run: pnpm run format:check | |
| build-release-artifacts: | |
| name: build release artifacts (no OIDC signing) | |
| needs: preflight | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6 | |
| with: | |
| version: 9.12.0 | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 | |
| with: | |
| node-version: 22.14.0 | |
| cache: pnpm | |
| registry-url: https://registry.npmjs.org | |
| - name: Use npm CLI pinned for release tooling | |
| run: | | |
| npm install -g npm@11.5.1 | |
| node --version | |
| npm --version | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm -r build | |
| - name: Generate tarballs | |
| run: | | |
| mkdir -p .release-artifacts | |
| ABS_DEST="$(pwd)/.release-artifacts" | |
| for pkg in baseline policies drift k8s-baseline cloudflare-baseline platform-patterns; do | |
| (cd "packages/$pkg" && pnpm pack --pack-destination "$ABS_DEST") | |
| done | |
| ls -la .release-artifacts/ | |
| - name: Generate tarball digest manifest | |
| run: | | |
| set -euo pipefail | |
| sha256sum .release-artifacts/*.tgz > .release-artifacts/tarballs-sha256.txt | |
| cat .release-artifacts/tarballs-sha256.txt | |
| - name: Generate SBOMs (CycloneDX, via cdxgen) | |
| # cdxgen is the multi-package-manager CycloneDX generator. It reads | |
| # `pnpm-lock.yaml` natively, so no transient `package-lock.json` | |
| # round-trip is needed. The previous fix (`npm install | |
| # --package-lock-only`) was intercepted by Corepack — when the | |
| # workspace's `package.json#packageManager` is `pnpm@9.12.0`, | |
| # Corepack rejects or rewrites direct npm calls, so the lockfile | |
| # never got created and `@cyclonedx/cyclonedx-npm` errored with | |
| # "No evidence: no package lock file nor `node_modules` dir". | |
| # Switching to cdxgen sidesteps the Corepack interaction | |
| # entirely. | |
| env: | |
| # cdxgen reads `pnpm-lock.yaml` from the workspace root and | |
| # the package.json from the per-package directory. Run from | |
| # the repo root so pnpm-lock.yaml is discoverable. | |
| FETCH_LICENSE: "true" | |
| run: | | |
| set -euo pipefail | |
| for pkg in baseline policies drift k8s-baseline cloudflare-baseline platform-patterns; do | |
| npx --yes @cyclonedx/cdxgen@11.10.0 \ | |
| --type js \ | |
| --no-recurse \ | |
| --spec-version 1.5 \ | |
| --output ".release-artifacts/sbom-${pkg}.cdx.json" \ | |
| "packages/$pkg" | |
| done | |
| - name: Verify release-artifact directory before upload (diagnostic) | |
| # The v1.4.0 release run regressed at this point — upload-artifact | |
| # said "No files were found" even though the previous `Generate | |
| # tarballs` step had ls-ed six .tgz files in this directory. This | |
| # diagnostic step captures the directory contents immediately | |
| # before the upload, fails the job loudly if the dir is empty, and | |
| # records the file count + total size in the run log. See PR #186 | |
| # post-mortem. | |
| run: | | |
| set -euo pipefail | |
| test -d .release-artifacts || { echo "::error::.release-artifacts/ does not exist"; exit 1; } | |
| ls -la .release-artifacts/ | |
| count=$(find .release-artifacts -mindepth 1 -maxdepth 1 -type f | wc -l) | |
| echo "file count: $count" | |
| if [ "$count" -eq 0 ]; then | |
| echo "::error::.release-artifacts/ is empty immediately before upload" | |
| exit 1 | |
| fi | |
| - name: Upload release artifacts for isolated signing | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: hulumi-release-artifacts | |
| # v5+ default `include-hidden-files: false` skips any directory | |
| # or file whose name starts with `.`. Because `.release-artifacts/` | |
| # is itself dot-prefixed, every path under it counts as hidden | |
| # and the action emits "No files were found" even when the | |
| # directory contains the .tgz + SBOM files (see PR #187 | |
| # post-mortem and the diagnostic step's `ls -la` output). | |
| # Setting include-hidden-files: true uploads the contents of the | |
| # dot-prefixed staging directory without changing repo-level | |
| # hidden-file handling (this glob is scoped to .release-artifacts). | |
| include-hidden-files: true | |
| path: .release-artifacts/** | |
| if-no-files-found: error | |
| retention-days: 7 | |
| sign-and-publish: | |
| name: SLSA attest + npm publish (reusable) | |
| needs: build-release-artifacts | |
| uses: ./.github/workflows/sign-and-publish.yml | |
| permissions: | |
| actions: read | |
| contents: write | |
| id-token: write | |
| attestations: write | |
| with: | |
| artifact-name: hulumi-release-artifacts | |
| package-list: baseline policies drift k8s-baseline cloudflare-baseline platform-patterns |