Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
d527ae1
Implement Git-Same CLI
manuelgruber Jan 19, 2026
37d4686
Continue Git-Same CLI implementation
manuelgruber Jan 19, 2026
e190156
Add CLI Aliases
manuelgruber Jan 19, 2026
117181a
Add cache and clone options
manuelgruber Jan 24, 2026
8fb71a1
Fix Rust toolchain action
manuelgruber Jan 24, 2026
61f86cc
Fix Issues
manuelgruber Jan 26, 2026
299e477
Add Conductor Scripts
manuelgruber Feb 20, 2026
61516f5
Add bugfixes
manuelgruber Feb 20, 2026
62b370e
Update Docs
manuelgruber Feb 21, 2026
c5482e3
Update Docs
manuelgruber Feb 21, 2026
145b01f
Reorg src structure
manuelgruber Feb 21, 2026
cbbd5b5
Reorg src structure
manuelgruber Feb 21, 2026
d29ada0
Improve code
manuelgruber Feb 21, 2026
9d32ea5
Add global build to Conductor run script
manuelgruber Feb 21, 2026
8219506
Set GA to manual
manuelgruber Feb 22, 2026
5882f7b
Restructure src folder
manuelgruber Feb 22, 2026
f02c866
Add ASCII art
manuelgruber Feb 22, 2026
a81873a
Add TUI
manuelgruber Feb 23, 2026
7c94ae0
Update Conductor scripts
manuelgruber Feb 23, 2026
4ec8427
Update Packages
manuelgruber Feb 23, 2026
a551e3c
Remove fallback paths
manuelgruber Feb 23, 2026
d881eb4
Update Rust and Cargo
manuelgruber Feb 23, 2026
a57a440
Delete outdated docs
manuelgruber Feb 23, 2026
bce5972
Add new GiSa commands
manuelgruber Feb 23, 2026
5b79da6
Add default workspace feature
manuelgruber Feb 23, 2026
1f2b86e
Setup GA workflows
manuelgruber Feb 24, 2026
495d54d
Add reset command
manuelgruber Feb 24, 2026
df3c1db
Reorganize config folder
manuelgruber Feb 24, 2026
59dce45
Remove base_path from Global Config
manuelgruber Feb 24, 2026
3811b47
Add cleanup to setup.sh
manuelgruber Feb 24, 2026
9de846a
Add new tagline
manuelgruber Feb 24, 2026
55bbb82
Optimize Workspace Manager
manuelgruber Feb 24, 2026
93c107a
Add init command to TUI
manuelgruber Feb 24, 2026
1e8d491
Add plan
manuelgruber Feb 24, 2026
1a353e0
Set defaults
manuelgruber Feb 24, 2026
3f2fe3e
Optimize user flow
manuelgruber Feb 24, 2026
0ce1d6f
Remove completions command
manuelgruber Feb 24, 2026
86b96ba
Increase Version
manuelgruber Feb 24, 2026
bf8e688
Rearrange TUI Dashboard
manuelgruber Feb 24, 2026
f4e699c
Improve TUI flow
manuelgruber Feb 24, 2026
5b14101
Fix TUI boxes
manuelgruber Feb 24, 2026
9588a5f
Revert headline colors back
manuelgruber Feb 24, 2026
f06222e
Improve Userflow
manuelgruber Feb 24, 2026
d67952b
Improve Dashboard boxes
manuelgruber Feb 24, 2026
505798e
Remove Workspace names
manuelgruber Feb 24, 2026
e7f1454
Improve Dashboard & Settings
manuelgruber Feb 24, 2026
9979a4f
Add sync animation
manuelgruber Feb 25, 2026
8f392f2
Consolidate Logo Banner
manuelgruber Feb 25, 2026
1d7532d
Rewrite Sync Progress Screen 1
manuelgruber Feb 25, 2026
1b0b5d8
Rewrite Sync Progress Screen 2
manuelgruber Feb 25, 2026
b15e5fd
Create new Workspace screen
manuelgruber Feb 25, 2026
8b6afde
Create new Workspace screen
manuelgruber Feb 25, 2026
a8c7a69
Improve Workspace screen
manuelgruber Feb 25, 2026
df6ba1d
Enhance Setup screen
manuelgruber Feb 26, 2026
c75c8a3
Improve Architecture
manuelgruber Feb 26, 2026
aab5944
Remove unused git clone fetch pull commands
manuelgruber Feb 26, 2026
d6f3482
Add review bugfixes
manuelgruber Feb 26, 2026
75ee901
Update Dashboard & Banner
manuelgruber Feb 26, 2026
7597ff0
Update Sync Screen
manuelgruber Feb 26, 2026
46c2c56
Move TUI items
manuelgruber Feb 26, 2026
81ffb51
Add instructions for co-located tests
manuelgruber Feb 26, 2026
59ad486
Introduce co-located tests
manuelgruber Feb 26, 2026
0043c75
fix(core): harden sync/discovery behavior and path safety
manuelgruber Feb 26, 2026
fdc24ae
fix(app): improve TUI/CLI error handling, messaging, and release flow
manuelgruber Feb 26, 2026
3dc0374
Update Keys on Setup Screen
manuelgruber Feb 26, 2026
5ba6fe1
Simplify Auth to gh-cli only
manuelgruber Feb 26, 2026
2cb4f1f
Fix test
manuelgruber Feb 26, 2026
c5b6390
Design folder selector screen
manuelgruber Feb 26, 2026
9e7db03
Update docs &plans
manuelgruber Feb 26, 2026
ded03fe
Add small changes
manuelgruber Feb 26, 2026
134df90
Add path fix
manuelgruber Feb 26, 2026
8b7eef6
Add PR bugfixes
manuelgruber Feb 26, 2026
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
112 changes: 112 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

