Skip to content

Commit 64f123a

Browse files
edhelielilmaxkfcampbell
authored
fix: Github Environments Policy feature causing the provider to produce inconsistent result (#1799)
* Add deployment policy resource - Add the initial code to manage the resource - Add sample configuration used to test it TODO - Documentation - Tests * Add schema description * Fix creation of resource ID * Add tests * Add documentation * Add terraform import support * Undo example add * Fix formatting * PR feedback * fix: environment branch policy failing to find the created resource The `Read` operation of the Environments Branch Policy resource was failing to find the newly created Branch policies, due to wrongly encoded environment name. Which cause the provider to be inconsistent. This fix uses `url.PathEscape` instead of `url.QueryEscape` since we are using path parameters with the Github API in that case. Additionally 2 operations - `Read` and `Delete` don't need to use it as they receive the environment name already parsed and attempting to encode it again breaks the name. * Fix incorrect merge --------- Co-authored-by: Massimiliano Donini <[email protected]> Co-authored-by: Keegan Campbell <[email protected]>
1 parent dc087bd commit 64f123a

5 files changed

+311
-1
lines changed

github/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func Provider() terraform.ResourceProvider {
140140
"github_repository_deploy_key": resourceGithubRepositoryDeployKey(),
141141
"github_repository_deployment_branch_policy": resourceGithubRepositoryDeploymentBranchPolicy(),
142142
"github_repository_environment": resourceGithubRepositoryEnvironment(),
143+
"github_repository_environment_deployment_policy": resourceGithubRepositoryEnvironmentDeploymentPolicy(),
143144
"github_repository_file": resourceGithubRepositoryFile(),
144145
"github_repository_milestone": resourceGithubRepositoryMilestone(),
145146
"github_repository_project": resourceGithubRepositoryProject(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"log"
6+
"net/http"
7+
"net/url"
8+
"strconv"
9+
10+
"github.com/google/go-github/v53/github"
11+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
12+
)
13+
14+
func resourceGithubRepositoryEnvironmentDeploymentPolicy() *schema.Resource {
15+
return &schema.Resource{
16+
Create: resourceGithubRepositoryEnvironmentDeploymentPolicyCreate,
17+
Read: resourceGithubRepositoryEnvironmentDeploymentPolicyRead,
18+
Update: resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate,
19+
Delete: resourceGithubRepositoryEnvironmentDeploymentPolicyDelete,
20+
Importer: &schema.ResourceImporter{
21+
State: schema.ImportStatePassthrough,
22+
},
23+
Schema: map[string]*schema.Schema{
24+
"repository": {
25+
Type: schema.TypeString,
26+
Required: true,
27+
ForceNew: true,
28+
Description: "The name of the repository. The name is not case sensitive.",
29+
},
30+
"environment": {
31+
Type: schema.TypeString,
32+
Required: true,
33+
ForceNew: true,
34+
Description: "The name of the environment.",
35+
},
36+
"branch_pattern": {
37+
Type: schema.TypeString,
38+
Required: true,
39+
ForceNew: false,
40+
Description: "The name pattern that branches must match in order to deploy to the environment.",
41+
},
42+
},
43+
}
44+
45+
}
46+
47+
func resourceGithubRepositoryEnvironmentDeploymentPolicyCreate(d *schema.ResourceData, meta interface{}) error {
48+
client := meta.(*Owner).v3client
49+
ctx := context.Background()
50+
51+
owner := meta.(*Owner).name
52+
repoName := d.Get("repository").(string)
53+
envName := d.Get("environment").(string)
54+
branchPattern := d.Get("branch_pattern").(string)
55+
escapedEnvName := url.PathEscape(envName)
56+
57+
createData := github.DeploymentBranchPolicyRequest{
58+
Name: github.String(branchPattern),
59+
}
60+
61+
resultKey, _, err := client.Repositories.CreateDeploymentBranchPolicy(ctx, owner, repoName, escapedEnvName, &createData)
62+
if err != nil {
63+
return err
64+
}
65+
66+
d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(resultKey.GetID(), 10)))
67+
return resourceGithubRepositoryEnvironmentDeploymentPolicyRead(d, meta)
68+
}
69+
70+
func resourceGithubRepositoryEnvironmentDeploymentPolicyRead(d *schema.ResourceData, meta interface{}) error {
71+
client := meta.(*Owner).v3client
72+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
73+
74+
owner := meta.(*Owner).name
75+
repoName, envName, branchPolicyIdString, err := parseThreePartID(d.Id(), "repository", "environment", "branchPolicyId")
76+
if err != nil {
77+
return err
78+
}
79+
80+
branchPolicyId, err := strconv.ParseInt(branchPolicyIdString, 10, 64)
81+
if err != nil {
82+
return err
83+
}
84+
85+
branchPolicy, _, err := client.Repositories.GetDeploymentBranchPolicy(ctx, owner, repoName, envName, branchPolicyId)
86+
if err != nil {
87+
if ghErr, ok := err.(*github.ErrorResponse); ok {
88+
if ghErr.Response.StatusCode == http.StatusNotModified {
89+
return nil
90+
}
91+
if ghErr.Response.StatusCode == http.StatusNotFound {
92+
log.Printf("[INFO] Removing branch deployment policy for %s/%s/%s from state because it no longer exists in GitHub",
93+
owner, repoName, envName)
94+
d.SetId("")
95+
return nil
96+
}
97+
}
98+
return err
99+
}
100+
101+
d.Set("branch_pattern", branchPolicy.GetName())
102+
return nil
103+
}
104+
105+
func resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
106+
client := meta.(*Owner).v3client
107+
ctx := context.Background()
108+
109+
owner := meta.(*Owner).name
110+
repoName := d.Get("repository").(string)
111+
envName := d.Get("environment").(string)
112+
branchPattern := d.Get("branch_pattern").(string)
113+
escapedEnvName := url.PathEscape(envName)
114+
_, _, branchPolicyIdString, err := parseThreePartID(d.Id(), "repository", "environment", "branchPolicyId")
115+
if err != nil {
116+
return err
117+
}
118+
119+
branchPolicyId, err := strconv.ParseInt(branchPolicyIdString, 10, 64)
120+
if err != nil {
121+
return err
122+
}
123+
124+
updateData := github.DeploymentBranchPolicyRequest{
125+
Name: github.String(branchPattern),
126+
}
127+
128+
resultKey, _, err := client.Repositories.UpdateDeploymentBranchPolicy(ctx, owner, repoName, escapedEnvName, branchPolicyId, &updateData)
129+
if err != nil {
130+
return err
131+
}
132+
d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(resultKey.GetID(), 10)))
133+
return resourceGithubRepositoryEnvironmentDeploymentPolicyRead(d, meta)
134+
}
135+
136+
func resourceGithubRepositoryEnvironmentDeploymentPolicyDelete(d *schema.ResourceData, meta interface{}) error {
137+
client := meta.(*Owner).v3client
138+
ctx := context.Background()
139+
140+
owner := meta.(*Owner).name
141+
repoName, envName, branchPolicyIdString, err := parseThreePartID(d.Id(), "repository", "environment", "branchPolicyId")
142+
if err != nil {
143+
return err
144+
}
145+
146+
branchPolicyId, err := strconv.ParseInt(branchPolicyIdString, 10, 64)
147+
if err != nil {
148+
return err
149+
}
150+
151+
_, err = client.Repositories.DeleteDeploymentBranchPolicy(ctx, owner, repoName, envName, branchPolicyId)
152+
if err != nil {
153+
return err
154+
}
155+
156+
return nil
157+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
9+
)
10+
11+
func TestAccGithubRepositoryEnvironmentDeploymentPolicy(t *testing.T) {
12+
13+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
14+
15+
t.Run("creates a repository environment with deployment policy", func(t *testing.T) {
16+
17+
config := fmt.Sprintf(`
18+
19+
data "github_user" "current" {
20+
username = ""
21+
}
22+
23+
resource "github_repository" "test" {
24+
name = "tf-acc-test-%s"
25+
}
26+
27+
resource "github_repository_environment" "test" {
28+
repository = github_repository.test.name
29+
environment = "environment/test"
30+
wait_timer = 10000
31+
reviewers {
32+
users = [data.github_user.current.id]
33+
}
34+
deployment_branch_policy {
35+
protected_branches = false
36+
custom_branch_policies = true
37+
}
38+
}
39+
40+
resource "github_repository_environment_deployment_policy" "test" {
41+
repository = github_repository.test.name
42+
environment = github_repository_environment.test.environment
43+
branch_pattern = "releases/*"
44+
}
45+
46+
`, randomID)
47+
48+
check := resource.ComposeTestCheckFunc(
49+
resource.TestCheckResourceAttr(
50+
"github_repository_environment_deployment_policy.test", "repository",
51+
fmt.Sprintf("tf-acc-test-%s", randomID),
52+
),
53+
resource.TestCheckResourceAttr(
54+
"github_repository_environment_deployment_policy.test", "environment",
55+
"environment/test",
56+
),
57+
resource.TestCheckResourceAttr(
58+
"github_repository_environment_deployment_policy.test", "branch_pattern",
59+
"releases/*",
60+
),
61+
)
62+
63+
testCase := func(t *testing.T, mode string) {
64+
resource.Test(t, resource.TestCase{
65+
PreCheck: func() { skipUnlessMode(t, mode) },
66+
Providers: testAccProviders,
67+
Steps: []resource.TestStep{
68+
{
69+
Config: config,
70+
Check: check,
71+
},
72+
},
73+
})
74+
}
75+
76+
t.Run("with an anonymous account", func(t *testing.T) {
77+
t.Skip("anonymous account not supported for this operation")
78+
})
79+
80+
t.Run("with an individual account", func(t *testing.T) {
81+
testCase(t, individual)
82+
})
83+
84+
t.Run("with an organization account", func(t *testing.T) {
85+
testCase(t, organization)
86+
})
87+
88+
})
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
layout: "github"
3+
page_title: "GitHub: github_repository_environment_deployment_policy"
4+
description: |-
5+
Creates and manages environment deployment branch policies for GitHub repositories
6+
---
7+
8+
# github_repository_environment_deployment_policy
9+
10+
This resource allows you to create and manage environment deployment branch policies for a GitHub repository.
11+
12+
## Example Usage
13+
14+
```hcl
15+
data "github_user" "current" {
16+
username = ""
17+
}
18+
19+
resource "github_repository" "test" {
20+
name = "tf-acc-test-%s"
21+
}
22+
23+
resource "github_repository_environment" "test" {
24+
repository = github_repository.test.name
25+
environment = "environment/test"
26+
wait_timer = 10000
27+
reviewers {
28+
users = [data.github_user.current.id]
29+
}
30+
deployment_branch_policy {
31+
protected_branches = false
32+
custom_branch_policies = true
33+
}
34+
}
35+
36+
resource "github_repository_environment_deployment_policy" "test" {
37+
repository = github_repository.test.name
38+
environment = github_repository_environment.test.environment
39+
branch_pattern = "releases/*"
40+
}
41+
```
42+
43+
## Argument Reference
44+
45+
The following arguments are supported:
46+
47+
* `environment` - (Required) The name of the environment.
48+
49+
* `repository` - (Required) The repository of the environment.
50+
51+
* `branch_pattern` - (Required) The name pattern that branches must match in order to deploy to the environment.
52+
53+
54+
## Import
55+
56+
GitHub Repository Environment Deployment Policy can be imported using an ID made up of `name` of the repository combined with the `environment` name of the environment with the `Id` of the deployment policy, separated by a `:` character, e.g.
57+
58+
```
59+
$ terraform import github_repository_environment.daily terraform:daily:123456
60+
```

website/github.erb

+4-1
Original file line numberDiff line numberDiff line change
@@ -307,11 +307,14 @@
307307
<li>
308308
<a href="/docs/providers/github/r/repository_environment.html">github_repository_environment</a>
309309
</li>
310+
<li>
311+
<a href="/docs/providers/github/r/repository_environment_deployment_policy.html">github_repository_environment_deployment_policy</a>
312+
</li>
310313
<li>
311314
<a href="/docs/providers/github/r/repository_environment_secret.html">github_repository_environment_secret</a>
312315
</li>
313316
<li>
314-
<a href="/docs/providers/github/r/repository_environment_variable.html">github_repository_environment_variable</a>
317+
<a href="/docs/providers/github/r/repository_environment_variable.html">github_repository_environment_variable</a>
315318
</li>
316319
<li>
317320
<a href="/docs/providers/github/r/repository_file.html">github_repository_file</a>

0 commit comments

Comments
 (0)