Files
larksuite-cli/shortcuts/apps/gitcred/gitconfig.go

139 lines
4.0 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package gitcred
import (
"context"
"os/exec"
"strings"
"github.com/larksuite/cli/errs"
"github.com/larksuite/cli/internal/validate"
)
type GitConfig interface {
SetHelper(ctx context.Context, gitHTTPURL, appID string) error
UnsetHelper(ctx context.Context, gitHTTPURL string) error
}
type GlobalGitConfig struct {
HelperCommand string
}
func (g GlobalGitConfig) SetHelper(ctx context.Context, gitHTTPURL, appID string) error {
normalizedURL, err := NormalizeGitHTTPURL(gitHTTPURL)
if err != nil {
return err
}
appID = strings.TrimSpace(appID)
if err := validate.ResourceName(appID, "appID"); err != nil {
return err
}
helper := g.helperCommand(appID)
helperKey := gitCredentialKey(normalizedURL, "helper")
useHTTPPathKey := gitCredentialKey(normalizedURL, "useHttpPath")
previousHelper, hadHelper, err := gitConfigGet(ctx, helperKey)
if err != nil {
return err
}
if hadHelper && previousHelper != helper && !g.isManagedHelper(previousHelper) {
return errs.NewValidationError(errs.SubtypeFailedPrecondition, "git credential helper already configured for %s; refusing to overwrite non-lark helper", normalizedURL)
}
if err := exec.CommandContext(ctx, "git", "config", "--global", helperKey, helper).Run(); err != nil {
return err
}
if err := exec.CommandContext(ctx, "git", "config", "--global", useHTTPPathKey, "true").Run(); err != nil {
if !hadHelper {
_ = exec.CommandContext(ctx, "git", "config", "--global", "--unset", helperKey).Run()
} else if previousHelper != helper {
_ = exec.CommandContext(ctx, "git", "config", "--global", helperKey, previousHelper).Run()
}
return err
}
return nil
}
func (g GlobalGitConfig) UnsetHelper(ctx context.Context, gitHTTPURL string) error {
normalizedURL, err := NormalizeGitHTTPURL(gitHTTPURL)
if err != nil {
return err
}
helperKey := gitCredentialKey(normalizedURL, "helper")
useHTTPPathKey := gitCredentialKey(normalizedURL, "useHttpPath")
helper, found, err := gitConfigGet(ctx, helperKey)
if err != nil {
return err
}
if found {
if !g.isManagedHelper(helper) {
return nil
}
}
if err := gitConfigUnset(ctx, helperKey); err != nil {
return err
}
if err := gitConfigUnset(ctx, useHTTPPathKey); err != nil {
return err
}
return nil
}
func (g GlobalGitConfig) helperCommand(appID string) string {
if g.HelperCommand != "" {
return g.HelperCommand
}
return "!lark-cli apps git-credential-helper --app-id " + shellQuoteArg(appID)
}
func (g GlobalGitConfig) isManagedHelper(helper string) bool {
helper = strings.TrimSpace(helper)
if g.HelperCommand != "" {
return helper == g.HelperCommand
}
return strings.HasPrefix(helper, "!lark-cli apps git-credential-helper ")
}
func gitCredentialKey(gitHTTPURL, name string) string {
return "credential." + gitHTTPURL + "." + name
}
func gitConfigGet(ctx context.Context, key string) (string, bool, error) {
out, err := exec.CommandContext(ctx, "git", "config", "--global", "--get", key).Output()
if err == nil {
return strings.TrimSpace(string(out)), true, nil
}
if isGitConfigGetMissing(err) {
return "", false, nil
}
return "", false, errs.NewInternalError(errs.SubtypeExternalTool, "git config get %s failed: %v", key, err).WithCause(err)
}
func gitConfigUnset(ctx context.Context, key string) error {
if err := exec.CommandContext(ctx, "git", "config", "--global", "--unset", key).Run(); err != nil {
if isGitConfigUnsetMissing(err) {
return nil
}
return errs.NewInternalError(errs.SubtypeExternalTool, "git config unset %s failed: %v", key, err).WithCause(err)
}
return nil
}
func isGitConfigGetMissing(err error) bool {
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 {
return true
}
return false
}
func isGitConfigUnsetMissing(err error) bool {
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 5 {
return true
}
return false
}
func shellQuoteArg(s string) string {
return "'" + strings.ReplaceAll(s, "'", "'\\''") + "'"
}