snap-maintainer 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.
- snap_maintainer-0.1.0/LICENSE +21 -0
- snap_maintainer-0.1.0/PKG-INFO +270 -0
- snap_maintainer-0.1.0/README.md +255 -0
- snap_maintainer-0.1.0/pyproject.toml +28 -0
- snap_maintainer-0.1.0/setup.cfg +4 -0
- snap_maintainer-0.1.0/src/snap_maintainer/__init__.py +1 -0
- snap_maintainer-0.1.0/src/snap_maintainer/__main__.py +3 -0
- snap_maintainer-0.1.0/src/snap_maintainer/archive.py +143 -0
- snap_maintainer-0.1.0/src/snap_maintainer/builders.py +205 -0
- snap_maintainer-0.1.0/src/snap_maintainer/cache.py +38 -0
- snap_maintainer-0.1.0/src/snap_maintainer/checks.py +340 -0
- snap_maintainer-0.1.0/src/snap_maintainer/cli.py +197 -0
- snap_maintainer-0.1.0/src/snap_maintainer/debver.py +52 -0
- snap_maintainer-0.1.0/src/snap_maintainer/discovery.py +216 -0
- snap_maintainer-0.1.0/src/snap_maintainer/hints.py +211 -0
- snap_maintainer-0.1.0/src/snap_maintainer/manifests.py +81 -0
- snap_maintainer-0.1.0/src/snap_maintainer/mcp_server.py +275 -0
- snap_maintainer-0.1.0/src/snap_maintainer/preflight.py +102 -0
- snap_maintainer-0.1.0/src/snap_maintainer/repo.py +114 -0
- snap_maintainer-0.1.0/src/snap_maintainer/snapcraft_yaml.py +115 -0
- snap_maintainer-0.1.0/src/snap_maintainer/store.py +181 -0
- snap_maintainer-0.1.0/src/snap_maintainer/ui.py +440 -0
- snap_maintainer-0.1.0/src/snap_maintainer/usn.py +54 -0
- snap_maintainer-0.1.0/src/snap_maintainer.egg-info/PKG-INFO +270 -0
- snap_maintainer-0.1.0/src/snap_maintainer.egg-info/SOURCES.txt +33 -0
- snap_maintainer-0.1.0/src/snap_maintainer.egg-info/dependency_links.txt +1 -0
- snap_maintainer-0.1.0/src/snap_maintainer.egg-info/entry_points.txt +2 -0
- snap_maintainer-0.1.0/src/snap_maintainer.egg-info/requires.txt +5 -0
- snap_maintainer-0.1.0/src/snap_maintainer.egg-info/top_level.txt +1 -0
- snap_maintainer-0.1.0/tests/test_builders.py +112 -0
- snap_maintainer-0.1.0/tests/test_checks.py +166 -0
- snap_maintainer-0.1.0/tests/test_cli.py +67 -0
- snap_maintainer-0.1.0/tests/test_hints.py +88 -0
- snap_maintainer-0.1.0/tests/test_mcp_server.py +74 -0
- snap_maintainer-0.1.0/tests/test_parsing.py +47 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Carlos Bravo
|
|
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,270 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: snap-maintainer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Find out which of your published snaps need a rebuild or a new release, and how to do it
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/carlosbravoa/snap-maintainer
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: PyYAML>=6
|
|
11
|
+
Requires-Dist: rich>=13
|
|
12
|
+
Provides-Extra: mcp
|
|
13
|
+
Requires-Dist: mcp>=2.3; extra == "mcp"
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# snap-maintainer
|
|
17
|
+
|
|
18
|
+
Find out which of your published snaps need a rebuild or a new release, and why.
|
|
19
|
+
|
|
20
|
+
`snap-maintainer` reads your Snap Store account, finds the source repository behind
|
|
21
|
+
each snap, and checks it against three things:
|
|
22
|
+
|
|
23
|
+
- **Security fixes.** Ubuntu has fixed a package your snap stages, and a rebuild
|
|
24
|
+
would pick up the fix.
|
|
25
|
+
- **Unreleased changes.** Your repo has commits, or a version bump, that aren't in
|
|
26
|
+
the published revision.
|
|
27
|
+
- **Upstream releases.** A part pulls from a git repo that has moved on.
|
|
28
|
+
|
|
29
|
+
For every snap that needs work, it writes out the exact steps to rebuild and release
|
|
30
|
+
it, matched to how that snap is built: a GitHub Actions workflow, a Launchpad recipe,
|
|
31
|
+
or a local `snapcraft pack`.
|
|
32
|
+
|
|
33
|
+
It never builds, uploads or releases anything itself. Those steps need someone
|
|
34
|
+
watching, so the tool stops at a plan that a person, or an agent, can follow. Agents
|
|
35
|
+
can also use it directly through its built-in [MCP server](#use-it-from-an-agent-mcp).
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- Python 3.10 or later
|
|
40
|
+
- [`snapcraft`](https://snapcraft.io/snapcraft), logged in to the Snap Store as the
|
|
41
|
+
snaps' publisher: `snapcraft login`
|
|
42
|
+
- The [GitHub CLI](https://cli.github.com/) `gh`, logged in: `gh auth login`
|
|
43
|
+
- Optional:
|
|
44
|
+
- `unsquashfs` (package `squashfs-tools`): reads build manifests from published
|
|
45
|
+
snaps, for exact security checks.
|
|
46
|
+
- `git`: compares pinned `source-tag`s with upstream tags.
|
|
47
|
+
- The `mcp` Python package: only for the MCP server (`snap-maintainer mcp`).
|
|
48
|
+
|
|
49
|
+
The tool checks all of these when it starts, and tells you the command to fix anything
|
|
50
|
+
that's missing.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
With [pipx](https://pipx.pypa.io/):
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
pipx install git+https://github.com/carlosbravoa/snap-maintainer.git
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
To use it from an agent, add the MCP support too:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
pipx inject snap-maintainer mcp
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
From a checkout, for development:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
git clone https://github.com/carlosbravoa/snap-maintainer.git
|
|
70
|
+
cd snap-maintainer
|
|
71
|
+
pipx install --editable .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
You can also run it without installing, from the checkout:
|
|
75
|
+
`PYTHONPATH=src python3 -m snap_maintainer <command>`.
|
|
76
|
+
|
|
77
|
+
## Quick start
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
mkdir ~/snaps && cd ~/snaps # the inventory is saved in the current directory
|
|
81
|
+
snap-maintainer discover # find your snaps, their repos and how they're built
|
|
82
|
+
snap-maintainer check # decide which need a rebuild or release
|
|
83
|
+
snap-maintainer plan # the steps to rebuild and release each of them
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The first `check` downloads Ubuntu archive and security data and the published snaps'
|
|
87
|
+
manifests, so it takes a minute or two. Later runs use a cache.
|
|
88
|
+
|
|
89
|
+
## Commands
|
|
90
|
+
|
|
91
|
+
| Command | What it does |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `discover [snaps...]` | Lists the snaps registered to your account, their channels, source repos and builders, and saves them to `inventory.json`. Prompts for a repo it can't find (`--no-input` to skip). |
|
|
94
|
+
| `check [snaps...]` | Gives each snap a verdict, `rebuild`, `update` or `up to date`, with the reasons. `-v` shows every notice, commit and note; `--refresh` re-downloads cached Ubuntu data. |
|
|
95
|
+
| `plan [snaps...]` | Shows the steps to rebuild and release every snap that needs it, or just the named ones. `--all` includes up-to-date snaps; `--markdown` and `--json` print it for an issue, an agent or a script. Runs nothing. |
|
|
96
|
+
| `show [--json]` | Prints the inventory without contacting anything. |
|
|
97
|
+
| `set-repo <snap> <url>` | Sets a snap's source repo by hand. |
|
|
98
|
+
| `set-build <snap> <method> [--workflow FILE]` | Sets how a snap is built: `github-workflow`, `launchpad`, `local` or `manual`. |
|
|
99
|
+
| `mcp` | Runs an MCP server so an agent can use the tool directly. See [Use it from an agent (MCP)](#use-it-from-an-agent-mcp). |
|
|
100
|
+
|
|
101
|
+
Repos and builders you set by hand are never overwritten by `discover`.
|
|
102
|
+
|
|
103
|
+
Global options go before the command: `-i FILE` uses another inventory file, and
|
|
104
|
+
`--debug` shows a traceback for unexpected errors. The `SNAP_MAINTAINER_INVENTORY`
|
|
105
|
+
environment variable sets the inventory file too, which is handy for always using the
|
|
106
|
+
same one.
|
|
107
|
+
|
|
108
|
+
Exit codes are `0` for success, `1` for an error, `2` when a required tool or login is
|
|
109
|
+
missing, and `130` when interrupted.
|
|
110
|
+
|
|
111
|
+
Output uses colours, a progress bar and clickable links in a terminal, and plain text
|
|
112
|
+
when piped or when `NO_COLOR` is set.
|
|
113
|
+
|
|
114
|
+
### Handing a plan to an agent
|
|
115
|
+
|
|
116
|
+
`plan --markdown` prints a self-contained checklist with the reasons, the target
|
|
117
|
+
channel and architectures, and copy-paste commands:
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
snap-maintainer plan --markdown > rebuild-plan.md
|
|
121
|
+
snap-maintainer plan --markdown hello-nginx | gh issue create -R you/hello-nginx \
|
|
122
|
+
--title "Rebuild for security fixes" --body-file -
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Use it from an agent (MCP)
|
|
126
|
+
|
|
127
|
+
`snap-maintainer mcp` runs a [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
128
|
+
server over stdio, so an agent such as Claude Code can discover, check and plan rebuilds
|
|
129
|
+
directly. It needs the optional `mcp` package:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
pipx inject snap-maintainer mcp
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Add it to Claude Code (`--scope user` makes it available in every project):
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
claude mcp add --scope user snap-maintainer -- snap-maintainer mcp
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For other MCP clients, add this to their server configuration:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"mcpServers": {
|
|
146
|
+
"snap-maintainer": { "command": "snap-maintainer", "args": ["mcp"] }
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The server provides these tools:
|
|
152
|
+
|
|
153
|
+
| Tool | What it does |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `get_inventory` | Known snaps, their repos, builders and last verdicts. Read-only and fast. |
|
|
156
|
+
| `discover_snaps` | Same as `discover`. |
|
|
157
|
+
| `check_snaps` | Same as `check`, with progress updates. |
|
|
158
|
+
| `rebuild_plan` | Same as `plan`, as Markdown or JSON. Read-only. |
|
|
159
|
+
| `set_repo`, `set_build` | Same as the commands. |
|
|
160
|
+
|
|
161
|
+
It also provides a `maintain_snaps` prompt that walks through the whole flow.
|
|
162
|
+
|
|
163
|
+
Like the command line, the server never builds, uploads or releases anything. Its
|
|
164
|
+
instructions tell the agent to show you the plan, and to ask before running any upload,
|
|
165
|
+
release, tag push or workflow, one snap at a time.
|
|
166
|
+
|
|
167
|
+
The server keeps its inventory in `~/.local/share/snap-maintainer/inventory.json`. To
|
|
168
|
+
share one inventory with the command line, set `SNAP_MAINTAINER_INVENTORY` for both, or
|
|
169
|
+
pass `-i FILE` before `mcp`.
|
|
170
|
+
|
|
171
|
+
If `check_snaps` or `discover_snaps` says it can't reach the keyring, the MCP client
|
|
172
|
+
started the server without your desktop session's `DBUS_SESSION_BUS_ADDRESS`, so
|
|
173
|
+
snapcraft can't read your login. Either pass that variable through in the client's
|
|
174
|
+
server configuration, or give the server an exported login instead:
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
(umask 077; snapcraft export-login - > ~/.config/snap-maintainer-login) # readable only by you
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Then set `SNAPCRAFT_STORE_CREDENTIALS` to that file's contents in the server's
|
|
181
|
+
environment.
|
|
182
|
+
|
|
183
|
+
## How it works
|
|
184
|
+
|
|
185
|
+
### Finding the repo
|
|
186
|
+
|
|
187
|
+
For each snap, the first of these that points to a repo that exists is used:
|
|
188
|
+
|
|
189
|
+
1. A repo you set with `set-repo`, or typed at the prompt.
|
|
190
|
+
2. The store listing's source code link.
|
|
191
|
+
3. A GitHub URL among the listing's website, issues or contact links.
|
|
192
|
+
4. `github.com/<your GitHub login>/<snap name>`. This catches private snaps, which the
|
|
193
|
+
public store API can't see.
|
|
194
|
+
5. A prompt, when running in a terminal.
|
|
195
|
+
|
|
196
|
+
On GitHub, renamed repos are followed (and noted), and the tool records the default
|
|
197
|
+
branch, the location of `snapcraft.yaml` and the workflow files. Repos on other hosts
|
|
198
|
+
are recorded but not inspected.
|
|
199
|
+
|
|
200
|
+
### Detecting the builder
|
|
201
|
+
|
|
202
|
+
| Builder | When |
|
|
203
|
+
|---|---|
|
|
204
|
+
| `github-workflow` | A workflow builds the snap (`snapcore/action-build`, or `snapcraft` / `snapcraft pack` in a step). The tool records whether it also uploads (`snapcraft upload`, `snapcore/action-publish`), what triggers it (tags, manual dispatch, releases, pushes), and any hard-coded channel. |
|
|
205
|
+
| `launchpad` | A Launchpad snap recipe builds this snap from the repo. |
|
|
206
|
+
| `local` | Neither of the above: build with `snapcraft pack` on your machine. |
|
|
207
|
+
| `manual` | Only when you set it: you rebuild the snap some other way. |
|
|
208
|
+
|
|
209
|
+
A workflow or recipe that uploads to the store is preferred over one that only
|
|
210
|
+
builds. The candidates that weren't chosen are kept in the inventory.
|
|
211
|
+
|
|
212
|
+
### Deciding what needs work
|
|
213
|
+
|
|
214
|
+
`check` looks at the most stable published revision of each architecture on the
|
|
215
|
+
`latest` track.
|
|
216
|
+
|
|
217
|
+
| Signal | Verdict | When |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| Security | `rebuild` | A [Ubuntu Security Notice](https://ubuntu.com/security/notices) fixes a package the snap stages, and the fix is in the public Ubuntu archive. |
|
|
220
|
+
| Source | `update` | The repo has commits since the revision was uploaded, or `snapcraft.yaml` declares a different version. |
|
|
221
|
+
| Upstream | `update` | A part's git source has a newer tag than its `source-tag`, or new commits on the branch it tracks (GitHub only). |
|
|
222
|
+
|
|
223
|
+
For security, the check is **exact** when the published snap contains a build
|
|
224
|
+
manifest (`snap/manifest.yaml`). That manifest records the version of every staged
|
|
225
|
+
package, so a notice counts only if the snap has an older version than the fix.
|
|
226
|
+
|
|
227
|
+
Without a manifest, the check is **inferred**: it works out which packages the snap
|
|
228
|
+
stages (its `stage-packages` plus their dependencies, minus what the base snap
|
|
229
|
+
provides), and counts any notice issued after the revision was uploaded.
|
|
230
|
+
|
|
231
|
+
snapcraft writes the manifest only when building with `SNAPCRAFT_BUILD_INFO=1`.
|
|
232
|
+
Launchpad and `snapcore/action-build` set it automatically; for local builds, the
|
|
233
|
+
plans include it in the build command.
|
|
234
|
+
|
|
235
|
+
## Limitations
|
|
236
|
+
|
|
237
|
+
- Private snaps can't be downloaded, so their security checks are always inferred.
|
|
238
|
+
- Fixes that exist only in Ubuntu Pro (ESM) are shown as notes, because a rebuild
|
|
239
|
+
can't pick them up.
|
|
240
|
+
- Non-security Ubuntu updates, and dependencies bundled by language plugins (pip,
|
|
241
|
+
npm, cargo, go), aren't checked.
|
|
242
|
+
- Repo inspection, workflow detection and branch checks only support GitHub.
|
|
243
|
+
- The inferred check reads the base snap's package list from `/snap/<base>/current`.
|
|
244
|
+
If that base isn't installed locally, it may include packages the base already
|
|
245
|
+
ships.
|
|
246
|
+
|
|
247
|
+
## Data and privacy
|
|
248
|
+
|
|
249
|
+
- `inventory.json` is written to the current directory, the file given with `-i`, or
|
|
250
|
+
`SNAP_MAINTAINER_INVENTORY`. The MCP server uses
|
|
251
|
+
`~/.local/share/snap-maintainer/inventory.json` unless one of those is set.
|
|
252
|
+
- Ubuntu archive and security data is cached for 24 hours in
|
|
253
|
+
`~/.cache/snap-maintainer` (about 20 MB per Ubuntu release and architecture).
|
|
254
|
+
- Manifests of published revisions are cached there permanently. Each public
|
|
255
|
+
revision is downloaded once.
|
|
256
|
+
- Your Snap Store and GitHub logins stay with `snapcraft` and `gh`; this tool never
|
|
257
|
+
reads or stores them.
|
|
258
|
+
|
|
259
|
+
## Development
|
|
260
|
+
|
|
261
|
+
```sh
|
|
262
|
+
pip install -e '.[mcp]' pytest
|
|
263
|
+
python3 -m pytest
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Without the `mcp` package, the MCP server tests are skipped.
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
[MIT](https://github.com/carlosbravoa/snap-maintainer/blob/master/LICENSE)
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# snap-maintainer
|
|
2
|
+
|
|
3
|
+
Find out which of your published snaps need a rebuild or a new release, and why.
|
|
4
|
+
|
|
5
|
+
`snap-maintainer` reads your Snap Store account, finds the source repository behind
|
|
6
|
+
each snap, and checks it against three things:
|
|
7
|
+
|
|
8
|
+
- **Security fixes.** Ubuntu has fixed a package your snap stages, and a rebuild
|
|
9
|
+
would pick up the fix.
|
|
10
|
+
- **Unreleased changes.** Your repo has commits, or a version bump, that aren't in
|
|
11
|
+
the published revision.
|
|
12
|
+
- **Upstream releases.** A part pulls from a git repo that has moved on.
|
|
13
|
+
|
|
14
|
+
For every snap that needs work, it writes out the exact steps to rebuild and release
|
|
15
|
+
it, matched to how that snap is built: a GitHub Actions workflow, a Launchpad recipe,
|
|
16
|
+
or a local `snapcraft pack`.
|
|
17
|
+
|
|
18
|
+
It never builds, uploads or releases anything itself. Those steps need someone
|
|
19
|
+
watching, so the tool stops at a plan that a person, or an agent, can follow. Agents
|
|
20
|
+
can also use it directly through its built-in [MCP server](#use-it-from-an-agent-mcp).
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Python 3.10 or later
|
|
25
|
+
- [`snapcraft`](https://snapcraft.io/snapcraft), logged in to the Snap Store as the
|
|
26
|
+
snaps' publisher: `snapcraft login`
|
|
27
|
+
- The [GitHub CLI](https://cli.github.com/) `gh`, logged in: `gh auth login`
|
|
28
|
+
- Optional:
|
|
29
|
+
- `unsquashfs` (package `squashfs-tools`): reads build manifests from published
|
|
30
|
+
snaps, for exact security checks.
|
|
31
|
+
- `git`: compares pinned `source-tag`s with upstream tags.
|
|
32
|
+
- The `mcp` Python package: only for the MCP server (`snap-maintainer mcp`).
|
|
33
|
+
|
|
34
|
+
The tool checks all of these when it starts, and tells you the command to fix anything
|
|
35
|
+
that's missing.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
With [pipx](https://pipx.pypa.io/):
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
pipx install git+https://github.com/carlosbravoa/snap-maintainer.git
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
To use it from an agent, add the MCP support too:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
pipx inject snap-maintainer mcp
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
From a checkout, for development:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
git clone https://github.com/carlosbravoa/snap-maintainer.git
|
|
55
|
+
cd snap-maintainer
|
|
56
|
+
pipx install --editable .
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
You can also run it without installing, from the checkout:
|
|
60
|
+
`PYTHONPATH=src python3 -m snap_maintainer <command>`.
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
mkdir ~/snaps && cd ~/snaps # the inventory is saved in the current directory
|
|
66
|
+
snap-maintainer discover # find your snaps, their repos and how they're built
|
|
67
|
+
snap-maintainer check # decide which need a rebuild or release
|
|
68
|
+
snap-maintainer plan # the steps to rebuild and release each of them
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The first `check` downloads Ubuntu archive and security data and the published snaps'
|
|
72
|
+
manifests, so it takes a minute or two. Later runs use a cache.
|
|
73
|
+
|
|
74
|
+
## Commands
|
|
75
|
+
|
|
76
|
+
| Command | What it does |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `discover [snaps...]` | Lists the snaps registered to your account, their channels, source repos and builders, and saves them to `inventory.json`. Prompts for a repo it can't find (`--no-input` to skip). |
|
|
79
|
+
| `check [snaps...]` | Gives each snap a verdict, `rebuild`, `update` or `up to date`, with the reasons. `-v` shows every notice, commit and note; `--refresh` re-downloads cached Ubuntu data. |
|
|
80
|
+
| `plan [snaps...]` | Shows the steps to rebuild and release every snap that needs it, or just the named ones. `--all` includes up-to-date snaps; `--markdown` and `--json` print it for an issue, an agent or a script. Runs nothing. |
|
|
81
|
+
| `show [--json]` | Prints the inventory without contacting anything. |
|
|
82
|
+
| `set-repo <snap> <url>` | Sets a snap's source repo by hand. |
|
|
83
|
+
| `set-build <snap> <method> [--workflow FILE]` | Sets how a snap is built: `github-workflow`, `launchpad`, `local` or `manual`. |
|
|
84
|
+
| `mcp` | Runs an MCP server so an agent can use the tool directly. See [Use it from an agent (MCP)](#use-it-from-an-agent-mcp). |
|
|
85
|
+
|
|
86
|
+
Repos and builders you set by hand are never overwritten by `discover`.
|
|
87
|
+
|
|
88
|
+
Global options go before the command: `-i FILE` uses another inventory file, and
|
|
89
|
+
`--debug` shows a traceback for unexpected errors. The `SNAP_MAINTAINER_INVENTORY`
|
|
90
|
+
environment variable sets the inventory file too, which is handy for always using the
|
|
91
|
+
same one.
|
|
92
|
+
|
|
93
|
+
Exit codes are `0` for success, `1` for an error, `2` when a required tool or login is
|
|
94
|
+
missing, and `130` when interrupted.
|
|
95
|
+
|
|
96
|
+
Output uses colours, a progress bar and clickable links in a terminal, and plain text
|
|
97
|
+
when piped or when `NO_COLOR` is set.
|
|
98
|
+
|
|
99
|
+
### Handing a plan to an agent
|
|
100
|
+
|
|
101
|
+
`plan --markdown` prints a self-contained checklist with the reasons, the target
|
|
102
|
+
channel and architectures, and copy-paste commands:
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
snap-maintainer plan --markdown > rebuild-plan.md
|
|
106
|
+
snap-maintainer plan --markdown hello-nginx | gh issue create -R you/hello-nginx \
|
|
107
|
+
--title "Rebuild for security fixes" --body-file -
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Use it from an agent (MCP)
|
|
111
|
+
|
|
112
|
+
`snap-maintainer mcp` runs a [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
113
|
+
server over stdio, so an agent such as Claude Code can discover, check and plan rebuilds
|
|
114
|
+
directly. It needs the optional `mcp` package:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
pipx inject snap-maintainer mcp
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Add it to Claude Code (`--scope user` makes it available in every project):
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
claude mcp add --scope user snap-maintainer -- snap-maintainer mcp
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
For other MCP clients, add this to their server configuration:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"mcpServers": {
|
|
131
|
+
"snap-maintainer": { "command": "snap-maintainer", "args": ["mcp"] }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The server provides these tools:
|
|
137
|
+
|
|
138
|
+
| Tool | What it does |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `get_inventory` | Known snaps, their repos, builders and last verdicts. Read-only and fast. |
|
|
141
|
+
| `discover_snaps` | Same as `discover`. |
|
|
142
|
+
| `check_snaps` | Same as `check`, with progress updates. |
|
|
143
|
+
| `rebuild_plan` | Same as `plan`, as Markdown or JSON. Read-only. |
|
|
144
|
+
| `set_repo`, `set_build` | Same as the commands. |
|
|
145
|
+
|
|
146
|
+
It also provides a `maintain_snaps` prompt that walks through the whole flow.
|
|
147
|
+
|
|
148
|
+
Like the command line, the server never builds, uploads or releases anything. Its
|
|
149
|
+
instructions tell the agent to show you the plan, and to ask before running any upload,
|
|
150
|
+
release, tag push or workflow, one snap at a time.
|
|
151
|
+
|
|
152
|
+
The server keeps its inventory in `~/.local/share/snap-maintainer/inventory.json`. To
|
|
153
|
+
share one inventory with the command line, set `SNAP_MAINTAINER_INVENTORY` for both, or
|
|
154
|
+
pass `-i FILE` before `mcp`.
|
|
155
|
+
|
|
156
|
+
If `check_snaps` or `discover_snaps` says it can't reach the keyring, the MCP client
|
|
157
|
+
started the server without your desktop session's `DBUS_SESSION_BUS_ADDRESS`, so
|
|
158
|
+
snapcraft can't read your login. Either pass that variable through in the client's
|
|
159
|
+
server configuration, or give the server an exported login instead:
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
(umask 077; snapcraft export-login - > ~/.config/snap-maintainer-login) # readable only by you
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Then set `SNAPCRAFT_STORE_CREDENTIALS` to that file's contents in the server's
|
|
166
|
+
environment.
|
|
167
|
+
|
|
168
|
+
## How it works
|
|
169
|
+
|
|
170
|
+
### Finding the repo
|
|
171
|
+
|
|
172
|
+
For each snap, the first of these that points to a repo that exists is used:
|
|
173
|
+
|
|
174
|
+
1. A repo you set with `set-repo`, or typed at the prompt.
|
|
175
|
+
2. The store listing's source code link.
|
|
176
|
+
3. A GitHub URL among the listing's website, issues or contact links.
|
|
177
|
+
4. `github.com/<your GitHub login>/<snap name>`. This catches private snaps, which the
|
|
178
|
+
public store API can't see.
|
|
179
|
+
5. A prompt, when running in a terminal.
|
|
180
|
+
|
|
181
|
+
On GitHub, renamed repos are followed (and noted), and the tool records the default
|
|
182
|
+
branch, the location of `snapcraft.yaml` and the workflow files. Repos on other hosts
|
|
183
|
+
are recorded but not inspected.
|
|
184
|
+
|
|
185
|
+
### Detecting the builder
|
|
186
|
+
|
|
187
|
+
| Builder | When |
|
|
188
|
+
|---|---|
|
|
189
|
+
| `github-workflow` | A workflow builds the snap (`snapcore/action-build`, or `snapcraft` / `snapcraft pack` in a step). The tool records whether it also uploads (`snapcraft upload`, `snapcore/action-publish`), what triggers it (tags, manual dispatch, releases, pushes), and any hard-coded channel. |
|
|
190
|
+
| `launchpad` | A Launchpad snap recipe builds this snap from the repo. |
|
|
191
|
+
| `local` | Neither of the above: build with `snapcraft pack` on your machine. |
|
|
192
|
+
| `manual` | Only when you set it: you rebuild the snap some other way. |
|
|
193
|
+
|
|
194
|
+
A workflow or recipe that uploads to the store is preferred over one that only
|
|
195
|
+
builds. The candidates that weren't chosen are kept in the inventory.
|
|
196
|
+
|
|
197
|
+
### Deciding what needs work
|
|
198
|
+
|
|
199
|
+
`check` looks at the most stable published revision of each architecture on the
|
|
200
|
+
`latest` track.
|
|
201
|
+
|
|
202
|
+
| Signal | Verdict | When |
|
|
203
|
+
|---|---|---|
|
|
204
|
+
| Security | `rebuild` | A [Ubuntu Security Notice](https://ubuntu.com/security/notices) fixes a package the snap stages, and the fix is in the public Ubuntu archive. |
|
|
205
|
+
| Source | `update` | The repo has commits since the revision was uploaded, or `snapcraft.yaml` declares a different version. |
|
|
206
|
+
| Upstream | `update` | A part's git source has a newer tag than its `source-tag`, or new commits on the branch it tracks (GitHub only). |
|
|
207
|
+
|
|
208
|
+
For security, the check is **exact** when the published snap contains a build
|
|
209
|
+
manifest (`snap/manifest.yaml`). That manifest records the version of every staged
|
|
210
|
+
package, so a notice counts only if the snap has an older version than the fix.
|
|
211
|
+
|
|
212
|
+
Without a manifest, the check is **inferred**: it works out which packages the snap
|
|
213
|
+
stages (its `stage-packages` plus their dependencies, minus what the base snap
|
|
214
|
+
provides), and counts any notice issued after the revision was uploaded.
|
|
215
|
+
|
|
216
|
+
snapcraft writes the manifest only when building with `SNAPCRAFT_BUILD_INFO=1`.
|
|
217
|
+
Launchpad and `snapcore/action-build` set it automatically; for local builds, the
|
|
218
|
+
plans include it in the build command.
|
|
219
|
+
|
|
220
|
+
## Limitations
|
|
221
|
+
|
|
222
|
+
- Private snaps can't be downloaded, so their security checks are always inferred.
|
|
223
|
+
- Fixes that exist only in Ubuntu Pro (ESM) are shown as notes, because a rebuild
|
|
224
|
+
can't pick them up.
|
|
225
|
+
- Non-security Ubuntu updates, and dependencies bundled by language plugins (pip,
|
|
226
|
+
npm, cargo, go), aren't checked.
|
|
227
|
+
- Repo inspection, workflow detection and branch checks only support GitHub.
|
|
228
|
+
- The inferred check reads the base snap's package list from `/snap/<base>/current`.
|
|
229
|
+
If that base isn't installed locally, it may include packages the base already
|
|
230
|
+
ships.
|
|
231
|
+
|
|
232
|
+
## Data and privacy
|
|
233
|
+
|
|
234
|
+
- `inventory.json` is written to the current directory, the file given with `-i`, or
|
|
235
|
+
`SNAP_MAINTAINER_INVENTORY`. The MCP server uses
|
|
236
|
+
`~/.local/share/snap-maintainer/inventory.json` unless one of those is set.
|
|
237
|
+
- Ubuntu archive and security data is cached for 24 hours in
|
|
238
|
+
`~/.cache/snap-maintainer` (about 20 MB per Ubuntu release and architecture).
|
|
239
|
+
- Manifests of published revisions are cached there permanently. Each public
|
|
240
|
+
revision is downloaded once.
|
|
241
|
+
- Your Snap Store and GitHub logins stay with `snapcraft` and `gh`; this tool never
|
|
242
|
+
reads or stores them.
|
|
243
|
+
|
|
244
|
+
## Development
|
|
245
|
+
|
|
246
|
+
```sh
|
|
247
|
+
pip install -e '.[mcp]' pytest
|
|
248
|
+
python3 -m pytest
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Without the `mcp` package, the MCP server tests are skipped.
|
|
252
|
+
|
|
253
|
+
## License
|
|
254
|
+
|
|
255
|
+
[MIT](https://github.com/carlosbravoa/snap-maintainer/blob/master/LICENSE)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "snap-maintainer"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Find out which of your published snaps need a rebuild or a new release, and how to do it"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
dependencies = ["PyYAML>=6", "rich>=13"]
|
|
10
|
+
|
|
11
|
+
[project.optional-dependencies]
|
|
12
|
+
mcp = ["mcp>=2.3"]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://github.com/carlosbravoa/snap-maintainer"
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
snap-maintainer = "snap_maintainer.cli:main"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["setuptools>=77"]
|
|
22
|
+
build-backend = "setuptools.build_meta"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.packages.find]
|
|
25
|
+
where = ["src"]
|
|
26
|
+
|
|
27
|
+
[tool.pytest.ini_options]
|
|
28
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Discover and maintain published snaps."""
|