- LICENSE (MIT) + README truth-pass; docs/cli.md CLI contract - CLI subcommands (src/cli_cmds/) per "TUI for users, CLI for users+AI" - Hub/detail refactor: keymap dispatch, modal split, action enum, BgMsg bus - Fix: org repos were never refreshed (owner==username match); refresh now parallel — 49 repos in ~11s, surfaced 70 previously invisible PRs - Fix: 3 date bugs in detail/git.rs (format-local, Local::now, 1y month math) - Stash indicator + [m] actions, session persistence, gh CLI token fallback - Legacy UI modules removed; plan/ docs (roadmap, audit, debate, handoff)
283 lines
8.6 KiB
Markdown
283 lines
8.6 KiB
Markdown
# vulp — strategia cross-platform
|
||
|
||
> Cel: jedna binarka, trzy systemy, instalacja w 30 sekund, zero wiedzy technicznej wymaganej.
|
||
|
||
---
|
||
|
||
## Stan obecny
|
||
|
||
Rust kompiluje się do natywnych binarek bez zewnętrznych zależności runtime. To dobra podstawa — nie trzeba instalować Pythona, Node.js ani JVM. Ale dziś jedynym sposobem instalacji jest `cargo install --path .`, co wymaga zainstalowanego Rusta i środowiska build. Dla grafika — nierealne.
|
||
|
||
Obecny problem z `keyring`: na Linuksie wymaga `libdbus` lub `libsecret`. Do rozwiązania przy pakowaniu.
|
||
|
||
---
|
||
|
||
## Cel UX instalacji
|
||
|
||
**Windows:** Pobierasz plik `.exe`. Klikasz go dwukrotnie. Otwiera się terminal z konfiguratorem. Po 2 minutach piszesz `vulp status` i to działa.
|
||
|
||
**Linux:** Wklejasz jedną linię w terminal. Po 10 sekundach piszesz `vulp status` i to działa.
|
||
|
||
**macOS:** `brew install`. Jedna komenda.
|
||
|
||
---
|
||
|
||
## Strategia dystrybucji — GitHub Releases + CI
|
||
|
||
### GitHub Actions — automatyczne buildowanie binarek
|
||
|
||
Przy każdym tagu (`v*.*.*`) CI buduje binarkę na 4 targety:
|
||
|
||
```yaml
|
||
# .github/workflows/release.yml
|
||
strategy:
|
||
matrix:
|
||
include:
|
||
- os: ubuntu-latest
|
||
target: x86_64-unknown-linux-musl # statyczna binarka — działa na każdym Linuksie
|
||
artifact: vulp-linux-x64
|
||
- os: ubuntu-latest
|
||
target: aarch64-unknown-linux-musl # ARM (np. Raspberry Pi, nowe serwery)
|
||
artifact: vulp-linux-arm64
|
||
- os: windows-latest
|
||
target: x86_64-pc-windows-msvc
|
||
artifact: vulp-windows-x64.exe
|
||
- os: macos-latest
|
||
target: x86_64-apple-darwin
|
||
artifact: vulp-macos-x64
|
||
- os: macos-latest
|
||
target: aarch64-apple-darwin # Apple Silicon (M1/M2/M3)
|
||
artifact: vulp-macos-arm64
|
||
```
|
||
|
||
> Używamy `x86_64-unknown-linux-musl` zamiast `gnu` — binarka statyczna, nie wymaga żadnych systemowych `.so`. Keyring na Linuksie fallback do pliku szyfrowanego (jeśli brak dbus).
|
||
|
||
Pliki lądują w GitHub Releases jako Assets. Użytkownik pobiera odpowiedni plik dla swojego systemu.
|
||
|
||
---
|
||
|
||
## Linux — instalacja
|
||
|
||
### Skrypt jednolinijkowy (docelowy UX)
|
||
|
||
```bash
|
||
curl -fsSL https://github.com/twoj-user/vulp/releases/latest/download/install.sh | bash
|
||
```
|
||
|
||
Skrypt robi:
|
||
1. Wykrywa architekturę (`uname -m`)
|
||
2. Pobiera odpowiednią binarkę z GitHub Releases
|
||
3. Umieszcza ją w `~/.local/bin/vulp`
|
||
4. Dodaje `~/.local/bin` do `$PATH` w `.bashrc`/`.zshrc` jeśli jeszcze nie ma
|
||
5. Jeśli brak konfiguracji → uruchamia `vulp setup` (first-run wizard)
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
set -e
|
||
ARCH=$(uname -m)
|
||
OS=linux
|
||
case $ARCH in
|
||
x86_64) FILE="vulp-linux-x64" ;;
|
||
aarch64) FILE="vulp-linux-arm64" ;;
|
||
*) echo "Nieobsługiwana architektura: $ARCH"; exit 1 ;;
|
||
esac
|
||
|
||
LATEST=$(curl -s https://api.github.com/repos/twoj-user/vulp/releases/latest | grep tag_name | cut -d '"' -f 4)
|
||
URL="https://github.com/twoj-user/vulp/releases/download/${LATEST}/${FILE}"
|
||
|
||
mkdir -p ~/.local/bin
|
||
curl -fsSL "$URL" -o ~/.local/bin/vulp
|
||
chmod +x ~/.local/bin/vulp
|
||
|
||
# Dodaj do PATH jeśli brak
|
||
if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then
|
||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||
export PATH="$HOME/.local/bin:$PATH"
|
||
fi
|
||
|
||
echo "vulp zainstalowany. Uruchamianie konfiguracji..."
|
||
vulp setup
|
||
```
|
||
|
||
### Opcjonalnie: pakiety .deb i .rpm
|
||
|
||
Generowane przez `cargo-deb` i `cargo-rpm` w CI — przydatne dla systemów zarządzanych przez IT.
|
||
|
||
```bash
|
||
# Ubuntu/Debian
|
||
sudo dpkg -i vulp_0.1.0_amd64.deb
|
||
|
||
# Fedora/RHEL/CentOS
|
||
sudo rpm -i vulp-0.1.0.x86_64.rpm
|
||
```
|
||
|
||
---
|
||
|
||
## Windows — instalacja
|
||
|
||
### Opcja A: Kliknięcie `.exe` (docelowy UX dla grafika)
|
||
|
||
Gdy użytkownik dwukrotnie kliknie `vulp-windows-x64.exe` bez żadnych argumentów i brak konfiguracji, zamiast wyświetlić help i zamknąć okno — **otwiera PowerShell z konfiguratorem**.
|
||
|
||
Implementacja w `src/main.rs`:
|
||
```rust
|
||
#[cfg(windows)]
|
||
fn ensure_console() {
|
||
// Jeśli uruchomiono przez dwuklik (nie z terminala), otwórz nowe okno konsoli
|
||
// i uruchom vulp setup w nim
|
||
use std::os::windows::process::CommandExt;
|
||
if !is_launched_from_terminal() {
|
||
std::process::Command::new("cmd")
|
||
.args(["/k", "vulp.exe", "setup"])
|
||
.creation_flags(0x00000010) // CREATE_NEW_CONSOLE
|
||
.spawn().ok();
|
||
std::process::exit(0);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Opcja B: Skrypt PowerShell (instalacja systemowa)
|
||
|
||
```powershell
|
||
# Wklej w PowerShell jako Administrator:
|
||
iwr https://github.com/twoj-user/vulp/releases/latest/download/install.ps1 | iex
|
||
```
|
||
|
||
Skrypt:
|
||
1. Pobiera `vulp-windows-x64.exe`
|
||
2. Umieszcza w `%USERPROFILE%\AppData\Local\vulp\`
|
||
3. Dodaje ten folder do `$PATH` (przez `[Environment]::SetEnvironmentVariable`)
|
||
4. Uruchamia `vulp setup` w nowym oknie terminalu
|
||
|
||
### Opcja C: WinGet (Microsoft Store CLI) — przyszłość
|
||
|
||
```
|
||
winget install vulp
|
||
```
|
||
|
||
Wymaga zgłoszenia pakietu do repozytorium `winget-pkgs`.
|
||
|
||
---
|
||
|
||
## macOS — instalacja
|
||
|
||
### Homebrew (docelowy UX)
|
||
|
||
```bash
|
||
brew install twoj-user/tap/vulp
|
||
```
|
||
|
||
Wymaga stworzenia własnego "tap" (repozytorium z formularzem Homebrew):
|
||
|
||
```ruby
|
||
# Formula/vulp.rb
|
||
class Vulp < Formula
|
||
desc "Multi-repository git workflow manager"
|
||
homepage "https://github.com/twoj-user/vulp"
|
||
version "0.1.0"
|
||
|
||
on_macos do
|
||
if Hardware::CPU.arm?
|
||
url "https://github.com/twoj-user/vulp/releases/download/v0.1.0/vulp-macos-arm64"
|
||
sha256 "..."
|
||
else
|
||
url "https://github.com/twoj-user/vulp/releases/download/v0.1.0/vulp-macos-x64"
|
||
sha256 "..."
|
||
end
|
||
end
|
||
|
||
def install
|
||
bin.install "vulp-macos-arm64" => "vulp"
|
||
end
|
||
end
|
||
```
|
||
|
||
---
|
||
|
||
## First-run wizard
|
||
|
||
Kluczowy element UX. Gdy użytkownik uruchamia **dowolną komendę** po raz pierwszy (brak `~/.vulp/config.toml`), zamiast error'a dostaje automatyczny setup.
|
||
|
||
Implementacja w `src/app.rs`, przed każdym `match cli.command`:
|
||
|
||
```rust
|
||
// src/app.rs
|
||
pub async fn run(cli: Cli) -> Result<()> {
|
||
// Jeśli brak konfiguracji → uruchom first-run setup
|
||
if !config::config_path().exists() {
|
||
println!("Wygląda na to, że uruchamiasz vulp po raz pierwszy.");
|
||
println!("Przeprowadźmy krótką konfigurację (zajmie ~2 minuty).\n");
|
||
first_run_setup().await?;
|
||
}
|
||
|
||
let cfg = config::load_config()?;
|
||
// ... reszta
|
||
}
|
||
|
||
async fn first_run_setup() -> Result<()> {
|
||
// 1. Uruchom config TUI
|
||
let cfg = config::run_first_time().await?;
|
||
// 2. Zapytaj o dodanie konta
|
||
if inquire::Confirm::new("Czy chcesz teraz podłączyć konto GitHub/GitLab?")
|
||
.with_default(true).prompt()? {
|
||
// Uruchom auth add wizard
|
||
auth::run_add_wizard(&cfg).await?;
|
||
}
|
||
println!("\nGotowe! Wpisz 'vulp status' żeby zobaczyć swoje projekty.");
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Systemowe zależności — problemy i rozwiązania
|
||
|
||
| Zależność | Problem | Rozwiązanie |
|
||
|---|---|---|
|
||
| `libgit2` | Wymagana na Linuksie przy dynamicznym linkowaniu | Używamy `git2` z feature `vendored` — kompiluje libgit2 statycznie |
|
||
| `keyring` / `libsecret` | Wymaga `dbus` na Linuksie headless | Fallback do zaszyfrowanego pliku `~/.vulp/credentials.enc` gdy brak dbus |
|
||
| `openssl` | Różne wersje na różnych systemach | Feature `native-tls` → `rustls` — TLS w całości wbudowany w Rust |
|
||
|
||
```toml
|
||
# Cargo.toml — docelowa konfiguracja dla statycznych binarek
|
||
[dependencies]
|
||
git2 = { version = "0.19", features = ["vendored"] }
|
||
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
|
||
keyring = { version = "3", features = ["sync-secret-service"] }
|
||
```
|
||
|
||
---
|
||
|
||
## O nazwie
|
||
|
||
Aktualna nazwa `vulp` ("finally executable git") to placeholder — dobra jako codename, ale przed publicznym wydaniem warto rozważyć zmianę.
|
||
|
||
Kryteria dobrej nazwy narzędzia CLI:
|
||
- Krótka (2–4 znaki idealne, max 6)
|
||
- Łatwa do wymówienia i zapamiętania
|
||
- Nie koliduje z istniejącymi komendami systemowymi
|
||
- Nawiązuje do funkcji (opcjonalnie)
|
||
|
||
Kilka luźnych kierunków (bez rekomendacji — ostateczna decyzja należy do Ciebie):
|
||
- Coś od "workspace" / "repos" / "git"
|
||
- Coś sugerującego "wielość" lub "hub"
|
||
- Akronim który znaczy coś po polsku, a po angielsku brzmi jak komenda
|
||
|
||
Niezależnie od wyboru nazwy — warto go podjąć przed stworzeniem Homebrew tap i pierwszym publicznym Release, bo zmiana nazwy binarki po publikacji jest uciążliwa.
|
||
|
||
---
|
||
|
||
## Roadmap dystrybucji
|
||
|
||
```
|
||
v0.1 (teraz) cargo install — tylko dla developerów
|
||
v0.2 GitHub Releases z binaries (Linux + Windows + macOS)
|
||
Skrypt instalacyjny curl | bash
|
||
v0.3 First-run wizard (bez potrzeby wpisywania komend setup)
|
||
PowerShell install script dla Windows
|
||
v0.4 Homebrew tap dla macOS
|
||
v1.0 Stabilny API, binarka klikalna na Windows
|
||
WinGet (opcjonalnie)
|
||
Pakiety .deb/.rpm (opcjonalnie)
|
||
```
|