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:
@ -29,15 +29,25 @@ test** to make a PR green.
|
|||||||
|
|
||||||
## Procedure
|
## 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)
|
### 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; \
|
ssh cc-ci 'export PATH=/run/current-system/sw/bin:$PATH; \
|
||||||
git -C ~/.abra/recipes/<recipe> status --short; \
|
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; \
|
git -C ~/.abra/recipes/<recipe> fetch origin main; \
|
||||||
abra recipe versions <recipe> -m; \
|
script -qec "abra recipe versions <recipe> -m -n" /dev/null; \
|
||||||
abra recipe upgrade <recipe> -m -n'
|
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.
|
- **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:
|
- **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.)
|
config changes, risks. (No human review gate — proceed straight to implement.)
|
||||||
|
|
||||||
### 2. Implement the upgrade (follow recipe-upgrade-apply methodology, on cc-ci)
|
### 2. Implement the upgrade (follow recipe-upgrade-apply methodology, on cc-ci)
|
||||||
On cc-ci's `~/.abra/recipes/<recipe>`:
|
On cc-ci's `~/.abra/recipes/<recipe>` (wrap every abra call per the pseudo-TTY box above —
|
||||||
- `abra recipe upgrade <recipe> -n` for the tags it can bump; hand-edit `compose.yml` for any tags it
|
`ssh cc-ci 'script -qec "abra … -n" /dev/null'`):
|
||||||
won't, plus the config/env/volume/label changes the plan calls for.
|
- `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.
|
- 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
|
- 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.
|
PR branch name `upgrade-<new-version>`). Do **not** push to upstream; do **not** tag-push.
|
||||||
|
|
||||||
|
|||||||
@ -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__)$'
|
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; \
|
ssh cc-ci 'export PATH=/run/current-system/sw/bin:$PATH; \
|
||||||
git -C ~/.abra/recipes/<r> status --short; \
|
git -C ~/.abra/recipes/<r> status --short; \
|
||||||
abra recipe fetch <r> --force; \
|
script -qec "abra recipe fetch <r> --force -n" /dev/null; \
|
||||||
abra recipe upgrade <r> -m -n'
|
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
|
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`).
|
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)
|
## 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
|
- **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
|
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`**,
|
- **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.
|
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
|
- **Contention with active loop development:** while the loops are still building cc-ci, this run
|
||||||
|
|||||||
Reference in New Issue
Block a user