**Author:** Manuel from Eggenfelden.

## Build & Test Commands

```bash
cargo build # Debug build
cargo build --release # Optimized release build (LTO, stripped)
cargo test # Run all tests
cargo test <test_name> # Run a single test by name
cargo test --test integration_test # Run only integration tests
cargo fmt -- --check # Check formatting
cargo clippy -- -D warnings # Lint (zero warnings enforced)
```

Logging is controlled via `GISA_LOG` env var (e.g., `GISA_LOG=debug cargo run -- sync`).

## Architecture

Git-Same is a Rust CLI + TUI tool that discovers GitHub org/repo structures and mirrors them locally with parallel cloning and syncing.

**Binary aliases:** `git-same`, `gitsame`, `gitsa`, `gisa` — all point to `src/main.rs`.

**Dual mode:** Running with a subcommand (`gisa sync`) uses the CLI path. Running without a subcommand (`gisa`) launches the interactive TUI.

**CLI flow:** CLI parsing (`src/cli.rs`) → `main.rs` routes to command handler → handler orchestrates modules.

**Commands:** `init`, `setup`, `sync`, `status`, `workspace {list,default}`, `reset`.

### Core modules

- **`auth/`** — GitHub CLI (`gh`) authentication only (`gh auth token`), with SSH clone support
- **`config/`** — TOML config parser. Default location: `~/.config/git-same/config.toml`. Sections: `[clone]`, `[filters]`, `[[providers]]`
- **`discovery/`** — `DiscoveryOrchestrator` coordinates repo discovery via providers, applies filters, builds `ActionPlan` (what to clone vs sync)
- **`operations/clone/`** — `CloneManager` handles concurrent cloning (configurable 1–32, default 4)
- **`operations/sync/`** — `SyncManager` handles fetch/pull with concurrency. Detects repos with uncommitted changes and optionally skips them
- **`provider/`** — Trait-based provider abstraction (`Provider` trait in `traits.rs`). GitHub implementation in `github/client.rs` with pagination. Mock provider in `mock.rs` for testing
- **`git/`** — `GitOperations` trait (`traits.rs`) with `ShellGit` implementation (`shell.rs`) that shells out to `git` commands
- **`cache/`** — `DiscoveryCache` with TTL-based validity at `~/.cache/git-same/`
- **`errors/`** — Custom error hierarchy: `AppError`, `GitError`, `ProviderError` with `suggested_action()` methods
- **`output/`** — Verbosity levels and `indicatif` progress bars (`CloneProgressBar`, `SyncProgressBar`, `DiscoveryProgressBar`)
- **`types/repo.rs`** — Core data types: `Repo`, `Org`, `ActionPlan`, `OpResult`, `OpSummary`

