From 68c38af5d268fb84e377ebb974a4b6a4859415ae Mon Sep 17 00:00:00 2001 From: Bill Birch Date: Fri, 16 Oct 2020 19:15:27 +1100 Subject: [PATCH 1/5] branch policy - minimum reviewers - add more settings flags#206 MADM-312 --- .vscode/launch.json | 2 +- .../resource_branchpolicy_test.go | 9 ++- .../acceptancetests/testutils/commons.go | 8 +- .../resource_branchpolicy_auto_reviewers.go | 4 + .../resource_branchpolicy_min_reviewers.go | 79 ++++++++++++++----- ...esource_branchpolicy_min_reviewers_test.go | 54 +++++++++++++ .../branch_policy_min_reviewers.html.markdown | 19 ++++- 7 files changed, 146 insertions(+), 29 deletions(-) create mode 100644 azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers_test.go diff --git a/.vscode/launch.json b/.vscode/launch.json index 29a591d7b..8b8077cca 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "go", "request": "launch", "mode": "auto", - "program": "${file}", + "program": "${workspaceFolder}", "args": [ "-test.v", "-test.run", diff --git a/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go b/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go index 547d114f2..a9f344c51 100644 --- a/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go +++ b/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go @@ -44,12 +44,17 @@ func TestAccBranchPolicyMinReviewers_CreateAndUpdate(t *testing.T) { }) } -func getMinReviewersHcl(enabled bool, blocking bool, reviewers int, submitterCanVote bool) string { +func getMinReviewersHcl(enabled bool, blocking bool, reviewers int, flag bool) string { settings := fmt.Sprintf( ` reviewer_count = %d submitter_can_vote = %t - `, reviewers, submitterCanVote, + allow_completion_with_rejects_or_waits = %t + last_pusher_cannot_approve = %t + on_last_iteration_require_vote = %t + on_push_reset_all_votes = %t + on_push_reset_approved_votes = %t + `, reviewers, flag, flag, flag, flag, flag, flag, ) return getBranchPolicyHcl("azuredevops_branch_policy_min_reviewers", enabled, blocking, settings) diff --git a/azuredevops/internal/acceptancetests/testutils/commons.go b/azuredevops/internal/acceptancetests/testutils/commons.go index f1e478df4..0973353e2 100644 --- a/azuredevops/internal/acceptancetests/testutils/commons.go +++ b/azuredevops/internal/acceptancetests/testutils/commons.go @@ -49,12 +49,16 @@ func PreCheck(t *testing.T, additionalEnvVars *[]string) { if additionalEnvVars != nil { requiredEnvVars = append(requiredEnvVars, *additionalEnvVars...) } - + missing := false for _, variable := range requiredEnvVars { if _, ok := os.LookupEnv(variable); !ok { - t.Fatalf("`%s` must be set for this acceptance test!", variable) + missing = true + t.Errorf("`%s` must be set for this acceptance test!", variable) } } + if missing { + t.Fatalf("Some environment variables missing.") + } } // GenerateResourceName generates a random name with a constant prefix, useful for acceptance tests diff --git a/azuredevops/internal/service/policy/resource_branchpolicy_auto_reviewers.go b/azuredevops/internal/service/policy/resource_branchpolicy_auto_reviewers.go index 7bb001457..cb6051892 100644 --- a/azuredevops/internal/service/policy/resource_branchpolicy_auto_reviewers.go +++ b/azuredevops/internal/service/policy/resource_branchpolicy_auto_reviewers.go @@ -18,6 +18,10 @@ type autoReviewerPolicySettings struct { DisplayMessage string `json:"message"` } +const ( + schemaSubmitterCanVote = "submitter_can_vote" +) + const ( autoReviewerIds = "auto_reviewer_ids" pathFilters = "path_filters" diff --git a/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go b/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go index 4edbfb754..ba71c1fcb 100644 --- a/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go +++ b/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go @@ -3,6 +3,7 @@ package policy import ( "encoding/json" "fmt" + "reflect" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" @@ -11,14 +12,14 @@ import ( "github.com/microsoft/azure-devops-go-api/azuredevops/policy" ) -const ( - schemaReviewerCount = "reviewer_count" - schemaSubmitterCanVote = "submitter_can_vote" -) - type minReviewerPolicySettings struct { - ApprovalCount int `json:"minimumApproverCount"` - SubmitterCanVote bool `json:"creatorVoteCounts"` + ApprovalCount int `json:"minimumApproverCount" tf:"reviewer_count"` + SubmitterCanVote bool `json:"creatorVoteCounts" tf:"submitter_can_vote"` + AllowCompletionWithRejectsOrWaits bool `json:"allowDownvotes" tf:"allow_completion_with_rejects_or_waits"` + OnPushResetApprovedVotes bool `json:"resetOnSourcePush" tf:"on_push_reset_approved_votes"` + OnLastIterationRequireVote bool `json:"requireVoteOnLastIteration" tf:"on_last_iteration_require_vote"` + OnPushResetAllVotes bool `json:"resetRejectionsOnSourcePush" tf:"on_push_reset_all_votes"` + LastPusherCannotVote bool `json:"blockLastPusherVote" tf:"last_pusher_cannot_approve"` } // ResourceBranchPolicyMinReviewers schema and implementation for min reviewer policy resource @@ -30,19 +31,32 @@ func ResourceBranchPolicyMinReviewers() *schema.Resource { }) settingsSchema := resource.Schema[SchemaSettings].Elem.(*schema.Resource).Schema - settingsSchema[schemaReviewerCount] = &schema.Schema{ - Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.IntAtLeast(1), - } - settingsSchema[schemaSubmitterCanVote] = &schema.Schema{ - Type: schema.TypeBool, - Default: false, - Optional: true, + + tipe := reflect.TypeOf(minReviewerPolicySettings{}) + for i := 0; i < tipe.NumField(); i++ { + tfName := tipe.Field(i).Tag.Get("tf") + if _, ok := settingsSchema[tfName]; ok { + continue // skip those which are already set + } + if tipe.Field(i).Type == reflect.TypeOf(true) { + settingsSchema[tfName] = &schema.Schema{ + Type: schema.TypeBool, + Default: false, + Optional: true, + } + } + if tipe.Field(i).Type == reflect.TypeOf(0) { + settingsSchema[tfName] = &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(1), + } + } } return resource } +// API to TF func minReviewersFlattenFunc(d *schema.ResourceData, policyConfig *policy.PolicyConfiguration, projectID *string) error { err := baseFlattenFunc(d, policyConfig, projectID) if err != nil { @@ -62,13 +76,23 @@ func minReviewersFlattenFunc(d *schema.ResourceData, policyConfig *policy.Policy settingsList := d.Get(SchemaSettings).([]interface{}) settings := settingsList[0].(map[string]interface{}) - settings[schemaReviewerCount] = policySettings.ApprovalCount - settings[schemaSubmitterCanVote] = policySettings.SubmitterCanVote + tipe := reflect.TypeOf(policySettings) + for i := 0; i < tipe.NumField(); i++ { + tfName := tipe.Field(i).Tag.Get("tf") + ps := reflect.ValueOf(policySettings) + if tipe.Field(i).Type == reflect.TypeOf(true) { + settings[tfName] = ps.Field(i).Bool() + } + if tipe.Field(i).Type == reflect.TypeOf(0) { + settings[tfName] = ps.Field(i).Int() + } + } d.Set(SchemaSettings, settingsList) return nil } +// From TF to API func minReviewersExpandFunc(d *schema.ResourceData, typeID uuid.UUID) (*policy.PolicyConfiguration, *string, error) { policyConfig, projectID, err := baseExpandFunc(d, typeID) if err != nil { @@ -79,8 +103,23 @@ func minReviewersExpandFunc(d *schema.ResourceData, typeID uuid.UUID) (*policy.P settings := settingsList[0].(map[string]interface{}) policySettings := policyConfig.Settings.(map[string]interface{}) - policySettings["minimumApproverCount"] = settings[schemaReviewerCount].(int) - policySettings["creatorVoteCounts"] = settings[schemaSubmitterCanVote].(bool) + + tipe := reflect.TypeOf(minReviewerPolicySettings{}) + for i := 0; i < tipe.NumField(); i++ { + tags := tipe.Field(i).Tag + apiName := tags.Get("json") + tfName := tags.Get("tf") + if _, ok := policySettings[apiName]; ok { + continue + } + if tipe.Field(i).Type == reflect.TypeOf(true) { + policySettings[apiName] = settings[tfName].(bool) + } + if tipe.Field(i).Type == reflect.TypeOf(0) { + policySettings[apiName] = settings[tfName].(int) + } + + } return policyConfig, projectID, nil } diff --git a/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers_test.go b/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers_test.go new file mode 100644 index 000000000..f48fffd1b --- /dev/null +++ b/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers_test.go @@ -0,0 +1,54 @@ +// +build all resource_branchpolicy_min_reviewers +// +build !exclude_resource_branchpolicy_min_reviewers + +package policy + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/stretchr/testify/require" + + "github.com/google/uuid" + "github.com/microsoft/azure-devops-go-api/azuredevops/policy" + "github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/utils/converter" +) + +// verifies that the flatten/expand round trip path produces repeatable results +func TestBranchPolicyMinReviewers_ExpandFlatten_Roundtrip(t *testing.T) { + var projectID = uuid.New().String() + var randomUUID = uuid.New() + var testPolicy = &policy.PolicyConfiguration{ + Id: converter.Int(1), + IsEnabled: converter.Bool(true), + IsBlocking: converter.Bool(true), + Type: &policy.PolicyTypeRef{ + Id: &randomUUID, + }, + Settings: map[string]interface{}{ + "scope": []map[string]interface{}{ + { + "repositoryId": "test-repo-id", + "refName": "test-ref-name", + "matchKind": "test-match-kind", + }, + }, + "minimumApproverCount": 2, + "creatorVoteCounts": true, + "allowDownvotes": true, + "resetOnSourcePush": true, + "requireVoteOnLastIteration": true, + "resetRejectionsOnSourcePush": true, + "blockLastPusherVote": true, + }, + } + + resourceData := schema.TestResourceDataRaw(t, ResourceBranchPolicyMinReviewers().Schema, nil) + err := minReviewersFlattenFunc(resourceData, testPolicy, &projectID) + require.Nil(t, err) + expandedPolicy, expandedProjectID, err := minReviewersExpandFunc(resourceData, randomUUID) + require.Nil(t, err) + + require.Equal(t, testPolicy, expandedPolicy) + require.Equal(t, projectID, *expandedProjectID) +} diff --git a/website/docs/r/branch_policy_min_reviewers.html.markdown b/website/docs/r/branch_policy_min_reviewers.html.markdown index 6217dc54e..472f8ff5e 100644 --- a/website/docs/r/branch_policy_min_reviewers.html.markdown +++ b/website/docs/r/branch_policy_min_reviewers.html.markdown @@ -7,7 +7,7 @@ description: |- # azuredevops_branch_policy_min_reviewers -Manages a minimum reviewer branch policy within Azure DevOps. +Branch policy for reviewers on pull requests. Includes the minimum number of reviewers and other conditions. ## Example Usage @@ -31,8 +31,13 @@ resource "azuredevops_branch_policy_min_reviewers" "p" { blocking = true settings { - reviewer_count = 2 + reviewer_count = 7 submitter_can_vote = false + last_pusher_cannot_approve = true + allow_completion_with_rejects_or_waits = false + on_push_reset_approved_votes = true + on_push_reset_all_votes = false + on_last_iteration_require_vote = false scope { repository_id = azuredevops_git_repository.r.id @@ -41,7 +46,7 @@ resource "azuredevops_branch_policy_min_reviewers" "p" { } scope { - repository_id = azuredevops_git_repository.r.id + repository_id = null repository_ref = "refs/heads/releases" match_type = "Prefix" } @@ -61,7 +66,13 @@ The following arguments are supported: A `settings` block supports the following: - `reviewer_count` - (Required) The number of reviewrs needed to approve. -- `submitter_can_vote` - (Optional) Controls whether or not the submitter's vote counts. Defaults to `false`. +- `submitter_can_vote` - (Optional) Allow requestors to approve their own changes. Defaults to `false`. +- `last_pusher_cannot_approve`(Optional) Prohibit the most recent pusher from approving their own changes. Defaults to `false`. +- `allow_completion_with_rejects_or_waits` (Optional) Allow completion even if some reviewers vote to wait or reject. Defaults to `false`. +- `on_push_reset_approved_votes` (Optional) When new changes are pushed reset all approval votes (does not reset votes to reject or wait). Defaults to `false`. +- `on_push_reset_all_votes` (Optional) When new changes are pushed reset all code reviewer votes. Defaults to `false`. +- `on_last_iteration_require_vote` (Optional) On last iteration require vote. Defaults to `false`. + - `scope` (Required) Controls which repositories and branches the policy will be enabled for. This block must be defined at least once. A `settings` `scope` block supports the following: From 67ad7f7b6ee1cb7d85da3e216d75fb0a333639d2 Mon Sep 17 00:00:00 2001 From: Bill Birch Date: Tue, 20 Oct 2020 13:29:52 +1100 Subject: [PATCH 2/5] branch policy - minimum reviewers - PR changes flags#206 MADM-312 --- .../acceptancetests/resource_branchpolicy_test.go | 15 +++++++++++++++ .../resource_branchpolicy_auto_reviewers.go | 15 ++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go b/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go index a9f344c51..6ada59053 100644 --- a/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go +++ b/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go @@ -14,6 +14,7 @@ import ( ) func TestAccBranchPolicyMinReviewers_CreateAndUpdate(t *testing.T) { + // minReviewerTfNode := "azuredevops_branch_policy_min_reviewers.p" resource.Test(t, resource.TestCase{ @@ -26,6 +27,13 @@ func TestAccBranchPolicyMinReviewers_CreateAndUpdate(t *testing.T) { resource.TestCheckResourceAttrSet(minReviewerTfNode, "id"), resource.TestCheckResourceAttr(minReviewerTfNode, "blocking", "true"), resource.TestCheckResourceAttr(minReviewerTfNode, "enabled", "true"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.submitter_can_vote", "false"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.allow_completion_with_rejects_or_waits", "false"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.last_pusher_cannot_approve", "false"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_last_iteration_require_vote", "false"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_last_iteration_require_vote", "false"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_all_votes", "false"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_approved_votes", "false"), ), }, { Config: getMinReviewersHcl(false, false, 2, true), @@ -33,6 +41,13 @@ func TestAccBranchPolicyMinReviewers_CreateAndUpdate(t *testing.T) { resource.TestCheckResourceAttrSet(minReviewerTfNode, "id"), resource.TestCheckResourceAttr(minReviewerTfNode, "blocking", "false"), resource.TestCheckResourceAttr(minReviewerTfNode, "enabled", "false"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.submitter_can_vote", "true"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.allow_completion_with_rejects_or_waits", "true"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.last_pusher_cannot_approve", "true"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_last_iteration_require_vote", "true"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_last_iteration_require_vote", "true"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_all_votes", "true"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_approved_votes", "true"), ), }, { ResourceName: minReviewerTfNode, diff --git a/azuredevops/internal/service/policy/resource_branchpolicy_auto_reviewers.go b/azuredevops/internal/service/policy/resource_branchpolicy_auto_reviewers.go index cb6051892..5207bffb3 100644 --- a/azuredevops/internal/service/policy/resource_branchpolicy_auto_reviewers.go +++ b/azuredevops/internal/service/policy/resource_branchpolicy_auto_reviewers.go @@ -19,15 +19,12 @@ type autoReviewerPolicySettings struct { } const ( + autoReviewerIds = "auto_reviewer_ids" + pathFilters = "path_filters" + displayMessage = "message" schemaSubmitterCanVote = "submitter_can_vote" ) -const ( - autoReviewerIds = "auto_reviewer_ids" - pathFilters = "path_filters" - displayMessage = "message" -) - // ResourceBranchPolicyAutoReviewers schema and implementation for automatic code reviewer policy resource func ResourceBranchPolicyAutoReviewers() *schema.Resource { resource := genBasePolicyResource(&policyCrudArgs{ @@ -75,13 +72,13 @@ func autoReviewersFlattenFunc(d *schema.ResourceData, policyConfig *policy.Polic } policyAsJSON, err := json.Marshal(policyConfig.Settings) if err != nil { - return fmt.Errorf("Unable to marshal policy settings into JSON: %+v", err) + return fmt.Errorf("unable to marshal policy settings into JSON: %+v", err) } policySettings := autoReviewerPolicySettings{} err = json.Unmarshal(policyAsJSON, &policySettings) if err != nil { - return fmt.Errorf("Unable to unmarshal branch policy settings (%+v): %+v", policySettings, err) + return fmt.Errorf("unable to unmarshal branch policy settings (%+v): %+v", policySettings, err) } settingsList := d.Get(SchemaSettings).([]interface{}) @@ -91,7 +88,7 @@ func autoReviewersFlattenFunc(d *schema.ResourceData, policyConfig *policy.Polic settings[autoReviewerIds] = policySettings.AutoReviewerIds settings[pathFilters] = policySettings.PathFilters settings[displayMessage] = policySettings.DisplayMessage - d.Set(SchemaSettings, settingsList) + _ = d.Set(SchemaSettings, settingsList) return nil } From c0c8acab67f87e91bf59d628cda086cb2df4e641 Mon Sep 17 00:00:00 2001 From: Bill Birch Date: Mon, 7 Dec 2020 16:44:57 +1100 Subject: [PATCH 3/5] Update from PR review comments MADM-312 --- .vscode/launch.json | 5 +---- .../internal/acceptancetests/resource_branchpolicy_test.go | 2 +- website/docs/r/branch_policy_min_reviewers.html.markdown | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 8b8077cca..f66de5877 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,7 +1,4 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { @@ -9,7 +6,7 @@ "type": "go", "request": "launch", "mode": "auto", - "program": "${workspaceFolder}", + "program": "${file}", "args": [ "-test.v", "-test.run", diff --git a/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go b/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go index 6ada59053..65684cbe5 100644 --- a/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go +++ b/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go @@ -13,8 +13,8 @@ import ( "github.com/microsoft/terraform-provider-azuredevops/azuredevops/internal/acceptancetests/testutils" ) +// TestAccBranchPolicyMinReviewers_CreateAndUpdate - acceptance test for min reviewers branch policy attributes func TestAccBranchPolicyMinReviewers_CreateAndUpdate(t *testing.T) { - // minReviewerTfNode := "azuredevops_branch_policy_min_reviewers.p" resource.Test(t, resource.TestCase{ diff --git a/website/docs/r/branch_policy_min_reviewers.html.markdown b/website/docs/r/branch_policy_min_reviewers.html.markdown index 472f8ff5e..7bccc60aa 100644 --- a/website/docs/r/branch_policy_min_reviewers.html.markdown +++ b/website/docs/r/branch_policy_min_reviewers.html.markdown @@ -46,7 +46,7 @@ resource "azuredevops_branch_policy_min_reviewers" "p" { } scope { - repository_id = null + repository_id = null # All repositories in the project repository_ref = "refs/heads/releases" match_type = "Prefix" } From 2d9443fad818a6a0b9acbc7e127da49c363c82e7 Mon Sep 17 00:00:00 2001 From: Bill Birch Date: Wed, 16 Dec 2020 11:06:57 +1100 Subject: [PATCH 4/5] branch policy min reviewers fix linter error --- .../service/policy/resource_branchpolicy_min_reviewers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go b/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go index ba71c1fcb..713770282 100644 --- a/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go +++ b/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go @@ -118,7 +118,6 @@ func minReviewersExpandFunc(d *schema.ResourceData, typeID uuid.UUID) (*policy.P if tipe.Field(i).Type == reflect.TypeOf(0) { policySettings[apiName] = settings[tfName].(int) } - } return policyConfig, projectID, nil From 1f0948169de5bd29e81884222814ca9f92399094 Mon Sep 17 00:00:00 2001 From: Bill Birch Date: Wed, 20 Jan 2021 19:06:53 +1100 Subject: [PATCH 5/5] PR255 Changes requested ; fail if both `on_push_reset_all_votes` and `on_push_reset_approved_votes` are true #206 MADM-312 --- .../resource_branchpolicy_test.go | 24 +++++++++---------- .../resource_branchpolicy_min_reviewers.go | 24 ++++++++++++------- .../branch_policy_min_reviewers.html.markdown | 5 ++-- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go b/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go index 65684cbe5..7b1d88d9e 100644 --- a/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go +++ b/azuredevops/internal/acceptancetests/resource_branchpolicy_test.go @@ -32,8 +32,7 @@ func TestAccBranchPolicyMinReviewers_CreateAndUpdate(t *testing.T) { resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.last_pusher_cannot_approve", "false"), resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_last_iteration_require_vote", "false"), resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_last_iteration_require_vote", "false"), - resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_all_votes", "false"), - resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_approved_votes", "false"), + resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_approved_votes", "true"), ), }, { Config: getMinReviewersHcl(false, false, 2, true), @@ -47,7 +46,6 @@ func TestAccBranchPolicyMinReviewers_CreateAndUpdate(t *testing.T) { resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_last_iteration_require_vote", "true"), resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_last_iteration_require_vote", "true"), resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_all_votes", "true"), - resource.TestCheckResourceAttr(minReviewerTfNode, "settings.0.on_push_reset_approved_votes", "true"), ), }, { ResourceName: minReviewerTfNode, @@ -60,17 +58,19 @@ func TestAccBranchPolicyMinReviewers_CreateAndUpdate(t *testing.T) { } func getMinReviewersHcl(enabled bool, blocking bool, reviewers int, flag bool) string { + votes := "all" + if !flag { + votes = "approved" + } settings := fmt.Sprintf( ` - reviewer_count = %d - submitter_can_vote = %t - allow_completion_with_rejects_or_waits = %t - last_pusher_cannot_approve = %t - on_last_iteration_require_vote = %t - on_push_reset_all_votes = %t - on_push_reset_approved_votes = %t - `, reviewers, flag, flag, flag, flag, flag, flag, - ) + reviewer_count = %[1]d + submitter_can_vote = %[2]t + allow_completion_with_rejects_or_waits =%[2]t + last_pusher_cannot_approve = %[2]t + on_last_iteration_require_vote = %[2]t + on_push_reset_%[3]s_votes = true + `, reviewers, flag, votes) return getBranchPolicyHcl("azuredevops_branch_policy_min_reviewers", enabled, blocking, settings) } diff --git a/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go b/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go index 713770282..aa879df0a 100644 --- a/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go +++ b/azuredevops/internal/service/policy/resource_branchpolicy_min_reviewers.go @@ -16,9 +16,9 @@ type minReviewerPolicySettings struct { ApprovalCount int `json:"minimumApproverCount" tf:"reviewer_count"` SubmitterCanVote bool `json:"creatorVoteCounts" tf:"submitter_can_vote"` AllowCompletionWithRejectsOrWaits bool `json:"allowDownvotes" tf:"allow_completion_with_rejects_or_waits"` - OnPushResetApprovedVotes bool `json:"resetOnSourcePush" tf:"on_push_reset_approved_votes"` + OnPushResetApprovedVotes bool `json:"resetOnSourcePush" tf:"on_push_reset_approved_votes" ConflictsWith:"on_push_reset_all_votes"` OnLastIterationRequireVote bool `json:"requireVoteOnLastIteration" tf:"on_last_iteration_require_vote"` - OnPushResetAllVotes bool `json:"resetRejectionsOnSourcePush" tf:"on_push_reset_all_votes"` + OnPushResetAllVotes bool `json:"resetRejectionsOnSourcePush" tf:"on_push_reset_all_votes" ConflictsWith:"on_push_reset_approved_votes"` LastPusherCannotVote bool `json:"blockLastPusherVote" tf:"last_pusher_cannot_approve"` } @@ -32,26 +32,34 @@ func ResourceBranchPolicyMinReviewers() *schema.Resource { settingsSchema := resource.Schema[SchemaSettings].Elem.(*schema.Resource).Schema - tipe := reflect.TypeOf(minReviewerPolicySettings{}) - for i := 0; i < tipe.NumField(); i++ { - tfName := tipe.Field(i).Tag.Get("tf") + // Dynamically create the schema based on the minReviewerPolicySettings tags + metaField := reflect.TypeOf(minReviewerPolicySettings{}) + // Loop through the fields, adding schema for each one. + for i := 0; i < metaField.NumField(); i++ { + tfName := metaField.Field(i).Tag.Get("tf") if _, ok := settingsSchema[tfName]; ok { continue // skip those which are already set } - if tipe.Field(i).Type == reflect.TypeOf(true) { + if metaField.Field(i).Type == reflect.TypeOf(true) { settingsSchema[tfName] = &schema.Schema{ Type: schema.TypeBool, Default: false, Optional: true, } } - if tipe.Field(i).Type == reflect.TypeOf(0) { + if metaField.Field(i).Type == reflect.TypeOf(0) { settingsSchema[tfName] = &schema.Schema{ Type: schema.TypeInt, - Required: true, + Optional: true, ValidateFunc: validation.IntAtLeast(1), } } + if conflicts, ok := metaField.Field(i).Tag.Lookup("ConflictsWith"); ok { + if _, ok := settingsSchema[conflicts]; ok { + settingsSchema[conflicts].ConflictsWith = []string{SchemaSettings + ".0." + tfName} + settingsSchema[tfName].ConflictsWith = []string{SchemaSettings + ".0." + conflicts} + } + } } return resource } diff --git a/website/docs/r/branch_policy_min_reviewers.html.markdown b/website/docs/r/branch_policy_min_reviewers.html.markdown index 7bccc60aa..8f95e45ce 100644 --- a/website/docs/r/branch_policy_min_reviewers.html.markdown +++ b/website/docs/r/branch_policy_min_reviewers.html.markdown @@ -35,8 +35,7 @@ resource "azuredevops_branch_policy_min_reviewers" "p" { submitter_can_vote = false last_pusher_cannot_approve = true allow_completion_with_rejects_or_waits = false - on_push_reset_approved_votes = true - on_push_reset_all_votes = false + on_push_reset_approved_votes = true # OR on_push_reset_all_votes = true on_last_iteration_require_vote = false scope { @@ -73,6 +72,8 @@ A `settings` block supports the following: - `on_push_reset_all_votes` (Optional) When new changes are pushed reset all code reviewer votes. Defaults to `false`. - `on_last_iteration_require_vote` (Optional) On last iteration require vote. Defaults to `false`. +Only one of `on_push_reset_all_votes` or `on_push_reset_approved_votes` may be specified. + - `scope` (Required) Controls which repositories and branches the policy will be enabled for. This block must be defined at least once. A `settings` `scope` block supports the following: