149 lines
3.7 KiB
Go
149 lines
3.7 KiB
Go
// Package gitclient provides a shared adapter from the cli/cli git.Client
|
|
// (via cmdutil.Factory) to the narrow interfaces used by skills commands.
|
|
package gitclient
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/cli/cli/v2/pkg/cmdutil"
|
|
)
|
|
|
|
// RootResolver can resolve the git repository root directory.
|
|
type RootResolver interface {
|
|
ToplevelDir() (string, error)
|
|
}
|
|
|
|
// RemoteResolver can resolve git remote URLs.
|
|
type RemoteResolver interface {
|
|
RemoteURL(name string) (string, error)
|
|
}
|
|
|
|
// Client is the full git operations interface used by skills commands.
|
|
type Client interface {
|
|
RootResolver
|
|
RemoteResolver
|
|
GitDir(dir string) error
|
|
Remotes() ([]string, error)
|
|
CurrentBranch(dir string) (string, error)
|
|
IsIgnored(dir, path string) bool
|
|
}
|
|
|
|
// FactoryClient adapts the cli/cli git.Client to the Client interface.
|
|
type FactoryClient struct {
|
|
F *cmdutil.Factory
|
|
}
|
|
|
|
// ToplevelDir returns the root directory of the current git repository.
|
|
func (g *FactoryClient) ToplevelDir() (string, error) {
|
|
cmd, err := g.F.GitClient.Command(context.Background(), "rev-parse", "--show-toplevel")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(string(out)), nil
|
|
}
|
|
|
|
// RemoteURL returns the URL configured for the named git remote.
|
|
func (g *FactoryClient) RemoteURL(name string) (string, error) {
|
|
cmd, err := g.F.GitClient.Command(context.Background(), "remote", "get-url", name)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(string(out)), nil
|
|
}
|
|
|
|
// GitDir validates that the given directory is inside a git repository.
|
|
func (g *FactoryClient) GitDir(dir string) error {
|
|
cmd, err := g.F.GitClient.Command(context.Background(), "-C", dir, "rev-parse", "--git-dir")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = cmd.Output()
|
|
return err
|
|
}
|
|
|
|
// Remotes returns the list of configured git remote names.
|
|
func (g *FactoryClient) Remotes() ([]string, error) {
|
|
cmd, err := g.F.GitClient.Command(context.Background(), "remote")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return strings.Fields(string(out)), nil
|
|
}
|
|
|
|
// CurrentBranch returns the current branch name, or "" if HEAD is detached.
|
|
func (g *FactoryClient) CurrentBranch(dir string) (string, error) {
|
|
cmd, err := g.F.GitClient.Command(context.Background(), "-C", dir, "rev-parse", "--abbrev-ref", "HEAD")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
branch := strings.TrimSpace(string(out))
|
|
if branch == "HEAD" {
|
|
return "", nil // detached HEAD
|
|
}
|
|
return branch, nil
|
|
}
|
|
|
|
// IsIgnored reports whether the given path is git-ignored in the given directory.
|
|
func (g *FactoryClient) IsIgnored(dir, path string) bool {
|
|
cmd, err := g.F.GitClient.Command(context.Background(), "-C", dir, "check-ignore", "-q", path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
_, err = cmd.Output()
|
|
return err == nil
|
|
}
|
|
|
|
// ResolveGitRoot returns the git repository root using the provided resolver,
|
|
// falling back to the current working directory on error.
|
|
func ResolveGitRoot(resolver RootResolver) string {
|
|
if resolver == nil {
|
|
if cwd, err := os.Getwd(); err == nil {
|
|
return cwd
|
|
}
|
|
return ""
|
|
}
|
|
root, err := resolver.ToplevelDir()
|
|
if err != nil {
|
|
if cwd, cwdErr := os.Getwd(); cwdErr == nil {
|
|
return cwd
|
|
}
|
|
return ""
|
|
}
|
|
return root
|
|
}
|
|
|
|
// ResolveHomeDir returns the user's home directory, or "" on error.
|
|
func ResolveHomeDir() string {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return home
|
|
}
|
|
|
|
// TruncateSHA returns the first 8 characters of a SHA, or the full string
|
|
// if it is shorter.
|
|
func TruncateSHA(sha string) string {
|
|
if len(sha) > 8 {
|
|
return sha[:8]
|
|
}
|
|
return sha
|
|
}
|