autogits/bots-common/git_utils.go
2024-07-11 16:45:49 +02:00

226 lines
4.9 KiB
Go

package common
import (
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
)
//func (h *RequestHandler) ProcessBranchList() []string {
// if h.HasError() {
// return make([]string, 0)
// }
//
// trackedBranches, err := os.ReadFile(path.Join(h.GitPath, DefaultGitPrj, TrackedBranchesFile))
// if err != nil {
// if errors.Is(err, os.ErrNotExist) {
// trackedBranches = []byte("factory")
// } else {
// h.LogError("file error reading '%s' file in repo", TrackedBranchesFile)
// h.Error = err
// return make([]string, 0)
// }
// }
//
// return strings.Split(string(trackedBranches), "\n")
//}
type GitReference struct {
Branch string
Id string
}
type GitReferences struct {
refs []GitReference
}
func (refs *GitReferences) addReference(id, branch string) {
for _, ref := range refs.refs {
if ref.Id == id && ref.Branch == branch {
return
}
}
refs.refs = append(refs.refs, GitReference{Branch: branch, Id: id})
}
func processRefs(gitDir string) ([]GitReference, error) {
packedRefsPath := path.Join(gitDir, "packed-refs")
stat, err := os.Stat(packedRefsPath)
if err != nil {
return nil, err
}
if stat.Size() > 10000 || stat.IsDir() {
return nil, fmt.Errorf("Funny business with 'packed-refs' in '%s'", gitDir)
}
data, err := os.ReadFile(packedRefsPath)
if err != nil {
return nil, err
}
var references GitReferences
for _, line := range strings.Split(string(data), "\n") {
if len(line) < 1 || line[0] == '#' {
continue
}
splitLine := strings.Split(line, " ")
if len(splitLine) != 2 {
return nil, fmt.Errorf("Unexpected packaged-refs entry '%#v' in '%s'", splitLine, packedRefsPath)
}
id, ref := splitLine[0], splitLine[1]
const remoteRefPrefix = "refs/remotes/origin/"
if ref[0:len(remoteRefPrefix)] != remoteRefPrefix {
continue
}
references.addReference(id, ref[len(remoteRefPrefix):])
}
return references.refs, nil
}
func findGitDir(p string) (string, error) {
gitFile := path.Join(p, ".git")
stat, err := os.Stat(gitFile)
if err != nil {
return "", err
}
if stat.IsDir() {
return path.Join(p, ".git"), nil
}
data, err := os.ReadFile(gitFile)
if err != nil {
return "", err
}
for _, line := range strings.Split(string(data), "\n") {
refs := strings.Split(line, ":")
if len(refs) != 2 {
return "", fmt.Errorf("Unknown format of .git file: '%s'\n", line)
}
if refs[0] != "gitdir" {
return "", fmt.Errorf("Unknown header of .git file: '%s'\n", refs[0])
}
return path.Join(p, strings.TrimSpace(refs[1])), nil
}
return "", fmt.Errorf("Can't find git subdirectory in '%s'", p)
}
func (e *RequestHandler) GitBranchHead(gitDir, branchName string) (string, error) {
if e.HasError() {
return "", e.Error
}
path, err := findGitDir(path.Join(e.GitPath, gitDir))
if err != nil {
e.LogError("Error identifying gitdir in `%s`: %#v", gitDir, err)
e.Error = err
}
refs, err := processRefs(path)
if err != nil {
e.LogError("Error finding branches (%s): %#v", branchName, err)
e.Error = err
return "", e.Error
}
for _, ref := range refs {
if ref.Branch == branchName {
return ref.Id, nil
}
}
e.Error = fmt.Errorf("Can't find default remote branch: %s", branchName)
e.LogError("%s", e.Error.Error())
return "", e.Error
}
type ExecStream interface {
Close()
HasError() bool
GitExec(cwd string, param ...string) ExecStream
}
func (e *RequestHandler) Close() {
if e.GitPath == "" {
return
}
e.Error = os.RemoveAll(e.GitPath)
e.GitPath = ""
return
}
func (e *RequestHandler) HasError() bool {
return e.Error != nil
}
type writeFunc func(data []byte) (int, error)
func (f writeFunc) Write(data []byte) (int, error) {
return f(data)
}
func (e *RequestHandler) GitExec(cwd string, params ...string) ExecStream {
if e.Error != nil {
return e
}
cmd := exec.Command("/usr/bin/git", params...)
cmd.Env = []string{
"GIT_CEILING_DIRECTORIES=" + e.GitPath,
"GIT_CONFIG_GLOBAL=/dev/null",
"GIT_AUTHOR_NAME=" + e.GitCommiter,
"EMAIL=not@exist@src.opensuse.org",
"GIT_LFS_SKIP_SMUDGE=1",
"GIT_SSH_COMMAND=/usr/bin/ssh -o StrictHostKeyChecking=yes",
}
cmd.Dir = filepath.Join(e.GitPath, cwd)
cmd.Stdout = writeFunc(func(data []byte) (int, error) {
e.Logger.Log("%s", data)
return len(data), nil
})
cmd.Stderr = writeFunc(func(data []byte) (int, error) {
e.Logger.LogError("%s", data)
return len(data), nil
})
cmd.Stdin = nil
e.Log("git execute: %#v", cmd.Args)
e.Error = cmd.Run()
return e
}
type writerFunc func([]byte) (int, error)
// Implement the Write method for the custom type
func (f writerFunc) Write(p []byte) (int, error) {
return f(p)
}
func (e *RequestHandler) GitSubmoduleCommitId(cwd, packageName string) (string, bool) {
if e.Error != nil {
return "", false
}
cmd := exec.Command("/usr/bin/git", "cat-file", "--batch")
cmd.Dir = filepath.Join(e.GitPath, cwd)
cmd.Stdout = writerFunc(func(p []byte) (int, error) {
return len(p), nil
})
return "", false
}