-
Notifications
You must be signed in to change notification settings - Fork 1
215 lines (195 loc) · 8.98 KB
/
Copy pathrelease.yml
File metadata and controls
215 lines (195 loc) · 8.98 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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
name: Release
# Triggered by pushing a `v*` tag (e.g. `v0.1.8`). Builds every cross-compile
# target Bun supports, generates SHA-256 sidecars, attaches them all to a
# fresh GitHub release. The install script (`deploy/install.sh`) consumes
# these assets by name — keep this list in sync with the script.
#
# Manual trigger via `workflow_dispatch` is also wired so you can rebuild
# release assets without re-tagging (useful when binaries get clobbered).
on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
tag:
description: "Tag to (re)release (e.g. v0.1.8). Must already exist."
required: true
permissions:
contents: write # publish releases + upload assets
id-token: write # cosign keyless signing via Sigstore OIDC
jobs:
build:
name: Build cross-compile targets
runs-on: ubuntu-latest
timeout-minutes: 25
outputs:
tag: ${{ steps.resolve-tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
- id: resolve-tag
name: Resolve tag
run: |
TAG="${{ github.event.inputs.tag || github.ref_name }}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "Building release ${TAG}"
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install root dependencies
run: bun install --frozen-lockfile
- name: Install admin dependencies
# Admin has its own package.json + bun.lock — `cd admin && bun run build`
# would otherwise hit "module not found: zustand / @types/react" in CI
# because the root install does not descend into the admin subproject.
working-directory: admin
run: bun install --frozen-lockfile
- name: Build admin SPA (embedded in binaries via Bun macro)
# Vite + Rollup peak heap is ~2.5 GB while bundling Monaco. Default
# Node old-space is 2 GB, so the runner OOMs without this bump.
env:
NODE_OPTIONS: "--max-old-space-size=6144"
run: bun run build:admin
- name: Verify admin build output
# Fail loudly if admin/dist is missing or empty — the Bun macro embeds
# this content into every binary. An empty dist silently produces
# binaries that serve "Admin UI not built" at /_/.
run: |
set -eu
if [ ! -f admin/dist/index.html ]; then
echo "::error::admin/dist/index.html missing — admin build did not produce output"
ls -la admin/dist/ 2>/dev/null || echo "(admin/dist does not exist)"
exit 1
fi
ASSET_COUNT=$(find admin/dist/assets -type f 2>/dev/null | wc -l)
if [ "$ASSET_COUNT" -lt 1 ]; then
echo "::error::admin/dist/assets has no files"
exit 1
fi
echo "admin/dist OK — $(du -sh admin/dist | cut -f1), $ASSET_COUNT asset files"
- name: Cross-compile all targets
run: |
mkdir -p releases
# Bun supports cross-compile from any host — no Docker / cross-toolchain.
bun run build:linux-x64
bun run build:linux-arm64
bun run build:linux-x64-musl
bun run build:macos-x64
bun run build:macos-arm64
bun run build:windows-x64
- name: Verify admin assets are embedded in each binary
# The Bun macro inlines `admin/dist/*` as base64-encoded gzip blobs;
# the asset *keys* (filenames) survive as literal strings in the
# bundle. `strings(1)` is unreliable here (Bun's `--compile` writes
# the data section in a layout strings doesn't sequence well), so
# use `grep -a` which treats the binary as text. We expect ≥3 hits
# for `index-`-prefixed chunks and ≥1 each for the high-signal
# vendor chunks (primereact, monaco, react-vendor).
run: |
set -eu
for f in releases/vaultbase-linux-x64 releases/vaultbase-linux-arm64 \
releases/vaultbase-linux-x64-musl releases/vaultbase-macos-x64 \
releases/vaultbase-macos-arm64 releases/vaultbase-windows-x64.exe; do
asset_hits=$(grep -ac 'index-' "$f" || true)
primereact_hits=$(grep -ac 'primereact' "$f" || true)
monaco_hits=$(grep -ac 'monaco' "$f" || true)
echo " $f → index- $asset_hits / primereact $primereact_hits / monaco $monaco_hits"
if [ "$asset_hits" -lt 3 ] || [ "$primereact_hits" -lt 1 ] || [ "$monaco_hits" -lt 1 ]; then
echo "::error::$f does not appear to have the admin SPA embedded"
exit 1
fi
done
- name: Generate SHA-256 sidecars
run: |
cd releases
for f in vaultbase-*; do
# Skip already-generated .sha256 files (rerun safety).
case "$f" in *.sha256) continue ;; esac
sha256sum "$f" | awk -v name="$f" '{ print $1 " " name }' > "${f}.sha256"
done
ls -la
- name: Generate combined SHA256SUMS
run: |
cd releases
# One file with all hashes — easier to verify with a single download:
# curl -O .../SHA256SUMS && sha256sum -c SHA256SUMS
cat vaultbase-*.sha256 \
| grep -v '^$' \
> SHA256SUMS
cat SHA256SUMS
# ── SBOM via syft ────────────────────────────────────────────────────
# CycloneDX JSON next to each binary lets downstream consumers know what
# was bundled — drop into trivy / grype / dependency-track to track
# advisories per release.
- name: Install syft
uses: anchore/sbom-action/download-syft@v0.20.0
- name: Generate SBOMs (CycloneDX JSON per binary)
run: |
cd releases
for f in vaultbase-linux-x64 vaultbase-linux-arm64 vaultbase-linux-x64-musl \
vaultbase-macos-x64 vaultbase-macos-arm64 vaultbase-windows-x64.exe; do
echo "→ SBOM for $f"
syft "$f" -o cyclonedx-json > "${f}.cdx.json"
done
# One SBOM for the source tree too (NPM dependency graph + lockfile).
syft dir:. -o cyclonedx-json > vaultbase-source.cdx.json
ls -la *.cdx.json
# ── Cosign keyless sigs ─────────────────────────────────────────────
# Uses the GitHub Actions OIDC token via Sigstore Fulcio — no static
# signing key to manage. Verifiers check the cert chain + transparency
# log entry. install.sh's --verify-sig flag consumes these.
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Sign release artifacts (keyless)
run: |
cd releases
for f in vaultbase-linux-x64 vaultbase-linux-arm64 vaultbase-linux-x64-musl \
vaultbase-macos-x64 vaultbase-macos-arm64 vaultbase-windows-x64.exe \
SHA256SUMS; do
echo "→ signing $f"
cosign sign-blob --yes \
--output-signature="${f}.sig" \
--output-certificate="${f}.pem" \
"$f"
done
ls -la *.sig *.pem
- name: Upload release assets
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.resolve-tag.outputs.tag }}
name: ${{ steps.resolve-tag.outputs.tag }}
generate_release_notes: true
fail_on_unmatched_files: true
files: |
releases/vaultbase-linux-x64
releases/vaultbase-linux-x64.sha256
releases/vaultbase-linux-x64.sig
releases/vaultbase-linux-x64.pem
releases/vaultbase-linux-x64.cdx.json
releases/vaultbase-linux-arm64
releases/vaultbase-linux-arm64.sha256
releases/vaultbase-linux-arm64.sig
releases/vaultbase-linux-arm64.pem
releases/vaultbase-linux-arm64.cdx.json
releases/vaultbase-linux-x64-musl
releases/vaultbase-linux-x64-musl.sha256
releases/vaultbase-linux-x64-musl.sig
releases/vaultbase-linux-x64-musl.pem
releases/vaultbase-linux-x64-musl.cdx.json
releases/vaultbase-macos-x64
releases/vaultbase-macos-x64.sha256
releases/vaultbase-macos-x64.sig
releases/vaultbase-macos-x64.pem
releases/vaultbase-macos-x64.cdx.json
releases/vaultbase-macos-arm64
releases/vaultbase-macos-arm64.sha256
releases/vaultbase-macos-arm64.sig
releases/vaultbase-macos-arm64.pem
releases/vaultbase-macos-arm64.cdx.json
releases/vaultbase-windows-x64.exe
releases/vaultbase-windows-x64.exe.sig
releases/vaultbase-windows-x64.exe.pem
releases/vaultbase-windows-x64.exe.cdx.json
releases/SHA256SUMS
releases/SHA256SUMS.sig
releases/SHA256SUMS.pem
releases/vaultbase-source.cdx.json