This is an automated email from the ASF dual-hosted git repository.

yuluo-yx pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 8f3ee32eab infra: add /retest action (#7683)
8f3ee32eab is described below

commit 8f3ee32eab8538bb7129f3fb5e1797323737b634
Author: shown <[email protected]>
AuthorDate: Sat Oct 17 00:16:00 2026 +0800

    infra: add /retest action (#7683)
    
    Signed-off-by: yuluo-yx <[email protected]>
---
 .github/workflows/retest.yml | 124 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/.github/workflows/retest.yml b/.github/workflows/retest.yml
new file mode 100644
index 0000000000..68d293b9af
--- /dev/null
+++ b/.github/workflows/retest.yml
@@ -0,0 +1,124 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: Retest
+
+# Once this workflow reaches the default branch, anyone can request a retest
+# by posting /retest as the entire comment in an open PR's conversation.
+# A rocket reaction acknowledges the request, including when no runs failed.
+on:
+  issue_comment:
+    types: [created]
+
+permissions: {}
+
+jobs:
+  retest:
+    if: github.event.issue.pull_request && contains(github.event.comment.body, 
'/retest')
+    runs-on: ubuntu-latest
+    timeout-minutes: 5
+    permissions:
+      actions: write
+      pull-requests: write
+    concurrency:
+      group: retest-${{ github.event.issue.number }}
+      cancel-in-progress: false
+    steps:
+      # issue_comment loads this script from the default branch; no checkout,
+      # dependency installation, or execution of PR code is needed.
+      - name: Rerun failed pull request checks
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const {issue, comment, action} = context.payload;
+            if (action !== 'created' || !issue?.pull_request || 
comment?.body?.trim() !== '/retest') {
+                return;
+            }
+
+            const repo = context.repo;
+            const pullRequest = {...repo, pull_number: issue.number};
+            const {data: pr} = await github.rest.pulls.get(pullRequest);
+            if (pr.state !== 'open' || !pr.head.repo) {
+                core.info('Skipping a closed pull request or deleted source 
repository.');
+                return;
+            }
+
+            // Acknowledge the command and discover runs with concurrent API 
requests.
+            const [, runs] = await Promise.all([
+                github.rest.reactions.createForIssueComment({
+                    ...repo,
+                    comment_id: comment.id,
+                    content: 'rocket',
+                }),
+                github.paginate(github.rest.actions.listWorkflowRunsForRepo, {
+                    ...repo,
+                    event: 'pull_request',
+                    head_sha: pr.head.sha,
+                    per_page: 100,
+                }),
+            ]);
+
+            const latest = new Map();
+            for (const run of runs) {
+                // Fork runs can have an empty pull_requests array. Match the 
source as
+                // well as the SHA, and verify the PR number whenever GitHub 
supplies it.
+                if (run.event !== 'pull_request' || run.head_sha !== 
pr.head.sha
+                    || run.head_repository?.id !== pr.head.repo.id || 
run.head_branch !== pr.head.ref
+                    || (run.pull_requests?.length && 
!run.pull_requests.some(pull => pull.number === pr.number))) {
+                    continue;
+                }
+                const previous = latest.get(run.workflow_id);
+                // Include successful and active runs so an older failure is 
not retried.
+                if (!previous || run.run_number > previous.run_number) {
+                    latest.set(run.workflow_id, run);
+                }
+            }
+
+            let requested = 0;
+            const errors = [];
+            // Retry failed jobs and their dependents, preserving successful 
jobs.
+            // Cancelled runs and workflows awaiting approval are left alone.
+            const isFailed = run => run.status === 'completed' && ['failure', 
'timed_out'].includes(run.conclusion);
+            for (const run of latest.values()) {
+                if (!isFailed(run)) {
+                    continue;
+                }
+                try {
+                    // Recheck after discovery: another command may have 
already retried
+                    // this run, or the contributor may have pushed a new 
commit.
+                    const {data: currentPr} = await 
github.rest.pulls.get(pullRequest);
+                    if (currentPr.state !== 'open' || currentPr.head.sha !== 
pr.head.sha) {
+                        core.info('The pull request changed; stopping this 
retest request.');
+                        break;
+                    }
+                    const {data: currentRun} = await 
github.rest.actions.getWorkflowRun({...repo, run_id: run.id});
+                    if (!isFailed(currentRun) || currentRun.run_attempt !== 
run.run_attempt) {
+                        core.info(`Run ${run.id} has already changed; skipping 
it.`);
+                        continue;
+                    }
+                    await 
github.rest.actions.reRunWorkflowFailedJobs({...repo, run_id: run.id});
+                    requested++;
+                    core.info(`Requested failed-job rerun: ${run.html_url}`);
+                } catch (error) {
+                    // A rejected/expired run must not prevent retrying other 
workflows.
+                    errors.push(run.id);
+                    core.warning(`Could not rerun ${run.html_url}: 
${error.message}`);
+                }
+            }
+
+            core.info(`Requested reruns for ${requested} workflow(s) at 
${pr.head.sha}.`);
+            if (errors.length) {
+                core.setFailed(`Failed to request reruns for workflow run(s): 
${errors.join(', ')}.`);
+            }

Reply via email to