name: Publish npm packages # Publishes the consumable a11oy moat packages to GitHub Packages so the other # SZL modules (amaru/sentra/vessels) can import the REAL policy gates + receipt # substrate instead of the @workspace/a11oy-orchestration stub. # # Packages published (by path — these are not pnpm-workspace members, so each is # built and published in place): # @szl-holdings/a11oy-policy (packages/policy) # @szl-holdings/a11oy-receipt-substrate (packages/receipt-substrate) # # Both ship raw TypeScript in-repo (main: ./src/index.ts). For a consumable # package we emit JS + .d.ts via tsconfig.publish.json so downstreams don't need # allowImportingTsExtensions and browsers never see node: imports at runtime # (consumers import receipt-substrate TYPES only). # # Triggers: # 1. workflow_dispatch{ version } — manual publish/backfill # 2. release: published, guarded to tags matching pkg-v* (does NOT collide # with the uds-v* SBOM/sign lanes) # # Doctrine v7 §10: no fabricated assets. The published tarball is built from # tracked source by tsc; nothing is hand-uploaded. on: workflow_dispatch: inputs: version: description: 'Version to publish (e.g. 0.1.0). Must match each package.json or use dry-run.' required: false type: string dry_run: description: 'npm publish --dry-run (no upload)' required: false type: boolean default: true release: types: [published] permissions: contents: read jobs: publish: name: Build + publish to GitHub Packages runs-on: ubuntu-latest permissions: contents: read packages: write if: | github.event_name == 'workflow_dispatch' || (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'pkg-v')) strategy: matrix: pkg: - packages/policy - packages/receipt-substrate fail-fast: false steps: - name: Checkout uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Setup Node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '20' registry-url: 'https://npm.pkg.github.com' scope: '@szl-holdings' - name: Install pnpm uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 with: version: 9 - name: Install deps (workspace root) run: pnpm install --frozen-lockfile || pnpm install - name: Install package deps (resolve published @szl-holdings deps from GHCR) working-directory: ${{ matrix.pkg }} env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # packages/policy depends on the published @szl-holdings/a11oy-receipt-substrate. # These packages are NOT pnpm-workspace members, so the root install does not # link them. Install in-place so tsc can resolve the published types + JS. # The package-local .npmrc points @szl-holdings at npm.pkg.github.com and uses # ${NODE_AUTH_TOKEN}. receipt-substrate has no @szl-holdings deps, so this is a # no-op there. if [ -f package.json ] && node -e "process.exit(Object.keys(require('./package.json').dependencies||{}).length ? 0 : 1)"; then npm install --no-save --no-package-lock else echo "No runtime dependencies to install for ${{ matrix.pkg }}" fi - name: Build package (emit JS + d.ts) working-directory: ${{ matrix.pkg }} run: | echo "Building ${{ matrix.pkg }} for publish..." npx tsc -p tsconfig.publish.json ls -la dist - name: Repoint package.json entrypoints to dist (publish-only, not committed) working-directory: ${{ matrix.pkg }} run: | # In-repo, main/types/exports point at raw ./src/*.ts for tsx dev. # For the published tarball we repoint them at the emitted ./dist/*.js # + .d.ts so downstreams import compiled JS (no node: at runtime in the # browser; receipt-substrate is consumed types-only there anyway). node -e ' const fs = require("fs"); const p = JSON.parse(fs.readFileSync("package.json", "utf8")); const toDist = (s) => s.replace(/^\.\/src\//, "./dist/").replace(/\.ts$/, ".js"); const toDts = (s) => s.replace(/^\.\/src\//, "./dist/").replace(/\.ts$/, ".d.ts"); if (p.main) p.main = toDist(p.main); if (p.types) p.types = toDts(p.types); if (p.exports) { const remap = (e) => { if (typeof e === "string") return toDist(e); const out = {}; for (const k of Object.keys(e)) out[k] = k === "types" ? toDts(e[k]) : toDist(e[k]); return out; }; for (const k of Object.keys(p.exports)) p.exports[k] = remap(p.exports[k]); } fs.writeFileSync("package.json", JSON.stringify(p, null, 2) + "\n"); console.log("repointed:", JSON.stringify({ main: p.main, types: p.types }, null, 2)); ' - name: Determine dry-run id: mode run: | if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.dry_run }}" == "false" ]]; then echo "flag=" >> "$GITHUB_OUTPUT" elif [[ "${{ github.event_name }}" == "release" ]]; then echo "flag=" >> "$GITHUB_OUTPUT" else echo "flag=--dry-run" >> "$GITHUB_OUTPUT" fi - name: Publish working-directory: ${{ matrix.pkg }} env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | npm publish ${{ steps.mode.outputs.flag }} echo "Published ${{ matrix.pkg }} (${{ steps.mode.outputs.flag }})"