fix: remove vulnerability alerts from state for archived/deleted repos - #3553
fix: remove vulnerability alerts from state for archived/deleted repos#3553DrFaust92 wants to merge 2 commits into
Conversation
|
👋 Hi, and thank you for this contribution! This repo is maintained by GitHub and community members on a best-effort basis. We'll get to this as soon as we can. You can help us prioritize by joining the discussion on open issues and PRs, sharing details on the changes you need, and reviewing other contributions. 🤖 This is an automated message. |
The Read function for github_repository_vulnerability_alerts returned a
hard error when the underlying repository was archived or no longer
existed, wedging every subsequent plan/apply with:
repository <name> is archived, please remove the resource from your
configuration
Per the provider's own Read convention (a 404 means "remove from state"),
Read now calls d.SetId("") and returns nil in these cases instead of
erroring, so Terraform drops the resource gracefully. Also guards against
a nil *http.Response before dereferencing StatusCode, and fixes the
existing 404 branch that set the ID to "" but then returned an error
(which the SDK discards, so the removal never took effect).
Resolves integrations#3369
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a5959ba to
32b0152
Compare
There was a problem hiding this comment.
Pull request overview
These provider review instructions are being used.
Updates vulnerability-alert refresh behavior so archived or deleted repositories are removed from Terraform state rather than blocking refresh.
Changes:
- Gracefully clears state for unavailable alerts and archived/deleted repositories.
- Updates acceptance coverage for archived repositories.
- Documents archived-repository behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
github/resource_github_repository_vulnerability_alerts.go |
Updates read/error handling. |
github/resource_github_repository_vulnerability_alerts_test.go |
Tests archived-repository refresh. |
templates/resources/repository_vulnerability_alerts.md.tmpl |
Adds behavior notes. |
docs/resources/repository_vulnerability_alerts.md |
Regenerates documentation. |
| repo, _, err := client.Repositories.Get(ctx, owner, repoName) | ||
| if err != nil { | ||
| return diag.Errorf("repository doesn't exist anymore, please remove the resource from your configuration: %s", err.Error()) | ||
| tflog.Warn(ctx, "Repository doesn't exist anymore, removing resource from state", map[string]any{"owner": owner, "repo_name": repoName}) | ||
| d.SetId("") | ||
| return nil |
There was a problem hiding this comment.
Good catch — fixed in 51c87ad. The out-of-band Repositories.Get now captures its response and only clears state when it returns 404; permission, rate-limit, network, and 5xx errors are propagated via diag.Errorf instead of silently dropping valid state and forcing a recreate.
| Config: fmt.Sprintf(withAlertsConfig, repoName, true), | ||
| ExpectNonEmptyPlan: true, |
There was a problem hiding this comment.
Added in 51c87ad — new removes_from_state_when_repository_deleted acceptance step deletes the repository out of band, then RefreshState: true + ExpectNonEmptyPlan: true asserts the deleted-repo Read path removes the resource from state. Used RefreshState (matching resource_github_branch_default_test.go) rather than a re-apply, since re-applying would recreate the repo and yield an empty plan.
Address Copilot review on integrations#3553: - Repository lookup now clears state only when it returns 404; permission, rate-limit, network, and 5xx errors are propagated instead of silently dropping valid state and forcing a recreate. - Guard resp against nil before reading StatusCode in the debug log, matching the nil guards added elsewhere in Read. - Add a deleted-repository acceptance step (out-of-band delete + refresh + non-empty plan) covering the deleted-repo path.
| vulnerabilityAlertsEnabled, resp, err := client.Repositories.GetVulnerabilityAlerts(ctx, owner, repoName) | ||
| if err != nil { | ||
| if resp.StatusCode == http.StatusNotFound { | ||
| if resp != nil && resp.StatusCode == http.StatusNotFound { |
There was a problem hiding this comment.
Please use the established pattern for error handling: https://github.com/integrations/terraform-provider-github/blob/main/ARCHITECTURE.md#error-handling
| if err != nil { | ||
| if resp.StatusCode == http.StatusNotFound { | ||
| if resp != nil && resp.StatusCode == http.StatusNotFound { | ||
| tflog.Warn(ctx, "Vulnerability alerts don't exist, removing resource from state", map[string]any{"owner": owner, "repo_name": repoName}) |
There was a problem hiding this comment.
Please lowercase log messages
| tflog.Warn(ctx, "Vulnerability alerts don't exist, removing resource from state", map[string]any{"owner": owner, "repo_name": repoName}) | |
| tflog.Warn(ctx, "vulnerability alerts don't exist, removing resource from state", map[string]any{"owner": owner, "repo_name": repoName}) |
| responseStatus := 0 | ||
| if resp != nil { | ||
| responseStatus = resp.StatusCode | ||
| } |
There was a problem hiding this comment.
question: What's the purpose of this?
| // If no error, but the response status code is 404, we need to check if the repository is accessible. | ||
| if resp.StatusCode == http.StatusNotFound { | ||
| repo, _, err := client.Repositories.Get(ctx, owner, repoName) | ||
| if resp != nil && resp.StatusCode == http.StatusNotFound { |
There was a problem hiding this comment.
question: Is there a case when resp could be nil here?
| // GitHub's side. Read must remove the resource from state | ||
| // instead of erroring. Because the config still declares the | ||
| // resource, the follow-up plan is non-empty (Terraform wants | ||
| // to recreate it) rather than failing the whole run. |
There was a problem hiding this comment.
suggestion: Let's add a ConfigPlanCheck to verify that it tries to create a new resource
| resource "github_repository" "test" { | ||
| name = "%s" | ||
| visibility = "private" | ||
| auto_init = true | ||
| } |
There was a problem hiding this comment.
Please use the mustCreateTestRepository function instead of declaring the repo in Terraform code
| { | ||
| Config: fmt.Sprintf(withAlertsConfig, repoName), | ||
| ConfigStateChecks: []statecheck.StateCheck{ | ||
| statecheck.ExpectKnownValue("github_repository_vulnerability_alerts.test", tfjsonpath.New("enabled"), knownvalue.Bool(true)), |
There was a problem hiding this comment.
No need to check for same value as config
| statecheck.ExpectKnownValue("github_repository_vulnerability_alerts.test", tfjsonpath.New("enabled"), knownvalue.Bool(true)), |
| // Delete the repository out of band. On refresh, the | ||
| // vulnerability alerts Read hits a deleted repository and must | ||
| // remove the resource from state instead of erroring, leaving a | ||
| // non-empty plan (Terraform wants to recreate it). |
There was a problem hiding this comment.
Please add a plan check here as well for the Create Action
Resolves #3369
Before the change?
resourceGithubRepositoryVulnerabilityAlertsReadreturned a hard error when the underlying repository was archived or no longer existed:Because this surfaces during refresh, every subsequent
plan/applyfails until the resource is manually removed from both config and state — you can't eventerraform state rmcleanly while the read errors. Archiving a repo auto-disables its vulnerability alerts on GitHub's side, so the resource is genuinely unmanageable at that point.The existing 404 branch also set
d.SetId("")and then returneddiag.Errorf(...); since the SDK discards state changes when Read returns an error, that removal never actually took effect.After the change?
Read now follows the provider's documented convention (
.github/instructions/schema-and-state.instructions.md: "In Read, a 404 from GitHub means 'remove from state' — calld.SetId("")and return nil"):d.SetId("")+return nil(removed the straydiag.Errorf).d.SetId("")+return nil.d.SetId("")+return nil, with atflog.Warn.Also guards against a nil
*http.Responsebefore dereferencingStatusCode.Net effect: Terraform drops the resource from state gracefully. If the config still declares it on an archived repo, the follow-up plan is non-empty (recreate), and
Createstill returns the pre-existingcannot enable vulnerability alerts on archived repositoryerror — so users get a clear signal to remove the block, instead of a wedged refresh.Pull request checklist
errors_when_reading_archived_repository→removes_from_state_when_reading_archived_repository, asserting graceful removal + non-empty plan instead of an error)Does this introduce a breaking change?
Read no longer returns an error for archived/deleted repositories; instead the resource is removed from state. This is a behavior change but not a schema breaking change.