Files
wehub-resource-sync bf9395e022
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
chore: import upstream snapshot with attribution
2026-07-13 12:22:54 +08:00

201 lines
4.8 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package gitcred
import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"path/filepath"
"github.com/larksuite/cli/errs"
"github.com/larksuite/cli/internal/core"
"github.com/larksuite/cli/internal/validate"
"github.com/larksuite/cli/internal/vfs" //nolint:depguard // git credential metadata is CLI config-dir state, not user file I/O.
)
type AppStorage interface {
Read(appID, key string) ([]byte, error)
Write(appID, key string, data []byte) error
Delete(appID, key string) error
}
type Store struct {
path string
appID string
storage AppStorage
}
func NewStore() *Store {
return &Store{path: filepath.Join(core.GetConfigDir(), MetadataFilename)}
}
func NewAppStore(appID string, storage AppStorage) *Store {
return &Store{appID: appID, storage: storage}
}
func NewStoreAt(path string) *Store {
return &Store{path: path}
}
func (s *Store) Path() string {
if s.storage != nil {
return fmt.Sprintf("apps:%s/%s", s.appID, MetadataFilename)
}
return s.path
}
func (s *Store) Load() (*CredentialFile, error) {
data, err := s.read()
if errors.Is(err, fs.ErrNotExist) {
return &CredentialFile{Version: CurrentCredentialVersion}, nil
}
if err != nil {
return nil, err
}
if len(data) == 0 {
return &CredentialFile{Version: CurrentCredentialVersion}, nil
}
var file CredentialFile
if err := json.Unmarshal(data, &file); err != nil {
return nil, errs.NewConfigError(errs.SubtypeInvalidConfig, "invalid %s: %s", MetadataFilename, err).
WithHint("the local Git credential metadata is damaged; rerun `lark-cli apps +git-credential-init --app-id <app_id>` after backing up or removing the damaged app metadata").
WithCause(err)
}
if file.Version == 0 {
file.Version = CurrentCredentialVersion
}
if file.Version > CurrentCredentialVersion {
return nil, &errs.ConfigError{Problem: errs.Problem{
Category: errs.CategoryConfig,
Subtype: errs.SubtypeInvalidConfig,
Message: fmt.Sprintf("%s version %d is newer than supported version %d", MetadataFilename, file.Version, CurrentCredentialVersion),
Hint: "upgrade lark-cli and retry",
}}
}
return &file, nil
}
func (s *Store) Save(file *CredentialFile) error {
if file == nil {
file = &CredentialFile{}
}
file.Version = CurrentCredentialVersion
data, _ := json.MarshalIndent(file, "", " ")
return s.write(append(data, '\n'))
}
func (s *Store) Upsert(record CredentialRecord) error {
file, err := s.Load()
if err != nil {
return err
}
file.CredentialRecord = record
return s.Save(file)
}
func (s *Store) Current() (*CredentialRecord, error) {
file, err := s.Load()
if err != nil {
return nil, err
}
if file.GitHTTPURL == "" {
return nil, nil
}
return &file.CredentialRecord, nil
}
func (s *Store) DeleteByURL(gitHTTPURL string) (*CredentialRecord, error) {
file, err := s.Load()
if err != nil {
return nil, err
}
if file.GitHTTPURL != gitHTTPURL || file.GitHTTPURL == "" {
return nil, nil
}
record := file.CredentialRecord
if err := s.delete(); err != nil {
return nil, err
}
return &record, nil
}
func (s *Store) FindByURL(gitHTTPURL string) (*CredentialRecord, error) {
file, err := s.Load()
if err != nil {
return nil, err
}
if file.GitHTTPURL != gitHTTPURL || file.GitHTTPURL == "" {
return nil, nil
}
return &file.CredentialRecord, nil
}
func (s *Store) Records() ([]CredentialRecord, error) {
file, err := s.Load()
if err != nil {
return nil, err
}
if file.GitHTTPURL == "" {
return []CredentialRecord{}, nil
}
return []CredentialRecord{file.CredentialRecord}, nil
}
func (s *Store) FindByAppID(appID string, profile ProfileContext) ([]CredentialRecord, error) {
records, err := s.Records()
if err != nil {
return nil, err
}
out := make([]CredentialRecord, 0)
for _, record := range records {
if record.AppID != appID {
continue
}
if profile.Profile != "" && record.Profile != profile.Profile {
continue
}
if profile.ProfileAppID != "" && record.ProfileAppID != profile.ProfileAppID {
continue
}
if profile.UserOpenID != "" && record.UserOpenID != profile.UserOpenID {
continue
}
out = append(out, record)
}
return out, nil
}
func (s *Store) read() ([]byte, error) {
if s.storage != nil {
data, err := s.storage.Read(s.appID, MetadataFilename)
if data == nil && err == nil {
return nil, fs.ErrNotExist
}
return data, err
}
return vfs.ReadFile(s.path)
}
func (s *Store) write(data []byte) error {
if s.storage != nil {
return s.storage.Write(s.appID, MetadataFilename, data)
}
if err := vfs.MkdirAll(filepath.Dir(s.path), 0700); err != nil {
return err
}
return validate.AtomicWrite(s.path, data, 0600)
}
func (s *Store) delete() error {
if s.storage != nil {
return s.storage.Delete(s.appID, MetadataFilename)
}
if err := vfs.Remove(s.path); err != nil && !errors.Is(err, fs.ErrNotExist) {
return err
}
return nil
}