-
Notifications
You must be signed in to change notification settings - Fork 288
Added the resource for git branch lock azuredevops_git_repository_branch_lock #1289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
//go:build (all || core || resource_git_repository_branch_lock) && !exclude_resource_git_repository_branch_lock | ||
// +build all core resource_git_repository_branch_lock | ||
// +build !exclude_resource_git_repository_branch_lock | ||
|
||
package acceptancetests | ||
|
||
// The tests in this file use the mock clients in mock_client.go to mock out | ||
// the Azure DevOps client operations. | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/acceptancetests/testutils" | ||
) | ||
|
||
func TestAccGitRepoBranchLock_CreateAndUpdate(t *testing.T) { | ||
projectName := testutils.GenerateResourceName() | ||
gitRepoName := testutils.GenerateResourceName() | ||
branchName := testutils.GenerateResourceName() | ||
branchNameChanged := testutils.GenerateResourceName() | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testutils.PreCheck(t, nil) }, | ||
Providers: testutils.GetProviders(), | ||
Steps: []resource.TestStep{ | ||
// Test branch lock | ||
{ | ||
Config: hclGitRepoBranchLock(projectName, gitRepoName, branchName), | ||
Check: resource.ComposeTestCheckFunc( | ||
// test-branch | ||
resource.TestCheckResourceAttr("azuredevops_git_repository_branch.from_master", "name", fmt.Sprintf("testbranch-%s", branchName)), | ||
resource.TestCheckResourceAttr("azuredevops_git_repository_branch.from_master", "ref_branch", "master"), | ||
resource.TestCheckResourceAttr("azuredevops_git_repository_branch_lock.master", "is_locked", "true"), | ||
), | ||
}, | ||
// Test branch unlock | ||
{ | ||
Config: hclGitRepoBranchUnlock(projectName, gitRepoName, branchNameChanged), | ||
Check: resource.ComposeTestCheckFunc( | ||
// test-branch | ||
resource.TestCheckResourceAttr("azuredevops_git_repository_branch.from_master", "name", fmt.Sprintf("testbranch-%s", branchNameChanged)), | ||
resource.TestCheckResourceAttr("azuredevops_git_repository_branch.from_master", "ref_branch", "master"), | ||
resource.TestCheckResourceAttr("azuredevops_git_repository_branch_lock.master", "is_locked", "false"), | ||
), | ||
}, | ||
}, | ||
}, | ||
) | ||
} | ||
|
||
func hclGitRepoBranchLock(projectName, gitRepoName, branchName string) string { | ||
return fmt.Sprintf(` | ||
resource "azuredevops_project" "test" { | ||
name = "%[1]s" | ||
description = "description" | ||
visibility = "private" | ||
version_control = "Git" | ||
work_item_template = "Agile" | ||
} | ||
|
||
resource "azuredevops_git_repository" "test" { | ||
project_id = azuredevops_project.test.id | ||
name = "%[2]s" | ||
initialization { | ||
init_type = "Clean" | ||
} | ||
} | ||
|
||
resource "azuredevops_git_repository_branch" "from_master" { | ||
repository_id = azuredevops_git_repository.test.id | ||
name = "testbranch-%[3]s" | ||
ref_branch = "master" | ||
} | ||
|
||
resource "azuredevops_git_repository_branch_lock" "master" { | ||
repository_id = azuredevops_git_repository.test.id | ||
branch = azuredevops_git_repository_branch.from_master.name | ||
is_locked = true | ||
} | ||
`, projectName, gitRepoName, branchName) | ||
} | ||
|
||
func hclGitRepoBranchUnlock(projectName, gitRepoName, branchName string) string { | ||
return fmt.Sprintf(` | ||
resource "azuredevops_project" "test" { | ||
name = "%[1]s" | ||
description = "description" | ||
visibility = "private" | ||
version_control = "Git" | ||
work_item_template = "Agile" | ||
} | ||
|
||
resource "azuredevops_git_repository" "test" { | ||
project_id = azuredevops_project.test.id | ||
name = "%[2]s" | ||
initialization { | ||
init_type = "Clean" | ||
} | ||
} | ||
|
||
resource "azuredevops_git_repository_branch" "from_master" { | ||
repository_id = azuredevops_git_repository.test.id | ||
name = "testbranch-%[3]s" | ||
ref_branch = "master" | ||
} | ||
|
||
resource "azuredevops_git_repository_branch_lock" "master" { | ||
repository_id = azuredevops_git_repository.test.id | ||
branch = azuredevops_git_repository_branch.from_master.name | ||
is_locked = false | ||
} | ||
`, projectName, gitRepoName, branchName) | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,168 @@ | ||||||
package git | ||||||
|
||||||
import ( | ||||||
"context" | ||||||
"fmt" | ||||||
"strings" | ||||||
"time" | ||||||
|
||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/git" | ||||||
"github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/client" | ||||||
"github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/utils" | ||||||
"github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/utils/converter" | ||||||
) | ||||||
|
||||||
func ResourceGitRepositoryBranchLock() *schema.Resource { | ||||||
return &schema.Resource{ | ||||||
CreateContext: resourceGitRepositoryBranchLockCreate, | ||||||
ReadContext: resourceGitRepositoryBranchLockRead, | ||||||
DeleteContext: resourceGitRepositoryBranchLockDelete, | ||||||
Timeouts: &schema.ResourceTimeout{ | ||||||
Create: schema.DefaultTimeout(5 * time.Minute), | ||||||
Read: schema.DefaultTimeout(5 * time.Minute), | ||||||
Delete: schema.DefaultTimeout(5 * time.Minute), | ||||||
}, | ||||||
Schema: map[string]*schema.Schema{ | ||||||
"repository_id": { | ||||||
Type: schema.TypeString, | ||||||
Required: true, | ||||||
ForceNew: true, | ||||||
ValidateFunc: validation.IsUUID, | ||||||
}, | ||||||
"branch": { | ||||||
Type: schema.TypeString, | ||||||
Required: true, | ||||||
ForceNew: true, | ||||||
ValidateFunc: validation.StringIsNotEmpty, | ||||||
}, | ||||||
"is_locked": { | ||||||
Type: schema.TypeBool, | ||||||
Required: true, | ||||||
ForceNew: true, | ||||||
Default: true, | ||||||
}, | ||||||
}, | ||||||
} | ||||||
} | ||||||
|
||||||
func resourceGitRepositoryBranchLockCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||||||
clients := m.(*client.AggregatedClient) | ||||||
repoID := d.Get("repository_id").(string) | ||||||
branch := d.Get("branch").(string) | ||||||
isLocked := d.Get("is_locked").(bool) | ||||||
|
||||||
if strings.HasPrefix(branch, REF_BRANCH_PREFIX) { | ||||||
return diag.Errorf("Branch name must be in short format without refs/heads/ prefix, got: %q", branch) | ||||||
} | ||||||
|
||||||
branchRef := REF_BRANCH_PREFIX + branch | ||||||
|
||||||
// Get current ref to get the objectId | ||||||
refs, err := clients.GitReposClient.GetRefs(ctx, git.GetRefsArgs{ | ||||||
RepositoryId: &repoID, | ||||||
Filter: converter.String(strings.TrimPrefix(branchRef, "refs/")), | ||||||
}) | ||||||
if err != nil { | ||||||
return diag.FromErr(fmt.Errorf("Error getting branch ref: %v", err)) | ||||||
} | ||||||
if len(refs.Value) == 0 { | ||||||
return diag.Errorf("Branch %s not found", branch) | ||||||
} | ||||||
|
||||||
// Update the ref with lock status | ||||||
refUpdate := git.GitRefUpdate{ | ||||||
IsLocked: converter.Bool(isLocked), | ||||||
} | ||||||
|
||||||
_, err = clients.GitReposClient.UpdateRef(ctx, git.UpdateRefArgs{ | ||||||
NewRefInfo: &refUpdate, | ||||||
RepositoryId: &repoID, | ||||||
Filter: converter.String(strings.TrimPrefix(branchRef, "refs/")), | ||||||
}) | ||||||
if err != nil { | ||||||
return diag.FromErr(fmt.Errorf("Error updating branch lock: %v", err)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
d.SetId(fmt.Sprintf("%s:%s", repoID, branch)) | ||||||
return resourceGitRepositoryBranchLockRead(ctx, d, m) | ||||||
} | ||||||
|
||||||
func resourceGitRepositoryBranchLockRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||||||
clients := m.(*client.AggregatedClient) | ||||||
repoID, branch, err := parseBranchLockID(d.Id()) | ||||||
if err != nil { | ||||||
return diag.FromErr(err) | ||||||
} | ||||||
|
||||||
branchRef := REF_BRANCH_PREFIX + branch | ||||||
refs, err := clients.GitReposClient.GetRefs(ctx, git.GetRefsArgs{ | ||||||
RepositoryId: &repoID, | ||||||
Filter: converter.String(strings.TrimPrefix(branchRef, "refs/")), | ||||||
}) | ||||||
|
||||||
if err != nil { | ||||||
if utils.ResponseWasNotFound(err) { | ||||||
d.SetId("") | ||||||
return nil | ||||||
} | ||||||
return diag.FromErr(fmt.Errorf("Error reading branch ref: %v", err)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
if len(refs.Value) == 0 { | ||||||
d.SetId("") | ||||||
return nil | ||||||
} | ||||||
|
||||||
d.Set("is_locked", refs.Value[0].IsLocked) | ||||||
return nil | ||||||
} | ||||||
|
||||||
func resourceGitRepositoryBranchLockDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { | ||||||
// For delete, we'll just set isLocked to false | ||||||
clients := m.(*client.AggregatedClient) | ||||||
repoID, branch, err := parseBranchLockID(d.Id()) | ||||||
if err != nil { | ||||||
return diag.FromErr(err) | ||||||
} | ||||||
|
||||||
branchRef := REF_BRANCH_PREFIX + branch | ||||||
refs, err := clients.GitReposClient.GetRefs(ctx, git.GetRefsArgs{ | ||||||
RepositoryId: &repoID, | ||||||
Filter: converter.String(strings.TrimPrefix(branchRef, "refs/")), | ||||||
}) | ||||||
if err != nil { | ||||||
return diag.FromErr(fmt.Errorf("Error getting branch ref: %v", err)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
if len(refs.Value) == 0 { | ||||||
return nil | ||||||
} | ||||||
|
||||||
// Update the ref with lock status of False | ||||||
refUpdate := git.GitRefUpdate{ | ||||||
IsLocked: converter.Bool(false), | ||||||
} | ||||||
|
||||||
_, err = clients.GitReposClient.UpdateRef(ctx, git.UpdateRefArgs{ | ||||||
NewRefInfo: &refUpdate, | ||||||
RepositoryId: &repoID, | ||||||
Filter: converter.String(strings.TrimPrefix(branchRef, "refs/")), | ||||||
}) | ||||||
if err != nil { | ||||||
return diag.FromErr(fmt.Errorf("Error unlocking branch: %v", err)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
return nil | ||||||
} | ||||||
|
||||||
func parseBranchLockID(id string) (repoID string, branch string, err error) { | ||||||
parts := strings.Split(id, ":") | ||||||
if len(parts) != 2 { | ||||||
err = fmt.Errorf("Invalid branch lock ID: %s", id) | ||||||
return | ||||||
} | ||||||
repoID = parts[0] | ||||||
branch = parts[1] | ||||||
return | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
[CmdletBinding()] | ||
param ( | ||
[Parameter()] | ||
[string] | ||
[ValidateNotNullOrEmpty()] | ||
$BrandName, | ||
|
||
[Parameter()] | ||
[string] | ||
[ValidateNotNullOrEmpty()] | ||
$ResourceName, | ||
|
||
[Parameter()] | ||
[string] | ||
[ValidateNotNullOrEmpty()] | ||
[ValidateSet('resource', 'data')] | ||
$ResourceType, | ||
|
||
[Parameter()] | ||
[string] | ||
[ValidateNotNullOrEmpty()] | ||
$ResourceId | ||
) | ||
|
||
$script:PSDefaultParameterValues = @{ | ||
'*:Confirm' = $false | ||
'*:ErrorAction' = 'Stop' | ||
} | ||
|
||
|
||
Write-Host "==> Scaffolding Documentation..." | ||
|
||
& go run azuredevops/internal/website-scaffold/main.go ` | ||
-name $ResourceName ` | ||
-brand-name $BrandName ` | ||
-type $ResourceType ` | ||
-resource-id $ResourceId ` | ||
-website-path "./website/" | ||
|
||
Write-Host "==> Done." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Required
property cannot have default valuesis_locked
should be updateable