Repository navigation
scheduled refresh PR #114 @ 48d6c9cf11db1afe425a11adbb213ee527f56ad2 #3919
This file contains hidden or 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
| name: pr-agent-context-refresh | |
| run-name: >- | |
| ${{ | |
| github.event_name == 'workflow_dispatch' && | |
| format( | |
| 'scheduled refresh PR #{0} @ {1}', | |
| github.event.inputs.pull_request_number, | |
| github.event.inputs.pull_request_head_sha | |
| ) || | |
| github.event_name == 'schedule' && | |
| 'scheduled refresh fanout' || | |
| github.event_name == 'pull_request_review' && | |
| format('review refresh PR #{0}', github.event.pull_request.number) || | |
| github.event_name == 'pull_request_review_comment' && | |
| format('review-comment refresh PR #{0}', github.event.pull_request.number) || | |
| github.event_name == 'check_run' && | |
| format( | |
| 'check refresh {0}', | |
| github.event.check_run.pull_requests[0].number || github.event.check_run.head_sha | |
| ) || | |
| github.workflow | |
| }} | |
| on: | |
| pull_request_review: | |
| types: [submitted, edited, dismissed] | |
| pull_request_review_comment: | |
| types: [created, edited, deleted] | |
| check_run: | |
| types: [completed] | |
| workflow_dispatch: | |
| inputs: | |
| pull_request_number: | |
| description: Pull request number to refresh explicitly. | |
| required: true | |
| type: string | |
| pull_request_base_sha: | |
| description: Base SHA for the explicit refresh target. | |
| required: true | |
| type: string | |
| pull_request_head_sha: | |
| description: Head SHA for the explicit refresh target. | |
| required: true | |
| type: string | |
| trigger_event_name: | |
| description: Synthetic trigger event name used in rendered metadata. | |
| required: false | |
| default: workflow_dispatch | |
| type: string | |
| trigger_event_action: | |
| description: Synthetic trigger event action used in rendered metadata. | |
| required: false | |
| default: "" | |
| type: string | |
| schedule: | |
| - cron: "*/15 * * * *" | |
| concurrency: | |
| group: >- | |
| ${{ | |
| github.event_name == 'workflow_dispatch' && | |
| format( | |
| '{0}-{1}-{2}', | |
| github.workflow, | |
| github.event.inputs.pull_request_number, | |
| github.event.inputs.pull_request_head_sha | |
| ) || | |
| format( | |
| '{0}-{1}', | |
| github.workflow, | |
| github.event.pull_request.number || | |
| github.event.check_run.pull_requests[0].number || | |
| github.event.check_run.head_sha || | |
| github.ref_name || | |
| github.sha | |
| ) | |
| }} | |
| cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' }} | |
| permissions: | |
| contents: read | |
| actions: read | |
| pull-requests: write | |
| jobs: | |
| dispatch-scheduled-refreshes: | |
| name: Dispatch scheduled refreshes | |
| if: ${{ github.event_name == 'schedule' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: write | |
| pull-requests: read | |
| steps: | |
| - uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const pulls = await github.paginate(github.rest.pulls.list, { | |
| owner, | |
| repo, | |
| state: 'open', | |
| per_page: 100, | |
| }); | |
| const sameRepoPulls = pulls.filter( | |
| (pull) => pull.head?.repo?.full_name === `${owner}/${repo}`, | |
| ); | |
| const markerPrefix = '<!-- pr-agent-context:managed-comment;'; | |
| let dispatchCount = 0; | |
| let skipCount = 0; | |
| let errorCount = 0; | |
| const recentDispatchWindowMs = 60 * 60 * 1000; | |
| const recentDispatchCutoff = Date.now() - recentDispatchWindowMs; | |
| const { data: workflowRunsResponse } = await github.rest.actions.listWorkflowRuns({ | |
| owner, | |
| repo, | |
| workflow_id: 'pr-agent-context-refresh.yml', | |
| event: 'workflow_dispatch', | |
| per_page: 100, | |
| }); | |
| const workflowDispatchRuns = workflowRunsResponse.workflow_runs.filter((run) => { | |
| const createdTimestamp = run.created_at ? Date.parse(run.created_at) : 0; | |
| return createdTimestamp >= recentDispatchCutoff || run.status !== 'completed'; | |
| }); | |
| const hasCurrentRefreshComment = async (pull) => { | |
| const perPage = 100; | |
| const maxCommentsToInspect = 500; | |
| let page = 1; | |
| let inspectedComments = 0; | |
| while (inspectedComments < maxCommentsToInspect) { | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number: pull.number, | |
| per_page: perPage, | |
| page, | |
| sort: 'created', | |
| direction: 'desc', | |
| }); | |
| if ( | |
| comments.some( | |
| (comment) => | |
| comment.body?.startsWith(markerPrefix) && | |
| comment.body.includes('execution_mode=refresh;') && | |
| comment.body.includes(`head_sha=${pull.head.sha};`), | |
| ) | |
| ) { | |
| return true; | |
| } | |
| inspectedComments += comments.length; | |
| if (comments.length < perPage) { | |
| break; | |
| } | |
| page += 1; | |
| } | |
| return false; | |
| }; | |
| const hasRecentOrInFlightScheduledDispatchRun = (pull) => { | |
| const expectedTitle = `scheduled refresh PR #${pull.number} @ ${pull.head.sha}`; | |
| const now = Date.now(); | |
| return workflowDispatchRuns.some((run) => { | |
| if (run.display_title !== expectedTitle) { | |
| return false; | |
| } | |
| if (run.status !== 'completed') { | |
| return true; | |
| } | |
| const completedTimestamp = run.updated_at | |
| ? new Date(run.updated_at).getTime() | |
| : run.created_at | |
| ? new Date(run.created_at).getTime() | |
| : 0; | |
| return completedTimestamp > 0 && now - completedTimestamp <= recentDispatchWindowMs; | |
| }); | |
| }; | |
| for (const pull of sameRepoPulls) { | |
| try { | |
| const refreshCommentExists = await hasCurrentRefreshComment(pull); | |
| const hasRecentDispatchRun = hasRecentOrInFlightScheduledDispatchRun(pull); | |
| if (refreshCommentExists) { | |
| core.notice( | |
| `Skipping PR #${pull.number}: refresh comment for head ${pull.head.sha} already exists.`, | |
| ); | |
| skipCount += 1; | |
| continue; | |
| } | |
| if (hasRecentDispatchRun) { | |
| core.notice( | |
| `Skipping PR #${pull.number}: recent or in-flight scheduled dispatch for head ${pull.head.sha} already exists.`, | |
| ); | |
| skipCount += 1; | |
| continue; | |
| } | |
| await github.rest.actions.createWorkflowDispatch({ | |
| owner, | |
| repo, | |
| workflow_id: 'pr-agent-context-refresh.yml', | |
| ref: context.ref.replace('refs/heads/', ''), | |
| inputs: { | |
| pull_request_number: String(pull.number), | |
| pull_request_base_sha: pull.base.sha, | |
| pull_request_head_sha: pull.head.sha, | |
| trigger_event_name: 'schedule', | |
| trigger_event_action: '', | |
| }, | |
| }); | |
| dispatchCount += 1; | |
| } catch (error) { | |
| errorCount += 1; | |
| core.warning( | |
| `Unable to evaluate or dispatch PR #${pull.number}: ${error instanceof Error ? error.message : String(error)}`, | |
| ); | |
| } | |
| } | |
| core.notice( | |
| `Scheduled refresh guard dispatched ${dispatchCount} run(s) and skipped ${skipCount}.`, | |
| ); | |
| if (errorCount > 0) { | |
| core.warning(`Scheduled refresh guard encountered ${errorCount} per-PR error(s).`); | |
| } | |
| pr-agent-context-refresh: | |
| name: PR agent context refresh | |
| if: >- | |
| github.event_name != 'schedule' && | |
| ( | |
| (github.event_name == 'pull_request_review' && | |
| github.event.pull_request.head.repo.full_name == github.repository) || | |
| (github.event_name == 'pull_request_review_comment' && | |
| github.event.pull_request.head.repo.full_name == github.repository) || | |
| (github.event_name == 'check_run' && | |
| github.event.action == 'completed' && | |
| github.event.check_run.app.slug != 'github-actions' && | |
| toJson(github.event.check_run.pull_requests) != '[]' && | |
| github.event.check_run.pull_requests[0].head.repo.full_name == github.repository) || | |
| (github.event_name == 'workflow_dispatch' && | |
| github.event.inputs.pull_request_number != '' && | |
| github.event.inputs.pull_request_base_sha != '' && | |
| github.event.inputs.pull_request_head_sha != '') | |
| ) | |
| uses: shaypal5/pr-agent-context/.github/workflows/pr-agent-context.yml@v4 | |
| with: | |
| tool_ref: v4 | |
| execution_mode: refresh | |
| trigger_event_name_override: ${{ github.event.inputs.trigger_event_name || '' }} | |
| trigger_event_action_override: ${{ github.event.inputs.trigger_event_action || '' }} | |
| pull_request_number_override: ${{ github.event.inputs.pull_request_number || '' }} | |
| pull_request_base_sha_override: ${{ github.event.inputs.pull_request_base_sha || '' }} | |
| pull_request_head_sha_override: ${{ github.event.inputs.pull_request_head_sha || '' }} | |
| publish_mode: append | |
| publish_all_clear_comments_in_refresh: false | |
| wait_for_reviews_to_settle: true | |
| target_patch_coverage: "100" | |
| include_review_comments: true | |
| include_outdated_review_threads: true | |
| include_failing_checks: true | |
| include_failed_step_output: true | |
| include_patch_coverage: true | |
| coverage_artifact_prefix: pr-agent-context-coverage | |
| enable_cross_run_coverage_lookup: true | |
| coverage_source_workflows: CI | |
| prompt_template_file: .github/pr-agent-context-template.md | |
| debug_artifacts: true |