### TUI module (`src/tui/`, feature-gated behind `tui`)

Elm architecture: `app.rs` = Model, `screens/` = View, `handler.rs` = Update.

- **`app.rs`** — `App` struct holds all TUI state. `Screen` enum: `InitCheck`, `SetupWizard`, `Workspace`, `Dashboard`, `Progress`, `Settings`
- **`handler.rs`** — Keyboard input handlers per screen + `handle_backend_message` for async results
- **`backend.rs`** — Spawns Tokio tasks for async operations (sync, status scan), sends `BackendMessage` variants via unbounded channels
- **`event.rs`** — `AppEvent` (terminal input, backend messages, ticks) and `BackendMessage` enum
- **`screens/`** — Stateless render functions per screen (dashboard, workspace, settings, etc.)
- **`widgets/`** — Shared widgets (status bar, spinner)
- **`setup/`** — Setup wizard state machine (shared between CLI `setup` command and TUI `SetupWizard` screen)

### Recent workspace-screen updates

- Sidebar navigation now supports `←`/`→` in addition to `↑`/`↓` and `Tab` (see `handle_workspace_key`).
- Workspace actions changed: `[f]` opens workspace folder; `[o]` is no longer bound.
- Default workspace behavior is now set-only from `[d]`: pressing `[d]` on the current default does not clear it.
- Workspace detail view is grouped into sections (`Identity`, `Paths`, `Sync`, `Account`) with wrapped org rendering for narrow widths.
- Last sync display now shows relative time and, when parseable RFC3339 exists, an absolute timestamp line.
- Added focused tests in `src/tui/handler.rs` and `src/tui/screens/workspace.rs` for key handling and workspace detail formatting helpers.

### Key patterns

- **Trait-based abstractions:** `GitOperations`, `Provider`, progress traits — enables mocking in tests
- **Concurrency:** Tokio tasks with `Arc<dyn Trait>` for sharing progress reporters across tasks
- **Error handling:** `thiserror` for typed errors + `anyhow` for propagation. Custom `Result` type alias in `errors/`
- **Channel-based TUI updates:** Backend operations send `BackendMessage` through `mpsc::UnboundedSender<AppEvent>`, processed by the TUI event loop
- **Arrow-only navigation:** All directional movement uses arrow keys only (`←` `↑` `↓` `→`). No vim-style `j`/`k`/`h`/`l` letter navigation. Display hints use `[←] [↑] [↓] [→] Move`.

## Formatting

`rustfmt.toml`: `max_width = 100`, `tab_spaces = 4`, edition 2021.

## Testing

**Convention:** Colocated test files using `#[path]` attribute. Every source file `foo.rs` has a companion `foo_tests.rs` in the same directory.

In the source file:
```rust
#[cfg(test)]
#[path = "foo_tests.rs"]
mod tests;
```

The test file contains `use super::*;` and all `#[test]` / `#[tokio::test]` functions.

**Do not** write inline `#[cfg(test)] mod tests { ... }` blocks — always use separate `_tests.rs` files.

**Integration tests** remain in `tests/integration_test.rs`.

## CI/CD Workflows

