From 7e8a8be3ce67d015a0a9040c5c0189526fa1106b Mon Sep 17 00:00:00 2001 From: Steve Hipwell Date: Thu, 5 Dec 2024 17:22:01 +0000 Subject: [PATCH 1/2] fix: Fixed branch protection v3 status checks churn Signed-off-by: Steve Hipwell --- github/resource_github_branch_protection_v3.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/github/resource_github_branch_protection_v3.go b/github/resource_github_branch_protection_v3.go index 9c4942d473..c2111c789f 100644 --- a/github/resource_github_branch_protection_v3.go +++ b/github/resource_github_branch_protection_v3.go @@ -59,6 +59,7 @@ func resourceGithubBranchProtectionV3() *schema.Resource { "contexts": { Type: schema.TypeSet, Optional: true, + Computed: true, Deprecated: "GitHub is deprecating the use of `contexts`. Use a `checks` array instead.", Elem: &schema.Schema{ Type: schema.TypeString, @@ -67,10 +68,12 @@ func resourceGithubBranchProtectionV3() *schema.Resource { "checks": { Type: schema.TypeSet, Optional: true, + Computed: true, Description: "The list of status checks to require in order to merge into this branch. No status checks are required by default. Checks should be strings containing the 'context' and 'app_id' like so 'context:app_id'", Elem: &schema.Schema{ Type: schema.TypeString, }, + ConflictsWith: []string{"required_status_checks.0.contexts"}, }, }, }, From 60a81f5ad1dc6f094b29b5c6d399330245d52595 Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Tue, 21 Oct 2025 12:59:43 -0500 Subject: [PATCH 2/2] Adds tests on branch protection to validate no churn --- ...source_github_branch_protection_v3_test.go | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/github/resource_github_branch_protection_v3_test.go b/github/resource_github_branch_protection_v3_test.go index a585221ce4..13c7b8b335 100644 --- a/github/resource_github_branch_protection_v3_test.go +++ b/github/resource_github_branch_protection_v3_test.go @@ -544,3 +544,153 @@ func TestAccGithubBranchProtectionV3_branch_push_restrictions(t *testing.T) { }) } + +func TestAccGithubBranchProtectionV3_computed_status_checks_no_churn(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + t.Run("handles computed status checks without churn", func(t *testing.T) { + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + + resource "github_branch_protection_v3" "test" { + repository = github_repository.test.name + branch = "main" + + required_status_checks { + strict = true + checks = [ + "ci/test", + "ci/build" + ] + } + } + `, randomID) + + check := resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "github_branch_protection_v3.test", "required_status_checks.#", "1", + ), + resource.TestCheckResourceAttr( + "github_branch_protection_v3.test", "required_status_checks.0.strict", "true", + ), + resource.TestCheckResourceAttr( + "github_branch_protection_v3.test", "required_status_checks.0.checks.#", "2", + ), + resource.TestCheckTypeSetElemAttr( + "github_branch_protection_v3.test", "required_status_checks.0.checks.*", "ci/test", + ), + resource.TestCheckTypeSetElemAttr( + "github_branch_protection_v3.test", "required_status_checks.0.checks.*", "ci/build", + ), + ) + + 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, + }, + // Re-apply the same config to test for churn + { + Config: config, + Check: check, + PlanOnly: true, + }, + }, + }) + } + + 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) + }) + }) +} + +func TestAccGithubBranchProtectionV3_computed_status_contexts_no_churn(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + t.Run("handles computed status contexts without churn", func(t *testing.T) { + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + + resource "github_branch_protection_v3" "test" { + repository = github_repository.test.name + branch = "main" + + required_status_checks { + strict = true + contexts = [ + "ci/test", + "ci/build" + ] + } + } + `, randomID) + + check := resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "github_branch_protection_v3.test", "required_status_checks.#", "1", + ), + resource.TestCheckResourceAttr( + "github_branch_protection_v3.test", "required_status_checks.0.strict", "true", + ), + resource.TestCheckResourceAttr( + "github_branch_protection_v3.test", "required_status_checks.0.contexts.#", "2", + ), + resource.TestCheckTypeSetElemAttr( + "github_branch_protection_v3.test", "required_status_checks.0.contexts.*", "ci/test", + ), + resource.TestCheckTypeSetElemAttr( + "github_branch_protection_v3.test", "required_status_checks.0.contexts.*", "ci/build", + ), + ) + + 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, + }, + // Re-apply the same config to test for churn + { + Config: config, + Check: check, + PlanOnly: true, + }, + }, + }) + } + + 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) + }) + }) +}