diff --git a/github/data_source_github_organization_repository_role.go b/github/data_source_github_organization_repository_role.go index 66ef09aef5..453faf2a67 100644 --- a/github/data_source_github_organization_repository_role.go +++ b/github/data_source_github_organization_repository_role.go @@ -2,10 +2,8 @@ package github import ( "context" - "fmt" "strconv" - "github.com/google/go-github/v82/github" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -53,28 +51,11 @@ func dataSourceGithubOrganizationRepositoryRoleRead(ctx context.Context, d *sche roleId := int64(d.Get("role_id").(int)) - // TODO: Use this code when go-github is at v68+ - // role, _, err := client.Organizations.GetCustomRepoRole(ctx, orgName, roleId) - // if err != nil { - // return diag.FromErr(err) - // } - - roles, _, err := client.Organizations.ListCustomRepoRoles(ctx, orgName) + role, _, err := client.Organizations.GetCustomRepoRole(ctx, orgName, roleId) if err != nil { return diag.FromErr(err) } - var role *github.CustomRepoRoles - for _, r := range roles.CustomRepoRoles { - if r.GetID() == roleId { - role = r - break - } - } - if role == nil { - return diag.FromErr(fmt.Errorf("custom organization repo role with ID %d not found", roleId)) - } - r := map[string]any{ "role_id": role.GetID(), "name": role.GetName(), diff --git a/github/data_source_github_organization_repository_role_test.go b/github/data_source_github_organization_repository_role_test.go index 3367d51356..3ab91f7aab 100644 --- a/github/data_source_github_organization_repository_role_test.go +++ b/github/data_source_github_organization_repository_role_test.go @@ -11,7 +11,7 @@ import ( func TestAccGithubOrganizationRepositoryRoleDataSource(t *testing.T) { t.Run("queries an organization repository role", func(t *testing.T) { randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) - roleName := fmt.Sprintf(`tf-acc-test-%s`, randomID) + roleName := fmt.Sprintf(`%s%s`, testResourcePrefix, randomID) config := fmt.Sprintf(` resource "github_organization_repository_role" "test" { diff --git a/github/resource_github_organization_repository_role.go b/github/resource_github_organization_repository_role.go index b7bf49ab67..1359e66333 100644 --- a/github/resource_github_organization_repository_role.go +++ b/github/resource_github_organization_repository_role.go @@ -2,11 +2,14 @@ package github import ( "context" + "errors" "fmt" - "log" + "net/http" + "slices" "strconv" "github.com/google/go-github/v82/github" + "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -53,6 +56,8 @@ func resourceGithubOrganizationRepositoryRole() *schema.Resource { MinItems: 1, }, }, + + CustomizeDiff: resourceGithubOrganizationRepositoryRoleCustomizeDiff, } } @@ -81,6 +86,10 @@ func resourceGithubOrganizationRepositoryRoleCreate(ctx context.Context, d *sche return diag.FromErr(fmt.Errorf("error creating GitHub organization repository role (%s/%s): %w", orgName, d.Get("name").(string), err)) } + if err = d.Set("role_id", role.GetID()); err != nil { + return diag.FromErr(err) + } + d.SetId(fmt.Sprint(role.GetID())) return nil } @@ -99,36 +108,20 @@ func resourceGithubOrganizationRepositoryRoleRead(ctx context.Context, d *schema return diag.FromErr(err) } - // TODO: Use this code when go-github is v68+ - // role, _, err := client.Organizations.GetCustomRepoRole(ctx, orgName, roleId) - // if err != nil { - // if ghErr, ok := err.(*github.ErrorResponse); ok { - // if ghErr.Response.StatusCode == http.StatusNotFound { - // log.Printf("[WARN] GitHub organization repository role (%s/%d) not found, removing from state", orgName, roleId) - // d.SetId("") - // return nil - // } - // } - // return err - // } - - roles, _, err := client.Organizations.ListCustomRepoRoles(ctx, orgName) + role, _, err := client.Organizations.GetCustomRepoRole(ctx, orgName, roleId) if err != nil { - return diag.FromErr(err) - } - - var role *github.CustomRepoRoles - for _, r := range roles.CustomRepoRoles { - if r.GetID() == roleId { - role = r - break + var ghErr *github.ErrorResponse + if errors.As(err, &ghErr) { + if ghErr.Response.StatusCode == http.StatusNotFound { + tflog.Warn(ctx, "GitHub organization repository role not found, removing from state", map[string]any{ + "orgName": orgName, + "roleId": roleId, + }) + d.SetId("") + return nil + } } - } - - if role == nil { - log.Printf("[WARN] GitHub organization repository role (%s/%d) not found, removing from state", orgName, roleId) - d.SetId("") - return nil + return diag.FromErr(err) } if err = d.Set("role_id", role.GetID()); err != nil { @@ -206,3 +199,75 @@ func resourceGithubOrganizationRepositoryRoleDelete(ctx context.Context, d *sche return nil } + +// Snapshot of the response to https://docs.github.com/en/enterprise-cloud@latest/rest/orgs/custom-roles?apiVersion=2022-11-28#list-repository-fine-grained-permissions-for-an-organization +// The endpoint isn't covered in the SDK yet. +var validRolePermissions = []string{ + "add_assignee", + "add_label", + "bypass_branch_protection", + "close_discussion", + "close_issue", + "close_pull_request", + "convert_issues_to_discussions", + "create_discussion_category", + "create_solo_merge_queue_entry", + "create_tag", + "delete_alerts_code_scanning", + "delete_discussion", + "delete_discussion_comment", + "delete_issue", + "delete_tag", + "edit_category_on_discussion", + "edit_discussion_category", + "edit_discussion_comment", + "edit_repo_custom_properties_values", + "edit_repo_metadata", + "edit_repo_protections", + "jump_merge_queue", + "manage_deploy_keys", + "manage_settings_merge_types", + "manage_settings_pages", + "manage_settings_projects", + "manage_settings_wiki", + "manage_webhooks", + "mark_as_duplicate", + "push_protected_branch", + "read_code_quality", + "read_code_scanning", + "reopen_discussion", + "reopen_issue", + "reopen_pull_request", + "request_pr_review", + "resolve_dependabot_alerts", + "resolve_secret_scanning_alerts", + "set_interaction_limits", + "set_issue_type", + "set_milestone", + "set_social_preview", + "toggle_discussion_answer", + "toggle_discussion_comment_minimize", + "view_dependabot_alerts", + "view_secret_scanning_alerts", + "write_code_quality", + "write_code_scanning", + "write_repository_actions_environments", + "write_repository_actions_runners", + "write_repository_actions_secrets", + "write_repository_actions_settings", + "write_repository_actions_variables", +} + +func resourceGithubOrganizationRepositoryRoleCustomizeDiff(ctx context.Context, d *schema.ResourceDiff, m any) error { + tflog.Debug(ctx, "Customizing diff for GitHub organization repository role", map[string]any{"permissionsChanged": d.HasChange("permissions")}) + if d.HasChange("permissions") { + newPermissions := d.Get("permissions").(*schema.Set).List() + tflog.Debug(ctx, "Validating permissions values", map[string]any{"newPermissions": newPermissions}) + for _, permission := range newPermissions { + if !slices.Contains(validRolePermissions, permission.(string)) { + return fmt.Errorf("invalid permission: %+v", permission) + } + } + } + return nil +} diff --git a/github/resource_github_organization_repository_role_test.go b/github/resource_github_organization_repository_role_test.go index 72116c55a3..22e3bc1ad9 100644 --- a/github/resource_github_organization_repository_role_test.go +++ b/github/resource_github_organization_repository_role_test.go @@ -13,7 +13,7 @@ func TestAccGithubOrganizationRepositoryRole(t *testing.T) { randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) name := fmt.Sprintf("tf-acc-org-repo-role-%s", randomID) description := "This is a test org repo role." - baseRole := "write" + baseRole := "read" permission0 := "reopen_issue" permission1 := "reopen_pull_request" @@ -59,6 +59,7 @@ func TestAccGithubOrganizationRepositoryRole(t *testing.T) { config := fmt.Sprintf(` resource "github_organization_repository_role" "test" { name = "%s" + base_role = "read" permissions = [ "%s" ]