Initial commit: gtea CLI tool

Go CLI that wraps the Gitea API and git to create repos, commit,
push, and pull without leaving the terminal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 14:11:34 -04:00
commit b7ea167786
11 changed files with 404 additions and 0 deletions

103
cmd/root.go Normal file
View File

@@ -0,0 +1,103 @@
package cmd
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/spf13/cobra"
)
// Config holds Gitea connection details stored in ~/.gtea.json
type Config struct {
URL string `json:"url"`
Token string `json:"token"`
Username string `json:"username"`
}
var rootCmd = &cobra.Command{
Use: "gtea",
Short: "Gitea project and repo manager",
Long: "gtea — create Gitea repos, commit, push, and pull without leaving the terminal.",
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
func init() {
rootCmd.AddCommand(configCmd)
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(commitCmd)
rootCmd.AddCommand(pushCmd)
rootCmd.AddCommand(pullCmd)
rootCmd.AddCommand(statusCmd)
}
func configPath() string {
home, _ := os.UserHomeDir()
return filepath.Join(home, ".gtea.json")
}
func loadConfig() (*Config, error) {
data, err := os.ReadFile(configPath())
if err != nil {
return nil, fmt.Errorf("no config found — run 'gtea config' first")
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("invalid config: %w", err)
}
if cfg.URL == "" || cfg.Token == "" || cfg.Username == "" {
return nil, fmt.Errorf("incomplete config — run 'gtea config' to fill in missing fields")
}
return &cfg, nil
}
func saveConfig(cfg *Config) error {
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
return os.WriteFile(configPath(), data, 0600)
}
// runGit runs a git command in dir (use "." for current directory).
func runGit(dir string, args ...string) error {
c := exec.Command("git", args...)
c.Dir = dir
c.Stdout = os.Stdout
c.Stderr = os.Stderr
if err := c.Run(); err != nil {
return fmt.Errorf("git %s: %w", strings.Join(args, " "), err)
}
return nil
}
// currentBranch returns the name of the currently checked-out branch.
func currentBranch() (string, error) {
out, err := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
if err != nil {
return "", err
}
return string(bytes.TrimSpace(out)), nil
}
// authCloneURL injects token credentials into an HTTPS clone URL.
func authCloneURL(cfg *Config, cloneURL string) string {
if strings.HasPrefix(cloneURL, "https://") {
return strings.Replace(
cloneURL,
"https://",
fmt.Sprintf("https://%s:%s@", cfg.Username, cfg.Token),
1,
)
}
return cloneURL
}