Files

144 lines
6.3 KiB
Bash
Executable File

#!/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 --bootstrap-server-url WSS_URL [--output DIR] [--sign-windows-hook FILE]
Builds a fresh immutable bundle containing:
rvbox-server-linux-amd64
rvbox-server-linux-arm64
rvc-linux-amd64
rvc-linux-arm64
rvbox-windows-amd64.exe
SHA256SUMS
manifest.json
The default output is dist/rvbox-VERSION. --output must remain below this
repository's ignored dist/ tree. VERSION must be a safe release label
([0-9A-Za-z][0-9A-Za-z._+-]{0,63}); an existing final output is never replaced.
WSS_URL is the deliberate first-install endpoint embedded only in the Windows
installer; it must be an absolute `wss://` URL and is never inferred from a
test fixture. 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=
bootstrap_server_url=
while [ "$#" -gt 0 ]; do
case $1 in
--version) [ "$#" -ge 2 ] || fail "--version needs a value"; version=$2; shift 2 ;;
--bootstrap-server-url) [ "$#" -ge 2 ] || fail "--bootstrap-server-url needs a value"; bootstrap_server_url=$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
case $bootstrap_server_url in
wss://*) ;;
*) fail "--bootstrap-server-url must be an absolute wss:// URL" ;;
esac
case $bootstrap_server_url in
*[!A-Za-z0-9:/._-]*) fail "--bootstrap-server-url contains unsupported characters" ;;
esac
if [ -z "$output" ]; then output=$repo_root/dist/rvbox-$version; fi
case $output in
/*) ;;
*) output=$repo_root/$output ;;
esac
case $output in
"$repo_root"/dist/*) ;;
*) fail "--output must be below $repo_root/dist" ;;
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
container_partial=/workspace/${partial#"$repo_root"/}
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"
windows_flags="$flags -X main.bootstrapServerURL=$3"
resource=/workspace/cmd/rvbox/rvbox-release_windows_amd64.syso
icon="$out/.rvbox-release-icon.png"
trap "rm -f -- \"$resource\" \"$icon\"" EXIT HUP INT TERM
go run ./tools/releaseicon --out "$icon"
go-winres simply --arch amd64 --out /workspace/cmd/rvbox/rvbox-release --icon "$icon" --manifest gui --file-version "$version" --product-version "$version" --file-description "RVBox Windows client" --product-name "RVBox" --original-filename "rvbox.exe"
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=arm64 go build -trimpath -ldflags "$flags" -o "$out/rvbox-server-linux-arm64" ./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=linux GOARCH=arm64 go build -trimpath -ldflags "$flags" -o "$out/rvc-linux-arm64" ./cmd/rvc
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags "-H=windowsgui $windows_flags" -o "$out/rvbox-windows-amd64.exe" ./cmd/rvbox
' sh "$version" "$container_partial" "$bootstrap_server_url"
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 rvbox-server-linux-arm64 rvc-linux-amd64 rvc-linux-arm64 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 rvbox-server-linux-arm64 rvc-linux-amd64 rvc-linux-arm64 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/rvbox-server-linux-arm64" "$partial/rvc-linux-amd64" "$partial/rvc-linux-arm64" "$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"