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.
Files changed (35) hide show
  1. snap_maintainer-0.1.0/LICENSE +21 -0
  2. snap_maintainer-0.1.0/PKG-INFO +270 -0
  3. snap_maintainer-0.1.0/README.md +255 -0
  4. snap_maintainer-0.1.0/pyproject.toml +28 -0
  5. snap_maintainer-0.1.0/setup.cfg +4 -0
  6. snap_maintainer-0.1.0/src/snap_maintainer/__init__.py +1 -0
  7. snap_maintainer-0.1.0/src/snap_maintainer/__main__.py +3 -0
  8. snap_maintainer-0.1.0/src/snap_maintainer/archive.py +143 -0
  9. snap_maintainer-0.1.0/src/snap_maintainer/builders.py +205 -0
  10. snap_maintainer-0.1.0/src/snap_maintainer/cache.py +38 -0
  11. snap_maintainer-0.1.0/src/snap_maintainer/checks.py +340 -0
  12. snap_maintainer-0.1.0/src/snap_maintainer/cli.py +197 -0
  13. snap_maintainer-0.1.0/src/snap_maintainer/debver.py +52 -0
  14. snap_maintainer-0.1.0/src/snap_maintainer/discovery.py +216 -0
  15. snap_maintainer-0.1.0/src/snap_maintainer/hints.py +211 -0
  16. snap_maintainer-0.1.0/src/snap_maintainer/manifests.py +81 -0
  17. snap_maintainer-0.1.0/src/snap_maintainer/mcp_server.py +275 -0
  18. snap_maintainer-0.1.0/src/snap_maintainer/preflight.py +102 -0
  19. snap_maintainer-0.1.0/src/snap_maintainer/repo.py +114 -0
  20. snap_maintainer-0.1.0/src/snap_maintainer/snapcraft_yaml.py +115 -0
  21. snap_maintainer-0.1.0/src/snap_maintainer/store.py +181 -0
  22. snap_maintainer-0.1.0/src/snap_maintainer/ui.py +440 -0
  23. snap_maintainer-0.1.0/src/snap_maintainer/usn.py +54 -0
  24. snap_maintainer-0.1.0/src/snap_maintainer.egg-info/PKG-INFO +270 -0
  25. snap_maintainer-0.1.0/src/snap_maintainer.egg-info/SOURCES.txt +33 -0
  26. snap_maintainer-0.1.0/src/snap_maintainer.egg-info/dependency_links.txt +1 -0
  27. snap_maintainer-0.1.0/src/snap_maintainer.egg-info/entry_points.txt +2 -0
  28. snap_maintainer-0.1.0/src/snap_maintainer.egg-info/requires.txt +5 -0
  29. snap_maintainer-0.1.0/src/snap_maintainer.egg-info/top_level.txt +1 -0
  30. snap_maintainer-0.1.0/tests/test_builders.py +112 -0
  31. snap_maintainer-0.1.0/tests/test_checks.py +166 -0
  32. snap_maintainer-0.1.0/tests/test_cli.py +67 -0
  33. snap_maintainer-0.1.0/tests/test_hints.py +88 -0
  34. snap_maintainer-0.1.0/tests/test_mcp_server.py +74 -0
  35. 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ """Discover and maintain published snaps."""
@@ -0,0 +1,3 @@
1
+ from snap_maintainer.cli import main
2
+
3
+ raise SystemExit(main())