Repository navigation
fix: Stop storing ETags in state with the new client - #3693
benlangfeld wants to merge 4 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. |
|
@stevehipwell @deiga @robert-crandall since you've all weighed in on the I'm leaving it as a draft for now because I can't mark it ready for review while I'm still waiting on reviews of my other open PRs (#3685 and #3658), but it's complete and I'd welcome feedback in the meantime. |
stevehipwell
left a comment
There was a problem hiding this comment.
Please open an issue to request changes before opening a PR. There isn't a lot of maintainer bandwidth, and changes like this will be quicker to be implemented on our side than via an unsolicited PR.
|
@stevehipwell This is a fundamental bug that's been amply documented in issues going back many months. I figured if someone else was going to fix it they would have done so by now. I'll stop contributing then. Thanks. |
|
@benlangfeld which issue(s)? AFAIK all new client bugs have been fixed. Unfortunately we (the maintainers) are not free to merge code or cut releases without coordination with GitHub so there is a large backlog to get out. The whole process is highly time consuming and we're not getting any support outside of blocking reviews. |
GitHub's ETag for an authenticated response depends on the credentials, so with app auth every new installation token sees a different ETag for the same unchanged resource, and import and destroy plans never match. Only the legacy client reads the stored etag back, to send If-None-Match; the new client revalidates through its own response cache. Owner now has a storeEtag flag, set only for the legacy client, and resources set etag only when it is true. Acceptance tests run with the new client, so they no longer ignore or assert etag. Resolves integrations#3694 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d2d0ab8 to
882d74a
Compare
stevehipwell
left a comment
There was a problem hiding this comment.
Thanks for making the changes @benlangfeld. I've added some comments, but the changes look good in principal.
Functions that read storeEtag through an inline meta.(*Owner) assertion now take m any and assert once with meta, _ := m.(*Owner), which the forcetypeassert linter accepts. The etag sets inside the storeEtag checks use the if err := d.Set(...); err != nil idiom. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ping The repository read now returns an error from setting etag, like the other resources, rather than discarding it. The EMU group mapping reads the ETag header inside the storeEtag check instead of into a local beforehand, so it is only read and traced when it is stored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Direct regression tests are needed to verify that ETag storage remains enabled for legacy clients and disabled for new clients.
1 open issue
What changed in this PR
These provider review instructions are being used.
This PR addresses #3694 by preventing the new client's credential-dependent ETags from being written to Terraform state, reducing import and destroy plan differences under GitHub App authentication.
Changes:
- Derives
Owner.storeEtagfrom the legacy-client configuration. - Gates ETag writes across resources and data sources without changing schemas.
- Removes ETag presence assertions and import-verification exclusions from affected acceptance tests.
| File | Description |
|---|---|
github/resource_organization_block.go |
Gates organization-block ETag writes. |
github/resource_github_user_ssh_key.go |
Gates SSH-key ETag writes. |
github/resource_github_team.go |
Gates team lifecycle ETag writes. |
github/resource_github_team_test.go |
Removes ETag assertions and import exclusions. |
github/resource_github_team_sync_group_mapping.go |
Gates team-sync mapping ETag writes. |
github/resource_github_team_repository.go |
Gates team-repository ETag writes. |
github/resource_github_team_membership.go |
Gates team-membership ETag writes. |
github/resource_github_repository.go |
Gates repository ETag writes. |
github/resource_github_repository_ruleset.go |
Gates repository-ruleset ETag writes. |
github/resource_github_repository_ruleset_test.go |
Includes ETags in import verification. |
github/resource_github_repository_deployment_branch_policy.go |
Gates deployment-policy ETag writes. |
github/resource_github_repository_deployment_branch_policy_test.go |
Removes the ETag presence assertion. |
github/resource_github_repository_deploy_key.go |
Gates deploy-key ETag writes. |
github/resource_github_organization_webhook.go |
Gates organization-webhook ETag writes. |
github/resource_github_organization_webhook_test.go |
Includes ETags in import verification. |
github/resource_github_organization_ruleset.go |
Gates organization-ruleset ETag writes. |
github/resource_github_organization_ruleset_test.go |
Includes ETags in import verification. |
github/resource_github_membership.go |
Gates organization-membership ETag writes. |
github/resource_github_issue.go |
Gates issue ETag writes. |
github/resource_github_issue_test.go |
Includes ETags in import verification. |
github/resource_github_issue_label.go |
Gates issue-label ETag writes. |
github/resource_github_enterprise_actions_runner_group.go |
Gates enterprise runner-group ETag writes. |
github/resource_github_emu_group_mapping.go |
Gates EMU mapping ETag writes and associated logging. |
github/resource_github_emu_group_mapping_test.go |
Removes ETag assertions and import exclusions. |
github/resource_github_branch.go |
Gates branch ETag writes. |
github/resource_github_branch_protection_v3.go |
Gates branch-protection ETag writes. |
github/resource_github_branch_default.go |
Gates default-branch lifecycle and import ETag writes. |
github/resource_github_branch_default_test.go |
Removes ETag assertions and import exclusion. |
github/resource_github_actions_runner_group.go |
Gates organization runner-group ETag writes. |
github/provider.go |
Sets ETag storage mode from LegacyClient. |
github/data_source_github_ref.go |
Gates reference data-source ETag writes. |
github/data_source_github_membership.go |
Gates membership data-source ETag writes. |
github/data_source_github_membership_test.go |
Removes the ETag presence assertion. |
github/data_source_github_branch.go |
Gates branch data-source ETag writes. |
github/config.go |
Adds the Owner.storeEtag flag. |
Review effort: Balanced
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
robert-crandall
left a comment
There was a problem hiding this comment.
I checked the resource and data-source paths along with the Test_configureProviderMeta check and the ImportStateVerifyIgnore test cleanup, and everything looks good to me. Thank you for the contribution!
|
@deiga do you want to review this or are you happy for me to merge it now? |

Resolves #3694
Before the change?
etag.etagchanges from run to run. Import and destroy plans print every attribute, so two plans of the same change never match. The org-mode acceptance tests hit this (e.g.TestAccGithubRepository/imports_repositories_without_errorin run 39583616753), and it's why several tests passImportStateVerifyIgnore: []string{"etag"}.etagTransportturns it intoIf-None-Match). The new client revalidates through its transport cache, so withlegacy_client = falsethe attribute has no consumer.After the change?
Ownerhas astoreEtagfield, set fromLegacyClientinconfigureProviderMeta.d.Set("etag", …)is wrapped inif meta.storeEtag { … }(ormeta.(*Owner).storeEtagwhere the function has no local*Owner). The legacy client behaves exactly as before; the new client leavesetagunset. The schema is untouched.GITHUB_LEGACY_CLIENT: "false"in CI), so theetagentries inImportStateVerifyIgnoreare removed, as are the checks assertingetagis set, since with the new client it now isn't.make test) pass. I haven't run the acceptance tests locally against this revision.Pull request checklist
Does this introduce a breaking change?
Please see our docs on breaking changes to help!
etagis unset with the new client, which is opt-in (legacy_client = false); nothing in the provider reads it in that mode.🤖 Generated with Claude Code