@ship-et/buildatlas-cli 0.14.0 → 0.14.2-beta.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/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +72 -1
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +42 -1
- package/dist/client.test.js.map +1 -1
- package/dist/commands/build.js +2 -2
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/env.d.ts +1 -0
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +65 -42
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/migrate/build-numbers.d.ts +2 -1
- package/dist/commands/migrate/build-numbers.d.ts.map +1 -1
- package/dist/commands/migrate/build-numbers.js +26 -6
- package/dist/commands/migrate/build-numbers.js.map +1 -1
- package/dist/commands/migrate/credentials.d.ts +17 -15
- package/dist/commands/migrate/credentials.d.ts.map +1 -1
- package/dist/commands/migrate/credentials.js +110 -45
- package/dist/commands/migrate/credentials.js.map +1 -1
- package/dist/commands/migrate/eas-client.d.ts +57 -5
- package/dist/commands/migrate/eas-client.d.ts.map +1 -1
- package/dist/commands/migrate/eas-client.js +112 -16
- package/dist/commands/migrate/eas-client.js.map +1 -1
- package/dist/commands/migrate/env.d.ts +39 -0
- package/dist/commands/migrate/env.d.ts.map +1 -0
- package/dist/commands/migrate/env.js +138 -0
- package/dist/commands/migrate/env.js.map +1 -0
- package/dist/commands/migrate/env.test.d.ts +2 -0
- package/dist/commands/migrate/env.test.d.ts.map +1 -0
- package/dist/commands/migrate/env.test.js +41 -0
- package/dist/commands/migrate/env.test.js.map +1 -0
- package/dist/commands/migrate/index.d.ts.map +1 -1
- package/dist/commands/migrate/index.js +146 -58
- package/dist/commands/migrate/index.js.map +1 -1
- package/dist/commands/migrate/report.d.ts +26 -0
- package/dist/commands/migrate/report.d.ts.map +1 -0
- package/dist/commands/migrate/report.js +84 -0
- package/dist/commands/migrate/report.js.map +1 -0
- package/dist/commands/submit.d.ts +6 -2
- package/dist/commands/submit.d.ts.map +1 -1
- package/dist/commands/submit.js +19 -9
- package/dist/commands/submit.js.map +1 -1
- package/dist/prompt.d.ts +16 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +143 -0
- package/dist/prompt.js.map +1 -1
- package/dist/spinner.d.ts.map +1 -1
- package/dist/spinner.js +3 -1
- package/dist/spinner.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import pc from 'picocolors';
|
|
2
1
|
import { apiRequest } from '../../client.js';
|
|
3
2
|
import { getLatestBuildVersion } from './eas-client.js';
|
|
4
3
|
import { withRetry } from './retry.js';
|
|
@@ -7,28 +6,49 @@ import { withRetry } from './retry.js';
|
|
|
7
6
|
* accepted from Expo, so the first BuildAtlas build doesn't collide with a
|
|
8
7
|
* version number Apple/Google have already seen.
|
|
9
8
|
*/
|
|
10
|
-
export async function continueBuildNumbers(config, slug, auth, appId, local) {
|
|
9
|
+
export async function continueBuildNumbers(config, slug, auth, appId, local, report) {
|
|
11
10
|
const platforms = [
|
|
12
11
|
{ platform: 'ios', applicationIdentifier: local?.bundleIdentifier },
|
|
13
12
|
{ platform: 'android', applicationIdentifier: local?.androidPackage },
|
|
14
13
|
];
|
|
14
|
+
// What BuildAtlas will hand out next. Read first because the API only ever
|
|
15
|
+
// raises a counter: on a rerun after BuildAtlas has built past Expo, writing
|
|
16
|
+
// Expo's number would be refused, and that is not a failure — it is done.
|
|
17
|
+
const current = await withRetry('reading BuildAtlas build numbers', () => apiRequest(config, `/projects/${slug}/build-numbers`, {
|
|
18
|
+
token: config.token,
|
|
19
|
+
}));
|
|
15
20
|
for (const { platform, applicationIdentifier } of platforms) {
|
|
16
21
|
if (!applicationIdentifier)
|
|
17
22
|
continue;
|
|
18
|
-
const
|
|
19
|
-
|
|
23
|
+
const stepLabel = `${platform} build number`;
|
|
24
|
+
// Wrapped so "Expo has no build" and "the check failed" stay distinguishable.
|
|
25
|
+
const checked = await withRetry(`checking Expo's ${platform} build number`, async () => ({
|
|
26
|
+
latest: await getLatestBuildVersion(auth, appId, platform, applicationIdentifier),
|
|
27
|
+
}));
|
|
28
|
+
if (!checked) {
|
|
29
|
+
report.failed(stepLabel, "could not read Expo's build number");
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const { latest } = checked;
|
|
33
|
+
if (latest === undefined) {
|
|
34
|
+
report.skipped(stepLabel, 'no build on Expo yet');
|
|
20
35
|
continue;
|
|
36
|
+
}
|
|
21
37
|
const nextBuildNumber = latest + 1;
|
|
38
|
+
if (current && current[platform] >= nextBuildNumber) {
|
|
39
|
+
report.skipped(stepLabel, `already continues from ${current[platform]} (Expo was at ${latest})`);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
22
42
|
const synced = await withRetry(`syncing ${platform} build number`, () => apiRequest(config, `/projects/${slug}/build-number`, {
|
|
23
43
|
method: 'PATCH',
|
|
24
44
|
token: config.token,
|
|
25
45
|
body: JSON.stringify({ platform, nextBuildNumber }),
|
|
26
46
|
}));
|
|
27
47
|
if (synced === undefined) {
|
|
28
|
-
|
|
48
|
+
report.failed(stepLabel, `not updated — set it with \`buildatlas projects set-build-number ${nextBuildNumber}\``);
|
|
29
49
|
continue;
|
|
30
50
|
}
|
|
31
|
-
|
|
51
|
+
report.done(stepLabel, `continues from ${nextBuildNumber} (Expo was at ${latest})`);
|
|
32
52
|
}
|
|
33
53
|
}
|
|
34
54
|
//# sourceMappingURL=build-numbers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-numbers.js","sourceRoot":"","sources":["../../../src/commands/migrate/build-numbers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"build-numbers.js","sourceRoot":"","sources":["../../../src/commands/migrate/build-numbers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAiB,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAoB,EACpB,IAAY,EACZ,IAAc,EACd,KAAa,EACb,KAAyE,EACzE,MAAqB;IAErB,MAAM,SAAS,GAAsE;QACnF,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,gBAAgB,EAAE;QACnE,EAAE,QAAQ,EAAE,SAAS,EAAE,qBAAqB,EAAE,KAAK,EAAE,cAAc,EAAE;KACtE,CAAC;IAEF,2EAA2E;IAC3E,6EAA6E;IAC7E,0EAA0E;IAC1E,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,kCAAkC,EAAE,GAAG,EAAE,CACvE,UAAU,CAAmC,MAAM,EAAE,aAAa,IAAI,gBAAgB,EAAE;QACtF,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC,CACH,CAAC;IAEF,KAAK,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,IAAI,SAAS,EAAE,CAAC;QAC5D,IAAI,CAAC,qBAAqB;YAAE,SAAS;QACrC,MAAM,SAAS,GAAG,GAAG,QAAQ,eAAe,CAAC;QAE7C,8EAA8E;QAC9E,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,mBAAmB,QAAQ,eAAe,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YACvF,MAAM,EAAE,MAAM,qBAAqB,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,qBAAqB,CAAC;SAClF,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,oCAAoC,CAAC,CAAC;YAC/D,SAAS;QACX,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAC3B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;YAClD,SAAS;QACX,CAAC;QAED,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,CAAC;QACnC,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,eAAe,EAAE,CAAC;YACpD,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,0BAA0B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,MAAM,GAAG,CAAC,CAAC;YACjG,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,WAAW,QAAQ,eAAe,EAAE,GAAG,EAAE,CACtE,UAAU,CAAC,MAAM,EAAE,aAAa,IAAI,eAAe,EAAE;YACnD,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;SACpD,CAAC,CACH,CAAC;QACF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,oEAAoE,eAAe,IAAI,CAAC,CAAC;YAClH,SAAS;QACX,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,eAAe,iBAAiB,MAAM,GAAG,CAAC,CAAC;IACtF,CAAC;AACH,CAAC"}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import type { AuthedConfig } from './auth.ts';
|
|
2
2
|
import { type ExpoAuth } from './eas-client.ts';
|
|
3
|
+
import type { ProjectReport } from './report.ts';
|
|
3
4
|
/**
|
|
4
|
-
* Pulls
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Pulls everything EAS holds for this app into BuildAtlas's vault, so a
|
|
6
|
+
* migrated project can build and submit immediately instead of needing someone
|
|
7
|
+
* to run `eas credentials` by hand and re-upload the files themselves:
|
|
8
|
+
*
|
|
9
|
+
* - iOS: distribution cert, and a provisioning profile for *every* target —
|
|
10
|
+
* the app plus extensions such as OneSignal's notification service extension
|
|
11
|
+
* - iOS: APNs push key (what OneSignal and every other sender deliver through)
|
|
12
|
+
* - iOS: App Store Connect API key used for submissions
|
|
13
|
+
* - Android: upload keystore, and the Play service account used for submissions
|
|
9
14
|
*
|
|
10
15
|
* Runs the same way whether the BuildAtlas project was just created or already
|
|
11
16
|
* existed — `migrate` on a project that is already linked is how someone picks
|
|
12
|
-
* up credentials generated on EAS after the fact
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* not pile up duplicate rows for the same kind.
|
|
17
|
+
* up credentials generated on EAS after the fact. What is already in the vault
|
|
18
|
+
* (this project's own row, or a shared one) is left alone rather than
|
|
19
|
+
* re-uploaded, so running this repeatedly does not pile up duplicate rows.
|
|
16
20
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* OneSignal's own dashboard separately; that step has no EAS equivalent to
|
|
21
|
-
* migrate from.
|
|
21
|
+
* Not migrated, and reported as such: the FCM V1 key (only Expo's own push
|
|
22
|
+
* service reads it) and anything OneSignal keeps on its own dashboard, which
|
|
23
|
+
* EAS has no copy of.
|
|
22
24
|
*/
|
|
23
|
-
export declare function migrateCredentials(config: AuthedConfig, slug: string, auth: ExpoAuth, appId: string,
|
|
25
|
+
export declare function migrateCredentials(config: AuthedConfig, slug: string, auth: ExpoAuth, appId: string, report: ProjectReport): Promise<void>;
|
|
24
26
|
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate/credentials.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate/credentials.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,QAAQ,EAA8D,MAAM,iBAAiB,CAAC;AAC5G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAUjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC,CA4Hf"}
|
|
@@ -1,63 +1,102 @@
|
|
|
1
|
-
import pc from 'picocolors';
|
|
2
1
|
import { apiRequest } from '../../client.js';
|
|
3
|
-
import { getEasCredentials } from './eas-client.js';
|
|
2
|
+
import { getEasCredentials, getEasServiceKeys, mainBundleIdentifier } from './eas-client.js';
|
|
4
3
|
import { withRetry } from './retry.js';
|
|
5
4
|
/**
|
|
6
|
-
* Pulls
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* Pulls everything EAS holds for this app into BuildAtlas's vault, so a
|
|
6
|
+
* migrated project can build and submit immediately instead of needing someone
|
|
7
|
+
* to run `eas credentials` by hand and re-upload the files themselves:
|
|
8
|
+
*
|
|
9
|
+
* - iOS: distribution cert, and a provisioning profile for *every* target —
|
|
10
|
+
* the app plus extensions such as OneSignal's notification service extension
|
|
11
|
+
* - iOS: APNs push key (what OneSignal and every other sender deliver through)
|
|
12
|
+
* - iOS: App Store Connect API key used for submissions
|
|
13
|
+
* - Android: upload keystore, and the Play service account used for submissions
|
|
11
14
|
*
|
|
12
15
|
* Runs the same way whether the BuildAtlas project was just created or already
|
|
13
16
|
* existed — `migrate` on a project that is already linked is how someone picks
|
|
14
|
-
* up credentials generated on EAS after the fact
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* not pile up duplicate rows for the same kind.
|
|
17
|
+
* up credentials generated on EAS after the fact. What is already in the vault
|
|
18
|
+
* (this project's own row, or a shared one) is left alone rather than
|
|
19
|
+
* re-uploaded, so running this repeatedly does not pile up duplicate rows.
|
|
18
20
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* OneSignal's own dashboard separately; that step has no EAS equivalent to
|
|
23
|
-
* migrate from.
|
|
21
|
+
* Not migrated, and reported as such: the FCM V1 key (only Expo's own push
|
|
22
|
+
* service reads it) and anything OneSignal keeps on its own dashboard, which
|
|
23
|
+
* EAS has no copy of.
|
|
24
24
|
*/
|
|
25
|
-
export async function migrateCredentials(config, slug, auth, appId,
|
|
25
|
+
export async function migrateCredentials(config, slug, auth, appId, report) {
|
|
26
26
|
const creds = await withRetry('fetching signing credentials from EAS', () => getEasCredentials(auth, appId));
|
|
27
|
-
if (!creds
|
|
28
|
-
|
|
29
|
-
'shipped before, generate them with `eas credentials`, then run ' +
|
|
30
|
-
`\`buildatlas credentials add --project ${slug}\`.`));
|
|
27
|
+
if (!creds) {
|
|
28
|
+
report.failed('signing credentials', 'could not read them from EAS — rerun migrate to try again');
|
|
31
29
|
return;
|
|
32
30
|
}
|
|
33
|
-
// Already-vaulted
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// back to attempting every upload, same as before this existed.
|
|
31
|
+
// Already-vaulted rows — this project's own plus whatever it inherits shared —
|
|
32
|
+
// so a rerun tops up what is missing instead of duplicating what a build would
|
|
33
|
+
// already find. A failed check falls back to attempting every upload.
|
|
37
34
|
const existingList = await withRetry('checking what is already stored', () => apiRequest(config, `/projects/${slug}/credentials`, {
|
|
38
35
|
token: config.token,
|
|
39
36
|
}));
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
const result = await withRetry(`uploading ${kind}`, () => apiRequest(config, `/projects/${slug}/credentials`, {
|
|
37
|
+
const existing = (existingList?.credentials ?? []).filter((row) => !row.revokedAt);
|
|
38
|
+
const haveKind = (kind) => existing.some((row) => row.kind === kind);
|
|
39
|
+
const haveProfileFor = (bundleId) => existing.some((row) => row.kind === 'ios_provisioning_profile' && row.meta?.appId === bundleId);
|
|
40
|
+
const label = `migrated from ${report.name}`;
|
|
41
|
+
const upload = async (kind, payload, stepLabel = kind) => {
|
|
42
|
+
const result = await withRetry(`uploading ${stepLabel}`, () => apiRequest(config, `/projects/${slug}/credentials`, {
|
|
48
43
|
method: 'POST',
|
|
49
44
|
token: config.token,
|
|
50
45
|
body: JSON.stringify({ kind, label, payload }),
|
|
51
46
|
}));
|
|
52
|
-
|
|
47
|
+
if (result === undefined)
|
|
48
|
+
report.failed(stepLabel, 'upload failed');
|
|
49
|
+
else
|
|
50
|
+
report.done(stepLabel);
|
|
53
51
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
// iOS --------------------------------------------------------------------
|
|
53
|
+
if (creds.ios.length === 0) {
|
|
54
|
+
report.skipped('iOS signing', 'none on EAS');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const main = mainBundleIdentifier(creds.ios.map((t) => t.bundleIdentifier));
|
|
58
|
+
const mainTarget = creds.ios.find((t) => t.bundleIdentifier === main) ?? creds.ios[0];
|
|
59
|
+
if (haveKind('ios_dist_cert')) {
|
|
60
|
+
report.skipped('ios_dist_cert', 'already in the vault');
|
|
58
61
|
}
|
|
62
|
+
else {
|
|
63
|
+
await upload('ios_dist_cert', { p12Base64: mainTarget.p12Base64, password: mainTarget.password ?? '' });
|
|
64
|
+
}
|
|
65
|
+
// Extensions first, the app itself last: the vault hands out the newest
|
|
66
|
+
// profile when a build can only take one, and that should be the app's.
|
|
67
|
+
const ordered = [...creds.ios.filter((t) => t !== mainTarget), mainTarget];
|
|
68
|
+
for (const target of ordered) {
|
|
69
|
+
const stepLabel = `ios_provisioning_profile (${target.bundleIdentifier})`;
|
|
70
|
+
if (!target.profileBase64) {
|
|
71
|
+
report.warn(stepLabel, 'EAS has no App Store profile for this target — add one with `buildatlas credentials add`');
|
|
72
|
+
}
|
|
73
|
+
else if (haveProfileFor(target.bundleIdentifier)) {
|
|
74
|
+
report.skipped(stepLabel, 'already in the vault');
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
await upload('ios_provisioning_profile', { profileBase64: target.profileBase64 }, stepLabel);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (creds.iosPushKey) {
|
|
82
|
+
if (haveKind('ios_push_key')) {
|
|
83
|
+
report.skipped('ios_push_key', 'already in the vault');
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
await upload('ios_push_key', { keyId: creds.iosPushKey.keyId, p8Base64: creds.iosPushKey.p8Base64 });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if (creds.ios.length) {
|
|
90
|
+
report.warn('ios_push_key', 'none on EAS — if the app sends push (OneSignal included), add the .p8 with `buildatlas credentials add`');
|
|
91
|
+
}
|
|
92
|
+
// Android ----------------------------------------------------------------
|
|
93
|
+
if (!creds.android) {
|
|
94
|
+
report.skipped('android_keystore', 'none on EAS');
|
|
59
95
|
}
|
|
60
|
-
if (
|
|
96
|
+
else if (haveKind('android_keystore')) {
|
|
97
|
+
report.skipped('android_keystore', 'already in the vault');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
61
100
|
await upload('android_keystore', {
|
|
62
101
|
keystoreBase64: creds.android.keystoreBase64,
|
|
63
102
|
keystorePassword: creds.android.keystorePassword,
|
|
@@ -66,13 +105,39 @@ export async function migrateCredentials(config, slug, auth, appId, appFullName)
|
|
|
66
105
|
keyPassword: creds.android.keyPassword ?? creds.android.keystorePassword,
|
|
67
106
|
});
|
|
68
107
|
}
|
|
69
|
-
|
|
70
|
-
|
|
108
|
+
// Submission and push keys ------------------------------------------------
|
|
109
|
+
const keys = await withRetry('fetching submission and push keys from EAS', () => getEasServiceKeys(auth, appId));
|
|
110
|
+
if (!keys) {
|
|
111
|
+
report.warn('submission keys', 'could not read them from EAS (needs an Admin role on the Expo account)');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!keys.ascApiKey) {
|
|
115
|
+
report.skipped('asc_api_key', 'none on EAS');
|
|
116
|
+
}
|
|
117
|
+
else if (!keys.ascApiKey.issuerId) {
|
|
118
|
+
report.warn('asc_api_key', 'EAS has an individual key, which has no issuer id — add a team key with `buildatlas credentials add`');
|
|
119
|
+
}
|
|
120
|
+
else if (haveKind('asc_api_key')) {
|
|
121
|
+
report.skipped('asc_api_key', 'already in the vault');
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
await upload('asc_api_key', {
|
|
125
|
+
keyId: keys.ascApiKey.keyId,
|
|
126
|
+
issuerId: keys.ascApiKey.issuerId,
|
|
127
|
+
p8Base64: keys.ascApiKey.p8Base64,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (!keys.playServiceAccountJson) {
|
|
131
|
+
report.skipped('play_service_account', 'none on EAS');
|
|
132
|
+
}
|
|
133
|
+
else if (haveKind('play_service_account')) {
|
|
134
|
+
report.skipped('play_service_account', 'already in the vault');
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
await upload('play_service_account', { serviceAccountJson: keys.playServiceAccountJson });
|
|
71
138
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
'through OneSignal), generate one with `eas credentials` or add it directly: ' +
|
|
75
|
-
`\`buildatlas credentials add\` → iOS push notification key.`));
|
|
139
|
+
if (keys.fcmV1) {
|
|
140
|
+
report.skipped('FCM V1 key', `${keys.fcmV1.clientEmail} stays on EAS — only Expo's push service reads it; builds and OneSignal do not`);
|
|
76
141
|
}
|
|
77
142
|
}
|
|
78
143
|
//# sourceMappingURL=credentials.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../../src/commands/migrate/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../../src/commands/migrate/credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -45,7 +45,9 @@ export declare function getAppIdentifiers(auth: ExpoAuth, appId: string): Promis
|
|
|
45
45
|
bundleIdentifier?: string;
|
|
46
46
|
androidPackage?: string;
|
|
47
47
|
}>;
|
|
48
|
-
|
|
48
|
+
/** Signing material for one iOS target — the app itself, or one of its extensions. */
|
|
49
|
+
export interface EasIosTarget {
|
|
50
|
+
bundleIdentifier: string;
|
|
49
51
|
p12Base64: string;
|
|
50
52
|
password?: string;
|
|
51
53
|
profileBase64?: string;
|
|
@@ -61,14 +63,24 @@ export interface EasAndroidCredentials {
|
|
|
61
63
|
keyPassword?: string;
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
64
|
-
* The
|
|
65
|
-
*
|
|
66
|
+
* The main app's bundle id out of a set that may include extensions — the one
|
|
67
|
+
* every other id extends (`com.app` for `com.app.OneSignalNotificationServiceExtension`).
|
|
68
|
+
* Falls back to the first when nothing nests, which is the single-target case.
|
|
69
|
+
*/
|
|
70
|
+
export declare function mainBundleIdentifier(ids: string[]): string | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* The actual signing material EAS holds for this app — a distribution cert and
|
|
73
|
+
* provisioning profile per iOS target, a keystore for Android, and an APNs
|
|
66
74
|
* push key if one was ever generated for push notifications — in the same
|
|
67
75
|
* base64 shape `buildatlas credentials add` already expects (`ios_dist_cert`,
|
|
68
76
|
* `ios_provisioning_profile`, `android_keystore`, `ios_push_key`), so it can be
|
|
69
77
|
* forwarded as-is.
|
|
70
78
|
*
|
|
71
|
-
*
|
|
79
|
+
* Every iOS target, not just the first: an app with a notification service
|
|
80
|
+
* extension (OneSignal's, for one) has its own bundle id and its own profile on
|
|
81
|
+
* EAS, and an archive with a target missing its profile fails to export.
|
|
82
|
+
*
|
|
83
|
+
* Picks the App Store build credentials per target (ad-hoc/enterprise/development
|
|
72
84
|
* profiles are for internal testing, not what a migrated app should ship with)
|
|
73
85
|
* and whichever Android keystore is marked default. Omits a platform, or the
|
|
74
86
|
* push key, entirely rather than guessing when EAS has never generated one.
|
|
@@ -79,10 +91,50 @@ export interface EasAndroidCredentials {
|
|
|
79
91
|
* worker to warn about when a project looks push-capable but has none stored.
|
|
80
92
|
*/
|
|
81
93
|
export declare function getEasCredentials(auth: ExpoAuth, appId: string): Promise<{
|
|
82
|
-
ios
|
|
94
|
+
ios: EasIosTarget[];
|
|
83
95
|
android?: EasAndroidCredentials;
|
|
84
96
|
iosPushKey?: EasIosPushKey;
|
|
85
97
|
}>;
|
|
98
|
+
export interface EasServiceKeys {
|
|
99
|
+
/** App Store Connect API key EAS Submit uses. Null issuer = an individual key, which BuildAtlas cannot use. */
|
|
100
|
+
ascApiKey?: {
|
|
101
|
+
keyId: string;
|
|
102
|
+
issuerId: string | null;
|
|
103
|
+
p8Base64: string;
|
|
104
|
+
};
|
|
105
|
+
/** Google service account EAS Submit uses for Play uploads. */
|
|
106
|
+
playServiceAccountJson?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Firebase Cloud Messaging V1 service account, which only Expo's own push
|
|
109
|
+
* service reads. Reported, not stored: nothing in a build uses it, and
|
|
110
|
+
* OneSignal or a self-run sender keep their own copy.
|
|
111
|
+
*/
|
|
112
|
+
fcmV1?: {
|
|
113
|
+
projectId: string;
|
|
114
|
+
clientEmail: string;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The keys EAS keeps for submitting and push, separate from the signing query
|
|
119
|
+
* above: reading raw key material needs a higher role on the Expo account, and
|
|
120
|
+
* a permission error here should not cost the signing credentials too.
|
|
121
|
+
*/
|
|
122
|
+
export declare function getEasServiceKeys(auth: ExpoAuth, appId: string): Promise<EasServiceKeys>;
|
|
123
|
+
export interface EasEnvVar {
|
|
124
|
+
name: string;
|
|
125
|
+
/** Null for `SECRET` visibility — EAS never hands those back, to anyone. */
|
|
126
|
+
value: string | null;
|
|
127
|
+
environments: string[];
|
|
128
|
+
visibility: 'PUBLIC' | 'SENSITIVE' | 'SECRET';
|
|
129
|
+
type: 'STRING' | 'FILE_BASE64';
|
|
130
|
+
fileName: string | null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The app's EAS environment variables across every environment, with
|
|
134
|
+
* `SENSITIVE` values included (what `eas env:pull` reads). One variable may
|
|
135
|
+
* appear several times with different values — one record per environment.
|
|
136
|
+
*/
|
|
137
|
+
export declare function getEasEnvVars(auth: ExpoAuth, appId: string): Promise<EasEnvVar[]>;
|
|
86
138
|
/** Every app in every account this token or session can see. */
|
|
87
139
|
export declare function listExpoApps(auth: ExpoAuth): Promise<ExpoApp[]>;
|
|
88
140
|
//# sourceMappingURL=eas-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eas-client.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate/eas-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"eas-client.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate/eas-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,7 +25,10 @@ async function expoGraphQL(auth, query, variables) {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
catch (err) {
|
|
28
|
-
|
|
28
|
+
// undici's message is always "fetch failed"; the cause says which kind.
|
|
29
|
+
const cause = err.cause;
|
|
30
|
+
const why = cause?.code ?? cause?.message ?? err.message;
|
|
31
|
+
throw new Error(`Could not reach api.expo.dev (${why}). Check the connection and run it again.`);
|
|
29
32
|
}
|
|
30
33
|
if (res.status === 401 || res.status === 403) {
|
|
31
34
|
throw new Error(auth.kind === 'token' ? 'Expo rejected this token.' : 'Expo rejected this session.');
|
|
@@ -83,19 +86,40 @@ export async function getAppIdentifiers(auth, appId) {
|
|
|
83
86
|
}
|
|
84
87
|
}`, { appId });
|
|
85
88
|
return {
|
|
86
|
-
|
|
89
|
+
// Several entries when the app has extensions; the first is not reliably the app itself.
|
|
90
|
+
bundleIdentifier: mainBundleIdentifier(app.byId.iosAppCredentials.map((c) => c.appleAppIdentifier.bundleIdentifier)),
|
|
87
91
|
androidPackage: app.byId.androidAppCredentials.find((c) => c.applicationIdentifier)?.applicationIdentifier ?? undefined,
|
|
88
92
|
};
|
|
89
93
|
}
|
|
90
94
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
95
|
+
* EAS hands `.p8` keys back as the file's PEM text, while the vault stores
|
|
96
|
+
* every key file base64-encoded, as an uploaded file would be. Passed through
|
|
97
|
+
* when it is already base64, in case EAS ever changes which one it returns.
|
|
98
|
+
*/
|
|
99
|
+
function p8ToBase64(keyP8) {
|
|
100
|
+
return keyP8.includes('-----BEGIN') ? Buffer.from(keyP8, 'utf8').toString('base64') : keyP8.replace(/\s+/g, '');
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The main app's bundle id out of a set that may include extensions — the one
|
|
104
|
+
* every other id extends (`com.app` for `com.app.OneSignalNotificationServiceExtension`).
|
|
105
|
+
* Falls back to the first when nothing nests, which is the single-target case.
|
|
106
|
+
*/
|
|
107
|
+
export function mainBundleIdentifier(ids) {
|
|
108
|
+
return ids.find((id) => ids.every((other) => other === id || other.startsWith(`${id}.`))) ?? ids[0];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The actual signing material EAS holds for this app — a distribution cert and
|
|
112
|
+
* provisioning profile per iOS target, a keystore for Android, and an APNs
|
|
93
113
|
* push key if one was ever generated for push notifications — in the same
|
|
94
114
|
* base64 shape `buildatlas credentials add` already expects (`ios_dist_cert`,
|
|
95
115
|
* `ios_provisioning_profile`, `android_keystore`, `ios_push_key`), so it can be
|
|
96
116
|
* forwarded as-is.
|
|
97
117
|
*
|
|
98
|
-
*
|
|
118
|
+
* Every iOS target, not just the first: an app with a notification service
|
|
119
|
+
* extension (OneSignal's, for one) has its own bundle id and its own profile on
|
|
120
|
+
* EAS, and an archive with a target missing its profile fails to export.
|
|
121
|
+
*
|
|
122
|
+
* Picks the App Store build credentials per target (ad-hoc/enterprise/development
|
|
99
123
|
* profiles are for internal testing, not what a migrated app should ship with)
|
|
100
124
|
* and whichever Android keystore is marked default. Omits a platform, or the
|
|
101
125
|
* push key, entirely rather than guessing when EAS has never generated one.
|
|
@@ -111,6 +135,7 @@ export async function getEasCredentials(auth, appId) {
|
|
|
111
135
|
byId(appId: $appId) {
|
|
112
136
|
id
|
|
113
137
|
iosAppCredentials {
|
|
138
|
+
appleAppIdentifier { bundleIdentifier }
|
|
114
139
|
pushKey { keyIdentifier keyP8 }
|
|
115
140
|
iosAppBuildCredentialsList {
|
|
116
141
|
iosDistributionType
|
|
@@ -127,21 +152,26 @@ export async function getEasCredentials(auth, appId) {
|
|
|
127
152
|
}
|
|
128
153
|
}
|
|
129
154
|
}`, { appId });
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
155
|
+
const ios = [];
|
|
156
|
+
for (const target of app.byId.iosAppCredentials) {
|
|
157
|
+
const list = target.iosAppBuildCredentialsList;
|
|
158
|
+
const entry = list.find((c) => c.iosDistributionType === 'APP_STORE' && c.distributionCertificate?.certificateP12) ??
|
|
159
|
+
list.find((c) => c.distributionCertificate?.certificateP12);
|
|
160
|
+
if (!entry?.distributionCertificate?.certificateP12)
|
|
161
|
+
continue;
|
|
162
|
+
ios.push({
|
|
163
|
+
bundleIdentifier: target.appleAppIdentifier.bundleIdentifier,
|
|
164
|
+
p12Base64: entry.distributionCertificate.certificateP12,
|
|
165
|
+
password: entry.distributionCertificate.certificatePassword ?? undefined,
|
|
166
|
+
profileBase64: entry.provisioningProfile?.provisioningProfile ?? undefined,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
133
169
|
const androidBuildCredentials = app.byId.androidAppCredentials.flatMap((c) => c.androidAppBuildCredentialsList);
|
|
134
170
|
const androidEntry = androidBuildCredentials.find((c) => c.isDefault && c.androidKeystore) ??
|
|
135
171
|
androidBuildCredentials.find((c) => c.androidKeystore);
|
|
136
172
|
const pushKeyEntry = app.byId.iosAppCredentials.find((c) => c.pushKey)?.pushKey;
|
|
137
173
|
return {
|
|
138
|
-
ios
|
|
139
|
-
? {
|
|
140
|
-
p12Base64: iosEntry.distributionCertificate.certificateP12,
|
|
141
|
-
password: iosEntry.distributionCertificate.certificatePassword ?? undefined,
|
|
142
|
-
profileBase64: iosEntry.provisioningProfile?.provisioningProfile ?? undefined,
|
|
143
|
-
}
|
|
144
|
-
: undefined,
|
|
174
|
+
ios,
|
|
145
175
|
android: androidEntry?.androidKeystore
|
|
146
176
|
? {
|
|
147
177
|
keystoreBase64: androidEntry.androidKeystore.keystore,
|
|
@@ -151,10 +181,76 @@ export async function getEasCredentials(auth, appId) {
|
|
|
151
181
|
}
|
|
152
182
|
: undefined,
|
|
153
183
|
iosPushKey: pushKeyEntry
|
|
154
|
-
? { keyId: pushKeyEntry.keyIdentifier, p8Base64: pushKeyEntry.keyP8 }
|
|
184
|
+
? { keyId: pushKeyEntry.keyIdentifier, p8Base64: p8ToBase64(pushKeyEntry.keyP8) }
|
|
155
185
|
: undefined,
|
|
156
186
|
};
|
|
157
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* The keys EAS keeps for submitting and push, separate from the signing query
|
|
190
|
+
* above: reading raw key material needs a higher role on the Expo account, and
|
|
191
|
+
* a permission error here should not cost the signing credentials too.
|
|
192
|
+
*/
|
|
193
|
+
export async function getEasServiceKeys(auth, appId) {
|
|
194
|
+
const { app } = await expoGraphQL(auth, `query AppServiceKeys($appId: String!) {
|
|
195
|
+
app {
|
|
196
|
+
byId(appId: $appId) {
|
|
197
|
+
id
|
|
198
|
+
iosAppCredentials {
|
|
199
|
+
appStoreConnectApiKeyForSubmissions { keyIdentifier issuerIdentifier keyP8 }
|
|
200
|
+
}
|
|
201
|
+
androidAppCredentials {
|
|
202
|
+
googleServiceAccountKeyForSubmissions { keyJson }
|
|
203
|
+
googleServiceAccountKeyForFcmV1 { projectIdentifier clientEmail }
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}`, { appId });
|
|
208
|
+
const asc = app.byId.iosAppCredentials.find((c) => c.appStoreConnectApiKeyForSubmissions)
|
|
209
|
+
?.appStoreConnectApiKeyForSubmissions;
|
|
210
|
+
const play = app.byId.androidAppCredentials.find((c) => c.googleServiceAccountKeyForSubmissions)
|
|
211
|
+
?.googleServiceAccountKeyForSubmissions;
|
|
212
|
+
const fcm = app.byId.androidAppCredentials.find((c) => c.googleServiceAccountKeyForFcmV1)
|
|
213
|
+
?.googleServiceAccountKeyForFcmV1;
|
|
214
|
+
return {
|
|
215
|
+
ascApiKey: asc
|
|
216
|
+
? {
|
|
217
|
+
keyId: asc.keyIdentifier,
|
|
218
|
+
issuerId: asc.issuerIdentifier,
|
|
219
|
+
p8Base64: p8ToBase64(asc.keyP8),
|
|
220
|
+
}
|
|
221
|
+
: undefined,
|
|
222
|
+
playServiceAccountJson: play?.keyJson,
|
|
223
|
+
fcmV1: fcm ? { projectId: fcm.projectIdentifier, clientEmail: fcm.clientEmail } : undefined,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* The app's EAS environment variables across every environment, with
|
|
228
|
+
* `SENSITIVE` values included (what `eas env:pull` reads). One variable may
|
|
229
|
+
* appear several times with different values — one record per environment.
|
|
230
|
+
*/
|
|
231
|
+
export async function getEasEnvVars(auth, appId) {
|
|
232
|
+
const { app } = await expoGraphQL(auth, `query AppEnvironmentVariables($appId: String!) {
|
|
233
|
+
app {
|
|
234
|
+
byId(appId: $appId) {
|
|
235
|
+
id
|
|
236
|
+
environmentVariablesIncludingSensitive {
|
|
237
|
+
id
|
|
238
|
+
name
|
|
239
|
+
value
|
|
240
|
+
environments
|
|
241
|
+
visibility
|
|
242
|
+
type
|
|
243
|
+
fileName
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}`, { appId });
|
|
248
|
+
return app.byId.environmentVariablesIncludingSensitive.map((v) => ({
|
|
249
|
+
...v,
|
|
250
|
+
// Older projects report the built-in environments upper-case; profiles are lower-case.
|
|
251
|
+
environments: (v.environments ?? []).map((e) => e.toLowerCase()),
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
158
254
|
/** Every app in every account this token or session can see. */
|
|
159
255
|
export async function listExpoApps(auth) {
|
|
160
256
|
const { meActor } = await expoGraphQL(auth, `{ meActor { accounts { id name } } }`);
|