Publish to npm (v0.2.2, dispatch 30483010223) #4
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
| name: Publish to npm | |
| # The single npm publishing path: publishes the npm packages for an existing | |
| # GitHub release from the binaries attached to it, without cutting a new | |
| # release. Normally dispatched by release.yml after it publishes the GitHub | |
| # release; run it manually against the same tag to retry a partial or failed | |
| # publish. Only the repository's newest stable vX.Y.Z tag is accepted — | |
| # prereleases, malformed tags, and older releases are refused and must be | |
| # published manually. Per package, already-published versions are skipped | |
| # and the version must be strictly newer than the package's current npm | |
| # `latest`, so a retry publishes exactly the packages a partial earlier run | |
| # missed and the workflow can never move `latest` backwards. Keeping all | |
| # publishing in one workflow file lets npm's trusted publisher config (one | |
| # per package, matched by workflow filename) cover every publish. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Existing GitHub release tag whose binaries to publish (e.g. v0.2.1)" | |
| required: true | |
| dispatch_id: | |
| description: "Opaque id embedded in the run name so a dispatching workflow can find this exact run (leave empty for manual runs)" | |
| required: false | |
| default: "" | |
| # The dispatch_id in the run name is what release.yml greps for to identify | |
| # the run it dispatched — it matches "dispatch <id>)" including the closing | |
| # paren, so the id must stay immediately before the final ")". Do not reword | |
| # without updating the matching filter there. | |
| run-name: "Publish to npm (${{ inputs.tag }}${{ inputs.dispatch_id != '' && format(', dispatch {0}', inputs.dispatch_id) || '' }})" | |
| env: | |
| TAG: ${{ github.event.inputs.tag }} | |
| jobs: | |
| npm: | |
| name: Publish to npm | |
| runs-on: ubuntu-latest | |
| permissions: | |
| # Publishing authenticates via npm trusted publishing: the job mints a | |
| # GitHub OIDC token that npm verifies against each package's trusted | |
| # publisher config (this repo + this workflow filename). No registry | |
| # token exists anywhere. | |
| id-token: write | |
| contents: read | |
| steps: | |
| - name: Require the newest stable vX.Y.Z tag | |
| run: | | |
| if ! printf '%s' "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then | |
| echo "::error::Tag '$TAG' is not a stable vX.Y.Z release tag. Prerelease and other tags must be published manually." | |
| exit 1 | |
| fi | |
| # Only the newest stable tag may be published: retrying it fills in | |
| # packages a partial earlier run missed, but older tags are refused | |
| # outright — backpublishing is a manual operation. sort -V is safe | |
| # here because the grep leaves only plain X.Y.Z versions. | |
| NEWEST="$(gh api "repos/${GITHUB_REPOSITORY}/tags" --paginate --jq '.[].name' \ | |
| | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1)" | |
| if [ "$TAG" != "$NEWEST" ]; then | |
| echo "::error::Tag '$TAG' is not the newest stable release tag ('$NEWEST'). Backpublishing older releases must be done manually." | |
| exit 1 | |
| fi | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| # The tag's tree is what gets published: packaging scripts and package | |
| # metadata come from the tag itself, so a republish is reproducible. | |
| # Consequence: this only works for tags that contain scripts/build-npm.mjs. | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.TAG }} | |
| # No registry-url here: setup-node would write an .npmrc authToken | |
| # line referencing $NODE_AUTH_TOKEN, and npm hard-fails on any command | |
| # when a referenced env var is unset. Trusted publishing needs no | |
| # token; npm publish detects the OIDC environment on its own. | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| # npm >= 11.5.1 is required for trusted publishing; the runner's | |
| # bundled npm may lag. Both tools installed up front so a fetch | |
| # failure is a loud step failure (npx-on-demand in the publish loop | |
| # would be indistinguishable from "version not newer" and silently | |
| # skip a package), and pinned to exact versions: this is the only | |
| # third-party code fetched into the job that holds publish rights for | |
| # the @provablehq packages, and npm versions are immutable, so a pin | |
| # closes off both drift and package-takeover of new releases. | |
| - name: Install pinned npm and semver CLI | |
| run: npm install -g npm@12.0.1 semver@7.8.5 | |
| - name: Download release assets | |
| run: gh release download "$TAG" --pattern '*.zip' --dir artifacts | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Extract binaries by target | |
| run: | | |
| # artifacts/aleo-devnode-<tag>-<target>.zip | |
| # -> bins/<target>/aleo-devnode[.exe] | |
| for zip in artifacts/*.zip; do | |
| base="$(basename "$zip" .zip)" | |
| target="${base#aleo-devnode-${TAG}-}" | |
| mkdir -p "bins/$target" | |
| unzip -o "$zip" -d "bins/$target" | |
| done | |
| - name: Build npm packages | |
| run: node scripts/build-npm.mjs --version "${TAG#v}" --artifacts bins --out dist-npm | |
| - name: Publish | |
| run: | | |
| set -e | |
| VERSION="${TAG#v}" | |
| # Skip already-published packages so a partial earlier publish can | |
| # be resumed. Platform packages first, then the main launcher last, | |
| # so main's exact-pinned optionalDependencies always resolve on | |
| # install. | |
| publish() { | |
| local name latest | |
| name="$(node -p "require('./$1/package.json').name")" | |
| if npm view "${name}@${VERSION}" version >/dev/null 2>&1; then | |
| echo "${name}@${VERSION} already published, skipping" | |
| return | |
| fi | |
| # Never publish backwards: unless this version is strictly newer | |
| # than the package's current npm latest, skip it — publishing | |
| # would move the `latest` dist-tag onto an older version. Real | |
| # semver comparison (not sort -V), so a manually published | |
| # prerelease latest ranks below its release: 0.3.0-rc.1 < 0.3.0. | |
| latest="$(npm view "$name" dist-tags.latest 2>/dev/null || true)" | |
| if [ -n "$latest" ] && ! semver -r ">${latest}" "$VERSION" >/dev/null; then | |
| echo "${name}: npm latest (${latest}) >= ${VERSION}, skipping" | |
| return | |
| fi | |
| npm publish "$1" --access public | |
| } | |
| # The ./ prefix is load-bearing: a bare "dist-npm/<name>" matches | |
| # npm's GitHub owner/repo shorthand and is fetched over git instead | |
| # of being read as a directory. | |
| for pkg in dist-npm/*/; do | |
| [ "$pkg" = "dist-npm/main/" ] && continue | |
| publish "./${pkg%/}" | |
| done | |
| publish ./dist-npm/main |