summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-06-23 17:24:20 +0300
committerPaul Buetow <paul@buetow.org>2025-06-23 17:24:20 +0300
commit60691a6fb610cb7f7290d6ab3a26bc74f95af611 (patch)
tree8c6fb5cc4f3907c14bbc6258ed8eac85041acf05
parent97e0151ba6a260195ced76ab69d3e3bd58ba68fc (diff)
Add configuration file support with organization list
- Create config package with JSON parsing support - Define Organization struct with host and name - Add config file auto-detection in common locations - Add --config/-c flag for custom config path - Add --list-orgs flag to display configured organizations - Create example configuration file - Add comprehensive .gitignore Configuration supports multiple git organizations for future sync functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
-rw-r--r--.gitignore39
-rw-r--r--cmd/gitsyncer/main.go76
-rwxr-xr-xgitsyncerbin1523896 -> 3066401 bytes
-rw-r--r--gitsyncer.example.json12
-rw-r--r--internal/config/config.go83
5 files changed, 207 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7101fd7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
+# Binaries
+gitsyncer
+gitsyncer-*
+
+# Configuration files (except examples)
+gitsyncer.json
+.gitsyncer.json
+*.json
+!*.example.json
+
+# IDE and editor files
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# Go build artifacts
+*.exe
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool
+*.out
+
+# Dependency directories
+vendor/
+
+# Go workspace file
+go.work
+go.work.sum
+
+# OS files
+.DS_Store
+Thumbs.db \ No newline at end of file
diff --git a/cmd/gitsyncer/main.go b/cmd/gitsyncer/main.go
index 3a38d32..d859747 100644
--- a/cmd/gitsyncer/main.go
+++ b/cmd/gitsyncer/main.go
@@ -3,23 +3,93 @@ package main
import (
"flag"
"fmt"
+ "log"
"os"
+ "path/filepath"
+ "github.com/paul/gitsyncer/internal/config"
"github.com/paul/gitsyncer/internal/version"
)
func main() {
- var versionFlag bool
+ var (
+ versionFlag bool
+ configPath string
+ listOrgs bool
+ )
+
+ // Define command line flags
flag.BoolVar(&versionFlag, "version", false, "print version information")
flag.BoolVar(&versionFlag, "v", false, "print version information (short)")
+ flag.StringVar(&configPath, "config", "", "path to configuration file")
+ flag.StringVar(&configPath, "c", "", "path to configuration file (short)")
+ flag.BoolVar(&listOrgs, "list-orgs", false, "list configured organizations")
flag.Parse()
+ // Handle version flag
if versionFlag {
fmt.Println(version.GetVersion())
os.Exit(0)
}
+ // Determine config file path
+ if configPath == "" {
+ // Try default locations
+ home, err := os.UserHomeDir()
+ if err != nil {
+ log.Fatal("Failed to get home directory:", err)
+ }
+
+ // Check common config locations
+ configLocations := []string{
+ filepath.Join(".", "gitsyncer.json"),
+ filepath.Join(home, ".config", "gitsyncer", "config.json"),
+ filepath.Join(home, ".gitsyncer.json"),
+ }
+
+ for _, loc := range configLocations {
+ if _, err := os.Stat(loc); err == nil {
+ configPath = loc
+ break
+ }
+ }
+
+ if configPath == "" {
+ fmt.Println("No configuration file found. Please create one of:")
+ for _, loc := range configLocations {
+ fmt.Printf(" - %s\n", loc)
+ }
+ fmt.Println("\nOr specify a config file with --config flag")
+ fmt.Println("\nExample configuration:")
+ fmt.Println(`{
+ "organizations": [
+ {"host": "git@github.com", "name": "myorg"},
+ {"host": "git@codeberg.org", "name": "myorg"}
+ ]
+}`)
+ os.Exit(1)
+ }
+ }
+
+ // Load configuration
+ cfg, err := config.Load(configPath)
+ if err != nil {
+ log.Fatal("Failed to load configuration:", err)
+ }
+
+ fmt.Printf("Loaded configuration from: %s\n", configPath)
+
+ // Handle list organizations flag
+ if listOrgs {
+ fmt.Println("\nConfigured organizations:")
+ for _, org := range cfg.Organizations {
+ fmt.Printf(" - %s\n", org.GetGitURL())
+ }
+ os.Exit(0)
+ }
+
// TODO: Implement main gitsyncer functionality
- fmt.Println("gitsyncer - Git repository synchronization tool")
- fmt.Println("Use --version to display version information")
+ fmt.Println("\ngitsyncer - Git repository synchronization tool")
+ fmt.Printf("Configured with %d organization(s)\n", len(cfg.Organizations))
+ fmt.Println("\nUse --list-orgs to display configured organizations")
} \ No newline at end of file
diff --git a/gitsyncer b/gitsyncer
index 1f897cd..9134faa 100755
--- a/gitsyncer
+++ b/gitsyncer
Binary files differ
diff --git a/gitsyncer.example.json b/gitsyncer.example.json
new file mode 100644
index 0000000..3170286
--- /dev/null
+++ b/gitsyncer.example.json
@@ -0,0 +1,12 @@
+{
+ "organizations": [
+ {
+ "host": "git@codeberg.org",
+ "name": "snonux"
+ },
+ {
+ "host": "git@github.com",
+ "name": "snonux"
+ }
+ ]
+} \ No newline at end of file
diff --git a/internal/config/config.go b/internal/config/config.go
new file mode 100644
index 0000000..91c107a
--- /dev/null
+++ b/internal/config/config.go
@@ -0,0 +1,83 @@
+package config
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+)
+
+// Organization represents a git organization with its host and name
+type Organization struct {
+ Host string `json:"host"`
+ Name string `json:"name"`
+}
+
+// Config holds the application configuration
+type Config struct {
+ Organizations []Organization `json:"organizations"`
+}
+
+// Load reads and parses the configuration file
+func Load(path string) (*Config, error) {
+ // Expand home directory if needed
+ if path[:2] == "~/" {
+ home, err := os.UserHomeDir()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get home directory: %w", err)
+ }
+ path = filepath.Join(home, path[2:])
+ }
+
+ // Read config file
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read config file: %w", err)
+ }
+
+ // Parse JSON
+ var cfg Config
+ if err := json.Unmarshal(data, &cfg); err != nil {
+ return nil, fmt.Errorf("failed to parse config: %w", err)
+ }
+
+ // Validate configuration
+ if err := cfg.Validate(); err != nil {
+ return nil, fmt.Errorf("invalid configuration: %w", err)
+ }
+
+ return &cfg, nil
+}
+
+// Validate checks if the configuration is valid
+func (c *Config) Validate() error {
+ if len(c.Organizations) == 0 {
+ return fmt.Errorf("no organizations configured")
+ }
+
+ for i, org := range c.Organizations {
+ if org.Host == "" {
+ return fmt.Errorf("organization %d: missing host", i)
+ }
+ if org.Name == "" {
+ return fmt.Errorf("organization %d: missing name", i)
+ }
+ }
+
+ return nil
+}
+
+// GetGitURL returns the git URL for an organization
+func (o *Organization) GetGitURL() string {
+ return fmt.Sprintf("%s:%s", o.Host, o.Name)
+}
+
+// FindOrganization finds an organization by host
+func (c *Config) FindOrganization(host string) *Organization {
+ for _, org := range c.Organizations {
+ if org.Host == host {
+ return &org
+ }
+ }
+ return nil
+} \ No newline at end of file