Skip to content

Commit 3c62a2b

Browse files
authored
fix: carry remote-config run id for install-from-source (#375)
* fix: carry remote-config run id for install-from-source * test: reuse CLI capture helper in remote-config regression
1 parent d4ee32f commit 3c62a2b

2 files changed

Lines changed: 80 additions & 7 deletions

File tree

src/__tests__/cli-config.test.ts

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ async function runCliCapture(
2727
options?: {
2828
cwd?: string;
2929
env?: Record<string, string | undefined>;
30+
sendToDaemon?: (req: Omit<DaemonRequest, 'token'>) => Promise<DaemonResponse>;
3031
},
3132
): Promise<RunResult> {
3233
let stdout = '';
@@ -61,10 +62,12 @@ async function runCliCapture(
6162
return true;
6263
}) as typeof process.stderr.write;
6364

64-
const sendToDaemon = async (req: Omit<DaemonRequest, 'token'>): Promise<DaemonResponse> => {
65-
calls.push(req);
66-
return { ok: true, data: {} };
67-
};
65+
const sendToDaemon =
66+
options?.sendToDaemon ??
67+
(async (req: Omit<DaemonRequest, 'token'>): Promise<DaemonResponse> => {
68+
calls.push(req);
69+
return { ok: true, data: {} };
70+
});
6871

6972
try {
7073
await runCli(argv, { sendToDaemon });
@@ -265,6 +268,65 @@ test('remote config defaults override generic config and env for remote workflow
265268
fs.rmSync(root, { recursive: true, force: true });
266269
});
267270

271+
test('install-from-source forwards remote-config run id with explicit lease binding', async () => {
272+
const { root, home, project } = makeTempWorkspace();
273+
const remoteConfig = path.join(project, 'agent-device.remote.json');
274+
fs.writeFileSync(
275+
remoteConfig,
276+
JSON.stringify({
277+
daemonBaseUrl: 'http://remote-mac.example.test:9124/agent-device',
278+
tenant: 'micha-pierzcha-a',
279+
sessionIsolation: 'tenant',
280+
runId: 'demo-run-001',
281+
platform: 'android',
282+
}),
283+
'utf8',
284+
);
285+
286+
const calls: Array<Omit<DaemonRequest, 'token'>> = [];
287+
const result = await runCliCapture(
288+
[
289+
'install-from-source',
290+
'https://example.com/app.apk',
291+
'--remote-config',
292+
remoteConfig,
293+
'--lease-id',
294+
'lease-demo-001',
295+
'--json',
296+
],
297+
{
298+
cwd: project,
299+
env: { HOME: home },
300+
sendToDaemon: async (req) => {
301+
calls.push(req);
302+
return {
303+
ok: true,
304+
data: {
305+
launchTarget: 'com.example.demo',
306+
packageName: 'com.example.demo',
307+
},
308+
};
309+
},
310+
},
311+
);
312+
313+
assert.equal(result.code, null);
314+
assert.equal(result.stderr, '');
315+
const payload = JSON.parse(result.stdout);
316+
assert.equal(payload.success, true);
317+
assert.equal(payload.data.launchTarget, 'com.example.demo');
318+
assert.equal(calls.length, 1);
319+
assert.equal(calls[0]?.meta?.tenantId, 'micha-pierzcha-a');
320+
assert.equal(calls[0]?.meta?.runId, 'demo-run-001');
321+
assert.equal(calls[0]?.meta?.leaseId, 'lease-demo-001');
322+
assert.equal(calls[0]?.flags?.tenant, 'micha-pierzcha-a');
323+
assert.equal(calls[0]?.flags?.runId, 'demo-run-001');
324+
assert.equal(calls[0]?.flags?.leaseId, 'lease-demo-001');
325+
assert.deepEqual(calls[0]?.positionals, []);
326+
327+
fs.rmSync(root, { recursive: true, force: true });
328+
});
329+
268330
test('missing explicit remote config path returns parse error before daemon dispatch', async () => {
269331
const { root, home, project } = makeTempWorkspace();
270332

src/utils/remote-config.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
import type { CliFlags } from './command-schema.ts';
2-
import { REMOTE_OPEN_PROFILE_KEYS, type RemoteConfigProfile } from '../remote-config-schema.ts';
2+
import {
3+
REMOTE_CONFIG_FIELD_SPECS,
4+
REMOTE_OPEN_PROFILE_KEYS,
5+
type RemoteConfigProfile,
6+
} from '../remote-config-schema.ts';
37
import { resolveRemoteConfigProfile } from '../remote-config-core.ts';
48

5-
// This subset is intentional: only the flags used by remote-config-backed CLI defaults belong here.
9+
// Remote config can supply defaults for any supported CLI flag that exists in the profile schema.
10+
// Command validation later strips unsupported defaults for the active command.
11+
const REMOTE_CONFIG_DEFAULT_FLAG_KEYS = REMOTE_CONFIG_FIELD_SPECS.map(
12+
(spec) => spec.key,
13+
) as readonly (keyof RemoteConfigProfile)[];
14+
15+
// This subset is still intentional: open preserves extra remote-config defaults after command
16+
// parsing so it can prepare remote Metro without exposing every Metro flag as an open CLI option.
617
export const REMOTE_OPEN_FLAG_KEYS = [
718
'remoteConfig',
819
...REMOTE_OPEN_PROFILE_KEYS,
920
] as const satisfies readonly (keyof CliFlags)[];
1021

1122
function profileToCliFlags(profile: RemoteConfigProfile): Partial<CliFlags> {
1223
const flags: Partial<CliFlags> = {};
13-
for (const key of REMOTE_OPEN_PROFILE_KEYS) {
24+
for (const key of REMOTE_CONFIG_DEFAULT_FLAG_KEYS) {
1425
const value = profile[key];
1526
if (value !== undefined) {
1627
(flags as Record<string, unknown>)[key] = value;

0 commit comments

Comments
 (0)