build: add reproducible release bundles
This commit is contained in:
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/bin/sh
|
||||
# Build an inspectable, reproducible RVBox Linux-server/Windows-client bundle.
|
||||
# Publishing and certificate custody remain outside this repository; an optional
|
||||
# local signing hook can sign the Windows executable before checksums are made.
|
||||
set -eu
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
compose_file=$repo_root/deploy/compose.yaml
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
usage: scripts/release build --version VERSION [--output DIR] [--sign-windows-hook FILE]
|
||||
|
||||
Builds a fresh immutable bundle containing:
|
||||
rvbox-server-linux-amd64
|
||||
rvc-linux-amd64
|
||||
rvbox-windows-amd64.exe
|
||||
SHA256SUMS
|
||||
manifest.json
|
||||
|
||||
The default output is dist/rvbox-VERSION. VERSION must be a safe release label
|
||||
([0-9A-Za-z][0-9A-Za-z._+-]{0,63}); an existing final output is never replaced.
|
||||
Artifacts are built inside the pinned Docker toolchain with that exact version
|
||||
embedded in `--version`. The optional signing hook is a regular executable run
|
||||
on the host as: HOOK WINDOWS_EXE VERSION. It receives RVBOX_ARTIFACT and
|
||||
RVBOX_VERSION as environment variables and must sign the supplied executable
|
||||
in place. SHA256SUMS and manifest.json are generated only after it succeeds.
|
||||
|
||||
No signing hook means the manifest explicitly marks the Windows artifact as
|
||||
unsigned. This is deliberate for CI/test builds; do not publish it as signed.
|
||||
EOF
|
||||
}
|
||||
|
||||
fail() { printf '%s\n' "release: $*" >&2; exit 2; }
|
||||
|
||||
command=${1:-}
|
||||
[ "$#" -gt 0 ] && shift
|
||||
[ "$command" = build ] || { usage >&2; fail "expected build"; }
|
||||
|
||||
version=
|
||||
output=
|
||||
sign_hook=
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case $1 in
|
||||
--version) [ "$#" -ge 2 ] || fail "--version needs a value"; version=$2; shift 2 ;;
|
||||
--output) [ "$#" -ge 2 ] || fail "--output needs a directory"; output=$2; shift 2 ;;
|
||||
--sign-windows-hook) [ "$#" -ge 2 ] || fail "--sign-windows-hook needs an executable file"; sign_hook=$2; shift 2 ;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
*) fail "unknown argument $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case $version in
|
||||
''|[!0-9A-Za-z]*|*[!0-9A-Za-z._+-]*|?????????????????????????????????????????????????????????????????*)
|
||||
fail "--version must match [0-9A-Za-z][0-9A-Za-z._+-]{0,63}"
|
||||
;;
|
||||
esac
|
||||
if [ -z "$output" ]; then output=$repo_root/dist/rvbox-$version; fi
|
||||
case $output in
|
||||
/*) ;;
|
||||
*) output=$repo_root/$output ;;
|
||||
esac
|
||||
parent=$(dirname -- "$output")
|
||||
[ ! -e "$output" ] || fail "refusing to replace existing output $output"
|
||||
[ -d "$parent" ] || mkdir -p "$parent"
|
||||
[ ! -L "$parent" ] || fail "refusing symlink output parent $parent"
|
||||
if [ -n "$sign_hook" ]; then
|
||||
[ -f "$sign_hook" ] && [ ! -L "$sign_hook" ] && [ -x "$sign_hook" ] || fail "signing hook must be an executable regular file"
|
||||
fi
|
||||
|
||||
docker version >/dev/null 2>&1 || fail "Docker is unavailable"
|
||||
docker compose -f "$compose_file" version >/dev/null 2>&1 || fail "Docker Compose is unavailable"
|
||||
|
||||
partial=$parent/.rvbox-$version.partial-$$
|
||||
mkdir "$partial" || fail "could not create private release staging directory"
|
||||
cleanup() { rm -rf -- "$partial"; }
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
commit=$(git -C "$repo_root" rev-parse HEAD)
|
||||
dirty=$(git -C "$repo_root" status --porcelain)
|
||||
[ -z "$dirty" ] || fail "refusing release build from a dirty worktree"
|
||||
|
||||
docker compose -f "$compose_file" run --rm toolchain sh -ec '
|
||||
set -eu
|
||||
version=$1
|
||||
out=$2
|
||||
flags="-s -w -X main.buildVersion=$version"
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "$flags" -o "$out/rvbox-server-linux-amd64" ./cmd/rvbox-server
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "$flags" -o "$out/rvc-linux-amd64" ./cmd/rvc
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags "-H=windowsgui $flags" -o "$out/rvbox-windows-amd64.exe" ./cmd/rvbox
|
||||
' sh "$version" "$partial"
|
||||
|
||||
signed=false
|
||||
if [ -n "$sign_hook" ]; then
|
||||
RVBOX_ARTIFACT=$partial/rvbox-windows-amd64.exe RVBOX_VERSION=$version "$sign_hook" "$partial/rvbox-windows-amd64.exe" "$version"
|
||||
signed=true
|
||||
fi
|
||||
|
||||
for artifact in rvbox-server-linux-amd64 rvc-linux-amd64 rvbox-windows-amd64.exe; do
|
||||
[ -f "$partial/$artifact" ] && [ ! -L "$partial/$artifact" ] || fail "builder did not create regular $artifact"
|
||||
done
|
||||
(cd "$partial" && sha256sum rvbox-server-linux-amd64 rvc-linux-amd64 rvbox-windows-amd64.exe) >"$partial/SHA256SUMS"
|
||||
{
|
||||
printf '{\n'
|
||||
printf ' "schema": 1,\n'
|
||||
printf ' "version": "%s",\n' "$version"
|
||||
printf ' "git_commit": "%s",\n' "$commit"
|
||||
printf ' "windows_signed": %s,\n' "$signed"
|
||||
printf ' "artifacts": "SHA256SUMS"\n'
|
||||
printf '}\n'
|
||||
} >"$partial/manifest.json"
|
||||
chmod 0755 "$partial/rvbox-server-linux-amd64" "$partial/rvc-linux-amd64" "$partial/rvbox-windows-amd64.exe"
|
||||
chmod 0644 "$partial/SHA256SUMS" "$partial/manifest.json"
|
||||
mv -- "$partial" "$output"
|
||||
trap - EXIT HUP INT TERM
|
||||
printf 'release_bundle=%s\n' "$output"
|
||||
Reference in New Issue
Block a user