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

feat: Enable custom protection rule on GH environment #2352

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ func Provider() *schema.Provider {
"github_repository_deployment_branch_policy": resourceGithubRepositoryDeploymentBranchPolicy(),
"github_repository_environment": resourceGithubRepositoryEnvironment(),
"github_repository_environment_deployment_policy": resourceGithubRepositoryEnvironmentDeploymentPolicy(),
"github_repository_environment_custom_protection_rule": resourceGithubRepositoryEnvironmentDeploymentCustomProtectionRule(),
"github_repository_file": resourceGithubRepositoryFile(),
"github_repository_milestone": resourceGithubRepositoryMilestone(),
"github_repository_project": resourceGithubRepositoryProject(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package github

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

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

func resourceGithubRepositoryEnvironmentDeploymentCustomProtectionRule() *schema.Resource {
return &schema.Resource{
Create: resourceGithubRepositoryEnvironmentDeploymentCustomProtectionRuleCreate,
Read: resourceGithubRepositoryEnvironmentDeploymentCustomProtectionRuleRead,
Delete: resourceGithubRepositoryEnvironmentDeploymentCustomProtectionRuleDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
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.",
},
"integration_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "The ID of the custom app that will be enabled on the environment.",
},
},
}

}

func resourceGithubRepositoryEnvironmentDeploymentCustomProtectionRuleCreate(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)
integrationID := d.Get("integration_id").(int)
escapedEnvName := url.PathEscape(envName)

createData := github.CustomDeploymentProtectionRuleRequest{
IntegrationID: github.Int64(int64(integrationID)),
}

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

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

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

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

protectionRuleId, err := strconv.ParseInt(protectionRuleIdString, 10, 64)
if err != nil {
return err
}
protectionRule, _, err := client.Repositories.GetCustomDeploymentProtectionRule(ctx, owner, repoName, envName, protectionRuleId)
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 custom protection for %s/%s/%s from state because it no longer exists in GitHub",
owner, repoName, envName)
d.SetId("")
return nil
}
}
return err
}
log.Printf("[INFO] Custom protection rule with node_id %s is enabled", *protectionRule.NodeID)
return nil
}

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

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

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

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

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

import (
"fmt"
"os"
"testing"

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

func TestAccGithubRepositoryEnvironmentDeploymentCustomProtectionRule(t *testing.T) {

const APP_INTEGRATION_ID = "APP_INTEGRATION_ID"
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
integration_id, exists := os.LookupEnv(APP_INTEGRATION_ID)

t.Run("creates a repository environment with custom policy enabled", func(t *testing.T) {
if !exists {
t.Skipf("%s environment variable is missing", APP_INTEGRATION_ID)
}
config := fmt.Sprintf(`

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

resource "github_repository_environment" "test" {
repository = github_repository.test.name
environment = "environment / test"
}

resource "github_repository_environment_custom_protection_rule" "test" {
repository = github_repository.test.name
environment = github_repository_environment.test.environment
integration_id = %s
}

`, randomID, integration_id)

check := resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_repository_environment_custom_protection_rule.test", "repository",
fmt.Sprintf("tf-acc-test-%s", randomID),
),
resource.TestCheckResourceAttr(
"github_repository_environment_custom_protection_rule.test", "environment",
"environment / test",
),
resource.TestCheckResourceAttr(
"github_repository_environment_custom_protection_rule.test", "integration_id",
integration_id,
),
)

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,49 @@
---
layout: "github"
page_title: "GitHub: github_repository_environment_custom_protection_rule"
description: |-
Creates and manages environment deployment custom protection rules for GitHub repositories
---

# github_repository_environment_custom_protection_rule

This resource allows you to create and manage environment deployment custom protection rules for a GitHub repository.

## Example Usage

```hcl

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

resource "github_repository_environment" "test" {
repository = github_repository.test.name
environment = "environment/test"
}

resource "github_repository_environment_custom_protection_rule" "test" {
repository = github_repository.test.name
environment = github_repository_environment.test.environment
integration_id = 123456
}
```

## Argument Reference

The following arguments are supported:

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

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

* `integration_id` - (Required) The ID of the custom app that will be enabled on 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 protection rule, separated by a `:` character, e.g.

```
$ terraform import github_repository_environment_deployment_policy.daily terraform:daily:123456
```
3 changes: 3 additions & 0 deletions website/github.erb
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@
<li>
<a href="/docs/providers/github/r/repository_environment.html">github_repository_environment</a>
</li>
<li>
<a href="/docs/providers/github/r/website/docs/r/repository_environment_custom_protection_rule.html">github_repository_environment_custom_protection_rule</a>
</li>
<li>
<a href="/docs/providers/github/r/repository_environment_deployment_policy.html">github_repository_environment_deployment_policy</a>
</li>
Expand Down