c_template/AUTOMATIC_VERSIONING_GUIDE.md

9.4 KiB

Generic Automatic Version Increment System for Any Repository

Here's a generalized implementation guide for adding automatic versioning to any project:

Core Concept

Automatic patch version increment with each build - Every build automatically increments the patch version: v0.1.0 → v0.1.1 → v0.1.2, etc.

Implementation Steps

1. Add Version Increment Function to Build Script

Add this function to your build script (bash example):

# Function to automatically increment version
increment_version() {
    echo "[INFO] Incrementing version..."
    
    # Check if we're in a git repository
    if ! git rev-parse --git-dir > /dev/null 2>&1; then
        echo "[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
        echo "[ERROR] Invalid version format in tag: $LATEST_TAG"
        echo "[ERROR] Expected format: v0.1.0"
        return 1
    fi
    
    # Increment patch version
    NEW_PATCH=$((PATCH + 1))
    NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
    
    echo "[INFO] Current version: $LATEST_TAG"
    echo "[INFO] New version: $NEW_VERSION"
    
    # Create new git tag
    if git tag "$NEW_VERSION" 2>/dev/null; then
        echo "[SUCCESS] Created new version tag: $NEW_VERSION"
    else
        echo "[WARNING] Tag $NEW_VERSION already exists - using existing version"
        NEW_VERSION=$LATEST_TAG
    fi
    
    # Update VERSION file for compatibility
    echo "${NEW_VERSION#v}" > VERSION
    echo "[SUCCESS] Updated VERSION file to ${NEW_VERSION#v}"
}

2. Generate Version Header Files (For C/C++ Projects)

Add this to the increment_version function:

# Generate version.h header file (adjust path as needed)
cat > src/version.h << EOF
/*
 * Auto-Generated Version Header
 * DO NOT EDIT THIS FILE MANUALLY - Generated by build script
 */

#ifndef VERSION_H
#define VERSION_H

#define VERSION_MAJOR ${MAJOR}
#define VERSION_MINOR ${MINOR}
#define VERSION_PATCH ${NEW_PATCH}
#define VERSION_STRING "${MAJOR}.${MINOR}.${NEW_PATCH}"
#define VERSION_TAG "${NEW_VERSION}"

/* Build information */
#define BUILD_DATE "$(date +%Y-%m-%d)"
#define BUILD_TIME "$(date +%H:%M:%S)"
#define BUILD_TIMESTAMP "$(date '+%Y-%m-%d %H:%M:%S')"

/* Git information */
#define GIT_HASH "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
#define GIT_BRANCH "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"

/* Display versions */
#define VERSION_DISPLAY "${NEW_VERSION}"
#define VERSION_FULL_DISPLAY "${NEW_VERSION} ($(date '+%Y-%m-%d %H:%M:%S'), $(git rev-parse --short HEAD 2>/dev/null || echo 'unknown'))"

/* Version API functions */
const char* get_version(void);
const char* get_version_full(void);
const char* get_build_info(void);

#endif /* VERSION_H */
EOF

# Generate version.c implementation file
cat > src/version.c << EOF
/*
 * Auto-Generated Version Implementation
 * DO NOT EDIT THIS FILE MANUALLY - Generated by build script
 */

#include "version.h"

const char* get_version(void) {
    return VERSION_TAG;
}

const char* get_version_full(void) {
    return VERSION_FULL_DISPLAY;
}

const char* get_build_info(void) {
    return "Built on " BUILD_DATE " at " BUILD_TIME " from commit " GIT_HASH " on branch " GIT_BRANCH;
}
EOF

3. Generate Version File for Other Languages

Python (src/__version__.py):

cat > src/__version__.py << EOF
"""Auto-generated version file"""
__version__ = "${MAJOR}.${MINOR}.${NEW_PATCH}"
__version_tag__ = "${NEW_VERSION}"
__build_date__ = "$(date +%Y-%m-%d)"
__build_time__ = "$(date +%H:%M:%S)"
__git_hash__ = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
__git_branch__ = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
EOF

JavaScript/Node.js (update package.json):

# Update package.json version field
if [ -f package.json ]; then
    sed -i "s/\"version\": \".*\"/\"version\": \"${MAJOR}.${MINOR}.${NEW_PATCH}\"/" package.json
fi

Rust (update Cargo.toml):

