Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
f79feb4
Add MacOS Finder Badge
manuelgruber Apr 6, 2026
9db859f
Bump softprops/action-gh-release to v3 for Node 24 runtime
manuelgruber Apr 19, 2026
566a52e
Fix TUI sync hang by timing out gh calls and offloading auth to block…
manuelgruber Apr 19, 2026
ab05538
Add timeouts to SSH probe and pagination
manuelgruber Apr 19, 2026
465e44c
Extract RepoScanService to separate backend API from frontend consumers
manuelgruber Apr 19, 2026
5b20203
Harden daemon shutdown and scaffold Xcode project for Finder badge te…
manuelgruber Apr 19, 2026
d4fc443
Harden FinderSync extension sandbox and drawing for macOS 26 testing
manuelgruber Apr 19, 2026
7403ae7
Switch macOS Xcode project to in-repo source of truth, drop XcodeGen …
manuelgruber Apr 19, 2026
e51dcd6
Trim FinderSync debug logging to lifecycle events
manuelgruber Apr 19, 2026
3ab89f8
Group Finder context menu under one Git-Same submenu to reduce clutter
manuelgruber Apr 19, 2026
e78d693
Enrich Git-Same context menu with org aggregates and last-scan info
manuelgruber Apr 19, 2026
f4b7c2b
Group Git-Same menu data into Organization, Repositories, and details
manuelgruber Apr 20, 2026
e8618de
Classify owner folders as user/org via GitHub API for distinct badges
manuelgruber Apr 20, 2026
ddabff8
Add gray ambient badge case to Swift and ambient-repo tests
manuelgruber Apr 20, 2026
50b4a78
Skip grey R placeholder for O and U badges to avoid letter swap on paint
manuelgruber Apr 20, 2026
663fa90
Split menu into Organization+Workspace and adopt sentence-style Repos…
manuelgruber Apr 20, 2026
4445f0f
Prefill Finder badge cache from status.json, drop grey to color timer
manuelgruber Apr 20, 2026
f0247bc
Fix grey R badge painted on every folder under monitored root
manuelgruber Apr 20, 2026
6335f52
Update run.sh cheat-sheet with daemon, scan, and TUI commands
manuelgruber Apr 20, 2026
b7f7b1a
Nudge Finder daemon after sync so new clones get their repo badge imm…
manuelgruber Apr 22, 2026
b6c1e55
Seed StatusReader on first successful open so badges show without a s…
manuelgruber Apr 22, 2026
515b243
Nudge daemon after reset and add gisa refresh for manual badge resync
manuelgruber Apr 22, 2026
cbb5398
Add nonconcurrent Conductor run mode to prevent install races
manuelgruber May 6, 2026
7fb0c82
Bump version to 3.1.0 for next release
manuelgruber May 7, 2026
e33fc5d
Surface unreadable repos in status and stop calling [gone] branches s…
manuelgruber May 7, 2026
6364c19
Extract RepoEntry and SyncHistoryEntry to types/ to break core to TUI…
manuelgruber May 7, 2026
3afa34e
Rewrite intra-TUI imports to source RepoEntry and SyncHistoryEntry fr…
manuelgruber May 7, 2026
5ef18ff
Drop lib-level cfg gate from pub mod setup
manuelgruber May 7, 2026
2607e6e
Apply cargo fmt to reorder imports introduced in B0.2
manuelgruber May 7, 2026
ac8bebd
Split into Cargo workspace: git-same-core (engine) + git-same-cli (bi…
manuelgruber May 7, 2026
fbcd478
Update CI workflows and toolkit scripts for the workspace layout
manuelgruber May 7, 2026
f35cdf4
Update docs and CLAUDE.md to reflect the workspace split
manuelgruber May 7, 2026
86257ae
Rename CLI crate's package back to git-same to preserve cargo install…
manuelgruber May 7, 2026
4c13543
Move macOS IPC to app-group container and rename badges extension
manuelgruber May 7, 2026
6d24db3
Rename SwiftUI host target from GitSameBadge to GitSameSwiftApp
manuelgruber May 7, 2026
7fd2ae0
Move macOS IPC to app-group container and rename badges extension
manuelgruber May 7, 2026
e1a9eae
Add Tauri macOS app pipeline to ship GUI and Finder badges
manuelgruber May 7, 2026
88a7539
Document Tauri app dev setup and ignore Tauri build artifacts
manuelgruber May 7, 2026
9665be8
Boot Tauri app from Conductor setup/run scripts to cover full stack
manuelgruber May 7, 2026
9aae4f2
Split Tauri app UI into routes and shared store for maintainability
manuelgruber May 8, 2026
7d3727a
Fix Tauri dev launch by cd-ing into app dir instead of using cargo-on…
manuelgruber May 8, 2026
89d8c52
Wire D-App + D-Finder to ship together in 3.1.0 with entitlement pari…
manuelgruber May 8, 2026
994e499
Capitalize Git-Same brand string in Tauri app for consistent product …
manuelgruber May 8, 2026
bf85744
Add Tauri extension status command and first-launch banners for Finde…
manuelgruber May 8, 2026
4f0097c
Ignore non-macOS Tauri icon CLI output to keep working tree clean
manuelgruber May 8, 2026
01a6ea0
Regenerate Tauri app icons in 8-bit RGBA so the native window can launch
manuelgruber May 8, 2026
139fac4
Grant core:event capability to fix blocked listen() in Tauri app
manuelgruber May 8, 2026
e168a25
Rename daemon to monitor and lift run-loop into core
manuelgruber May 8, 2026
15e9ec6
Capitalize Git-Same in display strings, set cask desc, document monitor
manuelgruber May 8, 2026
6e61f88
Rename FinderSync principal class to Principal
manuelgruber May 8, 2026
91ef312
Rename macOS app bundle to Git-Same.app for release consistency
manuelgruber May 8, 2026
89ed58f
Fix Tauri status fallback so dashboard works without monitor
manuelgruber May 8, 2026
fabb5ce
Add shared progress and setup core for GUI reuse
manuelgruber May 8, 2026
804b641
Expand Tauri UI to manage workspaces, badges, and config
manuelgruber May 8, 2026
f92f177
Fix Tauri dev server port collision to prevent wrong UI loading
manuelgruber May 8, 2026
0d3ca6b
Fix Conductor Tauri launch to use workspace dev port
manuelgruber May 8, 2026
29bea06
Add monitor LaunchAgent repair actions to restore Finder badges
manuelgruber May 8, 2026
6196a7f
Simplify brand logo to gradient wordmark in Tauri app UI
manuelgruber May 8, 2026
c6a3289
Pin sidebar so only main content scrolls in Tauri app
manuelgruber May 8, 2026
3c87728
Pair GitHub and local repos in one table to align matching rows
manuelgruber May 8, 2026
2187360
Speed up dev loop by switching run.sh to incremental debug build
manuelgruber May 8, 2026
6c7ca26
Add monitor fullscan interval setting to Tauri Settings UI
manuelgruber May 8, 2026
0218a90
Stop Tauri app from triggering protected-folder TCC prompts
manuelgruber May 8, 2026
ceb37ec
Disable show_ambient by default to fix Finder badges on default install
manuelgruber May 10, 2026
6e9d19a
Bump pnpm to 11.0.9 in Tauri UI to track upstream patch release
manuelgruber May 11, 2026
d6a6386
Remove unused default_true helper to silence dead_code warning
manuelgruber May 11, 2026
4792e24
Watch boot-volume alias paths so Finder requests badges through them
manuelgruber May 11, 2026
c2bf378
Update Tauri UI deps to latest in-range versions
manuelgruber May 11, 2026
cd120d9
Render FinderSync badges via SF Symbols to bypass macOS 26.4 bug
manuelgruber May 11, 2026
e045fec
Tune Finder badge visuals: white-on-color square, blue O/U, 20% darker
manuelgruber May 11, 2026
ea51878
Replace app icon with twin-tiles design and add regen tooling
manuelgruber May 11, 2026
d69cc00
Restyle app icon as Liquid Glass with Finder Badge palette
manuelgruber May 11, 2026
4806524
Shorten BrandLogo subhead so it fits below the sidebar banner
manuelgruber May 11, 2026
51e6703
Reword BrandLogo subhead and center it below the banner
manuelgruber May 11, 2026
d04b17b
Paint workspace roots with Git-Same folder icon for Finder branding
manuelgruber May 11, 2026
171311a
Wire workspace folder icon hooks into Tauri commands and add tests
manuelgruber May 11, 2026
55fde59
Sign monitor helper with app-group entitlement to stop TCC popups
manuelgruber May 13, 2026
49b12d3
Update Cargo
manuelgruber May 14, 2026
20442a5
Update app dependencies and isolate refresh socket tests to keep chec…
manuelgruber May 14, 2026
880cffa
Make Finder badge extension container-only to stop TCC popups
manuelgruber May 31, 2026
dabe7f0
Set package authors to Manuel Gruber for crate metadata
manuelgruber May 31, 2026
7bc010f
Fix bash 3.2 empty-array crash that skipped app signing
manuelgruber May 31, 2026
17b522e
Fix stale Workspace source label and polish paired-table rows
manuelgruber May 31, 2026
f42618b
Update
manuelgruber May 31, 2026
c8f0cdf
Add filter, match-status gutter, and zebra rows to workspace repo table
manuelgruber Jun 11, 2026
fe7ddd7
Bump codecov-action from v6 to v7 to stay on the supported release line
manuelgruber Jun 12, 2026
6a51a85
Update dependencies to latest, migrating notify 8 and objc2 0.6 APIs
manuelgruber Jun 18, 2026
30e1e24
Update remaining dependency patches to keep lockfiles current
manuelgruber Jun 20, 2026
d9cdd2d
Resolve PR #16 review comments: mutex poisoning, socket framing, GHE …
manuelgruber Jun 21, 2026
2acea47
Bump UI deps to newer in-range releases for latest patches
manuelgruber Jun 25, 2026
fa0f926
Bump svelte-check and svelte-spa-router to latest in-range patches
manuelgruber Jun 26, 2026
fbe1633
Address PR #16 review: log Unknown-owner persist failures and pin rel…
manuelgruber Jun 27, 2026
de012c8
Reformat socket_handler test assert to pass rustfmt --check
manuelgruber Jun 27, 2026
bed5c43
Bump FinderSync extension bundle version to 3.1.0 to match workspace
manuelgruber Jun 27, 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
90 changes: 66 additions & 24 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,45 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Build & Test Commands

```bash
cargo build # Debug build
cargo build --release # Optimized release build (LTO, stripped)
cargo test --all-features -- --test-threads=1 # Run CI-equivalent tests
cargo test <test_name> # Run a single test by name
cargo test --test integration_test # Run only integration tests
cargo fmt --all -- --check # Check formatting
cargo clippy --all-targets --all-features -- -D warnings # Lint (zero warnings enforced)
cargo build --workspace # Debug build
cargo build --release --workspace # Optimized release (LTO, stripped)
cargo test --workspace # Run all tests
cargo test -p git-same-core # Tests for the engine crate only
cargo test -p git-same # Tests for the CLI crate only
cargo test --workspace <test_name> # Run a single test by name
cargo test -p git-same --test integration_test # Run only integration tests
cargo fmt --all -- --check # Check formatting
cargo clippy --workspace --all-targets --all-features -- -D warnings # Lint
```

Logging is controlled via `GISA_LOG` env var (e.g., `GISA_LOG=debug cargo run -- sync`).
Logging is controlled via `GISA_LOG` env var (e.g., `GISA_LOG=debug cargo run -p git-same -- 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:** Cargo defines the primary `git-same` binary at `src/main.rs`. Installers create `gitsame`, `gitsa`, and `gisa` symlinks from `toolkit/packaging/binary-aliases.txt`.
**Workspace layout:** the project is a Cargo workspace with two member crates:

- `git-same-core` (`crates/git-same-core/`) — the engine library. No UI dependencies (no clap, ratatui, crossterm). Holds discovery, clone/sync, IPC, status scanning, and shared types.
- `git-same` (lives at `crates/git-same-cli/` on disk; the directory name and package name intentionally diverge so `cargo install git-same` keeps working as it has since pre-3.x) — the CLI binary + TUI. Depends on `git-same-core`. Owns clap parsing, the TUI screens, the setup wizard, and command handlers. The produced binary is named `git-same` (per `[[bin]]` name) so installer aliases (`gisa`, `gitsa`, `gitsame`) and `target/release/git-same` are unchanged from the pre-split layout.

**Binary aliases:** `git-same`, `gitsame`, `gitsa`, `gisa` — all resolve to the binary built from `crates/git-same-cli/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.
**macOS host strategy:** Two macOS host apps coexist. The Tauri host (`crates/git-same-app/`, Svelte + TypeScript + Vite) is the primary GUI shipped via the cask. The SwiftUI host (`macos/GitSameSwiftApp/`) is intentionally kept as a fallback per the Phase C plan's "perfect macOS feel" escape hatch (`.context/plans/phase-c-tauri-app.md` §2). **Do not delete `macos/GitSameSwiftApp/` without explicit approval** — this overrides the migration plan's earlier "delete in Phase C" instruction.

**Commands:** `init`, `setup`, `sync`, `status`, `scan`, `workspace {list,default}`, `reset`.
**CLI flow:** CLI parsing (`crates/git-same-cli/src/cli.rs`) → `main.rs` routes to command handler → handler orchestrates engine modules from `git-same-core`.

### Core modules
**Commands:** `init`, `setup`, `sync`, `status`, `scan`, `workspace {list,default}`, `reset`, `monitor` (alias: `daemon`), `refresh`.

**Why `monitor` is a CLI subcommand and not solely a Tauri-host responsibility:** the LaunchAgent invokes `gisa monitor --foreground`, non-cask installs (`cargo install`, the homebrew formula) ship only the binary, `--status` / `--stop` are the supported debugging surface, and a future Linux file-manager extension would talk to the same `gisa monitor` over the same Unix socket. The CLI handler is a thin shim (~140 lines); the loop itself lives in `git-same-core::monitor`.

### Engine modules (`crates/git-same-core/src/`)

- **`app/`** — Top-level entry points: `app/cli/` runs the CLI subcommand path, `app/tui/` boots the interactive TUI. `main.rs` dispatches to one or the other based on whether a subcommand was given
- **`commands/`** — Per-subcommand handlers (`init`, `setup`, `sync_cmd`, `status`, `scan`, `reset`, `workspace`) plus shared `support/` helpers
- **`workflows/`** — Cross-cutting orchestration shared by CLI and TUI: `sync_workspace` (discover + clone + fetch/pull) and `status_scan` (walk local repos, collect git status)
- **`auth/`** — `gh_cli.rs` obtains GitHub API tokens via `gh auth token`. `ssh.rs` exposes low-level SSH probing primitives (`SshProbeResult`, `parse_ssh_probe_output`) used by clone-time diagnostics
- **`workflows/`** — Cross-cutting orchestration: `sync_workspace` (discover + clone + fetch/pull) and `status_scan` (walk local repos, collect git status)
- **`monitor/`**: Long-running monitor loop (periodic scan + Unix-socket server) used by `gisa monitor` and reusable by host apps like the Tauri GUI
- **`config/`** — TOML config parser. Default: `~/.config/git-same/config.toml`. Top-level keys: `workspaces`, `default_workspace`, plus `[clone]` and `[filters]` sections
- **`discovery.rs`** — `DiscoveryOrchestrator` coordinates repo discovery via providers, applies filters, builds `ActionPlan` (what to clone vs sync)
- **`operations/clone.rs`** — `CloneManager` handles concurrent cloning (configurable 1–32, default 4)
Expand All @@ -45,14 +55,24 @@ Git-Same is a Rust CLI + TUI tool that discovers GitHub org/repo structures and
- **`cache/`** — `discovery.rs` provides `DiscoveryCache` (TTL-based validity, persisted at `<workspace-root>/.git-same/cache.json`); `sync_history.rs` records sync runs at `<workspace-root>/.git-same/sync-history.json`
- **`domain/`** — Domain primitives, currently `repo_path_template.rs` for resolving `{org}/{repo}` style structures
- **`infra/storage/`** — Storage abstractions for workspace-local persistence
- **`setup/`** — Setup wizard state machine, shared between the CLI `setup` command and the TUI workspace-setup screen
- **`ipc/`** — Monitor ↔ Finder-extension interface (`status_file.rs`, `unix_socket.rs`)
- **`api/`** — Higher-level service helpers built on top of git/provider/config (e.g. `RepoScanService`)
- **`errors/`** — Custom error hierarchy: `AppError`, `GitError`, `ProviderError` with `suggested_action()` methods
- **`output/`** — `printer.rs` for verbosity-aware text output; `progress/` holds the `indicatif` progress bars (`CloneProgressBar`, `SyncProgressBar`, `DiscoveryProgressBar`)
- **`types/repo.rs`** — Core data types: `Repo`, `Org`, `ActionPlan`, `OpResult`, `OpSummary`
- **`types/`** — Core data types: `Repo`, `Org`, `ActionPlan`, `OpResult`, `OpSummary`, plus `RepoEntry`/`SyncHistoryEntry` (lifted out of the TUI in B0.1)
- **`checks.rs`** — System/runtime checks (presence of `git`, `gh`, auth status, SSH access via `check_ssh_github_access`)

### CLI / TUI modules (`crates/git-same-cli/src/`)

- **`app/`** — Top-level entry points: `app/cli/` runs the CLI subcommand path, `app/tui/` boots the interactive TUI. `main.rs` dispatches to one or the other based on whether a subcommand was given
- **`commands/`** — Per-subcommand handlers (`init`, `setup`, `sync_cmd`, `status`, `scan`, `reset`, `workspace`, `monitor`, `refresh`) plus shared `support/` helpers
- **`setup/`** — Setup wizard state machine + ratatui rendering, shared between the CLI `setup` command and the TUI workspace-setup screen (gated by the `tui` feature)
- **`tui/`** — Ratatui-based TUI (gated by the `tui` feature)
- **`cli.rs`** — clap derive types
- **`banner.rs`** — CLI banner rendering
- **`bin/gen_completions.rs`, `bin/gen_manpage.rs`** — Release-only helpers gated by the `release-tools` feature

### TUI module (`src/tui/`, feature-gated behind `tui`)
### TUI module (`crates/git-same-cli/src/tui/`, feature-gated behind `tui`)

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

Expand All @@ -71,6 +91,26 @@ Elm architecture: `app.rs` = Model, `screens/` = View, `handler.rs` = Update.
- **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`.

## FinderSync extension gotchas (macOS)

Three non-obvious traps in `macos/GitSameBadges/`. Each one silently breaks badges with no error log — the extension self-check still shows green.

1. **Boot-volume alias paths.** macOS auto-creates `/Volumes/<boot-volume-name>` as a symlink to `/`. Finder presents home-folder URLs with that prefix (`/Volumes/Manuel-SSD-4TB/Users/m/...`) and gates `requestBadgeIdentifier` on the URL matching an entry in `directoryURLs`. `Principal.updateMonitoredDirectories()` must register both the canonical and the alias-prefixed form of every watched root, otherwise the callback fires for nothing.

2. **macOS 26.4 sandbox rendering regression.** Both `NSImage.lockFocus()` and `NSImage(size:flipped:drawingHandler:)` produce empty/invalid pixel data when called inside a sandboxed FinderSync extension on 26.4. Symptom: Finder reserves the badge slot (folder icons shift) but no glyph renders. Workaround: build badges from SF Symbols (`NSImage(systemSymbolName:)` with palette `SymbolConfiguration`). SF Symbols are pre-rendered by macOS, no per-process drawing context required. Apple's own `r.circle.fill`/`o.circle.fill`/`u.circle.fill` are what `BadgeManager.symbolBadge` uses.

3. **Google Drive's FinderSync poisons the badge-rendering pipeline.** When `com.google.drivefs.finderhelper.findersync` is enabled, peer FinderSync extensions render no badge image even after Finder calls `setBadgeIdentifier`. Confirmed in this environment: badges only began appearing after the user disabled Google Drive in System Settings → Login Items & Extensions. Other peers (Keka, Synology, Dropbox) coexist fine. There is no code fix; document the workaround and surface it in the in-app self-check if you can.

`scan_roots` and `show_ambient`: defaults are `["~"]` / `false`. Never re-enable `show_ambient = true` with `~` in `scan_roots` — Finder refuses to call `requestBadgeIdentifier` on extensions whose `directoryURLs` contain the home folder (separate issue from the three above).

## Workspace folder branding (macOS)

The host paints a custom icon onto every workspace root via `NSWorkspace.setIcon` (wrapped in `crates/git-same-core/src/macos/folder_icon.rs`) so Finder shows it in the sidebar, column, list, icon, and Get Info views. A FinderSync extension can never replicate this — it only exposes corner badges. The icon is `crates/git-same-core/assets/workspace-folder.icns`, embedded via `include_bytes!` and regenerable via `bash toolkit/icons/build-workspace-folder-icns.sh`.

Lifecycle: painted by `core::setup::save_workspace` and `app::commands::save_workspace`, reapplied by the monitor (`monitor::run::reapply_workspace_folder_icons`) on every full scan if the `Icon\r` is missing, and stripped by `cli::commands::reset` and `app::commands::delete_workspace`. Opt out globally with `[ui] custom_folder_icon = false`.

**Finder Sidebar snapshot caveat.** `LSSharedFileList` captures a per-item icon bitmap into `~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.FavoriteItems.sfl3` at the moment the user drags a folder into Favorites. That snapshot is frozen — repainting the folder's `Icon\r` does **not** update the sidebar. The only refresh path is manual: right-click the stale sidebar item → Remove from Sidebar, then drag the folder back from a Finder window into Favorites. Don't waste time looking for a programmatic refresh API; the framework doesn't expose one, and the recommended workaround used by Synology / Dropbox is the same drag-and-drop.

## Formatting

`rustfmt.toml`: `max_width = 100`, `tab_spaces = 4`, edition 2021.
Expand All @@ -90,20 +130,22 @@ The test file contains `use super::*;` and all `#[test]` / `#[tokio::test]` func

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

**Integration tests** remain in `tests/integration_test.rs`.
**Integration tests** live in `crates/git-same-cli/tests/integration_test.rs`. They spawn the binary via `env!("CARGO_BIN_EXE_git-same")` (compile-time path), so they always run against the freshly built CLI binary at the workspace `target/`.

**Cross-crate test helpers:** `Repo::test()` in `git-same-core` is gated on `cfg(any(test, feature = "test-utils"))`. The CLI crate enables the `test-utils` feature in its `[dev-dependencies]` so its tests can call the helper without exposing it in production builds.

## CI/CD Workflows

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

| Workflow | Purpose | Trigger |
|----------|---------|---------|
| `S1-Test-CI.yml` | fmt, clippy, tests, release build dry-run, coverage, alias drift, workflow secret-safety, audit | Manual dispatch |
| `S2-Release-GitHub.yml` | Gated GitHub release assets for targets in `toolkit/packaging/targets.txt` (currently 4 targets) | Manual dispatch (select tag) |
| `S3-Publish-Homebrew.yml` | Publish Homebrew cask + formula-cli | Manual dispatch (select tag) |
| `S4-Publish-Crates.yml` | Publish crates.io package | Manual dispatch (select tag) |
| `S1-Test-CI.yml` | fmt, clippy, test, build dry-run, coverage, audit | Manual dispatch |
| `S2-Release-GitHub.yml` | Full CI + cross-compile 4 targets (per `toolkit/packaging/targets.txt`) + GitHub Release | Manual dispatch (select tag) |
| `S3-Publish-Homebrew.yml` | Download release tarballs and render `git-same-cli` formula + `git-same` cask templates into `zaai-com/homebrew-tap` | Manual dispatch (select tag) |
| `S4-Publish-Crates.yml` | Two-stage publish to crates.io: `git-same-core` → poll until indexed → `git-same` | Manual dispatch (select tag) |

S2 gates release asset builds on tests, coverage, alias drift, audit, and workflow secret-safety checks.
S2 runs all S1 jobs (test, coverage, audit) as gates before building release artifacts.

## Specs & Docs

Expand Down
46 changes: 41 additions & 5 deletions .github/workflows/S1-Test-CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ jobs:
run: cargo +${{ matrix.rust }} fmt --all -- --check

- name: Clippy
run: cargo +${{ matrix.rust }} clippy --all-targets --all-features -- -D warnings
run: cargo +${{ matrix.rust }} clippy --workspace --all-targets --all-features -- -D warnings

- name: Run tests
run: cargo +${{ matrix.rust }} test --all-features -- --test-threads=1
run: cargo +${{ matrix.rust }} test --workspace --all-features -- --test-threads=1

build:
name: Build (${{ matrix.target }})
Expand Down Expand Up @@ -81,7 +81,43 @@ jobs:
prefix-key: v1-rust-no-bin

- name: Build release
run: cargo +stable build --release --target ${{ matrix.target }}
run: cargo +stable build --release -p git-same --target ${{ matrix.target }}

tauri-debug-build:
name: Tauri App Debug Build
needs: [test]
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false

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

- uses: Swatinem/rust-cache@v2

- name: Install Node
uses: actions/setup-node@v6
with:
node-version: 24

- name: Enable pnpm
run: corepack enable pnpm

- name: Verify entitlements parity
run: bash toolkit/packaging/macos/check-entitlements-parity.sh

- name: Install frontend dependencies
run: pnpm --dir crates/git-same-app/ui install --frozen-lockfile

- name: Build frontend
run: pnpm --dir crates/git-same-app/ui build

- name: Build Tauri app binary
run: |
cd crates/git-same-app
ui/node_modules/.bin/tauri build --debug --no-bundle

coverage:
name: Code Coverage
Expand Down Expand Up @@ -114,7 +150,7 @@ jobs:
run: cargo +stable tarpaulin --all-features --workspace --timeout 120 --out xml --engine llvm

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@v7
with:
use_oidc: true
fail_ci_if_error: false
Expand Down Expand Up @@ -161,7 +197,7 @@ jobs:
}
in_bin && /^[[:space:]]*required-features[[:space:]]*=/ { has_req=1 }
END { flush(); print default_count, default_name }
' Cargo.toml)"
' crates/git-same-cli/Cargo.toml)"
if [ "${DEFAULT_COUNT:-0}" -ne 1 ]; then
echo "ERROR: Cargo.toml has ${DEFAULT_COUNT:-0} default [[bin]] entries, expected 1"; exit 1
fi
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/S2-Release-GitHub.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ jobs:
run: cargo +${{ matrix.rust }} fmt --all -- --check

- name: Clippy
run: cargo +${{ matrix.rust }} clippy --all-targets --all-features -- -D warnings
run: cargo +${{ matrix.rust }} clippy --workspace --all-targets --all-features -- -D warnings

- name: Run tests
run: cargo +${{ matrix.rust }} test --all-features -- --test-threads=1
run: cargo +${{ matrix.rust }} test --workspace --all-features -- --test-threads=1

coverage:
name: Code Coverage
Expand Down Expand Up @@ -76,7 +76,7 @@ jobs:
run: cargo +stable tarpaulin --all-features --workspace --timeout 120 --out xml --engine llvm

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@v7
with:
use_oidc: true
fail_ci_if_error: false
Expand Down Expand Up @@ -115,7 +115,7 @@ jobs:
}
in_bin && /^[[:space:]]*required-features[[:space:]]*=/ { has_req=1 }
END { flush(); print default_count, default_name }
' Cargo.toml)"
' crates/git-same-cli/Cargo.toml)"
if [ "${DEFAULT_COUNT:-0}" -ne 1 ]; then
echo "ERROR: Cargo.toml has ${DEFAULT_COUNT:-0} default [[bin]] entries, expected 1"; exit 1
fi
Expand Down Expand Up @@ -267,7 +267,7 @@ jobs:
prefix-key: v1-rust-no-bin

- name: Build
run: cargo +stable build --release --target ${{ matrix.target }}
run: cargo +stable build --release -p git-same --target ${{ matrix.target }}

- name: Resolve version from tag
if: startsWith(github.ref, 'refs/tags/')
Expand Down Expand Up @@ -388,7 +388,7 @@ jobs:
find artifacts -type f -exec cp {} release-assets/ \;

- name: Create/update release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with:
files: release-assets/*
env:
Expand Down
Loading