fix(upgrade skills): run abra over a pseudo-TTY (fixes FATA inappropriate ioctl)

abra over plain 'ssh cc-ci abra ...' has no TTY -> FATA 'inappropriate ioctl
for device' (the abra error). The working harness (runner/harness/abra.py)
wraps abra in util-linux 'script' for a pseudo-TTY + passes -n. Apply the
same in the recipe-upgrade and upgrade-all skills: every abra call becomes
ssh cc-ci 'script -qec "abra <args> -n" /dev/null'. Confirmed: abra server
ls FATAs plain, works pty-wrapped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
autonomic-bot
2026-06-02 04:06:38 +00:00
parent cdbc5bb42f
commit 027fdbd161
2 changed files with 29 additions and 12 deletions

View File

@ -29,15 +29,25 @@ test** to make a PR green.
## Procedure
> ### ⚠️ Run abra over a pseudo-TTY (or it FATAs `inappropriate ioctl for device`)
> `abra` needs a TTY. Over plain `ssh cc-ci 'abra …'` there is none, so many abra commands die with
> **`FATA inappropriate ioctl for device`** / `the input device is not a TTY`. This is how the working
> harness avoids it (`runner/harness/abra.py`): wrap every abra call in a pseudo-TTY via util-linux
> `script`, and pass `-n` (`--no-input`) so it never prompts. **Always invoke abra like this:**
> ```
> ssh cc-ci 'script -qec "abra <args> -n" /dev/null'
> ```
> (`git`/other commands do NOT need the wrapper — only `abra`.) Do this for every abra command below.
### 1. Plan the upgrade (research — follow recipe-upgrade-plan methodology)
On cc-ci, refresh the recipe and see what's available:
On cc-ci, refresh the recipe and see what's available (each abra call wrapped per the box above):
```
ssh cc-ci 'export PATH=/run/current-system/sw/bin:$PATH; \
git -C ~/.abra/recipes/<recipe> status --short; \
abra recipe fetch <recipe> --force; \
script -qec "abra recipe fetch <recipe> --force -n" /dev/null; \
git -C ~/.abra/recipes/<recipe> fetch origin main; \
abra recipe versions <recipe> -m; \
abra recipe upgrade <recipe> -m -n'
script -qec "abra recipe versions <recipe> -m -n" /dev/null; \
script -qec "abra recipe upgrade <recipe> -m -n" /dev/null'
```
- **Dirty worktree on cc-ci → abort this recipe** (status `SKIPPED — dirty-worktree`); don't prompt.
- **Reconcile the mirror first (always, even if up to date).** Run:
@ -61,11 +71,13 @@ ssh cc-ci 'export PATH=/run/current-system/sw/bin:$PATH; \
config changes, risks. (No human review gate — proceed straight to implement.)
### 2. Implement the upgrade (follow recipe-upgrade-apply methodology, on cc-ci)
On cc-ci's `~/.abra/recipes/<recipe>`:
- `abra recipe upgrade <recipe> -n` for the tags it can bump; hand-edit `compose.yml` for any tags it
won't, plus the config/env/volume/label changes the plan calls for.
On cc-ci's `~/.abra/recipes/<recipe>` (wrap every abra call per the pseudo-TTY box above —
`ssh cc-ci 'script -qec "abra … -n" /dev/null'`):
- `script -qec "abra recipe upgrade <recipe> -n" /dev/null` for the tags it can bump; hand-edit
`compose.yml` for any tags it won't, plus the config/env/volume/label changes the plan calls for.
- Bump the `coop-cloud.${STACK_NAME}.version` label to the new version string from the plan.
- `abra recipe lint <recipe> -C` — fix obvious lint errors; if unfixable, record it and continue.
- `script -qec "abra recipe lint <recipe> -C -n" /dev/null` — fix obvious lint errors; if unfixable,
record it and continue.
- Commit on a branch: `git commit -m "chore: upgrade to <new-version>"` (the commit message drives the
PR branch name `upgrade-<new-version>`). Do **not** push to upstream; do **not** tag-push.

View File

@ -30,12 +30,16 @@ Enrolled recipes = the cc-ci `tests/<recipe>/` dirs (same set `ci-test-review` s
```
ssh cc-ci 'cd /root/cc-ci/tests && ls -d */' | sed 's#/##' | grep -vE '^(_generic|unit|__pycache__)$'
```
(or the names passed in `$ARGUMENTS`). For each, on cc-ci, check availability — skip dirty/up-to-date:
(or the names passed in `$ARGUMENTS`). For each, on cc-ci, check availability — skip dirty/up-to-date.
> ⚠️ **Run abra over a pseudo-TTY** or it FATAs `inappropriate ioctl for device` (no TTY under plain
> ssh). Wrap every abra call in `script` (the working-harness fix in `runner/harness/abra.py`) and pass
> `-n`: `ssh cc-ci 'script -qec "abra <args> -n" /dev/null'`. `git`/other commands need no wrapper.
```
ssh cc-ci 'export PATH=/run/current-system/sw/bin:$PATH; \
git -C ~/.abra/recipes/<r> status --short; \
abra recipe fetch <r> --force; \
abra recipe upgrade <r> -m -n'
script -qec "abra recipe fetch <r> --force -n" /dev/null; \
script -qec "abra recipe upgrade <r> -m -n" /dev/null'
```
Build `RECIPES_TO_UPGRADE` = recipes with a **clean worktree** AND **≥1 available upgrade**. Others go
to `SKIPPED_UPFRONT` with a reason (`dirty-worktree`, `up-to-date`, `not-fetchable`).
@ -103,7 +107,8 @@ End with the report path and a reminder that **nothing was merged**.
## Safety / coordination (this matters — shared host with the build loops)
- **Sequential is the default for a reason.** Recipe deploys are **stateful on the shared Swarm** and
parallel deploys can OOM/collide. Between sequential recipes, the per-recipe `recipe-upgrade` tears
down what it deployed; verify a recipe is undeployed before the next starts (`abra app ls` on cc-ci).
down what it deployed; verify a recipe is undeployed before the next starts
(`ssh cc-ci 'script -qec "abra app ls -n" /dev/null'` — pseudo-TTY wrapped, per the box above).
- **Single-writer:** every PR (recipe or cc-ci test) is on a dedicated branch; **never push `main`**,
never touch the build loops' `/cc-ci` `/cc-ci-adv` working clones or their in-flight state.
- **Contention with active loop development:** while the loops are still building cc-ci, this run