if [ -f Cargo.toml ]; then
    sed -i "s/^version = \".*\"/version = \"${MAJOR}.${MINOR}.${NEW_PATCH}\"/" Cargo.toml
fi

Go (generate version.go):

cat > version.go << EOF
// Auto-generated version file
package main

const (
    VersionMajor = ${MAJOR}
    VersionMinor = ${MINOR}
    VersionPatch = ${NEW_PATCH}
    VersionString = "${MAJOR}.${MINOR}.${NEW_PATCH}"
    VersionTag = "${NEW_VERSION}"
    BuildDate = "$(date +%Y-%m-%d)"
    BuildTime = "$(date +%H:%M:%S)"
    GitHash = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
    GitBranch = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
)
EOF

Java (generate Version.java):

cat > src/main/java/Version.java << EOF
// Auto-generated version class
public class Version {
    public static final int VERSION_MAJOR = ${MAJOR};
    public static final int VERSION_MINOR = ${MINOR};
    public static final int VERSION_PATCH = ${NEW_PATCH};
    public static final String VERSION_STRING = "${MAJOR}.${MINOR}.${NEW_PATCH}";
    public static final String VERSION_TAG = "${NEW_VERSION}";
    public static final String BUILD_DATE = "$(date +%Y-%m-%d)";
    public static final String BUILD_TIME = "$(date +%H:%M:%S)";
    public static final String GIT_HASH = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')";
    public static final String GIT_BRANCH = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')";
}
EOF

4. Integrate into Build Targets

Call increment_version before your main build commands:

build_library() {
    increment_version
    echo "[INFO] Building library..."
    # Your actual build commands here
    make clean && make
}

build_release() {
    increment_version
    echo "[INFO] Building release..."
    # Your release build commands
}

build_package() {
    increment_version
    echo "[INFO] Building package..."
    # Your packaging commands
}

5. Update .gitignore

Add generated version files to .gitignore:

# Auto-generated version files
src/version.h
src/version.c
src/__version__.py
version.go
src/main/java/Version.java
VERSION

6. Update Build System Files

For Makefile projects:

# Add version.c to your source files
SOURCES = main.c utils.c version.c

For CMake projects:

# Add version files to your target
target_sources(your_target PRIVATE src/version.c)

For Node.js projects:

{
  "scripts": {
    "build": "node build.js && increment_version",
    "version": "node -e \"console.log(require('./package.json').version)\""
  }
}

7. Create Initial Version Tag

# Start with initial version
git tag v0.1.0

Usage Pattern

./build.sh          # v0.1.0 → v0.1.1
./build.sh release  # v0.1.1 → v0.1.2
./build.sh package  # v0.1.2 → v0.1.3

Manual Version Control

Major/Minor Version Bumps

# For feature releases (minor bump)
git tag v0.2.0  # Next build: v0.2.1

# For breaking changes (major bump)
git tag v1.0.0  # Next build: v1.0.1

Version Reset

# Delete incorrect tags (if needed)
git tag -d v0.2.1
git push origin --delete v0.2.1  # If pushed to remote

# Create correct base version
git tag v0.2.0

# Next build will create v0.2.1

Example Build Script Template

#!/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"; }

# Insert increment_version function here

case "${1:-build}" in
    build)
        increment_version
        print_status "Building project..."
        # Your build commands
        ;;
    clean)
        print_status "Cleaning build artifacts..."
        # Your clean commands
        ;;
    test)
        print_status "Running tests..."
        # Your test commands (no version increment)
        ;;
    release)
        increment_version
        print_status "Building release..."
        # Your release commands
        ;;
    *)
        echo "Usage: $0 {build|clean|test|release}"
        exit 1
        ;;
esac

Benefits

  1. Zero maintenance - No manual version editing
  2. Build traceability - Every build has unique version + metadata
  3. Git integration - Automatic version tags
  4. Language agnostic - Adapt generation for any language
  5. CI/CD friendly - Works in automated environments
  6. Rollback friendly - Easy to revert to previous versions

Troubleshooting

Version Not Incrementing

  • Ensure you're in a git repository
  • Check that git tags exist: git tag --list
  • Verify tag format matches v*.*.* pattern

Tag Already Exists

If a tag already exists, the build continues with existing version:

[WARNING] Tag v0.2.1 already exists - using existing version

Missing Git Information

If git is unavailable, version files show "unknown" for git hash and branch.