All workflows are `workflow_dispatch` (manual trigger) in `.github/workflows/`:

| Workflow | Purpose | Trigger |
|----------|---------|---------|
| `S1-Test-CI.yml` | fmt, clippy, test, build dry-run, coverage, audit | Manual dispatch |
| `S2-Release-GitHub.yml` | Full CI + cross-compile 6 targets + GitHub Release | Manual dispatch (select tag) |
| `S3-Publish-Homebrew.yml` | Update Homebrew tap formula | Manual dispatch (select tag) |
| `S4-Publish-Crates.yml` | `cargo publish` to crates.io | Manual dispatch (select tag) |

S2 runs all S1 jobs (test, coverage, audit) as gates before building release artifacts.

## Specs & Docs

Design specifications live in `docs/specs/` (S1–S5). Internal documentation in `.context/GIT-SAME-DOCUMENTATION.md`.
114 changes: 114 additions & 0 deletions .github/workflows/S1-Test-CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: S1 - Test CI

on:
workflow_dispatch:

Comment on lines +3 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List all workflow files
echo "=== GitHub Workflows ===" 
find .github/workflows -type f -name "*.yml" -o -name "*.yaml" 2>/dev/null | sort

# Read the S1-Test-CI.yml file
echo -e "\n=== S1-Test-CI.yml content ===" 
cat -n .github/workflows/S1-Test-CI.yml 2>/dev/null || echo "File not found"

Repository: ZAAI-com/git-same

Length of output: 3979


🏁 Script executed:

#!/bin/bash
# Read the other workflow files
echo "=== S2-Release-GitHub.yml (first 20 lines) ===" 
head -20 .github/workflows/S2-Release-GitHub.yml 2>/dev/null

echo -e "\n=== S3-Publish-Homebrew.yml (first 20 lines) ===" 
head -20 .github/workflows/S3-Publish-Homebrew.yml 2>/dev/null

echo -e "\n=== S4-Publish-Crates.yml (first 20 lines) ===" 
head -20 .github/workflows/S4-Publish-Crates.yml 2>/dev/null

# Check for branch protection settings or required checks
echo -e "\n=== Looking for protection rules or config ===" 
find . -maxdepth 2 -type f \( -name "*.json" -o -name "*.yml" -o -name "*.yaml" \) | xargs grep -l "required.*check\|branch.*protection" 2>/dev/null | head -5

Repository: ZAAI-com/git-same

Length of output: 1289


Add automatic CI triggers for pull requests and pushes to main.

S1-Test-CI only has workflow_dispatch, meaning comprehensive tests (formatting, clippy, multi-OS builds, code coverage, security audits) don't run automatically on PRs or pushes. This allows changes to merge without validation.

Suggested trigger update
 on:
+  pull_request:
+  push:
+    branches: [main]
   workflow_dispatch:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
on:
workflow_dispatch:
on:
pull_request:
push:
branches: [main]
workflow_dispatch:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/S1-Test-CI.yml around lines 3 - 5, The workflow currently
only uses manual trigger (on: workflow_dispatch) so CI won't run automatically;
update the workflow's triggers to include automatic events by replacing or
extending the on: block to include pull_request (to run on PRs) and push with
branches: [main] (to run on pushes to main) so the S1-Test-CI job executes for
PRs and main pushes; locate the on: workflow_dispatch entry in
.github/workflows/S1-Test-CI.yml and add those events accordingly.

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable]
include:
- os: ubuntu-latest
rust: beta

steps:
- uses: actions/checkout@v6

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
components: rustfmt, clippy

- uses: Swatinem/rust-cache@v2

- name: Check formatting
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --all-targets --all-features -- -D warnings

- name: Run tests
run: cargo test --all-features

build:
name: Build (${{ matrix.target }})
needs: [test]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-apple-darwin
os: macos-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
- target: aarch64-pc-windows-msvc
os: windows-latest

