Skip to content

Commit fe4dd69

Browse files
committed
fix: handle sha_pinning_required=false
Split `CreateOrUpdate` into separate `Create` and `Update` functions to properly handle the `sha_pinning_required` boolean field's zero-value problem: - Use `d.GetOkExists()` in `Create` to only send the field when the user explicitly sets it in configuration, even to `false`. - Use `d.HasChange()` in `Update` to detect when the value changes and `d.Get()` to read the new value. Keep schema as `Optional` + `Computed` (not `Default: false`) so the resource inherits the organization-level setting when not explicitly configured. This follows the same pattern used by `allow_forking` in `resource_github_repository.go`. Refactor both resources to use context-aware CRUD functions (`CreateContext`, `ReadContext`, `UpdateContext`, `DeleteContext`) with `diag.Diagnostics` return types. Replace `log.Printf` with `tflog.Debug` for structured logging. Remove `d.SetId()` from Update functions and set computed fields directly instead of calling Read. Fix #3223
1 parent 1af72d4 commit fe4dd69

4 files changed

+310
-67
lines changed

github/resource_github_actions_organization_permissions.go

Lines changed: 96 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@ package github
33
import (
44
"context"
55
"errors"
6-
"log"
76

87
"github.com/google/go-github/v83/github"
8+
"github.com/hashicorp/terraform-plugin-log/tflog"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
910
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1011
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1112
)
1213

