mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-23 15:45:13 +00:00
* feat(ci): add auto-generated CHANGELOG.md synced from releases (#4872) Add a Keep a Changelog CHANGELOG.md that is fully derived from the project's GitHub Releases, so users no longer have to dig through commit history to see what changed between versions. - scripts/generate-changelog.js: fetch releases via the gh CLI, keep only stable vX.Y.Z tags (nightly/preview omitted), and re-group each release's auto-generated "What's Changed" list into Added/Changed/Fixed/Performance/ Documentation/Other sections by the conventional-commit prefix every PR title uses. Zero runtime deps (node builtins + gh). - release.yml: regenerate and commit CHANGELOG.md on stable releases; the commit rides the existing release-branch PR into main. - package.json: add 'npm run changelog'. - .prettierignore: skip the generated file. - Seed CHANGELOG.md from the current stable release history. Closes #4872 * ci(release): make CHANGELOG regeneration non-blocking Add continue-on-error to the CHANGELOG step. Its only realistic failures (the gh API read and the git push) are transient, and the generator rebuilds from the full release history each run, so a skipped update self-heals on the next stable release. This keeps a changelog hiccup from blocking the version-bump PR to main that follows. * fix(ci): harden changelog generator per review Address review findings on the changelog generator: - Command injection: fetchReleasesJsonl built a shell string with the repo interpolated; switch to execFileSync (no shell) plus an owner/name format check so --repo can never be a shell payload. - Empty-response clobber: if the API returns zero stable releases (rate limit, auth, 5xx), refuse to overwrite CHANGELOG.md and exit 1 instead of committing a header-only stub. Paired with set -euo pipefail in the workflow so the failure is visible and the non-blocking step self-heals next release. - Arg parsing: switch getArgs() to parseArgs() (already imported) so a --dryrun typo errors instead of silently overwriting, and -h works as documented. - Breaking changes: capture the conventional-commit ! marker and prefix the entry with **BREAKING** (the repo uses feat()!:/refactor()!: in practice). - Bot authors: ENTRY_RE now accepts a trailing [bot] so GitHub App authors (e.g. @dependabot[bot]) are not dropped from release notes. Regenerates CHANGELOG.md (two entries now flagged **BREAKING**). * refactor(ci): simplify changelog generator Quality-only cleanup (output is byte-identical; 23 tests green): - Single source of truth for sections: derive TYPE_TO_SECTION and SECTION_ORDER from one SECTIONS list so they can't drift. - Parse each entry once: formatRelease now reuses the categorize() result for the noise check, section lookup, and formatEntry (was parsed up to 3x). - Drop the redundant sortKey field; sort directly from version. - Collapse the duplicated double-.map() setup in the selectStableReleases test.
541 lines
20 KiB
YAML
541 lines
20 KiB
YAML
name: 'Release'
|
|
|
|
on:
|
|
schedule:
|
|
# Runs every day at midnight UTC for the nightly release.
|
|
- cron: '0 0 * * *'
|
|
# Runs every Tuesday at 23:59 UTC for the preview release.
|
|
- cron: '59 23 * * 2'
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'The version to release (e.g., v0.1.11 or v0.1.11-preview.0). Required for manual patch releases.'
|
|
required: false
|
|
type: 'string'
|
|
ref:
|
|
description: 'The branch or ref (full git sha) to release from.'
|
|
required: true
|
|
type: 'string'
|
|
default: 'main'
|
|
dry_run:
|
|
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
|
|
required: true
|
|
type: 'boolean'
|
|
default: true
|
|
create_nightly_release:
|
|
description: 'Auto apply the nightly release tag, input version is ignored.'
|
|
required: false
|
|
type: 'boolean'
|
|
default: false
|
|
create_preview_release:
|
|
description: 'Create a preview release. If version is X.Y.Z-preview.N, use it as-is. If version is X.Y.Z, derive X.Y.Z-preview.0.'
|
|
required: false
|
|
type: 'boolean'
|
|
default: false
|
|
force_skip_tests:
|
|
description: 'Skip the release validation jobs ("quality", "integration_none", and "integration_docker"), allowing publish to proceed without them. Prod releases should run validation.'
|
|
required: false
|
|
type: 'boolean'
|
|
default: false
|
|
|
|
jobs:
|
|
prepare:
|
|
name: 'Prepare Release Metadata'
|
|
runs-on: 'ubuntu-latest'
|
|
if: |-
|
|
${{ github.repository == 'QwenLM/qwen-code' }}
|
|
permissions:
|
|
contents: 'read'
|
|
outputs:
|
|
release_tag: '${{ steps.version.outputs.RELEASE_TAG }}'
|
|
release_version: '${{ steps.version.outputs.RELEASE_VERSION }}'
|
|
npm_tag: '${{ steps.version.outputs.NPM_TAG }}'
|
|
previous_release_tag: '${{ steps.version.outputs.PREVIOUS_RELEASE_TAG }}'
|
|
is_nightly: '${{ steps.vars.outputs.is_nightly }}'
|
|
is_preview: '${{ steps.vars.outputs.is_preview }}'
|
|
is_dry_run: '${{ steps.vars.outputs.is_dry_run }}'
|
|
|
|
steps:
|
|
- name: 'Checkout'
|
|
uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2
|
|
with:
|
|
ref: '${{ github.event.inputs.ref || github.sha }}'
|
|
fetch-depth: 0
|
|
|
|
- name: 'Set booleans for simplified logic'
|
|
id: 'vars'
|
|
env:
|
|
CREATE_NIGHTLY_RELEASE: '${{ github.event.inputs.create_nightly_release }}'
|
|
CREATE_PREVIEW_RELEASE: '${{ github.event.inputs.create_preview_release }}'
|
|
CRON: '${{ github.event.schedule }}'
|
|
DRY_RUN_INPUT: '${{ github.event.inputs.dry_run }}'
|
|
run: |-
|
|
is_nightly="false"
|
|
if [[ "${CRON}" == "0 0 * * *" || "${CREATE_NIGHTLY_RELEASE}" == "true" ]]; then
|
|
is_nightly="true"
|
|
fi
|
|
echo "is_nightly=${is_nightly}" >> "${GITHUB_OUTPUT}"
|
|
|
|
is_preview="false"
|
|
if [[ "${CRON}" == "59 23 * * 2" || "${CREATE_PREVIEW_RELEASE}" == "true" ]]; then
|
|
is_preview="true"
|
|
fi
|
|
echo "is_preview=${is_preview}" >> "${GITHUB_OUTPUT}"
|
|
|
|
is_dry_run="false"
|
|
if [[ "${DRY_RUN_INPUT}" == "true" ]]; then
|
|
is_dry_run="true"
|
|
fi
|
|
echo "is_dry_run=${is_dry_run}" >> "${GITHUB_OUTPUT}"
|
|
|
|
- name: 'Setup Node.js'
|
|
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'npm'
|
|
cache-dependency-path: 'package-lock.json'
|
|
|
|
- name: 'Install Dependencies'
|
|
env:
|
|
NPM_CONFIG_PREFER_OFFLINE: 'true'
|
|
run: |-
|
|
npm ci --no-audit --progress=false
|
|
|
|
- name: 'Get the version'
|
|
id: 'version'
|
|
env:
|
|
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
|
IS_NIGHTLY: '${{ steps.vars.outputs.is_nightly }}'
|
|
IS_PREVIEW: '${{ steps.vars.outputs.is_preview }}'
|
|
MANUAL_VERSION: '${{ inputs.version }}'
|
|
run: |-
|
|
VERSION_ARGS=()
|
|
if [[ "${IS_NIGHTLY}" == "true" ]]; then
|
|
VERSION_ARGS+=(--type=nightly)
|
|
elif [[ "${IS_PREVIEW}" == "true" ]]; then
|
|
VERSION_ARGS+=(--type=preview)
|
|
if [[ -n "${MANUAL_VERSION}" ]]; then
|
|
MANUAL_CLEAN="${MANUAL_VERSION#v}"
|
|
if [[ "${MANUAL_CLEAN}" =~ ^[0-9]+\.[0-9]+\.[0-9]+-preview\.[0-9]+$ ]]; then
|
|
VERSION_ARGS+=("--preview_version_override=${MANUAL_CLEAN}")
|
|
elif [[ "${MANUAL_CLEAN}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION_ARGS+=("--preview_version_override=${MANUAL_CLEAN}-preview.0")
|
|
else
|
|
echo "::error::For preview releases, version must be X.Y.Z or X.Y.Z-preview.N; got ${MANUAL_VERSION}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
else
|
|
VERSION_ARGS+=(--type=stable)
|
|
if [[ -n "${MANUAL_VERSION}" ]]; then
|
|
VERSION_ARGS+=("--stable_version_override=${MANUAL_VERSION}")
|
|
fi
|
|
fi
|
|
|
|
VERSION_JSON=$(node scripts/get-release-version.js "${VERSION_ARGS[@]}")
|
|
echo "RELEASE_TAG=$(echo "$VERSION_JSON" | jq -r .releaseTag)" >> "$GITHUB_OUTPUT"
|
|
echo "RELEASE_VERSION=$(echo "$VERSION_JSON" | jq -r .releaseVersion)" >> "$GITHUB_OUTPUT"
|
|
echo "NPM_TAG=$(echo "$VERSION_JSON" | jq -r .npmTag)" >> "$GITHUB_OUTPUT"
|
|
echo "PREVIOUS_RELEASE_TAG=$(echo "$VERSION_JSON" | jq -r .previousReleaseTag)" >> "$GITHUB_OUTPUT"
|
|
|
|
quality:
|
|
name: 'Quality Checks'
|
|
runs-on: 'ubuntu-latest'
|
|
needs: 'prepare'
|
|
if: |-
|
|
${{ github.event.inputs.force_skip_tests != 'true' }}
|
|
permissions:
|
|
contents: 'read'
|
|
env:
|
|
OPENAI_API_KEY: '${{ secrets.OPENAI_API_KEY }}'
|
|
OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}'
|
|
OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}'
|
|
|
|
steps:
|
|
- name: 'Checkout'
|
|
uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2
|
|
with:
|
|
ref: '${{ github.event.inputs.ref || github.sha }}'
|
|
fetch-depth: 0
|
|
|
|
- name: 'Setup Node.js'
|
|
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'npm'
|
|
cache-dependency-path: 'package-lock.json'
|
|
|
|
- name: 'Install Dependencies'
|
|
env:
|
|
NPM_CONFIG_PREFER_OFFLINE: 'true'
|
|
run: |-
|
|
npm ci --no-audit --progress=false
|
|
|
|
- name: 'Format Project'
|
|
run: |-
|
|
npm run format
|
|
|
|
- name: 'Run Lint'
|
|
run: |-
|
|
npm run lint:ci
|
|
|
|
- name: 'Build Project'
|
|
run: |-
|
|
npm run build
|
|
|
|
- name: 'Typecheck Project'
|
|
run: |-
|
|
npm run typecheck
|
|
|
|
- name: 'Run Workspace Tests'
|
|
run: |-
|
|
npm run test:ci
|
|
|
|
integration_none:
|
|
name: 'Integration Tests (No Sandbox)'
|
|
runs-on: 'ubuntu-latest'
|
|
needs: 'prepare'
|
|
if: |-
|
|
${{ github.event.inputs.force_skip_tests != 'true' }}
|
|
permissions:
|
|
contents: 'read'
|
|
env:
|
|
OPENAI_API_KEY: '${{ secrets.OPENAI_API_KEY }}'
|
|
OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}'
|
|
OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}'
|
|
|
|
steps:
|
|
- name: 'Checkout'
|
|
uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2
|
|
with:
|
|
ref: '${{ github.event.inputs.ref || github.sha }}'
|
|
fetch-depth: 0
|
|
|
|
- name: 'Setup Node.js'
|
|
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'npm'
|
|
cache-dependency-path: 'package-lock.json'
|
|
|
|
- name: 'Install Dependencies'
|
|
env:
|
|
NPM_CONFIG_PREFER_OFFLINE: 'true'
|
|
run: |-
|
|
npm ci --no-audit --progress=false
|
|
|
|
- name: 'Run CLI Integration Tests'
|
|
run: |-
|
|
npm run test:integration:cli:sandbox:none
|
|
|
|
- name: 'Run Interactive Integration Tests'
|
|
run: |-
|
|
npm run test:integration:interactive:sandbox:none
|
|
|
|
integration_docker:
|
|
name: 'Integration Tests (Docker)'
|
|
runs-on: 'ubuntu-latest'
|
|
needs: 'prepare'
|
|
if: |-
|
|
${{ github.event.inputs.force_skip_tests != 'true' }}
|
|
permissions:
|
|
contents: 'read'
|
|
env:
|
|
OPENAI_API_KEY: '${{ secrets.OPENAI_API_KEY }}'
|
|
OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}'
|
|
OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}'
|
|
|
|
steps:
|
|
- name: 'Checkout'
|
|
uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2
|
|
with:
|
|
ref: '${{ github.event.inputs.ref || github.sha }}'
|
|
fetch-depth: 0
|
|
|
|
- name: 'Setup Node.js'
|
|
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'npm'
|
|
cache-dependency-path: 'package-lock.json'
|
|
|
|
- name: 'Install Dependencies'
|
|
env:
|
|
NPM_CONFIG_PREFER_OFFLINE: 'true'
|
|
run: |-
|
|
npm ci --no-audit --progress=false
|
|
|
|
- name: 'Set up Docker'
|
|
uses: 'docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd' # ratchet:docker/setup-buildx-action@v4
|
|
|
|
- name: 'Build Sandbox'
|
|
env:
|
|
QWEN_SANDBOX: 'docker'
|
|
run: |-
|
|
npm run build:sandbox -- -s
|
|
|
|
- name: 'Run CLI Docker Integration Tests'
|
|
run: |-
|
|
# The package.json docker test scripts each rebuild the sandbox image.
|
|
# Run vitest directly here so this job reuses the image built above.
|
|
QWEN_SANDBOX=docker npx vitest run --root ./integration-tests cli
|
|
|
|
- name: 'Run Interactive Docker Integration Tests'
|
|
run: |-
|
|
QWEN_SANDBOX=docker npx vitest run --root ./integration-tests interactive
|
|
|
|
publish:
|
|
name: 'Publish Release'
|
|
runs-on: 'ubuntu-latest'
|
|
needs:
|
|
- 'prepare'
|
|
- 'quality'
|
|
- 'integration_none'
|
|
- 'integration_docker'
|
|
if: |-
|
|
${{
|
|
always() &&
|
|
needs.prepare.result == 'success' &&
|
|
(
|
|
github.event.inputs.force_skip_tests == 'true' ||
|
|
(
|
|
needs.quality.result == 'success' &&
|
|
needs.integration_none.result == 'success' &&
|
|
needs.integration_docker.result == 'success'
|
|
)
|
|
)
|
|
}}
|
|
environment:
|
|
name: 'production-release'
|
|
url: '${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ needs.prepare.outputs.release_tag }}'
|
|
permissions:
|
|
contents: 'write'
|
|
packages: 'write'
|
|
id-token: 'write'
|
|
pull-requests: 'write'
|
|
issues: 'write'
|
|
|
|
steps:
|
|
- name: 'Checkout'
|
|
uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2
|
|
with:
|
|
ref: '${{ github.event.inputs.ref || github.sha }}'
|
|
fetch-depth: 0
|
|
|
|
- name: 'Setup Node.js'
|
|
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'npm'
|
|
cache-dependency-path: 'package-lock.json'
|
|
registry-url: 'https://registry.npmjs.org'
|
|
scope: '@qwen-code'
|
|
|
|
- name: 'Install Dependencies'
|
|
env:
|
|
NPM_CONFIG_PREFER_OFFLINE: 'true'
|
|
run: |-
|
|
npm ci --no-audit --progress=false
|
|
|
|
- name: 'Configure Git User'
|
|
run: |-
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
|
|
- name: 'Create and switch to a release branch'
|
|
id: 'release_branch'
|
|
env:
|
|
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
|
|
run: |-
|
|
BRANCH_NAME="release/${RELEASE_TAG}"
|
|
git switch -c "${BRANCH_NAME}"
|
|
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
|
|
|
|
- name: 'Update package versions'
|
|
env:
|
|
RELEASE_VERSION: '${{ needs.prepare.outputs.release_version }}'
|
|
run: |-
|
|
npm run release:version "${RELEASE_VERSION}"
|
|
|
|
- name: 'Commit and Conditionally Push package versions'
|
|
env:
|
|
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
|
IS_DRY_RUN: '${{ needs.prepare.outputs.is_dry_run }}'
|
|
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
|
|
run: |-
|
|
git add package.json package-lock.json packages/*/package.json packages/channels/*/package.json
|
|
if git diff --staged --quiet; then
|
|
echo "No version changes to commit"
|
|
else
|
|
git commit -m "chore(release): ${RELEASE_TAG}"
|
|
fi
|
|
if [[ "${IS_DRY_RUN}" == "false" ]]; then
|
|
echo "Pushing release branch to remote..."
|
|
git push --set-upstream origin "${BRANCH_NAME}" --follow-tags
|
|
else
|
|
echo "Dry run enabled. Skipping push."
|
|
fi
|
|
|
|
- name: 'Build Bundle and Prepare Package'
|
|
run: |-
|
|
npm run bundle
|
|
npm run prepare:package
|
|
|
|
- name: 'Build Standalone Archives'
|
|
env:
|
|
RELEASE_VERSION: '${{ needs.prepare.outputs.release_version }}'
|
|
run: 'npm run package:standalone:release -- --version "${RELEASE_VERSION}" --out-dir dist/standalone'
|
|
|
|
- name: 'Publish @qwen-code/qwen-code'
|
|
working-directory: 'dist'
|
|
run: |-
|
|
npm publish --access public --tag=${{ needs.prepare.outputs.npm_tag }} ${{ needs.prepare.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
|
env:
|
|
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
|
|
|
|
- name: 'Publish @qwen-code/channel-base'
|
|
working-directory: 'packages/channels/base'
|
|
run: |-
|
|
npm publish --access public --tag=${{ needs.prepare.outputs.npm_tag }} ${{ needs.prepare.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
|
env:
|
|
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
|
|
|
|
- name: 'Verify Standalone Archives'
|
|
run: |-
|
|
npm run verify:installation-release -- --dir dist/standalone
|
|
|
|
- name: 'Create GitHub Release and Tag'
|
|
if: |-
|
|
${{ needs.prepare.outputs.is_dry_run == 'false' }}
|
|
env:
|
|
# CI_BOT_PAT required: GITHUB_TOKEN events cannot trigger downstream workflows (sync-release-to-oss.yml).
|
|
GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}'
|
|
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
|
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
|
|
PREVIOUS_RELEASE_TAG: '${{ needs.prepare.outputs.previous_release_tag }}'
|
|
IS_NIGHTLY: '${{ needs.prepare.outputs.is_nightly }}'
|
|
IS_PREVIEW: '${{ needs.prepare.outputs.is_preview }}'
|
|
run: |-
|
|
PRERELEASE_FLAG=""
|
|
if [[ "${IS_NIGHTLY}" == "true" || "${IS_PREVIEW}" == "true" ]]; then
|
|
PRERELEASE_FLAG="--prerelease"
|
|
fi
|
|
|
|
gh release create "${RELEASE_TAG}" \
|
|
dist/cli.js \
|
|
dist/standalone/qwen-code-* \
|
|
dist/standalone/SHA256SUMS \
|
|
--target "${RELEASE_BRANCH}" \
|
|
--title "Release ${RELEASE_TAG}" \
|
|
--notes-start-tag "${PREVIOUS_RELEASE_TAG}" \
|
|
--generate-notes \
|
|
${PRERELEASE_FLAG}
|
|
|
|
- name: 'Regenerate CHANGELOG.md'
|
|
# Stable releases only: nightly/preview ship daily and would drown out
|
|
# the changelog. The just-created GitHub Release is already queryable,
|
|
# so the generator picks it up. The release branch was already pushed
|
|
# above, so push this follow-up commit too — otherwise the PR opened
|
|
# below (whose head is the remote branch) would not include it.
|
|
#
|
|
# Non-blocking by design: the only realistic failures are transient
|
|
# (the gh API read or the git push). The changelog is rebuilt from the
|
|
# full release history on every run, so a skipped update self-heals on
|
|
# the next stable release — never worth blocking the version-bump PR to
|
|
# main that follows.
|
|
if: |-
|
|
${{ needs.prepare.outputs.is_dry_run == 'false' && needs.prepare.outputs.is_nightly == 'false' && needs.prepare.outputs.is_preview == 'false' }}
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
|
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
|
|
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
|
run: |-
|
|
set -euo pipefail
|
|
node scripts/generate-changelog.js
|
|
git add CHANGELOG.md
|
|
if git diff --cached --quiet -- CHANGELOG.md; then
|
|
echo "CHANGELOG.md already up to date."
|
|
else
|
|
git commit -m "docs(changelog): sync for ${RELEASE_TAG}"
|
|
git push origin "${BRANCH_NAME}"
|
|
fi
|
|
|
|
- name: 'Create PR to merge release branch into main'
|
|
if: |-
|
|
${{ needs.prepare.outputs.is_dry_run == 'false' && needs.prepare.outputs.is_nightly == 'false' && needs.prepare.outputs.is_preview == 'false' }}
|
|
id: 'pr'
|
|
env:
|
|
GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}'
|
|
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
|
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
|
|
run: |-
|
|
set -euo pipefail
|
|
|
|
pr_url="$(gh pr list --head "${RELEASE_BRANCH}" --base main --json url --jq '.[0].url')"
|
|
if [[ -z "${pr_url}" ]]; then
|
|
pr_url="$(gh pr create \
|
|
--base main \
|
|
--head "${RELEASE_BRANCH}" \
|
|
--title "chore(release): ${RELEASE_TAG}" \
|
|
--body "Automated release PR for ${RELEASE_TAG}. Syncs package.json versions and CHANGELOG.md on main.")"
|
|
fi
|
|
|
|
echo "PR_URL=${pr_url}" >> "${GITHUB_OUTPUT}"
|
|
|
|
- name: 'Enable auto-merge for release PR'
|
|
if: |-
|
|
${{ needs.prepare.outputs.is_dry_run == 'false' && needs.prepare.outputs.is_nightly == 'false' && needs.prepare.outputs.is_preview == 'false' }}
|
|
env:
|
|
GITHUB_TOKEN: '${{ secrets.CI_BOT_PAT }}'
|
|
PR_URL: '${{ steps.pr.outputs.PR_URL }}'
|
|
RELEASE_TAG: '${{ needs.prepare.outputs.release_tag }}'
|
|
run: |-
|
|
set -euo pipefail
|
|
# Keep [skip ci] only on the squash commit that lands on main. The
|
|
# release branch commit and PR title intentionally omit it so tag-push
|
|
# workflows and PR metadata stay unaffected.
|
|
gh pr merge "${PR_URL}" \
|
|
--squash \
|
|
--auto \
|
|
--delete-branch \
|
|
--subject "chore(release): ${RELEASE_TAG} [skip ci]"
|
|
|
|
notify_failure:
|
|
name: 'Notify Release Failure'
|
|
runs-on: 'ubuntu-latest'
|
|
needs:
|
|
- 'prepare'
|
|
- 'quality'
|
|
- 'integration_none'
|
|
- 'integration_docker'
|
|
- 'publish'
|
|
if: |-
|
|
${{
|
|
always() &&
|
|
(
|
|
github.event_name == 'schedule' ||
|
|
github.event.inputs.dry_run != 'true'
|
|
) &&
|
|
(
|
|
needs.prepare.result == 'failure' ||
|
|
needs.quality.result == 'failure' ||
|
|
needs.integration_none.result == 'failure' ||
|
|
needs.integration_docker.result == 'failure' ||
|
|
needs.publish.result == 'failure'
|
|
)
|
|
}}
|
|
permissions:
|
|
issues: 'write'
|
|
|
|
steps:
|
|
- name: 'Create Issue on Failure'
|
|
env:
|
|
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
|
GH_REPO: '${{ github.repository }}'
|
|
RELEASE_TAG: "${{ needs.prepare.outputs.release_tag || 'N/A' }}"
|
|
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
|
run: |-
|
|
gh issue create \
|
|
--title "Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')" \
|
|
--body "The release workflow failed. See the full run for details: ${DETAILS_URL}"
|