Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9909ecc
Ensure tests use `GITHUB_TEST_*` values for Owner and Organization
deiga Dec 2, 2025
d3bafe6
Update `skipUnlessMode` to check for enterprise ENV variables
deiga Dec 2, 2025
a4f929b
Use `testOwnerFunc` to set `testOwner` so that `GITHUB_TEST_OWNER` wo…
deiga Dec 3, 2025
fbc8707
Switch from `NewSubsystemLoggingHTTPTransport` to `NewLoggingHTTPTran…
deiga Dec 3, 2025
6966690
Update to use Context aware CRUD functions
deiga Dec 7, 2025
a60ac7f
Ensure `update_allows_fetch_and_merge` isn't added for Org Ruleset
deiga Dec 4, 2025
3e2cd38
Update name of resource, to make debugging failing tests easier
deiga Dec 4, 2025
d82c614
Need to have one of `repository_name` or `repository_id` defined
deiga Dec 4, 2025
b2c3570
Need to have a valid repo reference
deiga Dec 4, 2025
6954994
Actions repository access level needs to be set properly
deiga Dec 4, 2025
a41b2f3
Required workflow needs to be in the `.github/workflows` folder
deiga Dec 4, 2025
41b3dec
Update indentation
deiga Dec 4, 2025
512db50
Need to have one of `repository_name` or `repository_id` defined
deiga Dec 4, 2025
bdc4729
Add handling of `AllowedMergeMethods`
deiga Dec 4, 2025
405d3b6
Add `allowed_merge_methods` to `rules` for Org & Repo rulesets
deiga Dec 6, 2025
986ae22
Fixed type conversion for `allowed_merge_methods`
deiga Dec 6, 2025
581ff8c
Update test content to actually pass the GH API
deiga Dec 4, 2025
e97f75c
Convert `github_repository` to use `*Context` CRUD methods
deiga Dec 6, 2025
f096f44
Enable `TestGithubOrganizationRulesets/Creates_and_updates_organizati…
deiga Dec 6, 2025
2d77c1e
`make fmt`
deiga Dec 7, 2025
9d22505
Add workaround for GH API bug that OrgAdmin actor_id is returned as `…
deiga Dec 6, 2025
74bcfe7
`make fmt`
deiga Dec 7, 2025
4808483
Fix `TestGithubOrganizationRulesets/Creates_organization_ruleset_with…
deiga Dec 7, 2025
f698cd2
Fix tests regarding `bypass_actors` ordering
deiga Dec 7, 2025
3752c16
Rename test resources for easier debugging
deiga Dec 7, 2025
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
2 changes: 1 addition & 1 deletion github/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var GHECDataResidencyHostMatch = regexp.MustCompile(`^[a-zA-Z0-9.\-]+\.ghe\.com\
func RateLimitedHTTPClient(client *http.Client, writeDelay, readDelay, retryDelay time.Duration, parallelRequests bool, retryableErrors map[int]bool, maxRetries int) *http.Client {
client.Transport = NewEtagTransport(client.Transport)
client.Transport = NewRateLimitTransport(client.Transport, WithWriteDelay(writeDelay), WithReadDelay(readDelay), WithParallelRequests(parallelRequests))
client.Transport = logging.NewSubsystemLoggingHTTPTransport("GitHub", client.Transport)
client.Transport = logging.NewLoggingHTTPTransport(client.Transport)
client.Transport = newPreviewHeaderInjectorTransport(map[string]string{
// TODO: remove when Stone Crop preview is moved to general availability in the GraphQL API
"Accept": "application/vnd.github.stone-crop-preview+json",
Expand Down
8 changes: 5 additions & 3 deletions github/provider_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var (
isPaidPlan = os.Getenv("GITHUB_PAID_FEATURES")
testEnterprise = os.Getenv("ENTERPRISE_SLUG")
testOrganization = testOrganizationFunc()
testOwner = os.Getenv("GITHUB_OWNER")
testOwner = testOwnerFunc()
testToken = os.Getenv("GITHUB_TOKEN")
testBaseURLGHES = os.Getenv("GHES_BASE_URL")
)
Expand Down Expand Up @@ -54,8 +54,8 @@ func skipUnlessMode(t *testing.T, providerMode string) {
t.Log("GITHUB_TOKEN environment variable should be empty")
}
case enterprise:
if os.Getenv("GITHUB_TOKEN") == "" {
t.Log("GITHUB_TOKEN environment variable should be set")
if os.Getenv("GITHUB_TOKEN") == "" || os.Getenv("ENTERPRISE_ACCOUNT") != "true" || os.Getenv("ENTERPRISE_SLUG") == "" {
t.Log("GITHUB_TOKEN and ENTERPRISE_ACCOUNT and ENTERPRISE_SLUG environment variables should be set")
} else {
return
}
Expand Down Expand Up @@ -120,6 +120,7 @@ func testOrganizationFunc() string {
organization := os.Getenv("GITHUB_ORGANIZATION")
if organization == "" {
organization = os.Getenv("GITHUB_TEST_ORGANIZATION")
os.Setenv("GITHUB_ORGANIZATION", organization)
}
return organization
}
Expand All @@ -128,6 +129,7 @@ func testOwnerFunc() string {
owner := os.Getenv("GITHUB_OWNER")
if owner == "" {
owner = os.Getenv("GITHUB_TEST_OWNER")
os.Setenv("GITHUB_OWNER", owner)
}
return owner
}
Expand Down
59 changes: 34 additions & 25 deletions github/resource_github_organization_ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ import (
"strconv"

"github.com/google/go-github/v77/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceGithubOrganizationRuleset() *schema.Resource {
return &schema.Resource{
Create: resourceGithubOrganizationRulesetCreate,
Read: resourceGithubOrganizationRulesetRead,
Update: resourceGithubOrganizationRulesetUpdate,
Delete: resourceGithubOrganizationRulesetDelete,
CreateContext: resourceGithubOrganizationRulesetCreate,
ReadContext: resourceGithubOrganizationRulesetRead,
UpdateContext: resourceGithubOrganizationRulesetUpdate,
DeleteContext: resourceGithubOrganizationRulesetDelete,
Importer: &schema.ResourceImporter{
State: resourceGithubOrganizationRulesetImport,
StateContext: resourceGithubOrganizationRulesetImport,
},

SchemaVersion: 1,
Expand All @@ -45,7 +46,7 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
Description: "Possible values for Enforcement are `disabled`, `active`, `evaluate`. Note: `evaluate` is currently only supported for owners of type `organization`.",
},
"bypass_actors": {
Type: schema.TypeList,
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
Optional: true,
DiffSuppressFunc: bypassActorsDiffSuppressFunc,
Description: "The actors that can bypass the rules in this ruleset.",
Expand Down Expand Up @@ -197,6 +198,16 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
Description: "Require all commits be made to a non-target branch and submitted via a pull request before they can be merged.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"allowed_merge_methods": {
Type: schema.TypeList,
Required: true,
MinItems: 1,
Description: "Array of allowed merge methods. Allowed values include `merge`, `squash`, and `rebase`. At least one option must be enabled.",
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: toDiagFunc(validation.StringInSlice([]string{"merge", "squash", "rebase"}, false), "allowed_merge_methods"),
},
},
"dismiss_stale_reviews_on_push": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -585,36 +596,35 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
}
}

func resourceGithubOrganizationRulesetCreate(d *schema.ResourceData, meta any) error {
func resourceGithubOrganizationRulesetCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client

rulesetReq := resourceGithubRulesetObject(d, meta.(*Owner).name)

org := meta.(*Owner).name
ctx := context.Background()

var ruleset *github.RepositoryRuleset
var err error

ruleset, _, err = client.Organizations.CreateRepositoryRuleset(ctx, org, *rulesetReq)
if err != nil {
return err
return diag.FromErr(err)
}
d.SetId(strconv.FormatInt(*ruleset.ID, 10))

return resourceGithubOrganizationRulesetRead(d, meta)
return resourceGithubOrganizationRulesetRead(ctx, d, meta)
}

func resourceGithubOrganizationRulesetRead(d *schema.ResourceData, meta any) error {
func resourceGithubOrganizationRulesetRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client

org := meta.(*Owner).name
rulesetID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return unconvertibleIdErr(d.Id(), err)
return diag.FromErr(unconvertibleIdErr(d.Id(), err))
}

ctx := context.WithValue(context.Background(), ctxId, rulesetID)
ctx = context.WithValue(ctx, ctxId, rulesetID)
if !d.IsNewResource() {
ctx = context.WithValue(ctx, ctxEtag, d.Get("etag").(string))
}
Expand All @@ -636,7 +646,7 @@ func resourceGithubOrganizationRulesetRead(d *schema.ResourceData, meta any) err
return nil
}
}
return err
return diag.FromErr(err)
}

if ruleset == nil {
Expand All @@ -659,46 +669,46 @@ func resourceGithubOrganizationRulesetRead(d *schema.ResourceData, meta any) err
return nil
}

func resourceGithubOrganizationRulesetUpdate(d *schema.ResourceData, meta any) error {
func resourceGithubOrganizationRulesetUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client

rulesetReq := resourceGithubRulesetObject(d, meta.(*Owner).name)

org := meta.(*Owner).name
rulesetID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return unconvertibleIdErr(d.Id(), err)
return diag.FromErr(unconvertibleIdErr(d.Id(), err))
}

ctx := context.WithValue(context.Background(), ctxId, rulesetID)
ctx = context.WithValue(ctx, ctxId, rulesetID)

var ruleset *github.RepositoryRuleset

ruleset, _, err = client.Organizations.UpdateRepositoryRuleset(ctx, org, rulesetID, *rulesetReq)
if err != nil {
return err
return diag.FromErr(err)
}
d.SetId(strconv.FormatInt(*ruleset.ID, 10))

return resourceGithubOrganizationRulesetRead(d, meta)
return resourceGithubOrganizationRulesetRead(ctx, d, meta)
}

func resourceGithubOrganizationRulesetDelete(d *schema.ResourceData, meta any) error {
func resourceGithubOrganizationRulesetDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v3client
org := meta.(*Owner).name

rulesetID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return unconvertibleIdErr(d.Id(), err)
return diag.FromErr(unconvertibleIdErr(d.Id(), err))
}
ctx := context.WithValue(context.Background(), ctxId, rulesetID)
ctx = context.WithValue(ctx, ctxId, rulesetID)

log.Printf("[DEBUG] Deleting organization ruleset: %s: %d", org, rulesetID)
_, err = client.Organizations.DeleteRepositoryRuleset(ctx, org, rulesetID)
return err
return diag.FromErr(err)
}

func resourceGithubOrganizationRulesetImport(d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
func resourceGithubOrganizationRulesetImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
rulesetID, err := strconv.ParseInt(d.Id(), 10, 64)
if err != nil {
return []*schema.ResourceData{d}, unconvertibleIdErr(d.Id(), err)
Expand All @@ -710,7 +720,6 @@ func resourceGithubOrganizationRulesetImport(d *schema.ResourceData, meta any) (

client := meta.(*Owner).v3client
org := meta.(*Owner).name
ctx := context.Background()

ruleset, _, err := client.Organizations.GetRepositoryRuleset(ctx, org, rulesetID)
if ruleset == nil || err != nil {
Expand Down
Loading