Skip to content
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

fix: Github Environments Policy feature causing the provider to produce inconsistent result #1799

Merged
merged 15 commits into from
Jul 24, 2023
Merged
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ func Provider() terraform.ResourceProvider {
"github_repository_deploy_key": resourceGithubRepositoryDeployKey(),
"github_repository_deployment_branch_policy": resourceGithubRepositoryDeploymentBranchPolicy(),
"github_repository_environment": resourceGithubRepositoryEnvironment(),
"github_repository_environment_deployment_policy": resourceGithubRepositoryEnvironmentDeploymentPolicy(),
"github_repository_file": resourceGithubRepositoryFile(),
"github_repository_milestone": resourceGithubRepositoryMilestone(),
"github_repository_project": resourceGithubRepositoryProject(),
Expand Down
157 changes: 157 additions & 0 deletions github/resource_github_repository_environment_deployment_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package github

import (
"context"
"log"
"net/http"
"net/url"
"strconv"

"github.com/google/go-github/v53/github"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func resourceGithubRepositoryEnvironmentDeploymentPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceGithubRepositoryEnvironmentDeploymentPolicyCreate,
Read: resourceGithubRepositoryEnvironmentDeploymentPolicyRead,
Update: resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate,
Delete: resourceGithubRepositoryEnvironmentDeploymentPolicyDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"repository": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the repository. The name is not case sensitive.",
},
"environment": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the environment.",
},
"branch_pattern": {
Type: schema.TypeString,
Required: true,
ForceNew: false,
Description: "The name pattern that branches must match in order to deploy to the environment.",
},
},
}

}

func resourceGithubRepositoryEnvironmentDeploymentPolicyCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
ctx := context.Background()

owner := meta.(*Owner).name
repoName := d.Get("repository").(string)
envName := d.Get("environment").(string)
branchPattern := d.Get("branch_pattern").(string)
escapedEnvName := url.PathEscape(envName)

createData := github.DeploymentBranchPolicyRequest{
Name: github.String(branchPattern),
}

resultKey, _, err := client.Repositories.CreateDeploymentBranchPolicy(ctx, owner, repoName, escapedEnvName, &createData)
if err != nil {
return err
}

d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(resultKey.GetID(), 10)))
return resourceGithubRepositoryEnvironmentDeploymentPolicyRead(d, meta)
}

func resourceGithubRepositoryEnvironmentDeploymentPolicyRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
ctx := context.WithValue(context.Background(), ctxId, d.Id())

owner := meta.(*Owner).name
repoName, envName, branchPolicyIdString, err := parseThreePartID(d.Id(), "repository", "environment", "branchPolicyId")
if err != nil {
return err
}

branchPolicyId, err := strconv.ParseInt(branchPolicyIdString, 10, 64)
if err != nil {
return err
}

branchPolicy, _, err := client.Repositories.GetDeploymentBranchPolicy(ctx, owner, repoName, envName, branchPolicyId)
if err != nil {
if ghErr, ok := err.(*github.ErrorResponse); ok {
if ghErr.Response.StatusCode == http.StatusNotModified {
return nil
}
if ghErr.Response.StatusCode == http.StatusNotFound {
log.Printf("[INFO] Removing branch deployment policy for %s/%s/%s from state because it no longer exists in GitHub",
owner, repoName, envName)
d.SetId("")
return nil
}
}
return err
}

d.Set("branch_pattern", branchPolicy.GetName())
return nil
}

func resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
ctx := context.Background()

owner := meta.(*Owner).name
repoName := d.Get("repository").(string)
envName := d.Get("environment").(string)
branchPattern := d.Get("branch_pattern").(string)
escapedEnvName := url.PathEscape(envName)
_, _, branchPolicyIdString, err := parseThreePartID(d.Id(), "repository", "environment", "branchPolicyId")
if err != nil {
return err
}

branchPolicyId, err := strconv.ParseInt(branchPolicyIdString, 10, 64)
if err != nil {
return err
}

updateData := github.DeploymentBranchPolicyRequest{
Name: github.String(branchPattern),
}

