diff options
| author | Paul Buetow <paul@buetow.org> | 2025-06-24 10:00:28 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-06-24 10:00:28 +0300 |
| commit | af8ab19f5def6f00081b0a6d1e5b20b76683f720 (patch) | |
| tree | 89ab045f8da4af0bb6af8b26e7851e7bddfad6e7 /doc | |
| parent | 577d3d37a47dc7279d7e56975448aa330d6b5469 (diff) | |
refactor: use value semantics for GitHub and Codeberg clients
- Changed github.NewClient() to return Client instead of *Client
- Changed codeberg.NewClient() to return Client instead of *Client
- Updated sync_handlers.go to handle value semantics properly
- Both clients only contain immutable string fields, making value semantics more appropriate
docs: add comprehensive documentation
- Added doc/ directory with full documentation
- Created architecture overview explaining system design
- Added complete API reference for all packages, types, and functions
- Created configuration guide with examples
- Added usage examples and common workflows
- Created development guide for contributors
- Updated README with links to documentation
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/README.md | 34 | ||||
| -rw-r--r-- | doc/api-reference.md | 514 | ||||
| -rw-r--r-- | doc/architecture.md | 207 | ||||
| -rw-r--r-- | doc/configuration.md | 278 | ||||
| -rw-r--r-- | doc/development.md | 447 | ||||
| -rw-r--r-- | doc/examples.md | 373 |
6 files changed, 1853 insertions, 0 deletions
diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..02ea0f0 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,34 @@ +# GitSyncer Documentation + +Welcome to the GitSyncer documentation. This directory contains comprehensive documentation about the GitSyncer project structure, architecture, and API reference. + +## Table of Contents + +1. [Architecture Overview](architecture.md) - High-level system design and architecture +2. [API Reference](api-reference.md) - Complete reference of all packages, types, and functions +3. [Configuration Guide](configuration.md) - How to configure GitSyncer +4. [Usage Examples](examples.md) - Common usage patterns and examples +5. [Development Guide](development.md) - Guide for contributors + +## Quick Links + +- [Project README](../README.md) - Main project documentation +- [Source Code](https://codeberg.org/snonux/gitsyncer) - Repository on Codeberg + +## Overview + +GitSyncer is a tool for synchronizing Git repositories across multiple platforms (GitHub, Codeberg, self-hosted Git servers). It supports: + +- Bidirectional synchronization between multiple Git hosts +- Automatic branch management and filtering +- Repository creation on supported platforms +- Conflict detection and reporting +- Abandoned branch analysis + +## Getting Started + +1. Install GitSyncer +2. Create a configuration file +3. Run `gitsyncer --sync-all` to sync all configured repositories + +See the [Configuration Guide](configuration.md) for detailed setup instructions.
\ No newline at end of file diff --git a/doc/api-reference.md b/doc/api-reference.md new file mode 100644 index 0000000..cc4f69c --- /dev/null +++ b/doc/api-reference.md @@ -0,0 +1,514 @@ +# GitSyncer API Reference + +This document provides a complete reference for all packages, types, and functions in GitSyncer. + +## Table of Contents + +- [Package main](#package-main) +- [Package cli](#package-cli) +- [Package codeberg](#package-codeberg) +- [Package config](#package-config) +- [Package github](#package-github) +- [Package sync](#package-sync) +- [Package version](#package-version) + +--- + +## Package main + +**Location**: `cmd/gitsyncer/main.go` + +The main package provides the application entry point. + +### Functions + +#### func main() +Application entry point that: +- Parses command-line flags +- Routes to appropriate handlers +- Manages exit codes + +--- + +## Package cli + +**Location**: `internal/cli/` + +The cli package handles all command-line interface operations. + +### Types + +#### type Flags +```go +type Flags struct { + VersionFlag bool // Show version information + ConfigPath string // Path to configuration file + ListOrgs bool // List configured organizations + ListRepos bool // List configured repositories + SyncRepo string // Single repository to sync + SyncAll bool // Sync all configured repositories + SyncCodebergPublic bool // Sync all public Codeberg repos + SyncGitHubPublic bool // Sync all public GitHub repos + FullSync bool // Full bidirectional sync + CreateGitHubRepos bool // Auto-create GitHub repositories + CreateCodebergRepos bool // Auto-create Codeberg repositories + DryRun bool // Preview mode without changes + WorkDir string // Working directory for operations + TestGitHubToken bool // Test GitHub authentication +} +``` + +### Functions + +#### func ParseFlags() *Flags +Parses command-line arguments and returns a Flags struct with all options. + +#### func HandleVersion() int +Displays version information and returns exit code 0. + +#### func HandleTestGitHubToken() int +Tests GitHub token authentication: +- Loads token from config/env/file +- Validates token with API call +- Returns 0 on success, 1 on failure + +#### func LoadConfig(configPath string) (*config.Config, error) +Loads configuration from specified path or default locations: +- `./gitsyncer.json` +- `~/.config/gitsyncer/config.json` +- `~/.gitsyncer.json` + +#### func ShowConfigHelp() +Displays help for creating configuration files with example. + +#### func HandleListOrgs(cfg *config.Config) int +Lists all configured organizations from config. + +#### func HandleListRepos(cfg *config.Config) int +Lists all configured repositories from config. + +#### func ShowUsage(cfg *config.Config) +Displays comprehensive usage information. + +#### func HandleSync(cfg *config.Config, flags *Flags) int +Synchronizes a single repository specified by `--sync` flag. + +#### func HandleSyncAll(cfg *config.Config, flags *Flags) int +Synchronizes all repositories listed in configuration. + +#### func HandleSyncCodebergPublic(cfg *config.Config, flags *Flags) int +Discovers and syncs all public Codeberg repositories to other platforms. + +#### func HandleSyncGitHubPublic(cfg *config.Config, flags *Flags) int +Discovers and syncs all public GitHub repositories to other platforms. + +#### func ShowFullSyncMessage() +Displays information about full sync mode. + +### Helper Functions (sync_handlers.go) + +#### func createGitHubRepoIfNeeded(cfg *config.Config, repoName string) error +Creates GitHub repository if it doesn't exist and token is available. + +#### func initGitHubClient(cfg *config.Config) *github.Client +Initializes GitHub client with token from configuration. + +#### func createRepoWithClient(client *github.Client, repoName, description string) error +Creates repository using provided GitHub client. + +#### func showReposToSync(repoNames []string) +Displays list of repositories that will be synced. + +#### func syncCodebergRepos(cfg *config.Config, flags *Flags, repos []codeberg.Repository, repoNames []string) int +Synchronizes discovered Codeberg repositories. + +#### func syncGitHubRepos(cfg *config.Config, flags *Flags, repos []github.Repository, repoNames []string) int +Synchronizes discovered GitHub repositories. + +--- + +## Package codeberg + +**Location**: `internal/codeberg/codeberg.go` + +The codeberg package provides a client for interacting with Codeberg's Gitea API. + +### Types + +#### type Repository +```go +type Repository struct { + ID int64 `json:"id"` // Repository ID + Name string `json:"name"` // Repository name + FullName string `json:"full_name"` // Full name (org/repo) + Description string `json:"description"` // Repository description + Private bool `json:"private"` // Is private repository + Fork bool `json:"fork"` // Is fork + CreatedAt time.Time `json:"created_at"` // Creation timestamp + UpdatedAt time.Time `json:"updated_at"` // Last update timestamp + CloneURL string `json:"clone_url"` // HTTPS clone URL + SSHURL string `json:"ssh_url"` // SSH clone URL + Size int `json:"size"` // Repository size + Archived bool `json:"archived"` // Is archived + Empty bool `json:"empty"` // Is empty repository +} +``` + +#### type Client +```go +type Client struct { + baseURL string // API base URL (https://codeberg.org/api/v1) + org string // Organization or username +} +``` + +### Functions + +#### func NewClient(org string) Client +Creates a new Codeberg API client for the specified organization/user. + +### Methods + +#### func (c *Client) ListPublicRepos() ([]Repository, error) +Lists all public repositories for an organization: +- Handles pagination automatically +- Filters out private, fork, archived, and empty repos +- Returns error on API failure + +#### func (c *Client) ListUserPublicRepos() ([]Repository, error) +Lists all public repositories for a user: +- Same filtering as ListPublicRepos +- Use when org endpoint fails (for user accounts) + +#### func GetRepoNames(repos []Repository) []string +Extracts repository names from a slice of Repository structs. + +--- + +## Package config + +**Location**: `internal/config/config.go` + +The config package handles configuration loading and validation. + +### Types + +#### type Organization +```go +type Organization struct { + Host string `json:"host"` // Git host (e.g., "git@github.com") + Name string `json:"name"` // Organization/username + GitHubToken string `json:"github_token"` // Optional GitHub API token +} +``` + +#### type Config +```go +type Config struct { + Organizations []Organization `json:"organizations"` // List of git organizations + Repositories []string `json:"repositories"` // Specific repos to sync + ExcludeBranches []string `json:"exclude_branches"` // Regex patterns for branch exclusion +} +``` + +### Functions + +#### func Load(path string) (*Config, error) +Loads configuration from JSON file: +- Validates JSON structure +- Calls Validate() on loaded config +- Returns error on failure + +### Methods + +#### func (c *Config) Validate() error +Validates configuration: +- Ensures at least one organization exists +- Returns error if validation fails + +#### func (o *Organization) GetGitURL() string +Returns the Git URL for the organization in format `host:name`. + +#### func (c *Config) FindOrganization(host string) *Organization +Finds organization by host string. + +#### func (o *Organization) IsCodeberg() bool +Returns true if organization host contains "codeberg.org". + +#### func (c *Config) FindCodebergOrg() *Organization +Finds first Codeberg organization in config. + +#### func (o *Organization) IsGitHub() bool +Returns true if organization host contains "github.com". + +#### func (c *Config) FindGitHubOrg() *Organization +Finds first GitHub organization in config. + +--- + +## Package github + +**Location**: `internal/github/github.go` + +The github package provides a client for GitHub API operations. + +### Types + +#### type Client +```go +type Client struct { + token string // GitHub personal access token + org string // Organization or username +} +``` + +#### type Repository +```go +type Repository struct { + Name string `json:"name"` // Repository name + Description string `json:"description"` // Repository description + Private bool `json:"private"` // Is private repository + Fork bool `json:"fork"` // Is fork + Archived bool `json:"archived"` // Is archived + Disabled bool `json:"disabled"` // Is disabled + Size int `json:"size"` // Repository size in KB +} +``` + +#### type CreateRepoRequest +```go +type CreateRepoRequest struct { + Name string `json:"name"` // Repository name + Description string `json:"description"` // Repository description + Private bool `json:"private"` // Create as private + AutoInit bool `json:"auto_init"` // Initialize with README +} +``` + +#### type CreateRepoResponse +```go +type CreateRepoResponse struct { + ID int64 `json:"id"` // Repository ID + Name string `json:"name"` // Repository name + FullName string `json:"full_name"` // Full name (owner/repo) + Private bool `json:"private"` // Is private + SSHURL string `json:"ssh_url"` // SSH clone URL + CloneURL string `json:"clone_url"` // HTTPS clone URL +} +``` + +#### type ErrorResponse +```go +type ErrorResponse struct { + Message string `json:"message"` // Error message + Errors []struct { + Resource string `json:"resource"` // Resource type + Field string `json:"field"` // Field with error + Code string `json:"code"` // Error code + } `json:"errors,omitempty"` +} +``` + +### Functions + +#### func NewClient(token, org string) Client +Creates new GitHub API client: +- If token is empty, tries GITHUB_TOKEN env var +- If still empty, tries ~/.gitsyncer_github_token file +- Returns client with loaded token + +### Methods + +#### func (c *Client) HasToken() bool +Returns true if client has a token configured. + +#### func (c *Client) RepoExists(repoName string) (bool, error) +Checks if repository exists: +- Returns (true, nil) if exists +- Returns (false, nil) if not found (404) +- Returns (false, error) for other errors + +#### func (c *Client) CreateRepo(repoName, description string, private bool) error +Creates a new repository: +- Checks if repo already exists first +- Creates with provided settings +- Returns nil if already exists or created successfully + +#### func (c *Client) ListPublicRepos() ([]Repository, error) +Lists all public repositories: +- Handles pagination automatically +- Filters out private, fork, archived, disabled, and empty repos +- Requires authentication token + +#### func GetRepoNames(repos []Repository) []string +Extracts repository names from Repository slice. + +--- + +## Package sync + +**Location**: `internal/sync/` + +The sync package contains the core synchronization logic. + +### Types + +#### type Syncer +```go +type Syncer struct { + config *config.Config // Configuration + workDir string // Working directory + repoName string // Current repository name + abandonedReports map[string]*AbandonedBranchReport // Abandoned branch reports + branchFilter *BranchFilter // Branch exclusion filter +} +``` + +#### type BranchInfo +```go +type BranchInfo struct { + Name string // Branch name + LastCommit time.Time // Last commit timestamp + Remote string // Remote name + IsAbandoned bool // Whether branch is abandoned + AbandonReason string // Reason for abandonment +} +``` + +#### type AbandonedBranchReport +```go +type AbandonedBranchReport struct { + MainBranchUpdated bool // Is main branch active + MainBranchLastCommit time.Time // Main branch last commit + AbandonedBranches []BranchInfo // List of abandoned branches + TotalBranches int // Total number of branches +} +``` + +#### type BranchFilter +```go +type BranchFilter struct { + excludePatterns []*regexp.Regexp // Compiled regex patterns +} +``` + +### Functions + +#### func New(cfg *config.Config, workDir string) *Syncer +Creates new Syncer instance with configuration and working directory. + +### Syncer Methods + +#### func (s *Syncer) SyncRepository(repoName string) error +Main synchronization method: +1. Creates work directory +2. Sets up repository (clone or configure remotes) +3. Fetches from all remotes +4. Gets and filters branches +5. Syncs each branch +6. Analyzes abandoned branches +7. Returns error on failure + +#### func (s *Syncer) GenerateAbandonedBranchSummary() string +Generates summary report of abandoned branches across all synced repositories. + +### Branch Filter Functions + +#### func NewBranchFilter(excludePatterns []string) (*BranchFilter, error) +Creates new branch filter with compiled regex patterns. + +### BranchFilter Methods + +#### func (f *BranchFilter) ShouldExclude(branchName string) bool +Returns true if branch matches any exclusion pattern. + +#### func (f *BranchFilter) FilterBranches(branches []string) []string +Returns branches that don't match exclusion patterns. + +#### func (f *BranchFilter) GetExcludedBranches(branches []string) []string +Returns branches that match exclusion patterns. + +#### func FormatExclusionReport(excludedBranches []string, patterns []string) string +Formats a report of excluded branches with patterns used. + +### Git Operation Functions (git_operations.go) + +#### func checkForMergeConflicts() (bool, string, error) +Checks if repository has merge conflicts. + +#### func stashChanges() error +Stashes uncommitted changes. + +#### func popStash() +Pops the last stash (called via defer). + +#### func getRemotesList() (map[string]bool, error) +Returns map of configured remotes. + +#### func getAllUniqueBranches(gitOutput []byte) []string +Parses git branch output and returns unique branch names. + +#### func changeToRepoDirectory(repoPath string) (func(), error) +Changes to repository directory and returns restore function. + +#### func fetchRemote(remote string) error +Fetches from a specific remote with prune. + +#### func checkoutExistingBranch(branch string) error +Checks out an existing local branch. + +#### func createTrackingBranch(branch, remoteName string) error +Creates new branch tracking a remote branch. + +#### func mergeFromRemotes(branch string, remotesWithBranch map[string]bool) error +Merges changes from all remotes that have the branch. + +#### func pushToAllRemotes(branch string, remotes map[string]*config.Organization, remotesWithBranch map[string]bool) error +Pushes branch to all configured remotes. + +### Internal Helper Functions + +#### func (s *Syncer) setupRepository(repoPath string) error +Sets up repository by cloning or adding remotes. + +#### func (s *Syncer) analyzeAbandonedBranches() (*AbandonedBranchReport, error) +Analyzes branches for abandonment (6+ months inactive). + +#### func (s *Syncer) findMainBranch(branches []string) string +Finds the main branch (main, master, or develop). + +#### func (s *Syncer) trackRemotesWithBranch(branch string, remotes map[string]*config.Organization) map[string]bool +Returns map of remotes that have the specified branch. + +--- + +## Package version + +**Location**: `internal/version/version.go` + +The version package provides version information. + +### Variables + +```go +var ( + Version = "0.1.0" // Application version + GitCommit = "unknown" // Git commit hash (set at build time) + BuildDate = "unknown" // Build date (set at build time) + GoVersion = runtime.Version() // Go version used for build +) +``` + +### Functions + +#### func GetVersion() string +Returns full version string with all metadata: +``` +gitsyncer version 0.1.0 + Git commit: abc123 + Built: 2024-01-15 + Go version: go1.21.5 +``` + +#### func GetShortVersion() string +Returns just the version number: `0.1.0`
\ No newline at end of file diff --git a/doc/architecture.md b/doc/architecture.md new file mode 100644 index 0000000..ccb5cbd --- /dev/null +++ b/doc/architecture.md @@ -0,0 +1,207 @@ +# GitSyncer Architecture + +## Overview + +GitSyncer is designed as a command-line tool that synchronizes Git repositories across multiple platforms. It follows a modular architecture with clear separation of concerns between CLI handling, API clients, configuration management, and core synchronization logic. + +## High-Level Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CLI Layer │ +│ (cmd/gitsyncer/main.go) │ +└─────────────────────────┬───────────────────────────────────┘ + │ +┌─────────────────────────┴───────────────────────────────────┐ +│ CLI Handlers │ +│ (internal/cli/handlers.go) │ +│ (internal/cli/sync_handlers.go) │ +└──────┬──────────────────┬──────────────────┬────────────────┘ + │ │ │ +┌──────┴──────┐ ┌──────┴──────┐ ┌─────┴──────┐ +│ Config │ │ API Clients │ │ Sync │ +│ Manager │ │ │ │ Engine │ +│(config.go) │ │ - GitHub │ │ (sync.go) │ +│ │ │ - Codeberg │ │ │ +└─────────────┘ └──────────────┘ └────────────┘ +``` + +## Component Architecture + +### 1. Entry Point (cmd/gitsyncer/main.go) + +The main function serves as the application entry point and: +- Parses command-line flags +- Routes to appropriate handlers based on flags +- Manages application lifecycle and exit codes + +### 2. CLI Layer (internal/cli/) + +The CLI layer is responsible for user interaction and consists of: + +#### flags.go +- Defines all command-line flags +- Provides flag parsing logic +- Returns a structured `Flags` object + +#### handlers.go +- General command handlers (version, config, list operations) +- Configuration loading and validation +- Error presentation to users + +#### sync_handlers.go +- Sync-specific operations +- Orchestrates API clients and sync engine +- Handles batch operations + +### 3. Configuration Management (internal/config/) + +- Loads JSON configuration files +- Validates configuration structure +- Provides helper methods for finding organizations +- Supports multiple configuration file locations + +### 4. API Clients + +#### GitHub Client (internal/github/) +- Authenticates using personal access tokens +- Creates repositories via GitHub API +- Lists public repositories with pagination +- Handles multiple token sources (config, env, file) + +#### Codeberg Client (internal/codeberg/) +- Interacts with Codeberg's Gitea API +- Lists public repositories for users/organizations +- Supports pagination for large repository lists +- No authentication required for public operations + +### 5. Sync Engine (internal/sync/) + +The core synchronization logic is divided into several components: + +#### sync.go - Main Orchestrator +- Coordinates the entire sync process +- Manages working directory +- Handles repository-level operations + +#### repository_setup.go +- Clones repositories or ensures they exist +- Configures Git remotes +- Handles initial repository setup + +#### branch_sync.go +- Manages branch-level synchronization +- Tracks which remotes have which branches +- Orchestrates merge and push operations + +#### git_operations.go +- Low-level Git command wrappers +- Handles merge conflicts +- Manages stashing and checkout operations + +#### branch_filter.go +- Implements regex-based branch filtering +- Excludes branches based on patterns +- Provides filtering reports + +#### branch_analyzer.go +- Detects abandoned branches (6+ months inactive) +- Generates abandonment reports +- Analyzes branch activity + +### 6. Version Management (internal/version/) + +- Provides version information +- Supports build-time metadata injection +- Formats version strings for display + +## Data Flow + +### Sync Operation Flow + +1. **Configuration Loading** + ``` + User → CLI → Config Loader → Config Validation + ``` + +2. **Repository Discovery** + ``` + Config → API Clients → Repository Lists → Filtering + ``` + +3. **Synchronization Process** + ``` + For each repository: + └→ Setup/Clone Repository + └→ Configure Remotes + └→ Fetch All Remotes + └→ Get All Branches + └→ Filter Branches + └→ For each branch: + └→ Checkout/Create Branch + └→ Merge from Remotes + └→ Push to All Remotes + └→ Analyze Abandoned Branches + └→ Generate Reports + ``` + +## Design Principles + +### 1. Modularity +Each package has a single, well-defined responsibility: +- CLI handling is separate from business logic +- API clients are independent and interchangeable +- Core sync logic is platform-agnostic + +### 2. Configuration-Driven +- All behavior is controlled via configuration +- No hard-coded organization or repository names +- Flexible remote naming based on hosts + +### 3. Error Handling +- Graceful degradation (missing repos don't stop sync) +- Clear error messages with actionable guidance +- Proper exit codes for scripting + +### 4. Extensibility +- New platforms can be added by implementing API clients +- Branch filtering is regex-based for flexibility +- Sync strategies can be extended + +## Security Considerations + +### Token Management +- GitHub tokens are never logged or displayed +- Multiple token sources for flexibility +- Tokens are loaded on-demand + +### Git Operations +- All operations use standard Git commands +- No custom Git protocol implementation +- Respects Git's security model + +## Performance Characteristics + +### Scalability +- Handles multiple repositories in sequence +- Pagination support for large repository lists +- Efficient branch filtering + +### Resource Usage +- Minimal memory footprint +- Disk usage proportional to repository sizes +- Network usage optimized with selective fetching + +## Future Architecture Considerations + +### Planned Enhancements +1. **Parallel Synchronization** - Sync multiple repos concurrently +2. **Webhook Support** - Trigger syncs on push events +3. **More Platforms** - GitLab, Bitbucket, Gitea support +4. **Conflict Resolution** - Automated conflict resolution strategies + +### Extension Points +- Platform interface for new Git hosts +- Pluggable authentication mechanisms +- Custom sync strategies +- Hook system for pre/post sync actions
\ No newline at end of file diff --git a/doc/configuration.md b/doc/configuration.md new file mode 100644 index 0000000..c4dcc50 --- /dev/null +++ b/doc/configuration.md @@ -0,0 +1,278 @@ +# GitSyncer Configuration Guide + +## Overview + +GitSyncer uses a JSON configuration file to define organizations, repositories, and sync behavior. The configuration file can be placed in several locations or specified via command line. + +## Configuration File Locations + +GitSyncer looks for configuration files in the following order: + +1. Path specified by `--config` flag +2. `./gitsyncer.json` (current directory) +3. `~/.config/gitsyncer/config.json` +4. `~/.gitsyncer.json` + +## Configuration Structure + +### Basic Structure + +```json +{ + "organizations": [ + { + "host": "git@github.com", + "name": "myorg", + "github_token": "ghp_xxxxxxxxxxxx" + }, + { + "host": "git@codeberg.org", + "name": "myorg" + } + ], + "repositories": [ + "repo1", + "repo2" + ], + "exclude_branches": [ + "^temp-", + "-wip$" + ] +} +``` + +### Configuration Fields + +#### organizations (required) +Array of organization objects. At least one organization must be configured. + +##### Organization Object +- **host** (string, required): Git host URL + - Format: `git@hostname` for SSH + - Format: `file:///path/to/repos` for local repositories + - Examples: `git@github.com`, `git@codeberg.org`, `git@gitlab.com` +- **name** (string, required): Organization or username +- **github_token** (string, optional): GitHub personal access token + - Only needed for GitHub organizations + - Can also be set via environment variable or file + +#### repositories (optional) +Array of repository names to sync. If empty, use `--sync-codeberg-public` or `--sync-github-public` to discover repositories. + +#### exclude_branches (optional) +Array of regex patterns for branches to exclude from synchronization. + +## Examples + +### Minimal Configuration + +Sync between GitHub and Codeberg: + +```json +{ + "organizations": [ + {"host": "git@github.com", "name": "myusername"}, + {"host": "git@codeberg.org", "name": "myusername"} + ] +} +``` + +### With Specific Repositories + +```json +{ + "organizations": [ + {"host": "git@github.com", "name": "myorg"}, + {"host": "git@codeberg.org", "name": "myorg"} + ], + "repositories": [ + "project1", + "project2", + "project3" + ] +} +``` + +### With Branch Filtering + +```json +{ + "organizations": [ + {"host": "git@github.com", "name": "myorg"}, + {"host": "git@codeberg.org", "name": "myorg"} + ], + "repositories": ["myproject"], + "exclude_branches": [ + "^feature/experimental-", + "^temp-", + "-wip$", + "^old-" + ] +} +``` + +### Multiple Organizations + +```json +{ + "organizations": [ + {"host": "git@github.com", "name": "personal"}, + {"host": "git@github.com", "name": "work"}, + {"host": "git@codeberg.org", "name": "personal"}, + {"host": "git@gitlab.com", "name": "personal"} + ], + "repositories": ["shared-project"] +} +``` + +### Local Mirror Configuration + +```json +{ + "organizations": [ + {"host": "git@github.com", "name": "myorg"}, + {"host": "file:///home/user/git-mirror", "name": "myorg"} + ], + "repositories": ["important-project"] +} +``` + +## GitHub Token Configuration + +GitHub tokens are required for: +- Creating repositories (`--create-github-repos`) +- Listing private repositories +- Higher API rate limits + +### Token Sources (in order of precedence) + +1. **Configuration file**: `github_token` field in organization object +2. **Environment variable**: `GITHUB_TOKEN` +3. **Token file**: `~/.gitsyncer_github_token` + +### Creating a GitHub Token + +1. Go to GitHub Settings → Developer settings → Personal access tokens +2. Click "Generate new token (classic)" +3. Select scopes: + - `repo` (full control of private repositories) + - `read:org` (read organization membership) +4. Save the token securely + +### Setting the Token + +#### Method 1: Configuration File +```json +{ + "organizations": [ + { + "host": "git@github.com", + "name": "myorg", + "github_token": "ghp_xxxxxxxxxxxx" + } + ] +} +``` + +#### Method 2: Environment Variable +```bash +export GITHUB_TOKEN="ghp_xxxxxxxxxxxx" +gitsyncer --sync-all +``` + +#### Method 3: Token File +```bash +echo "ghp_xxxxxxxxxxxx" > ~/.gitsyncer_github_token +chmod 600 ~/.gitsyncer_github_token +``` + +### Testing Token + +```bash +gitsyncer --test-github-token +``` + +## Branch Exclusion Patterns + +The `exclude_branches` field accepts regular expressions to filter out branches from synchronization. + +### Common Patterns + +- `^temp-` - Exclude branches starting with "temp-" +- `-wip$` - Exclude branches ending with "-wip" +- `^feature/experimental-` - Exclude experimental feature branches +- `^(dev|development)$` - Exclude specific branch names +- `^release/\d+\.` - Exclude release branches (e.g., release/1.x) + +### Pattern Testing + +To see which branches are excluded: +```bash +gitsyncer --sync repo-name +# Output will show excluded branches and patterns +``` + +## Best Practices + +### 1. Start Simple +Begin with a minimal configuration and add complexity as needed. + +### 2. Use Dry Run +Test your configuration with `--dry-run` before actual synchronization: +```bash +gitsyncer --sync-all --dry-run +``` + +### 3. Secure Your Tokens +- Never commit tokens to version control +- Use environment variables or token files for sensitive data +- Restrict token permissions to minimum required + +### 4. Regular Expressions +- Test regex patterns before adding to configuration +- Use online regex testers to validate patterns +- Document complex patterns with comments + +### 5. Organization Naming +- Keep organization names consistent across platforms +- Use the same name on GitHub and Codeberg when possible + +## Troubleshooting + +### Configuration Not Found +```bash +$ gitsyncer --sync myrepo +No configuration file found. Please create one of: + - ./gitsyncer.json + - /home/user/.config/gitsyncer/config.json + - /home/user/.gitsyncer.json +``` + +**Solution**: Create a configuration file in one of the suggested locations. + +### Invalid JSON +```bash +$ gitsyncer --list-orgs +Failed to load configuration: invalid character '}' looking for beginning of object key string +``` + +**Solution**: Validate your JSON syntax using a JSON validator. + +### No Organizations Configured +```bash +$ gitsyncer --sync myrepo +Configuration must have at least one organization +``` + +**Solution**: Add at least one organization to the `organizations` array. + +### Token Issues +```bash +$ gitsyncer --test-github-token +ERROR: Token test failed: authentication failed (401) +``` + +**Solution**: +- Verify token is correct and not expired +- Check token has required permissions +- Ensure no extra whitespace in token
\ No newline at end of file diff --git a/doc/development.md b/doc/development.md new file mode 100644 index 0000000..b22fbd4 --- /dev/null +++ b/doc/development.md @@ -0,0 +1,447 @@ +# GitSyncer Development Guide + +This guide is for contributors who want to help develop GitSyncer. + +## Table of Contents + +- [Development Setup](#development-setup) +- [Project Structure](#project-structure) |