1314
func resourceGithubActionsOrganizationPermissions() *schema.Resource {
1415
return &schema.Resource{
15-
Create: resourceGithubActionsOrganizationPermissionsCreateOrUpdate,
16-
Read: resourceGithubActionsOrganizationPermissionsRead,
17-
Update: resourceGithubActionsOrganizationPermissionsCreateOrUpdate,
18-
Delete: resourceGithubActionsOrganizationPermissionsDelete,
16+
CreateContext: resourceGithubActionsOrganizationPermissionsCreate,
17+
ReadContext: resourceGithubActionsOrganizationPermissionsRead,
18+
UpdateContext: resourceGithubActionsOrganizationPermissionsUpdate,
19+
DeleteContext: resourceGithubActionsOrganizationPermissionsDelete,
1920
Importer: &schema.ResourceImporter{
2021
StateContext: schema.ImportStatePassthroughContext,
2122
},
@@ -137,17 +138,13 @@ func resourceGithubActionsEnabledRepositoriesObject(d *schema.ResourceData) ([]i
137138
return enabled, nil
138139
}
139140

140-
func resourceGithubActionsOrganizationPermissionsCreateOrUpdate(d *schema.ResourceData, meta any) error {
141+
func resourceGithubActionsOrganizationPermissionsCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
141142
client := meta.(*Owner).v3client
142143
orgName := meta.(*Owner).name
143-
ctx := context.Background()
144-
if !d.IsNewResource() {
145-
ctx = context.WithValue(ctx, ctxId, d.Id())
146-
}
147144

148145
err := checkOrganization(meta)
149146
if err != nil {
150-
return err
147+
return diag.FromErr(err)
151148
}
152149

153150
allowedActions := d.Get("allowed_actions").(string)
@@ -158,61 +155,125 @@ func resourceGithubActionsOrganizationPermissionsCreateOrUpdate(d *schema.Resour
158155
EnabledRepositories: &enabledRepositories,
159156
}
160157

161-
if v, ok := d.GetOk("sha_pinning_required"); ok {
158+
if v, ok := d.GetOkExists("sha_pinning_required"); ok { //nolint:staticcheck,SA1019 // Use `GetOkExists` to detect explicit false for booleans.
162159
actionsPermissions.SHAPinningRequired = github.Ptr(v.(bool))
163160
}
164161

165162
_, _, err = client.Actions.UpdateActionsPermissions(ctx,
166163
orgName,
167164
actionsPermissions)
168165
if err != nil {
169-
return err
166+
return diag.FromErr(err)
170167
}
171168

172169
if allowedActions == "selected" {
173170
actionsAllowedData := resourceGithubActionsOrganizationAllowedObject(d)
174171
if actionsAllowedData != nil {
175-
log.Printf("[DEBUG] Allowed actions config is set")
172+
tflog.Debug(ctx, "Set allowed actions configuration.")
176173
_, _, err = client.Actions.UpdateActionsAllowed(ctx,
177174
orgName,
178175
*actionsAllowedData)
179176
if err != nil {
180-
return err
177+
return diag.FromErr(err)
181178
}
182179
} else {
183-
log.Printf("[DEBUG] Allowed actions config not set, skipping")
180+
tflog.Debug(ctx, "Skip setting allowed actions configuration because none is specified.")
184181
}
185182
}
186183

187184
if enabledRepositories == "selected" {
188185
enabledReposData, err := resourceGithubActionsEnabledRepositoriesObject(d)
189186
if err != nil {
190-
return err
187+
return diag.FromErr(err)
191188
}
192189
_, err = client.Actions.SetEnabledReposInOrg(ctx,
193190
orgName,
194191
enabledReposData)
195192
if err != nil {
196-
return err
193+
return diag.FromErr(err)
197194
}
198195
}
199196

200197
d.SetId(orgName)
201-
return resourceGithubActionsOrganizationPermissionsRead(d, meta)
198+
return resourceGithubActionsOrganizationPermissionsRead(ctx, d, meta)
199+
}
200+
201+
func resourceGithubActionsOrganizationPermissionsUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
202+
client := meta.(*Owner).v3client
203+
orgName := meta.(*Owner).name
204+
205+
err := checkOrganization(meta)
206+
if err != nil {
207+
return diag.FromErr(err)
208+
}
209+
210+
allowedActions := d.Get("allowed_actions").(string)
211+
enabledRepositories := d.Get("enabled_repositories").(string)
212+
213+
actionsPermissions := github.ActionsPermissions{
214+
AllowedActions: &allowedActions,
215+
EnabledRepositories: &enabledRepositories,
216+
}
217+
218+
if d.HasChange("sha_pinning_required") {
219+
actionsPermissions.SHAPinningRequired = github.Ptr(d.Get("sha_pinning_required").(bool))
220+
}
221+
222+
_, _, err = client.Actions.UpdateActionsPermissions(ctx,
223+
orgName,
224+
actionsPermissions)
225+
if err != nil {
226+
return diag.FromErr(err)
227+
}
228+
229+
if allowedActions == "selected" {
230+
actionsAllowedData := resourceGithubActionsOrganizationAllowedObject(d)
231+
if actionsAllowedData != nil {
232+
tflog.Debug(ctx, "Update allowed actions configuration.")
233+
_, _, err = client.Actions.UpdateActionsAllowed(ctx,
234+
orgName,
235+
*actionsAllowedData)
236+
if err != nil {
237+
return diag.FromErr(err)
238+
}
239+
} else {
240+
tflog.Debug(ctx, "Skip updating allowed actions configuration because none is specified.")
241+
}
242+
}
243+
244+
if enabledRepositories == "selected" {
245+
enabledReposData, err := resourceGithubActionsEnabledRepositoriesObject(d)
246+
if err != nil {
247+
return diag.FromErr(err)
248+
}
249+
_, err = client.Actions.SetEnabledReposInOrg(ctx,
250+
orgName,
251+
enabledReposData)
252+
if err != nil {
253+
return diag.FromErr(err)
254+
}
255+
}
256+
257+
if d.HasChange("sha_pinning_required") {
258+
if err := d.Set("sha_pinning_required", d.Get("sha_pinning_required").(bool)); err != nil {
259+
return diag.FromErr(err)
260+
}
261+
}
262+
263+
return nil
202264
}
203265

204-
func resourceGithubActionsOrganizationPermissionsRead(d *schema.ResourceData, meta any) error {
266+
func resourceGithubActionsOrganizationPermissionsRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
205267
client := meta.(*Owner).v3client
206-
ctx := context.Background()
207268

208269
err := checkOrganization(meta)
209270
if err != nil {
210-
return err
271+
return diag.FromErr(err)
211272
}
212273

213274
actionsPermissions, _, err := client.Actions.GetActionsPermissions(ctx, d.Id())
214275
if err != nil {
215-
return err
276+
return diag.FromErr(err)
216277
}
217278

218279
// only load and fill allowed_actions_config if allowed_actions_config is also set
@@ -228,7 +289,7 @@ func resourceGithubActionsOrganizationPermissionsRead(d *schema.ResourceData, me
228289
if serverHasAllowedActionsConfig && userWantsAllowedActionsConfig {
229290
actionsAllowed, _, err := client.Actions.GetActionsAllowed(ctx, d.Id())
230291
if err != nil {
231-
return err
292+
return diag.FromErr(err)
232293
}
233294

234295
// If actionsAllowed set to local/all by removing all actions config settings, the response will be empty
@@ -240,12 +301,12 @@ func resourceGithubActionsOrganizationPermissionsRead(d *schema.ResourceData, me
240301
"verified_allowed": actionsAllowed.GetVerifiedAllowed(),
241302
},
242303
}); err != nil {
243-
return err
304+
return diag.FromErr(err)
244305
}
245306
}
246307
} else {
247308
if err = d.Set("allowed_actions_config", []any{}); err != nil {
248-
return err
309+
return diag.FromErr(err)
249310
}
250311
}
251312

@@ -257,7 +318,7 @@ func resourceGithubActionsOrganizationPermissionsRead(d *schema.ResourceData, me
257318
for {
258319
enabledRepos, resp, err := client.Actions.ListEnabledReposInOrg(ctx, d.Id(), &opts)
259320
if err != nil {
260-
return err
321+
return diag.FromErr(err)
261322
}
262323
allRepos = append(allRepos, enabledRepos.Repositories...)
263324

@@ -276,37 +337,36 @@ func resourceGithubActionsOrganizationPermissionsRead(d *schema.ResourceData, me
276337
"repository_ids": repoList,
277338
},
278339
}); err != nil {
279-
return err
340+
return diag.FromErr(err)
280341
}
281342
} else {
282343
if err = d.Set("enabled_repositories_config", []any{}); err != nil {
283-
return err
344+
return diag.FromErr(err)
284345
}
285346
}
286347
}
287348

