Skip to content

Commit 979acd1

Browse files
committed
release: v0.11.0 — docs consolidation + PyPI restoration
Doc-only release. No source code changes. Three deliverables: 1. Per-version release notes for v0.5.0 through v0.10.0 (docs/release-notes/v0.{5,6,7,7.1,8,9,10}.0.md). Previously only v0.2.0 had one. v0.7.1 is a thin stub pointing at v0.7.0 (the surface those two share). docs/README.md release-notes index re-sorted newest-first. 2. PyPI publish job restored in .github/workflows/release.yml after sitting deferred since the v0.5.0 invalid-publisher incident. Uses pypa/gh-action-pypi-publish with OIDC trusted publishing — runs in parallel with github-release after build, so a PyPI outage doesn't block the GitHub Release upload (and vice versa). Stages wheel + sdist into dist-pypi/ so the SBOM JSON (which PyPI rejects) stays out of the upload. docs/release.md gained a "PyPI trusted-publisher setup" section walking through the one-time PyPI + GitHub Environment config. If either side is missing, publish-pypi fails with invalid-publisher but github-release still succeeds. 3. docs/architecture.md refreshed: - core/ listing now includes docker.py (v0.5), host_service.py (v0.9), version.py (eight detectors as of v0.8) - tools/ listing extended with the eight new tools shipped since v0.4 (ports, hosts, ssh, env, gpg, logs, db, stack, web/nginx, cron, tls, framework) - Added an "Adding a new tool" guidance block: which primitive to reach for by tool kind (CommandRunner / DockerEnv / HostService / atomic-write), sub-app parent convention (framework laravel, web nginx). CHANGELOG entries for previous releases are unchanged — this is append-only. Gates: pytest 1027 passed, ruff clean, mypy strict clean. The publish-pypi job will fail on this release's tag UNLESS the trusted-publisher prerequisites in docs/release.md are completed on PyPI first. github-release will still succeed regardless.
1 parent c896239 commit 979acd1

14 files changed

Lines changed: 1079 additions & 28 deletions

File tree

.github/workflows/release.yml

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,50 @@ jobs:
7474
name: dist
7575
path: dist/
7676

