Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Fraser Davidson committed Nov 13, 2023
1 parent 2cf266d commit cd71fa6
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 20 deletions.
5 changes: 0 additions & 5 deletions azuredevops/azuredevops.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ import (
"github.com/microsoft/azure-devops-go-api/azuredevops"
)

type AzureDevOps struct {
connection *azuredevops.Connection
ctx context.Context
}

// NewAzureDevOps creates a new AzureDevOps
func NewAzureDevOps(organisationName string, personalAccessToken string) *AzureDevOps {
ctx := context.Background()
Expand Down
80 changes: 67 additions & 13 deletions azuredevops/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,67 @@ import (
"github.com/microsoft/azure-devops-go-api/azuredevops/build"
)

// CreateBuildDefinition creates a new BuildDefinition
func (a *AzureDevOps) CreateBuildDefinition(projectName string, repositoryId string, folderPath string, definitionName string, yamlFilename string) (*build.BuildDefinition, error) {
client, err := build.NewClient(a.ctx, a.connection)
if err != nil {
return nil, err
}

agentPoolQueueName := "Azure Pipelines"
yamlProcessType := 2
buildRepositoryDefaultBranch := "refs/heads/main"
buildRepositoryType := "tfsgit"
triggerBatchChanges := false
triggerBranchFilters := []string{}
triggerMaxConcurrentBuildsPerBranch := 1
triggerPathFilters := []string{}
triggerSettingsSourceType := 2
var triggers []interface{} // []build.ContinuousIntegrationTrigger # TODO: Why doesn't this work?
triggers = append(triggers, build.ContinuousIntegrationTrigger{
BatchChanges: &triggerBatchChanges,
BranchFilters: &triggerBranchFilters,
MaxConcurrentBuildsPerBranch: &triggerMaxConcurrentBuildsPerBranch,
PathFilters: &triggerPathFilters,
SettingsSourceType: &triggerSettingsSourceType,
TriggerType: &build.DefinitionTriggerTypeValues.ContinuousIntegration,
})
createDefinitionArgs := build.CreateDefinitionArgs{
Definition: &build.BuildDefinition{
Name: &definitionName,
Path: &folderPath,
Process: &build.YamlProcess{
Type: &yamlProcessType,
YamlFilename: &yamlFilename,
},
Queue: &build.AgentPoolQueue{
Name: &agentPoolQueueName,
},
QueueStatus: &build.DefinitionQueueStatusValues.Enabled,
Repository: &build.BuildRepository{
Id: &repositoryId,
DefaultBranch: &buildRepositoryDefaultBranch,
Properties: &map[string]string{
"reportBuildStatus": "true",
},
Type: &buildRepositoryType,
},
Triggers: &triggers,
},
Project: &projectName,
}
return client.CreateDefinition(a.ctx, createDefinitionArgs)
}

// GetBuild gets a Build
func (a *AzureDevOps) GetBuild(projectName string, buildId *int) (*build.Build, error) {
func (a *AzureDevOps) GetBuild(projectName string, buildId int) (*build.Build, error) {
client, err := build.NewClient(a.ctx, a.connection)
if err != nil {
return nil, err
}

getBuildArgs := build.GetBuildArgs{
BuildId: buildId,
BuildId: &buildId,
Project: &projectName,
}
return client.GetBuild(a.ctx, getBuildArgs)
Expand All @@ -46,7 +98,11 @@ func (a *AzureDevOps) GetBuildDefinitionByName(projectName string, name string)
}

if len(definitions.Value) == 0 {
return nil, fmt.Errorf("build definition with name '%s' not found in project '%s'", name, projectName)
// return nil, fmt.Errorf("build definition with name '%s' not found in project '%s'", name, projectName)
return nil, &BuildNotFoundError{
name: name,
projectName: projectName,
}
}
if len(definitions.Value) > 1 {
return nil, fmt.Errorf("multiple build definitions with name '%s' found in project '%s'", name, projectName)
Expand All @@ -66,7 +122,7 @@ type CustomDefinition struct {
}

// QueueBuild queues and returns a new Build
func (a *AzureDevOps) QueueBuild(projectName string, definitionId *int, sourceBranch string, templateParameters map[string]string, tags []string) (*build.Build, error) {
func (a *AzureDevOps) QueueBuild(projectName string, definitionId int, sourceBranch string, templateParameters map[string]string, tags []string) (*build.Build, error) {
buildClient, err := build.NewClient(a.ctx, a.connection)
if err != nil {
return nil, err
Expand All @@ -92,7 +148,7 @@ func (a *AzureDevOps) QueueBuild(projectName string, definitionId *int, sourceBr

queueBuildArgs := &CustomQueueBuildArgs{
Definition: CustomDefinition{
ID: definitionId,
ID: &definitionId,
},
SourceBranch: sourceBranch,
TemplateParameters: templateParameters,
Expand All @@ -113,10 +169,9 @@ func (a *AzureDevOps) QueueBuild(projectName string, definitionId *int, sourceBr

response, err := client.Send(a.ctx, http.MethodPost, locationId, "5.1", routeValues, queryParams, bytes.NewReader(body), "application/json", "application/json", nil)
if err != nil {
if _, ok := err.(azuredevops.WrappedError); ok {
wrappedError := err.(azuredevops.WrappedError)
if *wrappedError.TypeKey == "BuildRequestValidationFailedException" {
validationResults := (*wrappedError.CustomProperties)["ValidationResults"]
if e, ok := err.(azuredevops.WrappedError); ok {
if *e.TypeKey == "BuildRequestValidationFailedException" {
validationResults := (*e.CustomProperties)["ValidationResults"]
builder := &strings.Builder{}
for i, v := range validationResults.([]interface{}) {
message := v.(map[string]interface{})["message"]
Expand Down Expand Up @@ -149,15 +204,14 @@ func (a *AzureDevOps) QueueBuild(projectName string, definitionId *int, sourceBr
}

// WaitForBuild waits for a Build to complete
func (a *AzureDevOps) WaitForBuild(projectName string, buildId *int, attempts uint, interval int) error {
func (a *AzureDevOps) WaitForBuild(projectName string, buildId int, attempts uint, interval int) error {
err := retry.Do(
func() error {
var err error
build, err := a.GetBuild(projectName, buildId)
if err != nil {
if _, ok := err.(azuredevops.WrappedError); ok {
wrappedError := err.(azuredevops.WrappedError)
if *wrappedError.TypeKey == "BuildNotFoundException" {
if e, ok := err.(azuredevops.WrappedError); ok {
if *e.TypeKey == "BuildNotFoundException" {
return retry.Unrecoverable(err)
}
}
Expand Down
22 changes: 22 additions & 0 deletions azuredevops/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package azuredevops

import (
"context"
"fmt"

"github.com/microsoft/azure-devops-go-api/azuredevops"
)

type AzureDevOps struct {
connection *azuredevops.Connection
ctx context.Context
}

type BuildNotFoundError struct {
name string
projectName string
}

func (b *BuildNotFoundError) Error() string {
return fmt.Sprintf("build definition with name '%s' not found in project '%s'", b.name, b.projectName)
}
1 change: 1 addition & 0 deletions azuredevops/wiki_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package azuredevops
4 changes: 2 additions & 2 deletions exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ func RunCommand(name string, cwd string, args ...string) (stdout string, stderr

if err != nil {
// try to get the exit code
if exitError, ok := err.(*exec.ExitError); ok {
ws := exitError.Sys().(syscall.WaitStatus)
if e, ok := err.(*exec.ExitError); ok {
ws := e.Sys().(syscall.WaitStatus)
exitCode = ws.ExitStatus()
} else {
// This will happen (in OSX) if `name` is not available in $PATH,
Expand Down

0 comments on commit cd71fa6

Please sign in to comment.