#!/bin/bash set -e # increment_and_push.sh - Version increment and git automation script # # Usage: # ./increment_and_push.sh "meaningful git comment" # ./increment_and_push.sh -p "meaningful git comment" # patch bump (default) # ./increment_and_push.sh -m "meaningful git comment" # minor bump # ./increment_and_push.sh -M "meaningful git comment" # major bump # ./increment_and_push.sh -r -p "meaningful git comment" # release: build, tag, push, gitea release + assets # ./increment_and_push.sh --set-version vX.Y.Z "comment" # explicit version # # Mirrors the n_signer workflow: -p/-m/-M/-r flags, version read from git tags, # updates both VERSION and src/version.h, and (in release mode) builds the # binary, creates a Gitea release, and uploads the binary + source tarball. # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' print_status() { echo -e "${BLUE}[INFO]${NC} $1" >&2; } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" >&2; } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" >&2; } print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; } # --- Cleanup trap ------------------------------------------------------- # Any temp files registered here are removed on exit, including when the # script is interrupted (Ctrl+C) or aborted by `set -e`. This prevents # leftover sovereign_browser-*.tar.gz files from accumulating in the # project root when a release run is interrupted. CLEANUP_FILES=() cleanup_on_exit() { for f in "${CLEANUP_FILES[@]}"; do [[ -n "$f" && -f "$f" ]] && rm -f "$f" done } trap cleanup_on_exit EXIT INT TERM register_cleanup() { [[ -n "$1" ]] && CLEANUP_FILES+=("$1") } # --- Config ------------------------------------------------------------- # Gitea repo for release uploads (derived from the origin remote). GITEA_API_URL="https://git.laantungir.net/api/v1/repos/laantungir/sovereign_browser" BIN_NAME="sovereign_browser" # --- Args --------------------------------------------------------------- COMMIT_MESSAGE="" RELEASE_MODE=false VERSION_INCREMENT_TYPE="patch" TARGET_VERSION="" show_usage() { echo "sovereign_browser Increment and Push Script" echo "" echo "USAGE:" echo " $0 [OPTIONS] \"commit message\"" echo "" echo "OPTIONS:" echo " -p, --patch Increment patch version (default)" echo " -m, --minor Increment minor version" echo " -M, --major Increment major version" echo " -r, --release Create a Gitea release with built binary + tarball" echo " --set-version vX.Y.Z Set an explicit version instead of incrementing" echo " -h, --help Show this help message" echo "" echo "EXAMPLES:" echo " $0 \"Add hamburger menu\"" echo " $0 -m \"Security strip feature\"" echo " $0 -r -p \"Release: FIPS scheme handler\"" echo " $0 --set-version v0.1.0 \"First stable release\"" } while [[ $# -gt 0 ]]; do case $1 in -r|--release) RELEASE_MODE=true shift ;; -p|--patch) VERSION_INCREMENT_TYPE="patch" shift ;; -m|--minor) VERSION_INCREMENT_TYPE="minor" shift ;; -M|--major) VERSION_INCREMENT_TYPE="major" shift ;; --set-version) if [[ -z "$2" ]]; then print_error "--set-version requires a version argument (vX.Y.Z)" exit 1 fi TARGET_VERSION="$2" shift 2 ;; -h|--help) show_usage exit 0 ;; *) if [[ -z "$COMMIT_MESSAGE" ]]; then COMMIT_MESSAGE="$1" fi shift ;; esac done if [[ -z "$COMMIT_MESSAGE" ]]; then print_error "Commit message is required" show_usage exit 1 fi # --- Checks ------------------------------------------------------------- # Check if we're in the correct directory CURRENT_DIR=$(basename "$(pwd)") if [ "$CURRENT_DIR" != "sovereign_browser" ]; then print_error "Script must be run from the sovereign_browser directory" echo "" echo "Current directory: $CURRENT_DIR" echo "Expected directory: sovereign_browser" exit 1 fi # Check if git repository exists if ! git rev-parse --git-dir > /dev/null 2>&1; then print_error "Not a git repository. Please initialize git first." exit 1 fi # Check that the version header exists (C-side source of truth) if [ ! -f "src/version.h" ]; then print_error "src/version.h not found" exit 1 fi # --- Version logic ------------------------------------------------------ update_version_files() { local new_version="$1" # includes leading 'v' local major="$2" local minor="$3" local patch="$4" # VERSION file: plain X.Y.Z (no leading 'v'), matches nostr_core_lib echo "$major.$minor.$patch" > VERSION print_success "Updated VERSION file -> $major.$minor.$patch" # src/version.h: C defines (SB_ prefix to avoid clashing with nostr_core_lib's VERSION) sed -i "s/#define SB_VERSION .*/#define SB_VERSION \"v$major.$minor.$patch\"/" src/version.h sed -i "s/#define SB_VERSION_MAJOR .*/#define SB_VERSION_MAJOR $major/" src/version.h sed -i "s/#define SB_VERSION_MINOR .*/#define SB_VERSION_MINOR $minor/" src/version.h sed -i "s/#define SB_VERSION_PATCH .*/#define SB_VERSION_PATCH $patch/" src/version.h print_success "Updated src/version.h -> v$major.$minor.$patch" } increment_version() { local increment_type="$1" # Read the latest version from git tags (like n_signer), falling back to # the VERSION file if no tags exist yet. LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 2>/dev/null || echo "") if [[ -z "$LATEST_TAG" ]]; then if [ -f "VERSION" ]; then local file_ver file_ver=$(tr -d '[:space:]' < VERSION) file_ver="${file_ver#v}" LATEST_TAG="v$file_ver" else LATEST_TAG="v0.0.0" fi print_warning "No version tags found, starting from $LATEST_TAG" fi VERSION=${LATEST_TAG#v} if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then MAJOR=${BASH_REMATCH[1]} MINOR=${BASH_REMATCH[2]} PATCH=${BASH_REMATCH[3]} else print_error "Invalid version format in tag: $LATEST_TAG" exit 1 fi if [[ "$increment_type" == "major" ]]; then NEW_VERSION="v$((MAJOR + 1)).0.0" elif [[ "$increment_type" == "minor" ]]; then NEW_VERSION="v${MAJOR}.$((MINOR + 1)).0" else NEW_VERSION="v${MAJOR}.${MINOR}.$((PATCH + 1))" fi local new_no_v=${NEW_VERSION#v} local new_major=${new_no_v%%.*} local rest=${new_no_v#*.} local new_minor=${rest%%.*} local new_patch=${rest##*.} update_version_files "$NEW_VERSION" "$new_major" "$new_minor" "$new_patch" export NEW_VERSION } set_explicit_version() { if [[ ! "$TARGET_VERSION" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then print_error "Invalid target version format: $TARGET_VERSION (expected vX.Y.Z)" exit 1 fi NEW_VERSION="$TARGET_VERSION" local new_no_v=${NEW_VERSION#v} local new_major=${new_no_v%%.*} local rest=${new_no_v#*.} local new_minor=${rest%%.*} local new_patch=${rest##*.} update_version_files "$NEW_VERSION" "$new_major" "$new_minor" "$new_patch" export NEW_VERSION } # --- Git operations ----------------------------------------------------- git_commit_and_push() { git add . if ! git diff --staged --quiet; then git commit -m "$NEW_VERSION - $COMMIT_MESSAGE" print_success "Changes committed" else print_warning "No changes to commit" fi # Create (or recreate) the tag. if git tag "$NEW_VERSION" > /dev/null 2>&1; then print_success "Created tag: $NEW_VERSION" else git tag -d "$NEW_VERSION" > /dev/null 2>&1 || true git tag "$NEW_VERSION" > /dev/null 2>&1 print_warning "Replaced existing tag: $NEW_VERSION" fi CURRENT_BRANCH=$(git branch --show-current) git push origin "$CURRENT_BRANCH" || print_warning "git push branch returned non-zero (continuing)" git push origin "$NEW_VERSION" || git push --force origin "$NEW_VERSION" || print_warning "git push tag returned non-zero (continuing)" } # --- Release mode: build + Gitea release -------------------------------- build_release_binary() { if [[ ! -f "build.sh" ]]; then print_error "build.sh not found" return 1 fi # Prevent stale artifacts from previous builds. rm -f "$BIN_NAME" print_status "Building release binary..." ./build.sh > /dev/null 2>&1 || return 1 if [[ ! -x "$BIN_NAME" ]]; then print_error "Binary not found or not executable after build: $BIN_NAME" return 1 fi print_success "Release binary built: $BIN_NAME" return 0 } create_source_tarball() { local tarball_name="${BIN_NAME}-${NEW_VERSION#v}.tar.gz" if tar -czf "$tarball_name" \ --exclude="$BIN_NAME" \ --exclude='.git*' \ --exclude='*.log' \ --exclude='*.tar.gz' \ --exclude='nostr_core_lib' \ --exclude='tests/local-site/assets/*.mp4' \ --exclude='tests/local-site/assets/*.m4a' \ --exclude='tests/local-site/assets/*.webm' \ --exclude='tests/local-site/assets/*.ogg' \ . > /dev/null 2>&1; then echo "$tarball_name" else return 1 fi } create_gitea_release() { if [[ ! -f "$HOME/.gitea_token" ]]; then print_warning "No ~/.gitea_token found. Skipping release creation." return 0 fi local token token=$(tr -d '\n\r' < "$HOME/.gitea_token") local response response=$(curl -s -X POST "$GITEA_API_URL/releases" \ -H "Authorization: token $token" \ -H "Content-Type: application/json" \ -d "{\"tag_name\": \"$NEW_VERSION\", \"name\": \"$NEW_VERSION\", \"body\": \"$COMMIT_MESSAGE\"}") if echo "$response" | grep -q '"id"'; then echo "$response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2 else print_warning "Gitea release creation did not return an id. Response: $response" return 1 fi } upload_release_assets() { local release_id="$1" local binary_path="$2" local tarball_path="$3" if [[ ! -f "$HOME/.gitea_token" ]]; then print_warning "No ~/.gitea_token found. Skipping asset uploads." return 0 fi local token token=$(tr -d '\n\r' < "$HOME/.gitea_token") local assets_url="$GITEA_API_URL/releases/$release_id/assets" if [[ -f "$binary_path" ]]; then curl -s -X POST "$assets_url" \ -H "Authorization: token $token" \ -F "attachment=@$binary_path;filename=$(basename "$binary_path")" \ -F "name=$(basename "$binary_path")" > /dev/null print_success "Uploaded binary: $(basename "$binary_path")" fi if [[ -f "$tarball_path" ]]; then curl -s -X POST "$assets_url" \ -H "Authorization: token $token" \ -F "attachment=@$tarball_path;filename=$(basename "$tarball_path")" \ -F "name=$(basename "$tarball_path")" > /dev/null print_success "Uploaded tarball: $(basename "$tarball_path")" fi } # --- Main --------------------------------------------------------------- print_status "Starting version increment and push process..." if [[ -n "$TARGET_VERSION" ]]; then set_explicit_version else increment_version "$VERSION_INCREMENT_TYPE" fi print_status "New version: $NEW_VERSION" if [[ "$RELEASE_MODE" == true ]]; then # Build before committing so a failed build aborts the release. if ! build_release_binary; then print_error "Release build failed; aborting before push/upload" exit 1 fi git_commit_and_push local_tarball="" local_tarball=$(create_source_tarball || true) # Register the tarball for cleanup so it's removed on exit even if a # later step fails or the script is interrupted. The trap also catches # the case where create_gitea_release aborts via `set -e`. register_cleanup "$local_tarball" print_status "Creating Gitea release for $NEW_VERSION..." release_id="" release_id=$(create_gitea_release || true) if [[ -n "$release_id" ]]; then print_status "Uploading release assets (binary + tarball)..." upload_release_assets "$release_id" "$BIN_NAME" "$local_tarball" else print_warning "Gitea release creation failed or returned no id. Binary not uploaded." fi # The tarball is removed by the cleanup_on_exit trap registered above, # so no manual rm is needed here. The binary is gitignored already. print_success "Release flow completed: $NEW_VERSION" else git_commit_and_push print_success "Increment and push completed: $NEW_VERSION" fi echo "" echo "🎉 Done! Version $NEW_VERSION is live."