This project uses git hooks to automate code quality checks and streamline the development workflow. Git hooks are scripts that run automatically at specific points in the git workflow.
Run the setup script once after cloning the repository:
./.githooks/setup.shThis script will:
- Configure git to use the
.githooksdirectory (setscore.hooksPath) - Make all hook scripts executable
- Display a summary of installed hooks
If you prefer manual setup, you can configure git hooks with:
git config core.hooksPath .githooks
chmod +x .githooks/pre-commit
chmod +x .githooks/pre-pushPurpose: Enforces code style and license headers before allowing commits.
Tools:
- ktlint: Code formatting and style consistency
- Spotless: GPL-3.0 SPDX license headers
Features:
- Smart checking: Runs ktlintCheck and spotlessCheck first to detect issues early
- Fast-fails: Immediately blocks commits on non-auto-correctable issues
- Auto-formats code and adds missing license headers when possible
- Temporarily stashes unstaged changes to avoid modifying them
- Automatically re-stages formatted files
- Provides clear, color-coded console feedback with helpful guidance
Workflow:
-
Identify Staged Files:
- Detects which Kotlin files are staged for commit
- If no Kotlin files are staged → commit proceeds immediately
-
Isolate Staged Changes:
- Temporarily stashes any unstaged changes (if present)
- This ensures only staged files are checked/formatted
-
Check Code Style (Step 1):
- Runs
./gradlew ktlintCheck --daemon - Runs
./gradlew spotlessCheck --daemon(license headers) - If all checks pass → commit proceeds immediately (fast path!)
- If issues found → proceeds to analysis
- Runs
-
Analyze Issues:
- Detects if issues can be auto-corrected
- Searches for "(cannot be auto-corrected)" in ktlint output
- If non-auto-correctable issues found → fail immediately with guidance
- If all issues are auto-correctable → proceeds to formatting
-
Auto-Format (if applicable):
- Runs
./gradlew ktlintFormat --daemon(code style) - Runs
./gradlew spotlessApply --daemon(license headers) - Only runs if all issues were determined to be auto-correctable
- Runs
-
Verify (Step 2):
- Runs
./gradlew ktlintCheck --daemonagain - Runs
./gradlew spotlessCheck --daemonagain - Ensures formatting and headers are correct
- Runs
-
Restore and Update:
- Restores any stashed unstaged changes
- Re-stages formatted files automatically
- Commit proceeds if all checks pass
Example Outputs:
Scenario 1: No Kotlin files staged
════════════════════════════════════════════════════════════
Running ktlint checks on staged files...
════════════════════════════════════════════════════════════
✅ No Kotlin files staged for commit
✅ Commit proceeding...
Scenario 2: Code already formatted correctly
════════════════════════════════════════════════════════════
Running code quality checks on staged files...
════════════════════════════════════════════════════════════
Staged Kotlin files:
• android/mobile/app/src/main/java/Example.kt
Step 1: Checking code style (ktlint)...
Step 2: Checking license headers (spotless)...
✅ Code style check passed
✅ License headers check passed
✅ Commit proceeding...
Scenario 3: Issues auto-corrected successfully
════════════════════════════════════════════════════════════
Running code quality checks on staged files...
════════════════════════════════════════════════════════════
Staged Kotlin files:
• android/mobile/app/src/main/java/Example.kt
Temporarily stashing unstaged changes...
Step 1: Checking code style (ktlint)...
Step 2: Checking license headers (spotless)...
License header issues found. Will auto-fix...
Running auto-formatters...
• Running spotlessApply (license headers)...
Step 3: Verifying all checks now pass...
Auto-formatted files:
• android/mobile/app/src/main/java/Example.kt
Re-staging formatted files...
✅ Formatted files have been re-staged
✅ All code style checks passed
✅ All license headers present
✅ Commit proceeding...
Restoring unstaged changes...
Scenario 4: Non-auto-correctable issues (commit blocked)
════════════════════════════════════════════════════════════
Running ktlint checks on staged files...
════════════════════════════════════════════════════════════
Staged Kotlin files:
• shared-ui/home/di/HomeModuleHilt.kt
Step 1: Checking code style...
Code style issues found. Analyzing...
❌ Found 1 issue(s) that cannot be auto-corrected!
Non-auto-correctable issues:
HomeModuleHilt.kt:1:1 File 'HomeModuleHilt.kt' contains a single class and should be named 'HomeModule.kt' (cannot be auto-corrected)
These issues must be fixed manually before committing.
Common fixes:
• File naming: Add @file:Suppress("MatchingDeclarationName") if temporary
• Or rename the file to match the class name
After fixing issues:
1. Stage your changes: git add <files>
2. Try committing again
To bypass this check (not recommended):
git commit --no-verify
Purpose: Automatically handles remote changes before pushing, particularly useful for CI commits like dependency graph updates.
Features:
- Fetches and merges remote changes automatically
- Handles CI commits (like dependency graph updates) seamlessly
- Provides color-coded feedback about the merge process
- Skips on protected branches (master/develop)
Workflow:
-
Check Current Branch:
- Skips if on
masterordevelop(protected branches)
- Skips if on
-
Fetch Remote:
- Fetches the latest changes from the remote branch
-
Compare States:
- Up to date: Push proceeds immediately
- First push: No remote branch exists yet, proceeds
- Remote ahead: Automatically merges remote changes
- Local ahead: Push proceeds
- Diverged: Attempts to merge remote changes
-
Auto-merge:
- If merge succeeds → push proceeds
- If merge conflicts → push is blocked, manual resolution required
Example Output:
Branch up to date:
Pre-push: Checking for remote changes...
Pre-push: Branch is up to date
Auto-merging remote changes:
Pre-push: Checking for remote changes...
Pre-push: Remote has new commits (likely CI updates)
Pre-push: Automatically merging...
Pre-push: Successfully merged, proceeding with push
Merge conflicts:
Pre-push: Checking for remote changes...
Pre-push: Branches have diverged
Pre-push: Attempting to merge...
Pre-push: Merge failed - conflicts need manual resolution
Resolve conflicts, then: git merge --continue
Or abort with: git merge --abort
While not recommended, you can bypass hooks when necessary:
git commit --no-verifygit push --no-verifyNote: Bypassing hooks should only be done in exceptional circumstances, as they exist to maintain code quality and prevent issues.
If hooks aren't executing:
-
Verify hooks are configured:
git config core.hooksPath
Should output:
.githooks -
Re-run the setup script:
./.githooks/setup.sh
-
Check hook files are executable:
ls -la .githooks/
Hook files should have execute permissions (
-rwxr-xr-x)
./gradlew ktlintCheck spotlessCheck./gradlew ktlintFormat spotlessApplyTemplate: license-header.txt (project root)
Format: SPDX standard GPL-3.0 header
Applied to: All .kt and .gradle.kts files
To update copyright year:
- Edit
license-header.txt - Run
./gradlew spotlessApply - All headers update automatically
- Ktlint Formatting Guide - Detailed information about code formatting
- Gradle Tasks - Available Gradle commands
- CI Workflows - GitHub Actions and automated checks