Initial commit
This commit is contained in:
9
go/go.mod
Normal file
9
go/go.mod
Normal file
@@ -0,0 +1,9 @@
|
||||
module github.com/aky547/gitreposetup
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/go-git/go-git/v5 v5.11.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
381
go/main.go
Normal file
381
go/main.go
Normal file
@@ -0,0 +1,381 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"html"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Embedded assets
|
||||
//
|
||||
//go:embed ../../licenses/MIT.txt
|
||||
var mitLicense string
|
||||
|
||||
//go:embed ../../licenses/GPL-3.0.txt
|
||||
var gplLicense string
|
||||
|
||||
//go:embed ../../licenses/AGPL-3.0.txt
|
||||
var agplLicense string
|
||||
|
||||
//go:embed ../../licenses/Unlicense.txt
|
||||
var unlicense string
|
||||
|
||||
//go:embed ../../.gitignore
|
||||
var defaultGitignore string
|
||||
|
||||
type LicenseType string
|
||||
|
||||
const (
|
||||
LicenseMIT LicenseType = "MIT"
|
||||
LicenseGPLv3 LicenseType = "GPLv3"
|
||||
LicenseAGPLv3 LicenseType = "AGPLv3"
|
||||
LicenseUnlicense LicenseType = "Unlicense"
|
||||
)
|
||||
|
||||
type DeployType string
|
||||
|
||||
const (
|
||||
DeployDocker DeployType = "docker"
|
||||
DeployPyPI DeployType = "pypi"
|
||||
DeployCargo DeployType = "cargo"
|
||||
DeployGo DeployType = "go"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Owner string `yaml:"owner"`
|
||||
Name string `yaml:"name"`
|
||||
License string `yaml:"license"`
|
||||
DevelopBranch string `yaml:"develop_branch"`
|
||||
DefaultGitignore bool `yaml:"default_gitignore"`
|
||||
DefaultGitURL string `yaml:"default_git_url"`
|
||||
}
|
||||
|
||||
func defaultConfig() *Config {
|
||||
return &Config{
|
||||
Owner: "",
|
||||
Name: "",
|
||||
License: "MIT",
|
||||
DevelopBranch: "dev",
|
||||
DefaultGitignore: true,
|
||||
DefaultGitURL: "https://git.theprivateserver.de/{owner}/{repo}.git",
|
||||
}
|
||||
}
|
||||
|
||||
func getConfigPath() string {
|
||||
var home string
|
||||
if runtime.GOOS == "windows" {
|
||||
home = os.Getenv("USERPROFILE")
|
||||
} else {
|
||||
home = os.Getenv("HOME")
|
||||
}
|
||||
return filepath.Join(home, ".config", "GMS", ".config.yaml")
|
||||
}
|
||||
|
||||
func loadConfig() (*Config, error) {
|
||||
configPath := getConfigPath()
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
// Create default config
|
||||
config := defaultConfig()
|
||||
|
||||
// Create config directory
|
||||
dir := filepath.Dir(configPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Write default config
|
||||
yamlData, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("Created default config at: %s\n", configPath)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
if err := yaml.Unmarshal(data, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func runGitCommand(args []string, check bool) (string, error) {
|
||||
cmd := exec.Command("git", args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil && check {
|
||||
return "", fmt.Errorf("git command failed: %v", err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
func isGitRepo() bool {
|
||||
_, err := runGitCommand([]string{"rev-parse", "--git-dir"}, false)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func getGitUserName() string {
|
||||
name, _ := runGitCommand([]string{"config", "user.name"}, false)
|
||||
return name
|
||||
}
|
||||
|
||||
func decodeHTMLEntities(text string) string {
|
||||
return html.UnescapeString(text)
|
||||
}
|
||||
|
||||
func getLicenseContent(licenseType LicenseType, owner string) string {
|
||||
var licenseText string
|
||||
|
||||
switch licenseType {
|
||||
case LicenseMIT:
|
||||
licenseText = mitLicense
|
||||
case LicenseGPLv3:
|
||||
licenseText = gplLicense
|
||||
case LicenseAGPLv3:
|
||||
licenseText = agplLicense
|
||||
case LicenseUnlicense:
|
||||
licenseText = unlicense
|
||||
default:
|
||||
licenseText = mitLicense
|
||||
}
|
||||
|
||||
licenseText = decodeHTMLEntities(licenseText)
|
||||
|
||||
// Get fullname from git config or use owner
|
||||
fullname := getGitUserName()
|
||||
if fullname == "" {
|
||||
fullname = owner
|
||||
}
|
||||
|
||||
currentYear := fmt.Sprintf("%d", time.Now().Year())
|
||||
|
||||
// Substitute placeholders
|
||||
licenseText = strings.ReplaceAll(licenseText, "{year}", currentYear)
|
||||
licenseText = strings.ReplaceAll(licenseText, "{fullname}", fullname)
|
||||
|
||||
return licenseText
|
||||
}
|
||||
|
||||
func removeAllRemotes() {
|
||||
remotes, err := runGitCommand([]string{"remote"}, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, remote := range strings.Split(remotes, "\n") {
|
||||
remote = strings.TrimSpace(remote)
|
||||
if remote != "" {
|
||||
runGitCommand([]string{"remote", "remove", remote}, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setupGitRepo(config *Config, forceLicense bool) error {
|
||||
// Initialize repo if needed
|
||||
if !isGitRepo() {
|
||||
if _, err := runGitCommand([]string{"init"}, true); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Initialized new git repository")
|
||||
}
|
||||
|
||||
// Ensure main branch exists
|
||||
branches, _ := runGitCommand([]string{"branch", "--list"}, false)
|
||||
if !strings.Contains(branches, "main") {
|
||||
if strings.TrimSpace(branches) == "" {
|
||||
runGitCommand([]string{"checkout", "-b", "main"}, true)
|
||||
} else {
|
||||
runGitCommand([]string{"branch", "-M", "main"}, true)
|
||||
}
|
||||
} else {
|
||||
runGitCommand([]string{"checkout", "main"}, false)
|
||||
}
|
||||
|
||||
// Set up remote
|
||||
remoteURL := strings.ReplaceAll(config.DefaultGitURL, "{owner}", config.Owner)
|
||||
remoteURL = strings.ReplaceAll(remoteURL, "{repo}", config.Name)
|
||||
|
||||
// Remove all existing remotes and add new origin
|
||||
removeAllRemotes()
|
||||
if _, err := runGitCommand([]string{"remote", "add", "origin", remoteURL}, true); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Set remote 'origin' to: %s\n", remoteURL)
|
||||
|
||||
// Add LICENSE if missing or forced
|
||||
licensePath := "LICENSE"
|
||||
if _, err := os.Stat(licensePath); os.IsNotExist(err) || forceLicense {
|
||||
var licenseType LicenseType
|
||||
switch config.License {
|
||||
case "GPLv3":
|
||||
licenseType = LicenseGPLv3
|
||||
case "AGPLv3":
|
||||
licenseType = LicenseAGPLv3
|
||||
case "Unlicense":
|
||||
licenseType = LicenseUnlicense
|
||||
default:
|
||||
licenseType = LicenseMIT
|
||||
}
|
||||
|
||||
licenseContent := getLicenseContent(licenseType, config.Owner)
|
||||
if err := os.WriteFile(licensePath, []byte(licenseContent), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Added LICENSE: %s\n", config.License)
|
||||
}
|
||||
|
||||
// Add/overwrite .gitignore if enabled
|
||||
if config.DefaultGitignore {
|
||||
if err := os.WriteFile(".gitignore", []byte(defaultGitignore), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Added/updated .gitignore")
|
||||
}
|
||||
|
||||
// Stage changes
|
||||
runGitCommand([]string{"add", "."}, true)
|
||||
|
||||
// Commit if there are staged changes
|
||||
status, _ := runGitCommand([]string{"status", "--porcelain"}, false)
|
||||
if status != "" {
|
||||
if _, err := runGitCommand([]string{"commit", "-m", "Initial commit"}, true); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Created initial commit")
|
||||
}
|
||||
|
||||
// Create and checkout dev branch
|
||||
devBranch := config.DevelopBranch
|
||||
runGitCommand([]string{"checkout", "-b", devBranch}, false)
|
||||
fmt.Printf("Created and switched to branch: %s\n", devBranch)
|
||||
|
||||
// Try to push to remote
|
||||
_, err1 := runGitCommand([]string{"push", "-u", "origin", "main"}, false)
|
||||
_, err2 := runGitCommand([]string{"push", "-u", "origin", devBranch}, false)
|
||||
|
||||
if err1 == nil && err2 == nil {
|
||||
fmt.Println("Pushed to remote successfully")
|
||||
} else {
|
||||
fmt.Println("⚠️ Warning: Could not push to remote. Network may be unavailable or remote not accessible.")
|
||||
fmt.Println(" Repository configured locally. Push manually when ready.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupGiteaWorkflows(deployType DeployType) {
|
||||
giteaDir := ".gitea"
|
||||
workflowsDir := filepath.Join(giteaDir, "workflows")
|
||||
|
||||
// Create directories
|
||||
os.MkdirAll(workflowsDir, 0755)
|
||||
|
||||
// TODO: Copy/create workflow files based on deployType
|
||||
|
||||
fmt.Printf("Set up Gitea workflows for: %s\n", deployType)
|
||||
fmt.Println("⚠️ Note: Ensure required secrets are configured in Gitea user/org settings:")
|
||||
fmt.Println(" - GITEA_TOKEN, TOKEN, REGISTRY, DOCKER_USERNAME")
|
||||
|
||||
if deployType == DeployDocker {
|
||||
fmt.Println("⚠️ Warning: Docker workflows require a Dockerfile in the repository root.")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
owner string
|
||||
name string
|
||||
license string
|
||||
developBranch string
|
||||
defaultGitignore *bool
|
||||
defaultGitURL string
|
||||
force bool
|
||||
deployType string
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "gitreposetup",
|
||||
Short: "Initialize and configure git repositories with licenses and workflows",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Load config
|
||||
config, err := loadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Override config with CLI arguments
|
||||
if owner != "" {
|
||||
config.Owner = owner
|
||||
}
|
||||
if name != "" {
|
||||
config.Name = name
|
||||
}
|
||||
if license != "" {
|
||||
config.License = license
|
||||
}
|
||||
if developBranch != "" {
|
||||
config.DevelopBranch = developBranch
|
||||
}
|
||||
if defaultGitignore != nil {
|
||||
config.DefaultGitignore = *defaultGitignore
|
||||
}
|
||||
if defaultGitURL != "" {
|
||||
config.DefaultGitURL = defaultGitURL
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if config.Owner == "" || config.Name == "" {
|
||||
fmt.Fprintln(os.Stderr, "Error: --owner and --name are required (or set in config file)")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Setup git repository
|
||||
if err := setupGitRepo(config, force); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error setting up git repository: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Setup workflows if requested
|
||||
if deployType != "" {
|
||||
setupGiteaWorkflows(DeployType(deployType))
|
||||
}
|
||||
|
||||
fmt.Println("\n✅ Repository setup complete!")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.Flags().StringVar(&owner, "owner", "", "Repository owner/organization name")
|
||||
rootCmd.Flags().StringVar(&name, "name", "", "Repository name")
|
||||
rootCmd.Flags().StringVar(&license, "license", "", "License type (MIT, GPLv3, AGPLv3, Unlicense)")
|
||||
rootCmd.Flags().StringVar(&developBranch, "develop-branch-name", "", "Development branch name (default: dev)")
|
||||
defaultGitignore = rootCmd.Flags().Bool("default-gitignore", true, "Use default .gitignore")
|
||||
rootCmd.Flags().StringVar(&defaultGitURL, "default-git-url", "", "Git remote URL template")
|
||||
rootCmd.Flags().BoolVar(&force, "force", false, "Force overwrite existing LICENSE")
|
||||
rootCmd.Flags().StringVar(&deployType, "deploy-type", "", "Deployment type for Gitea workflows (docker, pypi, cargo, go)")
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user