flow run: a declined confirmation stops the flow; --apply --yes confirms write steps
confirm() prints 'declined: <reason>' and exits 3 whenever the answer is no or nobody can answer (not a terminal without --yes, serve without the owner's dialog), so no command reports success after declining. flow run stops at such a step with 'step N declined: <reason>' (status declined), never ok. flow run --apply --yes passes --yes to write steps only; --yes without --apply is refused. Pitfall 24.
This commit is contained in:
parent
5fbb9e63e3
commit
366362d62b
12 changed files with 116 additions and 19 deletions
|
|
@ -83,6 +83,12 @@ describe('steps', () => {
|
|||
expect(planStep({ id: 'sh', sh: 'npm run build' }, 'steps', 'apply', vars, C).skip).toBeUndefined();
|
||||
expect(planStep({ id: 'p', run: 'play build', if: 'play' }, 'steps', 'apply', vars, C).skip).toBe('if play: false');
|
||||
expect(planStep({ id: 'n', run: 'whats-new apply', if: 'exists store/whats-new' }, 'steps', 'apply', vars, C).skip).toBeUndefined();
|
||||
// flow run --apply --yes: the owner's approval reaches write steps only (never reads, waits or owner steps)
|
||||
expect(planStep(up, 'steps', 'apply', vars, C, {}, true)).toMatchObject({ argv: ['apple', 'upload', '--no-wait', '--yes'], yesAdded: true });
|
||||
expect(planStep(up, 'steps', 'dry', vars, C, {}, true).argv).toEqual(['apple', 'upload', '--no-wait', '--dry']);
|
||||
expect(planStep({ id: 'v', run: 'validate' }, 'steps', 'apply', vars, C, {}, true).argv).toEqual(['validate']);
|
||||
expect(planStep({ id: 'w', run: 'apple testflight wait --build 1' }, 'steps', 'apply', vars, C, {}, true).argv).not.toContain('--yes');
|
||||
expect(planStep({ id: 'r', run: 'apple release' }, 'steps', 'apply', vars, C, {}, true)).toMatchObject({ kind: 'owner', skip: expect.stringContaining('your click') });
|
||||
});
|
||||
test('conditions, variables, params', () => {
|
||||
expect(evalIf('apple && !play', C)).toBe(true);
|
||||
|
|
@ -191,6 +197,27 @@ steps:
|
|||
expect(sb(app, 'flow', 'run', 'nope').stderr.toString()).toContain('no flow "nope"');
|
||||
});
|
||||
|
||||
test('a declined confirmation (not a terminal, no --yes) stops the flow at that step, never "ok"', () => {
|
||||
const d = join(tmp, 'declined');
|
||||
mkdirSync(join(d, 'store', 'workflows'), { recursive: true });
|
||||
writeFileSync(join(d, 'storebound.config.ts'), `export default { app: { name: 'Example', privacyPolicyUrl: 'https://example.com/p', supportUrl: 'https://example.com', website: 'https://example.com', contactEmail: 'hi@example.com' },
|
||||
languages: [{ id: 'en', play: 'en-US' }], play: { packageName: 'com.example.game' } };\n`);
|
||||
writeFileSync(join(d, 'app.aab'), 'not really a bundle');
|
||||
writeFileSync(join(d, 'store', 'workflows', 'share.yml'), 'steps:\n - id: share\n run: play share --aab app.aab\n - id: after\n sh: touch after.txt\n');
|
||||
const direct = sb(d, 'play', 'share', '--aab', 'app.aab');
|
||||
expect(direct.exitCode).not.toBe(0);
|
||||
expect(direct.stderr.toString()).toContain('declined: not a terminal and no --yes');
|
||||
const r = sb(d, 'flow', 'run', 'share', '--apply');
|
||||
const out = r.stdout.toString() + r.stderr.toString();
|
||||
expect(r.exitCode).toBe(1);
|
||||
expect(out).toContain('step 1 declined: not a terminal and no --yes');
|
||||
expect(out).not.toContain('share: ok');
|
||||
expect(existsSync(join(d, 'after.txt'))).toBe(false);
|
||||
const state = JSON.parse(readFileSync(join(d, '.storebound/flows/share.json'), 'utf8'));
|
||||
expect(state.results.map((x: { id: string; status: string }) => `${x.id}:${x.status}`)).toEqual(['share:declined']);
|
||||
expect(sb(d, 'flow', 'run', 'share', '--yes').stderr.toString()).toContain('--yes only with --apply');
|
||||
});
|
||||
|
||||
test('MCP: flow_list and a dry flow_run that never applies', () => {
|
||||
const list = TOOLS.find(t => t.name === 'flow_list')!;
|
||||
const run = TOOLS.find(t => t.name === 'flow_run')!;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue