renovate-apply 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- renovate_apply-0.1.0/LICENSE +21 -0
- renovate_apply-0.1.0/PKG-INFO +219 -0
- renovate_apply-0.1.0/README.md +193 -0
- renovate_apply-0.1.0/pyproject.toml +244 -0
- renovate_apply-0.1.0/pyproject.toml.orig +244 -0
- renovate_apply-0.1.0/src/renovate_apply/__init__.py +19 -0
- renovate_apply-0.1.0/src/renovate_apply/config.py +50 -0
- renovate_apply-0.1.0/src/renovate_apply/errors.py +45 -0
- renovate_apply-0.1.0/src/renovate_apply/files.py +99 -0
- renovate_apply-0.1.0/src/renovate_apply/git.py +86 -0
- renovate_apply-0.1.0/src/renovate_apply/lockfiles.py +83 -0
- renovate_apply-0.1.0/src/renovate_apply/main.py +295 -0
- renovate_apply-0.1.0/src/renovate_apply/messages.py +74 -0
- renovate_apply-0.1.0/src/renovate_apply/models.py +124 -0
- renovate_apply-0.1.0/src/renovate_apply/py.typed +0 -0
- renovate_apply-0.1.0/src/renovate_apply/replace.py +274 -0
- renovate_apply-0.1.0/src/renovate_apply/report.py +185 -0
- renovate_apply-0.1.0/src/renovate_apply/scan.py +117 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Julio Batista Silva
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: renovate-apply
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Apply the dependency updates Renovate finds, locally, without a pull request
|
|
5
|
+
Keywords: renovate,dependencies,updates,cli,docker,pre-commit,github-actions
|
|
6
|
+
Author: Julio Batista Silva
|
|
7
|
+
Author-email: Julio Batista Silva <python@juliobs.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
16
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
17
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
18
|
+
Classifier: Topic :: System :: Software Distribution
|
|
19
|
+
Requires-Dist: cyclopts>=5.2.0
|
|
20
|
+
Requires-Dist: rich>=15.0.0
|
|
21
|
+
Requires-Python: >=3.15, <4
|
|
22
|
+
Project-URL: Documentation, https://jbsilva.github.io/renovate-apply/
|
|
23
|
+
Project-URL: Repository, https://github.com/jbsilva/renovate-apply
|
|
24
|
+
Project-URL: Issues, https://github.com/jbsilva/renovate-apply/issues
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# renovate-apply
|
|
28
|
+
|
|
29
|
+
[](https://github.com/jbsilva/renovate-apply/blob/main/LICENSE)
|
|
30
|
+
[](https://www.python.org/downloads/)
|
|
31
|
+
[](https://pypi.org/project/renovate-apply)
|
|
32
|
+
[](https://jbsilva.github.io/renovate-apply/)
|
|
33
|
+
[](https://github.com/jbsilva/renovate-apply/actions/workflows/tests.yml)
|
|
34
|
+
[](https://github.com/jbsilva/renovate-apply/actions/workflows/codeql.yml)
|
|
35
|
+
[](https://sonarcloud.io/summary/new_code?id=jbsilva_renovate-apply)
|
|
36
|
+
[](https://codecov.io/github/jbsilva/renovate-apply)
|
|
37
|
+
[](https://github.com/astral-sh/ruff)
|
|
38
|
+
[](https://github.com/astral-sh/uv)
|
|
39
|
+
[](https://github.com/j178/prek)
|
|
40
|
+
|
|
41
|
+
Apply the dependency updates [Renovate](https://docs.renovatebot.com) finds, in your working copy,
|
|
42
|
+
without a pull request. One commit per group, the same grouping your Renovate configuration gives a
|
|
43
|
+
pull request. Nothing is pushed.
|
|
44
|
+
|
|
45
|
+
**Full documentation:** <https://jbsilva.github.io/renovate-apply/>
|
|
46
|
+
|
|
47
|
+
```console
|
|
48
|
+
$ rnv
|
|
49
|
+
renovate-apply: scanning /home/you/infra ...
|
|
50
|
+
applied deps: update the atuin images
|
|
51
|
+
ghcr.io/atuinsh/atuin v18.13.3 -> 18.22.0
|
|
52
|
+
postgres 18-alpine, new digest
|
|
53
|
+
applied deps: update the hook revs
|
|
54
|
+
astral-sh/ruff-pre-commit v0.16.5 -> v0.16.8
|
|
55
|
+
crate-ci/typos v1.50.1 -> v1.50.2
|
|
56
|
+
|
|
57
|
+
Committed. Nothing is pushed.
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Why this exists
|
|
61
|
+
|
|
62
|
+
Renovate can read a local working copy. `renovate --platform=local` finds every pending update and
|
|
63
|
+
reports it. It cannot write one: the local platform turns the run into a lookup-only dry run as it
|
|
64
|
+
starts, so the run ends after the lookup, before the step that edits a file. No flag turns that off,
|
|
65
|
+
because the clamp is inside the platform, not in the configuration.
|
|
66
|
+
|
|
67
|
+
So the updates Renovate finds on your laptop can only be applied by hand, or by merging the pull
|
|
68
|
+
requests it opens on the server. This tool is the missing half: it runs that same scan, reads the
|
|
69
|
+
JSON report, and makes the replacement the report describes for each update.
|
|
70
|
+
|
|
71
|
+
The edit is not guesswork. For every dependency, the report carries `replaceString`, the exact text
|
|
72
|
+
the manager found in the file, and `autoReplaceStringTemplate`, how to write that text again with
|
|
73
|
+
the new version or digest in it. That pair is Renovate's own auto-replace, handed over as data.
|
|
74
|
+
|
|
75
|
+
## Highlights
|
|
76
|
+
|
|
77
|
+
- Works with everything Renovate's own managers report: image tags and digests in compose files and
|
|
78
|
+
Dockerfiles, GitHub Actions SHA pins with their version comment, pre-commit and prek hook revs,
|
|
79
|
+
versions matched by a custom regex manager, and the rest
|
|
80
|
+
- One commit per Renovate branch, so a grouped pull request and a local run produce the same commits
|
|
81
|
+
- Refreshes `uv.lock` when it raises a floor in `pyproject.toml`, and commits the two together, the
|
|
82
|
+
way Renovate's own pull request carries them
|
|
83
|
+
- Reads your repository's Renovate configuration by running Renovate itself, so grouping, holds,
|
|
84
|
+
package rules and versioning schemes all apply without being restated here
|
|
85
|
+
- Holds every major back until you ask for it
|
|
86
|
+
- Never skips your git hooks, and never commits anything it did not write
|
|
87
|
+
- Never leaves a `pyproject.toml` that does not lock: such a group is undone and held back
|
|
88
|
+
- `--dry-run` to read first, `--no-commit` or `--stage` to review everything at once
|
|
89
|
+
|
|
90
|
+
## Requirements
|
|
91
|
+
|
|
92
|
+
- Python 3.15+
|
|
93
|
+
- `git`
|
|
94
|
+
- Either `renovate` on `PATH` (`npm i -g renovate`) or Node.js, for `npx` to fetch it
|
|
95
|
+
- Optional: [`gh`](https://cli.github.com), logged in, or `GITHUB_TOKEN` in the environment. Without
|
|
96
|
+
a token, everything Renovate looks up on GitHub is rate limited, and updates can silently go
|
|
97
|
+
missing. Prefer either to `--token`, which puts the token in argv and in your shell history.
|
|
98
|
+
|
|
99
|
+
## Installation
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
uv tool install renovate-apply
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Or run it without installing:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
uvx renovate-apply --dry-run
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Usage
|
|
112
|
+
|
|
113
|
+
```console
|
|
114
|
+
$ renovate-apply --help
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Command | What it does |
|
|
118
|
+
| ----------------------------- | --------------------------------------------------------- |
|
|
119
|
+
| `rnv` | Scan, apply, one commit per group |
|
|
120
|
+
| `rnv --dry-run` | Say what would change and write nothing |
|
|
121
|
+
| `rnv --no-commit` | Write the files and stop, for one review of everything |
|
|
122
|
+
| `rnv --stage` | Write and stage the files, and commit nothing |
|
|
123
|
+
| `rnv --major` | Include the major updates, which are held back by default |
|
|
124
|
+
| `rnv --skip python` | Hold a dependency back by name, as a glob, repeatable |
|
|
125
|
+
| `rnv --prefix "build(deps):"` | Set the first word of every commit subject |
|
|
126
|
+
| `rnv --no-lock` | Do not refresh `uv.lock` after editing a `pyproject.toml` |
|
|
127
|
+
| `rnv ~/code/other-repo` | Work on another repository |
|
|
128
|
+
|
|
129
|
+
`renovate-apply` and `rnv` are the same command.
|
|
130
|
+
|
|
131
|
+
The working copy must be clean before a run that commits: a commit takes the whole index, not only
|
|
132
|
+
the files this staged, so anything else in flight would be swept into a dependency commit.
|
|
133
|
+
|
|
134
|
+
### Commit messages
|
|
135
|
+
|
|
136
|
+
The subject says what a person needs in `git log --oneline`; the body lists the group, aligned:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
deps: update the action pins
|
|
140
|
+
|
|
141
|
+
astral-sh/setup-uv v10.1.0 -> v10.2.0
|
|
142
|
+
github/codeql-action v4.38.0 -> v4.38.2
|
|
143
|
+
docker/setup-buildx-action v4.3.0 -> v4.4.1
|
|
144
|
+
actions/upload-artifact v7.0.1 -> v7.0.2
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Match `--prefix` to what your `renovate.json5` writes, so local commits and merged pull requests
|
|
148
|
+
read alike: `deps:` for `commitMessagePrefix`, `build(deps):` for semantic commits.
|
|
149
|
+
|
|
150
|
+
### Per-repository defaults
|
|
151
|
+
|
|
152
|
+
Any option can be set once, in the `pyproject.toml` of the repository being updated:
|
|
153
|
+
|
|
154
|
+
```toml
|
|
155
|
+
[tool.renovate-apply]
|
|
156
|
+
prefix = "build(deps):"
|
|
157
|
+
skip = ["python"]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The file is read from the directory being updated, so `rnv ~/code/other-repo` takes that
|
|
161
|
+
repository's settings and not those of wherever you happen to be standing. Only that directory is
|
|
162
|
+
read, never a parent of it, and a flag typed for one run wins over the file.
|
|
163
|
+
|
|
164
|
+
A repository without a `pyproject.toml` (an infrastructure repository, say) passes the flags
|
|
165
|
+
instead.
|
|
166
|
+
|
|
167
|
+
## What is held back
|
|
168
|
+
|
|
169
|
+
Nothing is applied silently past a hold. Whatever is held is listed at the end of the run, with the
|
|
170
|
+
reason:
|
|
171
|
+
|
|
172
|
+
- **Majors**, unless `--major`. Renovate can be configured to send some majors to the dependency
|
|
173
|
+
dashboard to be approved by hand. The report does not say which ones carry that flag, so holding
|
|
174
|
+
every major is the only reading that cannot contradict your configuration.
|
|
175
|
+
- **Updates Renovate would not open yet**, the ones still waiting out `minimumReleaseAge`. A
|
|
176
|
+
security fix bypasses that hold in Renovate, and bypasses it here too.
|
|
177
|
+
- **Renames**, where Renovate wants to replace a dependency with a different one. That edit is not a
|
|
178
|
+
version bump, and this tool does not write it.
|
|
179
|
+
- **Lock file updates**, where the range in the manifest already allows the new version, so only the
|
|
180
|
+
lock file beside it moves. There is no edit to make in the package file, and moving the lock file
|
|
181
|
+
takes that package manager. Only `uv.lock` is refreshed here, after an edit to its manifest.
|
|
182
|
+
- **Updates that do not lock**, where `uv lock` finds no solution for the new floor in
|
|
183
|
+
`pyproject.toml`. The group is undone, `uv.lock` included, and uv's explanation is printed.
|
|
184
|
+
- **A line two managers found**, when a regex manager in your Renovate configuration matches a line
|
|
185
|
+
a built-in manager also reads. Renovate reports the line twice. The first update makes the edit,
|
|
186
|
+
and the second is listed with it.
|
|
187
|
+
- **Anything named in `--skip`**, as a glob. That is the answer to the blind spot above on an update
|
|
188
|
+
that is not a major: a repository that keeps `requires-python` on the dependency dashboard writes
|
|
189
|
+
`skip = ["python"]` once and stops being asked.
|
|
190
|
+
|
|
191
|
+
## Limits
|
|
192
|
+
|
|
193
|
+
- **`dependencyDashboardApproval` is invisible.** Renovate resolves it per package and the report
|
|
194
|
+
does not carry it, so this tool cannot see which updates your configuration wants approved by
|
|
195
|
+
hand. Holding every major covers the usual case; `--skip` covers the rest.
|
|
196
|
+
- **Lock files: uv only.** A `pyproject.toml` with a `uv.lock` beside it is relocked and committed
|
|
197
|
+
together. Any other package manager's lock file is left untouched, so run its update yourself.
|
|
198
|
+
- **Renovate's lock file maintenance is not applied.** Those branches carry no dependency updates,
|
|
199
|
+
so they are not in the report at all.
|
|
200
|
+
- **A group that cannot be applied is reported and passed over.** The groups after it are still
|
|
201
|
+
applied and committed, and the run ends non-zero. Only a failure before the first group, such as a
|
|
202
|
+
scan that did not run or a working copy that is not clean, ends the run with nothing done.
|
|
203
|
+
- The scan is a full Renovate run, so it takes as long as Renovate takes, usually under a minute.
|
|
204
|
+
- A dependency Renovate cannot look up is one this cannot apply. Everything upstream of the report
|
|
205
|
+
is Renovate's own behavior, configuration included.
|
|
206
|
+
- This writes files and commits. It never pushes, never merges and never deploys.
|
|
207
|
+
|
|
208
|
+
## Development
|
|
209
|
+
|
|
210
|
+
```sh
|
|
211
|
+
uv sync --group dev --group test
|
|
212
|
+
uv run pytest
|
|
213
|
+
uv run poe check
|
|
214
|
+
uv run poe docs # preview the documentation site
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# renovate-apply
|
|
2
|
+
|
|
3
|
+
[](https://github.com/jbsilva/renovate-apply/blob/main/LICENSE)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://pypi.org/project/renovate-apply)
|
|
6
|
+
[](https://jbsilva.github.io/renovate-apply/)
|
|
7
|
+
[](https://github.com/jbsilva/renovate-apply/actions/workflows/tests.yml)
|
|
8
|
+
[](https://github.com/jbsilva/renovate-apply/actions/workflows/codeql.yml)
|
|
9
|
+
[](https://sonarcloud.io/summary/new_code?id=jbsilva_renovate-apply)
|
|
10
|
+
[](https://codecov.io/github/jbsilva/renovate-apply)
|
|
11
|
+
[](https://github.com/astral-sh/ruff)
|
|
12
|
+
[](https://github.com/astral-sh/uv)
|
|
13
|
+
[](https://github.com/j178/prek)
|
|
14
|
+
|
|
15
|
+
Apply the dependency updates [Renovate](https://docs.renovatebot.com) finds, in your working copy,
|
|
16
|
+
without a pull request. One commit per group, the same grouping your Renovate configuration gives a
|
|
17
|
+
pull request. Nothing is pushed.
|
|
18
|
+
|
|
19
|
+
**Full documentation:** <https://jbsilva.github.io/renovate-apply/>
|
|
20
|
+
|
|
21
|
+
```console
|
|
22
|
+
$ rnv
|
|
23
|
+
renovate-apply: scanning /home/you/infra ...
|
|
24
|
+
applied deps: update the atuin images
|
|
25
|
+
ghcr.io/atuinsh/atuin v18.13.3 -> 18.22.0
|
|
26
|
+
postgres 18-alpine, new digest
|
|
27
|
+
applied deps: update the hook revs
|
|
28
|
+
astral-sh/ruff-pre-commit v0.16.5 -> v0.16.8
|
|
29
|
+
crate-ci/typos v1.50.1 -> v1.50.2
|
|
30
|
+
|
|
31
|
+
Committed. Nothing is pushed.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Why this exists
|
|
35
|
+
|
|
36
|
+
Renovate can read a local working copy. `renovate --platform=local` finds every pending update and
|
|
37
|
+
reports it. It cannot write one: the local platform turns the run into a lookup-only dry run as it
|
|
38
|
+
starts, so the run ends after the lookup, before the step that edits a file. No flag turns that off,
|
|
39
|
+
because the clamp is inside the platform, not in the configuration.
|
|
40
|
+
|
|
41
|
+
So the updates Renovate finds on your laptop can only be applied by hand, or by merging the pull
|
|
42
|
+
requests it opens on the server. This tool is the missing half: it runs that same scan, reads the
|
|
43
|
+
JSON report, and makes the replacement the report describes for each update.
|
|
44
|
+
|
|
45
|
+
The edit is not guesswork. For every dependency, the report carries `replaceString`, the exact text
|
|
46
|
+
the manager found in the file, and `autoReplaceStringTemplate`, how to write that text again with
|
|
47
|
+
the new version or digest in it. That pair is Renovate's own auto-replace, handed over as data.
|
|
48
|
+
|
|
49
|
+
## Highlights
|
|
50
|
+
|
|
51
|
+
- Works with everything Renovate's own managers report: image tags and digests in compose files and
|
|
52
|
+
Dockerfiles, GitHub Actions SHA pins with their version comment, pre-commit and prek hook revs,
|
|
53
|
+
versions matched by a custom regex manager, and the rest
|
|
54
|
+
- One commit per Renovate branch, so a grouped pull request and a local run produce the same commits
|
|
55
|
+
- Refreshes `uv.lock` when it raises a floor in `pyproject.toml`, and commits the two together, the
|
|
56
|
+
way Renovate's own pull request carries them
|
|
57
|
+
- Reads your repository's Renovate configuration by running Renovate itself, so grouping, holds,
|
|
58
|
+
package rules and versioning schemes all apply without being restated here
|
|
59
|
+
- Holds every major back until you ask for it
|
|
60
|
+
- Never skips your git hooks, and never commits anything it did not write
|
|
61
|
+
- Never leaves a `pyproject.toml` that does not lock: such a group is undone and held back
|
|
62
|
+
- `--dry-run` to read first, `--no-commit` or `--stage` to review everything at once
|
|
63
|
+
|
|
64
|
+
## Requirements
|
|
65
|
+
|
|
66
|
+
- Python 3.15+
|
|
67
|
+
- `git`
|
|
68
|
+
- Either `renovate` on `PATH` (`npm i -g renovate`) or Node.js, for `npx` to fetch it
|
|
69
|
+
- Optional: [`gh`](https://cli.github.com), logged in, or `GITHUB_TOKEN` in the environment. Without
|
|
70
|
+
a token, everything Renovate looks up on GitHub is rate limited, and updates can silently go
|
|
71
|
+
missing. Prefer either to `--token`, which puts the token in argv and in your shell history.
|
|
72
|
+
|
|
73
|
+
## Installation
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
uv tool install renovate-apply
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Or run it without installing:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
uvx renovate-apply --dry-run
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
```console
|
|
88
|
+
$ renovate-apply --help
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| Command | What it does |
|
|
92
|
+
| ----------------------------- | --------------------------------------------------------- |
|
|
93
|
+
| `rnv` | Scan, apply, one commit per group |
|
|
94
|
+
| `rnv --dry-run` | Say what would change and write nothing |
|
|
95
|
+
| `rnv --no-commit` | Write the files and stop, for one review of everything |
|
|
96
|
+
| `rnv --stage` | Write and stage the files, and commit nothing |
|
|
97
|
+
| `rnv --major` | Include the major updates, which are held back by default |
|
|
98
|
+
| `rnv --skip python` | Hold a dependency back by name, as a glob, repeatable |
|
|
99
|
+
| `rnv --prefix "build(deps):"` | Set the first word of every commit subject |
|
|
100
|
+
| `rnv --no-lock` | Do not refresh `uv.lock` after editing a `pyproject.toml` |
|
|
101
|
+
| `rnv ~/code/other-repo` | Work on another repository |
|
|
102
|
+
|
|
103
|
+
`renovate-apply` and `rnv` are the same command.
|
|
104
|
+
|
|
105
|
+
The working copy must be clean before a run that commits: a commit takes the whole index, not only
|
|
106
|
+
the files this staged, so anything else in flight would be swept into a dependency commit.
|
|
107
|
+
|
|
108
|
+
### Commit messages
|
|
109
|
+
|
|
110
|
+
The subject says what a person needs in `git log --oneline`; the body lists the group, aligned:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
deps: update the action pins
|
|
114
|
+
|
|
115
|
+
astral-sh/setup-uv v10.1.0 -> v10.2.0
|
|
116
|
+
github/codeql-action v4.38.0 -> v4.38.2
|
|
117
|
+
docker/setup-buildx-action v4.3.0 -> v4.4.1
|
|
118
|
+
actions/upload-artifact v7.0.1 -> v7.0.2
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Match `--prefix` to what your `renovate.json5` writes, so local commits and merged pull requests
|
|
122
|
+
read alike: `deps:` for `commitMessagePrefix`, `build(deps):` for semantic commits.
|
|
123
|
+
|
|
124
|
+
### Per-repository defaults
|
|
125
|
+
|
|
126
|
+
Any option can be set once, in the `pyproject.toml` of the repository being updated:
|
|
127
|
+
|
|
128
|
+
```toml
|
|
129
|
+
[tool.renovate-apply]
|
|
130
|
+
prefix = "build(deps):"
|
|
131
|
+
skip = ["python"]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The file is read from the directory being updated, so `rnv ~/code/other-repo` takes that
|
|
135
|
+
repository's settings and not those of wherever you happen to be standing. Only that directory is
|
|
136
|
+
read, never a parent of it, and a flag typed for one run wins over the file.
|
|
137
|
+
|
|
138
|
+
A repository without a `pyproject.toml` (an infrastructure repository, say) passes the flags
|
|
139
|
+
instead.
|
|
140
|
+
|
|
141
|
+
## What is held back
|
|
142
|
+
|
|
143
|
+
Nothing is applied silently past a hold. Whatever is held is listed at the end of the run, with the
|
|
144
|
+
reason:
|
|
145
|
+
|
|
146
|
+
- **Majors**, unless `--major`. Renovate can be configured to send some majors to the dependency
|
|
147
|
+
dashboard to be approved by hand. The report does not say which ones carry that flag, so holding
|
|
148
|
+
every major is the only reading that cannot contradict your configuration.
|
|
149
|
+
- **Updates Renovate would not open yet**, the ones still waiting out `minimumReleaseAge`. A
|
|
150
|
+
security fix bypasses that hold in Renovate, and bypasses it here too.
|
|
151
|
+
- **Renames**, where Renovate wants to replace a dependency with a different one. That edit is not a
|
|
152
|
+
version bump, and this tool does not write it.
|
|
153
|
+
- **Lock file updates**, where the range in the manifest already allows the new version, so only the
|
|
154
|
+
lock file beside it moves. There is no edit to make in the package file, and moving the lock file
|
|
155
|
+
takes that package manager. Only `uv.lock` is refreshed here, after an edit to its manifest.
|
|
156
|
+
- **Updates that do not lock**, where `uv lock` finds no solution for the new floor in
|
|
157
|
+
`pyproject.toml`. The group is undone, `uv.lock` included, and uv's explanation is printed.
|
|
158
|
+
- **A line two managers found**, when a regex manager in your Renovate configuration matches a line
|
|
159
|
+
a built-in manager also reads. Renovate reports the line twice. The first update makes the edit,
|
|
160
|
+
and the second is listed with it.
|
|
161
|
+
- **Anything named in `--skip`**, as a glob. That is the answer to the blind spot above on an update
|
|
162
|
+
that is not a major: a repository that keeps `requires-python` on the dependency dashboard writes
|
|
163
|
+
`skip = ["python"]` once and stops being asked.
|
|
164
|
+
|
|
165
|
+
## Limits
|
|
166
|
+
|
|
167
|
+
- **`dependencyDashboardApproval` is invisible.** Renovate resolves it per package and the report
|
|
168
|
+
does not carry it, so this tool cannot see which updates your configuration wants approved by
|
|
169
|
+
hand. Holding every major covers the usual case; `--skip` covers the rest.
|
|
170
|
+
- **Lock files: uv only.** A `pyproject.toml` with a `uv.lock` beside it is relocked and committed
|
|
171
|
+
together. Any other package manager's lock file is left untouched, so run its update yourself.
|
|
172
|
+
- **Renovate's lock file maintenance is not applied.** Those branches carry no dependency updates,
|
|
173
|
+
so they are not in the report at all.
|
|
174
|
+
- **A group that cannot be applied is reported and passed over.** The groups after it are still
|
|
175
|
+
applied and committed, and the run ends non-zero. Only a failure before the first group, such as a
|
|
176
|
+
scan that did not run or a working copy that is not clean, ends the run with nothing done.
|
|
177
|
+
- The scan is a full Renovate run, so it takes as long as Renovate takes, usually under a minute.
|
|
178
|
+
- A dependency Renovate cannot look up is one this cannot apply. Everything upstream of the report
|
|
179
|
+
is Renovate's own behavior, configuration included.
|
|
180
|
+
- This writes files and commits. It never pushes, never merges and never deploys.
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
uv sync --group dev --group test
|
|
186
|
+
uv run pytest
|
|
187
|
+
uv run poe check
|
|
188
|
+
uv run poe docs # preview the documentation site
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "renovate-apply"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Apply the dependency updates Renovate finds, locally, without a pull request"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.15,<4"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
keywords = [
|
|
10
|
+
"renovate",
|
|
11
|
+
"dependencies",
|
|
12
|
+
"updates",
|
|
13
|
+
"cli",
|
|
14
|
+
"docker",
|
|
15
|
+
"pre-commit",
|
|
16
|
+
"github-actions",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Environment :: Console",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Natural Language :: English",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.15",
|
|
25
|
+
"Topic :: Software Development :: Build Tools",
|
|
26
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
27
|
+
"Topic :: System :: Software Distribution",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"cyclopts>=5.2.0",
|
|
31
|
+
"rich>=15.0.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[[project.authors]]
|
|
35
|
+
name = "Julio Batista Silva"
|
|
36
|
+
email = "python@juliobs.com"
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Documentation = "https://jbsilva.github.io/renovate-apply/"
|
|
40
|
+
Repository = "https://github.com/jbsilva/renovate-apply"
|
|
41
|
+
Issues = "https://github.com/jbsilva/renovate-apply/issues"
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
renovate-apply = "renovate_apply.main:main"
|
|
45
|
+
rnv = "renovate_apply.main:main"
|
|
46
|
+
|
|
47
|
+
[dependency-groups]
|
|
48
|
+
dev = [
|
|
49
|
+
"bandit>=1.9.4",
|
|
50
|
+
"poethepoet>=0.48.0",
|
|
51
|
+
"pycroscope>=0.5.0",
|
|
52
|
+
"ruff>=0.17.0",
|
|
53
|
+
"ty>=0.0.85",
|
|
54
|
+
"zizmor>=1.30.1",
|
|
55
|
+
"zuban>=0.10.0",
|
|
56
|
+
]
|
|
57
|
+
docs = ["zensical>=0.0.69"]
|
|
58
|
+
test = [
|
|
59
|
+
"pytest-cov>=7.1.0",
|
|
60
|
+
"pytest-random-order>=1.2.0",
|
|
61
|
+
"pytest-xdist[psutil]>=3.8.0",
|
|
62
|
+
"pytest>=9.1.1",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[tool.uv]
|
|
66
|
+
default-groups = [
|
|
67
|
+
"dev",
|
|
68
|
+
"test",
|
|
69
|
+
]
|
|
70
|
+
exclude-newer = "1 day"
|
|
71
|
+
|
|
72
|
+
[tool.uv.exclude-newer-package]
|
|
73
|
+
cyclopts = "6 hours"
|
|
74
|
+
ruff = "6 hours"
|
|
75
|
+
zensical = "6 hours"
|
|
76
|
+
zuban = "6 hours"
|
|
77
|
+
|
|
78
|
+
[tool.poe.executor]
|
|
79
|
+
type = "uv"
|
|
80
|
+
|
|
81
|
+
[tool.poe.tasks.lint]
|
|
82
|
+
help = "Lint with ruff and apply its fixes"
|
|
83
|
+
cmd = "ruff check --fix ."
|
|
84
|
+
|
|
85
|
+
[tool.poe.tasks.format]
|
|
86
|
+
help = "Format the code with ruff"
|
|
87
|
+
cmd = "ruff format ."
|
|
88
|
+
|
|
89
|
+
[tool.poe.tasks.typecheck]
|
|
90
|
+
help = "Type check with ty"
|
|
91
|
+
cmd = "ty check"
|
|
92
|
+
|
|
93
|
+
[tool.poe.tasks.zuban]
|
|
94
|
+
help = "Strict type check with zuban, which no hook or CI job runs"
|
|
95
|
+
cmd = "zuban check"
|
|
96
|
+
|
|
97
|
+
[tool.poe.tasks.pycroscope]
|
|
98
|
+
help = "Semi-static analysis with pycroscope, which no hook or CI job runs"
|
|
99
|
+
cmd = "pycroscope --config-file pyproject.toml"
|
|
100
|
+
|
|
101
|
+
[tool.poe.tasks.hooks]
|
|
102
|
+
help = "Run every prek hook on all files"
|
|
103
|
+
cmd = "prek run -a"
|
|
104
|
+
|
|
105
|
+
[tool.poe.tasks.hooks.executor]
|
|
106
|
+
with = ["prek"]
|
|
107
|
+
|
|
108
|
+
[tool.poe.tasks.check]
|
|
109
|
+
help = "Run all the checks to do before a commit"
|
|
110
|
+
sequence = [
|
|
111
|
+
"lint",
|
|
112
|
+
"format",
|
|
113
|
+
"typecheck",
|
|
114
|
+
"hooks",
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
[tool.poe.tasks.test]
|
|
118
|
+
help = "Run the test suite (extra arguments go to pytest)"
|
|
119
|
+
cmd = "pytest"
|
|
120
|
+
|
|
121
|
+
[tool.poe.tasks.docs]
|
|
122
|
+
help = "Serve the docs site locally with live reload"
|
|
123
|
+
cmd = "zensical serve"
|
|
124
|
+
|
|
125
|
+
[tool.poe.tasks.docs.executor]
|
|
126
|
+
group = "docs"
|
|
127
|
+
|
|
128
|
+
[tool.poe.tasks.docs-build]
|
|
129
|
+
help = "Build the static docs site into site/"
|
|
130
|
+
cmd = "zensical build --clean"
|
|
131
|
+
|
|
132
|
+
[tool.poe.tasks.docs-build.executor]
|
|
133
|
+
group = "docs"
|
|
134
|
+
|
|
135
|
+
[tool.ruff]
|
|
136
|
+
line-length = 100
|
|
137
|
+
indent-width = 4
|
|
138
|
+
target-version = "py315"
|
|
139
|
+
fix = true
|
|
140
|
+
unsafe-fixes = false
|
|
141
|
+
|
|
142
|
+
[tool.ruff.lint]
|
|
143
|
+
select = ["ALL"]
|
|
144
|
+
ignore = [
|
|
145
|
+
"COM812",
|
|
146
|
+
"CPY001",
|
|
147
|
+
"D203",
|
|
148
|
+
"D212",
|
|
149
|
+
"D413",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
[tool.ruff.lint.per-file-ignores]
|
|
153
|
+
"**/tests/{test_*.py,conftest.py,helpers.py}" = [
|
|
154
|
+
"S101",
|
|
155
|
+
"S603",
|
|
156
|
+
"S607",
|
|
157
|
+
"ARG001",
|
|
158
|
+
"INP001",
|
|
159
|
+
"TC001",
|
|
160
|
+
"TC002",
|
|
161
|
+
"TC003",
|
|
162
|
+
"PLR2004",
|
|
163
|
+
]
|
|
164
|
+
"**/__init__.py" = ["D104"]
|
|
165
|
+
|
|
166
|
+
[tool.ruff.lint.isort]
|
|
167
|
+
combine-as-imports = true
|
|
168
|
+
force-wrap-aliases = true
|
|
169
|
+
lines-after-imports = 2
|
|
170
|
+
|
|
171
|
+
[tool.ruff.format]
|
|
172
|
+
quote-style = "double"
|
|
173
|
+
indent-style = "space"
|
|
174
|
+
line-ending = "lf"
|
|
175
|
+
|
|
176
|
+
[tool.docformatter]
|
|
177
|
+
recursive = true
|
|
178
|
+
wrap-summaries = 100
|
|
179
|
+
wrap-descriptions = 100
|
|
180
|
+
pre-summary-newline = true
|
|
181
|
+
|
|
182
|
+
[tool.ty.terminal]
|
|
183
|
+
error-on-warning = true
|
|
184
|
+
|
|
185
|
+
[tool.zuban]
|
|
186
|
+
strict = true
|
|
187
|
+
disallow_untyped_defs = true
|
|
188
|
+
warn_unreachable = true
|
|
189
|
+
python_executable = ".venv/bin/python"
|
|
190
|
+
|
|
191
|
+
[tool.pycroscope]
|
|
192
|
+
paths = ["src/renovate_apply"]
|
|
193
|
+
import_paths = ["src"]
|
|
194
|
+
output_format = "concise"
|
|
195
|
+
possibly_undefined_name = true
|
|
196
|
+
unused_variable = true
|
|
197
|
+
value_always_true = true
|
|
198
|
+
use_fstrings = true
|
|
199
|
+
missing_return_annotation = true
|
|
200
|
+
missing_parameter_annotation = true
|
|
201
|
+
suggested_parameter_type = true
|
|
202
|
+
suggested_return_type = true
|
|
203
|
+
incompatible_override = true
|
|
204
|
+
missing_generic_parameters = true
|
|
205
|
+
|
|
206
|
+
[[tool.pycroscope.overrides]]
|
|
207
|
+
module = "tests"
|
|
208
|
+
missing_return_annotation = false
|
|
209
|
+
missing_parameter_annotation = false
|
|
210
|
+
suggested_parameter_type = false
|
|
211
|
+
suggested_return_type = false
|
|
212
|
+
|
|
213
|
+
[tool.bandit]
|
|
214
|
+
exclude_dirs = [
|
|
215
|
+
".git",
|
|
216
|
+
".venv",
|
|
217
|
+
"__pycache__",
|
|
218
|
+
"tests",
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
[tool.bandit.assert_used]
|
|
222
|
+
skips = ["tests/test_*.py"]
|
|
223
|
+
|
|
224
|
+
[tool.pytest.ini_options]
|
|
225
|
+
pythonpath = ["tests"]
|
|
226
|
+
addopts = [
|
|
227
|
+
"-ra",
|
|
228
|
+
"--durations=10",
|
|
229
|
+
"--import-mode=importlib",
|
|
230
|
+
"--random-order",
|
|
231
|
+
"--numprocesses=logical",
|
|
232
|
+
"--dist=load",
|
|
233
|
+
"--tb=short",
|
|
234
|
+
"--verbose",
|
|
235
|
+
"--cov=src/renovate_apply",
|
|
236
|
+
"--cov-branch",
|
|
237
|
+
"--cov-report=term-missing",
|
|
238
|
+
"--cov-report=xml:reports/coverage.xml",
|
|
239
|
+
]
|
|
240
|
+
filterwarnings = ["error"]
|
|
241
|
+
|
|
242
|
+
[build-system]
|
|
243
|
+
requires = ["uv_build>=0.12.23,<1.0.0"]
|
|
244
|
+
build-backend = "uv_build"
|