diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3d7e8..0d21a43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ Each release moves `plugin.json`'s `version`, the flake package version and ever together. A consumer adopts one by bumping its flake input, its workflow `@v…`/`devkit-ref` and its marketplace `ref` at once. +## Unreleased + +- `deps-merge --repo OWNER/REPO --unattended` merges only a green Dependabot PR that changes Python + manifests alone, every version by a patch or minor (read from the diff), with no `hold` label and no + commit by anyone else, pinned to the head commit its checks ran on. The interactive mode is unchanged + (marola-dev/marola#740, #150). + ## v0.17.0 — 2026-10-17 - **Breaking** for a caller that passes `token:` to `skills-update`: the secret is no longer declared, diff --git a/docs/4-reference_tools.md b/docs/4-reference_tools.md index 87c6b86..8cf83c4 100644 --- a/docs/4-reference_tools.md +++ b/docs/4-reference_tools.md @@ -17,7 +17,7 @@ making them. A tool that calls `gh` needs a login on the host; inside ai-jail it | `mip-stack` | `mip-stack.sh` | `just mip-stack` | Chain every open MIP draft PR into one GitHub Stack, resolving the shared `docs/MIPs/README.md` row conflict; `--resume`, `--skip `, `status`, `clean` | | `docs-mip-stack` | `docs-mip-stack.sh` | `just docs-mip-stack` | Chain un-merged `docs/mip-NNNN-*` branches: `auto` (push and plan the unambiguous local ones), `list`, `plan …` (conflict check with `git merge-tree`, prints the `gh pr create` lines) | | `deps-stack` | `deps-stack.sh` | `just deps-stack` | Chain open dependency-update PRs into one GitHub Stack, auto-resolving bump-vs-bump conflicts; `--resume`, `--skip`, `--include-steward`, `status`, `clean`. Built in `.tmp/wt-deps-stack`, never the caller's checkout | -| `deps-merge` | `deps-merge.sh` | `just deps-merge` | Merge every open dependency PR that is mergeable with every check green (skipped and neutral count as green); reports the rest | +| `deps-merge` | `deps-merge.sh` | `just deps-merge` | Merge every open dependency PR that is mergeable with every check green (skipped and neutral count as green); reports the rest. `--repo OWNER/REPO --unattended`, for a scheduled job, also needs Dependabot as the author of the PR and every commit, no `hold` label, only Python manifests changed (`requirements*.txt`, `pyproject.toml`, a lock file), and each changed line one dependency whose versions moved by a patch or minor (a 0.x minor counts as major), read from the diff; it pins the merge to the checked head commit and writes one stderr line per refused PR. The caller checks `AUTO_MERGE` | | `bump-merge` | `bump-merge.sh` | `just bump-merge` | Squash-merge the newest release's `chore/devkit-vX.Y.Z` PRs in every `consumers.txt` repo the umbrella first, with an optional soak and canary delay (both off by default); `--dry-run` prints each decision and command, `--now EPOCH` sets the clock. Run hourly and on `bump-ready` by `bump-merge.yml`; the rules are in [workflows](4-reference_workflows.md#bump-merge-not-reusable) | | `branches` | `branches.sh` | `just branches-clean`, `just branches-open` | `clean` deletes local branches whose PR merged (local and remote ref); `open` opens a `main`-based PR for each plain branch ahead of `main` with none | diff --git a/scripts/deps-merge.sh b/scripts/deps-merge.sh index 5e12c0e..b159ce4 100755 --- a/scripts/deps-merge.sh +++ b/scripts/deps-merge.sh @@ -4,7 +4,8 @@ # just deps-merge # merge what is ready, report what is not and why # just deps-merge --dry-run # print the gh commands, mutate nothing # just deps-merge --from-json f # judge this file instead of calling gh (testing) -# just deps-merge --self-test # scripts/fixtures/deps-merge-prs.json, assert the verdicts +# just deps-merge --self-test # scripts/fixtures/deps-merge-*.json, assert the verdicts +# deps-merge --repo OWNER/REPO --unattended # the scheduled job's mode, stricter rules below # # The counterpart to deps-stack.sh: that one chains open dependency PRs into a stack when they # arrive separately, this one closes them out. A PR is merged only when GitHub says MERGEABLE and @@ -14,27 +15,43 @@ # # Order is deps-stack's: github-actions first, then pip, each by PR number ascending, so a run # that merges several leaves the smallest rebase for whatever is left. +# +# --unattended (marola-dev/marola#740, MIP-0086 §5.5) also requires: Dependabot as author of the PR +# and of every commit, no `hold` label, only Python manifests changed, and every changed manifest +# line the same dependency with only its versions moved, none by a major (on 0.x, a minor counts as +# major). Versions come from the diff, not the PR text: Dependabot's pip range updates carry no +# update-type. The merge pins the head commit the checks ran on. The caller checks AUTO_MERGE. set -euo pipefail script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" dry=0 from_json="" self_test=0 +unattended=false +repo="" while [ $# -gt 0 ]; do case "$1" in --dry-run) dry=1 ;; --from-json) from_json="${2:?--from-json needs a file}"; shift ;; --self-test) self_test=1 ;; - -h|--help) sed -n '2,16p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;; + --unattended) unattended=true ;; + --repo) repo="${2:?--repo needs OWNER/REPO}"; shift ;; + -h|--help) sed -n '2,23p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "deps-merge: unknown argument: $1" >&2; exit 2 ;; esac shift done +if [ "$unattended" = true ] && [ -z "$repo" ] && [ "$self_test" -eq 0 ]; then + echo "deps-merge: --unattended needs --repo OWNER/REPO" >&2; exit 2 +fi +repo_args=() +[ -z "$repo" ] || repo_args=(-R "$repo") -# stdin: gh-pr-list-shaped JSON -> stdout: same, ordered, each with .ecosystem/.ready/.reason +# $1 true|false overrides --unattended. stdin: gh-pr-list-shaped JSON -> stdout: same, ordered, +# each with .ecosystem/.ready/.reason judge() { - jq ' + jq --argjson unattended "${1:-$unattended}" ' def eco: if (.headRefName | test("^dependabot/github_actions/")) then "github-actions" elif (.headRefName | test("^dependabot/pip/")) then "pip" @@ -56,7 +73,51 @@ judge() { elif running > 0 then "checks still running" elif (red | length) > 0 then "checks not green (\(red | unique | join(", ")))" else "" end; - map(. + {ecosystem: eco, rank: rank, reason: verdict}) + def manifest: test("(^|/)(requirements[^/]*\\.txt|pyproject\\.toml)$"); + def lockfile: test("(^|/)(uv|poetry|pdm|Pipfile)\\.lock$"); + def ver_re: "[0-9]+(?:\\.[0-9]+)*"; + # A requirements line or a quoted pyproject entry; `version = "1.2"` has no operator, so is not one. + def dep_line: + .[0:1] as $sign | .[1:] | sub("^\\s+"; "") | sub("^[\"\u0027]"; "") | sub("[\"\u0027],?\\s*$"; "") + | ([capture("^(?[A-Za-z0-9][A-Za-z0-9._-]*)(?\\s*(\\[[^]]*\\])?\\s*(===|==|>=|<=|~=|!=|<|>).*)$")] | first) + | if . == null then null + else {sign: $sign, name: (.name | ascii_downcase | gsub("[-_.]+"; "-")), rest: .rest} end; + def parts: split(".") | map(tonumber); + def is_major($o; $n): ($o | parts) as $a | ($n | parts) as $b + | $a[0] != $b[0] or ($a[0] == 0 and ($a[1] // 0) != ($b[1] // 0)); + def pair_verdict: + (map(select(.sign == "-"))[0].rest) as $o | (map(select(.sign == "+"))[0].rest) as $n + | ($o | [scan(ver_re)]) as $ov | ($n | [scan(ver_re)]) as $nv + | if ($o | gsub(ver_re; "V")) != ($n | gsub(ver_re; "V")) then "changes more than a version (\(.[0].name))" + elif any(range(0; $ov | length); is_major($ov[.]; $nv[.])) then + "a major update (\(.[0].name): \($o | ltrimstr(" ")) -> \($n | ltrimstr(" ")))" + else "" end; + def versions_verdict: + [.files[] | select(.path | manifest) | .patch | split("\n")[] | select(test("^[-+]")) | dep_line] as $ls + | ($ls | map(select(. != null)) | group_by(.name)) as $groups + | if any($ls[]; . == null) then "changes a manifest line that is not a dependency" + elif ($groups | length) == 0 then "no dependency version change to judge" + else ([$groups[] | select((map(.sign) | sort) != ["+", "-"]) | .[0].name] | first) as $odd + | if $odd != null then "adds or removes a dependency (\($odd))" + else ([$groups[] | pair_verdict | select(. != "")] | first) // "" end + end; + def unattended_verdict: + ([.files[]?.path | select((manifest or lockfile) | not)] | first) as $stray + | ([.files[]? | select((.path | manifest) and .patch == null) | .path] | first) as $nodiff + | if (.author.login // "") as $a | ["app/dependabot", "dependabot[bot]"] | index($a) | not + then "author is not Dependabot (\(.author.login // "unknown"))" + elif any(.labels[]?; .name == "hold") then "labelled hold" + elif (.commits // []) | length == 0 then "no commits reported" + elif any(.commits[].authors[]?; .login != "dependabot[bot]") then "has a commit not by Dependabot" + elif (.files // []) | length == 0 then "no changed files reported" + elif $stray != null then "changes a file outside the Python manifests (\($stray))" + elif $nodiff != null then "no diff for \($nodiff)" + elif any(.commits[]; (.messageBody // "") | test("update-type: version-update:semver-major")) + then "Dependabot marks a major update" + else versions_verdict end; + map(. + {ecosystem: eco, rank: rank, + reason: (if $unattended then (verdict as $v | unattended_verdict | if . == "" then $v else . end) + else verdict end)}) | map(. + {ready: (.reason == "")}) | sort_by([.rank, .number]) ' @@ -68,8 +129,20 @@ discover() { return 0 fi gh auth status >/dev/null 2>&1 || { echo "gh is not logged in — run: gh auth login" >&2; exit 1; } - gh pr list --state open --search "author:app/dependabot" \ - --json number,title,headRefName,mergeable,statusCheckRollup + if [ "$unattended" = false ]; then + gh pr list "${repo_args[@]}" --state open --search "author:app/dependabot" \ + --json number,title,headRefName,mergeable,statusCheckRollup + return 0 + fi + local pr n files + gh pr list -R "$repo" --state open --search "author:app/dependabot" \ + --json number,title,headRefName,headRefOid,mergeable,statusCheckRollup,author,labels,commits \ + | jq -c '.[]' | while read -r pr; do + n="$(jq -r .number <<<"$pr")" + # gh pr list has the paths but not the patches the version rule reads. + files="$(gh api --paginate "repos/$repo/pulls/$n/files" -q '.[] | {path: .filename, patch}' | jq -s .)" + jq --argjson files "$files" '. + {files: $files}' <<<"$pr" + done | jq -s . } self_test() { @@ -100,6 +173,40 @@ self_test() { got="$(jq -r '.[] | select(.number == 302) | .ready' <<<"$judged")" check "SKIPPED jobs count as green (ci.yml's path filters skip most)" "$got" "true" + fixture="$script_dir/fixtures/deps-merge-unattended.json" + got="$(judge <"$fixture" | jq -c '[.[] | select(.ready) | .number]')" + check "interactive mode ignores the unattended rules" "$got" "[401,402,403,404,407,408,409,410,411,412,413,414]" + judged="$(judge true <"$fixture")" + got="$(jq -c '[.[] | select(.ready) | .number]' <<<"$judged")" + check "unattended: only green patch/minor manifest-only Dependabot PRs merge" "$got" "[401,412]" + while IFS='|' read -r n want; do + got="$(jq -r --argjson n "$n" '.[] | select(.number == $n) | .reason' <<<"$judged")" + check "unattended refuses #$n" "$got" "$want" + done <<'EOF' +402|a major update (dspy: >=3.4.1,<4 -> >=4.0.0,<5) +403|author is not Dependabot (app/marola-pr-bot) +404|changes a file outside the Python manifests (.github/workflows/compile-prompt.yml) +405|checks not green (FAILURE) +406|checks still running +407|labelled hold +408|has a commit not by Dependabot +409|adds or removes a dependency (evil-pkg) +410|a major update (peft: >=0.21.2 -> >=0.22.0) +411|Dependabot marks a major update +413|a major update (mlflow: >=3.17.1,<4 -> >=3.17.1,<5) +414|changes a manifest line that is not a dependency +EOF + local out err + err="$(mktemp)" + out="$(bash "${BASH_SOURCE[0]}" --unattended --repo o/r --from-json "$fixture" --dry-run 2>"$err")" + got="$(grep -c '^+ gh pr merge' <<<"$out")" + check "unattended dry run merges two" "$got" "2" + got="$(grep -c -- '+ gh pr merge 401 -R o/r --squash --delete-branch --match-head-commit 401aaaa$' <<<"$out")" + check "the merge pins the head commit the checks ran on" "$got" "1" + got="$(grep -c '^deps-merge: #[0-9]* refused: ' "$err")" + check "one stderr line per refused PR" "$got" "12" + rm -f "$err" + echo "deps-merge self-test:" "$([ "$fails" -eq 0 ] && echo ok || echo "$fails FAILED")" [ "$fails" -eq 0 ] } @@ -112,24 +219,31 @@ if [ "$(jq 'length' <<<"$judged")" -eq 0 ]; then exit 0 fi -jq -r '.[] | " #\(.number) \(.ecosystem) \(if .ready then "READY" else "skip — \(.reason)" end)\n \(.title)"' <<<"$judged" +if [ "$unattended" = true ]; then + jq -r '.[] | select(.ready) | " #\(.number) READY \(.title)"' <<<"$judged" + jq -r '.[] | select(.ready | not) | "deps-merge: #\(.number) refused: \(.reason)"' <<<"$judged" >&2 +else + jq -r '.[] | " #\(.number) \(.ecosystem) \(if .ready then "READY" else "skip — \(.reason)" end)\n \(.title)"' <<<"$judged" +fi merged=0 failed=0 -while read -r n; do +while read -r n oid; do [ -n "$n" ] || continue + cmd=(gh pr merge "$n" "${repo_args[@]}" --squash --delete-branch) + [ "$unattended" = false ] || cmd+=(--match-head-commit "$oid") if [ "$dry" -eq 1 ]; then - echo "+ gh pr merge $n --squash --delete-branch" + echo "+ ${cmd[*]}" continue fi echo "merging #$n ..." - if gh pr merge "$n" --squash --delete-branch; then + if "${cmd[@]}"; then merged=$((merged + 1)) else failed=$((failed + 1)) echo "deps-merge: #$n did not merge — left open" >&2 fi -done < <(jq -r '.[] | select(.ready) | .number' <<<"$judged") +done < <(jq -r '.[] | select(.ready) | "\(.number) \(.headRefOid // "")"' <<<"$judged") [ "$dry" -eq 1 ] && { echo "dry run: nothing was merged"; exit 0; } echo "deps-merge: merged $merged, left $(jq '[.[] | select(.ready | not)] | length' <<<"$judged") not ready, $failed failed" diff --git a/scripts/fixtures/deps-merge-unattended.json b/scripts/fixtures/deps-merge-unattended.json new file mode 100644 index 0000000..3dda46d --- /dev/null +++ b/scripts/fixtures/deps-merge-unattended.json @@ -0,0 +1,170 @@ +[ + { + "number": 401, + "title": "green patch and minor pip updates in two directories", + "headRefName": "dependabot/marola_ml_deps-401", + "headRefOid": "401aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,7 @@\n # Pin loosely\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n \n@@ -20,4 +20,4 @@\n-mlflow>=3.16.1,<4\n+mlflow>=3.17.1,<4"}, {"path": "finetune/requirements.txt", "patch": "@@ -1,16 +1,16 @@\n torch>=2.14.1\n-transformers>=5.18.0\n+transformers>=5.19.0\n peft>=0.21.2\n-huggingface_hub>=2.1.1\n+huggingface_hub>=2.2.0\n\\ No newline at end of file"}] + }, + { + "number": 402, + "title": "a major: dspy 3 to 4", + "headRefName": "dependabot/marola_ml_deps-402", + "headRefOid": "402aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,1 +1,1 @@\n-dspy>=3.4.1,<4\n+dspy>=4.0.0,<5"}] + }, + { + "number": 403, + "title": "a PR not opened by Dependabot", + "headRefName": "dependabot/marola_ml_deps-403", + "headRefOid": "403aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/marola-pr-bot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,7 @@\n # Pin loosely\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n \n@@ -20,4 +20,4 @@\n-mlflow>=3.16.1,<4\n+mlflow>=3.17.1,<4"}] + }, + { + "number": 404, + "title": "a grouped PR that also bumps a workflow", + "headRefName": "dependabot/marola_ml_deps-404", + "headRefOid": "404aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": ".github/workflows/compile-prompt.yml", "patch": "@@ -38,7 +38,7 @@\n- - uses: actions/setup-python@v5\n+ - uses: actions/setup-python@v7"}, {"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,7 @@\n # Pin loosely\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n \n@@ -20,4 +20,4 @@\n-mlflow>=3.16.1,<4\n+mlflow>=3.17.1,<4"}] + }, + { + "number": 405, + "title": "a red check", + "headRefName": "dependabot/marola_ml_deps-405", + "headRefOid": "405aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "FAILURE"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,7 @@\n # Pin loosely\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n \n@@ -20,4 +20,4 @@\n-mlflow>=3.16.1,<4\n+mlflow>=3.17.1,<4"}] + }, + { + "number": 406, + "title": "a pending check", + "headRefName": "dependabot/marola_ml_deps-406", + "headRefOid": "406aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "IN_PROGRESS", "conclusion": null}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,7 @@\n # Pin loosely\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n \n@@ -20,4 +20,4 @@\n-mlflow>=3.16.1,<4\n+mlflow>=3.17.1,<4"}] + }, + { + "number": 407, + "title": "held by a person", + "headRefName": "dependabot/marola_ml_deps-407", + "headRefOid": "407aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}, {"name": "hold"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,7 @@\n # Pin loosely\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n \n@@ -20,4 +20,4 @@\n-mlflow>=3.16.1,<4\n+mlflow>=3.17.1,<4"}] + }, + { + "number": 408, + "title": "a person pushed onto the Dependabot branch", + "headRefName": "dependabot/marola_ml_deps-408", + "headRefOid": "408aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}, {"authors": [{"login": "someone"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,7 @@\n # Pin loosely\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n \n@@ -20,4 +20,4 @@\n-mlflow>=3.16.1,<4\n+mlflow>=3.17.1,<4"}] + }, + { + "number": 409, + "title": "a new dependency slipped into a bump", + "headRefName": "dependabot/marola_ml_deps-409", + "headRefOid": "409aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,8 @@\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n+evil-pkg>=1.0"}] + }, + { + "number": 410, + "title": "a 0.x minor counts as major", + "headRefName": "dependabot/marola_ml_deps-410", + "headRefOid": "410aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "finetune/requirements.txt", "patch": "@@ -1,1 +1,1 @@\n-peft>=0.21.2\n+peft>=0.22.0"}] + }, + { + "number": 411, + "title": "Dependabot's own metadata says major", + "headRefName": "dependabot/marola_ml_deps-411", + "headRefOid": "411aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "---\nupdated-dependencies:\n- dependency-name: dspy\n update-type: version-update:semver-major\n..."}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,7 +1,7 @@\n # Pin loosely\n-dspy>=3.4.0,<4\n+dspy>=3.4.1,<4\n \n@@ -20,4 +20,4 @@\n-mlflow>=3.16.1,<4\n+mlflow>=3.17.1,<4"}] + }, + { + "number": 412, + "title": "a pyproject bump with its lock file", + "headRefName": "dependabot/marola_ml_deps-412", + "headRefOid": "412aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "pyproject.toml", "patch": "@@ -8,3 +8,3 @@\n dependencies = [\n- \"httpx[http2]>=0.28.1,<1\",\n+ \"httpx[http2]>=0.28.2,<1\",\n ]"}, {"path": "uv.lock", "patch": "@@ -100,3 +100,3 @@\n-version = \"0.28.1\"\n+version = \"0.28.2\""}] + }, + { + "number": 413, + "title": "a raised upper bound is a major", + "headRefName": "dependabot/marola_ml_deps-413", + "headRefOid": "413aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "dspy/requirements.txt", "patch": "@@ -1,1 +1,1 @@\n-mlflow>=3.17.1,<4\n+mlflow>=3.17.1,<5"}] + }, + { + "number": 414, + "title": "the project's own version is not a dependency", + "headRefName": "dependabot/marola_ml_deps-414", + "headRefOid": "414aaaa", + "mergeable": "MERGEABLE", + "author": {"login": "app/dependabot", "is_bot": true}, + "labels": [{"name": "dependencies"}], + "commits": [{"authors": [{"login": "dependabot[bot]"}], "messageBody": "Updates `dspy` to 3.4.1\n\nSigned-off-by: dependabot[bot] "}], + "statusCheckRollup": [{"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS"}, {"__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SKIPPED"}], + "files": [{"path": "pyproject.toml", "patch": "@@ -1,3 +1,3 @@\n-version = \"1.2.0\"\n+version = \"1.3.0\""}] + } +]