diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index df1316e1..fd439f54 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -145,6 +145,8 @@ jobs: permissions: contents: read id-token: write # npm trusted publishing, so there is no npm token + outputs: + version: ${{ steps.publish.outputs.version }} steps: - uses: actions/checkout@v7 with: @@ -156,7 +158,12 @@ jobs: registry-url: "https://registry.npmjs.org" - run: npm ci # prepublishOnly builds the bundle. - - run: npm publish --access public + - name: Publish + id: publish + run: | + version="$(node -p "require('./package.json').version")" + npm publish --access public + echo "version=$version" >> "$GITHUB_OUTPUT" # Same shape as publish-npm. Tagging is a separate job, so nothing here needs # write access to the repository. @@ -285,10 +292,47 @@ jobs: ) }} runs-on: ubuntu-latest - timeout-minutes: 5 + # npm metadata is normally visible within about five minutes. Leave 2.5x that + # window for propagation, plus enough time to mint the token and dispatch. + timeout-minutes: 15 environment: release permissions: {} steps: + - name: Wait for package to become available + if: ${{ needs.publish-npm.result == 'success' || needs.publish-npm-preview.outputs.published == 'true' }} + env: + PACKAGE: "@agentclientprotocol/codex-acp" + VERSION: ${{ needs.publish-npm-preview.outputs.version || needs.publish-npm.outputs.version }} + #language=bash + run: | + if [ -z "$VERSION" ]; then + echo "::error::The publish job did not report a package version." + exit 1 + fi + + deadline=$((SECONDS + 750)) + while (( SECONDS < deadline )); do + if timeout 10s npm pack "$PACKAGE@$VERSION" \ + --dry-run \ + --ignore-scripts \ + --silent \ + --registry=https://registry.npmjs.org \ + --prefer-online \ + --fetch-retries=0 >/dev/null 2>&1; then + echo "$PACKAGE@$VERSION is available from npm." + exit 0 + fi + + remaining=$((deadline - SECONDS)) + if (( remaining > 0 )); then + echo "Waiting for $PACKAGE@$VERSION to become available (${remaining}s remaining)..." + sleep 10 + fi + done + + echo "::error::$PACKAGE@$VERSION was not available from npm after 12.5 minutes." + exit 1 + - name: Generate token scoped to the registry repo uses: actions/create-github-app-token@v3 id: registry-token diff --git a/docs/RELEASES.md b/docs/RELEASES.md index fa12b866..e11eccd8 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -61,9 +61,12 @@ handling for preview versions. version in the working tree, then publishes to npm. The `prepublishOnly` hook builds the bundle before publication. After publishing, `publish-tag-preview` creates the tag and `trigger-registry-update` dispatches the registry update -independently; neither waits for the other. The registry job is shared with the -stable path. A tag failure can be retried on its own with **Re-run failed jobs**, -leaving the successful npm publish untouched. +independently; neither waits for the other. Before dispatching, the registry job +polls npm for the exact published version for up to 12.5 minutes, including +downloading its tarball, so the registry never checks while npm is still +propagating the package. The registry job is shared with the stable path. A tag +failure can be retried on its own with **Re-run failed jobs**, leaving the +successful npm publish untouched. Previews start directly on push, without waiting for the [`CI`](../.github/workflows/ci.yml) workflow or the `release-please` job. The @@ -72,8 +75,9 @@ still requires the `verify` job to pass. The publish step runs `npm publish --access public --tag preview` and sets `published=true` only after it succeeds. Both downstream jobs use that output -to proceed with preview tagging and registry dispatch. This is a real publish, -with no dry-run stage. +to proceed with preview tagging and the registry availability check. This is a +real publish, with no dry-run stage; the later dry run downloads the published +tarball to verify it has propagated. A stable and a preview dispatch can never collide — a release merge publishes stable and skips the preview, every other push does the reverse — so the registry