77-
# PyPI publishing is deliberately disabled. Distribution for v0.2.x
78-
# is via the GitHub Release page's wheel + sdist. To re-enable PyPI,
79-
# restore the publish-pypi + bump-homebrew-tap jobs from git history
80-
# and configure trusted publishing at
81-
# https://pypi.org/manage/account/publishing/ (Owner=simtabi,
82-
# Repository=shimkit, Workflow=release.yml, Environment=pypi,
83-
# PyPI Project Name=shimkit).
77+
# PyPI publishing — restored in v0.11.0 after sitting deferred from
78+
# v0.5.0 through v0.10.0 (six releases shipped to GitHub Releases
79+
# only). The job runs in the `pypi` GitHub Environment so trusted
80+
# publishing (OIDC) can sign the upload without long-lived tokens.
81+
#
82+
# PREREQUISITES (one-time, on the user's side):
83+
# 1. https://pypi.org/manage/account/publishing/ → add a pending
84+
# publisher: Owner=simtabi, Repository=shimkit,
85+
# Workflow=release.yml, Environment=pypi,
86+
# PyPI Project Name=shimkit
87+
# 2. GitHub repo Settings → Environments → New environment "pypi".
88+
# No secrets required; OIDC provides credentials at runtime.
89+
#
90+
# If either step is missing the upload fails with `invalid-publisher`.
91+
# See docs/release.md for the full restoration recipe.
92+
publish-pypi:
93+
needs: build
94+
runs-on: ubuntu-latest
95+
environment: pypi
96+
permissions:
97+
id-token: write
98+
steps:
99+
- uses: actions/download-artifact@v8
100+
with:
101+
name: dist
102+
path: dist/
103+
- name: Stage wheel + sdist only for PyPI
104+
# The SBOM lives in dist/ for GitHub Release upload but PyPI
105+
# rejects unknown artifacts. Copy just *.whl + *.tar.gz into
106+
# a separate dir.
107+
run: |
108+
mkdir -p dist-pypi
109+
cp dist/*.whl dist-pypi/
110+
cp dist/*.tar.gz dist-pypi/
111+
ls -la dist-pypi/
112+
- name: Publish to PyPI
113+
uses: pypa/gh-action-pypi-publish@release/v1
114+
with:
115+
packages-dir: dist-pypi/
116+
# We already attest provenance in the build job above.
117+
attestations: false
118+
# Re-runs against an existing tag (workflow_dispatch path)
119+
# would otherwise fail when the version is already on PyPI.
120+
skip-existing: true
84121

85122
github-release:
86123
needs: build

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,41 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
66

77
## [Unreleased]
88

9+
## [0.11.0] — 2026-05-15
10+
11+
### Added
12+
13+
- `docs/release-notes/v0.5.0.md` through `v0.10.0.md` — per-version
14+
user-facing release notes. Previously only v0.2.0 had one.
15+
v0.7.1 has a thin stub pointing at v0.7.0.
16+
- `docs/release.md` — PyPI trusted-publisher setup section
17+
(one-time, by a maintainer with PyPI write access).
18+
19+
### Changed
20+
21+
- `.github/workflows/release.yml` — restored the `publish-pypi`
22+
job using `pypa/gh-action-pypi-publish` with OIDC trusted
23+
publishing. Runs in parallel with `github-release` after `build`
24+
so a PyPI outage doesn't block the GitHub Release. Stages
25+
wheel + sdist into `dist-pypi/` so the SBOM JSON (which PyPI
26+
rejects) stays out of the upload.
27+
- `docs/architecture.md` — extended the `core/` directory listing
28+
with `docker.py`, `host_service.py`, `version.py` (added across
29+
v0.5-v0.9). Extended the `tools/` listing with the eight new
30+
tools shipped since v0.4. Added an "Adding a new tool" guidance
31+
block: which primitive to reach for by tool kind, sub-app
32+
parent convention (framework laravel, web nginx).
33+
- `docs/README.md` — release-notes index lists v0.10 → v0.5.0
34+
newest-first plus the v0.7.1 patch.
35+
36+
### Notes
37+
38+
This is a **doc-only release**. No source code changes. PyPI
39+
publishing for v0.11.0 itself requires the user to complete the
40+
trusted-publisher setup on PyPI first (see `docs/release.md`);
41+
the workflow will succeed for `github-release` and fail at
42+
`publish-pypi` with `invalid-publisher` otherwise.
43+
944
## [0.10.0] — 2026-05-15
1045

1146
### Tests

docs/README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,27 @@ Top-level utilities (not tools):
6161

6262
## Release notes
6363

64-
Per-version, user-facing summaries:
65-
64+
Per-version, user-facing summaries (newest first):
65+
66+
- **[`v0.10.0`](release-notes/v0.10.0.md)** — coverage push 74% → 85%
67+
(+397 tests). No code changes.
68+
- **[`v0.9.0`](release-notes/v0.9.0.md)**`shimkit db --on-host`
69+
for mysql/mariadb/postgres. Manages existing host installs;
70+
refuses to install packages (the audit-completion bit).
71+
- **[`v0.8.0`](release-notes/v0.8.0.md)**`shimkit tls` (certbot
72+
container-first cert lifecycle). State at
73+
`~/.shimkit/data/tls/`; webroot ACME; daily renewal cron.
74+
- **[`v0.7.1`](release-notes/v0.7.1.md)** — version-drift recovery
75+
for v0.7.0.
76+
- **[`v0.7.0`](release-notes/v0.7.0.md)** — `shimkit framework
77+
laravel` (perms / env / cron-install / artisan). First framework
78+
recipe under the new `framework` parent.
79+
- **[`v0.6.0`](release-notes/v0.6.0.md)**`shimkit cron` (generic
80+
user-crontab editor). Atomic write + backup-on-mutate.
81+
- **[`v0.5.0`](release-notes/v0.5.0.md)** — ubuntu/ migration:
82+
three new sub-trees (`db`/`stack`/`web`), two new core
83+
primitives (`core/docker`, `core/version`), 152 new tests. Five
84+
Critical audit flags dissolved by Docker-first design.
6685
- **[`v0.2.0`](release-notes/v0.2.0.md)** — three new tools (`dns`,
6786
`adguard`, `docker-clean`); uniform CLI surface across all new
6887
subcommands; argv-list PM templates; container hardening + SBOM

docs/architecture.md

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ non-interactive methods, optional menu loop, Typer commands.
1212
> pre-v0.5.0 snapshot.
1313
> - [`architecture-target.md`](../.design/architecture-target.md)
1414
> v0.5.0 layout with the new `db` / `stack` / `web` sub-trees and
15-
> the two new core primitives (`core/version`, `core/docker`).
15+
> two new core primitives (`core/version`, `core/docker`). v0.6+
16+
> additions (cron, framework, tls, host_service) follow the same
17+
> patterns and slot into the same tree.
1618
> - [`version-constraints-spec.md`](../.design/version-constraints-spec.md)
1719
> how tool-version checks work.
1820
@@ -46,12 +48,41 @@ src/shimkit/
4648
cli_flags.py Shared Typer Option defaults so every tool's
4749
subcommands have identical --dry-run/--json/
4850
--quiet/--verbose/--log-file/--timeout semantics
51+
docker.py DockerEnv — single chokepoint for the docker-py
52+
SDK. Standardises container naming
53+
(shimkit-<scope>-<kind>-<id>) + volume layout.
54+
Refuses deletion outside ~/.shimkit/data/.
55+
Added in v0.5.0; gained run_oneshot() in v0.8.0.
56+
host_service.py HostService — cross-platform service-manager
57+
facade. SystemdHost (Linux) + BrewServicesHost
58+
(macOS). Added in v0.9.0 for `shimkit db
59+
--on-host`; available to any future tool that
60+
manages host daemons.
61+
version.py Tool-version detection + constraint enforcement.
62+
One source of truth (tools.versions registry)
63+
consulted at three points: install docs, runtime
64+
preflight, `shimkit doctor`. Eight built-in
65+
detectors: docker, nginx, git, gpg, python,
66+
php (v0.7), openssl (v0.8).
4967
tools/
5068
java/ OpenJDK manager (macOS + Linux)
51-
shell/ Shell upgrader (bash/zsh/fish/ksh)
69+
shell/ Shell upgrader (bash/zsh/fish/ksh) + colors
5270
dns/ macOS DNS resolver recovery (port of fixdns.sh)
5371
adguard/ AdGuard Home port-conflict fixer (Linux)
5472
docker_clean/ Docker resource cleanup (Linux + macOS + WSL)
73+
ports/ TCP/UDP port owner lookup + kill (v0.3.0)
74+
hosts/ /etc/hosts editor with atomic write (v0.3.0)
75+
ssh/ SSH keys + agent + perms hygiene (v0.3.0)
76+
env/ .env viewer + scaffolder, secret redaction (v0.4.0)
77+
gpg/ GPG keys + git-signing config (v0.4.0)
78+
logs/ System log tail/grep (macOS + Linux) (v0.4.0)
79+
db/ Container-first databases (5 engines) (v0.5.0)
80+
+ --on-host mode (v0.9.0)
81+
stack/ Multi-container app recipes (LEMP today) (v0.5.0)
82+
web/nginx/ Hardened nginx vhost generator (v0.5.0)
83+
cron/ Generic user-crontab editor (v0.6.0)
84+
tls/ TLS cert lifecycle via certbot container (v0.8.0)
85+
framework/ Framework-specific helpers (Laravel today) (v0.7.0)
5586
```
5687

5788
## The five load-bearing rules
@@ -220,6 +251,37 @@ ShimkitConfig instance (frozen pydantic v2)
220251
- CLI `--help` lists subcommands
221252
- At least one subcommand's exit-code contract
222253
5. Add `docs/tools/<name>.md` and link it from `docs/README.md`.
254+
6. Add a CHANGELOG entry under `[Unreleased]`.
255+
256+
### Choosing the right primitives
257+
258+
By tool kind:
259+
260+
| Tool kind | Use |
261+
|-----------|-----|
262+
| Pure host shell-outs | `CommandRunner` + `Platform` (java, shell, dns) |
263+
| Container orchestration | `DockerEnv` + `Platform`, preflight via `version.preflight(("docker",))` (db, stack, web, tls) |
264+
| Manages a host daemon | `HostService.detect(platform)` (db --on-host) |
265+
| Reads/writes a system file | atomic-write pattern: tempfile + `sudo install`, with a backup-on-mutate sidecar (hosts, web/nginx vhost apply, cron) |
266+
| Wraps another shimkit tool | Import its `Manager` directly (framework laravel cron-install → cron) |
267+
268+
### Sub-app parents
269+
270+
Some tools have a sub-app parent (e.g. `framework laravel`, `web
271+
nginx vhost`). The convention:
272+
273+
```
274+
tools/<parent>/
275+
__init__.py
276+
commands.py Parent Typer app — registers children
277+
<child>/
278+
__init__.py
279+
manager.py
280+
commands.py Child Typer app
281+
```
282+
283+
Sub-apps inherit the same five rules. Each child remains independently
284+
testable, and the parent is mostly a Typer-glue file.
223285

224286
A tool joins shimkit only if it shares ≥ 2 of `Platform` / `Shell` /
225287
`PackageManager` / `UI` / `Menu`. Otherwise it's a separate package.

docs/release-notes/v0.10.0.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# shimkit 0.10.0
2+
3+
Internal quality release. Coverage push from 74% to 85%. **No
4+
source-code changes** outside one small lint-config tweak. For the
5+
full machine-readable changelog, see
6+
[`CHANGELOG.md`](../../CHANGELOG.md).
7+
8+
---
9+
10+
## TL;DR
11+
12+
```
13+
test count: 630 → 1027 (+397)
14+
coverage: 74% → 85%
15+
```
16+
17+
Across 15 new test files targeting previously-thin code paths in
18+
the existing managers — no new tools, no new public APIs, no
19+
behavioural changes.
20+
21+
---
22+
23+
## What got covered
24+
25+
| Module | Old → New | Notes |
26+
|--------|-----------|-------|
27+
| `core/command` | 65% → 100% | sudo / has_sudo / CommandResult / exception paths |
28+
| `core/menu` | 50% → ~95% | FallbackMenu select / confirm / checkbox edges |
29+
| `core/host_service`| 45% → ~95% | SystemdHost + BrewServicesHost state/start/stop |
30+
| `core/systemd` | 58% → ~95% | lifecycle helpers, journal argv shape |
31+
| `core/platform` | 77% → ~95% | WSL detection, container probes, brew-prefix linux |
32+
| `core/ui` | 61% → ~85% | banner, spinner, quiet mode, color override |
33+
| `tools/java/manager` | 18% → ~85% | menu loop + all _menu_* branches |
34+
| `tools/java/scanner` | 21% → ~90% | scan / sdkman / JAVA_HOME / active_version |
35+
| `tools/java/installer` | 27% → ~85% | install/reinstall/uninstall/upgrade/switch |
36+
| `tools/java/brew` | 37% → ~90% | prefix cache + lifecycle + outdated parse |
37+
| `tools/ssh/manager`| 57% → ~80% | keys + agent + known_hosts + perms + config |
38+
| `tools/ssh/scanner`| 73% → ~95% | pure parsers (agent / known_hosts / audit) |
39+
| `tools/adguard/manager` | 59% → ~70% | service / logs / rollback / config_validate |
40+
| `tools/dns/manager`| 70% → ~80% | diagnose / flush / show / test / set / reset / fix |
41+
| `tools/dns/fixer` | 63% → ~75% | detect_interference / backup-dir safety / rollback |
42+
| `tools/db/manager` | 78% → ~85% | _to_status_row / --on-host JSON / dump-to-file |
43+
| `tools/gpg/manager`| 64% → ~75% | keys / git-signing / agent / run loop |
44+
| `tools/docker_clean/manager` | 64% → ~70% | dispatch / quick / nuke / inspect / compose-down |
45+
| `tools/tls/manager`| 76% → ~85% | revoke dry-run / renew failure / cron-install |
46+
| `tools/hosts/manager` | 70% → ~80% | _read_source / _back_up / _atomic_write fallback |
47+
| `tools/web/nginx/manager` | 73% → ~80% | list empty / apply + remove severe-token gates |
48+
| `cli` | 73% → ~85% | doctor / config show/path/edit/validate / per-tool --help |
49+
50+
---
51+
52+
## Test pattern
53+
54+
Same as the rest of the suite: mock at the boundary
55+
(`Platform.detect`, `CommandRunner.run`, `shutil.which`,
56+
component factories), no real subprocess or daemon access. The new
57+
tests follow the existing module convention — each
58+
`test_coverage_v010_*.py` covers a specific target area.
59+
60+
---
61+
62+
## Non-test changes
63+
64+
One tweak to `pyproject.toml`:
65+
66+
```toml
67+
[tool.ruff.lint.per-file-ignores]
68+
# Test stubs use throwaway classes to mock pydantic models; RUF012
69+
# (mutable class-attribute defaults need ClassVar) is real-code
70+
# advice that doesn't apply to dataclass-style test fakes.
71+
"tests/*" = ["RUF012"]
72+
```
73+
74+
That's it. Zero source changes.
75+
76+
---
77+
78+
## What's left uncovered
79+
80+
1,266 of 8,300 lines still uncovered (15%), mostly:
81+
82+
- UI rendering paths in `adguard/manager` that branch on
83+
`Menu.select` choices — exhaustive coverage would require
84+
re-implementing each manager's dispatch table.
85+
- Linux-only paths in `dns/fixer` (`step_cycle_interface` for
86+
non-Wi-Fi devices, `step_nuclear` plist removal) where the
87+
test fixture would need to mock `airport_power` + `ifconfig`
88+
+ the SystemConfiguration plist tree.
89+
- macOS scutil + networksetup boundary modules — covered by the
90+
manager-level tests but not exhaustively.
91+
92+
---
93+
94+
## Stats
95+
96+
- Tests: 630 → 1027 (+397)
97+
- Files added: 15 (all under `tests/test_coverage_v010_*.py`)
98+
- Source LOC changes: 0
99+
- Gates: pytest 1027 passed, ruff clean, mypy strict clean
100+
101+
---
102+
103+
## Upgrading
104+
105+
```bash
106+
uv tool upgrade shimkit
107+
pipx upgrade shimkit
108+
```
109+
110+
No config changes, no breaking changes, no new dependencies.

0 commit comments

Comments
 (0)