Add setup-python version upgrade recipe - #219
Merged
timtebeek merged 4 commits intoAug 23, 2026
Merged
Conversation
poketopa
force-pushed
the
feat/setup-python-version-recipe
branch
from
August 23, 2026 13:24
7fe133f to
8adffa2
Compare
poketopa
marked this pull request as ready for review
August 23, 2026 13:30
…erns The recipe carried seven regexes and a `PythonVersion implements Comparable` class to decide whether a `python-version` was below the target. `org.openrewrite.semver.Semver` already answers both halves of that question, so delegate to it: - Concrete versions go through `Semver.compare(.., MAVEN)`. Maven precedence orders a two-part `major.minor`, which `Ecosystem.NODE` rejects as non-strict SemVer. - Ranges are decided by the comparator alone. Probing the target plus an implausibly high version separates a range bounded below the target (`<=3.13`, `~3.12`, `2.x`) from one that is open-ended or already compatible (`>=3.9`, `^3.9`, `3.x`, `>=4 || <3.14`), without any arithmetic on captured groups. One pattern remains, because that is the branch point the library cannot give us: `Semver.isVersion` returns true for `3.x` and `3.7 - 3.9`, as `RELEASE_PATTERN` swallows the trailing text into its qualifier group. The block scalar skip is now keyed on the scalar style rather than on a version pattern failing to match. A FOLDED/LITERAL value carries the block envelope, which `Yaml.Scalar#withValue` would clobber, so it is a write-safety guard rather than a version question, and it also covers the folded spelling the regex mismatch happened to miss. Also inline the visitor as an anonymous class, since it holds no per-visit state, and put a `python-version` key comparison in front of `JsonPathMatcher.matches`. That matcher re-visits the enclosing document on every call, so testing it against every mapping entry in a workflow made the cost quadratic in file size. Test indentation now follows `.editorconfig`, which sets a continuation indent of 2 for `src/test/java`.
`python-version: |` followed by a single version was left untouched,
because the recipe skipped every FOLDED/LITERAL scalar. That skip was
needed for write safety rather than for any version reason: the raw
`Yaml.Scalar#value` of a block scalar carries the envelope, so
`withValue("3.14")` collapsed the whole thing to `python-version: |3.14`.
`BlockScalar` exists for exactly this, and reads and writes the body
without touching the envelope. Use it, so a lone version in a block is
raised like any other.
A block holding several versions is still left alone. That is a
deliberate test matrix, and raising each entry below the target would
just repeat the target.
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's changed?
SetupPythonUpgradePythonVersion, following the existing setup-node/setup-java recipe shape.python-versionvalues and safely older npm-semver ranges to a requiredMAJOR.MINORtarget.actions/setup-pythonstep that usespython-version-file.What's your motivation?
actions/setup-pythonusers need a reusable recipe that raises outdated Python versions without changing workflows that source their version from a file.Anything in particular you'd like reviewers to focus on?
Please review the conservative range handling and the step-level
python-version-fileexclusion.Anyone you would like to review specifically?
No preference.
Have you considered any alternatives or workarounds?
An exact-version-only implementation would be simpler, but would leave valid older ranges unchanged. This implementation only rewrites range forms it can prove are below the requested target and leaves ambiguous or union ranges untouched.
Any additional context
This follows the maintainer guidance in #85 to use
SetupNodeUpgradeNodeVersionas the direct template and skip steps usingpython-version-file.Validation completed locally:
./gradlew test --tests org.openrewrite.github.SetupPythonUpgradePythonVersionTest --rerun-tasks— BUILD SUCCESSFUL./gradlew recipeCsvGenerate recipeCsvValidate— BUILD SUCCESSFUL; content and completeness validation passed./gradlew check— BUILD SUCCESSFUL./gradlew build— BUILD SUCCESSFULgit diff --check upstream/main— passedChecklist
./gradlew build../gradlew recipeCsvGenerateand committed the updatedrecipes.csv.git diff --check.