row 186 (#1684): the activate plan's backup line says what the run will do
activate --plan printed the backup line whenever the store file existed. But run_steps rolls back with store-newer or migration-needs-flag before backup_store, so in those two cases the plan said both that the run rolls back and that it backs up the store. The plan now prints "backup: none; the run rolls back before the backup" there. The no-store line comes first, since with no store file the run skips the whole store block. When S_CHANGED is set the plan keeps the backup line: a wrong backup line costs nothing, a wrong none line hides the directory a --restore needs. run_steps, backup_store, refusals, exit codes and flags are unchanged; the diff to stack-release.sh is inside plan only. docs/TOOLS.md "Pinned stack update" step 3 names the backup line. These are Filbert's N1 and N2 on row 185 (P19 404). stack-release.test.mjs updates T26's not-given case and adds T52, six activate cases that plan, check the one backup line and that nothing was written, then run and check the backup exists exactly when the plan said so. The three brief mutants are killed, and so is Filbert's M7 (an S_CHANGED read counted as early, by T29). Filbert's M6 (the early test ahead of the no-store test) survives, since no test plans a missing store with an out-of-chain pointer schema; that is a bounded follow-up with the store-unclean reading, on P19 404. Filbert approved round 1 in #1684 comment 29419, with two non-blocking notes.
This commit is contained in:
+6
-3
@@ -404,9 +404,12 @@ refuses `capture-failed` before it stops anything. Adopt uses the same rule.
|
||||
full id.
|
||||
3. Run `activate ID BUSINESS --plan` and take the output to Mos. It names the
|
||||
store's schema and the target's, any migration with `--migrate` and the
|
||||
decision 96 condition 2 line (Jason present), the commits the run adds,
|
||||
and the three install commands. It refuses `copies-unrelated` when neither
|
||||
copy is an ancestor of the other.
|
||||
decision 96 condition 2 line (Jason present), the backup, the commits the
|
||||
run adds, and the three install commands. The `backup:` line gives the
|
||||
directory the run backs the stopped store up to, or why it takes none:
|
||||
there is no store file, or the run rolls back with `store-newer` or
|
||||
`migration-needs-flag` before the backup. It refuses `copies-unrelated`
|
||||
when neither copy is an ancestor of the other.
|
||||
4. Mos removes any bus drop-in that overrides ExecStart or WorkingDirectory;
|
||||
`activate` refuses `drop-in-override` and names the file.
|
||||
5. With Mos's go, `activate ID BUSINESS`, plus `--migrate` when the plan
|
||||
|
||||
@@ -380,6 +380,7 @@ async function seed(e,{copy=B,pm=false,store='3g',pointer:how='adopt'}={}){
|
||||
|
||||
const rolledBack=(code,to=B)=>`stack-release: ${code}; rolled back to ${to}, which passed the gate\n`;
|
||||
const planBackup=(e)=>`backup: the run backs up the stopped store to ${e.data}/backups/stack/<activation>/ before any migration`;
|
||||
const planNoBackup='backup: none; the run rolls back before the backup';
|
||||
const planTail=(e,to,pm=false)=>[`bus: scripts/bus-service.sh install --copy ${to} --copies ${COPIES}${pm?' --pm':''} --claude ${e.claude}`,`Console: scripts/webui-service.sh install --copy ${to} --copies ${COPIES} --port ${e.port} --business acme`,`CLI: scripts/stack-copy.sh wrapper ${to} --copies ${COPIES} --bin ${e.bin}`];
|
||||
|
||||
// --- The T table ---
|
||||
@@ -643,9 +644,9 @@ test('T26 --plan lists the commits an activate adds, after the migration lines',
|
||||
'migration: 3g to 3h, after a backup of the stopped store','migration steps: 3g to 3h',`needs: --migrate (${mig})`,'needs: Jason present for this run (decision 96 condition 2)'];
|
||||
const adds=commitLines(`${B}..${C}`);
|
||||
assert.equal(adds.length,2);assert.match(adds[0],/^ {2}[0-9a-f]{7,} row 904: C$/);assert.match(adds[1],/^ {2}[0-9a-f]{7,} row 903: C1$/);
|
||||
for(const [flags,mig] of [[[],'not given; the run would roll back with migration-needs-flag'],[['--migrate'],'given']]){
|
||||
for(const [flags,mig,backup] of [[[],'not given; the run would roll back with migration-needs-flag',planNoBackup],[['--migrate'],'given',planBackup(e)]]){
|
||||
const r=await run(e,'activate',C,'acme','--plan',...flags);assert.equal(r.code,0,r.err);
|
||||
assert.equal(r.out,[...head(mig),planBackup(e),'commits: the run adds 2 (newest first, at most 200):',...adds,...planTail(e,C),''].join('\n'));
|
||||
assert.equal(r.out,[...head(mig),backup,'commits: the run adds 2 (newest first, at most 200):',...adds,...planTail(e,C),''].join('\n'));
|
||||
}
|
||||
const same=await run(e,'activate',C1,'acme','--plan');assert.equal(same.code,0,same.err);
|
||||
assert.ok(same.out.includes(`target schema: 3g\nmigration: none; the store is at the target's version\n${planBackup(e)}\ncommits: the run adds 1 (newest first, at most 200):\n`),same.out);
|
||||
@@ -1373,6 +1374,31 @@ test('T51 row 185 test 5: an activate with no store file takes no backup',async(
|
||||
const l=logLines(e).at(-1);assert.deepEqual([l.result,l.backup],['ok',null]);
|
||||
});
|
||||
|
||||
// Row 186 (#1684): the activate plan prints the backup line only when the run reaches backup_store.
|
||||
// Each case plans, checks nothing was written, then runs and checks a backup exists exactly when the plan said so.
|
||||
test('T52 row 186: the activate plan\'s backup line says what the run will do',async(t)=>{
|
||||
const newer='migration: none possible; 3z is not in 3h\'s migration chain, so the run would roll back with store-newer';
|
||||
const cases=[
|
||||
['same schema','3g',C1,[],'migration: none; the store is at the target\'s version',true,0],
|
||||
['same schema with --migrate','3g',C1,['--migrate'],'migration: none; the store is at the target\'s version',true,0],
|
||||
['in chain with --migrate','3g',C,['--migrate'],'needs: --migrate (given)',true,0],
|
||||
['in chain without --migrate','3g',C,[],'needs: --migrate (not given; the run would roll back with migration-needs-flag)',false,1],
|
||||
['outside the chain','3z',C,[],newer,false,1],
|
||||
['outside the chain with --migrate','3z',C,['--migrate'],newer,false,1],
|
||||
];
|
||||
for(const [name,store,to,flags,why,backup,code] of cases)await t.test(name,async(t)=>{
|
||||
const e=await mk(t);await seed(e,{store});
|
||||
const before=fp(e),data=tree(e.data),n=calls(e).length,logs=logLines(e).length;
|
||||
const p=await run(e,'activate',to,'acme','--plan',...flags);assert.equal(p.code,0,p.err);assert.equal(p.err,'');
|
||||
const out=p.out.split('\n');assert.ok(out.includes(why),p.out);
|
||||
assert.deepEqual(out.filter((x)=>x.startsWith('backup')),[backup?planBackup(e):planNoBackup]);
|
||||
assert.deepEqual(fp(e),before);assert.deepEqual(tree(e.data),data);assert.deepEqual(startsStops(e,n),[]);assert.equal(logLines(e).length,logs);
|
||||
assert.ok(!existsSync(join(e.data,'backups')));
|
||||
const r=await run(e,'activate',to,'acme',...flags,'--timeout','3');assert.equal(r.code,code,r.out+r.err);
|
||||
assert.equal(existsSync(join(e.data,'backups','stack')),backup);assert.equal(logLines(e).at(-1).backup??null,backup?logLines(e).at(-1).activation:null);
|
||||
});
|
||||
});
|
||||
|
||||
// Row 178: proof metadata uses temp fixture files, never the installed slot.
|
||||
async function seedProof(e) {
|
||||
await seed(e);
|
||||
|
||||
@@ -797,6 +797,9 @@ plan() {
|
||||
echo "store schema: $S_SCHEMA ($S_FROM)"
|
||||
echo "target schema: $T_SCHEMA"
|
||||
[ "$S_SCHEMA" = "$P_SCHEMA" ] || [ -n "$S_CHANGED" ] || echo "note: the pointer says $P_SCHEMA; the run reads the stopped store and goes by that"
|
||||
# Row 186: set when run_steps would roll back with store-newer or migration-needs-flag, which
|
||||
# come before backup_store. A read the bus stopped under judges nothing, so it keeps the backup line.
|
||||
local early=""
|
||||
if [ -n "$S_CHANGED" ]; then
|
||||
: # no migration or restore line from a read the bus stopped under
|
||||
elif [ "$S_SCHEMA" = "$T_SCHEMA" ]; then
|
||||
@@ -806,17 +809,21 @@ plan() {
|
||||
echo "migration steps: $(plan_steps)"
|
||||
echo "needs: --migrate$([ "$MIGRATE" = 1 ] && echo ' (given)' || echo ' (not given; the run would roll back with migration-needs-flag)')"
|
||||
echo "needs: Jason present for this run (decision 96 condition 2)"
|
||||
[ "$MIGRATE" = 1 ] || early=1
|
||||
elif [ "$OP" = activate ]; then
|
||||
echo "migration: none possible; $S_SCHEMA is not in $T_SCHEMA's migration chain, so the run would roll back with store-newer"
|
||||
early=1
|
||||
else
|
||||
echo "restore: the store must go back to $T_SCHEMA; needs --restore $(from_log backup || echo ACTIVATION)"
|
||||
fi
|
||||
if [ "$OP" != activate ]; then
|
||||
: # rollback takes no backup of its own
|
||||
elif [ -e "$STORE" ]; then
|
||||
echo "backup: the run backs up the stopped store to $DATA/backups/stack/<activation>/ before any migration"
|
||||
else
|
||||
elif [ ! -e "$STORE" ]; then
|
||||
echo "backup: none; there is no store file"
|
||||
elif [ -n "$early" ]; then
|
||||
echo "backup: none; the run rolls back before the backup"
|
||||
else
|
||||
echo "backup: the run backs up the stopped store to $DATA/backups/stack/<activation>/ before any migration"
|
||||
fi
|
||||
plan_commits
|
||||
[ "$P_PENDING" = 0 ] || echo "pm: the pointer is pending (activate --pm-after-harness); the bus runs without its PM"
|
||||
|
||||
Reference in New Issue
Block a user