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]}` :
'';