Skip to content

fix: remove repository file from state when its commit no longer exists - #3587

Open
erikmiller-gusto wants to merge 1 commit into
integrations:mainfrom
erikmiller-gusto:fix/repository-file-read-404-removes-from-state
Open

fix: remove repository file from state when its commit no longer exists#3587
erikmiller-gusto wants to merge 1 commit into
integrations:mainfrom
erikmiller-gusto:fix/repository-file-read-404-removes-from-state

Conversation

@erikmiller-gusto

Copy link
Copy Markdown

Resolves #3586


Before the change?

  • resourceGithubRepositoryFileRead returns a hard error when the file's commit can no longer be found. Both lookup paths fall through to a bare return diag.FromErr(err): a GitHub 404 from GetCommit (used when commit_sha is in state), and getFileCommit's cannot find file ... case (used when it is not).
  • Because read fails rather than clearing the ID, once the commit is unreachable the resource can neither be refreshed nor removed — every later plan/apply fails on the same read, and the entry needs manual state surgery to clear.
  • This is inconsistent with the sibling repository resources: resource_github_repository_custom_property.go, resource_github_repository_ruleset.go and resource_github_branch_default.go already detect a 404 on read and call d.SetId("").

After the change?

  • A read that establishes the commit is gone removes the resource from state, matching Terraform's contract and the sibling resources.
  • getFileCommit's exhaustion case is wrapped in an errFileCommitNotFound sentinel so the read can tell it apart from a genuine failure without string matching. That case is reachable independently of any HTTP status, which is why a sentinel is used rather than only checking for a 404.
  • Every error that is not "not found" still propagates unchanged.

Pull request checklist

  • Schema migrations have been created if needed — no schema change; this only affects read behaviour.
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been reviewed and added / updated if needed — no user-facing schema or option change, so I did not find anything in website/ that describes this behaviour. Happy to document it if you would like it called out.

Does this introduce a breaking change?

  • Yes
  • No

A configuration whose file still exists is unaffected. The change only alters what happens when the commit is already gone, where the previous outcome was a permanent read error.


Testing

Test_getFileCommit_NoCommitContainsFile drives getFileCommit against an httptest server that returns an empty commit list, and asserts errors.Is(err, errFileCommitNotFound). It uses the existing mustCreateTestGitHubClient helper and needs no credentials. I verified it discriminates: removing the %w wrap makes it fail with errors.Is(...) = false, which is the regression that would silently restore the old behaviour.

I could not exercise the resource-level 404 path, since the acceptance tests need a live org and credentials I do not have. Note that go test ./github/... does not pass cleanly on a fresh checkout in my environment either — TestAccGithubActionsOrganizationPermissions panics without credentials — and I confirmed that failure is identical with and without this change. go build ./..., go vet ./github/... and gofmt -l are clean.

AI use

Per the AI Use Policy: this change was written with AI assistance. I have reviewed it, and the verification described above is what I actually ran rather than a summary of intent. The reasoning for the sentinel over a status-code-only check, and the scope limits above, are stated deliberately — please push back if you would rather solve it differently, for example by handling the exhaustion case at the call site instead.

🤖

resourceGithubRepositoryFileRead surfaced a 404 from the commit lookup, and
getFileCommit's 'no commit contains the file' case, as read failures. Terraform's
contract for read is to clear the ID when the remote object is gone, which the
other repository resources already do. Erroring instead leaves the resource
unable to refresh or be removed without editing state by hand.

Wrap getFileCommit's exhaustion case in an errFileCommitNotFound sentinel so the
read can distinguish it from a genuine failure, and translate both it and a
GitHub 404 into d.SetId(""). Every other error keeps propagating unchanged.
@github-actions

Copy link
Copy Markdown

👋 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.

Comment thread github/util_repo_test.go

client := mustCreateTestGitHubClient(t, ts.URL+"/")

_, err := getFileCommit(context.Background(), client, "owner", "repo", "some/file.txt", "main")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please use t.Context() instead

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

These provider review instructions are being used.

The PR aims to handle unreachable repository-file commits during Terraform refresh. However, it can remove an existing file from state and lacks resource-level regression coverage.

Changes:

  • Adds an errFileCommitNotFound sentinel.
  • Clears state for missing commits.
  • Adds sentinel-wrapping coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
github/util_repo.go Adds and wraps the commit-not-found sentinel.
github/util_repo_test.go Tests sentinel detection.
github/resource_github_repository_file.go Handles commit lookup failures during read.
Suppressed comments (1)

github/resource_github_repository_file.go:346

  • This sentinel also does not establish that the managed file is gone: getFileCommit is called only after GetContents returned the current file successfully. Removing the ID here therefore schedules creation of a file that still exists and normally fails unless overwrite_on_create is enabled. Preserve the resource in state and propagate/handle the commit-metadata lookup failure instead; absence is already handled by the earlier contents lookup.
		if errors.Is(err, errFileCommitNotFound) {
			tflog.Info(ctx, "Removing repository file from state because no commit contains it")
			d.SetId("")
			return nil

Comment on lines +338 to +340
if ghErr, ok := errors.AsType[*github.ErrorResponse](err); ok && ghErr.Response != nil && ghErr.Response.StatusCode == http.StatusNotFound {
tflog.Info(ctx, "Removing repository file from state because its commit no longer exists in GitHub")
d.SetId("")
Comment thread github/util_repo_test.go
Comment on lines +26 to +28
_, err := getFileCommit(context.Background(), client, "owner", "repo", "some/file.txt", "main")
if err == nil {
t.Fatal("expected an error when no commit contains the file, got nil")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Bug Something isn't working as documented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

github_repository_file: read errors instead of removing the resource from state when its commit no longer exists

3 participants