Skip to content

Commit d2d9d44

Browse files
authored
feat: Fixup repository environment logic (#3162)
Signed-off-by: Steve Hipwell <steve.hipwell@gmail.com>
1 parent 19a8ecc commit d2d9d44

12 files changed

+1204
-899
lines changed

github/resource_github_repository_environment.go

Lines changed: 114 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,40 @@ package github
33
import (
44
"context"
55
"errors"
6-
"log"
6+
"fmt"
77
"net/http"
88
"net/url"
99

1010
"github.com/google/go-github/v83/github"
11+
"github.com/hashicorp/terraform-plugin-log/tflog"
1112
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
1214
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1315
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1416
)
1517

1618
func resourceGithubRepositoryEnvironment() *schema.Resource {
1719
return &schema.Resource{
18-
CreateContext: resourceGithubRepositoryEnvironmentCreate,
19-
ReadContext: resourceGithubRepositoryEnvironmentRead,
20-
UpdateContext: resourceGithubRepositoryEnvironmentUpdate,
21-
DeleteContext: resourceGithubRepositoryEnvironmentDelete,
22-
Importer: &schema.ResourceImporter{
23-
StateContext: resourceGithubRepositoryEnvironmentImport,
20+
SchemaVersion: 1,
21+
StateUpgraders: []schema.StateUpgrader{
22+
{
23+
Type: resourceGithubRepositoryEnvironmentV0().CoreConfigSchema().ImpliedType(),
24+
Upgrade: resourceGithubRepositoryEnvironmentStateUpgradeV0,
25+
Version: 0,
26+
},
2427
},
28+
2529
Schema: map[string]*schema.Schema{
2630
"repository": {
2731
Type: schema.TypeString,
2832
Required: true,
29-
ForceNew: true,
3033
Description: "The repository of the environment.",
3134
},
35+
"repository_id": {
36+
Description: "The ID of the GitHub repository.",
37+
Type: schema.TypeInt,
38+
Computed: true,
39+
},
3240
"environment": {
3341
Type: schema.TypeString,
3442
Required: true,
@@ -56,20 +64,22 @@ func resourceGithubRepositoryEnvironment() *schema.Resource {
5664
"reviewers": {
5765
Type: schema.TypeList,
5866
Optional: true,
59-
MaxItems: 6,
67+
MaxItems: 1,
6068
Description: "The environment reviewers configuration.",
6169
Elem: &schema.Resource{
6270
Schema: map[string]*schema.Schema{
6371
"teams": {
6472
Type: schema.TypeSet,
65-
Optional: true,
6673
Elem: &schema.Schema{Type: schema.TypeInt},
74+
Optional: true,
75+
MaxItems: 6,
6776
Description: "Up to 6 IDs for teams who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed.",
6877
},
6978
"users": {
7079
Type: schema.TypeSet,
71-
Optional: true,
7280
Elem: &schema.Schema{Type: schema.TypeInt},
81+
Optional: true,
82+
MaxItems: 6,
7383
Description: "Up to 6 IDs for users who may review jobs that reference the environment. Reviewers must have at least read access to the repository. Only one of the required reviewers needs to approve the job for it to proceed.",
7484
},
7585
},
@@ -96,12 +106,50 @@ func resourceGithubRepositoryEnvironment() *schema.Resource {
96106
},
97107
},
98108
},
109+
110+
CustomizeDiff: customdiff.All(
111+
diffRepository,
112+
resourceGithubRepositoryEnvironmentDiff,
113+
),
114+
115+
CreateContext: resourceGithubRepositoryEnvironmentCreate,
116+
ReadContext: resourceGithubRepositoryEnvironmentRead,
117+
UpdateContext: resourceGithubRepositoryEnvironmentUpdate,
118+
DeleteContext: resourceGithubRepositoryEnvironmentDelete,
119+
Importer: &schema.ResourceImporter{
120+
StateContext: resourceGithubRepositoryEnvironmentImport,
121+
},
122+
}
123+
}
124+
125+
func resourceGithubRepositoryEnvironmentDiff(_ context.Context, d *schema.ResourceDiff, _ any) error {
126+
if d.Id() == "" {
127+
return nil
128+
}
129+
130+
if v, ok := d.GetOk("reviewers"); ok {
131+
count := 0
132+
o := v.([]any)[0]
133+
if t, ok := o.(map[string]any)["teams"]; ok {
134+
count += t.(*schema.Set).Len()
135+
}
136+
137+
if t, ok := o.(map[string]any)["users"]; ok {
138+
count += t.(*schema.Set).Len()
139+
}
140+
141+
if count > 6 {
142+
return fmt.Errorf("a maximum of 6 reviewers (users and teams combined) can be set for an environment")
143+
}
99144
}
145+
146+
return nil
100147
}
101148

102-
func resourceGithubRepositoryEnvironmentCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
103-
client := meta.(*Owner).v3client
104-
owner := meta.(*Owner).name
149+
func resourceGithubRepositoryEnvironmentCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
150+
meta := m.(*Owner)
151+
client := meta.v3client
152+
owner := meta.name
105153

106154
repoName := d.Get("repository").(string)
107155
envName := d.Get("environment").(string)
@@ -118,41 +166,50 @@ func resourceGithubRepositoryEnvironmentCreate(ctx context.Context, d *schema.Re
118166
}
119167
d.SetId(id)
120168

169+
repo, _, err := client.Repositories.Get(ctx, owner, repoName)
170+
if err != nil {
171+
return diag.FromErr(err)
172+
}
173+
174+
if err := d.Set("repository_id", int(repo.GetID())); err != nil {
175+
return diag.FromErr(err)
176+
}
177+
121178
return nil
122179
}
123180

124-
func resourceGithubRepositoryEnvironmentRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
125-
client := meta.(*Owner).v3client
126-
owner := meta.(*Owner).name
181+
func resourceGithubRepositoryEnvironmentRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
182+
ctx = tflog.SetField(ctx, "id", d.Id())
127183

128-
repoName, envNamePart, err := parseID2(d.Id())
129-
if err != nil {
130-
return diag.FromErr(err)
131-
}
184+
meta := m.(*Owner)
185+
client := meta.v3client
186+
owner := meta.name
132187

133-
envName := unescapeIDPart(envNamePart)
188+
repoName := d.Get("repository").(string)
189+
envName := d.Get("environment").(string)
134190

135191
env, _, err := client.Repositories.GetEnvironment(ctx, owner, repoName, url.PathEscape(envName))
136192
if err != nil {
137193
var ghErr *github.ErrorResponse
138194
if errors.As(err, &ghErr) {
139195
if ghErr.Response.StatusCode == http.StatusNotFound {
140-
log.Printf("[INFO] Removing repository environment %s from state because it no longer exists in GitHub",
141-
d.Id())
196+
tflog.Info(ctx, "Repository environment not found, removing from state.", map[string]any{"repository": repoName, "environment": envName})
142197
d.SetId("")
143198
return nil
144199
}
145200
}
146201
return diag.FromErr(err)
147202
}
148203

149-
_ = d.Set("repository", repoName)
150-
_ = d.Set("environment", envName)
151-
_ = d.Set("wait_timer", nil)
152-
_ = d.Set("can_admins_bypass", env.CanAdminsBypass)
204+
if err := d.Set("wait_timer", nil); err != nil {
205+
return diag.FromErr(err)
206+
}
207+
if err := d.Set("can_admins_bypass", env.CanAdminsBypass); err != nil {
208+
return diag.FromErr(err)
209+
}
153210

154211
for _, pr := range env.ProtectionRules {
155-
switch *pr.Type {
212+
switch pr.GetType() {
156213
case "wait_timer":
157214
if err = d.Set("wait_timer", pr.WaitTimer); err != nil {
158215
return diag.FromErr(err)
@@ -199,15 +256,18 @@ func resourceGithubRepositoryEnvironmentRead(ctx context.Context, d *schema.Reso
199256
return diag.FromErr(err)
200257
}
201258
} else {
202-
_ = d.Set("deployment_branch_policy", []any{})
259+
if err := d.Set("deployment_branch_policy", []any{}); err != nil {
260+
return diag.FromErr(err)
261+
}
203262
}
204263

205264
return nil
206265
}
207266

208-
func resourceGithubRepositoryEnvironmentUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
209-
client := meta.(*Owner).v3client
210-
owner := meta.(*Owner).name
267+
func resourceGithubRepositoryEnvironmentUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
268+
meta := m.(*Owner)
269+
client := meta.v3client
270+
owner := meta.name
211271

212272
repoName := d.Get("repository").(string)
213273
envName := d.Get("environment").(string)
@@ -227,34 +287,43 @@ func resourceGithubRepositoryEnvironmentUpdate(ctx context.Context, d *schema.Re
227287
return nil
228288
}
229289

230-
func resourceGithubRepositoryEnvironmentDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
231-
client := meta.(*Owner).v3client
232-
owner := meta.(*Owner).name
290+
func resourceGithubRepositoryEnvironmentDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
291+
meta := m.(*Owner)
292+
client := meta.v3client
293+
owner := meta.name
233294

234-
repoName, envNamePart, err := parseID2(d.Id())
235-
if err != nil {
236-
return diag.FromErr(err)
237-
}
238-
239-
envName := unescapeIDPart(envNamePart)
295+
repoName := d.Get("repository").(string)
296+
envName := d.Get("environment").(string)
240297

241-
_, err = client.Repositories.DeleteEnvironment(ctx, owner, repoName, url.PathEscape(envName))
298+
_, err := client.Repositories.DeleteEnvironment(ctx, owner, repoName, url.PathEscape(envName))
242299
if err != nil {
243300
return diag.FromErr(deleteResourceOn404AndSwallow304OtherwiseReturnError(err, d, "environment (%s)", envName))
244301
}
245302

246303
return nil
247304
}
248305

249-
func resourceGithubRepositoryEnvironmentImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
306+
func resourceGithubRepositoryEnvironmentImport(ctx context.Context, d *schema.ResourceData, m any) ([]*schema.ResourceData, error) {
307+
meta := m.(*Owner)
308+
client := meta.v3client
309+
owner := meta.name
310+
250311
repoName, envNamePart, err := parseID2(d.Id())
251312
if err != nil {
252-
return nil, err
313+
return nil, fmt.Errorf("invalid id (%s), expected format <repository>:<environment>", d.Id())
314+
}
315+
316+
repo, _, err := client.Repositories.Get(ctx, owner, repoName)
317+
if err != nil {
318+
return nil, fmt.Errorf("failed to retrieve repository %s: %w", repoName, err)
253319
}
254320

255321
if err := d.Set("repository", repoName); err != nil {
256322
return nil, err
257323
}
324+
if err := d.Set("repository_id", int(repo.GetID())); err != nil {
325+
return nil, err
326+
}
258327
if err := d.Set("environment", unescapeIDPart(envNamePart)); err != nil {
259328
return nil, err
260329
}

0 commit comments

Comments
 (0)