Release #24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Cuts tagged releases for the public code-interpreter repo. Like ci.yml this | |
| # file is inert inside the monorepo — GitHub only runs workflows from the repo | |
| # root — and becomes a root workflow in the published repo. | |
| # | |
| # Three entry points feed one job: | |
| # | |
| # * successful CI on main — automatically releases deployable changes. The | |
| # next repository version follows Conventional Commit intent; changes that | |
| # only touch docs, workflows, or tests do not cut a release. | |
| # | |
| # * workflow_dispatch — pick a version in the Actions UI. The chart is | |
| # packaged before the tag is created, so a packaging failure aborts while | |
| # the release is still un-cut and the version is still free to reuse. | |
| # * push of a v* tag — for tags cut locally with `git tag -a … && git push`. | |
| # Tags this workflow pushes itself carry GITHUB_TOKEN, and GitHub does not | |
| # re-trigger workflows for those, so the two paths never double-publish. | |
| # | |
| # `main` accepts no direct pushes (see CONTRIBUTING.md), but the branch | |
| # ruleset does not cover tags, so the job can create them. GITHUB_TOKEN | |
| # defaults to read-only in this repository; the explicit `contents: write` | |
| # below is what lets the tag push and the release upload through. | |
| name: Release | |
| on: | |
| workflow_run: | |
| workflows: ['CI'] | |
| branches: [main] | |
| types: [completed] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Repository version to release, e.g. v1.0.0 or v1.1.0-rc1.' | |
| required: true | |
| type: string | |
| draft: | |
| description: 'Publish as a draft so the notes can be edited before going public' | |
| type: boolean | |
| default: false | |
| push: | |
| tags: | |
| - 'v*' | |
| permissions: | |
| contents: write | |
| concurrency: | |
| # Automatic runs serialize against one another. If main advances before an | |
| # older run starts, version resolution skips the stale SHA and the newest | |
| # successful run releases the full range instead. | |
| group: release-${{ github.event_name == 'workflow_run' && 'main' || github.event.inputs.version || github.ref_name }} | |
| cancel-in-progress: false | |
| jobs: | |
| release: | |
| name: Tag and publish | |
| if: >- | |
| github.event_name != 'workflow_run' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_branch == 'main') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| # `workflow_run` can be rerun for a commit older than this resolver. Save | |
| # the helper from the revision that supplied this workflow before the | |
| # release checkout replaces the working tree with that historical SHA. | |
| - name: Checkout release workflow | |
| if: github.event_name == 'workflow_run' | |
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| with: | |
| fetch-depth: 1 | |
| ref: ${{ github.workflow_sha }} | |
| - name: Preserve release resolver | |
| if: github.event_name == 'workflow_run' | |
| run: install -m 755 .github/scripts/resolve-release-version.sh "$RUNNER_TEMP/resolve-release-version.sh" | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| with: | |
| # Full history and tags: resolving whether this release is the newest | |
| # stable one compares it against every other tag in the repository. | |
| fetch-depth: 0 | |
| ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.ref }} | |
| - name: Resolve and validate version | |
| id: version | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| HEAD_SHA: ${{ github.event.workflow_run.head_sha }} | |
| INPUT_VERSION: ${{ github.event.inputs.version }} | |
| INPUT_DRAFT: ${{ github.event.inputs.draft }} | |
| REF_NAME: ${{ github.ref_name }} | |
| REF_TYPE: ${{ github.ref_type }} | |
| GH_TOKEN: ${{ github.token }} | |
| RESOLVER_PATH: ${{ github.event_name == 'workflow_run' && format('{0}/resolve-release-version.sh', runner.temp) || '.github/scripts/resolve-release-version.sh' }} | |
| # The resolution itself lives in a script so that every path through it | |
| # — automatic release, resumed release, dispatch, pushed tag, and the | |
| # runs that must skip or fail — is covered by | |
| # tests/release-version-resolution.sh in CI. | |
| run: "$RESOLVER_PATH" | |
| # helm is preinstalled on ubuntu-latest, the same way the chart tests in | |
| # ci.yml depend on it. | |
| - name: Package Helm chart | |
| id: chart | |
| if: steps.version.outputs.skip != 'true' | |
| run: | | |
| set -euo pipefail | |
| # Subcharts resolve through the Bitnami OCI mirror on Docker Hub, | |
| # which rate-limits anonymous pulls. A transient 429 should cost a | |
| # retry, not the release. | |
| for attempt in 1 2 3; do | |
| if helm dependency update helm/codeapi; then | |
| break | |
| fi | |
| if [ "$attempt" = 3 ]; then | |
| echo "::error::helm dependency update failed after 3 attempts" | |
| exit 1 | |
| fi | |
| sleep $(( attempt * 15 )) | |
| done | |
| helm package helm/codeapi --destination dist | |
| CHART_PATH="$(ls dist/codeapi-*.tgz)" | |
| { | |
| echo "path=$CHART_PATH" | |
| echo "name=$(basename "$CHART_PATH")" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Create tag | |
| if: steps.version.outputs.skip != 'true' && github.event_name != 'push' | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name 'github-actions[bot]' | |
| git config user.email '41898282+github-actions[bot]@users.noreply.github.com' | |
| if ! git rev-parse -q --verify "refs/tags/$VERSION" >/dev/null; then | |
| git tag -a "$VERSION" -m "$VERSION" | |
| git push origin "refs/tags/$VERSION" | |
| fi | |
| - name: Publish release | |
| if: steps.version.outputs.skip != 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| APP_VERSION: ${{ steps.version.outputs.app_version }} | |
| CHART_VERSION: ${{ steps.version.outputs.chart_version }} | |
| CHART_PATH: ${{ steps.chart.outputs.path }} | |
| CHART_NAME: ${{ steps.chart.outputs.name }} | |
| PRERELEASE: ${{ steps.version.outputs.prerelease }} | |
| LATEST: ${{ steps.version.outputs.latest }} | |
| DRAFT: ${{ steps.version.outputs.draft }} | |
| REPO_URL: ${{ github.server_url }}/${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| if gh release view "$VERSION" >/dev/null 2>&1; then | |
| echo "::error::Release $VERSION already exists" | |
| exit 1 | |
| fi | |
| # Quoted heredoc so the markdown backticks stay literal; the | |
| # placeholders are filled in afterwards. | |
| cat > release-notes.md <<'NOTES' | |
| Pin deployments to this tag instead of tracking `main`: | |
| ```bash | |
| git clone --branch __VERSION__ --depth 1 __REPO_URL__.git | |
| ``` | |
| The attached `__CHART_NAME__` is the packaged Helm chart (chart `__CHART_VERSION__`, appVersion `__APP_VERSION__`) with its Redis and MinIO subcharts vendored, so it installs without adding any chart repositories: | |
| ```bash | |
| helm install codeapi ./__CHART_NAME__ -f my-values.yaml | |
| ``` | |
| Chart configuration is documented in [helm/codeapi/README.md](__REPO_URL__/blob/__VERSION__/helm/codeapi/README.md). | |
| NOTES | |
| sed -i \ | |
| -e "s|__VERSION__|$VERSION|g" \ | |
| -e "s|__REPO_URL__|$REPO_URL|g" \ | |
| -e "s|__CHART_NAME__|$CHART_NAME|g" \ | |
| -e "s|__CHART_VERSION__|$CHART_VERSION|g" \ | |
| -e "s|__APP_VERSION__|$APP_VERSION|g" \ | |
| release-notes.md | |
| # --generate-notes appends the merged-pull-request changelog below | |
| # the body from --notes-file, categorised per .github/release.yml. | |
| gh release create "$VERSION" \ | |
| --title "$VERSION" \ | |
| --notes-file release-notes.md \ | |
| --generate-notes \ | |
| --verify-tag \ | |
| --prerelease="$PRERELEASE" \ | |
| --latest="$LATEST" \ | |
| --draft="$DRAFT" \ | |
| "$CHART_PATH#Helm chart ($CHART_NAME)" |