mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-13 16:23:29 +09:00
## Summary
- render the generated MCP version as visible body content immediately
below the page heading
- keep the GitBook navigation label stable as `MCP`
- stamp only the CLI version placeholder during release publication,
leaving Markdown content owned by the template
- remove empty `{}` blocks from the rendered MCP argument examples while
preserving meaningful examples and complete structured JSON data
## Root cause
GitBook uses the `SUMMARY.md` link label as the published page heading
and suppresses the Markdown H1 from the document body. The generated
version was therefore present in source but absent from the rendered
page.
The release workflow also coupled version stamping to the complete
rendered Markdown line. It now replaces and verifies only the standard
`0.0.0-webstudio-version` placeholder.
## Verification
- live page structure inspected: published H1 is `MCP`
- MCP manual regression test demonstrated failing before empty argument
blocks were filtered, then passing after the fix
- release placeholder stamping simulated against the generated page
- `pnpm --filter webstudio exec vitest run src/commands/man.test.ts`
- `pnpm --filter @webstudio-is/project-build typecheck`
- `pnpm --filter webstudio typecheck`
- `pnpm check:generated-docs`
- `pnpm lint`
- `pnpm oxfmt --check packages/project-build/src/mcp.ts
packages/cli/src/commands/man.ts packages/cli/src/commands/man.test.ts`
- `git diff --check`
Fixture inputs and outputs are unaffected.
## Todo
- [x] Generate the release version as visible GitBook body content.
- [x] Decouple release stamping from the rendered Markdown content.
- [x] Remove empty MCP argument example blocks.
- [x] Validate generated output and regression coverage.
- [ ] Merge this pull request.
- [ ] Publish the latest released revision to the `docs` branch.
- [ ] Verify the version on the live GitBook page.
Ref #5986
178 lines
6.5 KiB
YAML
178 lines
6.5 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
|
|
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
|
|
|
|
- name: Gate 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@${{ steps.version.outputs.value }}
|
|
WEBSTUDIO_RELEASE_SMOKE_BASELINE: webstudio@${{ steps.registry-baseline.outputs.value }}
|
|
WEBSTUDIO_RELEASE_SMOKE_EXPECT_REGISTRY_VERSION: ${{ steps.version.outputs.value }}
|
|
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-${{ steps.version.outputs.value }}
|
|
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
|