diff --git a/github/resource_github_repository_environment_deployment_policy.go b/github/resource_github_repository_environment_deployment_policy.go index 0e408766c7..918583e13b 100644 --- a/github/resource_github_repository_environment_deployment_policy.go +++ b/github/resource_github_repository_environment_deployment_policy.go @@ -3,13 +3,13 @@ package github import ( "context" "errors" - "log" "net/http" "net/url" "strconv" "github.com/google/go-github/v81/github" "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -21,7 +21,7 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicy() *schema.Resource { UpdateContext: resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate, DeleteContext: resourceGithubRepositoryEnvironmentDeploymentPolicyDelete, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: resourceGithubRepositoryEnvironmentDeploymentPolicyImport, }, Schema: map[string]*schema.Schema{ "repository": { @@ -78,30 +78,56 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyCreate(ctx context.Conte escapedEnvName := url.PathEscape(envName) var createData github.DeploymentBranchPolicyRequest + var patternType string if v, ok := d.GetOk("branch_pattern"); ok { + patternType = "branch" createData = github.DeploymentBranchPolicyRequest{ Name: github.Ptr(v.(string)), Type: github.Ptr("branch"), } } else if v, ok := d.GetOk("tag_pattern"); ok { + patternType = "tag" createData = github.DeploymentBranchPolicyRequest{ Name: github.Ptr(v.(string)), Type: github.Ptr("tag"), } - } else { - return diag.Errorf("only one of 'branch_pattern' or 'tag_pattern' must be specified") } + tflog.Debug(ctx, "Creating repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "pattern_type": patternType, + }) + resultKey, _, err := client.Repositories.CreateDeploymentBranchPolicy(ctx, owner, repoName, escapedEnvName, &createData) if err != nil { + tflog.Error(ctx, "Failed to create repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "error": err.Error(), + }) return diag.FromErr(err) } - d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(resultKey.GetID(), 10))) + policyID := resultKey.GetID() + d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(policyID, 10))) + + tflog.Info(ctx, "Created repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": policyID, + }) + return nil } func resourceGithubRepositoryEnvironmentDeploymentPolicyRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + tflog.Debug(ctx, "Reading repository environment deployment policy", map[string]any{ + "id": d.Id(), + }) client := meta.(*Owner).v3client owner := meta.(*Owner).name @@ -120,23 +146,44 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyRead(ctx context.Context var ghErr *github.ErrorResponse if errors.As(err, &ghErr) { if ghErr.Response.StatusCode == http.StatusNotModified { + tflog.Debug(ctx, "API responded with StatusNotModified, not refreshing state") 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) + tflog.Info(ctx, "Removing branch deployment policy from state because it no longer exists in GitHub", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + }) d.SetId("") return nil } } + tflog.Error(ctx, "Failed to read repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": branchPolicyId, + "error": err.Error(), + }) return diag.FromErr(err) } - if branchPolicy.GetType() == "branch" { + patternType := branchPolicy.GetType() + if patternType == "branch" { _ = d.Set("branch_pattern", branchPolicy.GetName()) } else { _ = d.Set("tag_pattern", branchPolicy.GetName()) } + + tflog.Debug(ctx, "Successfully read repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": branchPolicyId, + "pattern_type": patternType, + }) + return nil } @@ -159,6 +206,13 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate(ctx context.Conte return diag.FromErr(err) } + tflog.Debug(ctx, "Updating repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": branchPolicyId, + }) + pattern := branchPattern if branchPattern == "" { pattern = tagPattern @@ -170,9 +224,26 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyUpdate(ctx context.Conte resultKey, _, err := client.Repositories.UpdateDeploymentBranchPolicy(ctx, owner, repoName, escapedEnvName, branchPolicyId, &updateData) if err != nil { + tflog.Error(ctx, "Failed to update repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": branchPolicyId, + "error": err.Error(), + }) return diag.FromErr(err) } - d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(resultKey.GetID(), 10))) + + policyID := resultKey.GetID() + d.SetId(buildThreePartID(repoName, escapedEnvName, strconv.FormatInt(policyID, 10))) + + tflog.Info(ctx, "Updated repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": policyID, + }) + return nil } @@ -190,11 +261,32 @@ func resourceGithubRepositoryEnvironmentDeploymentPolicyDelete(ctx context.Conte return diag.FromErr(err) } + tflog.Debug(ctx, "Deleting repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": branchPolicyId, + }) + _, err = client.Repositories.DeleteDeploymentBranchPolicy(ctx, owner, repoName, envName, branchPolicyId) if err != nil { + tflog.Error(ctx, "Failed to delete repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": branchPolicyId, + "error": err.Error(), + }) return diag.FromErr(err) } + tflog.Info(ctx, "Deleted repository environment deployment policy", map[string]any{ + "owner": owner, + "repository": repoName, + "environment": envName, + "policy_id": branchPolicyId, + }) + return nil } @@ -210,3 +302,44 @@ func customDeploymentPolicyDiffFunction(_ context.Context, diff *schema.Resource return nil } + +func resourceGithubRepositoryEnvironmentDeploymentPolicyImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + tflog.Debug(ctx, "Importing repository environment deployment policy", map[string]any{ + "id": d.Id(), + }) + + repoName, envName, policyId, err := parseThreePartID(d.Id(), "repository", "environment", "branchPolicyId") + if err != nil { + tflog.Error(ctx, "Failed to parse import ID", map[string]any{ + "id": d.Id(), + "error": err.Error(), + }) + return nil, err + } + + if err := d.Set("repository", repoName); err != nil { + return nil, err + } + + envNameUnescaped, err := url.PathUnescape(envName) + if err != nil { + tflog.Error(ctx, "Failed to unescape environment name", map[string]any{ + "id": d.Id(), + "environment": envName, + "error": err.Error(), + }) + return nil, err + } + + if err := d.Set("environment", envNameUnescaped); err != nil { + return nil, err + } + + tflog.Info(ctx, "Imported repository environment deployment policy", map[string]any{ + "repository": repoName, + "environment": envNameUnescaped, + "policy_id": policyId, + }) + + return []*schema.ResourceData{d}, nil +} diff --git a/github/resource_github_repository_environment_deployment_policy_test.go b/github/resource_github_repository_environment_deployment_policy_test.go index d3cc2a1592..1ca97f9f92 100644 --- a/github/resource_github_repository_environment_deployment_policy_test.go +++ b/github/resource_github_repository_environment_deployment_policy_test.go @@ -194,6 +194,75 @@ func TestAccGithubRepositoryEnvironmentDeploymentPolicyBranchUpdate(t *testing.T }) } +func TestAccGithubRepositoryEnvironmentDeploymentPolicyBranch_import(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + repoName := fmt.Sprintf("%senv-deploy-%s", testResourcePrefix, randomID) + + config := fmt.Sprintf(` + + data "github_user" "current" { + username = "" + } + + resource "github_repository" "test" { + name = "%s" + ignore_vulnerability_alerts_during_read = true + } + + 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 = "main" + } + + `, repoName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnauthenticated(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_repository_environment_deployment_policy.test", "repository", + repoName, + ), + resource.TestCheckResourceAttr( + "github_repository_environment_deployment_policy.test", "environment", + "environment/test", + ), + resource.TestCheckResourceAttr( + "github_repository_environment_deployment_policy.test", "branch_pattern", + "main", + ), + resource.TestCheckNoResourceAttr( + "github_repository_environment_deployment_policy.test", "tag_pattern", + ), + ), + }, + { + ResourceName: "github_repository_environment_deployment_policy.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccGithubRepositoryEnvironmentDeploymentPolicyTag(t *testing.T) { t.Run("creates a repository environment with tag-based deployment policy", func(t *testing.T) { randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)