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

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new f493f3b6d .github/workflows: Skip depends-on PRs that are already 
merged.
f493f3b6d is described below

commit f493f3b6d7f7e5da1729249f2d45e982bb1d8d3b
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Oct 19 10:21:43 2026 +0200

    .github/workflows: Skip depends-on PRs that are already merged.
    
    When a depends-on PR is merged, its commits are in master. Build then
    cherry-picks the PR head again, the cherry-pick is empty or conflicts,
    and Fetch-Source fails. The author must remove the depends-on line and
    start CI again.
    
    Read the state of each dependency with the GitHub API. If the PR is
    merged and its merge commit is in the checkout, skip the PR. Apply
    open PRs, PRs merged into a different branch, and PRs whose merge
    commit is not in the checkout as before. If the API call fails, apply
    the dependency as before.
    
    A PR that is closed without merge is now an error: CI does not apply
    it, and the step fails with "closed without merge; remove it from
    depends-on or reopen it". Before, CI applied such a PR silently.
    
    The report marks a skipped dependency with "merged": true. A failure
    report marks a closed dependency with "closed": true and the error
    code dependency_closed. The comment shows "already merged, skipped",
    "closed without merge", and the reason for the failure.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 .github/workflows/build.yml              | 37 ++++++++++++++++++++++++++++++--
 .github/workflows/depends-on-comment.yml | 14 ++++++------
 2 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 03758b6b6..7f4c7014e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -135,6 +135,8 @@ jobs:
       - name: Apply depends-on PRs
         if: ${{ steps.gittargets.outputs.depends_on != '' }}
         shell: bash
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: |
           set -uo pipefail
 
@@ -142,9 +144,10 @@ jobs:
           git config --global user.name "github-actions"
 
           # Pass only fixed error codes to the trusted comment workflow.
+          # $3 and $4 name a dependency that is closed without merge.
           mark_failed() {
           echo "::error::could not apply $1 ($2)"
-          python3 - "$2" <<'PY'
+          python3 - "$2" "${3:-}" "${4:-}" <<'PY'
           import json, sys
           p = "depends-on-report/result.json"
           try:
@@ -153,6 +156,9 @@ jobs:
               d = {"version": 1, "pr_number": None, "head_sha": None, 
"dependencies": [], "warnings": []}
           d["status"] = "failed"
           d["error_code"] = sys.argv[1]
+          for dep in d.get("dependencies", []):
+              if [dep.get("repo"), str(dep.get("number"))] == sys.argv[2:4]:
+                  dep["closed"] = True
           open(p, "w").write(json.dumps(d))
           PY
           }
@@ -194,8 +200,30 @@ jobs:
             fi
 
             DEP_SHA=$(git -C "$REPO_PATH" rev-parse "dep-${DEP_PR_NUM}")
