Files
opencode/.github/workflows/release.yml
Workflow config file is invalid. Please check your config file: SetJob: yaml: unknown anchor 'env' referenced

770 lines
31 KiB
YAML

name: release
run-name: "${{ format('release {0}{1}', inputs.bump, inputs.version) }}"
# Builds are separated from signing and publishing.
# - build-* jobs run repository code (`bun run build`, `bun run pack`) with no environment, secrets or id-token.
# - sign-* and publish-* jobs run in the release environment, check out only .github/release/, and run those
# scripts on verified artifacts, which they treat as data. They never execute repository code.
# - plan decides version, channel and npm dist-tag from the event, inputs and ref; only a dispatch with a bump or
# version on dev reaches latest.
# .github/release/ and this file are the security boundary for releases.
on:
push:
branches: [dev, beta]
workflow_dispatch:
inputs:
bump:
description: "Bump major, minor, or patch"
required: false
type: choice
options: [major, minor, patch]
version:
description: "Override version (optional)"
required: false
type: string
release_notes:
description: "Reviewed V2 release notes for CHANGELOG.md and the GitHub release (required with bump or version)"
required: false
type: string
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ (inputs.bump || inputs.version) && 'release' || 'build' }}
# Beta ships only the newest commit to the team. A cancelled beta run can leave unreferenced files in R2, but only
# publish-desktop records a release, in one request.
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.ref_name == 'beta' }}
permissions:
contents: read
defaults:
run:
shell: bash
jobs:
plan:
if: github.repository == 'anomalyco/opencode'
runs-on: ubuntu-24.04
permissions:
contents: read
outputs:
version: ${{ steps.plan.outputs.version }}
channel: ${{ steps.plan.outputs.channel }}
npm_tag: ${{ steps.plan.outputs.npm_tag }}
dry_run: ${{ steps.plan.outputs.dry_run }}
cli: ${{ steps.plan.outputs.cli }}
desktop: ${{ steps.plan.outputs.desktop }}
node: ${{ steps.plan.outputs.node }}
release: ${{ steps.plan.outputs.release }}
steps:
- &checkout-release
name: Check out release scripts
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: ${{ github.sha }}
sparse-checkout: /.github/release/
sparse-checkout-cone-mode: false
persist-credentials: false
- id: plan
name: Plan
run: bash .github/release/plan-release.sh
env:
GH_TOKEN: ${{ github.token }} # reads the triggering actor's repository role for stable releases
INPUT_BUMP: ${{ inputs.bump }}
INPUT_VERSION: ${{ inputs.version }}
INPUT_RELEASE_NOTES: ${{ inputs.release_notes }}
# A versioned release builds from a commit with its versions and changelog on the dispatched dev commit,
# tagged v<version>. Runs git only.
tag:
needs: plan
runs-on: ubuntu-24.04
# Stable tags are pushed with the release deploy key, the only non-releasers actor the v* tag ruleset admits.
environment: ${{ needs.plan.outputs.channel == 'latest' && needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
env: &env
OPENCODE_VERSION: ${{ needs.plan.outputs.version }}
OPENCODE_CHANNEL: ${{ needs.plan.outputs.channel }}
OPENCODE_NPM_TAG: ${{ needs.plan.outputs.npm_tag }}
OPENCODE_DRY_RUN: ${{ needs.plan.outputs.dry_run }}
outputs:
sha: ${{ steps.tag.outputs.sha || github.sha }}
steps:
- name: Check out
if: needs.plan.outputs.channel == 'latest'
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ssh-key: ${{ secrets.RELEASE_TAG_KEY }}
- id: tag
name: Tag
if: needs.plan.outputs.channel == 'latest'
run: bash .github/release/tag-release.sh
env:
OPENCODE_RELEASE_NOTES: ${{ inputs.release_notes }}
# ---- Build: repository code; no environment, secrets or id-token --------------------------------------------
# Fetches the models.dev catalog once per run, so every CLI bundles the same copy.
models-catalog:
needs: [plan, tag]
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 10
env: *env
steps:
- &checkout
name: Check out
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: ${{ needs.tag.outputs.sha }}
persist-credentials: false
- name: Set up Bun
uses: ./.github/actions/setup-bun
- name: Fetch
run: bun packages/core/script/update-models-snapshot.ts .cache/models-catalog/api.json
- name: Upload
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: models-catalog
path: .cache/models-catalog/api.json
if-no-files-found: error
build-cli:
needs: [plan, tag, models-catalog]
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 30
env: *env
steps:
- *checkout
- name: Set up Bun
uses: ./.github/actions/setup-bun
with:
bun-version: 1.4.4
- &download-models-catalog
name: Download models catalog
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: models-catalog
path: .cache/models-catalog
- name: Build
working-directory: packages/cli
run: bun run build && rm -rf dist/.bun
env:
BUN_COMPILE_RELEASE: bun-v1.4.4
OPENCODE_MODELS_SNAPSHOT: ${{ github.workspace }}/.cache/models-catalog/api.json
OPENCODE_RELEASE_NOTES: ${{ needs.plan.outputs.channel == 'latest' && inputs.release_notes || '' }}
GH_TOKEN: ${{ github.token }} # read-only; raises the rate limit for Bun release downloads
- name: Verify bundled models catalog
run: bun packages/cli/script/catalog-smoke.ts packages/cli/dist/cli-linux-x64/bin/opencode
env:
OPENCODE_MODELS_SNAPSHOT: ${{ github.workspace }}/.cache/models-catalog/api.json
- name: Upload
uses: ./.github/release/upload
with:
name: build-cli
path: packages/cli/dist
build-node:
needs: [plan, tag, models-catalog]
if: needs.plan.outputs.node == 'true'
strategy:
fail-fast: false
matrix:
include:
- { target: linux-arm64, host: blacksmith-4vcpu-ubuntu-2404-arm }
- { target: linux-x64, host: blacksmith-4vcpu-ubuntu-2404 }
- { target: darwin-arm64, host: macos-26 }
- { target: windows-arm64, host: blacksmith-4vcpu-windows-2025 }
- { target: windows-x64, host: blacksmith-4vcpu-windows-2025 }
runs-on: ${{ matrix.host }}
env: *env
steps:
- *checkout
- name: Set up Bun
uses: ./.github/actions/setup-bun
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "26.4.0"
- *download-models-catalog
- name: Build
working-directory: packages/cli
run: bun run build:node
env:
OPENCODE_TARGET: ${{ matrix.target }}
OPENCODE_MODELS_SNAPSHOT: ${{ github.workspace }}/.cache/models-catalog/api.json
- name: Test
if: matrix.target != 'windows-arm64'
working-directory: packages/cli
run: bun run script/service-smoke.ts --node
- name: Verify bundled models catalog
if: matrix.target != 'windows-arm64'
run: bun packages/cli/script/catalog-smoke.ts "packages/cli/dist/node/cli-node-$OPENCODE_TARGET/bin/opencode2-node$EXE"
env:
OPENCODE_TARGET: ${{ matrix.target }}
EXE: ${{ startsWith(matrix.target, 'windows') && '.exe' || '' }}
OPENCODE_MODELS_SNAPSHOT: ${{ github.workspace }}/.cache/models-catalog/api.json
- name: Upload
uses: ./.github/release/upload
with:
name: build-node-${{ matrix.target }}
path: packages/cli/dist/node
# `bun run pack` packs every npm package into dist/npm and the Linux and Node CLI archives into dist/archives.
# The macOS and Windows CLI packages and archives are re-packed after signing.
build-npm:
needs: [plan, tag, build-cli, build-node]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.cli == 'true' }}
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 30
env: *env
steps:
- *checkout
- name: Set up Bun
uses: ./.github/actions/setup-bun
- &download-cli
name: Download
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: build-cli
path: packages/cli/dist
- name: Download Node CLI
if: needs.plan.outputs.node == 'true'
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: build-node-*
path: packages/cli/dist/node
merge-multiple: true
- name: Pack
run: rm packages/cli/dist/SHA256SUMS && bun run pack
- name: Upload
uses: ./.github/release/upload
with:
name: build-npm
path: dist/npm
- name: Upload archives
uses: ./.github/release/upload
with:
name: build-archives
path: dist/archives
build-docker:
needs: [plan, tag, build-cli]
if: needs.plan.outputs.release == 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
env: *env
steps:
- *checkout
- *download-cli
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- name: Set up Bun
uses: ./.github/actions/setup-bun
- name: Pack
working-directory: packages/cli
run: bun run pack:docker
- name: Upload
uses: ./.github/release/upload
with:
name: build-docker
path: dist/docker
# macOS and Windows pack an unsigned app; Linux packs the final AppImage, deb and rpm.
# Dev and beta package macOS x64 on Apple Silicon and Windows arm64 on Blacksmith for speed. electron-builder's
# afterPack rejects a bundle whose CLI or node-pty does not match the target CPU.
build-desktop:
needs: [plan, tag, build-cli]
if: needs.plan.outputs.desktop == 'true'
permissions:
contents: read
pull-requests: read # beta builds read who made each change and what it does for "What's new in beta"
strategy:
fail-fast: false
matrix:
include:
- target: darwin-x64
host: ${{ (needs.plan.outputs.channel == 'dev' || needs.plan.outputs.channel == 'beta') && 'macos-26' || 'macos-26-intel' }}
install: ${{ (needs.plan.outputs.channel == 'dev' || needs.plan.outputs.channel == 'beta') && '--os=darwin --cpu=*' || '--os=darwin --cpu=x64' }}
- { target: darwin-arm64, host: macos-26, install: --os=darwin --cpu=arm64 }
# github-hosted: blacksmith lacks the ARM64 MSVC cross-compilation toolchain
- target: windows-arm64
host: ${{ (needs.plan.outputs.channel == 'dev' || needs.plan.outputs.channel == 'beta') && 'blacksmith-4vcpu-windows-2025' || 'windows-2025' }}
install: --cpu=*
- { target: windows-x64, host: blacksmith-4vcpu-windows-2025 }
- { target: linux-x64, host: blacksmith-4vcpu-ubuntu-2404 }
- { target: linux-arm64, host: blacksmith-4vcpu-ubuntu-2404-arm }
runs-on: ${{ matrix.host }}
env: *env
steps:
- *checkout
- *download-cli
- name: Set up Bun
uses: ./.github/actions/setup-bun
with:
install-flags: ${{ matrix.install }}
- name: Set up rpm
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends rpm
# The beta build embeds the subjects of its newest commits for "What's new in beta"
# (packages/desktop/script/beta-notes.ts). Commits without file contents are enough.
- name: Fetch beta history
if: needs.plan.outputs.channel == 'beta'
run: git fetch --no-tags --filter=blob:none --depth=300 origin "$SHA"
env:
SHA: ${{ needs.tag.outputs.sha }}
# Dev and beta trade larger packages for faster packaging. fpm sets XZ_OPT for the deb and rpm level, but xz
# still reads XZ_DEFAULTS, so it uses every CPU.
- name: Preview packaging settings
if: (needs.plan.outputs.channel == 'dev' || needs.plan.outputs.channel == 'beta') && runner.os == 'Linux'
run: echo "XZ_DEFAULTS=-T0" >> "$GITHUB_ENV"
- name: Build
working-directory: packages/desktop
run: bun run build
env:
OPENCODE_TARGET: ${{ matrix.target }}
NODE_OPTIONS: --max-old-space-size=4096
VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }}
VITE_SENTRY_ENVIRONMENT: ${{ needs.plan.outputs.channel == 'latest' && 'production' || needs.plan.outputs.channel }}
VITE_SENTRY_RELEASE: desktop@${{ needs.plan.outputs.version }}
GITHUB_TOKEN: ${{ github.token }} # read-only; beta notes read pull request authors and descriptions
- name: Pack
working-directory: packages/desktop
timeout-minutes: 60
run: bun run pack
env:
OPENCODE_TARGET: ${{ matrix.target }}
- name: Upload
uses: ./.github/release/upload
with:
name: build-desktop-${{ matrix.target }}
path: dist/desktop
# Packs the NSIS installer around the signed app. This runs repository config, so it is a build job; the
# installer is signed next.
build-desktop-windows-installer:
needs: [plan, tag, sign-windows]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.desktop == 'true' }}
strategy:
fail-fast: false
matrix:
include:
- target: windows-arm64
host: ${{ (needs.plan.outputs.channel == 'dev' || needs.plan.outputs.channel == 'beta') && 'blacksmith-4vcpu-windows-2025' || 'windows-2025' }}
install: --cpu=*
- { target: windows-x64, host: blacksmith-4vcpu-windows-2025 }
runs-on: ${{ matrix.host }}
env: *env
steps:
- *checkout
- name: Set up Bun
uses: ./.github/actions/setup-bun
with:
install-flags: ${{ matrix.install }}
- name: Download
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: sign-desktop-${{ matrix.target }}
path: ${{ runner.temp }}/app
- name: Pack
working-directory: packages/desktop
timeout-minutes: 30
run: bun run pack
env:
OPENCODE_TARGET: ${{ matrix.target }}
OPENCODE_DESKTOP_APP: ${{ runner.temp }}/app/app
- name: Upload
uses: ./.github/release/upload
with:
name: build-desktop-windows-installer-${{ matrix.target }}
path: dist/desktop
# ---- Sign: release environment; only .github/release/ is checked out ----------------------------------------
# Pull requests run sign and publish jobs without an environment, so steps that need a secret skip or dry-run.
# Signs the macOS CLI; signs the desktop apps inside-out, notarizes them in one submission, staples them, and
# only then builds the zip, dmg and .app.tar.gz from the stapled apps.
sign-macos:
needs: [plan, build-npm, build-desktop]
if: ${{ !cancelled() && !failure() && (needs.plan.outputs.cli == 'true' || needs.plan.outputs.desktop == 'true') }}
runs-on: macos-26
timeout-minutes: 90
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
env: *env
outputs:
digest: ${{ steps.upload.outputs.digest }}
steps:
- *checkout-release
- name: Download
uses: ./.github/release/download
with:
pattern: "{build-npm,build-desktop-darwin-*}"
# The certificate has no password, which apple-actions/import-codesign-certs rejects. The sign scripts look up
# the identity in build.keychain, and codesign finds the key through the user search list.
- name: Import certificate
if: needs.plan.outputs.dry_run != 'true'
run: |
cert="$RUNNER_TEMP/cert.p12"
keychain_password=$(openssl rand -base64 32)
printf '%s' "$APPLE_SIGNING_CERTIFICATE" | base64 --decode > "$cert"
security create-keychain -p "$keychain_password" build.keychain
security set-keychain-settings -lut 21600 build.keychain
security unlock-keychain -p "$keychain_password" build.keychain
security import "$cert" -k build.keychain -P "" -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productsign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$keychain_password" build.keychain > /dev/null
security list-keychains -d user -s build.keychain $(security list-keychains -d user | tr -d '"')
rm -f "$cert"
env:
APPLE_SIGNING_CERTIFICATE: ${{ secrets.APPLE_SIGNING_CERTIFICATE }}
- name: Sign CLI
if: needs.plan.outputs.cli == 'true'
run: bash .github/release/sign-cli-macos.sh "$RUNNER_TEMP/in/build-npm" "$RUNNER_TEMP/out"
- name: Sign desktop
if: needs.plan.outputs.desktop == 'true'
run: |
bash .github/release/sign-desktop-macos.sh "$RUNNER_TEMP/out" \
"$RUNNER_TEMP"/in/build-desktop-darwin-*/opencode-desktop-mac-*.app.tar
env:
APPLE_NOTARY_KEY: ${{ secrets.APPLE_NOTARY_KEY }}
APPLE_NOTARY_KEY_ID: ${{ vars.APPLE_NOTARY_KEY_ID }}
APPLE_NOTARY_ISSUER_ID: ${{ vars.APPLE_NOTARY_ISSUER_ID }}
- id: upload
name: Upload
uses: ./.github/release/upload
with:
name: sign-macos
path: ${{ runner.temp }}/out
- name: Delete keychain
if: always() && needs.plan.outputs.dry_run != 'true'
run: |
rm -f "$RUNNER_TEMP/cert.p12"
security delete-keychain build.keychain || true
# Signs the Windows CLI and every executable and native module of the unpacked desktop apps.
sign-windows:
needs: [plan, build-npm, build-desktop]
if: ${{ !cancelled() && !failure() && (needs.plan.outputs.cli == 'true' || needs.plan.outputs.desktop == 'true') }}
runs-on: windows-2025
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
id-token: write
env: *env
outputs:
digest: ${{ steps.upload.outputs.digest }}
steps:
- *checkout-release
- name: Download
uses: ./.github/release/download
with:
pattern: "{build-npm,build-desktop-windows-*}"
- name: Unpack
run: bash .github/release/unpack-windows.sh "$RUNNER_TEMP/in" "$RUNNER_TEMP/work"
- &azure-login
name: Log in to Azure
if: needs.plan.outputs.dry_run != 'true'
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- &azure-sign
name: Sign
if: needs.plan.outputs.dry_run != 'true'
uses: azure/artifact-signing-action@b443cf8ea4124818d2ea9f043cba29fc3ec47b16 # v1.2.0
with:
endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
signing-account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }}
files-folder: ${{ runner.temp }}\work
files-folder-filter: exe,node
files-folder-recurse: true
exclude-environment-credential: true
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-cli-credential: false
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
- name: Repack
run: bash .github/release/repack-windows.sh "$RUNNER_TEMP/work" "$RUNNER_TEMP/out"
- id: upload
name: Upload
if: needs.plan.outputs.cli == 'true'
uses: ./.github/release/upload
with:
name: sign-windows
path: ${{ runner.temp }}/out
- name: Upload arm64 app
if: needs.plan.outputs.desktop == 'true'
uses: ./.github/release/upload
with:
name: sign-desktop-windows-arm64
path: ${{ runner.temp }}/work/desktop-windows-arm64
- name: Upload x64 app
if: needs.plan.outputs.desktop == 'true'
uses: ./.github/release/upload
with:
name: sign-desktop-windows-x64
path: ${{ runner.temp }}/work/desktop-windows-x64
sign-desktop-windows-installer:
needs: [plan, build-desktop-windows-installer]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.desktop == 'true' }}
runs-on: windows-2025
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
id-token: write
env: *env
outputs:
digest: ${{ steps.upload.outputs.digest }}
steps:
- *checkout-release
- name: Download
uses: ./.github/release/download
with:
pattern: build-desktop-windows-installer-*
- name: Unpack
run: mkdir -p "$RUNNER_TEMP/work" && cp "$RUNNER_TEMP"/in/build-desktop-windows-installer-*/*.exe "$RUNNER_TEMP/work/"
- *azure-login
- *azure-sign
- name: Repack
run: bash .github/release/repack-windows.sh "$RUNNER_TEMP/work" "$RUNNER_TEMP/out"
- id: upload
name: Upload
uses: ./.github/release/upload
with:
name: sign-desktop-windows-installer
path: ${{ runner.temp }}/work
# ---- Publish: release environment; only .github/release/ is checked out -------------------------------------
# Destinations, names and channels come from plan and the scripts, never from artifacts.
# Build provenance for the npm tarballs and Linux archives. No secrets; id-token only for Sigstore.
publish-attestations:
needs: [plan, build-npm]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.cli == 'true' && needs.plan.outputs.dry_run != 'true' }}
runs-on: ubuntu-24.04
permissions:
contents: read
id-token: write
attestations: write
steps:
- *checkout-release
- name: Download
uses: ./.github/release/download
with:
pattern: "{build-npm,build-archives}"
- name: Sign
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: |
${{ runner.temp }}/in/build-npm/*.tgz
${{ runner.temp }}/in/build-archives/opencode*
publish-npm:
needs: [plan, sign-macos, sign-windows, publish-attestations]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.cli == 'true' }}
runs-on: ubuntu-24.04
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
id-token: write
attestations: read
env: *env
steps:
- *checkout-release
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "24.11.0"
- &download-signed-cli
name: Download
uses: ./.github/release/download
with:
pattern: "{build-npm,build-archives,sign-macos,sign-windows}"
digests: sign-macos=${{ needs.sign-macos.outputs.digest }} sign-windows=${{ needs.sign-windows.outputs.digest }}
- name: Publish
run: bash .github/release/publish-npm.sh "$RUNNER_TEMP/in/build-npm" "$RUNNER_TEMP/in/sign-macos" "$RUNNER_TEMP/in/sign-windows"
env:
GH_TOKEN: ${{ github.token }}
publish-cli:
needs: [plan, sign-macos, sign-windows, publish-npm]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.cli == 'true' }}
runs-on: ubuntu-24.04
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
id-token: write
env: *env
steps:
- *checkout-release
- *download-signed-cli
- name: Publish
run: bash .github/release/publish-cli.sh "$RUNNER_TEMP/in/build-archives" "$RUNNER_TEMP/in/sign-macos" "$RUNNER_TEMP/in/sign-windows"
env: &cloudflare
CLOUDFLARE_ACCOUNT_ID: 15d29c8639fd3733b1b5486a2acfd968
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
publish-desktop:
needs: [plan, sign-macos, sign-desktop-windows-installer]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.desktop == 'true' }}
runs-on: ubuntu-24.04
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
id-token: write
env: *env
steps:
- *checkout-release
- name: Download
uses: ./.github/release/download
with:
pattern: "{sign-macos,sign-desktop-windows-installer,build-desktop-linux-*}"
digests: sign-macos=${{ needs.sign-macos.outputs.digest }} sign-desktop-windows-installer=${{ needs.sign-desktop-windows-installer.outputs.digest }}
- name: Publish
run: |
bash .github/release/publish-desktop.sh "$RUNNER_TEMP/in/sign-macos" "$RUNNER_TEMP/in/sign-desktop-windows-installer" \
"$RUNNER_TEMP/in/build-desktop-linux-x64" "$RUNNER_TEMP/in/build-desktop-linux-arm64"
env: *cloudflare
publish-docker:
needs: [plan, build-docker]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.release == 'true' }}
runs-on: ubuntu-24.04
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
packages: write
env: *env
steps:
- *checkout-release
- name: Download
uses: ./.github/release/download
with:
pattern: build-docker
- name: Publish
run: bash .github/release/publish-docker.sh "$RUNNER_TEMP/in/build-docker"
env:
GH_TOKEN: ${{ github.token }}
publish-homebrew:
needs: [plan, sign-macos, sign-windows, publish-cli]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.release == 'true' }}
runs-on: ubuntu-24.04
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
id-token: write
env: *env
steps:
- *checkout-release
- *download-signed-cli
- name: Publish
run: bash .github/release/publish-homebrew.sh "$RUNNER_TEMP/in/sign-macos" "$RUNNER_TEMP/in/build-archives"
env:
HOMEBREW_TAP_DEPLOY_KEY: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}
# The AUR package installs the published npm tarballs, so it follows the npm publish.
publish-aur:
needs: [plan, sign-macos, sign-windows, publish-npm]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.release == 'true' }}
runs-on: ubuntu-24.04
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
id-token: write
env: *env
steps:
- *checkout-release
- *download-signed-cli
- name: Publish
run: bash .github/release/publish-aur.sh "$RUNNER_TEMP/in/build-npm"
env:
AUR_KEY: ${{ secrets.AUR_KEY }}
# WinGet manifests as data. Submission to microsoft/winget-pkgs stays paused until the packages are accepted.
publish-winget:
needs: [plan, sign-windows, sign-desktop-windows-installer]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.release == 'true' }}
runs-on: ubuntu-24.04
env: *env
steps:
- *checkout-release
- name: Download
uses: ./.github/release/download
with:
pattern: "{sign-windows,sign-desktop-windows-installer}"
digests: sign-windows=${{ needs.sign-windows.outputs.digest }} sign-desktop-windows-installer=${{ needs.sign-desktop-windows-installer.outputs.digest }}
- name: Render
run: bash .github/release/render-winget.sh "$RUNNER_TEMP/in/sign-windows" "$RUNNER_TEMP/in/sign-desktop-windows-installer" "$RUNNER_TEMP/out"
- name: Upload
uses: ./.github/release/upload
with:
name: publish-winget
path: ${{ runner.temp }}/out
publish-github-release:
needs: [plan, sign-macos, sign-windows, sign-desktop-windows-installer, publish-cli, publish-desktop]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.release == 'true' }}
runs-on: ubuntu-24.04
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: write
env: *env
steps:
- *checkout-release
- name: Download
uses: ./.github/release/download
with:
pattern: "{build-archives,build-desktop-linux-*,sign-macos,sign-windows,sign-desktop-windows-installer}"
digests: >-
sign-macos=${{ needs.sign-macos.outputs.digest }} sign-windows=${{ needs.sign-windows.outputs.digest }}
sign-desktop-windows-installer=${{ needs.sign-desktop-windows-installer.outputs.digest }}
- name: Publish
run: bash .github/release/publish-github-release.sh "$RUNNER_TEMP"/in/*
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
OPENCODE_RELEASE_NOTES: ${{ inputs.release_notes }}
publish-discord:
needs: [plan, publish-github-release]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.release == 'true' }}
runs-on: ubuntu-24.04
environment: ${{ needs.plan.outputs.dry_run != 'true' && 'release' || '' }}
permissions:
contents: read
env: *env
steps:
- *checkout-release
- name: Publish
run: bash .github/release/publish-discord.sh
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
OPENCODE_RELEASE_NOTES: ${{ inputs.release_notes }}
# Brings the release commit back to dev. Pushing to dev is blocked by rulesets, so it opens a PR.
publish-version-pr:
needs: [plan, tag, publish-npm, publish-cli, publish-desktop, publish-github-release]
if: ${{ !cancelled() && !failure() && needs.plan.outputs.channel == 'latest' }}
runs-on: ubuntu-24.04
permissions:
contents: write
pull-requests: write
env: *env
steps:
- *checkout-release
- name: Publish
run: bash .github/release/publish-version-pr.sh "$SHA"
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
SHA: ${{ needs.tag.outputs.sha }}