all repos — homestead @ d9a119ca0abe7ffedd125c81c78f725aab886d29

Code for my website

shared/vcs/repository.go (view raw)

package vcs

import (
	"errors"
	"os"

	"alin.ovh/x/log"
	"github.com/Southclaws/fault"
	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"

	"alin.ovh/homestead/shared/config"
	"alin.ovh/homestead/shared/file"
)

type Options struct {
	LocalPath string
	RemoteURL config.URL
	Branch    string
}

type Repository struct {
	repo      *git.Repository
	log       *log.Logger
	remoteURL config.URL
}

func CloneOrOpen(cfg *Options, log *log.Logger) (*Repository, bool, error) {
	var r *git.Repository
	var err error
	exists := file.Exists(cfg.LocalPath)
	if !exists {
		r, err = git.PlainClone(cfg.LocalPath, false, &git.CloneOptions{
			URL:      cfg.RemoteURL.String(),
			Progress: os.Stdout,
		})
	} else {
		r, err = git.PlainOpen(cfg.LocalPath)
	}
	if err != nil {
		return nil, exists, fault.Wrap(err)
	}

	return &Repository{
		log:       log,
		remoteURL: cfg.RemoteURL,
		repo:      r,
	}, exists, nil
}

func (r *Repository) Update(rev string) (bool, error) {
	r.log.Info("updating repository", "from", r.HeadSHA())
	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, fault.Wrap(err)
	}

	rem, err := r.repo.Remote("origin")
	if err != nil {
		return false, fault.Wrap(err)
	}
	refs, err := rem.List(&git.ListOptions{
		Timeout: 5,
	})
	if err != nil {
		return false, fault.Wrap(err)
	}

	var hash plumbing.Hash
	if rev != "" {
		for _, ref := range refs {
			if ref.Name() == plumbing.Main {
				hash = ref.Hash()

				break
			}
		}
	} else {
		hash = plumbing.NewHash(rev)
	}

	wt, err := r.repo.Worktree()
	if err != nil {
		return false, fault.Wrap(err)
	}
	err = wt.Checkout(&git.CheckoutOptions{
		Hash:  hash,
		Force: true,
	})
	if err != nil {
		return false, fault.Wrap(err)
	}

	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 fault.Wrap(err)
	}

	if !st.IsClean() {
		err = wt.Clean(&git.CleanOptions{
			Dir: true,
		})
		if err != nil {
			return fault.Wrap(err)
		}
	}

	return nil
}

func (r *Repository) HeadSHA() string {
	head, err := r.repo.Head()
	if err != nil {
		return ""
	}

	return head.Hash().String()
}