Files
kmanage/cmd/git_init_repo.go

223 lines
6.9 KiB
Go

package cmd
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"kmanage/pkg/git"
"github.com/spf13/cobra"
)
var gitInitRepoCmd = &cobra.Command{
Use: "init-repo [name]",
Aliases: []string{"init", "create", "new"},
Short: "Create a remote Git repository and push the current project directory",
Long: `Creates a new remote repository on your configured Git provider (Gitea / Forgejo / GitHub)
for the current working directory, initializes Git locally if needed, creates an initial commit,
and pushes the branch to remote origin.
Examples:
# Create repo with current directory name and push:
kmanage git init-repo
# Create repo with custom name under an organization:
kmanage git init-repo my-service --org Docker
# Create private repo on GitHub:
kmanage git init-repo --account github --private
# Custom branch and commit message:
kmanage git init-repo -b main -m "Initial commit created by kmanage"`,
RunE: func(cmd *cobra.Command, args []string) error {
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current working directory: %w", err)
}
repoName := filepath.Base(cwd)
if len(args) > 0 && strings.TrimSpace(args[0]) != "" {
repoName = strings.TrimSpace(args[0])
}
accountFlag, _ := cmd.Flags().GetString("account")
orgFlag, _ := cmd.Flags().GetString("org")
ownerFlag, _ := cmd.Flags().GetString("owner")
if orgFlag == "" && ownerFlag != "" {
orgFlag = ownerFlag
}
privateFlag, _ := cmd.Flags().GetBool("private")
descFlag, _ := cmd.Flags().GetString("description")
branchFlag, _ := cmd.Flags().GetString("branch")
if branchFlag == "" {
branchFlag = "main"
}
commitMsg, _ := cmd.Flags().GetString("message")
if commitMsg == "" {
commitMsg = "Initial commit created by kmanage"
}
useHTTPS, _ := cmd.Flags().GetBool("https")
// 1. Verify Git CLI is available
if _, err := exec.LookPath("git"); err != nil {
return fmt.Errorf("git binary not found in PATH. Please install git")
}
// 2. Resolve Git client
client, err := getGitClientForAccount(accountFlag)
if err != nil {
return err
}
targetOwner := orgFlag
if targetOwner == "" {
targetOwner = client.AccountName
}
fmt.Printf("🔨 Creating remote repository '%s' on %s (%s)...\n", repoName, client.AccountName, client.BaseURL)
createdRepo, err := client.CreateRepository(git.CreateRepoOptions{
Name: repoName,
Description: descFlag,
Private: privateFlag,
AutoInit: false,
DefaultBranch: branchFlag,
Org: orgFlag,
})
if err != nil {
return fmt.Errorf("failed to create remote repository: %w", err)
}
// 3. Determine remote URL (prefer SSH unless HTTPS flag is specified)
remoteURL := createdRepo.SSHURL
if useHTTPS || remoteURL == "" {
remoteURL = createdRepo.CloneURL
}
if remoteURL == "" {
remoteURL = createdRepo.HTMLURL
}
// 4. Initialize local git repository if not already initialized
fmt.Printf("📦 Setting up local git repository in %s...\n", cwd)
if !isGitRepo(cwd) {
if err := runGitCmd(cwd, "init"); err != nil {
return fmt.Errorf("failed to initialize git repository: %w", err)
}
}
// 5. Ensure desired branch name (e.g. main)
_ = runGitCmd(cwd, "branch", "-M", branchFlag)
// 6. Configure remote origin
if hasRemote(cwd, "origin") {
if err := runGitCmd(cwd, "remote", "set-url", "origin", remoteURL); err != nil {
return fmt.Errorf("failed to update remote origin: %w", err)
}
} else {
if err := runGitCmd(cwd, "remote", "add", "origin", remoteURL); err != nil {
return fmt.Errorf("failed to add remote origin: %w", err)
}
}
// 7. Stage all files
fmt.Println("📝 Staging files...")
if err := runGitCmd(cwd, "add", "-A"); err != nil {
return fmt.Errorf("failed to stage files: %w", err)
}
// 8. Create commit if needed
if hasUncommittedChanges(cwd) {
fmt.Printf("📝 Creating commit (\"%s\")...\n", commitMsg)
if err := runGitCmd(cwd, "commit", "-m", commitMsg); err != nil {
return fmt.Errorf("failed to commit: %w", err)
}
} else if !hasAnyCommits(cwd) {
fmt.Printf("📝 Creating initial commit (\"%s\")...\n", commitMsg)
if err := runGitCmd(cwd, "commit", "--allow-empty", "-m", commitMsg); err != nil {
return fmt.Errorf("failed to commit: %w", err)
}
}
// 9. Push to remote origin
fmt.Printf("🚀 Pushing branch '%s' to remote origin...\n", branchFlag)
if err := runGitCmd(cwd, "push", "-u", "origin", branchFlag); err != nil {
return fmt.Errorf("failed to push to remote repository (%s): %w\nMake sure your SSH key or Git credentials are configured for remote access", remoteURL, err)
}
fmt.Println("\n✅ Repository successfully created and published!")
if createdRepo.HTMLURL != "" {
fmt.Printf(" 🔗 Web URL: %s\n", createdRepo.HTMLURL)
}
fmt.Printf(" 📡 Remote: %s\n", remoteURL)
fmt.Printf(" 🌿 Branch: %s\n", branchFlag)
return nil
},
}
func isGitRepo(dir string) bool {
cmd := exec.Command("git", "rev-parse", "--is-inside-work-tree")
cmd.Dir = dir
return cmd.Run() == nil
}
func hasRemote(dir, remoteName string) bool {
cmd := exec.Command("git", "remote", "get-url", remoteName)
cmd.Dir = dir
return cmd.Run() == nil
}
func hasUncommittedChanges(dir string) bool {
cmd := exec.Command("git", "status", "--porcelain")
cmd.Dir = dir
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
return false
}
return strings.TrimSpace(out.String()) != ""
}
func hasAnyCommits(dir string) bool {
cmd := exec.Command("git", "rev-parse", "--verify", "HEAD")
cmd.Dir = dir
return cmd.Run() == nil
}
func runGitCmd(dir string, args ...string) error {
cmd := exec.Command("git", args...)
cmd.Dir = dir
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
errStr := strings.TrimSpace(stderr.String())
if errStr != "" {
return fmt.Errorf("%s", errStr)
}
return err
}
return nil
}
func init() {
gitCmd.AddCommand(gitInitRepoCmd)
gitInitRepoCmd.Flags().StringP("account", "A", "", "Git account to create the repository on (default: active default account)")
gitInitRepoCmd.Flags().StringP("org", "o", "", "Organization or owner under which to create the repository")
gitInitRepoCmd.Flags().String("owner", "", "Alias for --org")
gitInitRepoCmd.Flags().BoolP("private", "p", false, "Make the repository private")
gitInitRepoCmd.Flags().StringP("description", "d", "", "Repository description")
gitInitRepoCmd.Flags().StringP("branch", "b", "main", "Initial/default branch name")
gitInitRepoCmd.Flags().StringP("message", "m", "Initial commit created by kmanage", "Commit message for the initial commit")
gitInitRepoCmd.Flags().Bool("https", false, "Use HTTPS clone URL instead of SSH for git remote")
_ = gitInitRepoCmd.RegisterFlagCompletionFunc("account", completeGitAccountNames)
_ = gitInitRepoCmd.RegisterFlagCompletionFunc("org", completeOwnerNames)
_ = gitInitRepoCmd.RegisterFlagCompletionFunc("owner", completeOwnerNames)
}