eclipse/lib/gitcache/gitcache.go

126 lines
2.6 KiB
Go
Raw Normal View History

2024-01-11 20:36:03 +00:00
// Copyright (C) 2024 Umorpha Systems
// SPDX-License-Identifier: AGPL-3.0-or-later
package gitcache
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"sync"
"time"
"github.com/kballard/go-shellquote"
)
const tsDir = "x-gitcache-ts"
type Cache struct {
Dir string
MinPeriod time.Duration
initOnce sync.Once
initErr error
}
// "ll" stands for "low level" /////////////////////////////////////////////////
func (cache *Cache) llInit() error {
if err := exec.Command("git", "init", "--bare", cache.Dir).Run(); err != nil {
return errorWithStderr(err)
}
if err := os.Mkdir(filepath.Join(cache.Dir, tsDir), 0o777); err != nil && !errors.Is(err, os.ErrExist) {
return err
}
return nil
}
func (cache *Cache) llFetch(namespace, url string) error {
cmd := exec.Command("git",
"--namespace="+namespace,
"fetch",
"--no-write-fetch-head",
"--no-recurse-submodules",
"--no-tags",
"--prune",
"--",
url, "*:*")
cmd.Dir = cache.Dir
if err := cmd.Run(); err != nil {
var eErr *exec.ExitError
if errors.As(err, &eErr) {
err = fmt.Errorf("%w: %s", err, eErr.Stderr)
}
return err
}
return nil
}
////////////////////////////////////////////////////////////////////////////////
func (cache *Cache) init() error {
cache.initOnce.Do(func() {
cache.initErr = cache.llInit()
})
return cache.initErr
}
func (cache *Cache) Fetch(url string) error {
if err := cache.init(); err != nil {
return err
}
id := url2id(url)
tsFile := filepath.Join(cache.Dir, tsDir, id)
if cache.MinPeriod != 0 {
ts, err := mtime(tsFile)
if err == nil && ts.Add(cache.MinPeriod).After(time.Now()) {
return nil
}
}
if err := cache.llFetch(id, url); err != nil {
return err
}
return touch(tsFile)
}
func (cache *Cache) RevParse(url, rev string) (string, error) {
if err := cache.Fetch(url); err != nil {
return "", err
}
cmd := exec.Command("git", "--namespace="+url2id(url), "rev-parse", "--verify", rev)
cmd.Dir = cache.Dir
out, err := cmd.Output()
if err != nil {
return "", errorWithStderr(err)
}
return string(out[:len(out)-1]), nil
}
func (cache *Cache) Clone(url, dir string, flags ...string) error {
if err := cache.Fetch(url); err != nil {
return err
}
cacheDir, err := filepath.Abs(cache.Dir)
if err != nil {
return err
}
cacheURL := "ext::" + shellquote.Join(
"git",
"--namespace="+url2id(url),
"%s",
"file://"+filepath.ToSlash(cacheDir))
cmd := exec.Command("git", append(append([]string{
"-c", "url." + cacheURL + ".insteadOf=" + url,
"clone",
}, flags...), "--", url, dir)...)
if err := cmd.Run(); err != nil {
return errorWithStderr(err)
}
return nil
}