Skip to content

📦 Release · Fork Build #109

📦 Release · Fork Build

📦 Release · Fork Build #109

Workflow file for this run

# ============================================================================
# 📦 Release · Fork Build
# ----------------------------------------------------------------------------
# Purpose : Build opencode CLI binaries (linux / macos / windows) for the
# LeXwDeX/opencode fork. Stripped-down version of upstream
# publish.yml — removes repo guard, Blacksmith runners, code
# signing, npm publish, and Tauri desktop.
# Trigger : Manual workflow_dispatch (PRIMARY) — pick main or dev
# Push to main/dev ONLY registers the workflow in the Actions UI
# (the build job is gated on `github.event_name == 'workflow_dispatch'`)
# Version : Independent GraphAgent SemVer derived only from graphagent-v* tags.
# dev builds the next stable version as X.Y.Z-dev.N; main publishes
# X.Y.Z and marks it Latest. OpenCode package versions are ignored.
# Inputs : create_release — when true, create a GitHub Release with the
# built binaries + SHA256SUMS attached. Builds
# from `dev` are marked prerelease (test build);
# `main` builds are formal and become Latest.
# platforms — comma-separated subset of linux,macos,windows
# to build. Leave blank to build all three.
# Jobs : package-templates — package latest reference templates from the
# opencode-dag-config repo into a release asset
# build-cli — 3-OS matrix, bundles ripgrep + single-file binary
# release — optional, creates GitHub Release from build-cli artifacts
# register — no-op for push triggers, just registers in Actions UI
# Output : Artifacts: opencode-{linux,macos,windows}.{tar.gz,zip} +
# dag-templates.tar.gz + SHA256SUMS
# ============================================================================
name: 📦 Release · Fork Build
on:
# Manual trigger — the primary way to build releases
workflow_dispatch:
inputs:
create_release:
description: "Create a GitHub Release with the built binaries"
required: false
type: boolean
default: true
platforms:
description: "Platforms to build, comma-separated (linux,macos,windows). Leave blank for all."
required: false
type: string
# Also run on push to main/dev so the workflow registers in Actions UI
push:
branches:
- main
- dev
paths:
- ".github/workflows/release-fork.yml"
permissions:
contents: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: false
concurrency:
group: release-fork
cancel-in-progress: false
jobs:
version:
name: Resolve GraphAgent Version
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
outputs:
channel: ${{ steps.release-version.outputs.channel }}
version: ${{ steps.release-version.outputs.version }}
tag: ${{ steps.release-version.outputs.tag }}
prerelease: ${{ steps.release-version.outputs.prerelease }}
latest: ${{ steps.release-version.outputs.latest }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
save-cache: false
- name: Resolve Version
id: release-version
run: bun run ./packages/opencode/script/release-version.ts
# Package the latest reference templates from the dedicated config repo
# (LeXwDeX/opencode-dag-config) into a release asset. dev/main do not manage
# these templates anymore — the config repo is the single source of truth.
# Read-only: no commits, no pushes, so branch protection never blocks it.
#
# Validate-before-package: the releasing runtime commit runs its directory
# validator against the config repo HEAD BEFORE any copy/package step. Any
# invalid template — or an unavailable validator — fails the job (fail
# closed), so an unchecked archive can never be uploaded or embedded.
package-templates:
name: Package Reference Templates
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Runtime (releasing commit)
uses: actions/checkout@v4
- name: Clone Config Repo
uses: actions/checkout@v4
with:
repository: LeXwDeX/opencode-dag-config
path: dag-config
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
save-cache: false
- name: Install Runtime Dependencies
run: bun install --frozen-lockfile
- name: Validate and Package Templates (fail closed)
working-directory: packages/opencode
run: |
echo "Packaging config commit $(git -C "$GITHUB_WORKSPACE/dag-config" rev-parse HEAD) with runtime commit $(git rev-parse HEAD)"
bun run script/package-dag-templates.ts "$GITHUB_WORKSPACE/dag-config" "$GITHUB_WORKSPACE/dag-templates.tar.gz"
- name: Upload Templates Artifact
uses: actions/upload-artifact@v4
with:
name: dag-templates
path: dag-templates.tar.gz
retention-days: 7
build-cli:
name: Build CLI (${{ matrix.name }})
needs: [version, package-templates]
# Skip the actual build when triggered by push (registration-only). Per-
# matrix-entry platform filtering can't live here — `matrix` isn't in
# scope for a job-level `if:` — so it's applied to each step below instead.
if: github.event_name == 'workflow_dispatch'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
name: linux
- os: macos-latest
name: macos
- os: windows-latest
name: windows
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- name: Checkout Repository
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Bun
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
uses: ./.github/actions/setup-bun
with:
# Linux and Windows bun caches are each owned by a single saver
# elsewhere (ci-typecheck.yml linux, ci-test.yml e2e windows) to
# avoid racing on the same {OS}-bun-{hash} key when this manual
# release build runs concurrently with a push-triggered CI run.
# macOS has no other job in the repo, so it's safe to save here.
save-cache: ${{ matrix.name == 'macos' }}
# Embed a models.dev catalog snapshot into the binary so air-gapped
# users can start opencode without reaching https://models.dev at
# runtime (see packages/opencode/script/generate.ts fallback order).
- name: Fetch models.dev Snapshot
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
run: |
snapshot_path="$RUNNER_TEMP/models-dev-api.json"
if command -v cygpath &>/dev/null; then
snapshot_path="$(cygpath -m "$snapshot_path")"
fi
for attempt in 1 2 3; do
if curl -fsSL --max-time 30 https://models.dev/api.json -o "$snapshot_path"; then
echo "models.dev snapshot downloaded (attempt $attempt)"
echo "MODELS_DEV_API_JSON=$snapshot_path" >> "$GITHUB_ENV"
exit 0
fi
echo "models.dev download failed (attempt $attempt), retrying..."
sleep 5
done
echo "::warning::Failed to download models.dev api.json; build will fall back to @opencode-ai/models snapshot or an empty catalog"
# Embed the latest DAG reference templates into the binary so air-gapped
# installs ship the curated workflows (see dag/workflows.ts builtin scope
# and script/generate.ts DAG_TEMPLATES_DIR loading).
- name: Download Templates Artifact
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
uses: actions/download-artifact@v4
with:
name: dag-templates
path: dag-templates-artifact
- name: Extract Templates
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
run: |
mkdir -p dag-templates-src
tar -xzf dag-templates-artifact/dag-templates.tar.gz -C dag-templates-src
templates_dir="$GITHUB_WORKSPACE/dag-templates-src"
if command -v cygpath &>/dev/null; then
templates_dir="$(cygpath -m "$templates_dir")"
fi
echo "DAG_TEMPLATES_DIR=$templates_dir" >> "$GITHUB_ENV"
- name: Build CLI
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
run: ./packages/opencode/script/build.ts --single --skip-install
env:
OPENCODE_CHANNEL: ${{ needs.version.outputs.channel == 'main' && 'latest' || 'dev' }}
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
# Bundle ripgrep into each dist/opencode-*/bin so air-gapped users do not
# hit the runtime download in packages/core/src/ripgrep/binary.ts.
- name: Prefetch Ripgrep
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
run: bun run ./packages/opencode/script/prefetch-ripgrep.ts
- name: Package Artifacts
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
working-directory: packages/opencode/dist
run: |
for dir in opencode-*/; do
base="${dir%/}"
if [[ "$base" == *linux* ]]; then
bun run ../script/package-cli-artifact.ts "$base" "${base}.tar.gz"
else
bun run ../script/package-cli-artifact.ts "$base" "${base}.zip"
fi
done
- name: Upload Artifacts
if: inputs.platforms == '' || contains(inputs.platforms, matrix.name)
uses: actions/upload-artifact@v4
with:
name: opencode-${{ matrix.name }}
path: |
packages/opencode/dist/*.tar.gz
packages/opencode/dist/*.zip
release:
name: Create GitHub Release
needs: [version, build-cli, package-templates]
if: inputs.create_release
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
path: release-assets
merge-multiple: true
- name: Generate SHA256SUMS
working-directory: release-assets
run: |
shasum -a 256 * > SHA256SUMS || sha256sum * > SHA256SUMS
echo "--- SHA256SUMS ---"
cat SHA256SUMS
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
# dev releases are prereleases and can never become Latest. main
# releases are formal and are explicitly promoted to Latest.
run: |
EXTRA_FLAGS=()
if [ "${{ needs.version.outputs.channel }}" = "main" ]; then
EXTRA_FLAGS+=(--latest)
else
EXTRA_FLAGS+=(--prerelease --latest=false)
fi
gh release create "${{ needs.version.outputs.tag }}" \
--title "OpenCode GraphAgent v${{ needs.version.outputs.version }}" \
--notes "GraphAgent release from branch ${{ github.ref_name }}" \
--target "${{ github.sha }}" \
"${EXTRA_FLAGS[@]}" \
release-assets/*
# No-op job for push-triggered runs — just registers the workflow in Actions UI
register:
name: Register Workflow
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- name: Register Workflow
run: echo "Workflow registered in Actions UI"