fix: read immediate github_team_members only, not child-team members#3498
Draft
robert-crandall wants to merge 2 commits into
Draft
fix: read immediate github_team_members only, not child-team members#3498robert-crandall wants to merge 2 commits into
robert-crandall wants to merge 2 commits into
Conversation
PR #3461 switched the github_team_members Read from a GraphQL query (members with membership: IMMEDIATE) to the REST ListTeamMembers endpoint. That endpoint always includes members inherited from child teams, so organizations using nested teams would see inherited members as perpetual drift. Restore the GraphQL IMMEDIATE query for the member list, which returns only direct members. The out-of-band-deletion handling added in #3461 is preserved by resolving the team via getTeam first and removing the resource from state on a 404. Create, Update, and Delete (including the 404 skip-on-delete fix) are unchanged. Fixes #3497 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
👋 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. |
Collaborator
|
@robert-crandall I think the first thing to do here is to add a test for the regression, I'll take a look at the tests and open a PR to modernize the existing ones and add a test for ignoring child team members. |
deiga
requested changes
Jun 19, 2026
deiga
left a comment
Collaborator
There was a problem hiding this comment.
Good catch! Do we have a regression test for this?
| teamSlug := team.GetSlug() | ||
|
|
||
| var teamMembersAndMaintainers []any | ||
| log.Printf("[DEBUG] Reading team members: %s", teamIdString) |
Collaborator
There was a problem hiding this comment.
Let's refactor all touched log.* lines to tflog if feasible
Suggested change
| log.Printf("[DEBUG] Reading team members: %s", teamIdString) | |
| tflog.Debug(ctx, "Reading team members", map[string]any{"teamID": teamIdString}) |
Convert the log.Printf calls in the Read function (the function this PR rewrites) to structured tflog.Debug, matching the tflog.Info calls already used in this file. Read has a context available, so the conversion is straightforward. The log.Printf calls in Create/Update/Delete are left as-is since this PR does not touch those functions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3497
Before the change?
PR #3461 switched the
github_team_membersRead from a GraphQL query (members(membership: IMMEDIATE)) to the RESTListTeamMembersendpoint (ListTeamMembersByIDIter/ListTeamMembersBySlugIter).Those two are not equivalent. The REST list-members endpoint always includes members inherited from child teams - this is documented ("Team members will include the members of child teams") and confirmed in the
github/githubimplementation, which builds the member scope with a hard-codedmembership: "all"and exposes only arolefilter (no immediate-only option). The old GraphQLIMMEDIATEreturned direct members only.Result: for organizations that use nested (parent/child) teams, Read pulled in members inherited from child teams that the user never configured. Since
github_team_membersmanages the member set authoritatively, that shows up as perpetual drift, and an apply could try to "remove" inherited members.After the change?
Read goes back to the GraphQL
members(membership: IMMEDIATE)query, so only direct members of the team are returned. Flat team structures are unaffected; nested-team orgs no longer see inherited members as drift.The useful parts of #3461 are kept:
getTeamfirst; a 404 there means the team is gone, so wed.SetId("")and return cleanly. This covers both id-based and slug-basedteam_id.Username and role are still lowercased on read (matching #3461's released behavior), so this PR changes only the member scope - immediate vs. all - and nothing about casing.
How I verified it
go build ./github/- passes.go vet ./github/- passes.gofmt -lon the changed file - clean.golangci-lint run ./github/- 0 issues.go test ./github/ -run TestAccGithubTeamMembers- compiles and skips cleanly without org credentials.github/githubmonolith: theteams/list-members-in-orgendpoint constructs its scope withmembership: "all"(valid values areimmediate/child_team/all), which is what makes the REST path include child-team members.TeamMembershipTypehasIMMEDIATE(alongsideCHILD_TEAM/ALL);Team.membersacceptsmembership/first/after;TeamMemberEdgehasnode(User) +role;TeamMemberConnectionhasedges+pageInfo;User.login,PageInfo.endCursor/hasNextPage, andTeamMemberRole={MEMBER, MAINTAINER}all exist.integrations/terraform-provider-core-maintainers): 5 edges, everyloginpopulated,roleMAINTAINER ->maintainer, correctpageInfo, zero query errors.membership: ALL) and GraphQLIMMEDIATEdiverge:integrations/hubbersreports IMMEDIATE=14 vs ALL=40 (26 inherited child-team members), andgithub/salesreports IMMEDIATE=144 vs ALL=744 (600 inherited). fix: skip github_team_members deletion when parent team is already gone #3461's REST Read would have surfaced those inherited members as drift; the restored GraphQLIMMEDIATEquery reports only the directly-configured members.What I did NOT verify
TF_ACC=1) - I don't have org credentials in this environment.getTeam-404 -> remove-from-state path and should pass, but I have not run it against a live org.GH_TEST_ORG_USER. I'd rather flag this than add a test that can't actually demonstrate the fix. Open to adding one if there's an agreed fixture/second-user convention.Marking this as a draft until the acceptance path can be run against a real org.
Does this introduce a breaking change?