@imagina-build/ias-cli 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/update/index.mjs +23 -11
- package/src/config.mjs +2 -2
- package/src/ota.mjs +4 -3
package/package.json
CHANGED
|
@@ -18,10 +18,14 @@ import { readSecret, readSecretFile, isReference, tokenKind } from '../../secret
|
|
|
18
18
|
|
|
19
19
|
export const PLATFORMS = ['ios', 'android'];
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The environment of the app's tools: none of this shell's EXPO_PUBLIC_* (they would go into the bundle) or secrets.
|
|
23
|
+
* With `tmp`, TMPDIR is a folder of this run: Metro keeps its cache in TMPDIR (metro-cache) and a cache from another
|
|
24
|
+
* run brings the paths of its folder into the bundle. The runner does the same for each build.
|
|
25
|
+
*/
|
|
26
|
+
function appEnv(tmp) {
|
|
23
27
|
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !/^(EXPO_PUBLIC_|OTA_|IAS_)/.test(key) && !['EXPO_TOKEN', 'NODE_ENV'].includes(key)));
|
|
24
|
-
return { ...env, CI: '1', EXPO_NO_TELEMETRY: '1' };
|
|
28
|
+
return { ...env, CI: '1', EXPO_NO_TELEMETRY: '1', ...(tmp === undefined ? {} : { TMPDIR: tmp }) };
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
/**
|
|
@@ -134,11 +138,13 @@ function copyOf(settings, ref, output) {
|
|
|
134
138
|
run('git', ['archive', '--format=tar', '-o', join(dir, 'source.tar'), commit], { cwd: settings.projectDir });
|
|
135
139
|
const app = join(dir, 'app');
|
|
136
140
|
run('mkdir', [app]);
|
|
141
|
+
const tmp = join(dir, 'tmp');
|
|
142
|
+
run('mkdir', [tmp]);
|
|
137
143
|
run('tar', ['-xf', join(dir, 'source.tar'), '-C', app]);
|
|
138
144
|
const install = INSTALLS.find(([lockfile]) => existsSync(join(app, lockfile)));
|
|
139
145
|
if (!install) throw new Error('the commit has no lockfile (package-lock.json, yarn.lock, pnpm-lock.yaml or bun.lock): the copy could not install the same dependencies');
|
|
140
146
|
output.progress(`${install[1]} ${install[2][0]} in a copy of ${commit.slice(0, 10)}…`);
|
|
141
|
-
gently(install[1], install[2], { cwd: app, env: appEnv(), stdio: ['ignore', 'pipe', 'pipe'] });
|
|
147
|
+
gently(install[1], install[2], { cwd: app, env: appEnv(tmp), stdio: ['ignore', 'pipe', 'pipe'] });
|
|
142
148
|
const mustContain = [];
|
|
143
149
|
if (settings.envFile) {
|
|
144
150
|
const envFile = join(settings.projectDir, settings.envFile);
|
|
@@ -152,11 +158,11 @@ function copyOf(settings, ref, output) {
|
|
|
152
158
|
} else if (settings.bundleEnv.length > 0) {
|
|
153
159
|
throw new Error(`${settings.names.config} lists bundleEnv but no envFile to take them from`);
|
|
154
160
|
}
|
|
155
|
-
return { app, commit, mustContain };
|
|
161
|
+
return { app, tmp, commit, mustContain };
|
|
156
162
|
}
|
|
157
163
|
|
|
158
|
-
function runtimeVersion(cwd, platform) {
|
|
159
|
-
const output = run('npx', ['expo-updates', 'runtimeversion:resolve', '--platform', platform], { cwd, env: appEnv(), label: 'expo-updates runtimeversion:resolve' });
|
|
164
|
+
function runtimeVersion(cwd, platform, tmp) {
|
|
165
|
+
const output = run('npx', ['expo-updates', 'runtimeversion:resolve', '--platform', platform], { cwd, env: appEnv(tmp), label: 'expo-updates runtimeversion:resolve' });
|
|
160
166
|
return JSON.parse(output.slice(output.indexOf('{'))).runtimeVersion;
|
|
161
167
|
}
|
|
162
168
|
|
|
@@ -174,13 +180,13 @@ export async function publish(settings, { ref = 'HEAD', message, platforms = PLA
|
|
|
174
180
|
const copy = copyOf(settings, ref, output);
|
|
175
181
|
const dist = join(dirname(copy.app), 'dist');
|
|
176
182
|
output.progress(`exporting ${copy.commit.slice(0, 10)} (${platforms.join(', ')})…`);
|
|
177
|
-
gently('npx', ['expo', 'export', ...platforms.flatMap((platform) => ['--platform', platform]), '--output-dir', dist], { cwd: copy.app, env: appEnv(), stdio: ['ignore', 'pipe', 'pipe'] });
|
|
183
|
+
gently('npx', ['expo', 'export', ...platforms.flatMap((platform) => ['--platform', platform]), '--output-dir', dist], { cwd: copy.app, env: appEnv(copy.tmp), stdio: ['ignore', 'pipe', 'pipe'] });
|
|
178
184
|
const { manifests, files } = buildManifests({
|
|
179
185
|
dist,
|
|
180
186
|
metadata: JSON.parse(readFileSync(join(dist, 'metadata.json'), 'utf8')),
|
|
181
187
|
platforms,
|
|
182
|
-
runtimes: Object.fromEntries(platforms.map((platform) => [platform, runtimeVersion(copy.app, platform)])),
|
|
183
|
-
expoClient: JSON.parse(run('npx', ['expo', 'config', '--type', 'public', '--json'], { cwd: copy.app, env: appEnv(), label: 'expo config' })),
|
|
188
|
+
runtimes: Object.fromEntries(platforms.map((platform) => [platform, runtimeVersion(copy.app, platform, copy.tmp)])),
|
|
189
|
+
expoClient: JSON.parse(run('npx', ['expo', 'config', '--type', 'public', '--json'], { cwd: copy.app, env: appEnv(copy.tmp), label: 'expo config' })),
|
|
184
190
|
assetPath: settings.assetPath,
|
|
185
191
|
createdAt: new Date().toISOString(),
|
|
186
192
|
mustContain: copy.mustContain,
|
|
@@ -387,6 +393,12 @@ export async function check(settings, { platforms = PLATFORMS }, output) {
|
|
|
387
393
|
return { checks };
|
|
388
394
|
}
|
|
389
395
|
|
|
396
|
+
/**
|
|
397
|
+
* The URL of a file the manifest points to, asked of the server this publication went to: a manifest may name any of
|
|
398
|
+
* the hosts the server answers for (the old domain, the new one), and they all serve the same path.
|
|
399
|
+
*/
|
|
400
|
+
const assetOf = (url, settings) => new URL(new URL(url, settings.manifestUrl).pathname, settings.base);
|
|
401
|
+
|
|
390
402
|
/**
|
|
391
403
|
* Asks for the manifest as a phone would and checks the signature, the id and every file it points to. `checked`
|
|
392
404
|
* holds the files already checked in this run (one per settings): the platforms share most of them. `channel` and
|
|
@@ -418,7 +430,7 @@ export async function checkServed(settings, platform, runtime, expectedId, outpu
|
|
|
418
430
|
checked.add(asset.hash);
|
|
419
431
|
// Each file once, and the bundle also without gzip.
|
|
420
432
|
for (const encoding of index === 0 ? [undefined, 'identity'] : [undefined]) {
|
|
421
|
-
const answer = await download(
|
|
433
|
+
const answer = await download(assetOf(asset.url, settings), encoding ? { 'accept-encoding': encoding } : {}, output);
|
|
422
434
|
if (!answer.ok) throw new Error(`${platform}: ${asset.url} answered ${answer.status}`);
|
|
423
435
|
if (sha256(Buffer.from(await answer.arrayBuffer())) !== asset.hash) throw new Error(`${platform}: ${asset.url} does not match its hash`);
|
|
424
436
|
}
|
package/src/config.mjs
CHANGED
|
@@ -163,7 +163,7 @@ function iasUpdateSettings({ dir, config }, env) {
|
|
|
163
163
|
manifestUrl: manifestUrl.href,
|
|
164
164
|
base,
|
|
165
165
|
project: manifestUrl.pathname.match(MANIFEST_PATH)[1],
|
|
166
|
-
assetPath: `${
|
|
166
|
+
assetPath: `${base}/assets/`,
|
|
167
167
|
keyid,
|
|
168
168
|
certificate: new X509Certificate(readFileSync(certificateFile)),
|
|
169
169
|
signingSource: appCertificate ? 'app.json' : 'updates.signing of ias.json',
|
|
@@ -203,7 +203,7 @@ function legacyUpdateSettings(projectDir, env) {
|
|
|
203
203
|
manifestUrl: manifestUrl.href,
|
|
204
204
|
base,
|
|
205
205
|
project: manifestUrl.pathname.match(MANIFEST_PATH)[1],
|
|
206
|
-
assetPath: `${
|
|
206
|
+
assetPath: `${base}/assets/`,
|
|
207
207
|
keyid: updates.codeSigningMetadata.keyid,
|
|
208
208
|
certificate,
|
|
209
209
|
privateKey,
|
package/src/ota.mjs
CHANGED
|
@@ -30,9 +30,10 @@ export function readEnvFile(path, names) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* One manifest per platform from an expo export folder: the same createdAt, the runtime of each platform,
|
|
34
|
-
* asset URLs
|
|
35
|
-
*
|
|
33
|
+
* One manifest per platform from an expo export folder: the same createdAt, the runtime of each platform, absolute
|
|
34
|
+
* asset URLs (the released expo-updates clients do not resolve relative ones), metadata {} (filters could make phones
|
|
35
|
+
* load an older update) and the public config as extra.expoClient. Returns the manifests and the files to upload, by
|
|
36
|
+
* hash. Throws if a bundle lacks a value it must carry.
|
|
36
37
|
*/
|
|
37
38
|
export function buildManifests({ dist, metadata, platforms, runtimes, expoClient, assetPath, createdAt, mustContain = [] }) {
|
|
38
39
|
const files = new Map();
|