Files
.gitea
cli
cmd
pkg
app
autocomplete
catalogue
client
compose
config
container
context
dns
git
clone.go
common.go
init.go
read.go
limit
recipe
secret
server
ssh
upstream
web
scripts
tests
.drone.yml
.envrc.sample
.gitignore
.goreleaser.yml
Makefile
README.md
go.mod
go.sum
renovate.json
abra/pkg/git/read.go

190 lines
3.9 KiB
Go

package git
import (
"io/ioutil"
"os"
"os/user"
"path"
"path/filepath"
"strings"
"coopcloud.tech/abra/pkg/config"
"github.com/go-git/go-git/v5"
gitConfigPkg "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
"github.com/sirupsen/logrus"
)
// GetRecipeHead retrieves latest HEAD metadata.
func GetRecipeHead(recipeName string) (*plumbing.Reference, error) {
recipeDir := path.Join(config.ABRA_DIR, "apps", recipeName)
repo, err := git.PlainOpen(recipeDir)
if err != nil {
return nil, err
}
head, err := repo.Head()
if err != nil {
return nil, err
}
return head, nil
}
// IsClean checks if a repo has unstaged changes
func IsClean(recipeName string) (bool, error) {
recipeDir := path.Join(config.ABRA_DIR, "apps", recipeName)
repo, err := git.PlainOpen(recipeDir)
if err != nil {
return false, err
}
worktree, err := repo.Worktree()
if err != nil {
return false, err
}
patterns, err := GetExcludesFiles()
if err != nil {
return false, err
}
if len(patterns) > 0 {
worktree.Excludes = append(patterns, worktree.Excludes...)
}
status, err := worktree.Status()
if err != nil {
return false, err
}
if status.String() != "" {
logrus.Debugf("discovered git status for %s repository: %s", recipeName, status.String())
} else {
logrus.Debugf("discovered clean git status for %s repository", recipeName)
}
return status.IsClean(), nil
}
// GetExcludesFiles reads the exlude files from a global git ignore
func GetExcludesFiles() ([]gitignore.Pattern, error) {
var err error
var patterns []gitignore.Pattern
cfg, err := parseGitConfig()
if err != nil {
return patterns, err
}
excludesfile := getExcludesFile(cfg)
patterns, err = parseExcludesFile(excludesfile)
if err != nil {
return patterns, err
}
return patterns, nil
}
func parseGitConfig() (*gitConfigPkg.Config, error) {
cfg := gitConfigPkg.NewConfig()
usr, err := user.Current()
if err != nil {
return nil, err
}
globalGitConfig := filepath.Join(usr.HomeDir, ".gitconfig")
if _, err := os.Stat(globalGitConfig); err != nil {
if os.IsNotExist(err) {
logrus.Debugf("no %s exists, not reading any global git ignore config", globalGitConfig)
return cfg, nil
}
return cfg, err
}
b, err := ioutil.ReadFile(globalGitConfig)
if err != nil {
return nil, err
}
if err := cfg.Unmarshal(b); err != nil {
return nil, err
}
return cfg, err
}
func getExcludesFile(cfg *gitConfigPkg.Config) string {
for _, sec := range cfg.Raw.Sections {
if sec.Name == "core" {
for _, opt := range sec.Options {
if opt.Key == "excludesfile" {
return opt.Value
}
}
}
}
return "~/.gitignore"
}
func parseExcludesFile(excludesfile string) ([]gitignore.Pattern, error) {
var ps []gitignore.Pattern
excludesfile, err := expandTilde(excludesfile)
if err != nil {
return nil, err
}
if _, err := os.Stat(excludesfile); err != nil {
if os.IsNotExist(err) {
logrus.Debugf("no %s exists, skipping reading ignore paths", excludesfile)
return ps, nil
}
return ps, err
}
data, err := ioutil.ReadFile(excludesfile)
if err != nil {
return nil, err
}
var pathsRaw []string
for _, s := range strings.Split(string(data), "\n") {
if !strings.HasPrefix(s, "#") && len(strings.TrimSpace(s)) > 0 {
pathsRaw = append(pathsRaw, s)
ps = append(ps, gitignore.ParsePattern(s, nil))
}
}
logrus.Debugf("read global ignore paths: %s", strings.Join(pathsRaw, " "))
return ps, nil
}
func expandTilde(path string) (string, error) {
if !strings.HasPrefix(path, "~") {
return path, nil
}
var paths []string
u, err := user.Current()
if err != nil {
return "", err
}
for _, p := range strings.Split(path, string(filepath.Separator)) {
if p == "~" {
paths = append(paths, u.HomeDir)
} else {
paths = append(paths, p)
}
}
return filepath.Join(paths...), nil
}