#!/bin/bash set -euo pipefail # c65gm Release Builder # # Fully automated release process: # 1. Verifies git working tree is clean # 2. Reads BUILDNUM, derives version (MAJOR.BUILDNUM) # 3. Verifies the tag doesn't already exist # 4. Bumps BUILDNUM # 5. Builds all target binaries (embedded lib, version injected) # 6. Generates SHA256 checksums # 7. Commits the BUILDNUM bump (with checksums in commit body) # 8. Creates an annotated tag pointing at PRE-bump commit # (tag message contains checksums + build info for verification) # # To publish the release: # git push --follow-tags origin MAJOR=1 print_ok() { echo " [OK] $1"; } print_err() { echo " [ERROR] $1" >&2; } echo "" echo "=== c65gm Release Builder ===" echo "" # --------------------------------------------------------------------------- # 1. Verify git working tree is clean # --------------------------------------------------------------------------- if [ -n "$(git status --porcelain 2>/dev/null)" ]; then echo "" git status --short echo "" print_err "working tree is not clean — commit or stash changes first" exit 1 fi print_ok "Working tree is clean" # --------------------------------------------------------------------------- # 2. Read BUILDNUM # --------------------------------------------------------------------------- if [ ! -f BUILDNUM ]; then echo "1" > BUILDNUM fi BUILDNUM=$(cat BUILDNUM) VERSION="${MAJOR}.${BUILDNUM}" TAG="v${VERSION}" # --------------------------------------------------------------------------- # 3. Verify tag doesn't already exist # --------------------------------------------------------------------------- if git rev-parse --quiet --verify "refs/tags/${TAG}" >/dev/null 2>&1; then print_err "tag ${TAG} already exists — bump BUILDNUM manually if needed" exit 1 fi print_ok "Tag ${TAG} is available" # --------------------------------------------------------------------------- # 4. Save source commit (before any changes) # --------------------------------------------------------------------------- SOURCE_COMMIT=$(git rev-parse HEAD) if [ -z "$SOURCE_COMMIT" ]; then print_err "could not determine HEAD commit" exit 1 fi echo "" echo " Version: ${TAG}" echo " Source: ${SOURCE_COMMIT}" echo " BUILDNUM: ${BUILDNUM} (bumps to $((BUILDNUM + 1)))" echo "" # --------------------------------------------------------------------------- # 5. Bump BUILDNUM # --------------------------------------------------------------------------- NEXT_BUILDNUM=$((BUILDNUM + 1)) echo "${NEXT_BUILDNUM}" > BUILDNUM # --------------------------------------------------------------------------- # 6. Copy lib for embedding # --------------------------------------------------------------------------- echo " Preparing embedded library..." rm -rf internal/preproc/lib cp -r lib internal/preproc/ print_ok "Library copied for embedding" # --------------------------------------------------------------------------- # Build metadata # --------------------------------------------------------------------------- GO_VERSION="$(go version 2>/dev/null || echo 'unknown')" BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u +%Y-%m-%d)" LDFLAGS="-s -w -X main.version=${VERSION}" DIST="dist" mkdir -p "$DIST" # SHA256 command detection (Linux vs macOS) SHA_CMD="" if command -v sha256sum &>/dev/null; then SHA_CMD="sha256sum" elif command -v shasum &>/dev/null; then SHA_CMD="shasum -a 256" else print_err "no sha256sum or shasum command found" exit 1 fi # Targets TARGETS=( "linux/amd64" "linux/arm64" "darwin/amd64" "darwin/arm64" "windows/amd64" ) echo "" echo " Building targets: ${TARGETS[*]}" echo "" # --------------------------------------------------------------------------- # 7. Build all targets # --------------------------------------------------------------------------- BUILD_DIR="$(mktemp -d)" trap 'rm -rf "$BUILD_DIR"' EXIT for target in "${TARGETS[@]}"; do GOOS="${target%%/*}" GOARCH="${target##*/}" bin_name="c65gm" [ "$GOOS" = "windows" ] && bin_name="c65gm.exe" echo " ${target}..." GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=0 \ go build -trimpath -ldflags="$LDFLAGS" -o "${BUILD_DIR}/${bin_name}" archive_name="c65gm_v${VERSION}_${GOOS}_${GOARCH}" if [ "$GOOS" = "windows" ]; then (cd "$BUILD_DIR" && zip -q "${OLDPWD}/${DIST}/${archive_name}.zip" "$bin_name") echo " => ${DIST}/${archive_name}.zip" else tar -czf "${DIST}/${archive_name}.tar.gz" -C "$BUILD_DIR" "$bin_name" echo " => ${DIST}/${archive_name}.tar.gz" fi rm -f "${BUILD_DIR}/${bin_name}" done echo "" print_ok "All targets built" # --------------------------------------------------------------------------- # 8. Generate checksums # --------------------------------------------------------------------------- echo "" echo "=== SHA256 checksums ===" echo "" ( cd "$DIST" $SHA_CMD * > checksums.txt cat checksums.txt ) CHECKSUMS_TEXT="$(cat "${DIST}/checksums.txt")" print_ok "Checksums generated" # --------------------------------------------------------------------------- # 9. Commit BUILDNUM bump # --------------------------------------------------------------------------- echo "" echo "=== Creating commit ===" COMMIT_MSG=$(cat << COMMIT_EOF ${TAG} Built with: ${GO_VERSION} Build date: ${BUILD_DATE} SHA256 checksums: ${CHECKSUMS_TEXT} COMMIT_EOF ) git add BUILDNUM git commit -m "$COMMIT_MSG" print_ok "Commit created (BUILDNUM bumped to ${NEXT_BUILDNUM})" # --------------------------------------------------------------------------- # 10. Create annotated tag pointing at source commit # --------------------------------------------------------------------------- echo "" echo "=== Creating tag ===" TAG_MSG=$(cat << TAG_EOF c65gm ${TAG} Built with: ${GO_VERSION} Build date: ${BUILD_DATE} SHA256 checksums: ${CHECKSUMS_TEXT} To reproduce this build: git checkout ${TAG} go build -trimpath -ldflags="${LDFLAGS}" Then verify: sha256sum c65gm TAG_EOF ) git tag -a "$TAG" "$SOURCE_COMMIT" -m "$TAG_MSG" print_ok "Tag ${TAG} created (pointing at ${SOURCE_COMMIT})" # --------------------------------------------------------------------------- # 11. Summary # --------------------------------------------------------------------------- echo "" echo "============================================" echo " c65gm ${TAG} released!" echo "" echo " Source commit: ${SOURCE_COMMIT}" echo " BUILDNUM: ${BUILDNUM} → ${NEXT_BUILDNUM}" echo " Artifacts in: ${DIST}/" echo " Go version: ${GO_VERSION}" echo "" echo " !!! IMPORTANT !!!" echo " The tag '${TAG}' exists ONLY on your machine." echo " It must be pushed separately to make it available." echo "" echo " To publish the complete release:" echo " git push --follow-tags origin" echo "" echo " If you already pushed without --follow-tags:" echo " git push origin ${TAG}" echo "" echo " Artifacts:" ls -lh "${DIST}/" | sed 's/^/ /' echo "============================================" echo ""