2025-12-08 09:49:07 +01:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
2025-12-16 12:38:19 +01:00
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
2025-12-08 09:49:07 +01:00
|
|
|
"text/tabwriter"
|
|
|
|
|
|
|
|
|
|
"code.gitea.io/sdk/gitea"
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
"codeberg.org/romaintb/fgj/internal/api"
|
|
|
|
|
"codeberg.org/romaintb/fgj/internal/config"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var repoCmd = &cobra.Command{
|
|
|
|
|
Use: "repo",
|
|
|
|
|
Short: "Manage repositories",
|
|
|
|
|
Long: "View and manage repositories.",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var repoViewCmd = &cobra.Command{
|
|
|
|
|
Use: "view [owner/name]",
|
|
|
|
|
Short: "View repository details",
|
|
|
|
|
Long: "Display detailed information about a repository.",
|
|
|
|
|
Args: cobra.MaximumNArgs(1),
|
|
|
|
|
RunE: runRepoView,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var repoListCmd = &cobra.Command{
|
|
|
|
|
Use: "list",
|
|
|
|
|
Short: "List your repositories",
|
|
|
|
|
Long: "List repositories owned by the authenticated user.",
|
|
|
|
|
RunE: runRepoList,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var repoCloneCmd = &cobra.Command{
|
2025-12-16 12:38:19 +01:00
|
|
|
Use: "clone <owner/name> [destination]",
|
2025-12-08 09:49:07 +01:00
|
|
|
Short: "Clone a repository",
|
2025-12-16 12:38:19 +01:00
|
|
|
Long: "Clone a repository locally. If destination is not specified, the repository name is used.",
|
|
|
|
|
Args: cobra.RangeArgs(1, 2),
|
2025-12-08 09:49:07 +01:00
|
|
|
RunE: runRepoClone,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var repoForkCmd = &cobra.Command{
|
|
|
|
|
Use: "fork <owner/name>",
|
|
|
|
|
Short: "Fork a repository",
|
|
|
|
|
Long: "Create a fork of a repository.",
|
|
|
|
|
Args: cobra.ExactArgs(1),
|
|
|
|
|
RunE: runRepoFork,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
rootCmd.AddCommand(repoCmd)
|
|
|
|
|
repoCmd.AddCommand(repoViewCmd)
|
|
|
|
|
repoCmd.AddCommand(repoListCmd)
|
|
|
|
|
repoCmd.AddCommand(repoCloneCmd)
|
|
|
|
|
repoCmd.AddCommand(repoForkCmd)
|
|
|
|
|
|
|
|
|
|
repoCloneCmd.Flags().StringP("protocol", "p", "https", "Clone protocol: https or ssh")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runRepoView(cmd *cobra.Command, args []string) error {
|
|
|
|
|
var repo string
|
|
|
|
|
if len(args) > 0 {
|
|
|
|
|
repo = args[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
owner, name, err := parseRepo(repo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 12:47:28 +01:00
|
|
|
client, err := api.NewClientFromConfig(cfg, "", getDetectedHost())
|
2025-12-08 09:49:07 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repository, _, err := client.GetRepo(owner, name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get repository: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("Repository: %s/%s\n", repository.Owner.UserName, repository.Name)
|
|
|
|
|
fmt.Printf("Description: %s\n", repository.Description)
|
|
|
|
|
fmt.Printf("URL: %s\n", repository.HTMLURL)
|
|
|
|
|
fmt.Printf("Clone URL (HTTPS): %s\n", repository.CloneURL)
|
|
|
|
|
fmt.Printf("Clone URL (SSH): %s\n", repository.SSHURL)
|
|
|
|
|
fmt.Printf("Default Branch: %s\n", repository.DefaultBranch)
|
|
|
|
|
fmt.Printf("Stars: %d\n", repository.Stars)
|
|
|
|
|
fmt.Printf("Forks: %d\n", repository.Forks)
|
|
|
|
|
fmt.Printf("Open Issues: %d\n", repository.OpenIssues)
|
|
|
|
|
fmt.Printf("Private: %v\n", repository.Private)
|
|
|
|
|
fmt.Printf("Created: %s\n", repository.Created.Format("2006-01-02 15:04:05"))
|
|
|
|
|
fmt.Printf("Updated: %s\n", repository.Updated.Format("2006-01-02 15:04:05"))
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runRepoList(cmd *cobra.Command, args []string) error {
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 12:47:28 +01:00
|
|
|
client, err := api.NewClientFromConfig(cfg, "", getDetectedHost())
|
2025-12-08 09:49:07 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
user, _, err := client.GetMyUserInfo()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get user info: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repos, _, err := client.ListUserRepos(user.UserName, gitea.ListReposOptions{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to list repositories: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(repos) == 0 {
|
|
|
|
|
fmt.Println("No repositories found")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
2025-12-08 10:00:50 +01:00
|
|
|
_, _ = fmt.Fprintf(w, "NAME\tVISIBILITY\tDESCRIPTION\n")
|
2025-12-08 09:49:07 +01:00
|
|
|
for _, repo := range repos {
|
|
|
|
|
visibility := "public"
|
|
|
|
|
if repo.Private {
|
|
|
|
|
visibility = "private"
|
|
|
|
|
}
|
|
|
|
|
desc := repo.Description
|
|
|
|
|
if len(desc) > 50 {
|
|
|
|
|
desc = desc[:47] + "..."
|
|
|
|
|
}
|
2025-12-08 10:00:50 +01:00
|
|
|
_, _ = fmt.Fprintf(w, "%s/%s\t%s\t%s\n", repo.Owner.UserName, repo.Name, visibility, desc)
|
2025-12-08 09:49:07 +01:00
|
|
|
}
|
2025-12-08 10:00:50 +01:00
|
|
|
_ = w.Flush()
|
2025-12-08 09:49:07 +01:00
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runRepoClone(cmd *cobra.Command, args []string) error {
|
|
|
|
|
repo := args[0]
|
|
|
|
|
protocol, _ := cmd.Flags().GetString("protocol")
|
|
|
|
|
|
|
|
|
|
owner, name, err := parseRepo(repo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 12:47:28 +01:00
|
|
|
client, err := api.NewClientFromConfig(cfg, "", getDetectedHost())
|
2025-12-08 09:49:07 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repository, _, err := client.GetRepo(owner, name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get repository: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cloneURL string
|
|
|
|
|
if protocol == "ssh" {
|
|
|
|
|
cloneURL = repository.SSHURL
|
|
|
|
|
} else {
|
|
|
|
|
cloneURL = repository.CloneURL
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-16 12:38:19 +01:00
|
|
|
// Determine destination path
|
|
|
|
|
var destination string
|
|
|
|
|
if len(args) > 1 {
|
|
|
|
|
destination = args[1]
|
|
|
|
|
} else {
|
|
|
|
|
destination = name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("Cloning %s/%s to %s...\n", owner, name, destination)
|
|
|
|
|
|
|
|
|
|
// Create parent directory if it doesn't exist
|
|
|
|
|
if dir := filepath.Dir(destination); dir != "." {
|
|
|
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create destination directory: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute git clone
|
|
|
|
|
gitCmd := exec.Command("git", "clone", cloneURL, destination)
|
|
|
|
|
gitCmd.Stdout = os.Stdout
|
|
|
|
|
gitCmd.Stderr = os.Stderr
|
|
|
|
|
gitCmd.Stdin = os.Stdin
|
|
|
|
|
|
|
|
|
|
if err := gitCmd.Run(); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to clone repository: %w", err)
|
|
|
|
|
}
|
2025-12-08 09:49:07 +01:00
|
|
|
|
2025-12-16 12:38:19 +01:00
|
|
|
fmt.Printf("Repository cloned successfully to %s\n", destination)
|
2025-12-08 09:49:07 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runRepoFork(cmd *cobra.Command, args []string) error {
|
|
|
|
|
repo := args[0]
|
|
|
|
|
|
|
|
|
|
owner, name, err := parseRepo(repo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 12:47:28 +01:00
|
|
|
client, err := api.NewClientFromConfig(cfg, "", getDetectedHost())
|
2025-12-08 09:49:07 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fork, _, err := client.CreateFork(owner, name, gitea.CreateForkOption{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fork repository: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("Repository forked successfully\n")
|
|
|
|
|
fmt.Printf("View at: %s\n", fork.HTMLURL)
|
|
|
|
|
fmt.Printf("Clone URL: %s\n", fork.CloneURL)
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|