CI: Auto label on RTLMeter and Code coverage failure

This commit is contained in:
Geza Lore
2026-09-19 21:45:20 +01:00
parent 18ad801887
commit 8c6dd7a164
5 changed files with 211 additions and 66 deletions
@@ -0,0 +1,62 @@
---
# DESCRIPTION: Github actions composite action
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Update PR label
description: >-
Add or remove a label on a pull request. Does nothing unless the pull
request is still open and still at the commit the result is for, so that a
result superseded by a push does not label/unlabel the work that replaced it.
inputs:
token:
description: "Token to label with"
required: true
pr-number:
description: "Number of the pull request"
required: true
head-sha:
description: "Commit the result is for"
required: true
label:
description: "Label to set"
required: true
set:
description: "'true' to add the label, 'false' to remove it"
required: true
runs:
using: composite
steps:
- name: Set the label
shell: bash
env:
GH_TOKEN: ${{ inputs.token }}
REPO: ${{ github.repository }}
PR: ${{ inputs.pr-number }}
SHA: ${{ inputs.head-sha }}
LABEL: ${{ inputs.label }}
SET: ${{ inputs.set }}
run: |-
# The state and head of the pull request as they are now, and whether
# it carries the label already
read -r STATE HEAD LABELLED < <(gh pr view "${PR}" \
--repo "${REPO}" \
--json state,headRefOid,labels \
--jq '"\(.state) \(.headRefOid) \(any(.labels[]; .name == env.LABEL))"')
# Only act on a change, so a run does not reapply what is already set
if [ "${STATE}" != OPEN ]; then
# Only label open PRs
echo "PR #${PR} is ${STATE}"
elif [ "${HEAD}" != "${SHA}" ]; then
# Pushed to since, so this result is stale and a new run will decide
echo "PR #${PR} has moved on from ${SHA}"
elif [ "${SET}" = true ] && [ "${LABELLED}" = false ]; then
echo "Adding '${LABEL}' to PR #${PR}"
gh pr edit "${PR}" --repo "${REPO}" --add-label "${LABEL}"
elif [ "${SET}" = false ] && [ "${LABELLED}" = true ]; then
echo "Removing '${LABEL}' from PR #${PR}"
gh pr edit "${PR}" --repo "${REPO}" --remove-label "${LABEL}"
else
echo "No change to PR #${PR}"
fi
+19 -2
View File
@@ -36,8 +36,8 @@ concurrency:
jobs:
build:
name: Build
start:
name: Start
# Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks)
# Only run pull request jobs if labelled as needing a coverage run
# Always run workflow dispatch jobs
@@ -51,6 +51,23 @@ jobs:
&& github.event.action != 'unlabeled'
&& contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage')))) ||
(github.event_name == 'workflow_dispatch')
runs-on: ubuntu-slim
steps:
- name: Save the pull request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.number }}" > pr-number.txt
- name: Upload the pull request number
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
overwrite: true
build:
name: Build
needs: start
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
+117 -63
View File
@@ -10,9 +10,10 @@ on:
pull_request_target: # 'pull_request' can have no write permissions
types: [opened, synchronize, reopened, closed]
workflow_run: # To react to the result of a workflow on a pull request
workflows: ["Code coverage", "Regression"]
workflows: ["Code coverage", "RTLMeter", "Regression"]
types: [completed]
permissions: {} # Everything below uses the CI app token instead
permissions:
contents: read # To check out the local action, everything else uses the CI app token
defaults:
run:
@@ -102,50 +103,42 @@ jobs:
permission-actions: read
permission-pull-requests: write
- name: "Set the 'pr-blocked: fix-regression' label from the result"
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
SHA: ${{ github.event.workflow_run.head_sha }}
LABEL: "pr-blocked: fix-regression"
FAILED: ${{ github.event.workflow_run.conclusion == 'failure' }}
run: |-
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
PR=$(cat pr-number.txt)
# Its state and head as they are now, and whether it carries the
# label already
read -r STATE HEAD LABELLED < <(gh pr view "${PR}" \
--repo "${{ github.repository }}" \
--json state,headRefOid,labels \
--jq '"\(.state) \(.headRefOid) \(any(.labels[]; .name == env.LABEL))"')
# Only act on a change, so a run does not reapply what is already set
if [ "${STATE}" != OPEN ]; then
# Closing removes all 'pr*' labels, do not put one back on
echo "PR #${PR} is ${STATE}"
elif [ "${HEAD}" != "${SHA}" ]; then
# Pushed to since, so this result is stale and a new run will decide
echo "PR #${PR} has moved on from ${SHA}"
elif [ "${FAILED}" = true ] && [ "${LABELLED}" = false ]; then
echo "Regression failed, blocking PR #${PR}"
gh pr edit "${PR}" --repo "${{ github.repository }}" --add-label "${LABEL}"
elif [ "${FAILED}" = false ] && [ "${LABELLED}" = true ]; then
echo "Regression succeeded, unblocking PR #${PR}"
gh pr edit "${PR}" --repo "${{ github.repository }}" --remove-label "${LABEL}"
else
echo "No change to PR #${PR}"
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
coverage-complete:
name: Coverage complete
- name: "Update the 'pr-blocked: fix-regression' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-regression"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
rtlmeter-complete:
name: RTLMeter complete
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'workflow_run'
&& github.event.workflow_run.name == 'Code coverage'
&& github.event.workflow_run.name == 'RTLMeter'
&& github.event.workflow_run.event == 'pull_request'
&& github.event.workflow_run.conclusion == 'success'
&& (github.event.workflow_run.conclusion == 'success'
|| github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
@@ -159,40 +152,101 @@ jobs:
permission-actions: read
permission-pull-requests: write
- name: "Set the 'pr-blocked: improve-coverage' label from the result"
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
SHA: ${{ github.event.workflow_run.head_sha }}
LABEL: "pr-blocked: improve-coverage"
run: |-
# The run tells us which pull request it was for, and whether every
# line its patch touches is covered
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: fix-rtlmeter' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-rtlmeter"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
coverage-complete:
name: Coverage complete
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'workflow_run'
&& github.event.workflow_run.name == 'Code coverage'
&& github.event.workflow_run.event == 'pull_request'
&& (github.event.workflow_run.conclusion == 'success'
|| github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-actions: read
permission-pull-requests: write
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: fix-dev-coverage' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-dev-coverage"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
- name: Get the line coverage
id: coverage
if: ${{ github.event.workflow_run.conclusion == 'success' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# Whether every line the patch touches is covered
if ! gh run download "${{ github.event.workflow_run.id }}" --name coverage-status; then
echo "Run has no 'coverage-status' artifact"
exit 0
fi
STATUS=$(cat coverage-status.txt)
PR=${STATUS%% *}
COVERED=${STATUS##* }
# Its state and head as they are now, and whether it carries the
# label already
read -r STATE HEAD LABELLED < <(gh pr view "${PR}" \
--repo "${{ github.repository }}" \
--json state,headRefOid,labels \
--jq '"\(.state) \(.headRefOid) \(any(.labels[]; .name == env.LABEL))"')
# Only act on a change, so a run does not reapply what is already set
if [ "${STATE}" != OPEN ]; then
# Closing removes all 'pr*' labels, do not put one back on
echo "PR #${PR} is ${STATE}"
elif [ "${HEAD}" != "${SHA}" ]; then
# Pushed to since, so this result is stale and a new run will decide
echo "PR #${PR} has moved on from ${SHA}"
elif [ "${COVERED}" = false ] && [ "${LABELLED}" = false ]; then
echo "Line coverage incomplete, blocking PR #${PR}"
gh pr edit "${PR}" --repo "${{ github.repository }}" --add-label "${LABEL}"
elif [ "${COVERED}" = true ] && [ "${LABELLED}" = true ]; then
echo "Line coverage complete, unblocking PR #${PR}"
gh pr edit "${PR}" --repo "${{ github.repository }}" --remove-label "${LABEL}"
else
echo "No change to PR #${PR}"
fi
echo "covered=$(cat coverage-status.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: improve-coverage' label"
if: ${{ steps.pr.outputs.number && steps.coverage.outputs.covered }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: improve-coverage"
set: ${{ steps.coverage.outputs.covered != 'true' }}
+12
View File
@@ -58,6 +58,18 @@ jobs:
old-sha: ${{ steps.start.outputs.old-sha }}
cases: ${{ steps.cases.outputs.cases }}
steps:
- name: Save the pull request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.number }}" > pr-number.txt
- name: Upload the pull request number
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
overwrite: true
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
+1 -1
View File
@@ -129,5 +129,5 @@ fi
if [ -n "${COVERED}" ]; then
echo "Line coverage complete: ${COVERED}"
echo "${PR_NUMBER} ${COVERED}" > coverage-status.txt
echo "${COVERED}" > coverage-status.txt
fi