forked from toolshed/abra
.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
190 lines
3.9 KiB
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
|
|
}
|