Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
cbe3621
Update descriptions and validations
deiga Nov 27, 2025
5ebc3d3
Add `CustomizeDiff` logic to validate on `plan`
deiga Nov 27, 2025
ae114d7
Remove unused leftover
deiga Nov 29, 2025
7be8666
Add first validation test
deiga Nov 29, 2025
edd0c8b
Add validation error when `conditions` is missing
deiga Nov 30, 2025
7a53ffd
Add further validation tests
deiga Nov 30, 2025
9c2ade7
Remove unnecessary skip blocks as `individual` and `anonymous` access…
deiga Dec 2, 2025
f6e453b
Switch `ref_name` to `Optional` as `push` doesn't need a `ref_name`
deiga Dec 2, 2025
620ee7e
Add Debug logging with `tflog` to validation
deiga Dec 3, 2025
0187a91
Fix validation as `ref_name`, `repository_name` and `repository_id` a…
deiga Dec 3, 2025
9b14abd
Fix test output expectation
deiga Dec 3, 2025
9c14bb6
Remove unnecessary panic test
deiga Dec 3, 2025
9dafd20
Improve validation output messages
deiga Dec 3, 2025
2dab967
Fix condition to require only one of `repository_name` or `repository…
deiga Dec 3, 2025
dd4b1d0
Add validation to `required_workflow.path`
deiga Dec 6, 2025
1a85db2
`make fmt`
deiga Dec 6, 2025
5158315
Rename test resources for easier debugging
deiga Dec 7, 2025
3f2d00f
Fix linter issues
deiga Dec 7, 2025
736cd02
Add validation to ensure `rules.required_status_checks.required_check…
deiga Dec 8, 2025
3d3f8e8
Add test to ensure that `required_checks` is always required
deiga Dec 8, 2025
28c785c
Fix tests after rebase
deiga Dec 10, 2025
27f1582
Improve legibility of `conditions` description
deiga Dec 10, 2025
a3ddbd3
Update descriptions
deiga Dec 14, 2025
22dc452
Add Acc test for push ruleset.
deiga Dec 14, 2025
1233fb8
Add failing test for `flattenConditions` with no `ref_name` condition
deiga Dec 14, 2025
77a4c16
Fix `flattenConditions` to work with `push` rulesets
deiga Dec 15, 2025
094409b
Add more tests for `flattenConditions`
deiga Dec 15, 2025
c76b1dc
Enable debug logging in `flattenConditions`
deiga Dec 15, 2025
6eb9ea1
Ensures that `flattenConditions` returns an empty list on empty API r…
deiga Dec 15, 2025
036dd6c
Add validation for `push` `rules`
deiga Dec 16, 2025
e4134bb
`github_repository`: Remove comment which doesn't hold true anymore
deiga Dec 16, 2025
e1ff452
`github_repository`: Ensures that we don't try to PATCH an archived repo
deiga Dec 16, 2025
7d2e899
`github_repository_ruleset`: Change `TestGithubRepositoryRulesetArchi…
deiga Dec 16, 2025
f747bf5
Get `TestAccGithubRepositoryRulesets` to work
deiga Dec 17, 2025
804c347
`repository_ruleset`: Add tests for validations
deiga Dec 17, 2025
525e787
`repository_ruleset`: Implement validations for `target`, `conditions…
deiga Dec 17, 2025
7a30bdb
Updated ruleset docs
deiga Dec 17, 2025
1fd5acb
Extract validation functions to separate utils file with unit tests
deiga Dec 31, 2025
6d99fb5
Fix `ExpectError` message
deiga Jan 2, 2026
b322f02
Fix push ruleset test config
deiga Jan 10, 2026
d7f8863
Remove `repository` target after thorough testing that it doesn't do …
deiga Jan 10, 2026
a51779b
Use idiomatic test naming convention
deiga Jan 13, 2026
c924617
Address code structure comment in tests
deiga Jan 13, 2026
88154f3
Address import shadowing
deiga Jan 13, 2026
105fb64
Replace unnecessary `fmt.Errorf` with `errors.New`
deiga Jan 13, 2026
27018d2
Fix inconsistent logging
deiga Jan 13, 2026
a9b535d
Fix returning wrong `err`
deiga Jan 13, 2026
1fc283d
Refactor to use typed constant string for ruleset `Target`
deiga Jan 13, 2026
f217677
Fix indentation issue with `github_repository_file` and heredocs
deiga Jan 14, 2026
d51fea0
Refactor `github_repository_file` to use Context-aware functions for …
deiga Jan 14, 2026
cd7913d
Rename files to be more sensible
deiga Jan 14, 2026
5c0112b
Refactor validation functions so that Repo and Org share almost every…
deiga Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ linters:
- misspell
- modernize
- nilerr
- nilnesserr
- predeclared
- staticcheck
- unconvert
Expand Down
6 changes: 3 additions & 3 deletions github/resource_github_issue_labels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func TestAccGithubIssueLabels(t *testing.T) {
}

