-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(ci): housekeeping scripts & workflows related to PRs (#1023)
* chore: make lerna list explicit * chore: update codeowners to team * chore: updated CI scripts/workflow for merge events * fix: re-added commons * chore: update PR template * fix: repo name
- Loading branch information
1 parent
1a06fed
commit 6fe48ba
Showing
14 changed files
with
439 additions
and
95 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
# These owners will be the default owners for everything in | ||
# the repo. | ||
# TODO: revisit this list | ||
* @saragerion, @alan-churley, @heitorlessa | ||
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners | ||
|
||
* @awslabs/aws-lambda-powertools-typescript |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
module.exports = Object.freeze({ | ||
/** @type {string} */ | ||
// Values: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request | ||
"PR_ACTION": process.env.PR_ACTION?.replace(/"/g, '') || "", | ||
|
||
/** @type {string} */ | ||
"PR_AUTHOR": process.env.PR_AUTHOR?.replace(/"/g, '') || "", | ||
|
||
/** @type {string} */ | ||
"PR_BODY": process.env.PR_BODY || "", | ||
|
||
/** @type {string} */ | ||
"PR_TITLE": process.env.PR_TITLE || "", | ||
|
||
/** @type {number} */ | ||
"PR_NUMBER": process.env.PR_NUMBER || 0, | ||
|
||
/** @type {string} */ | ||
"PR_IS_MERGED": process.env.PR_IS_MERGED || "false", | ||
|
||
/** @type {string} */ | ||
"LABEL_BLOCK": "do-not-merge", | ||
|
||
/** @type {string} */ | ||
"LABEL_BLOCK_REASON": "need-issue", | ||
|
||
/** @type {string} */ | ||
"LABEL_PENDING_RELEASE": "pending-release", | ||
|
||
/** @type {string} */ | ||
"HANDLE_MAINTAINERS_TEAM": "@awslabs/aws-lambda-powertools-typescript", | ||
|
||
/** @type {string[]} */ | ||
"IGNORE_AUTHORS": ["dependabot[bot]"], | ||
|
||
/** @type {string[]} */ | ||
"AREAS": [ | ||
"tracer", | ||
"metrics", | ||
"logger", | ||
], | ||
}); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
module.exports = async ({github, context, core}) => { | ||
const fs = require('fs'); | ||
|
||
const workflowRunId = process.env.WORKFLOW_ID; | ||
core.info(`Listing artifacts for workflow run ${workflowRunId}`); | ||
|
||
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
run_id: workflowRunId, | ||
}); | ||
|
||
const matchArtifact = artifacts.data.artifacts.filter(artifact => artifact.name == "pr")[0]; | ||
|
||
core.info(`Downloading artifacts for workflow run ${workflowRunId}`); | ||
const artifact = await github.rest.actions.downloadArtifact({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
artifact_id: matchArtifact.id, | ||
archive_format: 'zip', | ||
}); | ||
|
||
core.info("Saving artifact found", artifact); | ||
|
||
fs.writeFileSync('pr.zip', Buffer.from(artifact.data)); | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
const { | ||
PR_ACTION, | ||
PR_AUTHOR, | ||
PR_BODY, | ||
PR_NUMBER, | ||
IGNORE_AUTHORS, | ||
LABEL_BLOCK, | ||
LABEL_BLOCK_REASON | ||
} = require("./constants"); | ||
|
||
module.exports = async ({github, context, core}) => { | ||
if (IGNORE_AUTHORS.includes(PR_AUTHOR)) { | ||
return core.notice("Author in IGNORE_AUTHORS list; skipping..."); | ||
} | ||
|
||
if (PR_ACTION != "opened") { | ||
return core.notice("Only newly open PRs are labelled to avoid spam; skipping"); | ||
} | ||
|
||
const RELATED_ISSUE_REGEX = /Issue number:[^\d\r\n]+(?<issue>\d+)/; | ||
const isMatch = RELATED_ISSUE_REGEX.exec(PR_BODY); | ||
if (isMatch == null) { | ||
core.info(`No related issue found, maybe the author didn't use the template but there is one.`); | ||
|
||
let msg = "No related issues found. Please ensure there is an open issue related to this change to avoid significant delays or closure."; | ||
await github.rest.issues.createComment({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
body: msg, | ||
issue_number: PR_NUMBER, | ||
}); | ||
|
||
return await github.rest.issues.addLabels({ | ||
issue_number: PR_NUMBER, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: [LABEL_BLOCK, LABEL_BLOCK_REASON] | ||
}); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
const { PR_NUMBER, PR_TITLE, AREAS } = require("./constants"); | ||
|
||
module.exports = async ({github, context, core}) => { | ||
const FEAT_REGEX = /feat(\((.+)\))?(:.+)/ | ||
const BUG_REGEX = /(fix|bug)(\((.+)\))?(:.+)/ | ||
const DOCS_REGEX = /(docs|doc)(\((.+)\))?(:.+)/ | ||
const CHORE_REGEX = /(chore)(\((.+)\))?(:.+)/ | ||
const DEPRECATED_REGEX = /(deprecated)(\((.+)\))?(:.+)/ | ||
const REFACTOR_REGEX = /(refactor)(\((.+)\))?(:.+)/ | ||
|
||
const labels = { | ||
"feature": FEAT_REGEX, | ||
"bug": BUG_REGEX, | ||
"documentation": DOCS_REGEX, | ||
"internal": CHORE_REGEX, | ||
"enhancement": REFACTOR_REGEX, | ||
"deprecated": DEPRECATED_REGEX, | ||
}; | ||
|
||
// Maintenance: We should keep track of modified PRs in case their titles change | ||
let miss = 0; | ||
try { | ||
for (const label in labels) { | ||
const matcher = new RegExp(labels[label]); | ||
const matches = matcher.exec(PR_TITLE); | ||
if (matches != null) { | ||
core.info(`Auto-labeling PR ${PR_NUMBER} with ${label}`); | ||
|
||
await github.rest.issues.addLabels({ | ||
issue_number: PR_NUMBER, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: [label] | ||
}); | ||
|
||
const area = matches[2]; // second capture group contains the area | ||
if (AREAS.indexOf(area) > -1) { | ||
core.info(`Auto-labeling PR ${PR_NUMBER} with area ${area}`); | ||
await github.rest.issues.addLabels({ | ||
issue_number: PR_NUMBER, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: [`area/${area}`], | ||
}); | ||
} else { | ||
core.debug(`'${PR_TITLE}' didn't match any known area.`); | ||
} | ||
|
||
return; | ||
} else { | ||
core.debug(`'${PR_TITLE}' didn't match '${label}' semantic.`); | ||
miss += 1; | ||
} | ||
} | ||
} finally { | ||
if (miss == Object.keys(labels).length) { | ||
core.notice(`PR ${PR_NUMBER} title '${PR_TITLE}' doesn't follow semantic titles; skipping...`); | ||
} | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
const { | ||
PR_AUTHOR, | ||
PR_BODY, | ||
PR_NUMBER, | ||
IGNORE_AUTHORS, | ||
LABEL_PENDING_RELEASE, | ||
HANDLE_MAINTAINERS_TEAM, | ||
PR_IS_MERGED, | ||
} = require("./constants") | ||
|
||
module.exports = async ({github, context, core}) => { | ||
if (IGNORE_AUTHORS.includes(PR_AUTHOR)) { | ||
return core.notice("Author in IGNORE_AUTHORS list; skipping..."); | ||
} | ||
|
||
if (PR_IS_MERGED == "false") { | ||
return core.notice("Only merged PRs to avoid spam; skipping"); | ||
} | ||
|
||
const RELATED_ISSUE_REGEX = /Issue number:[^\d\r\n]+(?<issue>\d+)/; | ||
|
||
const isMatch = RELATED_ISSUE_REGEX.exec(PR_BODY); | ||
|
||
try { | ||
if (!isMatch) { | ||
core.setFailed(`Unable to find related issue for PR number ${PR_NUMBER}.\n\n Body details: ${PR_BODY}`); | ||
return await github.rest.issues.createComment({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
body: `${HANDLE_MAINTAINERS_TEAM} No related issues found. Please ensure '${LABEL_PENDING_RELEASE}' label is applied before releasing.`, | ||
issue_number: PR_NUMBER, | ||
}); | ||
} | ||
} catch (error) { | ||
core.setFailed(`Unable to create comment on PR number ${PR_NUMBER}.\n\n Error details: ${error}`); | ||
throw new Error(error); | ||
} | ||
|
||
const { groups: {issue} } = isMatch; | ||
|
||
try { | ||
core.info(`Auto-labeling related issue ${issue} for release`) | ||
await github.rest.issues.addLabels({ | ||
issue_number: issue, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: [LABEL_PENDING_RELEASE] | ||
}); | ||
} catch (error) { | ||
core.setFailed(`Is this issue number (${issue}) valid? Perhaps a discussion?`); | ||
throw new Error(error); | ||
} | ||
|
||
const { groups: {relatedIssueNumber} } = isMatch; | ||
|
||
core.info(`Auto-labeling related issue ${relatedIssueNumber} for release`); | ||
return await github.rest.issues.addLabels({ | ||
issue_number: relatedIssueNumber, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: [relatedIssueNumber] | ||
}); | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module.exports = async ({context, core}) => { | ||
const fs = require('fs'); | ||
const filename = "pr.txt"; | ||
|
||
try { | ||
fs.writeFileSync(`./${filename}`, JSON.stringify(context.payload)); | ||
|
||
return `PR successfully saved ${filename}`; | ||
} catch (err) { | ||
core.setFailed("Failed to save PR details"); | ||
console.error(err); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,38 @@ | ||
name: Label PR based on title | ||
|
||
# pull_request_target event sends an admin GH token to forks | ||
# this however depends on another workflow so it all runs within the base repo safely | ||
# "Record PR number" workflow safely captures PR title and number | ||
# This workflow uses this information to label the PR based on its semantic title | ||
on: | ||
workflow_run: | ||
workflows: ["Record PR number"] | ||
workflows: ["Record PR details"] | ||
types: | ||
- completed | ||
|
||
jobs: | ||
label_pr: | ||
runs-on: ubuntu-latest | ||
get_pr_details: | ||
# Guardrails to only ever run if PR recording workflow was indeed | ||
# run in a PR event and ran successfully | ||
if: > | ||
${{ github.event.workflow_run.event == 'pull_request' && | ||
github.event.workflow_run.conclusion == 'success' }} | ||
if: ${{ github.event.workflow_run.conclusion == 'success' }} | ||
uses: ./.github/workflows/reusable_export_pr_details.yml | ||
with: | ||
record_pr_workflow_id: ${{ github.event.workflow_run.id }} | ||
workflow_origin: ${{ github.event.repository.full_name }} | ||
secrets: | ||
token: ${{ secrets.GITHUB_TOKEN }} | ||
label_pr: | ||
needs: get_pr_details | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: 'Download artifact' | ||
uses: actions/github-script@v6 | ||
# For security, we only download artifacts tied to the successful PR recording workflow | ||
with: | ||
script: | | ||
const fs = require('fs'); | ||
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
run_id: ${{github.event.workflow_run.id }}, | ||
}); | ||
const matchArtifact = artifacts.data.artifacts.filter(artifact => artifact.name == "pr")[0]; | ||
const artifact = await github.rest.actions.downloadArtifact({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
artifact_id: matchArtifact.id, | ||
archive_format: 'zip', | ||
}); | ||
fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(artifact.data)); | ||
# NodeJS standard library doesn't provide ZIP capabilities; use system `unzip` command instead | ||
- run: unzip pr.zip | ||
|
||
- name: 'Label PR based on title' | ||
- name: Checkout repository | ||
uses: actions/checkout@v3 | ||
- name: "Label PR based on title" | ||
uses: actions/github-script@v6 | ||
env: | ||
PR_NUMBER: ${{ needs.get_pr_details.outputs.prNumber }} | ||
PR_TITLE: ${{ needs.get_pr_details.outputs.prTitle }} | ||
with: | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
# This safely runs in our base repo, not on fork | ||
# thus allowing us to provide a write access token to label based on PR title | ||
# and label PR based on semantic title accordingly | ||
script: | | ||
const fs = require('fs'); | ||
const pr_number = Number(fs.readFileSync('./number')); | ||
const pr_title = fs.readFileSync('./title', 'utf-8').trim(); | ||
const FEAT_REGEX = /feat(\((\w+)\))?(\:.+)/ | ||
const BUG_REGEX = /(fix|bug)(\((\w+)\))?(\:.+)/ | ||
const DOCS_REGEX = /(docs|doc)(\((\w+)\))?(\:.+)/ | ||
const CHORE_REGEX = /(chore)(\((\w+)\))?(\:.+)/ | ||
const DEPRECATED_REGEX = /(deprecated)(\((\w+)\))?(\:.+)/ | ||
const REFACTOR_REGEX = /(refactor)(\((\w+)\))?(\:.+)/ | ||
const labels = { | ||
"feature": FEAT_REGEX, | ||
"bug": BUG_REGEX, | ||
"documentation": DOCS_REGEX, | ||
"internal": CHORE_REGEX, | ||
"enhancement": REFACTOR_REGEX, | ||
"deprecated": DEPRECATED_REGEX, | ||
} | ||
for (const label in labels) { | ||
const matcher = new RegExp(labels[label]) | ||
const isMatch = matcher.exec(pr_title) | ||
if (isMatch != null) { | ||
console.info(`Auto-labeling PR ${pr_number} with ${label}`) | ||
await github.rest.issues.addLabels({ | ||
issue_number: pr_number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: [label] | ||
}) | ||
break | ||
} | ||
} | ||
const script = require('.github/scripts/label_pr_based_on_title.js') | ||
await script({github, context, core}) |
Oops, something went wrong.