From 3f4fc4e57536614b924699b54545694a5058f7bc Mon Sep 17 00:00:00 2001 From: Stoovon <10003354+stoovon@users.noreply.github.com> Date: Sun, 25 Sep 2022 22:53:54 +0100 Subject: [PATCH] feat: check teams Check team membership. Fixes #13 --- .github/CODEOWNERS | 2 +- index.js | 36 +++++++++++++++++++++++++++++++++--- index.test.js | 14 ++++++++++---- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ded93ab3..461b21d4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,7 +15,7 @@ packages/documentation/copy/ja/**/*.ts @sasurau4 @Quramy @Naturalclar @Takepepe # Collaborators for Portuguese Translation of the Website packages/playground-examples/copy/pt/**/*.md @khaosdoctor @danilofuchs @orta packages/playground-examples/copy/pt/**/*.ts @khaosdoctor @danilofuchs @orta -packages/tsconfig-reference/copy/pt/**/*.md @khaosdoctor @danilofuchs @orta +packages/tsconfig-reference/copy/pt/**/*.md @khaosdoctor @danilofuchs @orta @teamA packages/typescriptlang-org/src/copy/pt/**/*.ts @khaosdoctor @danilofuchs @orta packages/documentation/copy/pt/**/*.ts @khaosdoctor @danilofuchs @orta diff --git a/index.js b/index.js index 1d53ce31..62808b38 100644 --- a/index.js +++ b/index.js @@ -239,13 +239,23 @@ class Actor { function getFilesNotOwnedByCodeOwner(owner, files, cwd) { const filesWhichArentOwned = [] const codeowners = new Codeowners(cwd); + const octokit = getOctokit(process.env.GITHUB_TOKEN) for (const file of files) { const relative = file.startsWith("/") ? file.slice(1) : file let owners = codeowners.getOwner(relative); - if (!owners.includes(owner)) { - filesWhichArentOwned.push(file) + if (owners.includes(owner)) { + continue + } + + if (owners + .filter(owner => owner.startsWith("@")) + .some(teamowner => getTeamMembers(octokit, teamowner).then(result => result.includes(owner))) + ) { + continue } + + filesWhichArentOwned.push(file) } return filesWhichArentOwned @@ -263,7 +273,21 @@ function getFilesNotOwnedByCodeOwner(owner, files, cwd) { const codeowners = new Codeowners(cwd); const contents = readFileSync(codeowners.codeownersFilePath, "utf8").toLowerCase() - return contents.includes("@" + login.toLowerCase() + " ") || contents.includes("@" + login.toLowerCase() + "\n") + if (contents.includes("@" + login.toLowerCase() + " ") || contents.includes("@" + login.toLowerCase() + "\n")) { + return true + } + + const regex = /\@[a-zA-Z0-9]+\/[a-zA-Z0-9]+ /g; + const potentialTeams = contents.match(regex); + if (potentialTeams == null || potentialTeams.length == 0) { + return false + } + + const octokit = getOctokit(process.env.GITHUB_TOKEN) + + return potentialTeams.some(team => + getTeamMembers(octokit, team.replace(" ","")).then(result => result.includes(login.toLowerCase())) + ) } @@ -314,6 +338,12 @@ async function getPRChangedFiles(octokit, repoDeets, prNumber) { return fileStrings } +async function getTeamMembers(octokit, teamname) { + const components = teamname.replace("@","").split("/") + const teamMembers = await octokit.paginate('GET /orgs/:org/teams/:team/members', {org: components[0], team: components[1]}) + return teamMembers.map(teammember => teammember.login) +} + async function createOrAddLabel(octokit, repoDeets, labelConfig) { let label = null const existingLabels = await octokit.paginate('GET /repos/:owner/:repo/labels', { owner: repoDeets.owner, repo: repoDeets.repo }) diff --git a/index.test.js b/index.test.js index 0dcc2b50..eacbe80d 100644 --- a/index.test.js +++ b/index.test.js @@ -9,15 +9,16 @@ test("determine who owns a set of files", () => { }); test("real world", () => { + // Stoovon is a member of teamA. const changed = ["/packages/tsconfig-reference/copy/pt/options/files.md"]; const filesNotInCodeowners = findCodeOwnersForChangedFiles(changed, "."); - expect(filesNotInCodeowners.users).toEqual(["@khaosdoctor", "@danilofuchs", "@orta"]); + expect(filesNotInCodeowners.users).toEqual(["@khaosdoctor", "@danilofuchs", "@orta", "@stoovon"]); }); - +Or test("real world 2", () => { const changed = ["/packages/typescriptlang-org/src/copy/pt/index.ts", "/packages/typescriptlang-org/src/copy/pt/nav.ts"]; const filesNotInCodeowners = findCodeOwnersForChangedFiles(changed, "."); - expect(filesNotInCodeowners.users).toEqual(["@khaosdoctor", "@danilofuchs", "@orta"]); + expect(filesNotInCodeowners.users).toEqual(["@khaosdoctor", "@danilofuchs", "@orta", "@stoovon"]); }); test("real world with labels", () => { @@ -36,10 +37,15 @@ test("deciding if someone has access to merge", () => { }); describe(githubLoginIsInCodeowners, () => { - test("allows folks found in the codeowners", () => { + test("allows folks found in the codeowners", () => {orta const ortaIn = githubLoginIsInCodeowners("orta", "."); expect(ortaIn).toEqual(true); }); + test("allows folks in teams found in the codeowners", () => { + // Stoovon is a member of teamA. + const stoovonIn = githubLoginIsInCodeowners("stoovon", "."); + expect(stoovonIn).toEqual(true); + }); test("ignores case", () => { const ortaIn = githubLoginIsInCodeowners("OrTa", "."); expect(ortaIn).toEqual(true);