Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
name: Build Binaries

on:
push:
branches: [ main ]
tags:
- 'v*.*.*'
pull_request:
branches: [ main ]

jobs:
build:
name: Build ${{ matrix.target }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# macOS
- target: x86_64-apple-darwin
os: macos-13
arch: x86_64
suffix: apple-darwin-x86_64
- target: aarch64-apple-darwin
os: macos-14
arch: arm64
suffix: apple-darwin-arm64
# Linux (Disabled - Uncomment if needed)
# - target: x86_64-unknown-linux-gnu
# os: ubuntu-22.04
# arch: x86_64
# suffix: unknown-linux-gnu-x86_64
# - target: aarch64-unknown-linux-gnu
# os: ubuntu-22.04
# arch: arm64
# suffix: unknown-linux-gnu-arm64
# Windows (Disabled - Uncomment if needed)
# - target: x86_64-pc-windows-msvc
# os: windows-2022
# arch: x86_64
# suffix: pc-windows-msvc-x86_64
# - target: aarch64-pc-windows-msvc
# os: windows-2022
# arch: arm64
# suffix: pc-windows-msvc-arm64

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Install macOS dependencies
if: matrix.os == 'macos-13' || matrix.os == 'macos-14'
run: |
brew install cmake pkg-config

- name: Install Linux dependencies
if: matrix.os == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y cmake pkg-config libssl-dev

- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-${{ matrix.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}

- name: Build binary
run: |
rustup target add ${{ matrix.target }}
cargo build --release --target ${{ matrix.target }}
# Strip binary for smaller size (macOS/Linux)
if [ "${{ matrix.os }}" != "windows-2022" ]; then
strip target/${{ matrix.target }}/release/git-ca
fi

- name: Create archive
run: |
cd target/${{ matrix.target }}/release
if [ "${{ matrix.os }}" == "windows-2022" ]; then
7z a git-ca-${{ matrix.suffix }}.zip git-ca.exe
else
tar czf git-ca-${{ matrix.suffix }}.tar.gz git-ca
fi

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: git-ca-${{ matrix.suffix }}
path: target/${{ matrix.target }}/release/git-ca-${{ matrix.suffix }}.*
retention-days: 30

release:
name: Create Release
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
needs: build
permissions:
contents: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/

- name: Display structure of downloaded files
run: ls -R artifacts/

- name: Get version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

- name: Generate changelog
id: changelog
run: |
PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -v ${{ github.ref_name }} | head -n 1)
if [ -z "$PREVIOUS_TAG" ]; then
CHANGELOG=$(git log --pretty=format:"* %s (%an)" ${{ github.ref_name }})
else
CHANGELOG=$(git log --pretty=format:"* %s (%an)" $PREVIOUS_TAG..${{ github.ref_name }})
fi
echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Upload release assets
run: |
cd artifacts
find . -name "git-ca-*" -type f | while read file; do
echo "Uploading $file"
done

- name: Create Release
uses: softprops/action-gh-release@v1
with:
name: git-ca ${{ github.ref_name }}
body: |
## Changelog
${{ steps.changelog.outputs.CHANGELOG }}

## Downloads

### macOS (Apple Silicon)
- [git-ca-${{ steps.get_version.outputs.VERSION }}-apple-darwin-arm64.tar.gz](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/git-ca-${{ steps.get_version.outputs.VERSION }}-apple-darwin-arm64.tar.gz)

### macOS (Intel)
- [git-ca-${{ steps.get_version.outputs.VERSION }}-apple-darwin-x86_64.tar.gz](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/git-ca-${{ steps.get_version.outputs.VERSION }}-apple-darwin-x86_64.tar.gz)

## Installation

### Using Homebrew (Recommended)
```bash
brew tap zh30/tap
brew install git-ca
```

### From Release
Download the appropriate archive for your platform and extract it:
```bash
# macOS
tar -xzf git-ca-VERSION-PLATFORM.tar.gz
sudo mv git-ca /usr/local/bin/
```
draft: false
prerelease: false
generate_release_notes: true
files: |
artifacts/*/git-ca-*

- name: Compute checksums
run: |
mkdir checksums
cd artifacts
find . -name "git-ca-*" -type f | while read file; do
if [[ "$file" == *.tar.gz ]]; then
sha256sum "$file" >> ../checksums/checksums.txt
elif [[ "$file" == *.zip ]]; then
sha256sum "$file" >> ../checksums/checksums.txt
fi
done

- name: Upload checksums
uses: softprops/action-gh-release@v1
with:
files: checksums/checksums.txt
105 changes: 67 additions & 38 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- 'v*.*.*'

jobs:
create-release:
update-homebrew:
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -20,58 +20,87 @@ jobs:
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

- name: Generate changelog
id: changelog
- name: Download release assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -v ${{ github.ref_name }} | head -n 1)
if [ -z "$PREVIOUS_TAG" ]; then
CHANGELOG=$(git log --pretty=format:"* %s (%an)" ${{ github.ref_name }})
else
CHANGELOG=$(git log --pretty=format:"* %s (%an)" $PREVIOUS_TAG..${{ github.ref_name }})
fi
echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
with:
name: git-ca ${{ github.ref_name }}
body: |
## Changelog
${{ steps.changelog.outputs.CHANGELOG }}
draft: false
prerelease: false
generate_release_notes: true

- name: Get SHA256
id: get_sha
# Download all artifacts from the build-binaries workflow
gh api -H "Accept: application/vnd.github+json" \
"/repos/${{ github.repository }}/actions/artifacts?per_page=100" \
--jq '.artifacts[] | select(.name | contains("git-ca-")) | .archive_download_url' \
| xargs -I {} -n 1 bash -c 'curl -L -H "Authorization: token $GITHUB_TOKEN" {} -o artifacts/$(basename {})'

# Or download from release assets
gh release download ${{ github.ref_name }} --pattern 'git-ca-*' -D artifacts/

- name: Display downloaded files
run: ls -la artifacts/

- name: Calculate checksums
run: |
TARBALL_URL="https://github.com/${{ github.repository }}/archive/refs/tags/${{ github.ref_name }}.tar.gz"
SHA256=$(curl -L $TARBALL_URL | shasum -a 256 | awk '{print $1}')
echo "SHA256=$SHA256" >> $GITHUB_OUTPUT
cd artifacts
for file in git-ca-*; do
echo "=== $file ==="
sha256sum "$file"
done > ../checksums.txt
cat ../checksums.txt

- name: Extract checksums for each platform
id: extract_checksums
run: |
# Extract checksums for each platform
ARM64_MACOS=$(grep "apple-darwin-arm64" checksums.txt | awk '{print $1}')
X86_64_MACOS=$(grep "apple-darwin-x86_64" checksums.txt | awk '{print $1}')
# Linux builds disabled
ARM64_LINUX="DISABLED_LINUX"
X86_64_LINUX="DISABLED_LINUX"

echo "ARM64_MACOS=${ARM64_MACOS}" >> $GITHUB_OUTPUT
echo "X86_64_MACOS=${X86_64_MACOS}" >> $GITHUB_OUTPUT
echo "ARM64_LINUX=${ARM64_LINUX}" >> $GITHUB_OUTPUT
echo "X86_64_LINUX=${X86_64_LINUX}" >> $GITHUB_OUTPUT

echo "Checksums extracted:"
echo " ARM64 macOS: ${ARM64_MACOS}"
echo " x86_64 macOS: ${X86_64_MACOS}"
echo ""
echo "Note: Linux builds are disabled due to compilation issues"
echo " Windows builds are available via GitHub Releases but not distributed via Homebrew"

- name: Update Homebrew formula
- name: Update Homebrew formula with bottle checksums
run: |
VERSION=${{ steps.get_version.outputs.VERSION }}
SHA256=${{ steps.get_sha.outputs.SHA256 }}
sed -i "s|url \".*\"|url \"https://github.com/${{ github.repository }}/archive/refs/tags/v${VERSION}.tar.gz\"|" git-ca.rb
sed -i "s|sha256 \".*\"|sha256 \"${SHA256}\"|" git-ca.rb

# Update version and root URL
sed -i.bak "s|url \"https://github.com/${{ github.repository }}/archive/refs/tags/v.*\.tar.gz\"|url \"https://github.com/${{ github.repository }}/archive/refs/tags/v${VERSION}.tar.gz\"|" git-ca.rb

# Update bottle checksums
sed -i.bak "s|sha256 cellar: :any_skip_relocate, arm64_sequoia: \".*\"|sha256 cellar: :any_skip_relocate, arm64_sequoia: \"${{ steps.extract_checksums.outputs.ARM64_MACOS }}\"|" git-ca.rb
sed -i.bak "s|sha256 cellar: :any_skip_relocate, x86_64_sequoia: \".*\"|sha256 cellar: :any_skip_relocate, x86_64_sequoia: \"${{ steps.extract_checksums.outputs.X86_64_MACOS }}\"|" git-ca.rb
sed -i.bak "s|sha256 cellar: :any_skip_relocate, arm64_linux: \".*\"|sha256 cellar: :any_skip_relocate, arm64_linux: \"${{ steps.extract_checksums.outputs.ARM64_LINUX }}\"|" git-ca.rb
sed -i.bak "s|sha256 cellar: :any_skip_relocate, x86_64_linux: \".*\"|sha256 cellar: :any_skip_relocate, x86_64_linux: \"${{ steps.extract_checksums.outputs.X86_64_LINUX }}\"|" git-ca.rb

# Update version number in formula
sed -i.bak "s|git-ca/archive/refs/tags/v.*\.tar.gz|git-ca/archive/refs/tags/v${VERSION}.tar.gz|" git-ca.rb

echo "Updated git-ca.rb:"
cat git-ca.rb

- name: Update Homebrew Tap
env:
GITHUB_TOKEN: ${{ secrets.TARGET_REPO_PAT }}
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"

# Clone the homebrew-tap repository
git clone https://x-access-token:${{ secrets.TARGET_REPO_PAT }}@github.com/zh30/homebrew-tap.git

# Copy the updated formula to the tap repository
cp git-ca.rb homebrew-tap/

# Commit and push the changes to the tap repository
cd homebrew-tap
git add git-ca.rb
git commit -m "chore: update git-ca to v${{ steps.get_version.outputs.VERSION }}"
git commit -m "chore: update git-ca to v${{ steps.get_version.outputs.VERSION }} with bottle checksums"
git push
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ out
dist
node_modules
.vscode-test/
*.vsix
*.vsix
.git-ca/
32 changes: 12 additions & 20 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
# Repository Guidelines

## Project Structure & Module Organization
- `src/main.rs` hosts the CLI entrypoint, Git/Ollama integrations, and all user prompts; keep new modules small and import them from `main.rs` until a `src/` submodule is justified.
- `install-git-ca.sh` and `git-ca.rb` handle installer automation (shell and Homebrew); update both when distribution steps change.
- `README*.md`, `CLAUDE.md`, and `DEPLOY.md` are user-facing references—mirror any behavior changes here.
- Build artifacts land in `target/`; never commit that directory. Generated assets belong under `target/` or a new ignored path, not under `src/`.
The CLI entrypoint, prompt workflow, and llama.cpp bindings live in `src/main.rs`, with engine-specific helpers in `src/llama.rs`. Keep new logic scoped to these files until the surface area justifies an extracted module. Inline unit tests belong beside the code they cover; multi-step workflows (Git staging, model selection) should be promoted to `tests/`. Generated assets remain under `target/` or another ignored directory—never check them into `src/` or `tests/`.

## Build, Test, and Development Commands
- `cargo build --release` compiles the binary that installers copy into `~/.git-plugins`.
- `cargo run -- git ca` runs the CLI with local changes; use staged diffs in a sample repo to validate prompts.
- `cargo fmt` and `cargo clippy -- -D warnings` enforce Rust style and catch regressions before review.
- `cargo build --release` — produce the optimized binary that installers copy to `~/.git-plugins`.
- `cargo run -- git ca` — run the analyzer against staged changes in the current repo to validate prompt flows.
- `cargo fmt` — enforce rustfmt defaults (4-space indent, 100-column width).
- `cargo clippy -- -D warnings` — lint with warnings treated as build failures.
- `cargo test` — execute all unit tests; run before every commit and PR.
- Llama.cpp context length is fixed to 1024 tokens.

## Coding Style & Naming Conventions
- Follow rustfmt defaults (4-space indent, 100-column wrap) via `cargo fmt`; do not hand-format.
- Use `snake_case` for functions/files, `SCREAMING_SNAKE_CASE` for constants like `COMMIT_TYPES`, and `CamelCase` for types/enums.
- Prefer descriptive error messages and `?` propagation; add short comments only around non-obvious Git/Ollama logic.
Use `snake_case` for functions/files, `CamelCase` for types/enums, and `SCREAMING_SNAKE_CASE` for constants such as `COMMIT_TYPES`. Let rustfmt manage alignment and spacing. Prefer error propagation with `?`, returning `AppError::Custom` only when you need a user-facing message. Comments should explain non-obvious Git plumbing or llama-specific constraints; avoid restating what the code already conveys.

## Testing Guidelines
- Add unit tests in `#[cfg(test)]` modules next to the code under test; name functions `fn handles_*` to reflect behavior.
- Place integration tests under `tests/` when flows require multiple Git operations.
- Run `cargo test` locally; target meaningful branch coverage for new logic and document any manual verification (`git ca` run with staged fixtures) in the PR.
Unit tests live in `#[cfg(test)]` modules with descriptive names like `handles_retry_backoff`. Integration flows that combine Git operations, prompt generation, and llama inference should move into `tests/`. Always run `cargo test` locally and note any manual `cargo run -- git ca` checks (e.g., staged fixture repos) in PR descriptions. Target meaningful branch coverage over exhaustive mocking.

## Commit & Pull Request Guidelines
- Use Conventional Commit prefixes observed in history (e.g., `feat(client): ...`, `chore: ...`); scopes and descriptions can be English or Simplified Chinese.
- Squash work into logically complete commits with passing builds/tests.
- PRs must include: summary of behavior change, testing notes (`cargo test`, manual `git ca` checks), and updates to impacted docs.
- Link related issues and add screenshots or terminal captures when altering user prompts or install UX.
Follow the existing Conventional Commit style—examples include `feat(cli): simplify prompt`, `fix(llama): handle kv cache reset`, `chore(deps): update dependencies`. Each PR must summarise behavior changes, list verification steps (tests, manual runs), and update affected docs (`README*.md`, `DEPLOY.md`, `CLAUDE.md`). Link relevant issues and include terminal captures when altering user-visible prompts or installer UX.

## Ollama & Model Configuration Tips
- Keep a local Ollama instance running at `localhost:11434`; document any alternative endpoints in `DEPLOY.md` before merging.
- When introducing new model flows, ensure defaults are persisted via `git config` keys `commit-analyzer.model` and `commit-analyzer.language`.
## Model & Configuration Tips
By default the tool scans `./models` and cache directories for llama.cpp-compatible GGUF files, persists the user's selection, and reuses it on subsequent runs. Non-interactive invocations reuse the stored model or fall back to the first discovered GGUF. Document any alternative endpoints or model defaults in `DEPLOY.md` before merging. Store credentials in ignored env files, not in tracked sources, and confirm large lockfiles remain ignored or summarized automatically by the diff truncation logic.
Loading
Loading