Files
Oleg Isonen 58eaf8c7c9 fix: Publish docs independently of release smoke (#6120)
## Summary

- finish the `publish` job after npm publication and exact
registry-artifact verification
- run packaged CLI verification in a separate `release-smoke` job
- keep `publish-docs` dependent on successful release creation and
package publication, but independent of post-publish smoke results
- verify the Codex registration command through `webstudio connect codex
--print` in CI instead of requiring an installed Codex client

## Root cause

The release workflow ran post-publish CLI smoke verification inside the
`publish` job. GitHub therefore marked package publication as failed
whenever the smoke failed, even though npm publication had already
completed. Because `publish-docs` declared `needs: [release, publish]`,
GitHub skipped documentation publication.

The recurring failure was the smoke invoking `webstudio connect codex`
on a runner without the `codex` executable.

## Technical choices and tradeoffs

Package publication remains strict: npm publication and exact registry
visibility must both succeed before downstream jobs run. Post-publish
smoke verification remains a red, visible release job and still uploads
its diagnostic artifact, but it no longer changes the truthful outcome
of publication or blocks docs.

The Codex package smoke now validates the generated registration command
without mutating a real Codex installation. The command subprocess
boundary remains covered by the existing focused connect-command tests.

No migrations or compatibility changes are involved.

## Todo

- [x] Separate post-publish CLI smoke from package publication.
- [x] Keep docs gated on successful release and registry publication.
- [x] Make Codex connection verification deterministic in CI.
- [x] Manually advance the `docs` branch to release 0.290.0.
- [x] Review documentation impact: no product documentation update is
required because this changes release automation only.
- [x] Verify formatting, workflow syntax and topology, CLI tests, lint,
and typechecking.
- [x] Run the non-model release smoke and record the remaining unrelated
failure.

## Verification

- `pnpm exec oxfmt --check .github/workflows/release.yml
packages/cli/scripts/release-smoke.ts`
- parsed `.github/workflows/release.yml` with Ruby Psych
- asserted the parsed workflow job graph with the repository's `yaml`
dependency
- `pnpm --filter webstudio exec vitest run
scripts/release-smoke-registry.test.ts src/commands/connect.test.ts` —
22 tests passed
- `pnpm --filter webstudio typecheck`
- `pnpm exec oxlint packages/cli/scripts/release-smoke.ts`
- `pnpm --filter webstudio test:release-smoke` — the corrected Codex
path passed; the smoke later reached `preview.start` and failed on an
unrelated current-main fixture error: `Project session is missing
preview data: assetFolders`

Fixture review: this change does not modify fixture inputs or generated
fixture outputs.

Closes #6119
2026-08-12 22:50:41 +02:00

198 lines
7.1 KiB
YAML

name: Release
on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+"
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo,
})
const commits = await github.rest.repos.compareCommitsWithBasehead({
owner: context.repo.owner,
repo: context.repo.repo,
basehead: `refs/tags/${latestRelease.data.tag_name}...${context.ref}`,
})
const groups = {
feat: [`## Features\n`],
fix: [`## Fixes\n`],
docs: [`## Documentation\n`],
experimental: [`## Experimental\n`],
other: [`## Other changes\n`],
}
for (const commit of commits.data.commits) {
const match = commit.commit.message.match(/^(?<type>\w+)\s*:\s*(?<message>.+)\n*/)
const type = match?.groups?.type
const message = match?.groups?.message
if (type && message) {
const availableType = type in groups ? type : 'other'
const capitalized = message[0].toLocaleUpperCase() + message.slice(1)
groups[availableType].push(`- ${capitalized}`)
}
}
const tag_name = context.ref.slice('refs/tags/'.length)
const fullChangelog = `**Full Changelog**: https://github.com/${context.repo.owner}/${context.repo.repo}/compare/${latestRelease.data.tag_name}...${tag_name}`
const changelog = Object.values(groups)
.filter(lines => lines.length > 1)
.map(lines => lines.join('\n'))
.concat(fullChangelog)
.join('\n\n')
console.info(changelog)
await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name,
name: tag_name,
body: changelog,
})
publish:
runs-on: ubuntu-latest
outputs:
registry_baseline: ${{ steps.registry-baseline.outputs.value }}
version: ${{ steps.version.outputs.value }}
permissions:
contents: write
environment:
name: development
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }} # tag name
- uses: ./.github/actions/submodules-checkout
with:
submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- id: version
run: echo "value=$(echo ${{ github.ref }} | sed 's/refs\/tags\///')" >> $GITHUB_OUTPUT
- id: registry-baseline
name: Resolve pre-publish CLI baseline
run: echo "value=$(npm view webstudio@latest version)" >> "$GITHUB_OUTPUT"
- name: bump version to ${{ steps.version.outputs.value }}
run: |
pnpx replace-in-files-cli \
--string="0.0.0-webstudio-version" \
--replacement="${{ steps.version.outputs.value }}" \
"**/package.json"
- name: pnpm instal
run: pnpm install --ignore-scripts
- run: pnpm --filter="webstudio..." build
- run: pnpm --filter="webstudio..." dts
- name: Creating .npmrc
run: |
cat << EOF > "$HOME/.npmrc"
//registry.npmjs.org/:_authToken=$NPM_TOKEN
EOF
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: pnpm -r publish --access public --no-git-checks
- name: Wait for exact CLI registry artifact
env:
VERSION: ${{ steps.version.outputs.value }}
run: |
for attempt in $(seq 1 30); do
integrity=$(npm view "webstudio@$VERSION" dist.integrity --json 2>/dev/null || true)
if [ -n "$integrity" ] && [ "$integrity" != "null" ]; then
exit 0
fi
sleep 10
done
echo "Exact registry artifact webstudio@$VERSION was not observable with integrity metadata." >&2
exit 1
release-smoke:
needs: publish
timeout-minutes: 20
runs-on: ubuntu-24.04
permissions:
contents: read
environment:
name: development
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }} # tag name
- uses: ./.github/actions/submodules-checkout
with:
submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
- uses: ./.github/actions/ci-setup
- name: Verify exact published CLI against baseline
id: registry-smoke
env:
DATABASE_URL: postgres://
AUTH_SECRET: test
WEBSTUDIO_RELEASE_SMOKE_TOKEN_BUDGET: 6000
WEBSTUDIO_RELEASE_SMOKE_CANDIDATE: webstudio@${{ needs.publish.outputs.version }}
WEBSTUDIO_RELEASE_SMOKE_BASELINE: webstudio@${{ needs.publish.outputs.registry_baseline }}
WEBSTUDIO_RELEASE_SMOKE_EXPECT_REGISTRY_VERSION: ${{ needs.publish.outputs.version }}
WEBSTUDIO_RELEASE_SMOKE_REPORT: ${{ github.workspace }}/artifacts/cli-release-smoke.json
run: pnpm --filter webstudio test:release-smoke:registry
- name: Upload published CLI comparison report
if: ${{ always() && (steps.registry-smoke.outcome == 'success' || steps.registry-smoke.outcome == 'failure') }}
uses: actions/upload-artifact@v4
with:
name: cli-release-smoke-${{ needs.publish.outputs.version }}
path: artifacts/cli-release-smoke.json
if-no-files-found: error
publish-docs:
needs: [release, publish]
runs-on: ubuntu-latest
concurrency:
group: publish-docs
cancel-in-progress: false
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: docs
- name: Publish released documentation snapshot
env:
RELEASE_SHA: ${{ github.sha }}
RELEASE_VERSION: ${{ github.ref_name }}
run: |
if git merge-base --is-ancestor "$RELEASE_SHA" HEAD; then
echo "The docs branch already contains release $RELEASE_VERSION or a newer revision."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git merge --no-edit "$RELEASE_SHA"
git checkout "$RELEASE_SHA" -- docs/university/mcp.md
CLI_VERSION_PLACEHOLDER="0.0.0-webstudio-version"
grep -Fq "$CLI_VERSION_PLACEHOLDER" docs/university/mcp.md
sed -i.bak "s/$CLI_VERSION_PLACEHOLDER/$RELEASE_VERSION/g" docs/university/mcp.md
rm docs/university/mcp.md.bak
! grep -Fq "$CLI_VERSION_PLACEHOLDER" docs/university/mcp.md
grep -Fq "$RELEASE_VERSION" docs/university/mcp.md
git add docs/university/mcp.md
git commit -m "docs: publish Webstudio CLI $RELEASE_VERSION"
git push origin docs