Documentation ¶
Overview ¶
Package mirror periodically mirrors (bare clones) remote repositories locally. The mirror is created with `--mirror=fetch` hence everything in `refs/*` on the remote will be directly mirrored into `refs/*` in the local repository. it can also maintain multiple mirrored checked out worktrees on different references.
The implementation borrows heavily from kubernetes/git-sync. If you want to sync single repository on one reference then you are probably better off with kubernetes/git-sync, as it provides a lot more customisation. `git-mirror` should be used if multiple mirrored repositories with multiple checked out branches (worktrees) is required.
Usages ¶
please see examples below
Logging: ¶
package takes slog reference for logging and prints logs up to 'trace' level
Example:
loggerLevel = new(slog.LevelVar) levelStrings = map[string]slog.Level{ "trace": slog.Level(-8), "debug": slog.LevelDebug, "info": slog.LevelInfo, "warn": slog.LevelWarn, "error": slog.LevelError, } logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ Level: loggerLevel, })) loggerLevel.Set(levelStrings["trace"]) repos, err := NewRepoPool(conf, logger.With("logger", "git-mirror"), nil) if err != nil { panic(err) }
Example (WithOutWorktree) ¶
tmpRoot, err := os.MkdirTemp("", "git-mirror-without-worktree-example-*") if err != nil { panic(err) } defer os.RemoveAll(tmpRoot) config := ` defaults: root: interval: 30s mirror_timeout: 2m git_gc: always repositories: - remote: https://github.com/utilitywarehouse/git-mirror.git ` ctx := context.Background() conf := mirror.RepoPoolConfig{} err = yaml.Unmarshal([]byte(config), &conf) if err != nil { panic(err) } conf.Defaults.Root = tmpRoot repos, err := mirror.NewRepoPool(conf, slog.Default(), nil) if err != nil { panic(err) } // perform 1st mirror to ensure all repositories // initial mirror might take longer if err := repos.Mirror(ctx, 5*time.Minute); err != nil { panic(err) } // start mirror Loop repos.StartLoop() hash, err := repos.Hash(ctx, "https://github.com/utilitywarehouse/git-mirror.git", "main", "") if err != nil { panic(err) } fmt.Println("last commit hash at main", "hash", hash) msg, err := repos.LogMsg(ctx, "https://github.com/utilitywarehouse/git-mirror.git", "main", "") if err != nil { panic(err) } fmt.Println("last commit msg at main", "msg", msg)
Output:
Example (Worktree) ¶
tmpRoot, err := os.MkdirTemp("", "git-mirror-worktree-example-*") if err != nil { panic(err) } defer os.RemoveAll(tmpRoot) config := ` defaults: root: /tmp/git-mirror interval: 30s mirror_timeout: 2m git_gc: always repositories: - remote: https://github.com/utilitywarehouse/git-mirror.git worktrees: - link: main ` ctx := context.Background() conf := mirror.RepoPoolConfig{} err = yaml.Unmarshal([]byte(config), &conf) if err != nil { panic(err) } conf.Defaults.Root = tmpRoot repos, err := mirror.NewRepoPool(conf, slog.Default(), nil) if err != nil { panic(err) } // perform 1st mirror to ensure all repositories // initial mirror might take longer if err := repos.Mirror(ctx, 5*time.Minute); err != nil { panic(err) } // start mirror Loop repos.StartLoop() hash, err := repos.Hash(ctx, "https://github.com/utilitywarehouse/git-mirror.git", "main", "") if err != nil { panic(err) } fmt.Println("last commit hash at main", "hash", hash) msg, err := repos.LogMsg(ctx, "https://github.com/utilitywarehouse/git-mirror.git", "main", "") if err != nil { panic(err) } fmt.Println("last commit msg at main", "msg", msg) // make sure file exists in the tree _, err = os.Stat(tmpRoot + "/main/pkg/mirror/repository.go") if err != nil { panic(err) }
Output:
Index ¶
- Variables
- func EnableMetrics(metricsNamespace string, registerer prometheus.Registerer)
- func NormaliseURL(rawURL string) string
- func SameRawURL(lRepo, rRepo string) (bool, error)
- func SameURL(lURL, rURL *GitURL) bool
- type Auth
- type DefaultConfig
- type GitURL
- type RepoPool
- func (rp *RepoPool) AddRepository(repo *Repository) error
- func (rp *RepoPool) AddWorktreeLink(remote string, link, ref, pathspec string) error
- func (rp *RepoPool) Clone(ctx context.Context, remote, dst, branch, pathspec string, rmGitDir bool) (string, error)
- func (rp *RepoPool) Hash(ctx context.Context, remote, ref, path string) (string, error)
- func (rp *RepoPool) LogMsg(ctx context.Context, remote, ref, path string) (string, error)
- func (rp *RepoPool) Mirror(ctx context.Context, timeout time.Duration) error
- func (rp *RepoPool) Repository(remote string) (*Repository, error)
- func (rp *RepoPool) StartLoop()
- type RepoPoolConfig
- type Repository
- func (r *Repository) AddWorktreeLink(link, ref, pathspec string) error
- func (r *Repository) Clone(ctx context.Context, dst, branch, pathspec string, rmGitDir bool) (string, error)
- func (r *Repository) Hash(ctx context.Context, ref, path string) (string, error)
- func (r *Repository) LogMsg(ctx context.Context, ref, path string) (string, error)
- func (r *Repository) Mirror(ctx context.Context) error
- func (r *Repository) StartLoop(ctx context.Context)
- type RepositoryConfig
- type WorkTreeLink
- type WorktreeConfig
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrExist = fmt.Errorf("repo already exist") ErrNotExist = fmt.Errorf("repo does not exist") )
Functions ¶
func EnableMetrics ¶
func EnableMetrics(metricsNamespace string, registerer prometheus.Registerer)
EnableMetrics will enable metrics collection for git mirrors. Available metrics are...
- git_last_mirror_timestamp - (tags: repo) A Gauge that captures the Timestamp of the last successful git sync per repo.
- git_mirror_count - (tags: repo,success) A Counter for each repo sync, incremented with each sync attempt and tagged with the result (success=true|false)
- git_mirror_latency_seconds - (tags: repo) A Summary that keeps track of the git sync latency per repo.
func SameRawURL ¶
SameRawURL returns whether or not the two remote URL strings are equivalent
Types ¶
type Auth ¶
type Auth struct { // path to the ssh key used to fetch remote SSHKeyPath string `yaml:"ssh_key_path"` // path to the known hosts of the remote host SSHKnownHostsPath string `yaml:"ssh_known_hosts_path"` }
Auth represents authentication config of the repository
type DefaultConfig ¶
type DefaultConfig struct { // Root is the absolute path to the root dir where all repositories directories // will be created all repos worktree links will be created here if not // specified in repo config Root string `yaml:"root"` // Interval is time duration for how long to wait between mirrors Interval time.Duration `yaml:"interval"` // MirrorTimeout represents the total time allowed for the complete mirror loop MirrorTimeout time.Duration `yaml:"mirror_timeout"` // GitGC garbage collection string. valid values are // 'auto', 'always', 'aggressive' or 'off' GitGC string `yaml:"git_gc"` // Auth config to fetch remote repos Auth Auth `yaml:"auth"` }
DefaultConfig is the default config for repositories if not set at repo level
type GitURL ¶
type GitURL struct {
// contains filtered or unexported fields
}
GitURL represents parsed git url
func ParseGitURL ¶
ParseGitURL parses a raw url into a GitURL structure. valid git urls are...
- user@host.xz:path/to/repo.git
- ssh://user@host.xz[:port]/path/to/repo.git
- https://host.xz[:port]/path/to/repo.git
type RepoPool ¶
type RepoPool struct {
// contains filtered or unexported fields
}
RepoPool represents the collection of mirrored repositories it provides simple wrapper around Repository methods. A RepoPool is safe for concurrent use by multiple goroutines.
func NewRepoPool ¶
NewRepoPool will create mirror repositories based on given config. Remote repo will not be mirrored until either Mirror() or StartLoop() is called
func (*RepoPool) AddRepository ¶
func (rp *RepoPool) AddRepository(repo *Repository) error
AddRepository will add given repository to repoPool. Remote repo will not be mirrored until either Mirror() or StartLoop() is called
func (*RepoPool) AddWorktreeLink ¶
AddWorktreeLink is wrapper around repositories AddWorktreeLink method
func (*RepoPool) Clone ¶
func (rp *RepoPool) Clone(ctx context.Context, remote, dst, branch, pathspec string, rmGitDir bool) (string, error)
Clone is wrapper around repositories Clone method
func (*RepoPool) Mirror ¶
Mirror will trigger mirror on every repo in foreground with given timeout. It will error out if any of the repository mirror errors. Ideally Mirror should be used for the first mirror cycle to ensure repositories are successfully mirrored
func (*RepoPool) Repository ¶
func (rp *RepoPool) Repository(remote string) (*Repository, error)
Repository will return Repository object based on given remote URL
type RepoPoolConfig ¶
type RepoPoolConfig struct { // default config for all the repositories if not set Defaults DefaultConfig `yaml:"defaults"` // List of mirrored repositories. Repositories []RepositoryConfig `yaml:"repositories"` }
RepoPoolConfig is the configuration to create repoPool
func (*RepoPoolConfig) ApplyDefaults ¶
func (rpc *RepoPoolConfig) ApplyDefaults()
ApplyDefaults will add given default config to repository config if where needed
func (*RepoPoolConfig) ValidateDefaults ¶
func (rpc *RepoPoolConfig) ValidateDefaults() error
ValidateDefaults will verify default config
func (*RepoPoolConfig) ValidateLinkPaths ¶
func (rpc *RepoPoolConfig) ValidateLinkPaths() error
It is possible that same root is used for multiple repositories since Links are placed at the root, we need to make sure that all link's name (path) are diff. ValidateLinkPaths makes sures all link's absolute paths are different.
type Repository ¶
type Repository struct {
// contains filtered or unexported fields
}
Repository represents the mirrored repository of the given remote. The implementation borrows heavily from https://github.com/kubernetes/git-sync. A Repository is safe for concurrent use by multiple goroutines.
func NewRepository ¶
func NewRepository(repoConf RepositoryConfig, envs []string, log *slog.Logger) (*Repository, error)
NewRepository creates new repository from the given config. Remote repo will not be mirrored until either Mirror() or StartLoop() is called.
func (*Repository) AddWorktreeLink ¶
func (r *Repository) AddWorktreeLink(link, ref, pathspec string) error
AddWorktreeLink adds add workTree link to the mirror repository.
func (*Repository) Clone ¶
func (r *Repository) Clone(ctx context.Context, dst, branch, pathspec string, rmGitDir bool) (string, error)
Clone creates a single-branch local clone of the mirrored repository to a new location on disk. On success, it returns the hash of the new repository clone's HEAD. if pathspec is provided only those paths will be checked out. if rmGitDir is true `.git` folder will be deleted after the clone. if dst not empty all its contents will be removed
func (*Repository) Hash ¶
Hash returns the hash of the given revision and for the path if specified.
func (*Repository) LogMsg ¶
LogMsg returns the formatted log subject with author info of the given revision and for the path if specified.
func (*Repository) Mirror ¶
func (r *Repository) Mirror(ctx context.Context) error
Mirror will run mirror loop of the repository
- init and validate if existing repo dir
- fetch remote
- ensure worktrees
- cleanup if needed
func (*Repository) StartLoop ¶
func (r *Repository) StartLoop(ctx context.Context)
StartLoop mirrors repository periodically based on repo's mirror interval
type RepositoryConfig ¶
type RepositoryConfig struct { // git URL of the remote repo to mirror Remote string `yaml:"remote"` // Root is the absolute path to the root dir where repo dir // will be created. Worktree links will be created here if // absolute path is not provided Root string `yaml:"root"` // Interval is time duration for how long to wait between mirrors Interval time.Duration `yaml:"interval"` // MirrorTimeout represents the total time allowed for the complete mirror loop MirrorTimeout time.Duration `yaml:"mirror_timeout"` // GitGC garbage collection string. valid values are // 'auto', 'always', 'aggressive' or 'off' GitGC string `yaml:"git_gc"` // Auth config to fetch remote repos Auth Auth `yaml:"auth"` // Worktrees contains list of worktrees links which will be maintained. // worktrees are optional repo can be mirrored without worktree Worktrees []WorktreeConfig `yaml:"worktrees"` }
RepositoryConfig represents the config for the mirrored repository of the given remote.
type WorkTreeLink ¶
type WorkTreeLink struct {
// contains filtered or unexported fields
}
type WorktreeConfig ¶
type WorktreeConfig struct { // Link is the path at which to create a symlink to the worktree dir // if path is not absolute it will be created under repository root Link string `yaml:"link"` // Ref represents the git reference of the worktree branch, tags or hash // are supported. default is HEAD Ref string `yaml:"ref"` // Pathspec of the dirs to checkout if required Pathspec string `yaml:"pathspec"` }
Worktree represents maintained worktree on given link.