+
+            # Skip a dependency whose merge commit is already in the checkout.
+            # Its commits are in the target branch and cannot be applied again.
+            # A dependency that is closed without merge is an error.
+            if ! DEP_INFO=$(gh api "repos/${DEP_REPO}/pulls/${DEP_PR_NUM}" \
+                --jq 'if .merged then "merged \(.merge_commit_sha)" else 
.state end'); then
+              echo "::warning::Could not read the state of ${DEP}; applying 
it."
+              DEP_INFO=""
+            fi
+            read -r DEP_STATE MERGE_SHA <<< "$DEP_INFO"
+            if [ "$DEP_STATE" = "merged" ] && \
+               git -C "$REPO_PATH" merge-base --is-ancestor "$MERGE_SHA" HEAD 
2>/dev/null; then
+              echo "Dependency ${DEP} is merged as ${MERGE_SHA}; skipping it"
+              printf '%s\t%s\t%s\tmerged\n' "$DEP_REPO" "$DEP_PR_NUM" 
"$DEP_SHA" >> depends-on-report/applied.tsv
+              continue
+            fi
+
             printf '%s\t%s\t%s\n' "$DEP_REPO" "$DEP_PR_NUM" "$DEP_SHA" >> 
depends-on-report/applied.tsv
 
+            if [ "$DEP_STATE" = "closed" ]; then
+              echo "::error::Dependency ${DEP} is closed without merge; remove 
it from depends-on or reopen it."
+              mark_failed "$DEP" "dependency_closed" "$DEP_REPO" 
"$DEP_PR_NUM"; exit 1
+            fi
+
             # Stop on unrelated histories; HEAD..dep would otherwise include
             # every dependency commit and could cherry-pick unrelated changes.
             COMMON_BASE=$(git -C "$REPO_PATH" merge-base "dep-${DEP_PR_NUM}" 
HEAD || true)
@@ -226,12 +254,15 @@ jobs:
           python3 - <<'PY'
           import json
           shas = {}
+          merged = set()
           try:
               with open("depends-on-report/applied.tsv", encoding="utf-8") as 
f:
                   for line in f:
                       p = line.rstrip("\n").split("\t")
-                      if len(p) == 3:
+                      if len(p) in (3, 4):
                           shas[(p[0], p[1])] = p[2]
+                      if len(p) == 4 and p[3] == "merged":
+                          merged.add((p[0], p[1]))
           except FileNotFoundError:
               pass
           with open("depends-on-report/result.json", encoding="utf-8") as f:
@@ -240,6 +271,8 @@ jobs:
               key = (dep.get("repo"), str(dep.get("number")))
               if key in shas:
                   dep["head_sha"] = shas[key]
+              if key in merged:
+                  dep["merged"] = True
           with open("depends-on-report/result.json", "w", encoding="utf-8") as 
f:
               json.dump(d, f)
           PY
diff --git a/.github/workflows/depends-on-comment.yml 
b/.github/workflows/depends-on-comment.yml
index b6ade4d34..2e515bdfd 100644
--- a/.github/workflows/depends-on-comment.yml
+++ b/.github/workflows/depends-on-comment.yml
@@ -135,6 +135,10 @@ jobs:
             const runUrl = context.payload.workflow_run.html_url;
             const shortSha = (s) =>
               (typeof s === 'string' && /^[0-9a-f]{7,40}$/i.test(s)) ? ` @ 
${s.slice(0, 10)}` : '';
+            const depLine = (d) =>
+              `- 
https://github.com/${d.repo}/pull/${d.number}${shortSha(d.head_sha)}` +
+              (d.merged === true ? ' (already merged, skipped)' : '') +
+              (d.closed === true ? ' ❌ **closed without merge**' : '');
 
             let body;
             if (status === 'ok') {
@@ -142,9 +146,7 @@ jobs:
                 core.info('status=ok but no valid dependencies after 
validation; skipping.');
                 return;
               }
-              const list = deps
-                .map((d) => `- 
https://github.com/${d.repo}/pull/${d.number}${shortSha(d.head_sha)}`)
-                .join('\n');
+              const list = deps.map(depLine).join('\n');
               body =
                 `${marker}\n` +
                 `### 🔗 Cross-repo PR dependencies\n\n` +
@@ -157,9 +159,7 @@ jobs:
                 core.info('status=failed but no valid dependencies after 
validation; skipping.');
                 return;
               }
-              const list = deps
-                .map((d) => `- 
https://github.com/${d.repo}/pull/${d.number}${shortSha(d.head_sha)}`)
-                .join('\n');
+              const list = deps.map(depLine).join('\n');
               // Render only fixed text selected by an allowed error code.
               const REASONS = {
                 fetch_failed: 'the dependency PR could not be fetched (it may 
not exist)',
@@ -168,6 +168,8 @@ jobs:
                 cherry_pick_conflict: 'cherry-pick failed (if your PR has 
merge commits, rebase instead)',
                 unsupported_repo: 'the dependency repository is not supported',
                 report_parse_failed: 'the dependency report could not be read',
+                dependency_closed: 'a dependency PR is closed without merge; ' 
+
+                  'remove it from depends-on or reopen it',
               };
               const code = typeof report.error_code === 'string' ? 
report.error_code : '';
               const reason = REASONS[code] ? `\n\nReason: ${REASONS[code]}` : 
'';

Reply via email to