Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ marketplace `ref` at once.
- `wiring.py` counts a pin as a trigger of a workflow that runs on a push to `main` when a
`dorny/paths-filter` filter lists it and that filter's output gates a job's `if:`, so marola-app's
`ci.yml` is listed again for `corpus.version` (#169).
- `deps-stack --include-steward` and `pr-label`'s `kind/deps` find Scala Steward's PRs as
`app/marola-pr-bot` on an `update/` branch, not as `app/scala-steward`, a login no Steward PR ever
had. `pr_label_kind` now takes the head branch, and `pr-label` has a `--self-test` (#167).

## v0.16.0 — 2026-10-17

Expand Down
40 changes: 23 additions & 17 deletions scripts/deps-stack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# just deps-stack --dry-run # print every git/gh command; no push, no gh mutation
# just deps-stack --resume # continue after a conflict (see below)
# just deps-stack --skip 123 # drop PR #123 from the chain (repeatable)
# just deps-stack --include-steward # append scala-steward PRs after the dependabot ones
# just deps-stack --include-steward # append Scala Steward's update/* PRs after the dependabot ones
# just deps-stack --from-json f.json # use this file instead of `gh pr list` (testing, no gh)
# just deps-stack status # print the current chain + each PR's state
# just deps-stack clean # delete deps/* chain branches whose stacked PR is MERGED
Expand All @@ -22,8 +22,9 @@
# can't change, and force-pushing dependabot's own branches gets undone by its next rebase — so
# dependabot's branches are never touched (--retarget-dependabot only explains this).
#
# --include-steward's author:app/scala-steward query is UNVERIFIED with the default GITHUB_TOKEN
# (scala-steward.yml uses no PAT); check `gh pr list --search "is:pr author:app/scala-steward"`.
# --include-steward finds Scala Steward's PRs as author app/marola-pr-bot (marola-app's
# scala-steward.yml opens them with that App's token) on an update/ branch: the App also opens
# compiled-prompt, pointer-sync and skills PRs, so its login alone is not Steward.
#
# Needs `gh auth status` except for --dry-run/--from-json/--self-test. Run from the host, not ai-jail.
set -euo pipefail
Expand Down Expand Up @@ -171,7 +172,9 @@ order_prs() { # stdin: gh-pr-list-shaped JSON array -> stdout: same, filtered
elif eco == "pip" then 1
elif eco == "scala-steward" then 2
else 3 end;
def pr_bot: (.author.login // "") == "app/marola-pr-bot";
map(select(.number as $n | ($skip | index($n)) == null))
| map(select((pr_bot | not) or eco == "scala-steward"))
| map(select($include_steward == 1 or eco != "scala-steward"))
| map(. + {ecosystem: eco, rank: rank})
| sort_by([.rank, .number])
Expand All @@ -195,15 +198,15 @@ discover() { # -> ordered JSON array on stdout
raw="$(cat "$from_json")"
elif [ "$dry" -eq 1 ]; then
echo "+ gh pr list --state open --search \"author:app/dependabot\" --json number,headRefName,title,baseRefName,mergeable,files" >&2
[ "$include_steward" -eq 1 ] && echo "+ gh pr list --state open --search \"author:app/scala-steward\" --json number,headRefName,title,baseRefName,mergeable,files" >&2
[ "$include_steward" -eq 1 ] && echo "+ gh pr list --state open --search \"author:app/marola-pr-bot\" --json number,headRefName,title,baseRefName,mergeable,files,author" >&2
echo "[]"
return 0
else
gh auth status >/dev/null 2>&1 || { echo "gh is not logged in — run: gh auth login" >&2; exit 1; }
raw="$(gh pr list --state open --search "author:app/dependabot" --json number,headRefName,title,baseRefName,mergeable,files)"
if [ "$include_steward" -eq 1 ]; then
local steward
steward="$(gh pr list --state open --search "author:app/scala-steward" --json number,headRefName,title,baseRefName,mergeable,files)"
steward="$(gh pr list --state open --search "author:app/marola-pr-bot" --json number,headRefName,title,baseRefName,mergeable,files,author)"
raw="$(jq -s 'add' <<<"$raw"$'\n'"$steward")"
fi
fi
Expand All @@ -214,18 +217,21 @@ discover() { # -> ordered JSON array on stdout
self_test() {
local fixture="$script_dir/fixtures/deps-stack-prs.json"
[ -f "$fixture" ] || { echo "deps-stack self-test: fixture not found: $fixture" >&2; exit 1; }
local include_steward=0 skip_nums=() # the fixture has no steward/skip entries to exercise
local ordered
ordered="$(order_prs <"$fixture")"
local got expected
got="$(jq -c '[.[].number]' <<<"$ordered")"
expected="[101,103,102]" # github-actions (101, 103) by number, then pip (102)
echo "order: $got"
jq -r '.[] | " #\(.number) \(.ecosystem) \(.headRefName)"' <<<"$ordered"
if [ "$got" != "$expected" ]; then
echo "deps-stack self-test: FAILED — order $got, expected $expected" >&2
exit 1
fi
local include_steward skip_nums=() # the fixture has no skip entries to exercise
local ordered got expected
# github-actions (101, 103) by number, then pip (102), then Steward's 104 only when asked for;
# 105 is marola-pr-bot off an update/ branch, never a Steward PR.
for include_steward in 0 1; do
ordered="$(order_prs <"$fixture")"
got="$(jq -c '[.[].number]' <<<"$ordered")"
expected="$([ "$include_steward" -eq 1 ] && echo "[101,103,102,104]" || echo "[101,103,102]")"
echo "order (include_steward=$include_steward): $got"
jq -r '.[] | " #\(.number) \(.ecosystem) \(.headRefName)"' <<<"$ordered"
if [ "$got" != "$expected" ]; then
echo "deps-stack self-test: FAILED — order $got, expected $expected" >&2
exit 1
fi
done
local slug
slug="$(slug_of "dependabot/github_actions/actions/checkout-5")"
[ "$slug" = "actions-checkout-5" ] || { echo "deps-stack self-test: FAILED — slug_of gave '$slug', expected 'actions-checkout-5'" >&2; exit 1; }
Expand Down
18 changes: 18 additions & 0 deletions scripts/fixtures/deps-stack-prs.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,23 @@
"baseRefName": "main",
"mergeable": "MERGEABLE",
"files": [{ "path": ".github/workflows/ci.yml" }]
},
{
"number": 104,
"headRefName": "update/sbt-2.0.10",
"title": "Update sbt to 2.0.10",
"baseRefName": "main",
"mergeable": "MERGEABLE",
"author": { "login": "app/marola-pr-bot" },
"files": [{ "path": "project/build.properties" }]
},
{
"number": 105,
"headRefName": "ml/compiled-prompt-38761019113",
"title": "feat(prompt): recompile the prompts on llama3.2 (marola-ml)",
"baseRefName": "main",
"mergeable": "MERGEABLE",
"author": { "login": "app/marola-pr-bot" },
"files": [{ "path": "prompts/compiled.json" }]
}
]
10 changes: 6 additions & 4 deletions scripts/lib/pr_labels.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,13 @@ pr_label_layers() {
printf '%s\n' "${labels[@]}"
}

# pr_label_kind <author-login> <changed-paths-newline-separated> — best-effort, only when certain.
# pr_label_kind <author-login> <headRefName> <changed-paths-newline-separated> — best-effort, only
# when certain. marola-pr-bot opens Scala Steward's PRs and others too; update/ tells them apart.
pr_label_kind() {
local author="$1" paths="$2"
local author="$1" ref="$2" paths="$3"
case "$author" in
dependabot\[bot\]|app/dependabot|scala-steward\[bot\]|app/scala-steward) echo "kind/deps"; return ;;
dependabot\[bot\]|app/dependabot) echo "kind/deps"; return ;;
app/marola-pr-bot) [[ "$ref" == update/* ]] && { echo "kind/deps"; return; } ;;
esac
if [ -n "$paths" ] && ! grep -qvE '\.md$|^docs/' <<<"$paths"; then
echo "kind/docs-only"
Expand All @@ -137,6 +139,6 @@ pr_label_classify() {
printf '%s\n' $areas
layers="$(pr_label_layers "$paths")"
[ -n "$layers" ] && printf '%s\n' "$layers"
kind="$(pr_label_kind "$author" "$paths")"
kind="$(pr_label_kind "$author" "$ref" "$paths")"
[ -n "$kind" ] && printf '%s\n' "$kind"
}
21 changes: 20 additions & 1 deletion scripts/pr-label.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,35 @@ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=scripts/lib/pr_labels.sh.
source "$script_dir/lib/pr_labels.sh"

dry_run=0; pr_arg=""
dry_run=0; pr_arg=""; self_test=0
for arg in "$@"; do
case "$arg" in
--dry-run) dry_run=1 ;;
--self-test) self_test=1 ;;
-h|--help) sed -n '2,13p' "$0"; exit 0 ;;
[0-9]*|\#[0-9]*) pr_arg="${arg#\#}" ;;
*) echo "pr-label: unrecognized argument: $arg" >&2; exit 1 ;;
esac
done

self_test() {
local fails=0 got
check() { # label, got, want
if [ "$2" = "$3" ]; then echo " ok $1"; else fails=$((fails + 1)); echo " FAIL $1 — got '$2', want '$3'" >&2; fi
}
got="$(pr_label_kind app/marola-pr-bot update/sbt-2.0.10 "project/build.properties")"
check "a marola-pr-bot update/ PR is kind/deps" "$got" "kind/deps"
got="$(pr_label_kind app/marola-pr-bot ml/compiled-prompt-1 "prompts/compiled.json")"
check "a marola-pr-bot PR on another branch is not" "$got" ""
got="$(pr_label_kind app/dependabot dependabot/pip/x "requirements.txt")"
check "a dependabot PR is kind/deps" "$got" "kind/deps"
got="$(pr_label_classify update/sbt-2.0.10 app/marola-pr-bot "Update sbt to 2.0.10" "project/build.properties" | grep '^kind/' || true)"
check "pr_label_classify passes the branch on" "$got" "kind/deps"
echo "pr-label self-test:" "$([ "$fails" -eq 0 ] && echo ok || echo "$fails FAILED")"
[ "$fails" -eq 0 ]
}
if [ "$self_test" -eq 1 ]; then self_test; exit $?; fi

if [ -z "$pr_arg" ]; then
pr_arg="$(gh pr view --json number --jq .number 2>/dev/null)" || {
echo "pr-label: no PR number given and no PR found for the current branch" >&2
Expand Down
2 changes: 1 addition & 1 deletion tests/self-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sh_tests=(
scripts/agents-check.sh scripts/mip-stack.sh scripts/docs-mip-stack.sh
scripts/deps-stack.sh scripts/deps-merge.sh scripts/gha-runner.sh scripts/setup-runners.sh
scripts/runner-preflight.sh scripts/temps.sh scripts/bump-consumers.sh scripts/bump-merge.sh scripts/ruleset-sync.sh scripts/api-docs-push.sh
scripts/graph.sh
scripts/graph.sh scripts/pr-label.sh
plugins/marola-devkit/hooks/format.sh plugins/marola-devkit/hooks/stop-gate.sh
plugins/marola-devkit/hooks/session-start.sh
)
Expand Down
Loading