diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 04199602a..cd2ce3bb6 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -37,10 +37,13 @@ jobs: build: name: Build content runs-on: ubuntu-24.04 + outputs: + coverage-pr-run-ids: ${{ steps.build.outputs.coverage-pr-run-ids }} steps: - name: Checkout uses: actions/checkout@v5 - name: Build pages + id: build env: GH_TOKEN: ${{ github.token }} run: | @@ -62,3 +65,28 @@ jobs: steps: - name: Deploy to GitHub Pages uses: actions/deploy-pages@v4 + + notify: + name: Notify + needs: + - build + - deploy + runs-on: ubuntu-24.04 + if: ${{ github.repository == 'verilator/verilator' }} + steps: + - name: Checkout + uses: actions/checkout@v5 + # Use the Verilator CI app to post the comment + - name: Generate access token + id: generate-token + uses: actions/create-github-app-token@v2.1.4 + with: + app-id: ${{ vars.VERILATOR_CI_ID }} + private-key: ${{ secrets.VERILATOR_CI_KEY }} + permission-actions: write + permission-pull-requests: write + - name: Comment on PR + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + COVERAGE_PR_RUN_IDS: ${{ needs.build.outputs.coverage-pr-run-ids }} + run: bash -x ./ci/ci-pages-notify.bash diff --git a/ci/ci-pages-notify.bash b/ci/ci-pages-notify.bash new file mode 100755 index 000000000..3e892f868 --- /dev/null +++ b/ci/ci-pages-notify.bash @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# DESCRIPTION: Verilator: CI script for 'pages.yml', notifies PRs +# +# Copyright 2025 by Geza Lore. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +# Notify PRs via comment that their coverage reports are available + +# Get the current repo URL - might differ on a fork +readonly REPO_URL=$(gh repo view --json url --jq .url) + +# Create artifacts root directory +ARTIFACTS_ROOT=artifacts +mkdir -p ${ARTIFACTS_ROOT} + +for RUN_ID in ${COVERAGE_PR_RUN_IDS//,/ }; do + echo "@@@ Processing run ${RUN_ID}" + + # Create workflow artifacts directory + ARTIFACTS_DIR=${ARTIFACTS_ROOT}/${RUN_ID} + mkdir -p ${ARTIFACTS_DIR} + + # Download artifact of this run, if exists + gh run download ${RUN_ID} --name coverage-pr-notification --dir ${ARTIFACTS_DIR} || true + ls -lsha ${ARTIFACTS_DIR} + + # Move on if no notification is required + if [ ! -f ${ARTIFACTS_DIR}/pr-number.txt ]; then + echo "No notification found" + continue + fi + echo "Posting notification found" + + cat ${ARTIFACTS_DIR}/body.txt + gh pr comment $(cat ${ARTIFACTS_DIR}/pr-number.txt) --body-file ${ARTIFACTS_DIR}/body.txt + + # Get the artifact ID + ARTIFACT_ID=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "coverage-pr-notification") | .id') + + # Delete it, so we only notify once + gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}" +done diff --git a/ci/ci-pages.bash b/ci/ci-pages.bash index ec2eec318..c61a09c9c 100755 --- a/ci/ci-pages.bash +++ b/ci/ci-pages.bash @@ -24,9 +24,13 @@ mkdir -p ${PAGES_ROOT} # Get the current repo URL - might differ on a fork readonly REPO_URL=$(gh repo view --json url --jq .url) +# Set GITHUB_OUTPUT when run locally for testing +if [[ -z "$GITHUB_OUTPUT" ]]; then + GITHUB_OUTPUT=github-output.txt +fi + # Populates ${PAGES_ROOT}/coverage-reports compile_coverage_reports() { - # We will process all runs up to and including this date. This is chosen to be # slightly less than the artifact retention period for simplicity. local OLDEST=$(date --date="28 days ago" --iso-8601=date) @@ -50,8 +54,11 @@ compile_coverage_reports() { mkdir -p ${COVERAGE_ROOT} # Create index page contents fragment - local CONTENTSS=contents.tmp - echo > ${CONTENTSS} + local CONTENTS=contents.tmp + echo > ${CONTENTS} + + # Run IDs of PR jobs processed + local PR_RUN_IDS="" # Iterate over all unique event types that triggered the workflows for EVENT in $(jq -r 'map(.event) | sort | unique | .[]' completedRuns.json); do @@ -61,19 +68,28 @@ compile_coverage_reports() { EMIT_SECTION_HEADER=1 # For each worfklow run that was triggered by this event type - for WORKFLOW_ID in $(jq ".[] | select(.event == \"${EVENT}\") |.databaseId" completedRuns.json); do - echo "@@@ Processing run ${WORKFLOW_ID}" + for RUN_ID in $(jq ".[] | select(.event == \"${EVENT}\") |.databaseId" completedRuns.json); do + echo "@@@ Processing run ${RUN_ID}" # Extract the info of this run - jq ".[] | select(.databaseId == $WORKFLOW_ID)" completedRuns.json > workflow.json + jq ".[] | select(.databaseId == $RUN_ID)" completedRuns.json > workflow.json jq "." workflow.json + # Record run ID of PR job + if [[ $EVENT == "pull_request" ]]; then + if [[ -z "$PR_RUN_IDS" ]]; then + PR_RUN_IDS="$RUN_ID" + else + PR_RUN_IDS="$PR_RUN_IDS,$RUN_ID" + fi + fi + # Create workflow artifacts directory - local ARTIFACTS_DIR=${ARTIFACTS_ROOT}/${WORKFLOW_ID} + local ARTIFACTS_DIR=${ARTIFACTS_ROOT}/${RUN_ID} mkdir -p ${ARTIFACTS_DIR} # Download artifacts of this run, if exists - gh run download ${WORKFLOW_ID} --name coverage-report --dir ${ARTIFACTS_DIR} || true + gh run download ${RUN_ID} --name coverage-report --dir ${ARTIFACTS_DIR} || true ls -lsha ${ARTIFACTS_DIR} # Move on if no coverage report is available @@ -86,26 +102,34 @@ compile_coverage_reports() { # Emit section header if [[ -n $EMIT_SECTION_HEADER ]]; then unset EMIT_SECTION_HEADER - echo "