From 3fc7b225f50dd3418363a7d72fff8db93e11d178 Mon Sep 17 00:00:00 2001 From: Fraser Davidson Date: Mon, 6 Nov 2023 11:49:50 +0000 Subject: [PATCH] WIP --- azuredevops/artifacts_test.go | 1 + azuredevops/azuredevops.go | 17 +++++ azuredevops/{project.go => core.go} | 2 +- azuredevops/core_test.go | 1 + azuredevops/git_test.go | 1 + azuredevops/{identity.go => location.go} | 0 azuredevops/location_test.go | 1 + azuredevops/repository.go | 86 +++++++++++++----------- azuredevops/wiki.go | 6 +- git/external_git/config.go | 15 +++++ git/git.go | 3 + 11 files changed, 91 insertions(+), 42 deletions(-) create mode 100644 azuredevops/artifacts_test.go rename azuredevops/{project.go => core.go} (93%) create mode 100644 azuredevops/core_test.go create mode 100644 azuredevops/git_test.go rename azuredevops/{identity.go => location.go} (100%) create mode 100644 azuredevops/location_test.go diff --git a/azuredevops/artifacts_test.go b/azuredevops/artifacts_test.go new file mode 100644 index 0000000..5338789 --- /dev/null +++ b/azuredevops/artifacts_test.go @@ -0,0 +1 @@ +package azuredevops diff --git a/azuredevops/azuredevops.go b/azuredevops/azuredevops.go index 8f0d699..f2a6401 100644 --- a/azuredevops/azuredevops.go +++ b/azuredevops/azuredevops.go @@ -2,7 +2,9 @@ package azuredevops import ( "context" + "encoding/base64" "fmt" + "strings" "github.com/microsoft/azure-devops-go-api/azuredevops" ) @@ -22,3 +24,18 @@ func NewAzureDevOps(organisationName string, personalAccessToken string) *AzureD ctx: ctx, } } + +func (a *AzureDevOps) GetPAT() (*string, error) { + authString := a.connection.AuthorizationString + if !strings.Contains(authString, "Basic") { + return nil, fmt.Errorf("authorization string does not contain basic auth") + } + bytes, err := base64.StdEncoding.DecodeString(strings.TrimLeft(authString, "Basic ")) + if err != nil { + return nil, err + } + usernamePassword := string(bytes) + password := strings.Split(usernamePassword, ":") + + return &password[1], nil +} diff --git a/azuredevops/project.go b/azuredevops/core.go similarity index 93% rename from azuredevops/project.go rename to azuredevops/core.go index 0bdbfd8..2d81582 100644 --- a/azuredevops/project.go +++ b/azuredevops/core.go @@ -20,7 +20,7 @@ func (a *AzureDevOps) getProjectUUID(projectName string) (*uuid.UUID, error) { return nil, err } - for _, project := range *&projects.Value { + for _, project := range projects.Value { if *project.Name == projectName { return project.Id, nil } diff --git a/azuredevops/core_test.go b/azuredevops/core_test.go new file mode 100644 index 0000000..5338789 --- /dev/null +++ b/azuredevops/core_test.go @@ -0,0 +1 @@ +package azuredevops diff --git a/azuredevops/git_test.go b/azuredevops/git_test.go new file mode 100644 index 0000000..5338789 --- /dev/null +++ b/azuredevops/git_test.go @@ -0,0 +1 @@ +package azuredevops diff --git a/azuredevops/identity.go b/azuredevops/location.go similarity index 100% rename from azuredevops/identity.go rename to azuredevops/location.go diff --git a/azuredevops/location_test.go b/azuredevops/location_test.go new file mode 100644 index 0000000..5338789 --- /dev/null +++ b/azuredevops/location_test.go @@ -0,0 +1 @@ +package azuredevops diff --git a/azuredevops/repository.go b/azuredevops/repository.go index d6d6c28..f9fb049 100644 --- a/azuredevops/repository.go +++ b/azuredevops/repository.go @@ -3,10 +3,8 @@ package azuredevops import ( "fmt" "os" - "path/filepath" egit "github.com/frontierdigital/utils/git/external_git" - "github.com/frontierdigital/utils/output" "github.com/microsoft/azure-devops-go-api/azuredevops/git" "golang.org/x/exp/slices" ) @@ -41,21 +39,8 @@ func (a *AzureDevOps) GetRepository(projectName string, name string) (*git.GitRe return &(*repositories)[repositoryIdx], nil } -// configureRepository configures a repository -func configureRepository(repo *egit.ExternalGit, gitEmail string, gitUsername string) error { - err := repo.SetConfig("user.email", gitEmail) - if err != nil { - return err - } - err = repo.SetConfig("user.name", gitUsername) - if err != nil { - return err - } - return nil -} - // createRepositoryIfNotExists creates a repository if it does not exist -func createRepositoryIfNotExists(a *AzureDevOps, projectName string, repoName string, gitEmail string, gitUsername string, adoPat string) (*git.GitRepository, *string, error) { +func (a *AzureDevOps) createRepositoryIfNotExists(projectName string, repoName string, gitEmail string, gitUsername string) (*git.GitRepository, *string, error) { client, err := git.NewClient(a.ctx, a.connection) if err != nil { return nil, nil, err @@ -69,22 +54,16 @@ func createRepositoryIfNotExists(a *AzureDevOps, projectName string, repoName st r, err := client.GetRepository(a.ctx, getRepositoryArgs) if err == nil { - repoPath, err := os.MkdirTemp("", "") - if err != nil { - return nil, nil, err - } - repo := egit.NewGit(repoPath) - err = repo.CloneOverHttp(*r.RemoteUrl, adoPat, "x-oauth-basic") - if err != nil { - return nil, nil, err - } - err = configureRepository(repo, gitEmail, gitUsername) + repoPath, err := a.checkoutAndConfigure(*r.RemoteUrl, gitEmail, gitUsername) if err != nil { return nil, nil, err } - return r, &repoPath, nil + + return r, repoPath, nil } + // TODO: Check that err is a GitRepositoryNotFound error + createRepositoryArgs := git.CreateRepositoryArgs{ GitRepositoryToCreate: &git.GitRepositoryCreateOptions{ Name: &repoName, @@ -93,39 +72,70 @@ func createRepositoryIfNotExists(a *AzureDevOps, projectName string, repoName st } r, err = client.CreateRepository(a.ctx, createRepositoryArgs) + if err != nil { + return nil, nil, err + } + repoPath, err := a.initRepository(*r.RemoteUrl, gitEmail, gitUsername) if err != nil { return nil, nil, err } - localPath, err := initRepository("frontierdigital", projectName, repoName, gitEmail, gitUsername, adoPat) + return r, repoPath, nil +} +func (a *AzureDevOps) checkoutAndConfigure(remoteUrl string, gitEmail string, gitUsername string) (*string, error) { + repoPath, err := os.MkdirTemp("", "") if err != nil { - return nil, nil, err + return nil, err } - return r, localPath, nil + repo := egit.NewGit(repoPath) + pat, err := a.GetPAT() + if err != nil { + return nil, err + } + + err = repo.CloneOverHttp(remoteUrl, *pat, "x-oauth-basic") + if err != nil { + return nil, err + } + + err = repo.Configure(gitEmail, gitUsername) + if err != nil { + return nil, err + } + + repoPath = repo.GetRepositoryPath() + + return &repoPath, nil } // initRepository creates a main branch -func initRepository(organisationName string, projectName string, repoName string, gitEmail string, gitUsername string, adoPat string) (*string, error) { - repoUrl := fmt.Sprintf("https://dev.azure.com/%s/%s/_git/%s", organisationName, projectName, repoName) - +func (a *AzureDevOps) initRepository(remoteUrl string, gitEmail string, gitUsername string) (*string, error) { repoPath, err := os.MkdirTemp("", "") if err != nil { return nil, err } + repo := egit.NewGit(repoPath) - err = repo.CloneOverHttp(repoUrl, adoPat, "x-oauth-basic") + + pat, err := a.GetPAT() if err != nil { return nil, err } - err = configureRepository(repo, gitEmail, gitUsername) + + err = repo.CloneOverHttp(remoteUrl, *pat, "x-oauth-basic") if err != nil { return nil, err } - file, err := os.Create(filepath.Join(repoPath, "README.md")) + err = repo.Configure(gitEmail, gitUsername) + if err != nil { + return nil, err + } + + file, err := os.Create(repo.GetFilePath("README.md")) if err != nil { return nil, err } @@ -136,7 +146,7 @@ func initRepository(organisationName string, projectName string, repoName string return nil, err } - commitMessage := "Initial Commit" + commitMessage := "Initial commit." _, err = repo.Commit(commitMessage) if err != nil { return nil, err @@ -147,7 +157,5 @@ func initRepository(organisationName string, projectName string, repoName string return nil, err } - output.PrintlnfInfo("Pushed.") - return &repoPath, nil } diff --git a/azuredevops/wiki.go b/azuredevops/wiki.go index 46abb4f..8f02029 100644 --- a/azuredevops/wiki.go +++ b/azuredevops/wiki.go @@ -6,7 +6,7 @@ import ( ) // CreateWikiIfNotExists creates a code wiki if it does not exist. -func (a *AzureDevOps) CreateWikiIfNotExists(projectName string, wikiName string, gitEmail string, gitUsername string, adoPat string) (*string, error) { +func (a *AzureDevOps) CreateWikiIfNotExists(projectName string, wikiName string, gitEmail string, gitUsername string) (*string, error) { client, err := wiki.NewClient(a.ctx, a.connection) if err != nil { return nil, err @@ -17,7 +17,7 @@ func (a *AzureDevOps) CreateWikiIfNotExists(projectName string, wikiName string, WikiIdentifier: &wikiName, } - r, localPath, err := createRepositoryIfNotExists(a, projectName, wikiName, gitEmail, gitUsername, adoPat) + r, localPath, err := a.createRepositoryIfNotExists(projectName, wikiName, gitEmail, gitUsername) if err != nil { return nil, err } @@ -28,6 +28,8 @@ func (a *AzureDevOps) CreateWikiIfNotExists(projectName string, wikiName string, return localPath, nil } + // TODO: Check that this is a WikiNotFoundError + projectId, err := a.getProjectUUID(projectName) if err != nil { return nil, err diff --git a/git/external_git/config.go b/git/external_git/config.go index e02a949..c0968ce 100644 --- a/git/external_git/config.go +++ b/git/external_git/config.go @@ -1,5 +1,20 @@ package external_git +// Configure configures the repository +func (g *ExternalGit) Configure(gitEmail string, gitUsername string) error { + err := g.SetConfig("user.email", gitEmail) + if err != nil { + return err + } + + err = g.SetConfig("user.name", gitUsername) + if err != nil { + return err + } + + return nil +} + // SetConfig sets the value for the given key func (g *ExternalGit) SetConfig(key string, value string) error { _, err := g.Exec("config", "--local", key, value) diff --git a/git/git.go b/git/git.go index fa54dac..a199038 100644 --- a/git/git.go +++ b/git/git.go @@ -2,10 +2,13 @@ package git type Git interface { Add(path string) error + AddAll() error Checkout(branchName string, create bool) error CloneOverHttp(url string, username string, password string) error Commit(message string) (string, error) + Configure(gitEmail string, gitUsername string) error GetFilePath(filePath string) string GetRepositoryPath() string Push(force bool) error + SetConfig(key string, value string) error }