diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a151b72..2d79a73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: test: name: Test runs-on: ubuntu-latest - + steps: - name: Checkout code uses: actions/checkout@v4 @@ -47,95 +47,3 @@ jobs: run: | # Run tests using unittest (project uses unittest framework) uv run python -m unittest discover -s tests -p "test_*.py" -v || echo "Tests completed with warnings or failures" - - auto-version: - name: Auto Version Increment - runs-on: ubuntu-latest - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') - permissions: - contents: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 2 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Check for code changes - id: check-changes - run: | - # Get the previous commit (before the current push) - PREV_COMMIT=$(git rev-parse HEAD~1 2>/dev/null || echo "") - - if [ -z "$PREV_COMMIT" ]; then - echo "changed=true" >> $GITHUB_OUTPUT - echo "First commit or unable to determine previous commit, will increment version" - exit 0 - fi - - # Check if there are any changes in code files (excluding version file, docs, and CI files) - CHANGED_FILES=$(git diff --name-only $PREV_COMMIT HEAD | grep -v -E '^(fastskills/__version__|\.github/workflows/|README\.md|README\.zh\.md|\.gitignore|\.gitattributes)' || true) - - if [ -z "$CHANGED_FILES" ]; then - echo "changed=false" >> $GITHUB_OUTPUT - echo "No code changes detected, skipping version increment" - else - echo "changed=true" >> $GITHUB_OUTPUT - echo "Code changes detected in:" - echo "$CHANGED_FILES" - echo "Will increment patch version" - fi - - - name: Get current version - if: steps.check-changes.outputs.changed == 'true' - id: current-version - run: | - CURRENT_VERSION=$(cat fastskills/__version__) - echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT - echo "Current version: $CURRENT_VERSION" - - - name: Increment patch version - if: steps.check-changes.outputs.changed == 'true' - id: new-version - run: | - python3 scripts/bump_version.py patch - NEW_VERSION=$(cat fastskills/__version__) - echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT - echo "New version: $NEW_VERSION" - - - name: Commit and push version change - if: steps.check-changes.outputs.changed == 'true' - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add fastskills/__version__ - - # Check if there are changes to commit - if git diff --staged --quiet; then - echo "No version changes to commit" - exit 0 - fi - - NEW_VERSION="${{ steps.new-version.outputs.version }}" - git commit -m "chore: auto-increment patch version to $NEW_VERSION [skip ci]" - - # Push with retry logic - for i in 1 2 3; do - if git push; then - echo "Version update pushed successfully" - exit 0 - else - echo "Push failed, attempt $i/3" - sleep 2 - git pull --rebase || true - fi - done - - echo "Failed to push version update after 3 attempts" - exit 1 diff --git a/Makefile b/Makefile index 091ea5c..7dc1969 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: mypy mypy-fix ruff ruff-fix ruff-fix-unsafe version patch-version-increment minor-version-increment major-version-increment version-set help +.PHONY: mypy mypy-fix ruff ruff-fix ruff-fix-unsafe version patch-version-increment minor-version-increment major-version-increment version-set install-hook help help: @echo "Available targets:" @@ -12,6 +12,7 @@ help: @echo " minor-version-increment - Increment minor version (0.1.0 -> 0.2.0)" @echo " major-version-increment - Increment major version (0.1.0 -> 1.0.0)" @echo " version-set - Set version to a specific value (usage: make version-set VERSION=1.2.3)" + @echo " install-hook - Install pre-commit hook for version checking" mypy: @echo "Running mypy type checking..." @@ -61,3 +62,16 @@ version-set: exit 1; \ fi @python3 scripts/bump_version.py set $(VERSION) + +install-hook: + @echo "Installing pre-commit hook for version checking..." + @if [ ! -d .git ]; then \ + echo "Error: Not a git repository. Run this command from the project root."; \ + exit 1; \ + fi + @mkdir -p .git/hooks + @cp scripts/pre-commit-version-check.sh .git/hooks/pre-commit + @chmod +x .git/hooks/pre-commit + @echo "✓ Pre-commit hook installed successfully!" + @echo "" + @echo "The hook will now check that version numbers are updated when code changes are committed." diff --git a/README.md b/README.md index 69ed77f..f1b0b1b 100644 --- a/README.md +++ b/README.md @@ -444,7 +444,14 @@ Contributions are welcome! You can contribute by opening issues, submitting pull pip install -e . ``` -3. **Verify installation:** +4. **Install pre-commit hook (required):** + + ```bash + # Install the pre-commit hook using make + make install-hook + ``` + +5. **Verify installation:** ```bash python -c "import fastskills; print(fastskills.__version__)" ``` @@ -532,6 +539,12 @@ Before submitting a pull request, you **must** ensure your code passes all quali - [ ] No hard-coded values (use constants) - [ ] Proper error handling (no generic try-except) +- [ ] **Version Management:** + - [ ] Version number is updated if code changes were made + - [ ] Used appropriate version increment (patch/minor/major) + - [ ] Version file (`fastskills/__version__`) is included in the commit + - [ ] Pre-commit hook passes (automatically checks version updates) + - [ ] **Documentation:** - [ ] Code is properly commented where necessary - [ ] README is updated if needed (for user-facing changes) @@ -553,13 +566,11 @@ The project includes GitHub Actions workflows that automatically run on every pu - ✅ **Code Formatting** - Code must be properly formatted (checked by `ruff format`) - ✅ **Tests** - Test suite runs automatically -**Auto Version Increment (main/master branch only):** +**Version Management:** -- ✅ **Patch versions are automatically incremented** when code changes are pushed to `main` or `master` branch -- The version increment is automatically committed back to the repository -- Only triggers when actual code files are changed (excludes documentation, CI config, and version file itself) -- **Note:** Contributors don't need to manually increment patch versions - this is handled automatically by CI/CD -- **For minor/major versions:** Use manual commands (`make minor-version-increment` or `make major-version-increment`) before merging +- ⚠️ **Version updates are required** - All code changes must include a version number update +- A pre-commit hook automatically checks that version is updated when code changes +- See [Version Management](#version-management) section below for details **Available Make Targets:** @@ -572,37 +583,33 @@ make ruff-fix # Auto-fix linting errors (safe fixes) make ruff-fix-unsafe # Auto-fix linting errors (including unsafe fixes) make version # Show current version make patch-version-increment # Increment patch version -make minor-version-increment # Increment minor version +make minor-version-increment # Increment minor version make major-version-increment # Increment major version make version-set VERSION=x.y.z # Set version to a specific value +make install-hook # Install pre-commit hook for version checking ``` #### Version Management FastSkills uses [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PATCH). The version is managed centrally in the `fastskills/__version__` file. The version is automatically read by `fastskills/__init__.py` (accessible via `fastskills.__version__`) and synchronized with `pyproject.toml` during package builds. -**Automatic Version Management (CI/CD):** - -✅ **Patch Version** - **Automatically handled by CI/CD** +**⚠️ Important: Version Updates are Required** -- When code changes are pushed to `main` or `master` branch, the GitHub Actions workflow automatically increments the patch version -- Example: `0.1.0` → `0.1.1` (automatic on code changes) -- **No manual action required** - the CI/CD pipeline handles this automatically -- Only triggers when actual code files are changed (excludes documentation, CI config, and version file itself) +All code changes **must** include a version number update. A pre-commit hook automatically enforces this requirement - commits with code changes will be rejected if the version number is not updated. -**Manual Version Management:** +**Version Increment Guidelines:** -For **Minor** and **Major** version increments, use the manual commands: +- **Patch Version** (`make patch-version-increment`): Use for bug fixes and patches + - Example: `0.1.0` → `0.1.1` + - Use when: Fixing bugs, patching security issues, or making small improvements - **Minor Version** (`make minor-version-increment`): Use for new features that don't break compatibility - Example: `0.1.0` → `0.2.0` - Use when: Adding new features, enhancements, or improvements that maintain backward compatibility - - **Manual action required** - run this command before merging to main/master - **Major Version** (`make major-version-increment`): Use for breaking changes that may affect compatibility - Example: `0.1.0` → `1.0.0` - Use when: Making changes that break backward compatibility, significant API changes, or major architectural changes - - **Manual action required** - run this command before merging to main/master **Version Management Commands:** @@ -610,22 +617,63 @@ For **Minor** and **Major** version increments, use the manual commands: # Show current version make version -# Manual version increments (for minor/major versions only) -# Note: Patch version is handled automatically by CI/CD -make minor-version-increment # 0.1.0 -> 0.2.0 -make major-version-increment # 0.1.0 -> 1.0.0 +# Increment version (choose based on your changes) +make patch-version-increment # 0.1.0 -> 0.1.1 (bug fixes) +make minor-version-increment # 0.1.0 -> 0.2.0 (new features) +make major-version-increment # 0.1.0 -> 1.0.0 (breaking changes) # Set a specific version (for special cases) make version-set VERSION=2.0.0 ``` +**Pre-Commit Hook:** + +The project includes a pre-commit hook that automatically checks if the version number is updated when code changes are committed. This ensures version consistency across all commits. + +**Installing the Pre-Commit Hook:** + +```bash +# Install using make (recommended) +make install-hook + +# Or manually +cp scripts/pre-commit-version-check.sh .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit +``` + +**How It Works:** + +1. When you commit code changes, the hook checks if any code files were modified (excluding docs, CI config, etc.) +2. If code changes are detected, it verifies that `fastskills/__version__` is also staged and updated +3. If version is not updated, the commit is rejected with instructions on how to fix it +4. If only documentation or CI files are changed, version update is not required + +**Example Workflow:** + +```bash +# 1. Make your code changes +vim fastskills/some_file.py + +# 2. Stage your changes +git add fastskills/some_file.py + +# 3. Update version (required!) +make patch-version-increment # or minor/major depending on changes + +# 4. Stage the version file +git add fastskills/__version__ + +# 5. Commit (hook will verify version is updated) +git commit -m "fix: description of your changes" +``` + **Version Management Summary:** -| Version Type | Increment Method | When to Use | -| ------------ | ------------------------ | ------------------------------------------------ | -| **Patch** | ✅ **Automatic (CI/CD)** | Bug fixes, patches (automatic on code changes) | -| **Minor** | 🔧 **Manual** | New features, enhancements (backward compatible) | -| **Major** | 🔧 **Manual** | Breaking changes, major API changes | +| Version Type | Increment Method | When to Use | +| ------------ | ---------------- | ------------------------------------------------ | +| **Patch** | 🔧 **Manual** | Bug fixes, patches, small improvements | +| **Minor** | 🔧 **Manual** | New features, enhancements (backward compatible) | +| **Major** | 🔧 **Manual** | Breaking changes, major API changes | **Note:** If you're using `uv` (recommended), all commands will automatically use the project's virtual environment. If you're using `pip`, make sure to activate your virtual environment first. diff --git a/fastskills/__version__ b/fastskills/__version__ index 6e8bf73..17e51c3 100644 --- a/fastskills/__version__ +++ b/fastskills/__version__ @@ -1 +1 @@ -0.1.0 +0.1.1 diff --git a/scripts/pre-commit-version-check.sh b/scripts/pre-commit-version-check.sh new file mode 100755 index 0000000..78b83cf --- /dev/null +++ b/scripts/pre-commit-version-check.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# Pre-commit hook to check if version number is updated when code changes + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +VERSION_FILE="fastskills/__version__" + +# Get the list of staged files +STAGED_FILES=$(git diff --cached --name-only) + +# Check if version file is in the staged changes +VERSION_FILE_STAGED=$(echo "$STAGED_FILES" | grep -E "^$VERSION_FILE$" || true) + +# Check if there are any code changes (excluding version file, docs, and CI files) +CODE_CHANGES=$(echo "$STAGED_FILES" | grep -v -E '^(fastskills/__version__|\.github/workflows/|README\.md|README\.zh\.md|\.gitignore|\.gitattributes|scripts/pre-commit-version-check\.sh)' || true) + +# If no code changes, allow commit (version update not required) +if [ -z "$CODE_CHANGES" ]; then + echo -e "${GREEN}✓ No code changes detected, version update not required${NC}" + exit 0 +fi + +# If code changes exist but version file is not staged, reject commit +if [ -z "$VERSION_FILE_STAGED" ]; then + echo -e "${RED}✗ Error: Code changes detected but version number not updated!${NC}" + echo "" + echo -e "${YELLOW}You must update the version number when making code changes.${NC}" + echo "" + echo "To fix this, run one of the following commands:" + echo " make patch-version-increment # For bug fixes (0.1.0 -> 0.1.1)" + echo " make minor-version-increment # For new features (0.1.0 -> 0.2.0)" + echo " make major-version-increment # For breaking changes (0.1.0 -> 1.0.0)" + echo "" + echo "Then stage the version file:" + echo " git add $VERSION_FILE" + echo "" + echo "Or if you want to skip this check (not recommended):" + echo " git commit --no-verify" + exit 1 +fi + +# Check if version file actually changed +VERSION_DIFF=$(git diff --cached "$VERSION_FILE" 2>/dev/null || true) + +if [ -z "$VERSION_DIFF" ]; then + echo -e "${RED}✗ Error: Version file is staged but no version change detected!${NC}" + echo "" + echo "The version file must be updated. Run one of:" + echo " make patch-version-increment" + echo " make minor-version-increment" + echo " make major-version-increment" + exit 1 +fi + +# Get old version from HEAD (what's currently committed) +OLD_VERSION=$(git show HEAD:"$VERSION_FILE" 2>/dev/null | head -n1 | tr -d '[:space:]' || echo "") + +# Get new version from staged area (what will be committed) +NEW_VERSION=$(git show :"$VERSION_FILE" 2>/dev/null | head -n1 | tr -d '[:space:]' || echo "") + +# Fallback: if git show fails, try reading from diff +if [ -z "$NEW_VERSION" ]; then + NEW_VERSION=$(echo "$VERSION_DIFF" | grep -E "^\+" | grep -v "^+++" | sed 's/^+//' | head -n1 | tr -d '[:space:]') +fi + +if [ -z "$NEW_VERSION" ]; then + echo -e "${RED}✗ Error: Could not determine new version number!${NC}" + exit 1 +fi + +if [ -z "$OLD_VERSION" ]; then + # First commit, no old version in HEAD + echo -e "${GREEN}✓ Version file updated: $NEW_VERSION${NC}" +elif [ "$OLD_VERSION" = "$NEW_VERSION" ]; then + echo -e "${RED}✗ Error: Version file staged but version number unchanged!${NC}" + echo "" + echo "Current version: $OLD_VERSION" + echo "Staged version: $NEW_VERSION" + echo "" + echo "The version number must be different. Run one of:" + echo " make patch-version-increment" + echo " make minor-version-increment" + echo " make major-version-increment" + exit 1 +else + echo -e "${GREEN}✓ Version updated: $OLD_VERSION -> $NEW_VERSION${NC}" +fi + +exit 0