diff --git a/docs/resources/organization_ruleset.md b/docs/resources/organization_ruleset.md index b97af0fc27..89d97c5f7e 100644 --- a/docs/resources/organization_ruleset.md +++ b/docs/resources/organization_ruleset.md @@ -1,5 +1,6 @@ --- page_title: "github_organization_ruleset (Resource) - GitHub" +subcategory: "" description: |- Creates a GitHub organization ruleset. --- @@ -63,7 +64,9 @@ resource "github_organization_ruleset" "example" { } } } +``` +```terraform # Example with push ruleset # Note: Push targets must NOT have ref_name in conditions, only repository_name or repository_id resource "github_organization_ruleset" "example_push" { @@ -100,276 +103,421 @@ resource "github_organization_ruleset" "example_push" { } ``` -## Argument Reference +```terraform +# Example with repository ruleset +# Note: Repository targets must NOT have ref_name in conditions, only repository_name or repository_id +resource "github_organization_ruleset" "example_repository" { + name = "example_repository" + target = "repository" + enforcement = "active" -- `enforcement` - (Required) (String) Possible values for Enforcement are `disabled`, `active`, `evaluate`. Note: `evaluate` is currently only supported for owners of type `organization`. + conditions { + repository_name { + include = ["~ALL"] + exclude = [] + } + } -- `name` - (Required) (String) The name of the ruleset. + rules { + # Repository targets only support these rules: + # repository_create, repository_delete, repository_name, repository_transfer, repository_visibility + repository_create = true + repository_delete = true + repository_transfer = true -- `rules` - (Required) (Block List, Min: 1, Max: 1) Rules within the ruleset. (see [below for nested schema](#rules)) + repository_name { + pattern = "^team-" + negate = false + } -- `target` - (Required) (String) Possible values are `branch`, `tag` and `push`. + repository_visibility { + internal = true + private = true + } + } +} +``` + +~> **Note:** Rules are target-specific. `branch` and `tag` targets support rules such as `creation`, `deletion`, `pull_request` and `required_status_checks`. `push` targets only support `file_path_restriction`, `max_file_size`, `max_file_path_length` and `file_extension_restriction`. `repository` targets only support `repository_create`, `repository_delete`, `repository_name`, `repository_transfer` and `repository_visibility`. Using the wrong rules for a target results in a validation error. + +~> **Note:** at the time of writing this, the `RepositoryRole` bypass actor type corresponds to the following base repository roles and IDs: `maintain` -> `2`, `write` -> `4`, `admin` -> `5`. -- `bypass_actors` - (Optional) (Block List) The actors that can bypass the rules in this ruleset. (see [below for nested schema](#bypass_actors)) + +## Schema -- `conditions` - (Optional) (Block List, Max: 1) Parameters for an organization ruleset condition. For `branch` and `tag` targets, `ref_name` is required alongside one of `repository_name` or `repository_id`. For `push` targets, `ref_name` must NOT be set - only `repository_name` or `repository_id` should be used. (see [below for nested schema](#conditions)) +### Required -### Rules +- `enforcement` (String) The enforcement level of the ruleset. `evaluate` allows admins to test rules before enforcing them. Possible values are `disabled`, `active`, and `evaluate`. Note: `evaluate` is only available for Enterprise plans. +- `name` (String) The name of the ruleset. +- `rules` (Block List, Min: 1, Max: 1) Rules within the ruleset. (see [below for nested schema](#nestedblock--rules)) +- `target` (String) The target of the ruleset. Possible values are branch, tag, push and repository. -The `rules` block supports the following: +### Optional -~> **Note:** Rules are target-specific. `branch` and `tag` targets support rules like `creation`, `deletion`, `pull_request`, `required_status_checks`, etc. `push` targets only support `file_path_restriction`, `max_file_size`, `max_file_path_length`, and `file_extension_restriction`. Using the wrong rules for a target will result in a validation error. +- `bypass_actors` (Block List) The actors that can bypass the rules in this ruleset. (see [below for nested schema](#nestedblock--bypass_actors)) +- `conditions` (Block List, Max: 1) Parameters for an organization ruleset condition. Exactly one of `repository_name`, `repository_id` or `repository_property` must be set. For `branch` and `tag` targets, `ref_name` is required alongside it. For `push` and `repository` targets, `ref_name` must not be set. (see [below for nested schema](#nestedblock--conditions)) -- `branch_name_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the branch_name_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. Conflicts with `tag_name_pattern` as it only applies to rulesets with target `branch`. (see [below for nested schema](#rulesbranch_name_pattern)) +### Read-Only -- `commit_author_email_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the commit_author_email_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. (see [below for nested schema](#rulescommit_author_email_pattern)) +- `etag` (String) An etag representing the ruleset for caching purposes. +- `id` (String) The ID of this resource. +- `node_id` (String) GraphQL global node id for use with v4 API. +- `ruleset_id` (Number) GitHub ID for the ruleset. -- `commit_message_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the commit_message_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. (see [below for nested schema](#rulescommit_message_pattern)) + +### Nested Schema for `rules` -- `committer_email_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the committer_email_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. (see [below for nested schema](#rulescommitter_email_pattern)) +Optional: -- `creation` - (Optional) (Boolean) Only allow users with bypass permission to create matching refs. +- `branch_name_pattern` (Block List, Max: 1) Parameters to be used for the branch_name_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. Conflicts with `tag_name_pattern` as it only applies to rulesets with target `branch`. (see [below for nested schema](#nestedblock--rules--branch_name_pattern)) +- `commit_author_email_pattern` (Block List, Max: 1) Parameters to be used for the commit_author_email_pattern rule. (see [below for nested schema](#nestedblock--rules--commit_author_email_pattern)) +- `commit_message_pattern` (Block List, Max: 1) Parameters to be used for the commit_message_pattern rule. (see [below for nested schema](#nestedblock--rules--commit_message_pattern)) +- `committer_email_pattern` (Block List, Max: 1) Parameters to be used for the committer_email_pattern rule. (see [below for nested schema](#nestedblock--rules--committer_email_pattern)) +- `copilot_code_review` (Block List, Max: 1) Automatically request Copilot code review for new pull requests if the author has access to Copilot code review and their premium requests quota has not reached the limit. (see [below for nested schema](#nestedblock--rules--copilot_code_review)) +- `creation` (Boolean) Only allow users with bypass permission to create matching refs. +- `deletion` (Boolean) Only allow users with bypass permissions to delete matching refs. +- `file_extension_restriction` (Block List, Max: 1) Prevent pushes based on file extensions. Only valid for the `push` target. (see [below for nested schema](#nestedblock--rules--file_extension_restriction)) +- `file_path_restriction` (Block List, Max: 1) Prevent commits that include changes in specified file paths from being pushed to the commit graph. Only valid for the `push` target. (see [below for nested schema](#nestedblock--rules--file_path_restriction)) +- `max_file_path_length` (Block List, Max: 1) Prevent pushes based on file path length. Only valid for the `push` target. (see [below for nested schema](#nestedblock--rules--max_file_path_length)) +- `max_file_size` (Block List, Max: 1) Prevent pushes based on file size. Only valid for the `push` target. (see [below for nested schema](#nestedblock--rules--max_file_size)) +- `non_fast_forward` (Boolean) Prevent users with push access from force pushing to refs. +- `pull_request` (Block List, Max: 1) Require all commits be made to a non-target branch and submitted via a pull request before they can be merged. (see [below for nested schema](#nestedblock--rules--pull_request)) +- `repository_create` (Boolean) Only allow users with bypass permission to create matching repositories. Only valid for the `repository` target. +- `repository_delete` (Boolean) Only allow users with bypass permission to delete matching repositories. Only valid for the `repository` target. +- `repository_name` (Block List, Max: 1) Restrict the names matching repositories may have. Only valid for the `repository` target. (see [below for nested schema](#nestedblock--rules--repository_name)) +- `repository_transfer` (Boolean) Only allow users with bypass permission to transfer matching repositories out of the organization. Only valid for the `repository` target. +- `repository_visibility` (Block List, Max: 1) Restrict the visibilities matching repositories may have. Only valid for the `repository` target. (see [below for nested schema](#nestedblock--rules--repository_visibility)) +- `required_code_scanning` (Block List, Max: 1) Choose which tools must provide code scanning results before the reference is updated. When configured, code scanning must be enabled and have results for both the commit and the reference being updated. (see [below for nested schema](#nestedblock--rules--required_code_scanning)) +- `required_linear_history` (Boolean) Prevent merge commits from being pushed to matching branches. +- `required_signatures` (Boolean) Commits pushed to matching branches must have verified signatures. +- `required_status_checks` (Block List, Max: 1) Choose which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. (see [below for nested schema](#nestedblock--rules--required_status_checks)) +- `required_workflows` (Block List, Max: 1) Choose which Actions workflows must pass before branches can be merged into a branch that matches this rule. (see [below for nested schema](#nestedblock--rules--required_workflows)) +- `tag_name_pattern` (Block List, Max: 1) Parameters to be used for the tag_name_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. Conflicts with `branch_name_pattern` as it only applies to rulesets with target `tag`. (see [below for nested schema](#nestedblock--rules--tag_name_pattern)) +- `update` (Boolean) Only allow users with bypass permission to update matching refs. -- `deletion` - (Optional) (Boolean) Only allow users with bypass permissions to delete matching refs. + +### Nested Schema for `rules.branch_name_pattern` -- `non_fast_forward` - (Optional) (Boolean) Prevent users with push access from force pushing to branches. +Required: -- `pull_request` - (Optional) (Block List, Max: 1) Require all commits be made to a non-target branch and submitted via a pull request before they can be merged. (see [below for nested schema](#rulespull_request)) +- `operator` (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +- `pattern` (String) The pattern to match with. -- `copilot_code_review` - (Optional) (Block List, Max: 1) Automatically request Copilot code review for new pull requests if the author has access to Copilot code review and their premium requests quota has not reached the limit. (see [below for nested schema](#rulescopilot_code_review)) +Optional: -- `required_linear_history` - (Optional) (Boolean) Prevent merge commits from being pushed to matching branches. +- `name` (String) How this rule will appear to users. +- `negate` (Boolean) If true, the rule will fail if the pattern matches. -- `required_signatures` - (Optional) (Boolean) Commits pushed to matching branches must have verified signatures. -- `required_status_checks` - (Optional) (Block List, Max: 1) Choose which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. (see [below for nested schema](#rulesrequired_status_checks)) + +### Nested Schema for `rules.commit_author_email_pattern` -- `required_workflows` - (Optional) (Block List, Max: 1) Define which Actions workflows must pass before changes can be merged into a branch matching the rule. Multiple workflows can be specified. (see [below for nested schema](#rulesrequired_workflows)) +Required: -- `required_code_scanning` - (Optional) (Block List, Max: 1) Define which tools must provide code scanning results before the reference is updated. When configured, code scanning must be enabled and have results for both the commit and the reference being updated. Multiple code scanning tools can be specified. (see [below for nested schema](#rulesrequired_code_scanning)) +- `operator` (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +- `pattern` (String) The pattern to match with. -- `tag_name_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the tag_name_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. Conflicts with `branch_name_pattern` as it only applies to rulesets with target `tag`. (see [below for nested schema](#rulestag_name_pattern)) +Optional: -- `file_path_restriction` - (Optional) (Block List, Max: 1) Prevent commits that include changes to specified file paths from being pushed to the commit graph. This rule only applies to rulesets with target `push`. (see [below for nested schema](#rulesfile_path_restriction)) +- `name` (String) How this rule will appear to users. +- `negate` (Boolean) If true, the rule will fail if the pattern matches. -- `max_file_size` - (Optional) (Block List, Max: 1) Prevent commits that include files with a specified file size from being pushed to the commit graph. This rule only applies to rulesets with target `push`. (see [below for nested schema](#rulesmax_file_size)) -- `max_file_path_length` - (Optional) (Block List, Max: 1) Prevent commits that include file paths that exceed a specified character limit from being pushed to the commit graph. This rule only applies to rulesets with target `push`. (see [below for nested schema](#rulesmax_file_path_length)) + +### Nested Schema for `rules.commit_message_pattern` -- `file_extension_restriction` - (Optional) (Block List, Max: 1) Prevent commits that include files with specified file extensions from being pushed to the commit graph. This rule only applies to rulesets with target `push`. (see [below for nested schema](#rulesfile_extension_restriction)) +Required: -- `update` - (Optional) (Boolean) Only allow users with bypass permission to update matching refs. +- `operator` (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +- `pattern` (String) The pattern to match with. -#### rules.branch_name_pattern +Optional: -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +- `name` (String) How this rule will appear to users. +- `negate` (Boolean) If true, the rule will fail if the pattern matches. -- `pattern` - (Required) (String) The pattern to match with. -- `name` - (Optional) (String) How this rule will appear to users. + +### Nested Schema for `rules.committer_email_pattern` -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. +Required: -#### rules.commit_author_email_pattern +- `operator` (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +- `pattern` (String) The pattern to match with. -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +Optional: -- `pattern` - (Required) (String) The pattern to match with. +- `name` (String) How this rule will appear to users. +- `negate` (Boolean) If true, the rule will fail if the pattern matches. -- `name` - (Optional) (String) How this rule will appear to users. -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. + +### Nested Schema for `rules.copilot_code_review` -#### rules.commit_message_pattern +Optional: -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +- `review_draft_pull_requests` (Boolean) Copilot automatically reviews draft pull requests before they are marked as ready for review. Defaults to `false`. +- `review_on_push` (Boolean) Copilot automatically reviews each new push to the pull request. Defaults to `false`. -- `pattern` - (Required) (String) The pattern to match with. -- `name` - (Optional) (String) How this rule will appear to users. + +### Nested Schema for `rules.file_extension_restriction` -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. +Required: -#### rules.committer_email_pattern +- `restricted_file_extensions` (Set of String) The file extensions that are restricted from being pushed to the commit graph. -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. -- `pattern` - (Required) (String) The pattern to match with. + +### Nested Schema for `rules.file_path_restriction` -- `name` - (Optional) (String) How this rule will appear to users. +Required: -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. +- `restricted_file_paths` (List of String) The file paths that are restricted from being pushed to the commit graph. -#### rules.pull_request -- `allowed_merge_methods` - (Optional) (List of String, Min: 1) Array of merge methods to be allowed. Allowed values include `merge`, `squash`, and `rebase`. At least one must be enabled. + +### Nested Schema for `rules.max_file_path_length` -- `dismiss_stale_reviews_on_push` - (Optional) (Boolean) New, reviewable commits pushed will dismiss previous pull request review approvals. Defaults to `false`. +Required: -- `require_code_owner_review` - (Optional) (Boolean) Require an approving review in pull requests that modify files that have a designated code owner. Defaults to `false`. +- `max_file_path_length` (Number) The maximum allowed length of a file path. -- `require_last_push_approval` - (Optional) (Boolean) Whether the most recent reviewable push must be approved by someone other than the person who pushed it. Defaults to `false`. -- `required_approving_review_count` - (Optional) (Number) The number of approving reviews that are required before a pull request can be merged. Defaults to `0`. + +### Nested Schema for `rules.max_file_size` -- `required_review_thread_resolution` - (Optional) (Boolean) All conversations on code must be resolved before a pull request can be merged. Defaults to `false`. +Required: -#### rules.copilot_code_review +- `max_file_size` (Number) The maximum allowed size of a file in megabytes (MB). Valid range is 1-100 MB. -- `review_on_push` - (Optional) (Boolean) Copilot automatically reviews each new push to the pull request. Defaults to `false`. -- `review_draft_pull_requests` - (Optional) (Boolean) Copilot automatically reviews draft pull requests before they are marked as ready for review. Defaults to `false`. + +### Nested Schema for `rules.pull_request` -- `allowed_merge_methods` - (Required) (List of String, Min: 1) Array of merge methods to be allowed. Allowed values include `merge`, `squash`, and `rebase`. At least one must be enabled. +Optional: -- `required_reviewers` - (Optional) (Block List) Require specific reviewers to approve pull requests. Note: This feature is in beta. (see [below for nested schema](#rulespull_requestrequired_reviewers)) +- `allowed_merge_methods` (List of String) Array of allowed merge methods. Allowed values include `merge`, `squash`, and `rebase`. At least one option must be enabled. +- `dismiss_stale_reviews_on_push` (Boolean) New, reviewable commits pushed will dismiss previous pull request review approvals. Defaults to `false`. +- `require_code_owner_review` (Boolean) Require an approving review in pull requests that modify files that have a designated code owner. Defaults to `false`. +- `require_last_push_approval` (Boolean) Whether the most recent reviewable push must be approved by someone other than the person who pushed it. Defaults to `false`. +- `required_approving_review_count` (Number) The number of approving reviews that are required before a pull request can be merged. Defaults to `0`. +- `required_review_thread_resolution` (Boolean) All conversations on code must be resolved before a pull request can be merged. Defaults to `false`. +- `required_reviewers` (Block List) Require specific reviewers to approve pull requests targeting matching branches. Note: This feature is in beta and subject to change. (see [below for nested schema](#nestedblock--rules--pull_request--required_reviewers)) -#### rules.pull_request.required_reviewers + +### Nested Schema for `rules.pull_request.required_reviewers` -- `reviewer` - (Required) (Block List, Max: 1) The reviewer that must review matching files. (see [below for nested schema](#rulespull_requestrequired_reviewersreviewer)) +Required: -- `file_patterns` - (Required) (List of String) File patterns (fnmatch syntax) that this reviewer must approve. +- `file_patterns` (List of String) File patterns (fnmatch syntax) that this reviewer must approve. +- `minimum_approvals` (Number) Minimum number of approvals required from this reviewer. Set to 0 to make approval optional. +- `reviewer` (Block List, Min: 1, Max: 1) The reviewer that must review matching files. (see [below for nested schema](#nestedblock--rules--pull_request--required_reviewers--reviewer)) -- `minimum_approvals` - (Required) (Number) Minimum number of approvals required from this reviewer. Set to 0 to make approval optional. + +### Nested Schema for `rules.pull_request.required_reviewers.reviewer` -#### rules.pull_request.required_reviewers.reviewer +Required: -- `id` - (Required) (Number) The ID of the reviewer (Team ID). +- `id` (Number) The ID of the reviewer that must review. +- `type` (String) The type of reviewer. Currently only `Team` is supported. -- `type` - (Required) (String) The type of reviewer. Currently only `Team` is supported. -#### rules.required_status_checks -- `required_check` - (Required) (Block Set, Min: 1) Status checks that are required. Several can be defined. (see [below for nested schema](#rulesrequired_status_checksrequired_check)) -- `strict_required_status_checks_policy` - (Optional) (Boolean) Whether pull requests targeting a matching branch must be tested with the latest code. This setting will not take effect unless at least one status check is enabled. Defaults to `false`. + +### Nested Schema for `rules.repository_name` -- `do_not_enforce_on_create` - (Optional) (Boolean) Allow repositories and branches to be created if a check would otherwise prohibit it. Defaults to `false`. +Required: -#### rules.required_status_checks.required_check +- `pattern` (String) The pattern to match against the repository name. -- `context` - (Required) (String) The status check context name that must be present on the commit. +Optional: -- `integration_id` - (Optional) (Number) The optional integration ID that this status check must originate from. +- `negate` (Boolean) If true, the rule will fail if the pattern matches. -- `do_not_enforce_on_create` - (Optional) (Boolean) Allow repositories and branches to be created if a check would otherwise prohibit it. Defaults to `false`. -#### rules.required_workflows + +### Nested Schema for `rules.repository_visibility` -- `do_not_enforce_on_create` - (Optional) (Boolean) Allow repositories and branches to be created if a check would otherwise prohibit it. Defaults to `false`. +Optional: -- `required_workflow` - (Required) (Block Set, Min: 1) Actions workflows that are required. Multiple can be defined. (see [below for nested schema](#rulesrequired_workflowsrequired_workflow)) +- `internal` (Boolean) Allow matching repositories to be internal. +- `private` (Boolean) Allow matching repositories to be private. -#### rules.required_workflows.required_workflow -- `repository_id` - (Required) (Number) The ID of the repository. Names, full names and repository URLs are not supported. + +### Nested Schema for `rules.required_code_scanning` -- `path` - (Required) (String) The path to the YAML definition file of the workflow. +Required: -- `ref` - (Optional) (String) The optional ref from which to fetch the workflow. Defaults to `master`. +- `required_code_scanning_tool` (Block Set, Min: 1) Tools that must provide code scanning results for this rule to pass. (see [below for nested schema](#nestedblock--rules--required_code_scanning--required_code_scanning_tool)) -#### rules.required_code_scanning + +### Nested Schema for `rules.required_code_scanning.required_code_scanning_tool` -- `required_code_scanning_tool` - (Required) (Block Set, Min: 1) Actions code scanning tools that are required. Multiple can be defined. (see [below for nested schema](#rulesrequired_code_scanningrequired_code_scanning_tool)) +Required: -#### rules.required_code_scanning.required_code_scanning_tool +- `alerts_threshold` (String) The severity level at which code scanning results that raise alerts block a reference update. Can be one of: `none`, `errors`, `errors_and_warnings`, `all`. +- `security_alerts_threshold` (String) The severity level at which code scanning results that raise security alerts block a reference update. Can be one of: `none`, `critical`, `high_or_higher`, `medium_or_higher`, `all`. +- `tool` (String) The name of a code scanning tool. -- `alerts_threshold` - (Required) (String) The severity level at which code scanning results that raise alerts block a reference update. Can be one of: `none`, `errors`, `errors_and_warnings`, `all`. -- `security_alerts_threshold` - (Required) (String) The severity level at which code scanning results that raise security alerts block a reference update. Can be one of: `none`, `critical`, `high_or_higher`, `medium_or_higher`, `all`. -- `tool` - (Required) (String) The name of a code scanning tool. + +### Nested Schema for `rules.required_status_checks` -#### rules.tag_name_pattern +Required: -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +- `required_check` (Block Set, Min: 1) Status checks that are required. Several can be defined. (see [below for nested schema](#nestedblock--rules--required_status_checks--required_check)) -- `pattern` - (Required) (String) The pattern to match with. +Optional: -- `name` - (Optional) (String) How this rule will appear to users. +- `do_not_enforce_on_create` (Boolean) Allow repositories and branches to be created if a check would otherwise prohibit it. +- `strict_required_status_checks_policy` (Boolean) Whether pull requests targeting a matching branch must be tested with the latest code. This setting will not take effect unless at least one status check is enabled. Defaults to `false`. -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. + +### Nested Schema for `rules.required_status_checks.required_check` -#### rules.file_path_restriction +Required: -- `restricted_file_paths` - (Required) (Block Set, Min: 1) The file paths that are restricted from being pushed to the commit graph. +- `context` (String) The status check context name that must be present on the commit. -#### rules.max_file_size +Optional: -- `max_file_size` - (Required) (Integer) The maximum allowed size, in megabytes (MB), of a file. Valid range is 1-100 MB. +- `integration_id` (Number) The optional integration ID that this status check must originate from. -#### rules.max_file_path_length -- `max_file_path_length` - (Required) (Integer) The maximum number of characters allowed in file paths. -#### rules.file_extension_restriction + +### Nested Schema for `rules.required_workflows` -- `restricted_file_extensions` - (Required) (Block Set, Min: 1) The file extensions that are restricted from being pushed to the commit graph. +Required: -#### bypass_actors +- `required_workflow` (Block Set, Min: 1) Actions workflows that are required. Several can be defined. (see [below for nested schema](#nestedblock--rules--required_workflows--required_workflow)) -- `actor_id` - (Optional) (Number) The ID of the actor that can bypass a ruleset. Must be omitted for ID-less actor types: `OrganizationAdmin`, `EnterpriseOwner`, and `DeployKey` — the GitHub API does not use an ID for these types and will ignore any value set. +Optional: -- `actor_type` (String) The type of actor that can bypass a ruleset. Can be one of: `RepositoryRole`, `Team`, `Integration`, `OrganizationAdmin`, `DeployKey`, `EnterpriseOwner`. +- `do_not_enforce_on_create` (Boolean) Allow repositories and branches to be created if a check would otherwise prohibit it. -- `bypass_mode` - (Optional) (String) When the specified actor can bypass the ruleset. pull_request means that an actor can only bypass rules on pull requests. Can be one of: `always`, `pull_request`, `exempt`. + +### Nested Schema for `rules.required_workflows.required_workflow` -~>Note: at the time of writing this, the following actor types correspond to the following actor IDs: +Required: -- `RepositoryRole` (This is the actor type, the following are the base repository roles and their associated IDs.) - - `maintain` -> `2` - - `write` -> `4` - - `admin` -> `5` +- `path` (String) The path to the workflow YAML definition file. +- `repository_id` (Number) The repository in which the workflow is defined. -#### conditions +Optional: -- `ref_name` - (Optional) (Block List, Max: 1) Required for `branch` and `tag` targets. Must NOT be set for `push` targets. (see [below for nested schema](#conditionsref_name)) -- `repository_id` (Optional) (List of Number) The repository IDs that the ruleset applies to. One of these IDs must match for the condition to pass. -- `repository_name` (Optional) (Block List, Max: 1) Targets repositories that match the specified name patterns. (see [below for nested schema](#conditionsrepository_name)) -- `repository_property` (Optional) (Block List, Max: 1) Targets repositories by custom or system properties. (see [below for nested schema](#conditionsrepository_property)) +- `ref` (String) The ref (branch or tag) of the workflow file to use. -Exactly one of `repository_id`, `repository_name`, or `repository_property` must be set for the rule to target repositories. -~> **Note:** For `push` targets, do not include `ref_name` in conditions. Push rulesets operate on file content, not on refs. -#### conditions.ref_name + +### Nested Schema for `rules.tag_name_pattern` -- `exclude` - (Required) (List of String) Array of ref names or patterns to exclude. The condition will not pass if any of these patterns match. +Required: -- `include` - (Required) (List of String) Array of ref names or patterns to include. One of these patterns must match for the condition to pass. Also accepts `~DEFAULT_BRANCH` to include the default branch or `~ALL` to include all branches. +- `operator` (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. +- `pattern` (String) The pattern to match with. -#### conditions.repository_name +Optional: -- `exclude` - (Required) (List of String) Array of repository names or patterns to exclude. The condition will not pass if any of these patterns match. -- `include` - (Required) (List of String) Array of repository names or patterns to include. One of these patterns must match for the condition to pass. Also accepts `~ALL` to include all repositories. -- `protected` - (Optional) (Boolean) Whether renaming of target repositories is prevented. Defaults to `false`. +- `name` (String) How this rule will appear to users. +- `negate` (Boolean) If true, the rule will fail if the pattern matches. -#### conditions.repository_property -- `include` - (Optional) (List of Repository Properties) The repository properties and values to include. All of these properties must match for the condition to pass. (see [below for nested schema](#conditionsrepository_propertyproperties)) -- `exclude` - (Optional) (List of Repository Properties) The repository properties and values to exclude. The condition will not pass if any of these properties match. (see [below for nested schema](#conditionsrepository_propertyproperties)) + +### Nested Schema for `bypass_actors` -#### conditions.repository_property.properties +Required: -- `name` (Required) (String) The name of the repository property to target. +- `actor_type` (String) The type of actor that can bypass a ruleset. Can be one of: `Integration`, `OrganizationAdmin`, `RepositoryRole`, `Team`, `DeployKey`, or `EnterpriseOwner`. +- `bypass_mode` (String) When the specified actor can bypass the ruleset. pull_request means that an actor can only bypass rules on pull requests. Can be one of: `always`, `pull_request`, `exempt`. -- `property_values` (Required) (Array of String) The values to match for the repository property. +Optional: -- `source` (String) The source of the repository property. Defaults to 'custom' if not specified. Can be one of: `custom`, `system` +- `actor_id` (Number) The ID of the actor that can bypass a ruleset. Must be omitted for ID-less actor types: `OrganizationAdmin`, `EnterpriseOwner`, and `DeployKey` — the GitHub API does not use an ID for these types and will ignore any value set. -## Attributes Reference -The following additional attributes are exported: + +### Nested Schema for `conditions` -- `etag` (String) +Optional: -- `node_id` (String) GraphQL global node id for use with v4 API. +- `ref_name` (Block List, Max: 1) Targets refs that match the specified patterns. Required for `branch` and `tag` targets. (see [below for nested schema](#nestedblock--conditions--ref_name)) +- `repository_id` (List of Number) The repository IDs that the ruleset applies to. One of these IDs must match for the ruleset to apply. +- `repository_name` (Block List, Max: 1) Targets repositories that match the specified name patterns. (see [below for nested schema](#nestedblock--conditions--repository_name)) +- `repository_property` (Block List, Max: 1) Conditions to target repositories by custom or system properties. (see [below for nested schema](#nestedblock--conditions--repository_property)) -- `ruleset_id` (Number) GitHub ID for the ruleset. + +### Nested Schema for `conditions.ref_name` + +Required: + +- `exclude` (List of String) Array of ref names or patterns to exclude. The condition will not pass if any of these patterns match. +- `include` (List of String) Array of ref names or patterns to include. One of these patterns must match for the condition to pass. Also accepts `~DEFAULT_BRANCH` to include the default branch or `~ALL` to include all branches. + + + +### Nested Schema for `conditions.repository_name` + +Required: + +- `exclude` (List of String) Array of repository names or patterns to exclude. The condition will not pass if any of these patterns match. +- `include` (List of String) Array of repository names or patterns to include. One of these patterns must match for the condition to pass. Also accepts `~ALL` to include all repositories. + +Optional: + +- `protected` (Boolean) Whether renaming of target repositories is prevented. + + + +### Nested Schema for `conditions.repository_property` + +Optional: + +- `exclude` (List of Object) The repository properties and values to exclude. The ruleset will not apply if any of these properties match. (see [below for nested schema](#nestedatt--conditions--repository_property--exclude)) +- `include` (List of Object) The repository properties and values to include. All of these properties must match for the condition to pass. (see [below for nested schema](#nestedatt--conditions--repository_property--include)) + + +### Nested Schema for `conditions.repository_property.exclude` + +Optional: + +- `name` (String) +- `property_values` (List of String) +- `source` (String) + + + +### Nested Schema for `conditions.repository_property.include` + +Optional: + +- `name` (String) +- `property_values` (List of String) +- `source` (String) ## Import -GitHub Organization Rulesets can be imported using the GitHub ruleset ID e.g. +GitHub organization rulesets can be imported using the GitHub ruleset ID. + +Import is supported using the following syntax: + +In Terraform v1.5.0 and later, the [`import` block](https://developer.hashicorp.com/terraform/language/import) can be used with the `id` attribute, for example: -`$ terraform import github_organization_ruleset.example 12345` +```terraform +import { + to = github_organization_ruleset.example + id = "12345" +} +``` + +The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example: + +```shell +terraform import github_organization_ruleset.example 12345 +``` diff --git a/examples/resources/github_organization_ruleset/import-by-string-id.tf b/examples/resources/github_organization_ruleset/import-by-string-id.tf new file mode 100644 index 0000000000..d428e12a38 --- /dev/null +++ b/examples/resources/github_organization_ruleset/import-by-string-id.tf @@ -0,0 +1,4 @@ +import { + to = github_organization_ruleset.example + id = "12345" +} diff --git a/examples/resources/github_organization_ruleset/import.sh b/examples/resources/github_organization_ruleset/import.sh new file mode 100644 index 0000000000..467d21ddce --- /dev/null +++ b/examples/resources/github_organization_ruleset/import.sh @@ -0,0 +1 @@ +terraform import github_organization_ruleset.example 12345 diff --git a/examples/resources/organization_ruleset/example_1.tf b/examples/resources/github_organization_ruleset/resource_1.tf similarity index 56% rename from examples/resources/organization_ruleset/example_1.tf rename to examples/resources/github_organization_ruleset/resource_1.tf index 5a5ac17bdd..15f1286def 100644 --- a/examples/resources/organization_ruleset/example_1.tf +++ b/examples/resources/github_organization_ruleset/resource_1.tf @@ -48,38 +48,3 @@ resource "github_organization_ruleset" "example" { } } } - -# Example with push ruleset -# Note: Push targets must NOT have ref_name in conditions, only repository_name or repository_id -resource "github_organization_ruleset" "example_push" { - name = "example_push" - target = "push" - enforcement = "active" - - conditions { - repository_name { - include = ["~ALL"] - exclude = [] - } - } - - rules { - # Push targets only support these rules: - # file_path_restriction, max_file_size, max_file_path_length, file_extension_restriction - file_path_restriction { - restricted_file_paths = [".github/workflows/*", "*.env"] - } - - max_file_size { - max_file_size = 100 # 100 MB - } - - max_file_path_length { - max_file_path_length = 255 - } - - file_extension_restriction { - restricted_file_extensions = ["*.exe", "*.dll", "*.so"] - } - } -} diff --git a/examples/resources/github_organization_ruleset/resource_2.tf b/examples/resources/github_organization_ruleset/resource_2.tf new file mode 100644 index 0000000000..a0c91e947f --- /dev/null +++ b/examples/resources/github_organization_ruleset/resource_2.tf @@ -0,0 +1,34 @@ +# Example with push ruleset +# Note: Push targets must NOT have ref_name in conditions, only repository_name or repository_id +resource "github_organization_ruleset" "example_push" { + name = "example_push" + target = "push" + enforcement = "active" + + conditions { + repository_name { + include = ["~ALL"] + exclude = [] + } + } + + rules { + # Push targets only support these rules: + # file_path_restriction, max_file_size, max_file_path_length, file_extension_restriction + file_path_restriction { + restricted_file_paths = [".github/workflows/*", "*.env"] + } + + max_file_size { + max_file_size = 100 # 100 MB + } + + max_file_path_length { + max_file_path_length = 255 + } + + file_extension_restriction { + restricted_file_extensions = ["*.exe", "*.dll", "*.so"] + } + } +} diff --git a/examples/resources/github_organization_ruleset/resource_3.tf b/examples/resources/github_organization_ruleset/resource_3.tf new file mode 100644 index 0000000000..a9143d83e9 --- /dev/null +++ b/examples/resources/github_organization_ruleset/resource_3.tf @@ -0,0 +1,32 @@ +# Example with repository ruleset +# Note: Repository targets must NOT have ref_name in conditions, only repository_name or repository_id +resource "github_organization_ruleset" "example_repository" { + name = "example_repository" + target = "repository" + enforcement = "active" + + conditions { + repository_name { + include = ["~ALL"] + exclude = [] + } + } + + rules { + # Repository targets only support these rules: + # repository_create, repository_delete, repository_name, repository_transfer, repository_visibility + repository_create = true + repository_delete = true + repository_transfer = true + + repository_name { + pattern = "^team-" + negate = false + } + + repository_visibility { + internal = true + private = true + } + } +} diff --git a/github/resource_github_organization_ruleset.go b/github/resource_github_organization_ruleset.go index 3f8cd7bb6f..fcd9cea9d6 100644 --- a/github/resource_github_organization_ruleset.go +++ b/github/resource_github_organization_ruleset.go @@ -16,10 +16,16 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -var supportedOrgRulesetTargetTypes = []string{string(github.RulesetTargetBranch), string(github.RulesetTargetTag), string(github.RulesetTargetPush)} +var supportedOrgRulesetTargetTypes = []string{ + string(github.RulesetTargetBranch), + string(github.RulesetTargetTag), + string(github.RulesetTargetPush), + string(github.RulesetTargetRepository), +} func resourceGithubOrganizationRuleset() *schema.Resource { return &schema.Resource{ + Description: "Creates a GitHub organization ruleset.", CreateContext: resourceGithubOrganizationRulesetCreate, ReadContext: resourceGithubOrganizationRulesetRead, UpdateContext: resourceGithubOrganizationRulesetUpdate, @@ -40,9 +46,8 @@ func resourceGithubOrganizationRuleset() *schema.Resource { Description: "The name of the ruleset.", }, "target": { - Type: schema.TypeString, - Required: true, - // The API accepts an `repository` target, but we don't support it yet. + Type: schema.TypeString, + Required: true, ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(supportedOrgRulesetTargetTypes, false)), Description: "The target of the ruleset. Possible values are " + strings.Join(supportedOrgRulesetTargetTypes[:len(supportedOrgRulesetTargetTypes)-1], ", ") + " and " + supportedOrgRulesetTargetTypes[len(supportedOrgRulesetTargetTypes)-1] + ".", }, @@ -94,7 +99,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource { Type: schema.TypeList, Optional: true, MaxItems: 1, - Description: "Parameters for an organization ruleset condition.The branch and tag rulesets conditions object should contain both repository_name and ref_name properties, or both repository_id and ref_name properties, or both repository_property and ref_name properties. The push rulesets conditions object does not require the ref_name property.", + Description: "Parameters for an organization ruleset condition. Exactly one of `repository_name`, `repository_id` or `repository_property` must be set. For `branch` and `tag` targets, `ref_name` is required alongside it. For `push` and `repository` targets, `ref_name` must not be set.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "ref_name": { @@ -678,7 +683,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource { Type: schema.TypeList, Optional: true, MaxItems: 1, - Description: "Prevent commits that include changes in specified file paths from being pushed to the commit graph.", + Description: "Prevent commits that include changes in specified file paths from being pushed to the commit graph. Only valid for the `push` target.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "restricted_file_paths": { @@ -697,7 +702,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource { Type: schema.TypeList, Optional: true, MaxItems: 1, - Description: "Prevent pushes based on file size.", + Description: "Prevent pushes based on file size. Only valid for the `push` target.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "max_file_size": { @@ -713,7 +718,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource { Type: schema.TypeList, Optional: true, MaxItems: 1, - Description: "Prevent pushes based on file path length.", + Description: "Prevent pushes based on file path length. Only valid for the `push` target.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "max_file_path_length": { @@ -729,7 +734,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource { Type: schema.TypeList, Optional: true, MaxItems: 1, - Description: "Prevent pushes based on file extensions.", + Description: "Prevent pushes based on file extensions. Only valid for the `push` target.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "restricted_file_extensions": { @@ -744,6 +749,62 @@ func resourceGithubOrganizationRuleset() *schema.Resource { }, }, }, + "repository_create": { + Type: schema.TypeBool, + Optional: true, + Description: "Only allow users with bypass permission to create matching repositories. Only valid for the `repository` target.", + }, + "repository_delete": { + Type: schema.TypeBool, + Optional: true, + Description: "Only allow users with bypass permission to delete matching repositories. Only valid for the `repository` target.", + }, + "repository_transfer": { + Type: schema.TypeBool, + Optional: true, + Description: "Only allow users with bypass permission to transfer matching repositories out of the organization. Only valid for the `repository` target.", + }, + "repository_name": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Restrict the names matching repositories may have. Only valid for the `repository` target.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "pattern": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotEmpty), + Description: "The pattern to match against the repository name.", + }, + "negate": { + Type: schema.TypeBool, + Optional: true, + Description: "If true, the rule will fail if the pattern matches.", + }, + }, + }, + }, + "repository_visibility": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Restrict the visibilities matching repositories may have. Only valid for the `repository` target.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "internal": { + Type: schema.TypeBool, + Optional: true, + Description: "Allow matching repositories to be internal.", + }, + "private": { + Type: schema.TypeBool, + Optional: true, + Description: "Allow matching repositories to be private.", + }, + }, + }, + }, }, }, }, diff --git a/github/resource_github_organization_ruleset_test.go b/github/resource_github_organization_ruleset_test.go index 7d3ceef5af..57301427db 100644 --- a/github/resource_github_organization_ruleset_test.go +++ b/github/resource_github_organization_ruleset_test.go @@ -566,6 +566,72 @@ resource "github_organization_ruleset" "test" { }) }) + t.Run("create_repository_ruleset", func(t *testing.T) { + t.Parallel() + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + rulesetName := fmt.Sprintf("%s-repository-ruleset-%s", testResourcePrefix, randomID) + + config := fmt.Sprintf(` +resource "github_organization_ruleset" "test" { + name = "%s" + target = "repository" + enforcement = "active" + + conditions { + repository_name { + include = ["~ALL"] + exclude = [] + } + } + + rules { + repository_create = true + repository_delete = true + repository_transfer = true + + repository_name { + pattern = "^tf-acc-" + negate = false + } + + repository_visibility { + internal = true + private = true + } + } +} +`, rulesetName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessHasPaidOrgs(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("name"), knownvalue.StringExact(rulesetName)), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("target"), knownvalue.StringExact("repository")), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("enforcement"), knownvalue.StringExact("active")), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("repository_create"), knownvalue.Bool(true)), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("repository_delete"), knownvalue.Bool(true)), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("repository_transfer"), knownvalue.Bool(true)), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("repository_name").AtSliceIndex(0).AtMapKey("pattern"), knownvalue.StringExact("^tf-acc-")), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("repository_name").AtSliceIndex(0).AtMapKey("negate"), knownvalue.Bool(false)), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("repository_visibility").AtSliceIndex(0).AtMapKey("internal"), knownvalue.Bool(true)), + statecheck.ExpectKnownValue("github_organization_ruleset.test", tfjsonpath.New("rules").AtSliceIndex(0).AtMapKey("repository_visibility").AtSliceIndex(0).AtMapKey("private"), knownvalue.Bool(true)), + }, + }, + { + ResourceName: "github_organization_ruleset.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"etag"}, + }, + }, + }) + }) + t.Run("update_ruleset_name", func(t *testing.T) { t.Parallel() @@ -961,6 +1027,125 @@ resource "github_organization_ruleset" "test" { }) }) + t.Run("validates_repository_target_rejects_ref_name_condition", func(t *testing.T) { + t.Parallel() + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + resourceName := "test-repository-reject-ref-name" + config := fmt.Sprintf(` + resource "github_organization_ruleset" "%s" { + name = "test-repository-with-ref-%s" + target = "repository" + enforcement = "active" + + conditions { + ref_name { + include = ["~ALL"] + exclude = [] + } + repository_name { + include = ["~ALL"] + exclude = [] + } + } + + rules { + # Repository rulesets only support repository-specific rules + repository_delete = true + } + } + `, resourceName, randomID) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessHasPaidOrgs(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ExpectError: regexp.MustCompile("ref_name must not be set for repository target"), + }, + }, + }) + }) + + t.Run("validates_repository_target_rejects_branch_or_tag_rules", func(t *testing.T) { + t.Parallel() + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + resourceName := "test-repository-reject-branch-rules" + config := fmt.Sprintf(` + resource "github_organization_ruleset" "%s" { + name = "test-repository-branch-rule-%s" + target = "repository" + enforcement = "active" + + conditions { + repository_name { + include = ["~ALL"] + exclude = [] + } + } + + rules { + # 'creation' is a branch/tag rule, not valid for repository target + creation = true + } + } + `, resourceName, randomID) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessHasPaidOrgs(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ExpectError: regexp.MustCompile("rule .* is not valid for repository target"), + }, + }, + }) + }) + + t.Run("validates_branch_target_rejects_repository-only_rules", func(t *testing.T) { + t.Parallel() + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + resourceName := "test-branch-reject-repository-rules" + config := fmt.Sprintf(` + resource "github_organization_ruleset" "%s" { + name = "test-branch-repository-rule-%s" + target = "branch" + enforcement = "active" + + conditions { + ref_name { + include = ["~ALL"] + exclude = [] + } + repository_name { + include = ["~ALL"] + exclude = [] + } + } + + rules { + # 'repository_delete' is a repository-only rule, not valid for branch target + repository_delete = true + } + } + `, resourceName, randomID) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessHasPaidOrgs(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ExpectError: regexp.MustCompile("rule .* is not valid for branch target"), + }, + }, + }) + }) + t.Run("validates_branch_target_rejects_push-only_rules", func(t *testing.T) { t.Parallel() diff --git a/github/util_rules.go b/github/util_rules.go index eb8fdbe689..0bd9f461d3 100644 --- a/github/util_rules.go +++ b/github/util_rules.go @@ -655,6 +655,37 @@ func expandRules(input []any, org bool) *github.RepositoryRulesetRules { rulesetRules.CopilotCodeReview = params } + // Repository target rules (org-only) + if org { + if v, ok := rulesMap["repository_create"].(bool); ok && v { + rulesetRules.RepositoryCreate = &github.EmptyRuleParameters{} + } + + if v, ok := rulesMap["repository_delete"].(bool); ok && v { + rulesetRules.RepositoryDelete = &github.EmptyRuleParameters{} + } + + if v, ok := rulesMap["repository_transfer"].(bool); ok && v { + rulesetRules.RepositoryTransfer = &github.EmptyRuleParameters{} + } + + if v, ok := rulesMap["repository_name"].([]any); ok && len(v) != 0 { + repositoryNameMap := v[0].(map[string]any) + rulesetRules.RepositoryName = &github.SimplePatternRuleParameters{ + Negate: repositoryNameMap["negate"].(bool), + Pattern: repositoryNameMap["pattern"].(string), + } + } + + if v, ok := rulesMap["repository_visibility"].([]any); ok && len(v) != 0 { + repositoryVisibilityMap := v[0].(map[string]any) + rulesetRules.RepositoryVisibility = &github.RepositoryVisibilityRuleParameters{ + Internal: repositoryVisibilityMap["internal"].(bool), + Private: repositoryVisibilityMap["private"].(bool), + } + } + } + return rulesetRules } @@ -882,6 +913,31 @@ func flattenRules(ctx context.Context, rules *github.RepositoryRulesetRules, org rulesMap["copilot_code_review"] = copilotCodeReviewSlice } + // Repository target rules (org-only) + if org { + rulesMap["repository_create"] = rules.RepositoryCreate != nil + rulesMap["repository_delete"] = rules.RepositoryDelete != nil + rulesMap["repository_transfer"] = rules.RepositoryTransfer != nil + + if rules.RepositoryName != nil { + repositoryNameSlice := make([]map[string]any, 0) + repositoryNameSlice = append(repositoryNameSlice, map[string]any{ + "negate": rules.RepositoryName.Negate, + "pattern": rules.RepositoryName.Pattern, + }) + rulesMap["repository_name"] = repositoryNameSlice + } + + if rules.RepositoryVisibility != nil { + repositoryVisibilitySlice := make([]map[string]any, 0) + repositoryVisibilitySlice = append(repositoryVisibilitySlice, map[string]any{ + "internal": rules.RepositoryVisibility.Internal, + "private": rules.RepositoryVisibility.Private, + }) + rulesMap["repository_visibility"] = repositoryVisibilitySlice + } + } + return []any{rulesMap} } diff --git a/github/util_rules_test.go b/github/util_rules_test.go index 26f0d21eb0..c2ad539817 100644 --- a/github/util_rules_test.go +++ b/github/util_rules_test.go @@ -1457,3 +1457,109 @@ func Test_bypassActorCompareIdentity(t *testing.T) { }) } } + +func TestRoundTripRepositoryTargetRules(t *testing.T) { + t.Parallel() + + rulesMap := map[string]any{ + "repository_create": true, + "repository_delete": true, + "repository_transfer": true, + "repository_name": []any{ + map[string]any{ + "pattern": "^team-", + "negate": false, + }, + }, + "repository_visibility": []any{ + map[string]any{ + "internal": true, + "private": true, + }, + }, + } + + expandedRules := expandRules([]any{rulesMap}, true) + + if expandedRules.RepositoryCreate == nil { + t.Error("Expected RepositoryCreate rule to be set") + } + + if expandedRules.RepositoryDelete == nil { + t.Error("Expected RepositoryDelete rule to be set") + } + + if expandedRules.RepositoryTransfer == nil { + t.Error("Expected RepositoryTransfer rule to be set") + } + + if expandedRules.RepositoryName == nil { + t.Fatal("Expected RepositoryName rule to be set") + } + + if expandedRules.RepositoryName.Pattern != "^team-" { + t.Errorf("Expected Pattern to be %q, got %q", "^team-", expandedRules.RepositoryName.Pattern) + } + + if expandedRules.RepositoryName.Negate != false { + t.Errorf("Expected Negate to be false, got %v", expandedRules.RepositoryName.Negate) + } + + if expandedRules.RepositoryVisibility == nil { + t.Fatal("Expected RepositoryVisibility rule to be set") + } + + if expandedRules.RepositoryVisibility.Internal != true { + t.Errorf("Expected Internal to be true, got %v", expandedRules.RepositoryVisibility.Internal) + } + + if expandedRules.RepositoryVisibility.Private != true { + t.Errorf("Expected Private to be true, got %v", expandedRules.RepositoryVisibility.Private) + } + + flattenedRulesMap := flattenRules(t.Context(), expandedRules, true)[0].(map[string]any) + + for _, name := range []string{"repository_create", "repository_delete", "repository_transfer"} { + if flattenedRulesMap[name] != true { + t.Errorf("Expected %s to be true after round trip, got %v", name, flattenedRulesMap[name]) + } + } + + repositoryName := flattenedRulesMap["repository_name"].([]map[string]any) + if len(repositoryName) != 1 { + t.Fatalf("Expected 1 repository_name rule after round trip, got %d", len(repositoryName)) + } + + if repositoryName[0]["pattern"] != "^team-" { + t.Errorf("Expected pattern to be %q, got %v", "^team-", repositoryName[0]["pattern"]) + } + + repositoryVisibility := flattenedRulesMap["repository_visibility"].([]map[string]any) + if len(repositoryVisibility) != 1 { + t.Fatalf("Expected 1 repository_visibility rule after round trip, got %d", len(repositoryVisibility)) + } + + if repositoryVisibility[0]["internal"] != true { + t.Errorf("Expected internal to be true, got %v", repositoryVisibility[0]["internal"]) + } + + if repositoryVisibility[0]["private"] != true { + t.Errorf("Expected private to be true, got %v", repositoryVisibility[0]["private"]) + } +} + +func TestFlattenRulesRepositoryTargetRulesAreRepositoryRulesetOmitted(t *testing.T) { + t.Parallel() + + // Repository target rules only exist for organization rulesets. Flattening a + // repository ruleset must not emit keys that its schema does not declare. + flattenedRulesMap := flattenRules(t.Context(), &github.RepositoryRulesetRules{ + RepositoryDelete: &github.EmptyRuleParameters{}, + }, false)[0].(map[string]any) + + for _, name := range []string{"repository_create", "repository_delete", "repository_transfer", "repository_name", "repository_visibility"} { + if _, ok := flattenedRulesMap[name]; ok { + t.Errorf("Expected %s to be absent for a repository ruleset, got %v", name, flattenedRulesMap[name]) + } + } +} diff --git a/github/util_ruleset_validation.go b/github/util_ruleset_validation.go index d75b14c88c..d179fd5aa8 100644 --- a/github/util_ruleset_validation.go +++ b/github/util_ruleset_validation.go @@ -66,6 +66,25 @@ var pushOnlyRules = []github.RepositoryRuleType{ github.RulesetRuleTypeMaxFileSize, } +// repositoryOnlyRules contains rules that are only valid for the repository target. +// +// These rules govern the lifecycle, naming and visibility of the repositories +// themselves rather than their contents, so they are not supported for branch, +// tag or push rulesets. +// +// To verify/maintain this list: +// 1. Check the GitHub API documentation for organization rulesets: +// https://docs.github.com/en/rest/orgs/rules?apiVersion=2022-11-28#create-an-organization-repository-ruleset +// 2. Rules accepted for a `repository` target are rejected for every other +// target and vice versa. +var repositoryOnlyRules = []github.RepositoryRuleType{ + github.RulesetRuleTypeRepositoryCreate, + github.RulesetRuleTypeRepositoryDelete, + github.RulesetRuleTypeRepositoryName, + github.RulesetRuleTypeRepositoryTransfer, + github.RulesetRuleTypeRepositoryVisibility, +} + func validateRulesForTarget(ctx context.Context, d *schema.ResourceDiff) error { target := github.RulesetTarget(d.Get("target").(string)) tflog.Debug(ctx, "Validating rules for target", map[string]any{"target": target}) @@ -75,6 +94,8 @@ func validateRulesForTarget(ctx context.Context, d *schema.ResourceDiff) error { return validateRulesForPushTarget(ctx, d) case github.RulesetTargetBranch, github.RulesetTargetTag: return validateRulesForBranchTagTarget(ctx, d) + case github.RulesetTargetRepository: + return validateRulesForRepositoryTarget(ctx, d) } tflog.Debug(ctx, "Rules validation passed", map[string]any{"target": target}) @@ -89,6 +110,10 @@ func validateRulesForBranchTagTarget(ctx context.Context, d *schema.ResourceDiff return validateRules(ctx, d, branchTagOnlyRules) } +func validateRulesForRepositoryTarget(ctx context.Context, d *schema.ResourceDiff) error { + return validateRules(ctx, d, repositoryOnlyRules) +} + func validateRules(ctx context.Context, d *schema.ResourceDiff, allowedRules []github.RepositoryRuleType) error { target := github.RulesetTarget(d.Get("target").(string)) rules := d.Get("rules").([]any)[0].(map[string]any) @@ -146,6 +171,8 @@ func validateRulesetConditions(ctx context.Context, d *schema.ResourceDiff, isOr return validateConditionsFieldForBranchAndTagTargets(ctx, target, conditions, isOrg) case github.RulesetTargetPush: return validateConditionsFieldForPushTarget(ctx, conditions) + case github.RulesetTargetRepository: + return validateConditionsFieldForRepositoryTarget(ctx, conditions) } return nil } @@ -185,3 +212,14 @@ func validateConditionsFieldForPushTarget(ctx context.Context, conditions map[st tflog.Debug(ctx, "Conditions validation passed for push target") return nil } + +func validateConditionsFieldForRepositoryTarget(ctx context.Context, conditions map[string]any) error { + tflog.Debug(ctx, "Validating conditions field for repository target", map[string]any{"target": "repository", "conditions": conditions}) + + if conditions["ref_name"] != nil && len(conditions["ref_name"].([]any)) > 0 { + tflog.Debug(ctx, "Invalid ref_name for repository target", map[string]any{"ref_name": conditions["ref_name"]}) + return fmt.Errorf("ref_name must not be set for repository target") + } + tflog.Debug(ctx, "Conditions validation passed for repository target") + return nil +} diff --git a/github/util_ruleset_validation_test.go b/github/util_ruleset_validation_test.go index 65f802cdce..b4ee75a500 100644 --- a/github/util_ruleset_validation_test.go +++ b/github/util_ruleset_validation_test.go @@ -200,11 +200,80 @@ func Test_validateConditionsFieldForBranchAndTagTargets(t *testing.T) { func Test_ruleListsDoNotOverlap(t *testing.T) { t.Parallel() - for _, pushRule := range pushOnlyRules { - for _, branchTagRule := range branchTagOnlyRules { - if pushRule == branchTagRule { - t.Errorf("rule %q appears in both pushOnlyRules and branchTagOnlyRules", pushRule) + lists := map[string][]github.RepositoryRuleType{ + "pushOnlyRules": pushOnlyRules, + "branchTagOnlyRules": branchTagOnlyRules, + "repositoryOnlyRules": repositoryOnlyRules, + } + + for nameA, listA := range lists { + for nameB, listB := range lists { + if nameA >= nameB { + continue + } + for _, ruleA := range listA { + for _, ruleB := range listB { + if ruleA == ruleB { + t.Errorf("rule %q appears in both %s and %s", ruleA, nameA, nameB) + } + } } } } } + +func Test_validateConditionsFieldForRepositoryTarget(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + conditions map[string]any + expectError bool + errorMsg string + }{ + { + name: "valid repository target without ref_name", + conditions: map[string]any{ + "repository_name": []any{map[string]any{"include": []any{"~ALL"}, "exclude": []any{}}}, + }, + expectError: false, + }, + { + name: "valid repository target with nil ref_name", + conditions: map[string]any{"ref_name": nil}, + expectError: false, + }, + { + name: "valid repository target with empty ref_name slice", + conditions: map[string]any{"ref_name": []any{}}, + expectError: false, + }, + { + name: "invalid repository target with ref_name set", + conditions: map[string]any{ + "ref_name": []any{map[string]any{"include": []any{"~ALL"}, "exclude": []any{}}}, + }, + expectError: true, + errorMsg: "ref_name must not be set for repository target", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := validateConditionsFieldForRepositoryTarget(t.Context(), tt.conditions) + if tt.expectError { + if err == nil { + t.Errorf("expected error but got nil") + } else if err.Error() != tt.errorMsg { + t.Errorf("expected error %q, got %q", tt.errorMsg, err.Error()) + } + } else { + if err != nil { + t.Errorf("expected no error but got: %v", err) + } + } + }) + } +} diff --git a/templates/resources/organization_ruleset.md.tmpl b/templates/resources/organization_ruleset.md.tmpl index c23634dd77..28c7f31291 100644 --- a/templates/resources/organization_ruleset.md.tmpl +++ b/templates/resources/organization_ruleset.md.tmpl @@ -1,289 +1,55 @@ --- page_title: "{{.Name}} ({{.Type}}) - {{.RenderedProviderName}}" +subcategory: "" description: |- - Creates a GitHub organization ruleset. +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} --- # {{.Name}} ({{.Type}}) -Creates a GitHub organization ruleset. +{{ .Description | trimspace }} This resource allows you to create and manage rulesets on the organization level. When applied, a new ruleset will be created. When destroyed, that ruleset will be removed. +{{ if .HasExamples -}} ## Example Usage -{{ tffile "examples/resources/organization_ruleset/example_1.tf" }} +{{- range .ExampleFiles }} -## Argument Reference +{{ tffile . }} +{{- end }} +{{- end }} -- `enforcement` - (Required) (String) Possible values for Enforcement are `disabled`, `active`, `evaluate`. Note: `evaluate` is currently only supported for owners of type `organization`. +~> **Note:** Rules are target-specific. `branch` and `tag` targets support rules such as `creation`, `deletion`, `pull_request` and `required_status_checks`. `push` targets only support `file_path_restriction`, `max_file_size`, `max_file_path_length` and `file_extension_restriction`. `repository` targets only support `repository_create`, `repository_delete`, `repository_name`, `repository_transfer` and `repository_visibility`. Using the wrong rules for a target results in a validation error. -- `name` - (Required) (String) The name of the ruleset. +~> **Note:** at the time of writing this, the `RepositoryRole` bypass actor type corresponds to the following base repository roles and IDs: `maintain` -> `2`, `write` -> `4`, `admin` -> `5`. -- `rules` - (Required) (Block List, Min: 1, Max: 1) Rules within the ruleset. (see [below for nested schema](#rules)) +{{ .SchemaMarkdown | trimspace }} +{{- if or .HasImport .HasImportIDConfig .HasImportIdentityConfig }} -- `target` - (Required) (String) Possible values are `branch`, `tag` and `push`. - -- `bypass_actors` - (Optional) (Block List) The actors that can bypass the rules in this ruleset. (see [below for nested schema](#bypass_actors)) - -- `conditions` - (Optional) (Block List, Max: 1) Parameters for an organization ruleset condition. For `branch` and `tag` targets, `ref_name` is required alongside one of `repository_name` or `repository_id`. For `push` targets, `ref_name` must NOT be set - only `repository_name` or `repository_id` should be used. (see [below for nested schema](#conditions)) - -### Rules - -The `rules` block supports the following: - -~> **Note:** Rules are target-specific. `branch` and `tag` targets support rules like `creation`, `deletion`, `pull_request`, `required_status_checks`, etc. `push` targets only support `file_path_restriction`, `max_file_size`, `max_file_path_length`, and `file_extension_restriction`. Using the wrong rules for a target will result in a validation error. - -- `branch_name_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the branch_name_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. Conflicts with `tag_name_pattern` as it only applies to rulesets with target `branch`. (see [below for nested schema](#rulesbranch_name_pattern)) - -- `commit_author_email_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the commit_author_email_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. (see [below for nested schema](#rulescommit_author_email_pattern)) - -- `commit_message_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the commit_message_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. (see [below for nested schema](#rulescommit_message_pattern)) - -- `committer_email_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the committer_email_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. (see [below for nested schema](#rulescommitter_email_pattern)) - -- `creation` - (Optional) (Boolean) Only allow users with bypass permission to create matching refs. - -- `deletion` - (Optional) (Boolean) Only allow users with bypass permissions to delete matching refs. - -- `non_fast_forward` - (Optional) (Boolean) Prevent users with push access from force pushing to branches. - -- `pull_request` - (Optional) (Block List, Max: 1) Require all commits be made to a non-target branch and submitted via a pull request before they can be merged. (see [below for nested schema](#rulespull_request)) - -- `copilot_code_review` - (Optional) (Block List, Max: 1) Automatically request Copilot code review for new pull requests if the author has access to Copilot code review and their premium requests quota has not reached the limit. (see [below for nested schema](#rulescopilot_code_review)) - -- `required_linear_history` - (Optional) (Boolean) Prevent merge commits from being pushed to matching branches. - -- `required_signatures` - (Optional) (Boolean) Commits pushed to matching branches must have verified signatures. - -- `required_status_checks` - (Optional) (Block List, Max: 1) Choose which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. (see [below for nested schema](#rulesrequired_status_checks)) - -- `required_workflows` - (Optional) (Block List, Max: 1) Define which Actions workflows must pass before changes can be merged into a branch matching the rule. Multiple workflows can be specified. (see [below for nested schema](#rulesrequired_workflows)) - -- `required_code_scanning` - (Optional) (Block List, Max: 1) Define which tools must provide code scanning results before the reference is updated. When configured, code scanning must be enabled and have results for both the commit and the reference being updated. Multiple code scanning tools can be specified. (see [below for nested schema](#rulesrequired_code_scanning)) - -- `tag_name_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the tag_name_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. Conflicts with `branch_name_pattern` as it only applies to rulesets with target `tag`. (see [below for nested schema](#rulestag_name_pattern)) - -- `file_path_restriction` - (Optional) (Block List, Max: 1) Prevent commits that include changes to specified file paths from being pushed to the commit graph. This rule only applies to rulesets with target `push`. (see [below for nested schema](#rulesfile_path_restriction)) - -- `max_file_size` - (Optional) (Block List, Max: 1) Prevent commits that include files with a specified file size from being pushed to the commit graph. This rule only applies to rulesets with target `push`. (see [below for nested schema](#rulesmax_file_size)) - -- `max_file_path_length` - (Optional) (Block List, Max: 1) Prevent commits that include file paths that exceed a specified character limit from being pushed to the commit graph. This rule only applies to rulesets with target `push`. (see [below for nested schema](#rulesmax_file_path_length)) - -- `file_extension_restriction` - (Optional) (Block List, Max: 1) Prevent commits that include files with specified file extensions from being pushed to the commit graph. This rule only applies to rulesets with target `push`. (see [below for nested schema](#rulesfile_extension_restriction)) - -- `update` - (Optional) (Boolean) Only allow users with bypass permission to update matching refs. - -#### rules.branch_name_pattern - -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. - -- `pattern` - (Required) (String) The pattern to match with. - -- `name` - (Optional) (String) How this rule will appear to users. - -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. - -#### rules.commit_author_email_pattern - -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. - -- `pattern` - (Required) (String) The pattern to match with. - -- `name` - (Optional) (String) How this rule will appear to users. - -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. - -#### rules.commit_message_pattern - -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. - -- `pattern` - (Required) (String) The pattern to match with. - -- `name` - (Optional) (String) How this rule will appear to users. - -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. - -#### rules.committer_email_pattern - -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. - -- `pattern` - (Required) (String) The pattern to match with. - -- `name` - (Optional) (String) How this rule will appear to users. - -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. - -#### rules.pull_request - -- `allowed_merge_methods` - (Optional) (List of String, Min: 1) Array of merge methods to be allowed. Allowed values include `merge`, `squash`, and `rebase`. At least one must be enabled. - -- `dismiss_stale_reviews_on_push` - (Optional) (Boolean) New, reviewable commits pushed will dismiss previous pull request review approvals. Defaults to `false`. - -- `require_code_owner_review` - (Optional) (Boolean) Require an approving review in pull requests that modify files that have a designated code owner. Defaults to `false`. - -- `require_last_push_approval` - (Optional) (Boolean) Whether the most recent reviewable push must be approved by someone other than the person who pushed it. Defaults to `false`. - -- `required_approving_review_count` - (Optional) (Number) The number of approving reviews that are required before a pull request can be merged. Defaults to `0`. - -- `required_review_thread_resolution` - (Optional) (Boolean) All conversations on code must be resolved before a pull request can be merged. Defaults to `false`. - -#### rules.copilot_code_review - -- `review_on_push` - (Optional) (Boolean) Copilot automatically reviews each new push to the pull request. Defaults to `false`. - -- `review_draft_pull_requests` - (Optional) (Boolean) Copilot automatically reviews draft pull requests before they are marked as ready for review. Defaults to `false`. - -- `allowed_merge_methods` - (Required) (List of String, Min: 1) Array of merge methods to be allowed. Allowed values include `merge`, `squash`, and `rebase`. At least one must be enabled. - -- `required_reviewers` - (Optional) (Block List) Require specific reviewers to approve pull requests. Note: This feature is in beta. (see [below for nested schema](#rulespull_requestrequired_reviewers)) - -#### rules.pull_request.required_reviewers - -- `reviewer` - (Required) (Block List, Max: 1) The reviewer that must review matching files. (see [below for nested schema](#rulespull_requestrequired_reviewersreviewer)) - -- `file_patterns` - (Required) (List of String) File patterns (fnmatch syntax) that this reviewer must approve. - -- `minimum_approvals` - (Required) (Number) Minimum number of approvals required from this reviewer. Set to 0 to make approval optional. - -#### rules.pull_request.required_reviewers.reviewer - -- `id` - (Required) (Number) The ID of the reviewer (Team ID). - -- `type` - (Required) (String) The type of reviewer. Currently only `Team` is supported. - -#### rules.required_status_checks - -- `required_check` - (Required) (Block Set, Min: 1) Status checks that are required. Several can be defined. (see [below for nested schema](#rulesrequired_status_checksrequired_check)) - -- `strict_required_status_checks_policy` - (Optional) (Boolean) Whether pull requests targeting a matching branch must be tested with the latest code. This setting will not take effect unless at least one status check is enabled. Defaults to `false`. - -- `do_not_enforce_on_create` - (Optional) (Boolean) Allow repositories and branches to be created if a check would otherwise prohibit it. Defaults to `false`. - -#### rules.required_status_checks.required_check - -- `context` - (Required) (String) The status check context name that must be present on the commit. - -- `integration_id` - (Optional) (Number) The optional integration ID that this status check must originate from. - -- `do_not_enforce_on_create` - (Optional) (Boolean) Allow repositories and branches to be created if a check would otherwise prohibit it. Defaults to `false`. - -#### rules.required_workflows - -- `do_not_enforce_on_create` - (Optional) (Boolean) Allow repositories and branches to be created if a check would otherwise prohibit it. Defaults to `false`. - -- `required_workflow` - (Required) (Block Set, Min: 1) Actions workflows that are required. Multiple can be defined. (see [below for nested schema](#rulesrequired_workflowsrequired_workflow)) - -#### rules.required_workflows.required_workflow - -- `repository_id` - (Required) (Number) The ID of the repository. Names, full names and repository URLs are not supported. - -- `path` - (Required) (String) The path to the YAML definition file of the workflow. - -- `ref` - (Optional) (String) The optional ref from which to fetch the workflow. Defaults to `master`. - -#### rules.required_code_scanning - -- `required_code_scanning_tool` - (Required) (Block Set, Min: 1) Actions code scanning tools that are required. Multiple can be defined. (see [below for nested schema](#rulesrequired_code_scanningrequired_code_scanning_tool)) - -#### rules.required_code_scanning.required_code_scanning_tool - -- `alerts_threshold` - (Required) (String) The severity level at which code scanning results that raise alerts block a reference update. Can be one of: `none`, `errors`, `errors_and_warnings`, `all`. - -- `security_alerts_threshold` - (Required) (String) The severity level at which code scanning results that raise security alerts block a reference update. Can be one of: `none`, `critical`, `high_or_higher`, `medium_or_higher`, `all`. - -- `tool` - (Required) (String) The name of a code scanning tool. - -#### rules.tag_name_pattern - -- `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. - -- `pattern` - (Required) (String) The pattern to match with. - -- `name` - (Optional) (String) How this rule will appear to users. - -- `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. - -#### rules.file_path_restriction - -- `restricted_file_paths` - (Required) (Block Set, Min: 1) The file paths that are restricted from being pushed to the commit graph. - -#### rules.max_file_size - -- `max_file_size` - (Required) (Integer) The maximum allowed size, in megabytes (MB), of a file. Valid range is 1-100 MB. - -#### rules.max_file_path_length - -- `max_file_path_length` - (Required) (Integer) The maximum number of characters allowed in file paths. - -#### rules.file_extension_restriction - -- `restricted_file_extensions` - (Required) (Block Set, Min: 1) The file extensions that are restricted from being pushed to the commit graph. - -#### bypass_actors - -- `actor_id` - (Optional) (Number) The ID of the actor that can bypass a ruleset. Must be omitted for ID-less actor types: `OrganizationAdmin`, `EnterpriseOwner`, and `DeployKey` — the GitHub API does not use an ID for these types and will ignore any value set. - -- `actor_type` (String) The type of actor that can bypass a ruleset. Can be one of: `RepositoryRole`, `Team`, `Integration`, `OrganizationAdmin`, `DeployKey`, `EnterpriseOwner`. - -- `bypass_mode` - (Optional) (String) When the specified actor can bypass the ruleset. pull_request means that an actor can only bypass rules on pull requests. Can be one of: `always`, `pull_request`, `exempt`. - -~>Note: at the time of writing this, the following actor types correspond to the following actor IDs: - -- `RepositoryRole` (This is the actor type, the following are the base repository roles and their associated IDs.) - - `maintain` -> `2` - - `write` -> `4` - - `admin` -> `5` - -#### conditions - -- `ref_name` - (Optional) (Block List, Max: 1) Required for `branch` and `tag` targets. Must NOT be set for `push` targets. (see [below for nested schema](#conditionsref_name)) -- `repository_id` (Optional) (List of Number) The repository IDs that the ruleset applies to. One of these IDs must match for the condition to pass. -- `repository_name` (Optional) (Block List, Max: 1) Targets repositories that match the specified name patterns. (see [below for nested schema](#conditionsrepository_name)) -- `repository_property` (Optional) (Block List, Max: 1) Targets repositories by custom or system properties. (see [below for nested schema](#conditionsrepository_property)) - -Exactly one of `repository_id`, `repository_name`, or `repository_property` must be set for the rule to target repositories. - -~> **Note:** For `push` targets, do not include `ref_name` in conditions. Push rulesets operate on file content, not on refs. - -#### conditions.ref_name - -- `exclude` - (Required) (List of String) Array of ref names or patterns to exclude. The condition will not pass if any of these patterns match. - -- `include` - (Required) (List of String) Array of ref names or patterns to include. One of these patterns must match for the condition to pass. Also accepts `~DEFAULT_BRANCH` to include the default branch or `~ALL` to include all branches. - -#### conditions.repository_name - -- `exclude` - (Required) (List of String) Array of repository names or patterns to exclude. The condition will not pass if any of these patterns match. -- `include` - (Required) (List of String) Array of repository names or patterns to include. One of these patterns must match for the condition to pass. Also accepts `~ALL` to include all repositories. -- `protected` - (Optional) (Boolean) Whether renaming of target repositories is prevented. Defaults to `false`. - -#### conditions.repository_property - -- `include` - (Optional) (List of Repository Properties) The repository properties and values to include. All of these properties must match for the condition to pass. (see [below for nested schema](#conditionsrepository_propertyproperties)) - -- `exclude` - (Optional) (List of Repository Properties) The repository properties and values to exclude. The condition will not pass if any of these properties match. (see [below for nested schema](#conditionsrepository_propertyproperties)) - -#### conditions.repository_property.properties - -- `name` (Required) (String) The name of the repository property to target. - -- `property_values` (Required) (Array of String) The values to match for the repository property. +## Import -- `source` (String) The source of the repository property. Defaults to 'custom' if not specified. Can be one of: `custom`, `system` +GitHub organization rulesets can be imported using the GitHub ruleset ID. -## Attributes Reference +Import is supported using the following syntax: +{{- end }} +{{- if .HasImportIdentityConfig }} -The following additional attributes are exported: +In Terraform v1.12.0 and later, the [`import` block](https://developer.hashicorp.com/terraform/language/import) can be used with the `identity` attribute, for example: -- `etag` (String) +{{tffile .ImportIdentityConfigFile }} -- `node_id` (String) GraphQL global node id for use with v4 API. +{{ .IdentitySchemaMarkdown | trimspace }} +{{- end }} +{{- if .HasImportIDConfig }} -- `ruleset_id` (Number) GitHub ID for the ruleset. +In Terraform v1.5.0 and later, the [`import` block](https://developer.hashicorp.com/terraform/language/import) can be used with the `id` attribute, for example: -## Import +{{tffile .ImportIDConfigFile }} +{{- end }} +{{- if .HasImport }} -GitHub Organization Rulesets can be imported using the GitHub ruleset ID e.g. +The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example: -`$ terraform import github_organization_ruleset.example 12345` +{{codefile "shell" .ImportFile }} +{{- end }}