-
Notifications
You must be signed in to change notification settings - Fork 0
188 lines (170 loc) · 7.81 KB
/
Copy pathutmstack-release.yml
File metadata and controls
188 lines (170 loc) · 7.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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: x64 only. The native ARM64 build cannot start its terminal
# UI (a Bun/OpenTUI FFI limitation that breaks opencode's own
# windows-arm64 binary identically), so we do not ship one — on ARM64
# the installer uses the x64-baseline build under Windows emulation.
# baseline (no-AVX2) is required so the x64-on-ARM emulator can run it.
- { runner: windows-2022, target: windows-x64, ext: zip, baseline: true }
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.13
- 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,
# which requires Bun to fetch extra cross-compilation artifacts. That
# download is known to be flaky ("Failed to extract executable for
# bun-<target>-baseline"), so retry before giving up.
build() {
if [ "${{ matrix.baseline }}" = "true" ]; then
bun run script/build.ts --single --baseline
else
bun run script/build.ts --single
fi
}
ok=0
for attempt in 1 2 3; do
echo "build attempt $attempt"
if build; then ok=1; break; fi
echo "attempt $attempt failed; retrying in 15s"
sleep 15
done
if [ "$ok" != "1" ] && [ "${{ matrix.baseline }}" = "true" ]; then
# A flaky baseline artifact must not cost us the primary binary for
# this platform. Fall back to the native build alone; the installer
# only requests -baseline on non-AVX2 x64 hardware, and the native
# asset still serves everyone else.
echo "baseline builds failed 3x — falling back to the native build only"
bun run script/build.ts --single && ok=1
fi
[ "$ok" = "1" ] || { echo "build failed after retries"; exit 1; }
ls -la dist/
- name: Verify the binary is branded and self-contained
shell: bash
working-directory: packages/opencode
run: |
set -eu
# Address the exact matrix target. `find | head -1` picked whichever
# directory sorted first, which on x64 runs was the musl variant —
# a musl-linked binary cannot execute on a glibc runner (missing ELF
# interpreter, which surfaces as "No such file or directory" / 127).
DIST="dist/opencode-${{ matrix.target }}"
BIN="$DIST/bin/utmstack"
[ -f "$BIN" ] || BIN="$DIST/bin/utmstack.exe"
[ -f "$BIN" ] || { echo "no binary at $DIST/bin"; ls -R dist | head -40; 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
# The build emits musl variants for free, but they are untested and
# unadvertised, and the installer refuses musl outright. Publishing
# them would promise support we have not verified.
case "$name" in *-musl*) echo "skipping $name (musl, not published)"; continue ;; esac
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