mukan-ignite/ignite/pkg/xgit/xgit.go
Mukan Erkin Törük c32551b6f7
Some checks failed
Docs Deploy / build_and_deploy (push) Has been cancelled
Generate Docs / cli (push) Has been cancelled
Generate Config Doc / cli (push) Has been cancelled
Go formatting / go-formatting (push) Has been cancelled
Check links / markdown-link-check (push) Has been cancelled
Integration / pre-test (push) Has been cancelled
Integration / test on (push) Has been cancelled
Integration / status (push) Has been cancelled
Lint / Lint Go code (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:24 +03:00

181 lines
4.5 KiB
Go

package xgit
import (
"context"
"os"
"path/filepath"
"strings"
"time"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/errors"
)
var (
commitMsg = "Initialized with Ignite CLI"
defaultOpenOpts = git.PlainOpenOptions{DetectDotGit: true}
devXAuthor = &object.Signature{
Name: "Developer Experience team at Ignite",
Email: "hello@ignite.com",
When: time.Now(),
}
)
// InitAndCommit creates a git repo in path if path isn't already inside a git
// repository, then commits path content.
func InitAndCommit(path string) error {
repo, err := git.PlainOpenWithOptions(path, &defaultOpenOpts)
if err != nil {
if !errors.Is(err, git.ErrRepositoryNotExists) {
return errors.Errorf("open git repo %s: %w", path, err)
}
// not a git repo, creates a new one
repo, err = git.PlainInitWithOptions(path, &git.PlainInitOptions{
InitOptions: git.InitOptions{
DefaultBranch: plumbing.Main,
},
Bare: false,
})
if err != nil {
return errors.Errorf("init git repo %s: %w", path, err)
}
}
wt, err := repo.Worktree()
if err != nil {
return errors.Errorf("worktree %s: %w", path, err)
}
// wt.Add(path) takes only relative path, we need to turn path relative to
// repo path.
repoPath := wt.Filesystem.Root()
path, err = filepath.Rel(repoPath, path)
if err != nil {
return errors.Errorf("find relative path %s %s: %w", repoPath, path, err)
}
if _, err := wt.Add(path); err != nil {
return errors.Errorf("git add %s: %w", path, err)
}
_, err = wt.Commit(commitMsg, &git.CommitOptions{
All: true,
Author: devXAuthor,
})
if err != nil {
return errors.Errorf("git commit %s: %w", path, err)
}
return nil
}
// AreChangesCommitted returns true if dir is a clean git repository with no
// pending changes. It returns also true if dir is NOT a git repository.
func AreChangesCommitted(dir string) (bool, error) {
dir, err := filepath.Abs(dir)
if err != nil {
return false, err
}
repository, err := git.PlainOpen(dir)
if err != nil {
if errors.Is(err, git.ErrRepositoryNotExists) {
return true, nil
}
return false, err
}
w, err := repository.Worktree()
if err != nil {
return false, err
}
ws, err := w.Status()
if err != nil {
return false, err
}
return ws.IsClean(), nil
}
// Clone clones a git repository represented by urlRef, into dir.
// urlRef is the URL of the repository, with an optional ref, suffixed to the
// URL with a `@`. Ref can be a tag, a branch or a hash.
// Valid examples of urlRef: github.com/org/repo, github.com/org/repo@v1,
// github.com/org/repo@develop, github.com/org/repo@ab88cdf.
func Clone(ctx context.Context, urlRef, dir string) error {
// Ensure dir is empty if it exists (if it doesn't exist, the call to
// git.PlainCloneContext below will create it).
files, _ := os.ReadDir(dir)
if len(files) > 0 {
return errors.Errorf("clone: target directory %q is not empty", dir)
}
// Split urlRef
var (
parts = strings.Split(urlRef, "@")
url = parts[0]
ref string
)
if len(parts) > 1 {
ref = parts[1]
}
// First clone the repo
repo, err := git.PlainCloneContext(ctx, dir, false, &git.CloneOptions{
URL: url,
})
if err != nil {
return err
}
if ref == "" {
// if ref is not provided, job is done
return nil
}
// Reference provided, try to resolve
wt, err := repo.Worktree()
if err != nil {
return err
}
var h *plumbing.Hash
for _, ref := range []string{ref, "origin/" + ref} {
h, err = repo.ResolveRevision(plumbing.Revision(ref))
if err == nil {
break
}
}
if err != nil {
// Ref not found, clean up dir and return error
os.RemoveAll(dir)
return err
}
return wt.Checkout(&git.CheckoutOptions{
Hash: *h,
})
}
// IsRepository checks if a path contains a Git repository.
func IsRepository(path string) (bool, error) {
if _, err := git.PlainOpenWithOptions(path, &defaultOpenOpts); err != nil {
if errors.Is(err, git.ErrRepositoryNotExists) {
return false, nil
}
return false, err
}
return true, nil
}
// RepositoryURL returns the URL of the origin remote of a Git repository.
func RepositoryURL(path string) (string, error) {
repo, err := git.PlainOpenWithOptions(path, &defaultOpenOpts)
if err != nil {
return "", err
}
cfg, err := repo.Config()
if err != nil {
return "", err
}
origin, ok := cfg.Remotes["origin"]
if !ok {
return "", errors.Errorf("no origin remote found in %s", path)
}
return origin.URLs[0], nil
}