383383 - completed
384384
385385 permissions :
386- pull-requests : write
387- actions : write
386+ pull-requests : read
387+ actions : read
388388 contents : read
389389
390390 concurrency :
410410 emit_pr() {
411411 local pr_number="$1"
412412 local pr_author
413- pr_author=$(gh pr view "$pr_number" --repo "$REPO" --json author --jq '.author.login' 2>/dev/null || echo "")
414- if [ -z "$pr_author" ]; then
413+ pr_author=$(gh pr view "$pr_number" --repo "$REPO" --json author --jq '.author.login // empty ' 2>/dev/null || echo "")
414+ if [ -z "$pr_author" ] || [ "$pr_author" = "null" ] ; then
415415 echo "::warning::Could not fetch PR author for PR #$pr_number"
416416 fi
417417 echo "number=$pr_number" >> "$GITHUB_OUTPUT"
@@ -444,6 +444,10 @@ job:
444444 call-ci-failure-bot :
445445 needs : find-pr
446446 if : ${{ needs.find-pr.outputs.pr_number != '' }}
447+ permissions :
448+ pull-requests : write
449+ actions : write
450+ contents : read
447451 uses : openwisp/openwisp-utils/.github/workflows/reusable-bot-ci-failure.yml@master
448452 with :
449453 pr_number : ${{ needs.find-pr.outputs.pr_number }}
@@ -468,28 +472,137 @@ maintainer. It analyzes the PR's title, description, code changes, and
468472linked issues, then posts a properly formatted changelog entry as a
469473comment on the PR.
470474
475+ The bot uses a two-workflow pattern to support PRs from forks. The first
476+ workflow is triggered by ``pull_request_review ``: it checks whether the PR
477+ qualifies and uploads the PR number as an artifact, but requires no
478+ secrets. The second workflow executes via ``workflow_run `` once the first
479+ completes: it runs in the context of the base repository, has full access
480+ to secrets, and is the one that generates and posts the changelog comment.
481+
471482**Secrets **
472483
473484- ``GEMINI_API_KEY `` (required): Google Gemini API key.
474485- ``OPENWISP_BOT_APP_ID `` (required): OpenWISP Bot GitHub App ID.
475486- ``OPENWISP_BOT_PRIVATE_KEY `` (required): OpenWISP Bot GitHub App private
476487 key.
477488
478- **Usage Example **
489+ **Setup for Other Repositories **
490+
491+ To enable the changelog bot in any OpenWISP repository, create the
492+ following two workflow files.
479493
480- To enable the changelog bot in any OpenWISP repository, create a workflow
481- file at ``.github/workflows/changelog-bot.yml ``:
494+ **1. Trigger ** (``.github/workflows/bot-changelog-trigger.yml ``)
495+
496+ This workflow fires on PR approval, checks if the PR is noteworthy, and
497+ uploads the PR number as an artifact for the runner to pick up.
482498
483499.. code-block :: yaml
484500
485- name : Changelog Bot
501+ name : Changelog Bot Trigger
502+
486503 on :
487504 pull_request_review :
488505 types : [submitted]
506+
489507 jobs :
508+ check :
509+ if : |
510+ github.event.review.state == 'approved' &&
511+ (github.event.review.author_association == 'OWNER' ||
512+ github.event.review.author_association == 'MEMBER' ||
513+ github.event.review.author_association == 'COLLABORATOR')
514+ runs-on : ubuntu-latest
515+ steps :
516+ - name : Check for noteworthy PR
517+ id : check
518+ env :
519+ PR_TITLE : ${{ github.event.pull_request.title }}
520+ run : |
521+ if echo "$PR_TITLE" | grep -qiE '^\[(feature|fix|change)\]'; then
522+ echo "has_noteworthy=true" >> $GITHUB_OUTPUT
523+ fi
524+
525+ - name : Save PR metadata
526+ if : steps.check.outputs.has_noteworthy == 'true'
527+ env :
528+ PR_NUMBER : ${{ github.event.pull_request.number }}
529+ run : echo "$PR_NUMBER" > pr_number
530+
531+ - name : Upload PR metadata
532+ if : steps.check.outputs.has_noteworthy == 'true'
533+ uses : actions/upload-artifact@v4
534+ with :
535+ name : changelog-metadata
536+ path : pr_number
537+
538+ **2. Runner ** (``.github/workflows/bot-changelog-runner.yml ``)
539+
540+ This workflow triggers after the trigger completes, downloads the
541+ artifact, and calls the reusable workflow with full secret access.
542+
543+ .. code-block :: yaml
544+
545+ name : Changelog Bot Runner
546+
547+ on :
548+ workflow_run :
549+ workflows : ["Changelog Bot Trigger"]
550+ types :
551+ - completed
552+
553+ permissions :
554+ actions : read
555+ contents : read
556+ pull-requests : write
557+ issues : write
558+
559+ jobs :
560+ fetch-metadata :
561+ runs-on : ubuntu-latest
562+ if : github.event.workflow_run.conclusion == 'success'
563+ outputs :
564+ pr_number : ${{ steps.metadata.outputs.pr_number }}
565+ steps :
566+ - name : Download PR metadata
567+ id : download
568+ uses : actions/download-artifact@v4
569+ with :
570+ name : changelog-metadata
571+ github-token : ${{ secrets.GITHUB_TOKEN }}
572+ run-id : ${{ github.event.workflow_run.id }}
573+ continue-on-error : true
574+
575+ - name : Read PR metadata
576+ if : steps.download.outcome == 'success'
577+ id : metadata
578+ run : |
579+ PR_NUMBER=$(cat pr_number)
580+ if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
581+ echo "::error::Invalid PR number: $PR_NUMBER"
582+ exit 1
583+ fi
584+ echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
585+
490586 changelog :
587+ needs : fetch-metadata
588+ if : needs.fetch-metadata.outputs.pr_number != ''
491589 uses : openwisp/openwisp-utils/.github/workflows/reusable-bot-changelog.yml@master
590+ with :
591+ pr_number : ${{ needs.fetch-metadata.outputs.pr_number }}
492592 secrets :
493593 GEMINI_API_KEY : ${{ secrets.GEMINI_API_KEY }}
494594 OPENWISP_BOT_APP_ID : ${{ secrets.OPENWISP_BOT_APP_ID }}
495595 OPENWISP_BOT_PRIVATE_KEY : ${{ secrets.OPENWISP_BOT_PRIVATE_KEY }}
596+
597+ .. note ::
598+
599+ The ``name `` field in the trigger workflow must be exactly ``Changelog
600+ Bot Trigger ``. The runner watches for this name via ``workflow_run ``.
601+ Changing it will silently break the connection between the two
602+ workflows.
603+
604+ Both ``bot-changelog-trigger.yml `` and ``bot-changelog-runner.yml ``
605+ must be committed to the **default branch ** of the repository. GitHub
606+ only activates ``workflow_run `` listeners that exist on the default
607+ branch: adding the runner only to a feature branch will cause it to
608+ silently do nothing.
0 commit comments