#!/bin/bash set -e # 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"; } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } print_error() { echo -e "${RED}[ERROR]${NC} $1"; } # Global variable for commit message COMMIT_MESSAGE="" # Parse command line arguments - check if first arg is a command, otherwise treat as commit message COMMAND="" if [[ $# -gt 0 ]]; then case "$1" in build|clean|install|uninstall) COMMAND="$1" shift ;; *) # First argument is not a command, so default to build and treat all args as commit message COMMAND="build" ;; esac else # No arguments, default to build COMMAND="build" fi # Any remaining arguments become the commit message for arg in "$@"; do if [[ -z "$COMMIT_MESSAGE" ]]; then COMMIT_MESSAGE="$arg" else COMMIT_MESSAGE="$COMMIT_MESSAGE $arg" fi done # Function to automatically increment version increment_version() { print_status "Incrementing version..." # Check if we're in a git repository if ! git rev-parse --git-dir > /dev/null 2>&1; then print_warning "Not in a git repository - skipping version increment" return 0 fi # Get the highest version tag (not chronologically latest) LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.1.0") if [[ -z "$LATEST_TAG" ]]; then LATEST_TAG="v0.1.0" fi # Extract version components (remove 'v' prefix) VERSION=${LATEST_TAG#v} # Parse major.minor.patch using regex 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" print_error "Expected format: v0.1.0" return 1 fi # Increment patch version NEW_PATCH=$((PATCH + 1)) NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}" print_status "Current version: $LATEST_TAG" print_status "New version: $NEW_VERSION" # Stage all changes if git add . 2>/dev/null; then print_success "Staged all changes" else print_warning "Failed to stage changes (maybe not a git repository)" fi # Handle commit message - use global variable if set, otherwise prompt if [[ -z "$COMMIT_MESSAGE" ]]; then echo "" print_status "Please enter a meaningful commit message for version $NEW_VERSION:" echo -n "> " read -r COMMIT_MESSAGE fi # Check if user provided a message if [[ -z "$COMMIT_MESSAGE" ]]; then print_warning "No commit message provided. Using default message." COMMIT_MESSAGE="Automatic version increment" fi # Commit changes with user-provided message if git commit -m "Version $NEW_VERSION - $COMMIT_MESSAGE" 2>/dev/null; then print_success "Committed changes for version $NEW_VERSION" else print_warning "Failed to commit changes (maybe no changes to commit or not a git repository)" fi # Create new git tag if git tag "$NEW_VERSION" 2>/dev/null; then print_success "Created new version tag: $NEW_VERSION" # Push changes and tags to remote repository if git push 2>/dev/null; then print_success "Pushed changes to remote repository" else print_warning "Failed to push changes to remote repository" fi if git push --tags 2>/dev/null; then print_success "Pushed tags to remote repository" else print_warning "Failed to push tags to remote repository" fi else print_warning "Tag $NEW_VERSION already exists - using existing version" NEW_VERSION=$LATEST_TAG # Re-extract version components for existing tag VERSION=${NEW_VERSION#v} if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then MAJOR=${BASH_REMATCH[1]} MINOR=${BASH_REMATCH[2]} NEW_PATCH=${BASH_REMATCH[3]} fi fi # Update version strings in source code update_source_version "$NEW_VERSION" print_success "Version updated to ${NEW_VERSION}" } # Function to update version strings in source code update_source_version() { local NEW_VERSION="$1" print_status "Updating version strings in source code..." # Replace hardcoded version strings in src/otp.c with the current git tag if [ -f "src/otp.c" ]; then # Update main menu version sed -i "s/OTP v[0-9]\+\.[0-9]\+\.[0-9]\+/OTP $NEW_VERSION/g" src/otp.c # Update ASCII output version sed -i "s/Version: v[0-9]\+\.[0-9]\+\.[0-9]\+/Version: $NEW_VERSION/g" src/otp.c # Update usage/help text version sed -i "s/Implementation v[0-9]\+\.[0-9]\+\.[0-9]\+/Implementation $NEW_VERSION/g" src/otp.c print_success "Updated version strings in src/otp.c to $NEW_VERSION" else print_warning "src/otp.c not found - skipping version string updates" fi } # Cross-platform build functions check_cross_compiler() { if ! command -v aarch64-linux-gnu-gcc > /dev/null 2>&1; then print_error "ARM64/AArch64 cross-compiler not found!" print_error "Install with: sudo apt install gcc-aarch64-linux-gnu" print_error "Or on other distros: gcc-cross-aarch64" return 1 fi return 0 } upload_release_asset() { local api_url="$1" local token="$2" local version="$3" local filename="$4" local display_name="$5" if [ ! -f "$filename" ]; then print_warning "Binary $filename not found, skipping upload" return 1 fi print_status "Uploading $filename as '$display_name' to release..." # Get release ID first local release_id=$(curl -s -H "Authorization: token $token" \ "$api_url/releases/tags/$version" | \ grep -o '"id":[0-9]*' | head -n1 | cut -d: -f2) if [ -z "$release_id" ]; then print_error "Could not get release ID for $version" return 1 fi # Upload the asset using multipart/form-data curl -X POST "$api_url/releases/$release_id/assets" \ -H "Authorization: token $token" \ -F "attachment=@$filename;filename=$display_name" if [ $? -eq 0 ]; then print_success "Uploaded $filename as '$display_name' successfully" else print_warning "Failed to upload $filename" return 1 fi } create_gitea_release() { local version="$1" # Read token from ~/.gitea_token if [ ! -f "$HOME/.gitea_token" ]; then print_error "No ~/.gitea_token found. Cannot create release." print_error "Create ~/.gitea_token with your Gitea access token" return 1 fi local token=$(cat "$HOME/.gitea_token" | tr -d '\n\r') local api_url="https://git.laantungir.net/api/v1/repos/laantungir/otp" print_status "Creating Gitea release for $version..." # Create release local response=$(curl -s -X POST "$api_url/releases" \ -H "Authorization: token $token" \ -H "Content-Type: application/json" \ -d "{\"tag_name\": \"$version\", \"name\": \"$version\", \"body\": \"Automated release for $version\"}") if echo "$response" | grep -q '"id"'; then print_success "Created release $version" # Upload binaries with descriptive names from build directory upload_release_asset "$api_url" "$token" "$version" "build/otp-x86_64" "otp-${version}-linux-x86_64" upload_release_asset "$api_url" "$token" "$version" "build/otp-arm64" "otp-${version}-linux-arm64" else print_warning "Release may already exist or creation failed" print_status "Response: $response" # Try to upload to existing release anyway upload_release_asset "$api_url" "$token" "$version" "build/otp-x86_64" "otp-${version}-linux-x86_64" upload_release_asset "$api_url" "$token" "$version" "build/otp-arm64" "otp-${version}-linux-arm64" fi } build_project() { print_status "Cleaning previous build..." make clean increment_version # Check for cross-compiler if ! check_cross_compiler; then print_warning "ARM64/AArch64 cross-compiler not available, building x86_64 only" # Build x86_64 only print_status "Building OTP project for x86_64..." make CC=gcc ARCH=x86_64 if [ $? -eq 0 ]; then print_success "x86_64 build completed successfully" else print_error "x86_64 build failed" return 1 fi else # Build both architectures print_status "Building OTP project for x86_64..." make CC=gcc ARCH=x86_64 if [ $? -eq 0 ]; then print_success "x86_64 build completed successfully" else print_error "x86_64 build failed" return 1 fi print_status "Building OTP project for ARM64/AArch64..." # Clean only object files, not the x86_64 binary rm -f src/*.o make CC=aarch64-linux-gnu-gcc ARCH=arm64 if [ $? -eq 0 ]; then print_success "ARM64/AArch64 build completed successfully" else print_error "ARM64/AArch64 build failed" return 1 fi fi # Clean up object files after successful build print_status "Cleaning up object files..." rm -f src/*.o # Create Gitea release with binaries if [ -f "$HOME/.gitea_token" ]; then create_gitea_release "$NEW_VERSION" else print_warning "No ~/.gitea_token found. Skipping release creation." print_warning "Create ~/.gitea_token with your Gitea access token to enable releases." fi print_success "Build completed successfully" } clean_project() { print_status "Cleaning build artifacts..." make clean # Remove build directory rm -rf build print_success "Clean completed" } install_project() { print_status "Installing OTP project..." make install if [ $? -eq 0 ]; then print_success "Installation completed" else print_error "Installation failed" return 1 fi } uninstall_project() { print_status "Uninstalling OTP project..." make uninstall if [ $? -eq 0 ]; then print_success "Uninstallation completed" else print_error "Uninstallation failed" return 1 fi } # Main script logic case "$COMMAND" in build) build_project ;; clean) clean_project ;; install) install_project ;; uninstall) uninstall_project ;; *) echo "OTP Cipher Build Script" echo "Usage: $0 [command] [commit message]" echo "" echo "Arguments:" echo " command - {build|clean|install|uninstall} (default: build)" echo " commit message - Text to use as commit message (optional, skips interactive prompt)" echo "" echo "Commands:" echo " build - Cross-compile for x86_64 and ARM64/AArch64 with automatic version increment (default)" echo " clean - Clean build artifacts and cross-compiled binaries" echo " install - Install to system (requires build first)" echo " uninstall - Remove from system" echo "" echo "Build Output:" echo " build/otp-x86_64 - Native x86_64 binary" echo " build/otp-arm64 - ARM64/AArch64 binary for Raspberry Pi (if cross-compiler available)" echo "" echo "Gitea Integration:" echo " - Automatically creates releases with binaries if ~/.gitea_token exists" echo " - Requires: ARM64 cross-compiler (gcc-aarch64-linux-gnu)" echo "" echo "Examples:" echo " $0" echo " $0 \"Fixed checksum parsing bug\"" echo " $0 build \"Added new feature\"" echo " $0 clean" exit 1 ;; esac