mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
139 lines
4.0 KiB
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, "'", "'\\''") + "'"
|
|
}
|