This is an automated email from the ASF dual-hosted git repository. potiuk pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/airflow-label-when-approved.git
commit 7872312da76508d29f98d4fa68843ea91754cc59 Author: Tobiasz Kędzierski <[email protected]> AuthorDate: Wed Oct 28 20:48:12 2020 +0100 Add triggering by workflow_run (#4) --- README.md | 54 +++++++++++++++++++--- action.yml | 3 ++ dist/index.js | 116 ++++++++++------------------------------------ src/main.ts | 145 +++++++++++++--------------------------------------------- 4 files changed, 106 insertions(+), 212 deletions(-) diff --git a/README.md b/README.md index a6d05d4..ef14fd3 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,9 @@ - [Outputs](#outputs) - [Examples](#examples) - [Workflow Run event](#workflow-run-event) - - [Development environment](#development-environment) - - [License](#license) + - [Pull Request Review event](#pull-request-review-event) +- [Development environment](#development-environment) +- [License](#license) <!-- END doctoc generated TOC please keep comment here to allow auto update --> @@ -31,6 +32,13 @@ The required input `require_committers_approval` says is approval can be done by or by anyone. It may be useful in repositories which requires committers approvals like [Apache Software Foundation](https://github.com/apache/) projects. +It can be used in workflows triggered by "pull_request_review" or "workflow_run". +When used on "pull_request_review" any workflows triggered from pull request created from fork will fail +due to insufficient permissions. Because of this support for "workflow_run" was added. +It should be triggered by workflows "pull_request_review" and requires additional input `pullRequestNumber`. +Pull request number can be obtained by using [potiuk/get-workflow-origin)](https://github.com/potiuk/get-workflow-origin) action (see example [workflow-run-event](#workflow-run-event)). + + # Inputs and outputs ## Inputs @@ -41,6 +49,7 @@ projects. | `label` | no | `Approved by committers` | Label to be added/removed to the Pull Request if approved/not approved | | `require_committers_approval` | no | `true` | Is approval from user with write permission required | | `comment` | no | `PR approved by at least one committer and no changes requested.` | Add optional comment to the PR when approved (requires label input to be set) | +| `pullRequestNumber` | no | `${{ steps.source-run-info.outputs.pullRequestNumber }}` | Pull request number if triggered by "worfklow_run" | ## Outputs @@ -55,6 +64,38 @@ projects. ### Workflow Run event ```yaml +name: "Label when approved" +on: + workflow_run: + workflows: ["Workflow triggered on pull_request_review"] + types: ['requested'] + +jobs: + + label-when-approved: + name: "Label when approved" + runs-on: ubuntu-latest + steps: + - name: "Get information about the original trigger of the run" + uses: potiuk/get-workflow-origin@v1_2 + id: source-run-info + with: + token: ${{ secrets.GITHUB_TOKEN }} + sourceRunId: ${{ github.event.workflow_run.id }} + - name: Label when approved by anyone + uses: TobKed/[email protected] + id: label-when-approved-by-anyone + with: + token: ${{ secrets.GITHUB_TOKEN }} + require_committers_approval: 'true' + label: 'Approved by committer' + comment: 'PR approved by at least one committer and no changes requested.' + pullRequestNumber: ${{ steps.source-run-info.outputs.pullRequestNumber }} +``` + +### Pull Request Review event + +```yaml name: Label when approved on: pull_request_review @@ -68,7 +109,7 @@ jobs: isApprovedByAnyone: ${{ steps.label-when-approved-by-anyone.outputs.isApproved }} steps: - name: Label when approved by commiters - uses: TobKed/[email protected] + uses: TobKed/[email protected] id: label-when-approved-by-commiters with: token: ${{ secrets.GITHUB_TOKEN }} @@ -76,19 +117,18 @@ jobs: require_committers_approval: 'true' comment: 'PR approved by at least one committer and no changes requested.' - name: Label when approved by anyone - uses: TobKed/[email protected] + uses: TobKed/[email protected] id: label-when-approved-by-anyone with: token: ${{ secrets.GITHUB_TOKEN }} ``` - -## Development environment +# Development environment It is highly recommended tu use [pre commit](https://pre-commit.com). The pre-commits installed via pre-commit tool handle automatically linting (including automated fixes) as well as building and packaging Javascript index.js from the main.ts Typescript code, so you do not have to run it yourself. -## License +# License [MIT License](LICENSE) covers the scripts and documentation in this project. diff --git a/action.yml b/action.yml index ae16b48..9027254 100644 --- a/action.yml +++ b/action.yml @@ -14,6 +14,9 @@ inputs: comment: description: 'Comment to be added to Pull Request if approved' required: false + pullRequestNumber: + description: 'Pull request number if triggered by "worfklow_run"' + required: false outputs: isApproved: description: 'Is Pull Reqeuest approved' diff --git a/dist/index.js b/dist/index.js index b9ac4e2..8e040f7 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1470,15 +1470,9 @@ function verboseOutput(name, value) { core.info(`Setting output: ${name}: ${value}`); core.setOutput(name, value); } -function getPullRequest(octokit, context, owner, repo) { +function getPullRequest(octokit, owner, repo, pullRequestNumber) { return __awaiter(this, void 0, void 0, function* () { - const pullRequestNumber = context.payload.pull_request - ? context.payload.pull_request.number - : null; - if (pullRequestNumber === null) { - throw Error(`Could not find PR number in context payload.`); - } - core.info(`pullRequestNumber: ${pullRequestNumber}`); + core.info(`Fetching pull request with number: ${pullRequestNumber}`); const pullRequest = yield octokit.pulls.get({ owner, repo, @@ -1597,92 +1591,49 @@ function addComment(octokit, owner, repo, pullRequestNumber, comment) { }); }); } -function getWorkflowId(octokit, runId, owner, repo) { - return __awaiter(this, void 0, void 0, function* () { - const reply = yield octokit.actions.getWorkflowRun({ - owner, - repo, - // eslint-disable-next-line @typescript-eslint/camelcase - run_id: runId - }); - core.info(`The source run ${runId} is in ${reply.data.workflow_url} workflow`); - const workflowIdString = reply.data.workflow_url.split('/').pop() || ''; - if (!(workflowIdString.length > 0)) { - throw new Error('Could not resolve workflow'); - } - return parseInt(workflowIdString); - }); -} -function getPrWorkflowRunsIds(octokit, owner, repo, branch, sha, skipRunId) { - return __awaiter(this, void 0, void 0, function* () { - const workflowRuns = yield octokit.actions.listRepoWorkflowRuns({ - owner, - repo, - branch, - event: 'pull_request', - status: 'completed', - // eslint-disable-next-line @typescript-eslint/camelcase - per_page: 100 - }); - // may be no need to rerun pending/queued runs - const filteredRunsIds = []; - const filteredWorklowRunsIds = []; - for (const workflowRun of workflowRuns.data.workflow_runs) { - const workflowId = parseInt(workflowRun.workflow_url.split('/').pop() || '0'); - if (workflowRun.head_sha === sha && - !filteredRunsIds.includes(workflowId) && - workflowId !== skipRunId) { - filteredRunsIds.push(workflowId); - filteredWorklowRunsIds.push(workflowRun.id); - } - } - return filteredWorklowRunsIds; - }); -} -function rerunWorkflows(octokit, owner, repo, runIds) { - return __awaiter(this, void 0, void 0, function* () { - core.info(`Rerun worklowws: ${runIds}`); - for (const runId of runIds) { - yield octokit.actions.reRunWorkflow({ - owner, - repo, - // eslint-disable-next-line @typescript-eslint/camelcase - run_id: runId - }); - } - }); -} -function printDebug(item, description) { +function printDebug(item, description = '') { return __awaiter(this, void 0, void 0, function* () { const itemJson = JSON.stringify(item); - core.info(`\n ######### ${description} ######### \n: ${itemJson}\n\n`); + core.debug(`\n ######### ${description} ######### \n: ${itemJson}\n\n`); }); } function run() { - var _a, _b; return __awaiter(this, void 0, void 0, function* () { const token = core.getInput('token', { required: true }); const userLabel = core.getInput('label') || 'not set'; const requireCommittersApproval = core.getInput('require_committers_approval') === 'true'; const comment = core.getInput('comment') || ''; + const pullRequestNumberInput = core.getInput('pullRequestNumber') || 'not set'; const octokit = new github.GitHub(token); const context = github.context; const repository = getRequiredEnv('GITHUB_REPOSITORY'); const eventName = getRequiredEnv('GITHUB_EVENT_NAME'); - const selfRunId = parseInt(getRequiredEnv('GITHUB_RUN_ID')); const [owner, repo] = repository.split('/'); - const selfWorkflowId = yield getWorkflowId(octokit, selfRunId, owner, repo); - const branch = (_a = context.payload.pull_request) === null || _a === void 0 ? void 0 : _a.head.ref; - const sha = (_b = context.payload.pull_request) === null || _b === void 0 ? void 0 : _b.head.sha; + let pullRequestNumber; core.info(`\n############### Set Label When Approved start ##################\n` + `label: "${userLabel}"\n` + `requireCommittersApproval: ${requireCommittersApproval}\n` + - `comment: ${comment}`); - if (eventName !== 'pull_request_review') { - throw Error(`This action is only useful in "pull_request_review" triggered runs and you used it in "${eventName}"`); + `comment: ${comment}\n` + + `pullRequestNumber: ${pullRequestNumberInput}`); + if (eventName === 'pull_request_review') { + pullRequestNumber = context.payload.pull_request + ? context.payload.pull_request.number + : undefined; + if (pullRequestNumber === undefined) { + throw Error(`Could not find PR number in context payload.`); + } + } + else if (eventName === 'workflow_run') { + if (pullRequestNumberInput === 'not set') { + throw Error(`If action is triggered by "workflow_run" then input "pullRequestNumber" is required.`); + } + pullRequestNumber = parseInt(pullRequestNumberInput); + } + else { + throw Error(`This action is only useful in "pull_request_review" or "workflow_run" triggered runs and you used it in "${eventName}"`); } // PULL REQUEST - const pullRequest = yield getPullRequest(octokit, context, owner, repo); + const pullRequest = yield getPullRequest(octokit, owner, repo, pullRequestNumber); // LABELS const labelNames = getPullRequestLabels(pullRequest); // REVIEWS @@ -1704,23 +1655,6 @@ function run() { yield removeLabel(octokit, owner, repo, pullRequest.number, userLabel); } } - //// Future option to rerun workflows if PR approved - //// Rerun workflow can have dynamic matrixes which check presence of labels - //// it is not possible to rerun successful runs - //// https://github.community/t/cannot-re-run-a-successful-workflow-run-using-the-rest-api/123661 - // - // if (isLabelShouldBeSet) { - // const prWorkflowRunsIds = await getPrWorkflowRunsIds( - // octokit, - // owner, - // repo, - // branch, - // sha, - // selfWorkflowId - // ) - // - // await rerunWorkflows(octokit, owner, repo, prWorkflowRunsIds) - // } // OUTPUT verboseOutput('isApproved', String(isApproved)); verboseOutput('labelSet', String(isLabelShouldBeSet)); diff --git a/src/main.ts b/src/main.ts index 12e0fea..f7b61d6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,6 @@ import * as github from '@actions/github' import * as core from '@actions/core' import * as rest from '@octokit/rest' -import {Context} from '@actions/github/lib/context' function getRequiredEnv(key: string): string { const value = process.env[key] @@ -19,17 +18,11 @@ function verboseOutput(name: string, value: string): void { async function getPullRequest( octokit: github.GitHub, - context: Context, owner: string, - repo: string + repo: string, + pullRequestNumber: number ): Promise<rest.PullsGetResponse> { - const pullRequestNumber = context.payload.pull_request - ? context.payload.pull_request.number - : null - if (pullRequestNumber === null) { - throw Error(`Could not find PR number in context payload.`) - } - core.info(`pullRequestNumber: ${pullRequestNumber}`) + core.info(`Fetching pull request with number: ${pullRequestNumber}`) const pullRequest = await octokit.pulls.get({ owner, repo, @@ -179,88 +172,12 @@ async function addComment( }) } -async function getWorkflowId( - octokit: github.GitHub, - runId: number, - owner: string, - repo: string -): Promise<number> { - const reply = await octokit.actions.getWorkflowRun({ - owner, - repo, - // eslint-disable-next-line @typescript-eslint/camelcase - run_id: runId - }) - core.info(`The source run ${runId} is in ${reply.data.workflow_url} workflow`) - const workflowIdString = reply.data.workflow_url.split('/').pop() || '' - if (!(workflowIdString.length > 0)) { - throw new Error('Could not resolve workflow') - } - return parseInt(workflowIdString) -} - -async function getPrWorkflowRunsIds( - octokit: github.GitHub, - owner: string, - repo: string, - branch: string, - sha: string, - skipRunId: number -): Promise<number[]> { - const workflowRuns = await octokit.actions.listRepoWorkflowRuns({ - owner, - repo, - branch, - event: 'pull_request', - status: 'completed', - // eslint-disable-next-line @typescript-eslint/camelcase - per_page: 100 - }) - // may be no need to rerun pending/queued runs - const filteredRunsIds: number[] = [] - const filteredWorklowRunsIds: number[] = [] - - for (const workflowRun of workflowRuns.data.workflow_runs) { - const workflowId = parseInt( - workflowRun.workflow_url.split('/').pop() || '0' - ) - - if ( - workflowRun.head_sha === sha && - !filteredRunsIds.includes(workflowId) && - workflowId !== skipRunId - ) { - filteredRunsIds.push(workflowId) - filteredWorklowRunsIds.push(workflowRun.id) - } - } - - return filteredWorklowRunsIds -} - -async function rerunWorkflows( - octokit: github.GitHub, - owner: string, - repo: string, - runIds: number[] -): Promise<void> { - core.info(`Rerun worklowws: ${runIds}`) - for (const runId of runIds) { - await octokit.actions.reRunWorkflow({ - owner, - repo, - // eslint-disable-next-line @typescript-eslint/camelcase - run_id: runId - }) - } -} - async function printDebug( item: object | string | boolean | number, - description: string + description: string = '' ): Promise<void> { const itemJson = JSON.stringify(item) - core.info(`\n ######### ${description} ######### \n: ${itemJson}\n\n`) + core.debug(`\n ######### ${description} ######### \n: ${itemJson}\n\n`) } async function run(): Promise<void> { @@ -269,31 +186,49 @@ async function run(): Promise<void> { const requireCommittersApproval = core.getInput('require_committers_approval') === 'true' const comment = core.getInput('comment') || '' + const pullRequestNumberInput = core.getInput('pullRequestNumber') || 'not set' const octokit = new github.GitHub(token) const context = github.context const repository = getRequiredEnv('GITHUB_REPOSITORY') const eventName = getRequiredEnv('GITHUB_EVENT_NAME') - const selfRunId = parseInt(getRequiredEnv('GITHUB_RUN_ID')) const [owner, repo] = repository.split('/') - const selfWorkflowId = await getWorkflowId(octokit, selfRunId, owner, repo) - const branch = context.payload.pull_request?.head.ref - const sha = context.payload.pull_request?.head.sha + let pullRequestNumber: number | undefined core.info( `\n############### Set Label When Approved start ##################\n` + `label: "${userLabel}"\n` + `requireCommittersApproval: ${requireCommittersApproval}\n` + - `comment: ${comment}` + `comment: ${comment}\n` + + `pullRequestNumber: ${pullRequestNumberInput}` ) - if (eventName !== 'pull_request_review') { + if (eventName === 'pull_request_review') { + pullRequestNumber = context.payload.pull_request + ? context.payload.pull_request.number + : undefined + if (pullRequestNumber === undefined) { + throw Error(`Could not find PR number in context payload.`) + } + } else if (eventName === 'workflow_run') { + if (pullRequestNumberInput === 'not set') { + throw Error( + `If action is triggered by "workflow_run" then input "pullRequestNumber" is required.` + ) + } + pullRequestNumber = parseInt(pullRequestNumberInput) + } else { throw Error( - `This action is only useful in "pull_request_review" triggered runs and you used it in "${eventName}"` + `This action is only useful in "pull_request_review" or "workflow_run" triggered runs and you used it in "${eventName}"` ) } // PULL REQUEST - const pullRequest = await getPullRequest(octokit, context, owner, repo) + const pullRequest = await getPullRequest( + octokit, + owner, + repo, + pullRequestNumber + ) // LABELS const labelNames = getPullRequestLabels(pullRequest) @@ -331,24 +266,6 @@ async function run(): Promise<void> { } } - //// Future option to rerun workflows if PR approved - //// Rerun workflow can have dynamic matrixes which check presence of labels - //// it is not possible to rerun successful runs - //// https://github.community/t/cannot-re-run-a-successful-workflow-run-using-the-rest-api/123661 - // - // if (isLabelShouldBeSet) { - // const prWorkflowRunsIds = await getPrWorkflowRunsIds( - // octokit, - // owner, - // repo, - // branch, - // sha, - // selfWorkflowId - // ) - // - // await rerunWorkflows(octokit, owner, repo, prWorkflowRunsIds) - // } - // OUTPUT verboseOutput('isApproved', String(isApproved)) verboseOutput('labelSet', String(isLabelShouldBeSet))