func testAccGithubIssueLabelsConfig(repoName string, labels []map[string]any) string {
resource := ""
config := ""
if labels != nil {
var dynamic strings.Builder
for _, label := range labels {
Expand All @@ -94,7 +94,7 @@ func testAccGithubIssueLabelsConfig(repoName string, labels []map[string]any) st
`, label["name"], label["color"], label["description"]))
}

resource = fmt.Sprintf(`
config = fmt.Sprintf(`
resource "github_issue_labels" "test" {
repository = github_repository.test.name

Expand All @@ -110,7 +110,7 @@ func testAccGithubIssueLabelsConfig(repoName string, labels []map[string]any) st
}

%s
`, repoName, resource)
`, repoName, config)
}

func TestAccGithubIssueLabelsArchived(t *testing.T) {
Expand Down
65 changes: 44 additions & 21 deletions github/resource_github_organization_ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"regexp"
"strconv"

"github.com/google/go-github/v81/github"
Expand All @@ -26,6 +27,8 @@ func resourceGithubOrganizationRuleset() *schema.Resource {

SchemaVersion: 1,

CustomizeDiff: resourceGithubOrganizationRulesetValidate,

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Expand All @@ -34,16 +37,17 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
Description: "The name of the ruleset.",
},
"target": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"branch", "tag", "push"}, false),
Description: "Possible values are `branch`, `tag` and `push`. Note: The `push` target is in beta and is subject to change.",
Type: schema.TypeString,
Required: true,
// The API accepts an `repository` target, but any rule created with that doesn't show up in the UI, nor does it have any rules.
ValidateFunc: validation.StringInSlice([]string{string(TargetBranch), string(TargetTag), string(TargetPush)}, false),
Description: "The target of the ruleset. Possible values are `branch`, `tag` and `push`.",
},
"enforcement": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"disabled", "active", "evaluate"}, false),
Description: "Possible values for Enforcement are `disabled`, `active`, `evaluate`. Note: `evaluate` is currently only supported for owners of type `organization`.",
Description: "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.",
},
"bypass_actors": {
Type: schema.TypeList, // TODO: These are returned from GH API sorted by actor_id, we might want to investigate if we want to include sorting
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should sort the returned values based on the inputs which would stop churn.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds sensible, need to investigate where the best place for the sorting is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevehipwell after some investigation, it seems that schema.TypeSet would be the correct way to implement unsorted. What do you think?

Expand All @@ -62,7 +66,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"Integration", "OrganizationAdmin", "RepositoryRole", "Team", "DeployKey"}, false),
Description: "The type of actor that can bypass a ruleset. See https://docs.github.com/en/rest/orgs/rules for more information",
Description: "The type of actor that can bypass a ruleset. Can be one of: `Integration`, `OrganizationAdmin`, `RepositoryRole`, `Team`, or `DeployKey`.",
},
"bypass_mode": {
Type: schema.TypeString,
Expand All @@ -87,13 +91,14 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "Parameters for an organization ruleset condition. `ref_name` is required alongside one of `repository_name` or `repository_id`.",
Description: "Parameters for an organization ruleset condition. `ref_name` is required for `branch` and `tag` targets, but must not be set for `push` targets. One of `repository_name` or `repository_id` is always required.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"ref_name": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "Targets refs that match the specified patterns. Required for `branch` and `tag` targets.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"include": {
Expand All @@ -119,6 +124,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "Targets repositories that match the specified name patterns.",
ExactlyOneOf: []string{"conditions.0.repository_id"},
AtLeastOneOf: []string{"conditions.0.repository_id"},
Elem: &schema.Resource{
Expand Down Expand Up @@ -256,9 +262,10 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"context": {
Type: schema.TypeString,
Required: true,
Description: "The status check context name that must be present on the commit.",
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotEmpty),
Description: "The status check context name that must be present on the commit.",
},
"integration_id": {
Type: schema.TypeInt,
Expand Down Expand Up @@ -286,7 +293,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
"non_fast_forward": {
Type: schema.TypeBool,
Optional: true,
Description: "Prevent users with push access from force pushing to branches.",
Description: "Prevent users with push access from force pushing to refs.",
},
"commit_message_pattern": {
Type: schema.TypeList,
Expand Down Expand Up @@ -465,9 +472,10 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
Description: "The repository in which the workflow is defined.",
},
"path": {
Type: schema.TypeString,
Required: true,
Description: "The path to the workflow YAML definition file.",
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: toDiagFunc(validation.StringMatch(regexp.MustCompile(`^\.github\/workflows\/.*$`), "Path must be in the .github/workflows directory"), "path"),
Description: "The path to the workflow YAML definition file.",
},
"ref": {
Type: schema.TypeString,
Expand Down Expand Up @@ -589,8 +597,9 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
},
},
"etag": {
Type: schema.TypeString,
Computed: true,
Type: schema.TypeString,
Computed: true,
Description: "An etag representing the ruleset for caching purposes.",
},
},
}
Expand Down Expand Up @@ -688,7 +697,7 @@ func resourceGithubOrganizationRulesetRead(ctx context.Context, d *schema.Resour
_ = d.Set("target", ruleset.GetTarget())
_ = d.Set("enforcement", ruleset.Enforcement)
_ = d.Set("bypass_actors", flattenBypassActors(ruleset.BypassActors))
_ = d.Set("conditions", flattenConditions(ruleset.GetConditions(), true))
_ = d.Set("conditions", flattenConditionsWithContext(ctx, ruleset.GetConditions(), true))
_ = d.Set("rules", flattenRules(ruleset.Rules, true))
_ = d.Set("node_id", ruleset.GetNodeID())
_ = d.Set("etag", resp.Header.Get("ETag"))
Expand Down Expand Up @@ -804,7 +813,7 @@ func resourceGithubOrganizationRulesetImport(ctx context.Context, d *schema.Reso
"owner": owner,
"ruleset_id": rulesetID,
})
return []*schema.ResourceData{d}, fmt.Errorf("`ruleset_id` must be present")
return []*schema.ResourceData{d}, errors.New("`ruleset_id` must be present")
}

tflog.Debug(ctx, fmt.Sprintf("Importing organization ruleset: %s/%d", owner, rulesetID), map[string]any{
Expand All @@ -831,3 +840,17 @@ func resourceGithubOrganizationRulesetImport(ctx context.Context, d *schema.Reso

return []*schema.ResourceData{d}, nil
}

func resourceGithubOrganizationRulesetValidate(ctx context.Context, d *schema.ResourceDiff, _ any) error {
err := validateRulesetConditions(ctx, d, true)
if err != nil {
return err
}

err = validateRulesetRules(ctx, d)
if err != nil {
return err
}

return nil
}
Loading