178 lines
4.1 KiB
Go
178 lines
4.1 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", &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")
|
|
}
|
|
}
|