Modernize security manager team handling (incorporate PR #1010) - #1045
Merged
decyjphr merged 5 commits intoAug 4, 2026
Merged
Conversation
Bring PR #1010 (bug/issue-903) into this branch. The comprehensive teams.js here still used the deprecated GET /orgs/{org}/security-managers endpoint, so the security-manager modernization was not yet incorporated. - teams.js: identify security manager teams via the organization roles API (GET /orgs/{org}/organization-roles and .../{role_id}/teams) instead of the deprecated security-managers endpoint. Adapted to this branch's non-`rest` Octokit client convention (this.github.repos/teams.*). - Add team name/slug and role name normalization helpers so configured names match existing slugs without add/remove churn. - Add skipTeamDeletion guard: if security manager discovery fails, keep repository teams unchanged instead of deleting them. - app.yml already grants organization_custom_roles (write), so no permission change needed; document the org "Custom organization roles" permission in docs/deploy.md. - Port unit + integration test coverage for the new behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 64c2ef45-ed6c-4756-9ec6-58a35797dc0e
checkSecurityManager() only filtered security manager teams out of the existing repo team list, which suppressed deletes/updates when they were absent from config. But a config entry naming a security manager team then looked "missing" to Diffable.sync(), so add()/addOrUpdateRepoPermissionsInOrg still fired — letting this plugin modify security manager teams, contrary to the "should not be handled here" intent. Persist the discovered security manager team identifiers on the instance and no-op add(), update(), and remove() when the configured team matches them. In nop mode an INFO command is emitted so PR reviewers see the skip. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 64c2ef45-ed6c-4756-9ec6-58a35797dc0e
…ncorporate-pr-1010-security-manager # Conflicts: # test/unit/lib/plugins/teams.test.js
Contributor
There was a problem hiding this comment.
Pull request overview
Updates the teams plugin to identify and protect Security Manager teams using the organization roles API (instead of the deprecated security-managers endpoint), adds normalization to prevent add/remove churn, and expands unit/integration coverage to validate the new behavior and safety guardrails.
Changes:
- Modernize Security Manager discovery via
GET /orgs/{org}/organization-rolesand.../{role_id}/teams, persisting discovered identifiers and no-op’ing config-driven management for those teams. - Add normalization helpers for role/team identifiers and a deletion-suppression guard when discovery fails.
- Expand unit + integration tests and document the required “Custom organization roles: Read-only” org permission.
Show a summary per file
| File | Description |
|---|---|
| lib/plugins/teams.js | Switches Security Manager detection to org-roles API, adds normalization + skip logic, and prevents config from managing Security Manager teams. |
| test/unit/lib/plugins/teams.test.js | Adds focused unit coverage for Security Manager behavior and adjusts mocks for the new API usage. |
| test/integration/plugins/teams.test.js | Extends integration test nocks to cover organization-roles API calls and Security Manager team presence. |
| docs/deploy.md | Documents the needed org permission for custom organization roles read access. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
The mock returned a bare string, but production Octokit's request.endpoint()
returns an object with url/body. NopCommand reads endpoint.url and
endpoint.body, so the string mock silently produced undefined values and
reduced test fidelity. Return { url, body } to match the real shape.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 64c2ef45-ed6c-4756-9ec6-58a35797dc0e
…overy failure When security-manager discovery fails, remove() sets skipTeamDeletion and returned a bare Promise.resolve() even in nop mode. Diffable.sync() pushed that undefined into the nop command list, hiding the fact that a deletion was intentionally skipped. Return an INFO NopCommand in nop mode so the dry-run output is accurate and no undefined entries accumulate. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 64c2ef45-ed6c-4756-9ec6-58a35797dc0e
Contributor
There was a problem hiding this comment.
Review details
Suppressed comments (1)
lib/plugins/teams.js:68
- Similar to the roles listing call above,
octokit.paginate()is used forGET /orgs/{org}/organization-roles/{role_id}/teamswithout a map function. The REST response is an object wrapper (e.g.{ total_count, teams: [...] }), so this may yield incorrect results and prevent populatingsecurityManagerTeamIdentifiers, undermining the skip/no-op logic for security manager teams.
this.log.debug(`Calling API to get security manager teams ${JSON.stringify(this.github.request.endpoint('GET /orgs/{org}/organization-roles/{role_id}/teams', params))} `)
const resp = await this.github.paginate('GET /orgs/{org}/organization-roles/{role_id}/teams', params)
this.log.debug(`Response from the call is ${JSON.stringify(resp)}`)
const securityManagerTeams = this.toArray(resp, 'teams')
const securityManagerTeamIdentifiers = new Set(securityManagerTeams.flatMap(team => [team.slug, team.name].map(name => this.normalizeTeamIdentifier(name))).filter(Boolean))
- Files reviewed: 4/4 changed files
- Comments generated: 1
- Review effort level: Lite
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.
Summary
Incorporates PR #1010 (
bug/issue-903) intoyadhav/fix-recent-issuesand fixes a gap in its approach.The comprehensive
teams.json this branch still used the deprecatedGET /orgs/{org}/security-managersendpoint (the roles API was only present as commented-out code), so PR #1010's security-manager modernization was not yet incorporated. This PR brings it in, adapted to this branch's Octokit client convention, and closes a follow-up correctness gap.Changes
lib/plugins/teams.jsGET /orgs/{org}/organization-roles+.../{role_id}/teams) instead of the deprecated security-managers endpoint.this.github.rest.*; this branch uses the non-restclient (this.github.repos/teams.*), so the port matches that convention.skipTeamDeletionguard: if security manager discovery fails, repository teams are kept unchanged rather than deleted.Follow-up fix: never manage security manager teams from config
checkSecurityManager()only filtered security manager teams out of the existing team list. A config entry naming a security manager team then looked "missing" toDiffable.sync(), soadd()/addOrUpdateRepoPermissionsInOrgwould still fire — contradicting the "should not be handled here" intent. Now the discovered identifiers are persisted on the instance, andadd(),update(), andremove()no-op for security manager teams (emitting anINFOcommand in nop mode so reviewers see the skip).Docs
docs/deploy.md: document the "Custom organization roles: Read-only" org permission. (app.ymlalready grantsorganization_custom_roles, so no permission change was needed.)Tests
Verification
test:unit). The added nock mocks mirror PR Bug/issue-903 #1010.find→checkSecurityManager(org-roles API) →add. Phase 2 (drift removal) is blocked only by the drift-actor token lacking team-repo admin permission on the EMU org — an environment limitation, not a code issue.