177 lines
4.6 KiB
Go
177 lines
4.6 KiB
Go
|
package roast
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"git-roast/internal/config"
|
||
|
"git-roast/internal/log"
|
||
|
"net/url"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/go-git/go-git/v5"
|
||
|
)
|
||
|
|
||
|
type Roast struct {
|
||
|
repo *git.Repository
|
||
|
dir string
|
||
|
}
|
||
|
|
||
|
func getRepoOrigin() (string, error) {
|
||
|
repo, err := git.PlainOpen(".")
|
||
|
if err != nil {
|
||
|
return "", errors.Join(errors.New("failed to open repository"), err)
|
||
|
}
|
||
|
|
||
|
origin, err := repo.Remote("origin")
|
||
|
if err != nil {
|
||
|
return "", errors.Join(errors.New("failed to open remote origin"), err)
|
||
|
}
|
||
|
|
||
|
originConfig := origin.Config()
|
||
|
if originConfig == nil {
|
||
|
return "", errors.New("failed to read origin config")
|
||
|
}
|
||
|
|
||
|
if len(originConfig.URLs) == 0 {
|
||
|
return "", errors.New("no urls in remote origin")
|
||
|
}
|
||
|
|
||
|
return originConfig.URLs[0], nil
|
||
|
}
|
||
|
|
||
|
func Init() error {
|
||
|
repoOriginUrl, err := getRepoOrigin()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
id := url.PathEscape(repoOriginUrl)
|
||
|
|
||
|
p := filepath.Join(config.ContentPath, id)
|
||
|
|
||
|
initSuccessful := false
|
||
|
defer func() {
|
||
|
if !initSuccessful {
|
||
|
_ = os.RemoveAll(p)
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
if err := os.RemoveAll(p); err != nil && !os.IsNotExist(err) {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
var roastRepoOriginUrl string
|
||
|
if s, hasSuffix := strings.CutSuffix(repoOriginUrl, ".git"); hasSuffix {
|
||
|
roastRepoOriginUrl = s + ".roast.git"
|
||
|
} else {
|
||
|
roastRepoOriginUrl = s + ".roast"
|
||
|
}
|
||
|
|
||
|
{
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||
|
defer cancel()
|
||
|
cmd := exec.CommandContext(
|
||
|
ctx,
|
||
|
"git",
|
||
|
"clone",
|
||
|
roastRepoOriginUrl,
|
||
|
p,
|
||
|
)
|
||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
log.Printf("failed to clone %s\n%s\n%s\n", roastRepoOriginUrl, out, err.Error())
|
||
|
} else {
|
||
|
log.Debugf("cloned %s\n", roastRepoOriginUrl)
|
||
|
|
||
|
repo, err := git.PlainOpen(p)
|
||
|
if err != nil {
|
||
|
return errors.Join(errors.New("failed to open roast repo"), err)
|
||
|
}
|
||
|
if commitIter, err := repo.CommitObjects(); err != nil {
|
||
|
return errors.Join(errors.New("failed to get commits for roast repo"), err)
|
||
|
} else if _, err := commitIter.Next(); err != nil {
|
||
|
// repo has no commits
|
||
|
// git commit --allow-empty -m init
|
||
|
cmd = exec.Command("git", "commit", "--allow-empty", "-m", "init")
|
||
|
cmd.Dir = p
|
||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
return errors.Join(fmt.Errorf("failed to add initial commit to roast repo\n%s\n", out), err)
|
||
|
}
|
||
|
// git push -u origin main
|
||
|
cmd = exec.Command("git", "push", "-u", "origin", "main")
|
||
|
cmd.Dir = p
|
||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
return errors.Join(fmt.Errorf("failed to push roast repo to %s\n%s\n", roastRepoOriginUrl, out), err)
|
||
|
}
|
||
|
log.Debugf("roast repository init pushed to %s\n", roastRepoOriginUrl)
|
||
|
}
|
||
|
|
||
|
initSuccessful = true
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
// git init
|
||
|
cmd := exec.Command("git", "init", p)
|
||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
return errors.Join(fmt.Errorf("failed to init %s\n%s\n", roastRepoOriginUrl, out), err)
|
||
|
}
|
||
|
log.Debugf("init %s successful\n", roastRepoOriginUrl)
|
||
|
// git remote add origin $roastRepoOriginUrl
|
||
|
cmd = exec.Command("git", "remote", "add", "origin", roastRepoOriginUrl)
|
||
|
cmd.Dir = p
|
||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
return errors.Join(fmt.Errorf("failed to add remote %s to roast repo\n%s\n", roastRepoOriginUrl, out), err)
|
||
|
}
|
||
|
// git commit --allow-empty -m init
|
||
|
cmd = exec.Command("git", "commit", "--allow-empty", "-m", "init")
|
||
|
cmd.Dir = p
|
||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
return errors.Join(fmt.Errorf("failed to add initial commit to roast repo\n%s\n", out), err)
|
||
|
}
|
||
|
// git push -u origin main
|
||
|
cmd = exec.Command("git", "push", "-u", "origin", "main")
|
||
|
cmd.Dir = p
|
||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||
|
return errors.Join(fmt.Errorf("failed to push roast repo to %s\n%s\n", roastRepoOriginUrl, out), err)
|
||
|
}
|
||
|
log.Debugf("new roast repository pushed to %s\n", roastRepoOriginUrl)
|
||
|
}
|
||
|
|
||
|
initSuccessful = true
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func Open() (*Roast, error) {
|
||
|
repoOriginUrl, err := getRepoOrigin()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
id := url.PathEscape(repoOriginUrl)
|
||
|
|
||
|
p := filepath.Join(config.ContentPath, id)
|
||
|
if stat, err := os.Stat(p); err != nil {
|
||
|
if os.IsNotExist(err) {
|
||
|
return nil, errors.New("roast repo not initialized, use `git roast init`")
|
||
|
} else {
|
||
|
return nil, errors.Join(errors.New("failed to check if roast dir for repo exists"), err)
|
||
|
}
|
||
|
} else if !stat.IsDir() {
|
||
|
return nil, errors.Join(errors.New("path for roast dir for repo exists but is not a directory"), err)
|
||
|
}
|
||
|
|
||
|
roastRepo, err := git.PlainOpen(p)
|
||
|
if err != nil {
|
||
|
return nil, errors.Join(fmt.Errorf("failed to open roast repo %s", p), err)
|
||
|
}
|
||
|
|
||
|
return &Roast{
|
||
|
repo: roastRepo,
|
||
|
dir: p,
|
||
|
}, nil
|
||
|
}
|