chore: import upstream snapshot with attribution
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
// 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, "'", "'\\''") + "'"
|
||||
}
|
||||
Reference in New Issue
Block a user