Document the unsigned CLI binary #1
Workflow file for this run
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: utmstack-release | |
| # Builds the branded `utmstack` binaries for every platform the installers can | |
| # request, and publishes them with a checksums.txt the installers verify. | |
| # | |
| # The installer downloads "$APP-$target" (e.g. utmstack-linux-x64.tar.gz), so a | |
| # target missing here is a 404 for those users. The -baseline variants matter: | |
| # without them, any x64 machine lacking AVX2 — which includes a lot of cloud | |
| # VMs — cannot install. | |
| on: | |
| push: | |
| tags: ["v*"] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version to build (without the leading v)" | |
| required: true | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: ${{ matrix.target }} | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # macOS. Built natively so PyInstaller/Bun ad-hoc signing applies — | |
| # an unsigned arm64 binary will not execute on Apple Silicon. | |
| - { runner: macos-14, target: darwin-arm64, ext: zip } | |
| - { runner: macos-15-intel, target: darwin-x64, ext: zip, baseline: true } | |
| # Linux. x64 builds on 22.04 (glibc 2.35) so it runs on Ubuntu 22.04+ | |
| # and RHEL 9+. arm64 has no 22.04 ARM runner we can rely on, so it | |
| # builds on 24.04 (glibc 2.39) and needs Ubuntu 24.04+ / Debian 13+. | |
| - { runner: ubuntu-22.04, target: linux-x64, ext: tar.gz, baseline: true } | |
| - { runner: ubuntu-24.04-arm, target: linux-arm64, ext: tar.gz } | |
| # Windows, both architectures. | |
| - { runner: windows-2022, target: windows-x64, ext: zip, baseline: true } | |
| - { runner: windows-11-arm, target: windows-arm64, ext: zip } | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: 1.3.14 | |
| - name: Resolve version | |
| id: version | |
| shell: bash | |
| run: | | |
| if [ -n "${{ github.event.inputs.version }}" ]; then | |
| echo "value=${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "value=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Install dependencies | |
| run: bun install | |
| - name: Build | |
| working-directory: packages/opencode | |
| shell: bash | |
| env: | |
| OPENCODE_VERSION: ${{ steps.version.outputs.value }} | |
| run: | | |
| # --baseline additionally emits the no-AVX2 variant for x64 targets. | |
| if [ "${{ matrix.baseline }}" = "true" ]; then | |
| bun run script/build.ts --single --baseline | |
| else | |
| bun run script/build.ts --single | |
| fi | |
| ls -la dist/ | |
| - name: Verify the binary is branded and self-contained | |
| shell: bash | |
| working-directory: packages/opencode | |
| run: | | |
| set -eu | |
| BIN=$(find dist -type f \( -name utmstack -o -name utmstack.exe \) | head -1) | |
| [ -n "$BIN" ] || { echo "no utmstack binary produced"; exit 1; } | |
| echo "binary: $BIN" | |
| got=$("$BIN" --version | tr -d '\r') | |
| [ "$got" = "${{ steps.version.outputs.value }}" ] || { | |
| echo "version mismatch: got '$got'"; exit 1; } | |
| "$BIN" --help 2>&1 | grep -q "utmstack completion" || { | |
| echo "help output is not branded"; exit 1; } | |
| # First run must seed config and extract the embedded skills/agents. | |
| TMPCFG=$(mktemp -d) | |
| XDG_CONFIG_HOME="$TMPCFG" APPDATA="$TMPCFG" "$BIN" --help >/dev/null 2>&1 || true | |
| XDG_CONFIG_HOME="$TMPCFG" APPDATA="$TMPCFG" "$BIN" mcp list >/dev/null 2>&1 || true | |
| test -f "$TMPCFG/utmstack/utmstack.json" || { | |
| echo "default config was not seeded"; exit 1; } | |
| skills=$(ls "$TMPCFG/utmstack/skills" 2>/dev/null | wc -l | tr -d ' ') | |
| agents=$(ls "$TMPCFG/utmstack/agent"/*.md 2>/dev/null | wc -l | tr -d ' ') | |
| echo "seeded config, $skills skills, $agents agents" | |
| [ "$skills" -gt 0 ] || { echo "no skills extracted"; exit 1; } | |
| [ "$agents" -gt 0 ] || { echo "no agents extracted"; exit 1; } | |
| grep -q '"utmstack-mcp"' "$TMPCFG/utmstack/utmstack.json" || { | |
| echo "default config does not reference utmstack-mcp"; exit 1; } | |
| - name: Package | |
| shell: bash | |
| working-directory: packages/opencode | |
| run: | | |
| set -eu | |
| # Absolute output dir. A relative "../.." from inside dist/<name>/bin | |
| # is five levels from the repo root and is trivially miscounted. | |
| OUT="$GITHUB_WORKSPACE/out" | |
| mkdir -p "$OUT" | |
| for dir in dist/*/; do | |
| name=$(basename "$dir") | |
| [ -d "$dir/bin" ] || continue | |
| asset=$(echo "$name" | sed 's/^opencode-/utmstack-/') | |
| if [ "${{ matrix.ext }}" = "tar.gz" ]; then | |
| tar -czf "$OUT/${asset}.tar.gz" -C "$dir/bin" . | |
| else | |
| # COPYFILE_DISABLE keeps macOS AppleDouble "._*" files out of the | |
| # zip; -X drops extended attributes. Both are no-ops on Windows. | |
| (cd "$dir/bin" && env COPYFILE_DISABLE=1 7z a -tzip "$OUT/${asset}.zip" . >/dev/null) | |
| fi | |
| done | |
| ls -lh "$OUT" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.target }} | |
| path: out/* | |
| release: | |
| needs: build | |
| runs-on: ubuntu-22.04 | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: { path: artifacts } | |
| - name: Collect and checksum | |
| run: | | |
| mkdir -p out | |
| find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' \) -exec cp {} out/ \; | |
| cd out && sha256sum * > checksums.txt | |
| echo "Publishing:"; cat checksums.txt | |
| - uses: softprops/action-gh-release@v2 | |
| with: | |
| files: out/* | |
| generate_release_notes: true |