From 9735ca383cfd49ffcc85ca7588255b6ddd6a0b83 Mon Sep 17 00:00:00 2001 From: Thomas Flament Date: Thu, 2 Jul 2026 17:16:13 +0200 Subject: [PATCH 1/3] Publish to GitHub Packages alongside npm on release Add a publish-github job to the release workflow that reuses the build artifact and publishes @scality/cloudserverclient to GitHub Packages via GITHUB_TOKEN, in parallel with the existing npm publish. Remove the publishConfig.registry pin to npmjs so each publish job's setup-node controls its target registry, and gate the GitHub release on both publishes succeeding. Issue: CLDSRVCLT-16 --- .github/workflows/release.yml | 29 ++++++++++++++++++++++++++++- package.json | 3 +-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e14cc619..496de1e8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,10 +55,37 @@ jobs: - name: Publish to npm with provenance run: npm publish --provenance --tag latest + publish-github: + name: Publish to GitHub Packages + runs-on: ubuntu-24.04 + needs: build + permissions: + contents: read + packages: write + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Download build artifacts + uses: actions/download-artifact@v7 + with: + name: build-artifacts + + - name: Setup Node.js for GitHub Packages + uses: actions/setup-node@v6 + with: + node-version: '24' + registry-url: 'https://npm.pkg.github.com' + + - name: Publish to GitHub Packages + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + create-release: name: Create GitHub Release runs-on: ubuntu-24.04 - needs: [build, publish-npm] + needs: [build, publish-npm, publish-github] permissions: contents: write steps: diff --git a/package.json b/package.json index bdee3da1..8b3a7e7e 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,7 @@ "build/smithy/cloudserverProxyBackbeatApis/typescript-codegen" ], "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org" + "access": "public" }, "scripts": { "clean:build": "rm -rf build dist", From 77e77c7770cfc6f3f5d8e0498020e16c6ac30b6e Mon Sep 17 00:00:00 2001 From: Thomas Flament Date: Fri, 3 Jul 2026 11:39:51 +0200 Subject: [PATCH 2/3] Specify --tag latest on GitHub Packages publish for consistency Match the npm publish job's explicit dist-tag on the GitHub Packages job. Behaviorally a no-op (npm publish already defaults to the latest tag), but keeps the two publish steps symmetric and self-documenting. Issue: CLDSRVCLT-16 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 496de1e8..072b7d6b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -78,7 +78,7 @@ jobs: registry-url: 'https://npm.pkg.github.com' - name: Publish to GitHub Packages - run: npm publish + run: npm publish --tag latest env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ec4ff28813da1439ba40632286b378b28acc0b07 Mon Sep 17 00:00:00 2001 From: Thomas Flament Date: Fri, 3 Jul 2026 16:17:47 +0200 Subject: [PATCH 3/3] Build once, attest, and dual-publish the tarball on release Restructure the release workflow to pack a single tarball, generate a signed GitHub build-provenance attestation for it, and publish that same tarball to both npmjs and GitHub Packages. A single attestation (verifiable with `gh attestation verify`) then covers whatever lands in either registry, in addition to npm-native provenance on npmjs. Also harden the workflow, mirroring the arsenal release model: - reject releases from branches other than development/* or hotfix/* - fail early if the version's git tag or GitHub release already exists - skip a registry publish when that version is already present there, so a partial dual-publish can be re-run cleanly instead of hitting E409 - least-privilege permissions per job (attestations: write on build) npm publishing keeps OIDC trusted publishing and the latest dist-tag. Issue: CLDSRVCLT-16 --- .github/workflows/release.yml | 97 ++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 18 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 072b7d6b..8e062b80 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,30 +5,79 @@ run-name: Release on: workflow_dispatch: +permissions: {} + jobs: build: name: Build runs-on: ubuntu-24.04 + permissions: + contents: read + id-token: write + attestations: write outputs: version: ${{ steps.package-version.outputs.version }} steps: + - name: Reject disallowed branch + if: >- + ${{ !startsWith(github.ref, 'refs/heads/development/') + && !startsWith(github.ref, 'refs/heads/hotfix/') }} + env: + REF_NAME: ${{ github.ref_name }} + run: | + echo "::error::Releases must run from a development/* or" \ + "hotfix/* branch (got $REF_NAME)" + exit 1 + - name: Checkout code uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true - name: Get version from package.json id: package-version run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + - name: Fail if release already exists + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.package-version.outputs.version }} + run: | + if gh release view "$VERSION" --repo "$GITHUB_REPOSITORY" \ + >/dev/null 2>&1; then + echo "::error::Release $VERSION already exists" + exit 1 + fi + + - name: Fail if tag already exists + env: + VERSION: ${{ steps.package-version.outputs.version }} + run: | + if git rev-parse -q --verify "refs/tags/$VERSION" >/dev/null; then + echo "::error::Tag $VERSION already exists (possibly on a" \ + "different commit); refusing to move it" + exit 1 + fi + - name: Setup and Build uses: ./.github/actions/setup-and-build - - name: Upload build artifacts + - name: Pack tarball + run: npm pack --ignore-scripts + + - name: Attest build provenance + uses: actions/attest-build-provenance@v4 + with: + subject-path: '*.tgz' + + - name: Upload package artifact uses: actions/upload-artifact@v6 with: - name: build-artifacts - path: | - dist/ - build/ + name: package + path: '*.tgz' + if-no-files-found: error + retention-days: 1 publish-npm: name: Publish to npm registry @@ -38,49 +87,57 @@ jobs: contents: read id-token: write steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Download build artifacts + - name: Download package artifact uses: actions/download-artifact@v7 with: - name: build-artifacts + name: package - name: Setup Node.js for npm registry uses: actions/setup-node@v6 with: node-version: '24' registry-url: 'https://registry.npmjs.org' + scope: '@scality' - name: Publish to npm with provenance - run: npm publish --provenance --tag latest + env: + VERSION: ${{ needs.build.outputs.version }} + run: | + if [ -n "$(npm view "@scality/cloudserverclient@$VERSION" version 2>/dev/null)" ]; then + echo "::notice::@scality/cloudserverclient@$VERSION already on npm; skipping" + else + npm publish *.tgz --provenance --tag latest + fi publish-github: name: Publish to GitHub Packages runs-on: ubuntu-24.04 needs: build permissions: - contents: read packages: write steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Download build artifacts + - name: Download package artifact uses: actions/download-artifact@v7 with: - name: build-artifacts + name: package - name: Setup Node.js for GitHub Packages uses: actions/setup-node@v6 with: node-version: '24' registry-url: 'https://npm.pkg.github.com' + scope: '@scality' - name: Publish to GitHub Packages - run: npm publish --tag latest env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ needs.build.outputs.version }} + run: | + if [ -n "$(npm view "@scality/cloudserverclient@$VERSION" version 2>/dev/null)" ]; then + echo "::notice::@scality/cloudserverclient@$VERSION already on GitHub Packages; skipping" + else + npm publish *.tgz --tag latest + fi create-release: name: Create GitHub Release @@ -98,3 +155,7 @@ jobs: name: Release ${{ needs.build.outputs.version }} target_commitish: ${{ github.sha }} generate_release_notes: true + append_body: true + body: | + GitHub Packages: https://github.com/${{ github.repository }}/pkgs/npm/cloudserverclient + npm: https://www.npmjs.com/package/@scality/cloudserverclient