diff options
Diffstat (limited to 'internal/vcs')
-rw-r--r-- | internal/vcs/repository.go | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/internal/vcs/repository.go b/internal/vcs/repository.go new file mode 100644 index 0000000..5950e53 --- /dev/null +++ b/internal/vcs/repository.go @@ -0,0 +1,123 @@ +package vcs + +import ( + "os" + + "go.alanpearce.eu/website/internal/config" + "go.alanpearce.eu/x/log" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "gitlab.com/tozd/go/errors" +) + +type Config struct { + LocalPath string + RemoteURL config.URL + Branch string `conf:"default:main"` +} + +type Repository struct { + repo *git.Repository + log *log.Logger +} + +func CloneOrUpdate(cfg *Config, log *log.Logger) (*Repository, error) { + gr, err := git.PlainClone(cfg.LocalPath, false, &git.CloneOptions{ + URL: cfg.RemoteURL.String(), + Progress: os.Stdout, + }) + if err != nil { + if !errors.Is(err, git.ErrRepositoryAlreadyExists) { + return nil, err + } + gr, err = git.PlainOpen(cfg.LocalPath) + if err != nil { + return nil, err + } + repo := &Repository{ + repo: gr, + log: log, + } + _, err := repo.Update() + if err != nil { + return nil, err + } + + return repo, nil + } + + return &Repository{ + repo: gr, + log: log, + }, nil +} + +func (r *Repository) Update() (bool, error) { + r.log.Info("updating repository") + + head, err := r.repo.Head() + if err != nil { + return false, err + } + + r.log.Info("updating from", "rev", head.Hash().String()) + err = r.repo.Fetch(&git.FetchOptions{ + Prune: true, + }) + if err != nil { + if errors.Is(err, git.NoErrAlreadyUpToDate) { + r.log.Info("already up-to-date") + + return true, nil + } + + return false, err + } + + rem, err := r.repo.Remote("origin") + if err != nil { + return false, err + } + refs, err := rem.List(&git.ListOptions{ + Timeout: 5, + }) + + var hash plumbing.Hash + for _, ref := range refs { + if ref.Name() == plumbing.Main { + hash = ref.Hash() + } + } + + wt, err := r.repo.Worktree() + if err != nil { + return false, err + } + wt.Checkout(&git.CheckoutOptions{ + Hash: hash, + Force: true, + }) + + r.log.Info("updated to", "rev", hash) + + return true, r.Clean(wt) +} + +func (r *Repository) Clean(wt *git.Worktree) error { + st, err := wt.Status() + if err != nil { + return err + } + + if !st.IsClean() { + err = wt.Clean(&git.CleanOptions{ + Dir: true, + }) + if err != nil { + return err + } + } + + return nil +} |