📦 Release · Fork Build #107
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
| # ============================================================================ | |
| # 📦 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" |