-
Notifications
You must be signed in to change notification settings - Fork 218
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementation of project board automations (#3375)
Co-authored-by: sarayourfriend <24264157+sarayourfriend@users.noreply.github.com>
- Loading branch information
1 parent
f37a0cd
commit 8d8d4ea
Showing
14 changed files
with
828 additions
and
88 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
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 @@ | ||
# Runs all automations related to issue events. | ||
# | ||
# See `pr_automations_init.yml` and `pr_automations.yml` for the corresponding | ||
# implementation for PRs. | ||
name: Issue automations | ||
|
||
on: | ||
issues: | ||
types: | ||
- opened | ||
- reopened | ||
- closed | ||
- assigned | ||
- labeled | ||
- unlabeled | ||
|
||
jobs: | ||
run: | ||
name: Perform issue automations | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Setup CI env | ||
uses: ./.github/actions/setup-env | ||
with: | ||
setup_python: false | ||
install_recipe: node-install | ||
|
||
- name: Perform issue automations | ||
uses: actions/github-script@v7 | ||
env: | ||
EVENT_NAME: ${{ github.event_name }} | ||
EVENT_ACTION: ${{ github.event.action }} | ||
with: | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
script: | | ||
const { main } = await import('./automations/js/project_automation/issues.mjs') | ||
await main(github, context) |
This file was deleted.
Oops, something went wrong.
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,80 @@ | ||
# Runs all automations related to PR events. | ||
# | ||
# See `issue_automations.yml` for the corresponding implementation for issues. | ||
# | ||
# The automations for PR events are a little more complex than those for issues | ||
# because PRs are a less secure environment. To avoid leaking secrets, we need | ||
# to run automations with code as it appears on `main`. | ||
# | ||
# `pull_request_target` serves this purpose but there is no corresponding | ||
# `_target` version for `pull_request_review`. So we take this roundabout | ||
# approach: | ||
# | ||
# This workflow waits for the `pr_automations_init.yml` workflow to complete and | ||
# then uses its exports to run automations from main, with access to secrets. | ||
# | ||
# ...continued from `pr_automations_init.yml` | ||
# | ||
# 4. This workflow runs after `pr_automations_init.yml` workflow completes. | ||
# 5. It downloads the artifacts from that workflow run. | ||
# 6. It extracts the JSON file from the ZIP to `/tmp`. | ||
# 7. It runs the automations as a script, which can access secrets. | ||
|
||
name: PR automations | ||
|
||
on: | ||
workflow_run: | ||
workflows: | ||
- PR automations init | ||
types: | ||
- completed | ||
|
||
jobs: | ||
run: | ||
name: Perform PR automations | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Setup CI env | ||
uses: ./.github/actions/setup-env | ||
with: | ||
setup_python: false | ||
install_recipe: node-install | ||
|
||
# This step was copied from the GitHub docs. | ||
# Ref: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow | ||
- name: Download artifact | ||
uses: actions/github-script@v7 | ||
with: | ||
script: | | ||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
run_id: context.payload.workflow_run.id, | ||
}); | ||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { | ||
return artifact.name == "event_info" | ||
})[0]; | ||
let download = await github.rest.actions.downloadArtifact({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
artifact_id: matchArtifact.id, | ||
archive_format: 'zip', | ||
}); | ||
let fs = require('fs'); | ||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/event_info.zip`, Buffer.from(download.data)); | ||
- name: Unzip artifact | ||
run: | | ||
unzip event_info.zip | ||
mv event.json /tmp/event.json | ||
- name: Perform PR automations | ||
uses: actions/github-script@v7 | ||
with: | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
script: | | ||
const { main } = await import('./automations/js/project_automation/prs.mjs') | ||
await main(github) |
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,50 @@ | ||
# Initialises all automations related to PR events. | ||
# | ||
# See `issue_automations.yml` for the corresponding implementation for issues. | ||
# | ||
# The automations for PR events are a little more complex than those for issues | ||
# because PRs are a less secure environment. To avoid leaking secrets, we need | ||
# to run automations with code as it appears on `main`. | ||
# | ||
# `pull_request_target` serves this purpose but there is no corresponding | ||
# `_target` version for `pull_request_review`. So we take this roundabout | ||
# approach: | ||
# | ||
# 1. This workflow runs for the events and their subtypes we are interested in. | ||
# 2. It saves the event name, action and PR node ID to a JSON file. | ||
# 3. It uploads the JSON file as an artifact. | ||
# 4. Its completion triggers the `pr_automations.yml` workflow. | ||
# | ||
# continued in `pr_automations.yml`... | ||
|
||
name: PR automations init | ||
|
||
on: | ||
pull_request: | ||
types: | ||
- opened | ||
- reopened | ||
- edited | ||
- converted_to_draft | ||
- ready_for_review | ||
- closed | ||
pull_request_review: | ||
|
||
jobs: | ||
run: | ||
name: Save event info | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Save event info | ||
run: | | ||
echo '{"eventName": "'"$EVENT_NAME"'", "eventAction": "'"$EVENT_ACTION"'", "prNodeId": "'"$PR_NODE_ID"'"}' > /tmp/event.json | ||
env: | ||
EVENT_NAME: ${{ github.event_name }} | ||
EVENT_ACTION: ${{ github.event.action }} | ||
PR_NODE_ID: ${{ github.event.pull_request.node_id }} | ||
|
||
- name: Upload event info as artifact | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: event_info | ||
path: /tmp/event.json |
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,91 @@ | ||
import { getBoard } from '../utils/projects.mjs' | ||
|
||
/** | ||
* Set the "Priority" custom field based on the issue's labels. Also move | ||
* the card for critical issues directly to the "📅 To Do" column. | ||
* | ||
* @param issue {import('@octokit/rest')} | ||
* @param board {import('../utils/projects.mjs').Project} | ||
* @param card {import('../utils/projects.mjs').Card} | ||
*/ | ||
async function syncPriority(issue, board, card) { | ||
const priority = issue.labels.find((label) => | ||
label.name.includes('priority') | ||
)?.name | ||
if (priority) { | ||
await board.setCustomChoiceField(card.id, 'Priority', priority) | ||
} | ||
if (priority === '🟥 priority: critical') { | ||
await board.moveCard(card.id, board.columns.ToDo) | ||
} | ||
} | ||
|
||
/** | ||
* This is the entrypoint of the script. | ||
* | ||
* @param octokit {import('@octokit/rest').Octokit} the Octokit instance to use | ||
* @param context {import('@actions/github').context} info about the current event | ||
*/ | ||
export const main = async (octokit, context) => { | ||
const { EVENT_ACTION: eventAction } = process.env | ||
|
||
const issue = context.payload.issue | ||
const label = context.payload.label | ||
|
||
if (issue.labels.some((label) => label.name === '🧭 project: thread')) { | ||
// Do not add project threads to the Backlog board. | ||
process.exit(0) | ||
} | ||
|
||
const backlogBoard = await getBoard(octokit, 'Backlog') | ||
|
||
// Create new, or get the existing, card for the current issue. | ||
const card = await backlogBoard.addCard(issue.node_id) | ||
|
||
switch (eventAction) { | ||
case 'opened': | ||
case 'reopened': { | ||
if (issue.labels.some((label) => label.name === '⛔ status: blocked')) { | ||
await backlogBoard.moveCard(card.id, backlogBoard.columns.Blocked) | ||
} else { | ||
await backlogBoard.moveCard(card.id, backlogBoard.columns.Backlog) | ||
} | ||
|
||
await syncPriority(issue, backlogBoard, card) | ||
break | ||
} | ||
|
||
case 'closed': { | ||
if (issue.state_reason === 'completed') { | ||
await backlogBoard.moveCard(card.id, backlogBoard.columns.Done) | ||
} else { | ||
await backlogBoard.moveCard(card.id, backlogBoard.columns.Discarded) | ||
} | ||
break | ||
} | ||
|
||
case 'assigned': { | ||
if (card.status === backlogBoard.columns.Backlog) { | ||
await backlogBoard.moveCard(card.id, backlogBoard.columns.ToDo) | ||
} | ||
break | ||
} | ||
|
||
case 'labeled': { | ||
if (label.name === '⛔ status: blocked') { | ||
await backlogBoard.moveCard(card.id, backlogBoard.columns.Blocked) | ||
} | ||
await syncPriority(issue, backlogBoard, card) | ||
break | ||
} | ||
|
||
case 'unlabeled': { | ||
if (label.name === '⛔ status: blocked') { | ||
// TODO: Move back to the column it came from. | ||
await backlogBoard.moveCard(card.id, backlogBoard.columns.Backlog) | ||
} | ||
await syncPriority(issue, backlogBoard, card) | ||
break | ||
} | ||
} | ||
} |
Oops, something went wrong.