From 2c56b67e372b821d7bfb7c8cd9b28e1306c4d39e Mon Sep 17 00:00:00 2001 From: Daniel Martin <56157528+dmartinochoa@users.noreply.github.com> Date: Thu, 21 May 2026 10:34:11 +0200 Subject: [PATCH] ci(publish): don't strand the GitHub release on a transient registry failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both registry publishes now use `continue-on-error: true` and the "Create GitHub release" step runs as long as at least one registry accepted the publish. A new "Registry publish status" step at the end re-asserts the job-level failure when either publish errored, so partial-success runs are still visible in the run UI / branch protection / notifications. Motivation: v1.0.2's publish workflow failed at "Publish to Open VSX" with an HTTP 405 (transient registry-side; v1.0.1 shipped unchanged 14 h earlier). Because the publish step is a `run:` script with `bash -e`, the job aborted there and "Create GitHub release" never ran — so v1.0.2 made it to the VS Code Marketplace but was missing from both Open VSX and GitHub Releases. The recovery cost a fresh 1.0.x tag (v1.0.3). Behaviour matrix after this change: vsce | ovsx | GH release | job -----|------|------------|----- ✓ | ✓ | created | green (unchanged) ✓ | ✗ | created | red (was: not created, red) ✗ | ✓ | created | red (was: not created, red) ✗ | ✗ | skipped | red (unchanged) The status step gates itself on `steps..conclusion == 'success'` so it stays quiet when the job fails upstream of publish (lint, tests, audit, etc.) — `conclusion` is the post-`continue-on-error` roll-up, which is 'success' for any publish step that actually executed and 'skipped' for steps that never reached execution. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/publish.yml | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 352fc28..4d915d7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -186,7 +186,18 @@ jobs: with: subject-path: ${{ env.VSIX_PATH }} + # The two registry publishes use `continue-on-error: true` so a + # transient failure in one registry doesn't strand the GitHub + # release. v1.0.2 hit exactly this: Open VSX returned an HTTP 405 + # (registry-side hiccup) and `bash -e` aborted the job, which also + # skipped the release step — leaving the .vsix on Marketplace but + # no GitHub release for the tag. The final "Registry publish + # status" step below still marks the job red if either publish + # failed, so partial-success runs are visible in the run UI and + # to any branch-protection/notification wiring. - name: Publish to VS Code Marketplace + id: publish-vsce + continue-on-error: true env: VSCE_PAT: ${{ secrets.VSCE_PAT }} run: | @@ -195,6 +206,8 @@ jobs: --pat "$VSCE_PAT" - name: Publish to Open VSX + id: publish-ovsx + continue-on-error: true env: OVSX_PAT: ${{ secrets.OVSX_PAT }} run: | @@ -205,6 +218,15 @@ jobs: --pat "$OVSX_PAT" - name: Create GitHub release + # Run as long as at least one registry accepted the publish — + # that's the case where consumers need somewhere to download + # the .vsix from. If both registries failed, skip (no point + # shipping a release tied to a version nobody can install) and + # let the registry-status step below fail the job. + if: | + always() && !cancelled() && + (steps.publish-vsce.outcome == 'success' || + steps.publish-ovsx.outcome == 'success') env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -212,3 +234,31 @@ jobs: gh release create "v${version}" "$VSIX_PATH" "$SBOM_PATH" $GH_PRERELEASE \ --title "v${version}" \ --notes-file <(awk '/^## \[/{n++} n==2{exit} n==1{print}' CHANGELOG.md) + + - name: Registry publish status + # continue-on-error on the publish steps lets the workflow + # reach the GH release on partial success, but the job itself + # must still fail if any registry rejected the publish so the + # failure is visible in the run UI. + # + # Only run if at least one publish step actually executed — we + # check `conclusion` (the post-continue-on-error roll-up, which + # is 'success' for any step that ran, and 'skipped' for steps + # that didn't reach execution). This keeps the status check + # quiet on lint/test/audit failures upstream of publish. + if: | + always() && !cancelled() && + (steps.publish-vsce.conclusion == 'success' || + steps.publish-ovsx.conclusion == 'success') + env: + VSCE_OUTCOME: ${{ steps.publish-vsce.outcome }} + OVSX_OUTCOME: ${{ steps.publish-ovsx.outcome }} + run: | + set -euo pipefail + echo "VS Code Marketplace publish: $VSCE_OUTCOME" + echo "Open VSX publish: $OVSX_OUTCOME" + if [ "$VSCE_OUTCOME" != "success" ] || [ "$OVSX_OUTCOME" != "success" ]; then + echo "::error::One or more registry publishes failed — see step logs above" + exit 1 + fi + echo "Both registries accepted the publish."