resultKey, _, err := client.Repositories.UpdateDeploymentBranchPolicy(ctx, owner, repoName, escapedEnvName, branchPolicyId, &updateData)
if err != nil {
return err
}
d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(resultKey.GetID(), 10)))
return resourceGithubRepositoryEnvironmentDeploymentPolicyRead(d, meta)
}

func resourceGithubRepositoryEnvironmentDeploymentPolicyDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
ctx := context.Background()

owner := meta.(*Owner).name
repoName, envName, branchPolicyIdString, err := parseThreePartID(d.Id(), "repository", "environment", "branchPolicyId")
if err != nil {
return err
}

branchPolicyId, err := strconv.ParseInt(branchPolicyIdString, 10, 64)
if err != nil {
return err
}

_, err = client.Repositories.DeleteDeploymentBranchPolicy(ctx, owner, repoName, envName, branchPolicyId)
if err != nil {
return err
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package github

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

func TestAccGithubRepositoryEnvironmentDeploymentPolicy(t *testing.T) {

randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)

t.Run("creates a repository environment with deployment policy", func(t *testing.T) {

config := fmt.Sprintf(`

data "github_user" "current" {
username = ""
}

resource "github_repository" "test" {
name = "tf-acc-test-%s"
}

resource "github_repository_environment" "test" {
repository = github_repository.test.name
environment = "environment/test"
wait_timer = 10000
reviewers {
users = [data.github_user.current.id]
}
deployment_branch_policy {
protected_branches = false
custom_branch_policies = true
}
}

resource "github_repository_environment_deployment_policy" "test" {
repository = github_repository.test.name
environment = github_repository_environment.test.environment
branch_pattern = "releases/*"
}

`, randomID)

check := resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_repository_environment_deployment_policy.test", "repository",
fmt.Sprintf("tf-acc-test-%s", randomID),
),
resource.TestCheckResourceAttr(
"github_repository_environment_deployment_policy.test", "environment",
"environment/test",
),
resource.TestCheckResourceAttr(
"github_repository_environment_deployment_policy.test", "branch_pattern",
"releases/*",
),
)

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) {
testCase(t, individual)
})

t.Run("with an organization account", func(t *testing.T) {
testCase(t, organization)
})

})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
layout: "github"
page_title: "GitHub: github_repository_environment_deployment_policy"
description: |-
Creates and manages environment deployment branch policies for GitHub repositories
---

# github_repository_environment_deployment_policy

This resource allows you to create and manage environment deployment branch policies for a GitHub repository.

## Example Usage

```hcl
data "github_user" "current" {
username = ""
}

resource "github_repository" "test" {
name = "tf-acc-test-%s"
}

resource "github_repository_environment" "test" {
repository = github_repository.test.name
environment = "environment/test"
wait_timer = 10000
reviewers {
users = [data.github_user.current.id]
}
deployment_branch_policy {
protected_branches = false
custom_branch_policies = true
}
}

resource "github_repository_environment_deployment_policy" "test" {
repository = github_repository.test.name
environment = github_repository_environment.test.environment
branch_pattern = "releases/*"
}
```

## Argument Reference

The following arguments are supported:

* `environment` - (Required) The name of the environment.

* `repository` - (Required) The repository of the environment.

* `branch_pattern` - (Required) The name pattern that branches must match in order to deploy to the environment.


## Import

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.

```
$ terraform import github_repository_environment.daily terraform:daily:123456
```
5 changes: 4 additions & 1 deletion website/github.erb
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,14 @@
<li>
<a href="/docs/providers/github/r/repository_environment.html">github_repository_environment</a>
</li>
<li>
<a href="/docs/providers/github/r/repository_environment_deployment_policy.html">github_repository_environment_deployment_policy</a>
</li>
<li>
<a href="/docs/providers/github/r/repository_environment_secret.html">github_repository_environment_secret</a>
</li>
<li>
<a href="/docs/providers/github/r/repository_environment_variable.html">github_repository_environment_variable</a>
<a href="/docs/providers/github/r/repository_environment_variable.html">github_repository_environment_variable</a>
</li>
<li>
<a href="/docs/providers/github/r/repository_file.html">github_repository_file</a>
Expand Down