#!/bin/bash set -e # nt increment + push script # Canonical version source is latest git tag (vX.Y.Z) # This script increments the tag and updates NT_VERSION macros in src/main.c 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; } COMMIT_MESSAGE="" VERSION_INCREMENT_TYPE="patch" # patch|minor|major RELEASE_MODE=false show_usage() { echo "nt increment and push" 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 After bump+push, build/upload release assets to Gitea" echo " -h, --help Show help" echo "" echo "EXAMPLES:" echo " $0 \"Fix relay reconnect bug\"" echo " $0 -m \"Add profile editing\"" echo " $0 -r -p \"Release patch build\"" echo " $0 -r -m \"Release minor build\"" echo "" echo "RELEASE MODE (-r):" echo " 1) Normal bump + commit + tag + push runs first" echo " 2) Build x86_64 static binary as ./nt (required)" echo " 3) Build arm64 static binary as ./nt (warning only on failure)" echo " 4) Snapshot release assets under build/" echo " 5) Create source tarball" echo " 6) Create/upload Gitea release assets if ~/.gitea_token exists" } while [[ $# -gt 0 ]]; do case "$1" in -p|--patch) VERSION_INCREMENT_TYPE="patch" shift ;; -m|--minor) VERSION_INCREMENT_TYPE="minor" shift ;; -M|--major) VERSION_INCREMENT_TYPE="major" shift ;; -r|--release) RELEASE_MODE=true shift ;; -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" echo "" show_usage exit 1 fi if ! git rev-parse --git-dir > /dev/null 2>&1; then print_error "Not in a git repository" exit 1 fi get_and_increment_version() { LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || true) if [[ -z "$LATEST_TAG" ]]; then LATEST_TAG="v0.0.0" 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 case "$VERSION_INCREMENT_TYPE" in major) NEW_MAJOR=$((MAJOR + 1)) NEW_MINOR=0 NEW_PATCH=0 ;; minor) NEW_MAJOR=$MAJOR NEW_MINOR=$((MINOR + 1)) NEW_PATCH=0 ;; *) NEW_MAJOR=$MAJOR NEW_MINOR=$MINOR NEW_PATCH=$((PATCH + 1)) ;; esac NEW_VERSION="v${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}" print_status "Current version: $LATEST_TAG" print_status "New version: $NEW_VERSION" } update_version_macros() { if [[ ! -f "src/main.c" ]]; then print_error "src/main.c not found" exit 1 fi print_status "Updating version macros in src/main.c" sed -i "s/^#define NT_VERSION_MAJOR .*/#define NT_VERSION_MAJOR ${NEW_MAJOR}/" src/main.c sed -i "s/^#define NT_VERSION_MINOR .*/#define NT_VERSION_MINOR ${NEW_MINOR}/" src/main.c sed -i "s/^#define NT_VERSION_PATCH .*/#define NT_VERSION_PATCH ${NEW_PATCH}/" src/main.c sed -i "s/^#define NT_VERSION \".*\"/#define NT_VERSION \"${NEW_VERSION}\"/" src/main.c print_success "Updated version in src/main.c to $NEW_VERSION" } git_commit_tag_push() { print_status "Staging changes" git add src/main.c CMakeLists.txt build.sh increment_and_push.sh README.md plans/planning.md include/*.h src/*.c .test_mnemonic 2>/dev/null || git add . if git diff --staged --quiet; then print_warning "No changes to commit" else git commit -m "$NEW_VERSION - $COMMIT_MESSAGE" print_success "Committed changes" fi if git rev-parse "$NEW_VERSION" >/dev/null 2>&1; then print_warning "Tag $NEW_VERSION already exists locally; recreating" git tag -d "$NEW_VERSION" >/dev/null 2>&1 || true fi git tag "$NEW_VERSION" print_success "Created tag: $NEW_VERSION" print_status "Pushing commit and tag" git push git push origin "$NEW_VERSION" print_success "Pushed $NEW_VERSION" } build_release_binary() { print_status "Building release binaries" if [[ ! -x "./build.sh" ]]; then print_error "./build.sh not found or not executable" return 1 fi mkdir -p build if ./build.sh; then cp -f nt build/nt_x86_64 print_success "Built x86_64 static binary" else print_error "x86_64 build failed; aborting release mode" return 1 fi if ./build.sh --arch arm64; then cp -f nt build/nt_arm64 print_success "Built arm64 static binary" else print_warning "arm64 build failed (continuing release with available assets)" fi return 0 } create_source_tarball() { local tarball_name="nostr_terminal-${NEW_VERSION#v}.tar.gz" print_status "Creating source tarball: $tarball_name" if tar -czf "$tarball_name" \ --exclude='build/*' \ --exclude='.git*' \ --exclude='*.db' \ --exclude='*.db-*' \ --exclude='*.log' \ --exclude='*.tar.gz' \ .; then print_success "Created source tarball: $tarball_name" echo "$tarball_name" return 0 else print_error "Failed to create source tarball" return 1 fi } create_gitea_release() { if [[ ! -f "$HOME/.gitea_token" ]]; then print_warning "No $HOME/.gitea_token found. Skipping release creation/upload." return 0 fi local token token=$(tr -d '\n\r' < "$HOME/.gitea_token") local api_url="https://git.laantungir.net/api/v1/repos/laantungir/nostr_terminal" print_status "Creating Gitea release: $NEW_VERSION" local response response=$(curl -s -X POST "$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 local release_id release_id=$(echo "$response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2) print_success "Created release $NEW_VERSION (id=$release_id)" echo "$release_id" return 0 fi if echo "$response" | grep -qi "already exists"; then print_warning "Release $NEW_VERSION already exists; resolving release id" local existing existing=$(curl -s -H "Authorization: token $token" "$api_url/releases/tags/$NEW_VERSION") if echo "$existing" | grep -q '"id"'; then local release_id release_id=$(echo "$existing" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2) print_status "Using existing release id=$release_id" echo "$release_id" return 0 fi print_error "Could not resolve existing release ID for $NEW_VERSION" return 1 fi print_error "Failed to create release" print_error "Response: $response" return 1 } upload_one_asset_with_retry() { local assets_url="$1" local token="$2" local file_path="$3" local max_attempts=3 local attempt=1 while [[ $attempt -le $max_attempts ]]; do print_status "Uploading $(basename "$file_path") (attempt $attempt/$max_attempts)" local response response=$(curl -s -X POST "$assets_url" \ -H "Authorization: token $token" \ -F "attachment=@$file_path;filename=$(basename "$file_path")") if echo "$response" | grep -q '"id"'; then print_success "Uploaded $(basename "$file_path")" return 0 fi if [[ $attempt -lt $max_attempts ]]; then print_warning "Upload attempt failed for $(basename "$file_path"), retrying..." sleep 2 else print_error "Failed to upload $(basename "$file_path") after $max_attempts attempts" print_error "Response: $response" fi attempt=$((attempt + 1)) done return 1 } upload_release_assets() { local release_id="$1" local tarball_path="$2" if [[ ! -f "$HOME/.gitea_token" ]]; then print_warning "No $HOME/.gitea_token found. Skipping asset uploads." return 0 fi local token token=$(tr -d '\n\r' < "$HOME/.gitea_token") local api_url="https://git.laantungir.net/api/v1/repos/laantungir/nostr_terminal" local assets_url="$api_url/releases/$release_id/assets" if [[ -f "build/nt_x86_64" ]]; then upload_one_asset_with_retry "$assets_url" "$token" "build/nt_x86_64" || true else print_warning "Asset missing: build/nt_x86_64" fi if [[ -f "build/nt_arm64" ]]; then upload_one_asset_with_retry "$assets_url" "$token" "build/nt_arm64" || true else print_warning "Asset missing: build/nt_arm64 (skipping)" fi if [[ -n "$tarball_path" && -f "$tarball_path" ]]; then print_status "Uploading $(basename "$tarball_path")" local response response=$(curl -s -X POST "$assets_url" \ -H "Authorization: token $token" \ -F "attachment=@$tarball_path;filename=$(basename "$tarball_path")") if echo "$response" | grep -q '"id"'; then print_success "Uploaded $(basename "$tarball_path")" else print_warning "Failed to upload $(basename "$tarball_path"): $response" fi else print_warning "Tarball missing, skipping upload" fi } main() { get_and_increment_version update_version_macros git_commit_tag_push if [[ "$RELEASE_MODE" == true ]]; then print_status "Release mode enabled" if ! build_release_binary; then print_error "Release mode aborted due to required x86_64 build failure" exit 1 fi local tarball_path="" if tarball_path=$(create_source_tarball); then : else print_warning "Proceeding without source tarball" fi local release_id="" if release_id=$(create_gitea_release); then if [[ "$release_id" =~ ^[0-9]+$ ]]; then upload_release_assets "$release_id" "$tarball_path" print_success "Release workflow finished for $NEW_VERSION" else print_warning "Release creation/upload skipped (no release id)" fi else print_error "Release creation failed" exit 1 fi fi print_success "Done. Pushed $NEW_VERSION" } main