Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: improve and automate issue/PR labels #72

Merged
merged 4 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions .github/scripts/auto-label.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ const regexLabelMap = {
'^\\.mocharc.cjs$': ['test'],
'^eslint/': ['style'],
'^\\.eslintrc\\.js$': ['style'],
'^\\.github/workflows/': ['ci'],
'^\\.github/scripts/': ['ci'],
'^\\.github/': ['ci'],
'^plugin/': ['plugin'],
'^ui/config/': ['config'],
'^ui/eureka/eureka_config': ['config', 'eureka'],
Expand Down Expand Up @@ -127,7 +126,58 @@ const getLabels = async (github, owner, repo, pullNumber) => {
}
})));

return [...changedModule, ...changedLang.map((v) => langToLabel(v))];
// since this script clobbers all PR labels, we need to capture
// existing PR labels that are unrelated to scope and preserve them.
/**
* @typedef {{ name: string }} Label
* @type {Label[]}
*/
const prLabels = pullRequest.labels;
const scopeLabelsAll = nonNullUnique(lodash.flatten(Object.values(regexLabelMap)));
const prNonScopeLabels = prLabels
.map((label) => label.name)
.filter((label) => !scopeLabelsAll.includes(label));

// Determine if there is an approving review from a reviewer with write access;
// if not (and it's not a draft PR), add the 'needs-review' label.
const isApproved = await isPRApproved(github, prIdentifier);
if (!isApproved && !pullRequest.draft)
prNonScopeLabels.push('needs-review');

return [
...prNonScopeLabels,
...changedModule,
...changedLang.map((v) => langToLabel(v)),
];
};

/**
* @param {GitHub} github
* @param {identifier} prIdentifier
* @returns {Promise<boolean>}
*/
const isPRApproved = async (github, prIdentifier) => {
/**
* @typedef {{ state: string, author_association: string }} Review
* @type {{ [data: string]: Review[] }};
*/
const { data: reviews } = await github.rest.pulls.listReviews(prIdentifier);
if (reviews === undefined || reviews.length === 0) {
return false;
}
for (const review of reviews) {
if (review.state === 'APPROVED' && isReviewer(review.author_association))
return true;
}
return false;
};

/**
* @param {string} association
* @returns {boolean}
*/
const isReviewer = (association) => {
return association === 'COLLABORATOR' || association === 'OWNER';
};

/**
Expand Down
129 changes: 129 additions & 0 deletions .github/workflows/label-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# This workflow auto-labels PRs based on various criteria:
# - If a PR is opened or a new commit is pushed:
# - The workflow runs auto-label.cjs to add scope-related labels and to add 'needs-review'
# (unless the PR already has an approving review from a maintainer).
# - The workflow will check for linked issues and add the 'fix-me' label to them.
# - If a review (approving or otherwise) is added to the PR by a maintainer, the workflow
# removes the 'needs-review' label.
# - If 'auto merge' is enabled or disabled on the PR, the workflow adds/removes the
# 'auto-merge' label.
name: PR Auto Label

on:
pull_request_target:
types:
- edited
- synchronize
- opened
- reopened
- auto_merge_enabled
- auto_merge_disabled
- ready_for_review
pull_request_review:
types: [submitted]

jobs:
job_picker:
runs-on: ubuntu-latest
outputs:
run: ${{ steps.label_job.outputs.run }}
steps:
- name: Determine label job
id: label_job
shell: bash
run: |
if [ "${{ contains(github.event.action,'auto_merge_') }}" == "true" ]; then
echo "run=auto_merge" >> $GITHUB_OUTPUT
elif [ "${{ github.event.action }}" == "submitted" ]; then
echo "run=pr_review" >> $GITHUB_OUTPUT
elif [ "${{ github.event.action }}" == "ready_for_review" ]; then
echo "run=pr_ready" >> $GITHUB_OUTPUT
else
echo "run=push_commit" >> $GITHUB_OUTPUT
fi

auto_merge:
runs-on: ubuntu-latest
needs: job_picker
if: needs.job_picker.outputs.run == 'auto_merge'
steps:
- name: Add auto-merge label
uses: buildsville/[email protected]
if: github.event.action == 'auto_merge_enabled'
with:
token: ${{secrets.GITHUB_TOKEN}}
labels: auto-merge
type: add

- name: Remove auto-merge label
uses: buildsville/[email protected]
if: github.event.action == 'auto_merge_disabled'
with:
token: ${{secrets.GITHUB_TOKEN}}
labels: auto-merge
type: remove

pr_review:
runs-on: ubuntu-latest
needs: job_picker
if: needs.job_picker.outputs.run == 'pr_review'
steps:
- name: Remove needs-review label
if: >
github.event.review.author_association == 'COLLABORATOR' ||
github.event.review.author_association == 'OWNER'
uses: buildsville/[email protected]
with:
token: ${{secrets.GITHUB_TOKEN}}
labels: needs-review
type: remove

pr_ready:
runs-on: ubuntu-latest
needs: job_picker
if: needs.job_picker.outputs.run == 'pr_ready'
steps:
- name: Add needs-review label
uses: buildsville/[email protected]
with:
token: ${{secrets.GITHUB_TOKEN}}
labels: needs-review
type: add

push_commit:
runs-on: ubuntu-latest
needs: job_picker
if: needs.job_picker.outputs.run == 'push_commit'
steps:
- uses: actions/checkout@v3
with:
# force to ci checkout main repo to avoid
# unexpected PR code to be executed with out github token
ref: main

- name: Check for linked issues
id: find-linked-issues
uses: mondeja/pr-linked-issues-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Label linked issues
if: join(steps.find-linked-issues.outputs.issues) != ''
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Found linked issues: ${{ steps.find-linked-issues.outputs.issues }}"
issues=${{ steps.find-linked-issues.outputs.issues }}
for issue in ${issues//,/ }; do
echo "Adding 'fix-me' label to issue $issue"
gh issue edit $issue --add-label "fix-me"
done

- uses: ./.github/actions/setup-js-env

- name: Run Label Script
run: |
node .github/scripts/auto-label.cjs
env:
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
PR_NUMBER: ${{ github.event.number }}
24 changes: 0 additions & 24 deletions .github/workflows/labels.yaml

This file was deleted.

Loading