MCPcopy Create free account
hub / github.com/callstack/agent-device / resolveTarArchiveRootName

Function resolveTarArchiveRootName

src/daemon/artifact-archive.ts:24–75  ·  view source on GitHub ↗
(params: {
  archivePath: string;
  platform: 'ios' | 'android';
  expectedRootName?: string;
})

Source from the content-addressed store, hash-verified

22}
23
24export async function resolveTarArchiveRootName(params: {
25 archivePath: string;
26 platform: 'ios' | 'android';
27 expectedRootName?: string;
28}): Promise<string> {
29 const entriesResult = await runCmd('tar', ['-tf', params.archivePath], { allowFailure: true });
30 if (entriesResult.exitCode !== 0) {
31 throw new AppError('INVALID_ARGS', 'Artifact is not a valid tar archive', {
32 archivePath: params.archivePath,
33 stdout: entriesResult.stdout,
34 stderr: entriesResult.stderr,
35 exitCode: entriesResult.exitCode,
36 });
37 }
38
39 const entries = entriesResult.stdout
40 .split(/\r?\n/)
41 .map((entry) => entry.trim())
42 .filter(Boolean);
43 if (entries.length === 0) {
44 throw new AppError('INVALID_ARGS', 'Uploaded app bundle archive is empty');
45 }
46
47 const normalizedEntries = entries.map(normalizeArchiveEntry);
48 const rootName =
49 params.expectedRootName ?? resolveArchiveRootName(normalizedEntries, params.platform);
50 const hasExpectedRoot = normalizedEntries.some(
51 (entry) => entry === rootName || entry.startsWith(`${rootName}/`),
52 );
53 if (!hasExpectedRoot) {
54 throw new AppError(
55 'INVALID_ARGS',
56 `Uploaded archive must contain a top-level "${rootName}" bundle`,
57 );
58 }
59
60 for (const entry of normalizedEntries) {
61 validateArchiveEntryPath(entry, rootName);
62 }
63
64 const verboseResult = await runCmd('tar', ['-tvf', params.archivePath]);
65 for (const line of verboseResult.stdout.split(/\r?\n/).filter(Boolean)) {
66 if (line[0] === 'l' || line[0] === 'h') {
67 throw new AppError(
68 'INVALID_ARGS',
69 'Uploaded app bundle archive cannot contain symlinks or hard links',
70 );
71 }
72 }
73
74 return rootName;
75}
76
77export async function readZipEntries(archivePath: string): Promise<string[] | null> {
78 const result = await runCmd('unzip', ['-Z1', archivePath], { allowFailure: true });

Callers 2

detectIosArtifactKindFunction · 0.90

Calls 4

runCmdFunction · 0.90
resolveArchiveRootNameFunction · 0.85
validateArchiveEntryPathFunction · 0.85
startsWithMethod · 0.80

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…