mirror

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 28, 2024 License: MIT Imports: 17 Imported by: 0

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

Examples

Constants

This section is empty.

Variables

View Source
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 NormaliseURL

func NormaliseURL(rawURL string) string

NormaliseURL will return normalised url

func SameRawURL

func SameRawURL(lRepo, rRepo string) (bool, error)

SameRawURL returns whether or not the two remote URL strings are equivalent

func SameURL

func SameURL(lURL, rURL *GitURL) bool

SameURL returns whether or not the two parsed git URLs are equivalent. git URLs can be represented in multiple schemes so if host, path and repo name of URLs are same then those URLs are for the same remote repository

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

func ParseGitURL(rawURL string) (*GitURL, error)

ParseGitURL parses a raw url into a GitURL structure. valid git urls are...

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

func NewRepoPool(conf RepoPoolConfig, log *slog.Logger, commonENVs []string) (*RepoPool, error)

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 (rp *RepoPool) AddWorktreeLink(remote string, link, ref, pathspec string) error

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) Hash

func (rp *RepoPool) Hash(ctx context.Context, remote, ref, path string) (string, error)

Hash is wrapper around repositories hash method

func (*RepoPool) LogMsg

func (rp *RepoPool) LogMsg(ctx context.Context, remote, ref, path string) (string, error)

LogMsg is wrapper around repositories LogMsg method

func (*RepoPool) Mirror

func (rp *RepoPool) Mirror(ctx context.Context, timeout time.Duration) error

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

func (*RepoPool) StartLoop

func (rp *RepoPool) StartLoop()

StartLoop will start mirror loop on all repositories if its not already started

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 (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

func (r *Repository) Hash(ctx context.Context, ref, path string) (string, error)

Hash returns the hash of the given revision and for the path if specified.

func (*Repository) LogMsg

func (r *Repository) LogMsg(ctx context.Context, ref, path string) (string, error)

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

  1. init and validate if existing repo dir
  2. fetch remote
  3. ensure worktrees
  4. 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 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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL