diff --git a/github/provider.go b/github/provider.go index a0c3080674..e099bdb51a 100644 --- a/github/provider.go +++ b/github/provider.go @@ -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(), diff --git a/github/resource_github_repository_environment_deployment_policy.go b/github/resource_github_repository_environment_deployment_policy.go new file mode 100644 index 0000000000..b7b110a0a8 --- /dev/null +++ b/github/resource_github_repository_environment_deployment_policy.go @@ -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 +} diff --git a/github/resource_github_repository_environment_deployment_policy_test.go b/github/resource_github_repository_environment_deployment_policy_test.go new file mode 100644 index 0000000000..69fdd1a349 --- /dev/null +++ b/github/resource_github_repository_environment_deployment_policy_test.go @@ -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) + }) + + }) +} diff --git a/website/docs/r/repository_environment_deployment_policy.html.markdown b/website/docs/r/repository_environment_deployment_policy.html.markdown new file mode 100644 index 0000000000..df6739e4cb --- /dev/null +++ b/website/docs/r/repository_environment_deployment_policy.html.markdown @@ -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 +``` diff --git a/website/github.erb b/website/github.erb index c8f0288a2e..e7ed9a49c5 100644 --- a/website/github.erb +++ b/website/github.erb @@ -307,11 +307,14 @@