diff --git a/action.yml b/action.yml index f29a1a2a..887f9425 100644 --- a/action.yml +++ b/action.yml @@ -14,6 +14,9 @@ inputs: allowlist: description: "users in the allow list don't have to sign the CLA document" default: "" + exemptRepoOrgMembers: + description: "if `true`, members of the organization to which the repository belongs are automatically allowlisted" + default: false remote-repository-name: description: "provide the remote repository name where all the signatures should be stored" remote-organization-name: diff --git a/dist/index.js b/dist/index.js index 54d57014..4de3cea6 100644 --- a/dist/index.js +++ b/dist/index.js @@ -58,6 +58,29 @@ exports.checkAllowList = checkAllowList; "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -68,15 +91,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -const octokit_1 = __nccwpck_require__(3258); +const octokit = __importStar(__nccwpck_require__(3258)); const github_1 = __nccwpck_require__(5438); +const core = __importStar(__nccwpck_require__(2186)); function getCommitters() { return __awaiter(this, void 0, void 0, function* () { try { + let client = (octokit.isPersonalAccessTokenPresent()) ? octokit.getPATOctokit() : octokit.octokit; let committers = []; - let filteredCommitters = []; - let response = yield octokit_1.octokit.graphql(` + let desiredSignatories = []; + let response = yield client.graphql(` query($owner:String! $name:String! $number:Int! $cursor:String!){ + organization(login: $owner) { + membersWithRole(first: 100) { + edges { + node { + login + } + } + } + } repository(owner: $owner, name: $name) { pullRequest(number: $number) { commits(first: 100, after: $cursor) { @@ -91,6 +125,11 @@ function getCommitters() { id databaseId login + organizations(first: 100) { + nodes { + login + } + } } } committer { @@ -99,6 +138,11 @@ function getCommitters() { id databaseId login + organizations(first: 100) { + nodes { + login + } + } } } } @@ -119,11 +163,15 @@ function getCommitters() { cursor: '' }); response.repository.pullRequest.commits.edges.forEach(edge => { + core.debug(JSON.stringify(response.organization, undefined, 2)); const committer = extractUserFromCommit(edge.node.commit); let user = { name: committer.login || committer.name, id: committer.databaseId || '', - pullRequestNo: github_1.context.issue.number + pullRequestNo: github_1.context.issue.number, + orgLogins: committer.organizations.nodes.map(org => { + return org.login; + }) }; if (committers.length === 0 || committers.map((c) => { return c.name; @@ -131,10 +179,29 @@ function getCommitters() { committers.push(user); } }); - filteredCommitters = committers.filter((committer) => { - return committer.id !== 41898282; + desiredSignatories = committers.filter((committer) => { + if (committer.id === 41898282) { // Filter this one out. + return false; + } + if (core.getInput('exemptRepoOrgMembers')) { + // The `exemptRepoOrgMembers` input determines whether + // we automatically "allowlist" the members of the org + // owning the repository we are working in. If so, we + // can filter those committers here, thus allowing them + // to bypass the check informing them they need to sign + // the CLA. + let members = response.organization.membersWithRole.edges.map(edge => { + return edge.node.login; + }); + core.debug("Filtering based on these members:"); + core.debug(JSON.stringify(members, undefined, 2)); + core.debug("Current committer name to check for filtering:"); + return !members.includes(committer.name); // Negate so `includes()` filters out, not in. + } + return true; }); - return filteredCommitters; + core.debug(JSON.stringify(desiredSignatories, undefined, 2)); + return desiredSignatories; } catch (e) { throw new Error(`graphql call to get the committers details failed: ${e}`); @@ -657,6 +724,7 @@ var __importStar = (this && this.__importStar) || function (mod) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.commentContent = void 0; const input = __importStar(__nccwpck_require__(3611)); +const pr_sign_comment_1 = __nccwpck_require__(6718); function commentContent(signed, committerMap) { // using a `string` true or false purposely as github action input cannot have a boolean value if (input.getUseDcoFlag() == 'true') { @@ -715,7 +783,7 @@ function cla(signed, committerMap) { let lineOne = (input.getCustomNotSignedPrComment() || `
Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that $you sign our [Contributor License Agreement](${input.getPathToDocument()}) before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.
`).replace('$you', you); let text = `${lineOne} - - - - ${input.getCustomPrSignComment() || "I have read the CLA Document and I hereby sign the CLA"} + ${(0, pr_sign_comment_1.getPrSignComment)()} - - - `; if (committersCount > 1 && committerMap && committerMap.signed && committerMap.notSigned) { @@ -1089,6 +1157,45 @@ const lockPullRequestAfterMerge = () => core.getInput('lock-pullrequest-aftermer exports.lockPullRequestAfterMerge = lockPullRequestAfterMerge; +/***/ }), + +/***/ 6718: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getPrSignComment = void 0; +const input = __importStar(__nccwpck_require__(3611)); +function getPrSignComment() { + return input.getCustomPrSignComment() || "I have read the CLA Document and I hereby sign the CLA"; +} +exports.getPrSignComment = getPrSignComment; + + /***/ }), /***/ 7351: diff --git a/src/checkAllowList.ts b/src/checkAllowList.ts index 9bfe77b0..01e72b06 100644 --- a/src/checkAllowList.ts +++ b/src/checkAllowList.ts @@ -1,10 +1,9 @@ +import * as core from '@actions/core' import { CommittersDetails } from './interfaces' import * as _ from 'lodash' import * as input from './shared/getInputs' - - function isUserNotInAllowList(committer) { const allowListPatterns: string[] = input.getAllowListItem().split(',') @@ -23,4 +22,4 @@ function isUserNotInAllowList(committer) { export function checkAllowList(committers: CommittersDetails[]): CommittersDetails[] { const committersAfterAllowListCheck: CommittersDetails[] = committers.filter(committer => committer && !(isUserNotInAllowList !== undefined && isUserNotInAllowList(committer.name))) return committersAfterAllowListCheck -} \ No newline at end of file +} diff --git a/src/graphql.ts b/src/graphql.ts index 21b8f253..fad326b5 100644 --- a/src/graphql.ts +++ b/src/graphql.ts @@ -1,15 +1,24 @@ -import { octokit } from './octokit' +import * as octokit from './octokit' import { context } from '@actions/github' import { CommittersDetails } from './interfaces' - - +import * as core from '@actions/core' export default async function getCommitters(): Promise { try { + let client = (octokit.isPersonalAccessTokenPresent()) ? octokit.getPATOctokit() : octokit.octokit let committers: CommittersDetails[] = [] - let filteredCommitters: CommittersDetails[] = [] - let response: any = await octokit.graphql(` + let desiredSignatories: CommittersDetails[] = [] + let response: any = await client.graphql(` query($owner:String! $name:String! $number:Int! $cursor:String!){ + organization(login: $owner) { + membersWithRole(first: 100) { + edges { + node { + login + } + } + } + } repository(owner: $owner, name: $name) { pullRequest(number: $number) { commits(first: 100, after: $cursor) { @@ -24,6 +33,11 @@ export default async function getCommitters(): Promise { id databaseId login + organizations(first: 100) { + nodes { + login + } + } } } committer { @@ -32,6 +46,11 @@ export default async function getCommitters(): Promise { id databaseId login + organizations(first: 100) { + nodes { + login + } + } } } } @@ -52,11 +71,15 @@ export default async function getCommitters(): Promise { cursor: '' }) response.repository.pullRequest.commits.edges.forEach(edge => { + core.debug(JSON.stringify(response.organization, undefined, 2)) const committer = extractUserFromCommit(edge.node.commit) let user = { name: committer.login || committer.name, id: committer.databaseId || '', - pullRequestNo: context.issue.number + pullRequestNo: context.issue.number, + orgLogins: committer.organizations.nodes.map(org => { + return org.login + }) } if (committers.length === 0 || committers.map((c) => { return c.name @@ -64,14 +87,33 @@ export default async function getCommitters(): Promise { committers.push(user) } }) - filteredCommitters = committers.filter((committer) => { - return committer.id !== 41898282 - }) - return filteredCommitters + desiredSignatories = committers.filter((committer) => { + if (committer.id === 41898282) { // Filter this one out. + return false + } + if (core.getInput('exemptRepoOrgMembers')) { + // The `exemptRepoOrgMembers` input determines whether + // we automatically "allowlist" the members of the org + // owning the repository we are working in. If so, we + // can filter those committers here, thus allowing them + // to bypass the check informing them they need to sign + // the CLA. + let members = response.organization.membersWithRole.edges.map(edge => { + return edge.node.login + }) + core.debug("Filtering based on these members:") + core.debug(JSON.stringify(members, undefined, 2)) + core.debug("Current committer name to check for filtering:") + return ! members.includes(committer.name) // Negate so `includes()` filters out, not in. + } + + return true + }) + core.debug(JSON.stringify(desiredSignatories, undefined, 2)) + return desiredSignatories } catch (e) { throw new Error(`graphql call to get the committers details failed: ${e}`) } - } -const extractUserFromCommit = (commit) => commit.author.user || commit.committer.user || commit.author || commit.committer \ No newline at end of file +const extractUserFromCommit = (commit) => commit.author.user || commit.committer.user || commit.author || commit.committer diff --git a/src/interfaces.ts b/src/interfaces.ts index 07da19de..f4bb3eb2 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -22,6 +22,7 @@ export interface CommittersDetails { comment_id?: number, body?: string, repoId?: string + orgLogins?: string[] } export interface LabelName { current_name: string, @@ -38,4 +39,4 @@ export interface CommittersCommentDetails { export interface ClafileContentAndSha { claFileContent: any, sha: string -} \ No newline at end of file +}