Adds shell integration for automatic Gas Town context detection. Features: - `gt enable` / `gt disable` - Global on/off switch - `gt shell install|remove|status` - Shell integration management - `gt rig quick-add [path]` - One-command project setup - `gt uninstall` - Clean removal with options - Shell hook auto-sets GT_TOWN_ROOT/GT_RIG on cd Implementation: - XDG-compliant state storage (~/.local/state/gastown/) - Safe RC file manipulation with block markers - Environment overrides (GASTOWN_DISABLED/ENABLED) - Doctor check for global state validation Co-authored-by: Sohail Mohammad <sohailm25@gmail.com>
187 lines
5.0 KiB
Go
187 lines
5.0 KiB
Go
// ABOUTME: Quick-add command for adding a repo to Gas Town with minimal friction.
|
|
// ABOUTME: Used by shell hook for automatic "add to Gas Town?" prompts.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/steveyegge/gastown/internal/style"
|
|
"github.com/steveyegge/gastown/internal/workspace"
|
|
)
|
|
|
|
var (
|
|
quickAddUser string
|
|
quickAddYes bool
|
|
quickAddQuiet bool
|
|
)
|
|
|
|
var rigQuickAddCmd = &cobra.Command{
|
|
Use: "quick-add [path]",
|
|
Short: "Quickly add current repo to Gas Town",
|
|
Hidden: true,
|
|
Long: `Quickly add a git repository to Gas Town with minimal interaction.
|
|
|
|
This command is designed for the shell hook's "Add to Gas Town?" prompt.
|
|
It infers the rig name from the directory and git URL from the remote.
|
|
|
|
Examples:
|
|
gt rig quick-add # Add current directory
|
|
gt rig quick-add ~/Repos/myproject # Add specific path
|
|
gt rig quick-add --yes # Non-interactive`,
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: runRigQuickAdd,
|
|
}
|
|
|
|
func init() {
|
|
rigCmd.AddCommand(rigQuickAddCmd)
|
|
rigQuickAddCmd.Flags().StringVar(&quickAddUser, "user", "", "Crew workspace name (default: $USER)")
|
|
rigQuickAddCmd.Flags().BoolVar(&quickAddYes, "yes", false, "Non-interactive, assume yes")
|
|
rigQuickAddCmd.Flags().BoolVar(&quickAddQuiet, "quiet", false, "Minimal output")
|
|
}
|
|
|
|
func runRigQuickAdd(cmd *cobra.Command, args []string) error {
|
|
targetPath := "."
|
|
if len(args) > 0 {
|
|
targetPath = args[0]
|
|
}
|
|
|
|
absPath, err := filepath.Abs(targetPath)
|
|
if err != nil {
|
|
return fmt.Errorf("resolving path: %w", err)
|
|
}
|
|
|
|
if townRoot, err := workspace.Find(absPath); err == nil && townRoot != "" {
|
|
return fmt.Errorf("already part of a Gas Town workspace: %s", townRoot)
|
|
}
|
|
|
|
gitRoot, err := findGitRoot(absPath)
|
|
if err != nil {
|
|
return fmt.Errorf("not a git repository: %w", err)
|
|
}
|
|
|
|
gitURL, err := findGitRemoteURL(gitRoot)
|
|
if err != nil {
|
|
return fmt.Errorf("no git remote found: %w", err)
|
|
}
|
|
|
|
rigName := sanitizeRigName(filepath.Base(gitRoot))
|
|
|
|
townRoot, err := findOrCreateTown()
|
|
if err != nil {
|
|
return fmt.Errorf("finding Gas Town: %w", err)
|
|
}
|
|
|
|
rigPath := filepath.Join(townRoot, rigName)
|
|
if _, err := os.Stat(rigPath); err == nil {
|
|
return fmt.Errorf("rig %q already exists in %s", rigName, townRoot)
|
|
}
|
|
|
|
originalName := filepath.Base(gitRoot)
|
|
if rigName != originalName && !quickAddQuiet {
|
|
fmt.Printf("Note: Using %q as rig name (sanitized from %q)\n", rigName, originalName)
|
|
}
|
|
|
|
if !quickAddQuiet {
|
|
fmt.Printf("Adding %s to Gas Town...\n", style.Bold.Render(rigName))
|
|
fmt.Printf(" Repository: %s\n", gitURL)
|
|
fmt.Printf(" Town: %s\n", townRoot)
|
|
}
|
|
|
|
addArgs := []string{"rig", "add", rigName, gitURL}
|
|
addCmd := exec.Command("gt", addArgs...)
|
|
addCmd.Dir = townRoot
|
|
addCmd.Stdout = os.Stdout
|
|
addCmd.Stderr = os.Stderr
|
|
if err := addCmd.Run(); err != nil {
|
|
fmt.Printf("\n%s Failed to add rig. You can try manually:\n", style.Warning.Render("⚠"))
|
|
fmt.Printf(" cd %s && gt rig add %s %s\n", townRoot, rigName, gitURL)
|
|
return fmt.Errorf("gt rig add failed: %w", err)
|
|
}
|
|
|
|
user := quickAddUser
|
|
if user == "" {
|
|
user = os.Getenv("USER")
|
|
}
|
|
if user == "" {
|
|
user = "default"
|
|
}
|
|
|
|
if !quickAddQuiet {
|
|
fmt.Printf("\nCreating crew workspace for %s...\n", user)
|
|
}
|
|
|
|
crewArgs := []string{"crew", "add", user, "--rig", rigName}
|
|
crewCmd := exec.Command("gt", crewArgs...)
|
|
crewCmd.Dir = filepath.Join(townRoot, rigName)
|
|
crewCmd.Stdout = os.Stdout
|
|
crewCmd.Stderr = os.Stderr
|
|
if err := crewCmd.Run(); err != nil {
|
|
fmt.Printf(" %s Could not create crew workspace: %v\n", style.Dim.Render("⚠"), err)
|
|
fmt.Printf(" Run manually: cd %s && gt crew add %s --rig %s\n", filepath.Join(townRoot, rigName), user, rigName)
|
|
}
|
|
|
|
crewPath := filepath.Join(townRoot, rigName, "crew", user)
|
|
if !quickAddQuiet {
|
|
fmt.Printf("\n%s Added to Gas Town!\n", style.Success.Render("✓"))
|
|
fmt.Printf("\nYour workspace: %s\n", style.Bold.Render(crewPath))
|
|
}
|
|
|
|
fmt.Printf("GT_CREW_PATH=%s\n", crewPath)
|
|
|
|
return nil
|
|
}
|
|
|
|
func findGitRoot(path string) (string, error) {
|
|
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
|
cmd.Dir = path
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(string(out)), nil
|
|
}
|
|
|
|
func findGitRemoteURL(gitRoot string) (string, error) {
|
|
cmd := exec.Command("git", "remote", "get-url", "origin")
|
|
cmd.Dir = gitRoot
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(string(out)), nil
|
|
}
|
|
|
|
func sanitizeRigName(name string) string {
|
|
name = strings.ReplaceAll(name, "-", "_")
|
|
name = strings.ReplaceAll(name, ".", "_")
|
|
name = strings.ReplaceAll(name, " ", "_")
|
|
return name
|
|
}
|
|
|
|
func findOrCreateTown() (string, error) {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
candidates := []string{
|
|
filepath.Join(home, "gt"),
|
|
filepath.Join(home, "gastown"),
|
|
}
|
|
|
|
for _, path := range candidates {
|
|
mayorDir := filepath.Join(path, "mayor")
|
|
if _, err := os.Stat(mayorDir); err == nil {
|
|
return path, nil
|
|
}
|
|
}
|
|
|
|
return "", fmt.Errorf("no Gas Town found - run 'gt install ~/gt' first")
|
|
}
|