Skip to content

One Podman look for the status report and the app page; compose runs through Podman.stream - #50

Merged
Bobowski merged 1 commit into
mainfrom
status-and-podman
Nov 2, 2026
Merged

Bobowski merged 1 commit into
mainfrom
status-and-podman

Conversation

@Bobowski

@Bobowski Bobowski commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Intent

Remove the duplicate code of the status report and of the compose runner. Source: the user, in the Yard audit. This pull request stacks on #49.

Found

  • status.report and status.app_report each ran ps -a, stats, and the crash inspect, and each built the rows of an app.
  • Each status call sent one Caddy /adapt request for each app, through sites.list_hosts. yard status asked Caddy about every site file every time.
  • _row walked the data folder of an app once for each container of that app.
  • compose.py built its own Podman argv, its own --remote flag, and its own copy of os.environ (_podman, _process_env, _run). Podman holds the same logic. _process_env set CONTAINER_HOST only when it was missing. The yard container and the apply image always set it (ops/compose.yml, ops/Containerfile), so the function changed nothing in production.

Changed

  • status._look asks Podman once: the containers, the samples, and the crashes. report and app_report both use it and both use _app_rows. _app_rows reads the tip, the deploy record, and the data size of the app once.
  • _row takes the data size and no Config.
  • sites.list_hosts keeps the hosts of each site file in Yard.site_hosts, keyed by the (mtime_ns, size) of the file. Caddy adapts a file again only when the file changes.
  • Podman.stream(*args, on_line) runs a command with no timeout and streams each line. compose runs rm, run, logs -f, and build through it. _process_env and _run are gone.

Not done

  • I did not pass yard.podman into compose. compose makes a Podman from the config, so the signatures of compose.apply and of the test fakes stay the same.

Proof

$ just check
...
500 passed in 61.46s

test_list_hosts_adapts_each_file_once shows one /adapt for two reads, then a new adapt after the file changes. test_stream_calls_on_line_in_order checks the line stream and the exit code.

Questions

  • A missing podman binary in a compose call now raises the code podman in place of compose. Nothing reads that code.

🤖 Generated with Claude Code

…through Podman.stream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Bobowski
Bobowski merged commit 754d51a into main Nov 2, 2026
1 check passed
@Bobowski
Bobowski deleted the status-and-podman branch November 2, 2026 22:55
Bobowski added a commit that referenced this pull request Nov 2, 2026
…ollow for logs and events (#51)

## Intent

Let each forge own its own differences, and let the journal adapter own
the follow process. Source: the user, in the Yard audit. This pull
request stacks on #50.

## Found

- `origin_hook` and `github_hook` in `api.py` each read the body, parsed
the JSON, found the repo, logged `hook.received`, and scheduled a check.
Only the signature and the body shape differ.
- `forge_put` had one branch for each forge, with `isinstance` checks.
Each branch validated its fields, called `connect`, sent the same
journal event, and built the app answer.
- The error "is not connected. Run yard forge setup" was written three
times, in `apps.py` and two times in `repos.py`.
- `_stream_journal` (with and without a filter) and `_stream_events`
each ran the journalctl follow process, read lines, and killed the
process in a `finally`.
- `logs_get` had three read paths: JSON, text without a filter, and text
with a filter.
- `_deploy_row` in the events had its own copy of the exit-code rule
from #49.

## Changed

- `Forge` gets `signed`, `delivery`, `connect_with`, `app_info`,
`require_connected`, and the attributes `event_header`,
`signature_header`, and `connect_fields`. Origin and GitHub fill them
in. `HooksPage._hook` is the one handler for both routes. `forge_put`
validates `connect_fields` and has no `isinstance` checks.
- `journal.follow_entries(proc, tick)` yields the entries of a follow
process, and `None` at each tick with no new line. It kills the process
when it closes. Both streams use it in `contextlib.aclosing`.
`journal.follow` still starts the process before the headers go out, so
a missing journal is still a 502.
- `logs_get` reads the entries once, applies the filter once, then
writes JSON or text. `_logs_page` is gone.
- `_deploy_row` uses `status.deploy_status` and `status.deploy_error`.

The webhook body shapes moved from `api.py` into `forges.py`.
`forges.py` already reads the other forge-specific answers
(installations, tokens, field names), so a delivery body fits there.

## Not done

- The legacy installation state in `Forge.installations` stays. A later
pull request removes it with the 0.10 migration.

## Proof

```
$ just check
...
501 passed in 61.14s
```

The hook tests in `tests/server/test_api.py` (status codes, a bad
signature, a push, an installation event) and the log and event tests in
`tests/server/test_journal.py` pass without a change.

## Questions

- An Origin delivery with broken JSON now sets `last_delivery` before
the 400. Before, only a delivery that parsed set it. GitHub already set
it before the parse.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Bobowski added a commit that referenced this pull request Nov 2, 2026
## Intent

Remove the code that moves a 0.10 host to 0.11. Source: the user, in the
Yard audit. The user asked me to check the host with the yard CLI first.
This pull request stacks on #52.

## Found

The host runs 0.11 and has moved:

- `yard --version` prints `0.11.0`. `yard status` shows `yard-app-1` on
the commit of #47.
- The `host` column of `yard status` comes from the site files, so the
site files exist. Thus the 0.10 import does nothing at boot.
- `yard forge ls --json` shows the GitHub state with an `installations`
list, and each entry has an account. Thus no state has the old top-level
`installation_id`.

The code that only served the move:

- `sites._import_binds`, `_bind`, and `_owner`: with no site file, boot
wrote default sites from the 0.10 bind routes that Caddy resumed. Four
tests covered it, one of them on a real Caddy with a 0.10 autosave.
- `Forge.installations` read a top-level `installation_id` as one
installation. `installation_for` let an installation with no account own
every repo. Each `save` dropped the old keys.

## Changed

- `sites.boot` only loads the Caddyfile, with the same retry when Caddy
is not up yet. Three new small tests cover the retry, the give-up, and a
refused load.
- `Forge.installations` reads the `installations` list only.
`installation_for` matches the account, case-insensitively. The saves
drop only `installations`.
- `CHANGELOG.md`: two breaking lines ("upgrade a 0.10 host through 0.11
first") and the line for the hosts cache from #50. `AGENTS.md` no longer
names `_import_binds`.

## Not done

- `CADDY_ADMIN` stays in `ops/compose.yml`. `ops/Caddyfile` sets the
same admin socket, so the env is now redundant. But a change to the
Caddy service recreates the Caddy container on the next yard deploy, and
this pull request must not touch the proxy.
- `status._image_plan` (a host with no labelled deploy container) stays.
It also serves an app that has no deploy container at all.

## Proof

```
$ just check
...
498 passed in 64.96s
```

## Questions

None

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant