#!/usr/bin/env bash # release.sh — Verstak release script # Builds all binaries, packages, signs Firefox XPI, creates git tag. # # Usage: # ./scripts/release.sh # Full release (requires AMO tokens for Firefox signing) # ./scripts/release.sh --skip-firefox-sign # Skip Firefox XPI signing # ./scripts/release.sh --publish # Push git tag + create GitHub release # ./scripts/release.sh --help # Show usage set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" # --- Config --- SKIP_FIREFOX_SIGN=false PUBLISH=false for arg in "$@"; do case "$arg" in --skip-firefox-sign) SKIP_FIREFOX_SIGN=true ;; --publish) PUBLISH=true ;; --help) echo "Usage: $0 [--skip-firefox-sign] [--publish]" echo "" echo " --skip-firefox-sign Skip Firefox extension signing (fails by default if AMO tokens missing)" echo " --publish Push git tag and create GitHub release" exit 0 ;; esac done VERSION="$(cat VERSION 2>/dev/null || echo "0.1.0-dev")" RELEASE_DIR="release" LINUX_DIR="$RELEASE_DIR/linux" BUILD_DIR="build" echo "============================================" echo " Verstak Release v$VERSION" echo "============================================" echo "" # --- Pre-flight checks --- echo "==> Pre-flight checks..." if ! git diff --quiet --exit-code 2>/dev/null; then echo "ERROR: working tree is dirty. Commit or stash changes first." git diff --stat exit 1 fi if ! git diff --cached --quiet --exit-code 2>/dev/null; then echo "ERROR: staged but uncommitted changes. Commit first." exit 1 fi echo " Working tree clean ✓" if ! go version &>/dev/null; then echo "ERROR: go not found" exit 1 fi echo " Go: $(go version)" # --- Run tests --- echo "" echo "==> Running tests..." go vet ./... go test ./... -count=1 echo " Tests: all passed ✓" # --- Build --- echo "" echo "==> Building all binaries..." mkdir -p "$BUILD_DIR" "$LINUX_DIR" rm -f "$LINUX_DIR"/* ./scripts/build.sh release echo " Build complete ✓" # --- VERSION file --- echo "$VERSION" > "$LINUX_DIR/VERSION" # --- DEB package: GUI --- echo "" echo "==> Building DEB packages..." build_deb() { local binary="$1" local pkg_name="$2" local description="$3" local systemd_unit="${4:-}" local deb_dir="$BUILD_DIR/deb-tmp/$pkg_name" mkdir -p "$deb_dir/DEBIAN" mkdir -p "$deb_dir/usr/local/bin" mkdir -p "$deb_dir/usr/share/doc/$pkg_name" cp "$BUILD_DIR/$binary" "$deb_dir/usr/local/bin/$pkg_name" chmod 755 "$deb_dir/usr/local/bin/$pkg_name" if [[ -n "$systemd_unit" && -f "$systemd_unit" ]]; then mkdir -p "$deb_dir/lib/systemd/system" cp "$systemd_unit" "$deb_dir/lib/systemd/system/" fi # Simple control file cat > "$deb_dir/DEBIAN/control" < Description: $description CTRL # Copy README if exists [[ -f "README.md" ]] && cp "README.md" "$deb_dir/usr/share/doc/$pkg_name/" dpkg-deb --root-owner-group --build "$deb_dir" "$LINUX_DIR/${pkg_name}.deb" >/dev/null rm -rf "$BUILD_DIR/deb-tmp" echo " $LINUX_DIR/${pkg_name}.deb" } build_deb "verstak" "verstak" "Verstak — local-first working vault GUI" build_deb "verstak-server" "verstak-server" "Verstak Sync Server" "cmd/verstak-server/verstak-server.service" # --- RPM package: GUI --- echo "" echo "==> Building RPM packages..." build_rpm() { local binary="$1" local pkg_name="$2" local description="$3" local systemd_unit="${4:-}" local rpm_dir="$BUILD_DIR/rpm-tmp" mkdir -p "$rpm_dir/RPMS/x86_64" mkdir -p "$rpm_dir/BUILD" mkdir -p "$rpm_dir/SOURCES" # Build root for rpmbuild local buildroot="$rpm_dir/buildroot" mkdir -p "$buildroot/usr/local/bin" mkdir -p "$buildroot/usr/share/doc/$pkg_name" # Copy binary and systemd unit into the buildroot cp "$BUILD_DIR/$binary" "$buildroot/usr/local/bin/$pkg_name" chmod 755 "$buildroot/usr/local/bin/$pkg_name" if [[ -n "$systemd_unit" && -f "$systemd_unit" ]]; then mkdir -p "$buildroot/lib/systemd/system" cp "$systemd_unit" "$buildroot/lib/systemd/system/" fi [[ -f "README.md" ]] && cp "README.md" "$buildroot/usr/share/doc/$pkg_name/" # Also copy into SOURCES so rpmbuild %install can find them cp "$BUILD_DIR/$binary" "$rpm_dir/SOURCES/$pkg_name" if [[ -n "$systemd_unit" && -f "$systemd_unit" ]]; then cp "$systemd_unit" "$rpm_dir/SOURCES/$(basename "$systemd_unit")" fi [[ -f "README.md" ]] && cp "README.md" "$rpm_dir/SOURCES/README.md" # File list local file_list file_list="%attr(755, root, root) /usr/local/bin/$pkg_name" file_list+="%attr(644, root, root) /usr/share/doc/$pkg_name/*" if [[ -n "$systemd_unit" && -f "$systemd_unit" ]]; then # systemd file will be included via %files section file_list+="%attr(644, root, root) /lib/systemd/system/$(basename "$systemd_unit")" fi local spec_file="$rpm_dir/SPECS/${pkg_name}.spec" mkdir -p "$(dirname "$spec_file")" cat > "$spec_file" <> "$spec_file" <> "$spec_file" </dev/null || true %files /usr/local/bin/$pkg_name SPEC if [[ -n "$systemd_unit" && -f "$systemd_unit" ]]; then echo "/lib/systemd/system/$(basename "$systemd_unit")" >> "$spec_file" fi cat >> "$spec_file" </dev/null cp "$rpm_dir/RPMS/x86_64/${pkg_name}-${VERSION}-1.x86_64.rpm" \ "$LINUX_DIR/${pkg_name}.rpm" rm -rf "$rpm_dir" echo " $LINUX_DIR/${pkg_name}.rpm" } build_rpm "verstak" "verstak" "Verstak — local-first working vault GUI" build_rpm "verstak-server" "verstak-server" "Verstak Sync Server" "cmd/verstak-server/verstak-server.service" # --- Firefox signed XPI --- echo "" if [[ "$SKIP_FIREFOX_SIGN" == "true" ]]; then echo "==> Firefox extension signing skipped (--skip-firefox-sign)" elif [[ -f ".env" ]] && grep -q "WEB_EXT_API_KEY" ".env" 2>/dev/null; then echo "==> Signing Firefox extension..." ./scripts/release-firefox-xpi.sh else echo "ERROR: AMO tokens not found (WEB_EXT_API_KEY / WEB_EXT_API_SECRET in .env)." >&2 echo " Firefox signing is required for a full release." >&2 echo " To skip: run with --skip-firefox-sign" >&2 echo " To sign: copy .env.example -> .env and fill in AMO credentials from" >&2 echo " https://addons.mozilla.org/developers/" >&2 exit 1 fi # --- Checksums --- echo "" echo "==> Generating checksums..." cd "$LINUX_DIR" sha256sum verstak verstak-server *.deb *.rpm 2>/dev/null > checksums.txt || true cd "$ROOT_DIR" echo " $LINUX_DIR/checksums.txt" # --- Summary --- echo "" echo "============================================" echo " Release v$VERSION artifacts" echo "============================================" echo "" echo "Linux binaries:" ls -lh "$LINUX_DIR/" 2>/dev/null echo "" if [[ -d "$RELEASE_DIR/firefox" ]]; then echo "Firefox extension:" ls -lh "$RELEASE_DIR/firefox/" 2>/dev/null echo "" fi echo "============================================" # --- Git tag + release --- if [[ "$PUBLISH" == "true" ]]; then echo "" echo "==> Creating git tag v$VERSION..." git tag -s "v$VERSION" -m "Verstak v$VERSION" git push origin "v$VERSION" echo "==> Creating GitHub release..." gh release create "v$VERSION" \ --title "Verstak v$VERSION" \ --notes "See CHANGELOG.md for details" \ "$LINUX_DIR"/* \ $([[ -d "$RELEASE_DIR/firefox" ]] && echo "$RELEASE_DIR/firefox/"* || true) echo " GitHub release created: https://github.com/$(git remote get-url origin | sed 's|.*github.com[:/]||;s|\\.git$||')/releases/tag/v$VERSION" else echo "" echo "Dry-run: use --publish to create git tag + GitHub release." echo "Run: git tag -s v$VERSION -m \"Verstak v$VERSION\"" fi echo "" echo "Done."