288349
if err = d.Set("allowed_actions", actionsPermissions.GetAllowedActions()); err != nil {
289-
return err
350+
return diag.FromErr(err)
290351
}
291352
if err = d.Set("enabled_repositories", actionsPermissions.GetEnabledRepositories()); err != nil {
292-
return err
353+
return diag.FromErr(err)
293354
}
294355

295356
if err = d.Set("sha_pinning_required", actionsPermissions.GetSHAPinningRequired()); err != nil {
296-
return err
357+
return diag.FromErr(err)
297358
}
298359

299360
return nil
300361
}
301362

302-
func resourceGithubActionsOrganizationPermissionsDelete(d *schema.ResourceData, meta any) error {
363+
func resourceGithubActionsOrganizationPermissionsDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
303364
client := meta.(*Owner).v3client
304365
orgName := meta.(*Owner).name
305-
ctx := context.WithValue(context.Background(), ctxId, d.Id())
306366

307367
err := checkOrganization(meta)
308368
if err != nil {
309-
return err
369+
return diag.FromErr(err)
310370
}
311371

312372
// This will nullify any allowedActions elements
@@ -317,7 +377,7 @@ func resourceGithubActionsOrganizationPermissionsDelete(d *schema.ResourceData,
317377
EnabledRepositories: github.Ptr("all"),
318378
})
319379
if err != nil {
320-
return err
380+
return diag.FromErr(err)
321381
}
322382

323383
return nil

github/resource_github_actions_organization_permissions_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66

77
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
88
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
10+
"github.com/hashicorp/terraform-plugin-testing/statecheck"
11+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
912
)
1013

1114
func TestAccGithubActionsOrganizationPermissions(t *testing.T) {
@@ -104,6 +107,62 @@ func TestAccGithubActionsOrganizationPermissions(t *testing.T) {
104107
})
105108
})
106109

110+
t.Run("test setting sha_pinning_required to true", func(t *testing.T) {
111+
enabledRepositories := "all"
112+
113+
config := fmt.Sprintf(`
114+
resource "github_actions_organization_permissions" "test" {
115+
allowed_actions = "all"
116+
enabled_repositories = "%s"
117+
sha_pinning_required = true
118+
}
119+
`, enabledRepositories)
120+
121+
resource.Test(t, resource.TestCase{
122+
PreCheck: func() { skipUnlessHasOrgs(t) },
123+
ProviderFactories: providerFactories,
124+
Steps: []resource.TestStep{
125+
{
126+
Config: config,
127+
ConfigStateChecks: []statecheck.StateCheck{
128+
statecheck.ExpectKnownValue("github_actions_organization_permissions.test", tfjsonpath.New("sha_pinning_required"), knownvalue.Bool(true)),
129+
},
130+
},
131+
},
132+
})
133+
})
134+
135+
t.Run("test setting sha_pinning_required to false", func(t *testing.T) {
136+
enabledRepositories := "all"
137+
138+
configTmpl := `
139+
resource "github_actions_organization_permissions" "test" {
140+
allowed_actions = "all"
141+
enabled_repositories = "%s"
142+
sha_pinning_required = %t
143+
}
144+
`
145+
146+
resource.Test(t, resource.TestCase{
147+
PreCheck: func() { skipUnlessHasOrgs(t) },
148+
ProviderFactories: providerFactories,
149+
Steps: []resource.TestStep{
150+
{
151+
Config: fmt.Sprintf(configTmpl, enabledRepositories, true),
152+
ConfigStateChecks: []statecheck.StateCheck{
153+
statecheck.ExpectKnownValue("github_actions_organization_permissions.test", tfjsonpath.New("sha_pinning_required"), knownvalue.Bool(true)),
154+
},
155+
},
156+
{
157+
Config: fmt.Sprintf(configTmpl, enabledRepositories, false),
158+
ConfigStateChecks: []statecheck.StateCheck{
159+
statecheck.ExpectKnownValue("github_actions_organization_permissions.test", tfjsonpath.New("sha_pinning_required"), knownvalue.Bool(false)),
160+
},
161+
},
162+
},
163+
})
164+
})
165+
107166
t.Run("test setting of organization allowed actions", func(t *testing.T) {
108167
allowedActions := "selected"
109168
enabledRepositories := "all"

0 commit comments

Comments
 (0)