release #22
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
| name: release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: Release tag to build and publish, for example v0.8.4 | |
| required: true | |
| type: string | |
| concurrency: | |
| group: binary-release | |
| cancel-in-progress: false | |
| jobs: | |
| validate: | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| create_tag: ${{ steps.meta.outputs.create_tag }} | |
| release_sha: ${{ steps.meta.outputs.release_sha }} | |
| tag: ${{ steps.meta.outputs.tag }} | |
| version: ${{ steps.meta.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: 1.3.10 | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9.12.0 | |
| run_install: false | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Typecheck | |
| run: pnpm check:ts | |
| - name: Run test suite | |
| run: pnpm test:ts | |
| - name: Build workspace | |
| run: pnpm build:ts | |
| - name: Validate standalone bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bun build packages/cli/src/index.ts \ | |
| --compile \ | |
| --reject-unresolved \ | |
| --outfile="${RUNNER_TEMP}/polaris-standalone" \ | |
| --metafile="${RUNNER_TEMP}/polaris-metafile.json" | |
| jq -e \ | |
| 'any(.inputs | keys[]; endswith("packages/browser/src/index.tsx"))' \ | |
| "${RUNNER_TEMP}/polaris-metafile.json" >/dev/null | |
| - name: Validate release metadata | |
| id: meta | |
| shell: bash | |
| env: | |
| REQUESTED_TAG: ${{ inputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| root_version="$(node -p "require('./package.json').version")" | |
| cli_version="$(node -p "require('./packages/cli/package.json').version")" | |
| built_version="$(node packages/cli/dist/cli/src/index.js --version)" | |
| if [[ ! "${REQUESTED_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "Release tag must match vMAJOR.MINOR.PATCH." >&2 | |
| exit 1 | |
| fi | |
| if [[ "${root_version}" != "${cli_version}" || "${root_version}" != "${built_version}" ]]; then | |
| echo "Root, CLI package, and built CLI versions must match." >&2 | |
| echo "Root package: ${root_version}" >&2 | |
| echo "CLI package: ${cli_version}" >&2 | |
| echo "Built CLI: ${built_version}" >&2 | |
| exit 1 | |
| fi | |
| if [[ "${REQUESTED_TAG}" != "v${root_version}" ]]; then | |
| echo "Tag ${REQUESTED_TAG} does not match Polaris version v${root_version}." >&2 | |
| exit 1 | |
| fi | |
| create_tag="true" | |
| if git rev-parse -q --verify "refs/tags/${REQUESTED_TAG}" >/dev/null; then | |
| existing_sha="$(git rev-list -n1 "${REQUESTED_TAG}")" | |
| if [[ "${existing_sha}" != "${GITHUB_SHA}" ]]; then | |
| echo "Tag ${REQUESTED_TAG} already exists at ${existing_sha}, not ${GITHUB_SHA}." >&2 | |
| exit 1 | |
| fi | |
| create_tag="false" | |
| fi | |
| echo "create_tag=${create_tag}" >>"${GITHUB_OUTPUT}" | |
| echo "release_sha=${GITHUB_SHA}" >>"${GITHUB_OUTPUT}" | |
| echo "tag=${REQUESTED_TAG}" >>"${GITHUB_OUTPUT}" | |
| echo "version=${root_version}" >>"${GITHUB_OUTPUT}" | |
| release: | |
| needs: validate | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Create release tag | |
| if: needs.validate.outputs.create_tag == 'true' | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_SHA: ${{ needs.validate.outputs.release_sha }} | |
| RELEASE_TAG: ${{ needs.validate.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| gh api "repos/${GITHUB_REPOSITORY}/git/refs" \ | |
| --method POST \ | |
| --field ref="refs/tags/${RELEASE_TAG}" \ | |
| --field sha="${RELEASE_SHA}" | |
| - name: Create or resume mutable GitHub release | |
| id: stage | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_SHA: ${{ needs.validate.outputs.release_sha }} | |
| RELEASE_TAG: ${{ needs.validate.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| releases="$( | |
| gh api --paginate --slurp \ | |
| "repos/${GITHUB_REPOSITORY}/releases?per_page=100" | |
| )" | |
| matches="$( | |
| jq -c --arg tag "${RELEASE_TAG}" \ | |
| '[.[][] | select(.tag_name == $tag)]' <<<"${releases}" | |
| )" | |
| match_count="$(jq 'length' <<<"${matches}")" | |
| if [[ "${match_count}" -gt 1 ]]; then | |
| echo "Multiple GitHub releases use tag ${RELEASE_TAG}." >&2 | |
| exit 1 | |
| fi | |
| if [[ "${match_count}" -eq 0 ]]; then | |
| release="$( | |
| gh api "repos/${GITHUB_REPOSITORY}/releases" \ | |
| --method POST \ | |
| --field tag_name="${RELEASE_TAG}" \ | |
| --field target_commitish="${RELEASE_SHA}" \ | |
| --field name="${RELEASE_TAG}" \ | |
| --field draft=false \ | |
| --field prerelease=false \ | |
| --field generate_release_notes=true | |
| )" | |
| else | |
| release="$(jq -c '.[0]' <<<"${matches}")" | |
| if [[ "$(jq -r '.prerelease' <<<"${release}")" != "false" ]]; then | |
| echo "Release ${RELEASE_TAG} must not be a prerelease." >&2 | |
| exit 1 | |
| fi | |
| if [[ "$(jq -r '.immutable // false' <<<"${release}")" == "true" ]]; then | |
| echo "Release ${RELEASE_TAG} is immutable." >&2 | |
| exit 1 | |
| fi | |
| if [[ "$(jq '.assets | length' <<<"${release}")" -ne 0 ]]; then | |
| echo "Release ${RELEASE_TAG} already has assets; refusing to replace them." >&2 | |
| exit 1 | |
| fi | |
| if [[ "$(jq -r '.draft' <<<"${release}")" == "true" ]]; then | |
| release_id="$(jq -r '.id' <<<"${release}")" | |
| release="$( | |
| gh api "repos/${GITHUB_REPOSITORY}/releases/${release_id}" \ | |
| --method PATCH \ | |
| --field draft=false | |
| )" | |
| fi | |
| fi | |
| release_id="$(jq -r '.id' <<<"${release}")" | |
| if [[ ! "${release_id}" =~ ^[1-9][0-9]*$ ]]; then | |
| echo "Release ${RELEASE_TAG} has an invalid identifier." >&2 | |
| exit 1 | |
| fi | |
| echo "release_id=${release_id}" >>"${GITHUB_OUTPUT}" | |
| echo "attempt_started=true" >>"${GITHUB_OUTPUT}" | |
| - id: release | |
| uses: wevm/incur/release@v1 | |
| with: | |
| entry: packages/cli/src/index.ts | |
| name: polaris | |
| release_tag: ${{ needs.validate.outputs.tag }} | |
| smoke_command: account | |
| - name: Restore draft release after binary failure | |
| if: ${{ (failure() || cancelled()) && steps.stage.outputs.attempt_started == 'true' }} | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_ID: ${{ steps.stage.outputs.release_id }} | |
| RELEASE_TAG: ${{ needs.validate.outputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| release="$(gh api "repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}")" | |
| while IFS= read -r asset_id; do | |
| gh api \ | |
| "repos/${GITHUB_REPOSITORY}/releases/assets/${asset_id}" \ | |
| --method DELETE | |
| done < <(jq -r '.assets[].id' <<<"${release}") | |
| gh api "repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}" \ | |
| --method PATCH \ | |
| --field draft=true >/dev/null | |
| echo "Returned ${RELEASE_TAG} to draft after binary generation failed." >&2 |