steps:
- uses: actions/checkout@v6

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

- name: Install cross-compilation linker (Linux ARM64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu

- uses: Swatinem/rust-cache@v2

- name: Build release
run: cargo build --release --target ${{ matrix.target }}
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && 'aarch64-linux-gnu-gcc' || '' }}

coverage:
name: Code Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2

- name: Install cargo-tarpaulin
uses: taiki-e/install-action@v2
with:
tool: cargo-tarpaulin

- name: Generate coverage
run: cargo tarpaulin --all-features --workspace --timeout 120 --out xml

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: false

audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
165 changes: 165 additions & 0 deletions .github/workflows/S2-Release-GitHub.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
name: S2 - Release GitHub

on:
workflow_dispatch:

Comment on lines +3 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Release publishing won’t run automatically on tag pushes.

Line 3 only enables workflow_dispatch, while Line 142 requires a tag ref. A normal git push --tags won’t start this workflow, so release automation is skipped.

Suggested fix
 on:
   workflow_dispatch:
+  push:
+    tags:
+      - "v*"

Also applies to: 142-143

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/S2-Release-GitHub.yml around lines 3 - 5, The workflow
currently only defines the manual trigger symbol workflow_dispatch, so tag
pushes won't start the release job; update the top-level on: triggers to include
a push trigger for tag refs (add a push: tags: entry) so that git push --tags
will invoke the workflow and satisfy the tag-based ref checks used later in the
release job; ensure the tag pattern in the push: tags matches the tag ref logic
referenced around the release job (the existing tag/ref checks) so the workflow
runs automatically on tag creation.

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

permissions:
contents: read

jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable]
include:
- os: ubuntu-latest
rust: beta

steps:
- uses: actions/checkout@v6

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
components: rustfmt, clippy

- uses: Swatinem/rust-cache@v2

- name: Check formatting
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --all-targets --all-features -- -D warnings

- name: Run tests
run: cargo test --all-features

coverage:
name: Code Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2

- name: Install cargo-tarpaulin
uses: taiki-e/install-action@v2
with:
tool: cargo-tarpaulin

- name: Generate coverage
run: cargo tarpaulin --all-features --workspace --timeout 120 --out xml

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: false

audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}

build-release-assets:
name: Build Release Asset (${{ matrix.target }})
needs: [test, coverage, audit]
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
artifact_name: git-same
asset_name: git-same-linux-x86_64
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
artifact_name: git-same
asset_name: git-same-linux-aarch64
- target: x86_64-apple-darwin
os: macos-latest
artifact_name: git-same
asset_name: git-same-macos-x86_64
- target: aarch64-apple-darwin
os: macos-latest
artifact_name: git-same
asset_name: git-same-macos-aarch64
- target: x86_64-pc-windows-msvc
os: windows-latest
artifact_name: git-same.exe
asset_name: git-same-windows-x86_64.exe
- target: aarch64-pc-windows-msvc
os: windows-latest
artifact_name: git-same.exe
asset_name: git-same-windows-aarch64.exe

steps:
- uses: actions/checkout@v6

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

- name: Install cross-compilation linker (Linux ARM64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu

- uses: Swatinem/rust-cache@v2

- name: Build
run: cargo build --release --target ${{ matrix.target }}
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && 'aarch64-linux-gnu-gcc' || '' }}

- name: Rename binary
shell: bash
run: |
mv target/${{ matrix.target }}/release/${{ matrix.artifact_name }} ${{ matrix.asset_name }}

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.asset_name }}
path: ${{ matrix.asset_name }}

publish-release:
name: Publish GitHub Release
needs: [build-release-assets]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
steps:
- name: Download built artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Collect release assets
shell: bash
run: |
mkdir -p release-assets
find artifacts -type f -exec cp {} release-assets/ \;

- name: Create/update release
uses: softprops/action-gh-release@v2
with:
files: release-assets/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Loading