freeleaps-ops/operators/freeleaps-gitops-initializer/internal/repo/plugins/azure/azure.go
zhenyus f34f9838a5 chore(git): code staging
Signed-off-by: zhenyus <zhenyus@mathmast.com>
2025-02-17 18:14:40 +08:00

180 lines
4.2 KiB
Go

package azure
import (
"context"
"encoding/base64"
"errors"
"fmt"
"freeleaps.com/gitops/initializer/internal/repo"
"github.com/microsoft/azure-devops-go-api/azuredevops"
"github.com/microsoft/azure-devops-go-api/azuredevops/git"
)
type AzureDevOpsRepoManager struct {
options *repo.Options
client git.Client
}
var _ repo.RepoManager = &AzureDevOpsRepoManager{}
func init() {
repo.RegisterPlugin("azure-devops", func() repo.RepoManager {
return &AzureDevOpsRepoManager{}
})
}
// Initialize implements repo.RepoManager.
func (a *AzureDevOpsRepoManager) Initialize(context context.Context, opts ...repo.Option) error {
options := &repo.Options{}
for _, opt := range opts {
opt(options)
}
if options.Project == "" {
return errors.New("missing project name")
}
if options.Name == "" {
return errors.New("missing repository name")
}
if options.Branch == "" {
options.Branch = "master"
}
if options.AuthorName == "" {
options.AuthorName = repo.DefaultAuthorName
}
if options.AuthorEmail == "" {
options.AuthorEmail = repo.DefaultAuthorEmail
}
if options.PluginConfig == nil {
return errors.New("missing plugin config")
}
config := options.PluginConfig
pat, err := config.Get("pat")
if err != nil {
return errors.New("missing PAT")
}
organization, err := config.Get("organizationUrl")
if err != nil {
return errors.New("missing organization URL")
}
conn := azuredevops.NewPatConnection(organization.(string), pat.(string))
a.client, err = git.NewClient(context, conn)
if err != nil {
return fmt.Errorf("failed to create git client: %w", err)
}
a.options = options
return nil
}
func (a *AzureDevOpsRepoManager) getBranchHeadCommit() (string, error) {
filter := "heads/"
filterContains := a.options.Branch
refs, err := a.client.GetRefs(context.Background(), git.GetRefsArgs{
RepositoryId: &a.options.Name,
Project: &a.options.Project,
Filter: &filter,
FilterContains: &filterContains,
})
if err != nil {
return "", fmt.Errorf("failed to get branch ref: %v", err)
}
if len(refs.Value) == 0 || (refs.Value)[0].ObjectId == nil {
return "", fmt.Errorf("branch '%s' not found", a.options.Branch)
}
return *refs.Value[0].ObjectId, nil
}
// ApplyChanges implements repo.RepoManager.
func (a *AzureDevOpsRepoManager) ApplyChanges(context context.Context, changes []repo.ChangeRequest, message string) error {
targetRepo, err := a.client.GetRepository(context, git.GetRepositoryArgs{
RepositoryId: &a.options.Name,
Project: &a.options.Project,
})
if err != nil {
return fmt.Errorf("failed to get repository: %w", err)
}
// convert changes to git changes
gitChanges := make([]interface{}, 0, len(changes))
for _, change := range changes {
gitChange := git.GitChange{
ChangeType: toAzureGitChangeType(change.Operation),
Item: &git.GitItem{
Path: &change.Path,
},
}
if change.Operation != repo.OpDelete {
contentBase64 := base64.StdEncoding.EncodeToString(change.Content)
gitChange.NewContent = &git.ItemContent{
Content: &contentBase64,
ContentType: &git.ItemContentTypeValues.Base64Encoded,
}
}
gitChanges = append(gitChanges, gitChange)
}
// get head commit id
baseCommitId, err := a.getBranchHeadCommit()
if err != nil {
return err
}
// generate ref name
refName := "refs/heads/" + a.options.Branch
// create push
_, err = a.client.CreatePush(context, git.CreatePushArgs{
RepositoryId: &a.options.Name,
Project: targetRepo.Project.Name,
Push: &git.GitPush{
RefUpdates: &[]git.GitRefUpdate{
{
Name: &refName,
OldObjectId: &baseCommitId,
},
},
Commits: &[]git.GitCommitRef{
{
Comment: &message,
Changes: &gitChanges,
Author: &git.GitUserDate{
Name: &a.options.AuthorName,
Email: &a.options.AuthorEmail,
},
},
},
},
})
return err
}
func toAzureGitChangeType(op repo.FileOperation) *git.VersionControlChangeType {
switch op {
case repo.OpCreate:
return &git.VersionControlChangeTypeValues.Add
case repo.OpDelete:
return &git.VersionControlChangeTypeValues.Delete
case repo.OpUpdate:
return &git.VersionControlChangeTypeValues.Edit
default:
panic("unreachable")
}
}