Skip to content

fix+feat: v0.11.3 — MCP write-tool fixes, CLI help, secret reveal tog… #37

fix+feat: v0.11.3 — MCP write-tool fixes, CLI help, secret reveal tog…

fix+feat: v0.11.3 — MCP write-tool fixes, CLI help, secret reveal tog… #37

Workflow file for this run

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