From adc37f2dd00a785391e493fd21c6a01c86236671 Mon Sep 17 00:00:00 2001 From: "mark.johnson" Date: Tue, 5 Nov 2024 10:48:11 -0600 Subject: [PATCH] first_attempt_at_requires --- github/resource_github_branch_protection.go | 25 +++++++++ .../resource_github_branch_protection_test.go | 51 +++++++++++++++++++ github/util_v4_branch_protection.go | 36 +++++++++++++ github/util_v4_consts.go | 2 + go.mod | 12 ++--- go.sum | 13 +++++ .../docs/r/branch_protection.html.markdown | 6 +++ 7 files changed, 139 insertions(+), 6 deletions(-) diff --git a/github/resource_github_branch_protection.go b/github/resource_github_branch_protection.go index 3235aecd89..69bc38599a 100644 --- a/github/resource_github_branch_protection.go +++ b/github/resource_github_branch_protection.go @@ -119,6 +119,21 @@ func resourceGithubBranchProtection() *schema.Resource { }, }, }, + PROTECTION_REQUIRES_DEPLOYMENTS: { + Type: schema.TypeList, + Optional: true, + Description: "Are successful deployments required before merging.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + PROTECTION_REQUIRES_DEPLOYMENT_ENVIRONMENTS: { + Type: schema.TypeSet, + Optional: true, + Description: "The list of required deployment environments.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, PROTECTION_REQUIRES_STATUS_CHECKS: { Type: schema.TypeList, Optional: true, @@ -256,6 +271,8 @@ func resourceGithubBranchProtectionCreate(d *schema.ResourceData, meta interface ReviewDismissalActorIDs: githubv4NewIDSlice(githubv4IDSlice(data.ReviewDismissalActorIDs)), LockBranch: githubv4.NewBoolean(githubv4.Boolean(data.LockBranch)), RequireLastPushApproval: githubv4.NewBoolean(githubv4.Boolean(data.RequireLastPushApproval)), + RequiresDeployments: githubv4.NewBoolean(githubv4.Boolean(data.RequiresDeployments)), + RequiredDeploymentEnvironments: githubv4NewIDSlice(githubv4IDSliceEmpty(data.RequiredDeploymentEnvironments)), } ctx := context.Background() @@ -339,6 +356,12 @@ func resourceGithubBranchProtectionRead(d *schema.ResourceData, meta interface{} log.Printf("[DEBUG] Problem setting '%s' in %s %s branch protection (%s)", PROTECTION_REQUIRES_APPROVING_REVIEWS, protection.Repository.Name, protection.Pattern, d.Id()) } + rquiredDeployments := setRequiredDeployments(protection) + err = d.Set(PROTECTION_REQUIRES_DEPLOYMENTS, rquiredDeployments) + if err != nil { + log.Printf("[DEBUG] Problem setting '%s' in %s %s branch protection (%s)", PROTECTION_REQUIRES_DEPLOYMENTS, protection.Repository.Name, protection.Pattern, d.Id()) + } + statusChecks := setStatusChecks(protection) err = d.Set(PROTECTION_REQUIRES_STATUS_CHECKS, statusChecks) if err != nil { @@ -429,6 +452,8 @@ func resourceGithubBranchProtectionUpdate(d *schema.ResourceData, meta interface ReviewDismissalActorIDs: githubv4NewIDSlice(githubv4IDSlice(data.ReviewDismissalActorIDs)), LockBranch: githubv4.NewBoolean(githubv4.Boolean(data.LockBranch)), RequireLastPushApproval: githubv4.NewBoolean(githubv4.Boolean(data.RequireLastPushApproval)), + RequiresDeployments: githubv4.NewBoolean(githubv4.Boolean(data.RequiresDeployments)), + RequiredDeploymentEnvironments: githubv4NewIDSlice(githubv4IDSliceEmpty(data.RequiredDeploymentEnvironments)), } ctx := context.WithValue(context.Background(), ctxId, d.Id()) diff --git a/github/resource_github_branch_protection_test.go b/github/resource_github_branch_protection_test.go index 2cbe21e777..daf774334c 100644 --- a/github/resource_github_branch_protection_test.go +++ b/github/resource_github_branch_protection_test.go @@ -520,7 +520,58 @@ func TestAccGithubBranchProtectionV4(t *testing.T) { }) }) + t.Run("configures branch push restrictions with deployment environments", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + + resource "github_branch_protection" "test" { + + repository_id = github_repository.test.node_id + pattern = "main" + require_deployments { + environments = ["dev","test] + } + } + `, randomID) + + check := resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "github_branch_protection.test", "require_deployments.environments.#", "2", + ), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + + }) t.Run("configures branch push restrictions with blocksCreations false", func(t *testing.T) { randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) diff --git a/github/util_v4_branch_protection.go b/github/util_v4_branch_protection.go index eae3467cda..261922db62 100644 --- a/github/util_v4_branch_protection.go +++ b/github/util_v4_branch_protection.go @@ -91,6 +91,8 @@ type BranchProtectionRule struct { RestrictsReviewDismissals githubv4.Boolean RequireLastPushApproval githubv4.Boolean LockBranch githubv4.Boolean + RequiresDeployments githubv4.Boolean + RequiredDeploymentEnvironments []githubv4.String } type BranchProtectionResourceData struct { @@ -119,6 +121,8 @@ type BranchProtectionResourceData struct { ReviewDismissalActorIDs []string RequireLastPushApproval bool LockBranch bool + RequiresDeployments bool + RequiredDeploymentEnvironments []string } func branchProtectionResourceData(d *schema.ResourceData, meta interface{}) (BranchProtectionResourceData, error) { @@ -213,6 +217,23 @@ func branchProtectionResourceData(d *schema.ResourceData, meta interface{}) (Bra } } + if v, ok := d.GetOk(PROTECTION_REQUIRES_DEPLOYMENTS); ok { + data.RequiresDeployments = true + vL := v.([]interface{}) + if len(vL) > 1 { + return BranchProtectionResourceData{}, + fmt.Errorf("error multiple %s declarations", PROTECTION_REQUIRES_DEPLOYMENT_ENVIRONMENTS) + } + for _, v := range vL { + if v == nil { + break + } + + m := v.(map[string]interface{}) + data.RequiredDeploymentEnvironments = expandNestedSet(m, PROTECTION_REQUIRES_DEPLOYMENT_ENVIRONMENTS) + } + } + if v, ok := d.GetOk(PROTECTION_REQUIRES_STATUS_CHECKS); ok { data.RequiresStatusChecks = true vL := v.([]interface{}) @@ -515,6 +536,21 @@ func setApprovingReviews(protection BranchProtectionRule, data BranchProtectionR return approvalReviews } +func setRequiredDeployments(protection BranchProtectionRule) interface{} { + if !protection.RequiresDeployments { + return nil + } + + requiredDeployments := []interface{}{ + map[string]interface{}{ + PROTECTION_REQUIRES_DEPLOYMENTS: protection.RequiresDeployments, + PROTECTION_REQUIRES_DEPLOYMENT_ENVIRONMENTS: protection.RequiredDeploymentEnvironments, + }, + } + + return requiredDeployments +} + func setStatusChecks(protection BranchProtectionRule) interface{} { if !protection.RequiresStatusChecks { return nil diff --git a/github/util_v4_consts.go b/github/util_v4_consts.go index 43dcf00e8d..60b3eca583 100644 --- a/github/util_v4_consts.go +++ b/github/util_v4_consts.go @@ -24,6 +24,8 @@ const ( PROTECTION_RESTRICTS_PUSHES = "restrict_pushes" PROTECTION_RESTRICTS_REVIEW_DISMISSALS = "restrict_dismissals" PROTECTION_REVIEW_DISMISSAL_ALLOWANCES = "dismissal_restrictions" + PROTECTION_REQUIRES_DEPLOYMENTS = "required_deployments" + PROTECTION_REQUIRES_DEPLOYMENT_ENVIRONMENTS = "environments" REPOSITORY_ID = "repository_id" ) diff --git a/go.mod b/go.mod index cad7c78b36..5b517f6b13 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,9 @@ require ( github.com/google/uuid v1.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 - github.com/shurcooL/githubv4 v0.0.0-20221126192849-0b5c4c7994eb + github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.26.0 + golang.org/x/crypto v0.28.0 golang.org/x/oauth2 v0.22.0 ) @@ -176,7 +176,7 @@ require ( github.com/sashamelentyev/usestdlibvars v1.26.0 // indirect github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect - github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 // indirect + github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect github.com/sivchari/tenv v1.7.1 // indirect @@ -220,10 +220,10 @@ require ( golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect golang.org/x/mod v0.18.0 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect golang.org/x/tools v0.22.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect diff --git a/go.sum b/go.sum index 846603e138..cf74730212 100644 --- a/go.sum +++ b/go.sum @@ -580,10 +580,14 @@ github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqP github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shurcooL/githubv4 v0.0.0-20221126192849-0b5c4c7994eb h1:foJysa74+t41fG7adnt+TkfcNxQUWid8R/HlXe+Mmbw= github.com/shurcooL/githubv4 v0.0.0-20221126192849-0b5c4c7994eb/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= +github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M= +github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 h1:B1PEwpArrNp4dkQrfxh/abbBAOZBVp0ds+fBEOUOqOc= github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= +github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= +github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -720,6 +724,8 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -807,6 +813,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -892,6 +900,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -902,6 +912,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -917,6 +928,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/website/docs/r/branch_protection.html.markdown b/website/docs/r/branch_protection.html.markdown index ae2c6dc3d4..9ce0f6a822 100644 --- a/website/docs/r/branch_protection.html.markdown +++ b/website/docs/r/branch_protection.html.markdown @@ -117,6 +117,12 @@ If specified, usual value is the [job name](https://docs.github.com/en/actions/u For workflows that use matrixes, append the matrix name to the value using the following pattern `([, ])`. Matrixes should be specified based on the order of matrix properties in the workflow file. See [GitHub Documentation]("https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#using-a-matrix-strategy") for more information. For workflows that use reusable workflows, the pattern is ` / `. This can extend multiple levels. +### Required Deployments + +`required_deployments` supports the following arguments: + +* `environments`: (Optional) The list of required Environments that require deployment. + ### Required Pull Request Reviews `required_pull_request_reviews` supports the following arguments: