eas-cli 16.19.3 → 16.20.1
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/README.md +85 -85
- package/build/api.d.ts +1 -0
- package/build/api.js +5 -1
- package/build/build/android/build.js +1 -3
- package/build/build/build.d.ts +3 -3
- package/build/build/build.js +11 -4
- package/build/build/graphql.d.ts +4 -2
- package/build/build/graphql.js +3 -14
- package/build/build/ios/build.js +1 -3
- package/build/build/metadata.d.ts +1 -2
- package/build/build/metadata.js +0 -1
- package/build/commandUtils/workflow/validation.d.ts +6 -0
- package/build/commandUtils/workflow/validation.js +160 -0
- package/build/commands/build/configure.js +2 -19
- package/build/commands/env/create.js +0 -1
- package/build/commands/project/new.d.ts +33 -0
- package/build/commands/project/new.js +349 -0
- package/build/commands/project/onboarding.js +1 -1
- package/build/commands/update/index.js +1 -5
- package/build/commands/workflow/run.js +0 -2
- package/build/commands/workflow/validate.d.ts +1 -0
- package/build/commands/workflow/validate.js +8 -39
- package/build/credentials/ios/appstore/bundleIdCapabilities.js +1 -1
- package/build/credentials/ios/appstore/capabilityList.js +18 -8
- package/build/graphql/generated.d.ts +0 -6
- package/build/graphql/mutations/EnvironmentVariableMutation.d.ts +5 -24
- package/build/graphql/mutations/FingerprintMutation.d.ts +2 -3
- package/build/log.js +2 -1
- package/build/onboarding/installDependencies.d.ts +4 -1
- package/build/onboarding/installDependencies.js +13 -5
- package/build/project/maybeUploadFingerprintAsync.d.ts +2 -2
- package/build/project/maybeUploadFingerprintAsync.js +7 -12
- package/build/project/publish.d.ts +5 -5
- package/build/project/publish.js +1 -1
- package/build/utils/prompts.d.ts +4 -0
- package/build/utils/prompts.js +25 -1
- package/build/utils/workflowFile.d.ts +1 -3
- package/build/utils/workflowFile.js +2 -8
- package/oclif.manifest.json +23 -1
- package/package.json +6 -6
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatScriptCommand = exports.initializeGitRepositoryAsync = exports.mergeReadmeAsync = exports.copyProjectTemplatesAsync = exports.updatePackageJsonAsync = exports.generateEasConfigAsync = exports.generateAppConfigAsync = exports.generateConfigFilesAsync = exports.stripInvalidCharactersForBundleIdentifier = exports.createProjectAsync = exports.getAccountChoices = exports.installProjectDependenciesAsync = exports.cloneTemplateAsync = exports.promptForTargetDirectoryAsync = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const eas_json_1 = require("@expo/eas-json");
|
|
6
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
8
|
+
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
9
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
10
|
+
const ts_deepmerge_1 = tslib_1.__importDefault(require("ts-deepmerge"));
|
|
11
|
+
const api_1 = require("../../api");
|
|
12
|
+
const url_1 = require("../../build/utils/url");
|
|
13
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
14
|
+
const generated_1 = require("../../graphql/generated");
|
|
15
|
+
const AppMutation_1 = require("../../graphql/mutations/AppMutation");
|
|
16
|
+
const AppQuery_1 = require("../../graphql/queries/AppQuery");
|
|
17
|
+
const log_1 = tslib_1.__importStar(require("../../log"));
|
|
18
|
+
const git_1 = require("../../onboarding/git");
|
|
19
|
+
const installDependencies_1 = require("../../onboarding/installDependencies");
|
|
20
|
+
const runCommand_1 = require("../../onboarding/runCommand");
|
|
21
|
+
const ora_1 = require("../../ora");
|
|
22
|
+
const expoConfig_1 = require("../../project/expoConfig");
|
|
23
|
+
const fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync_1 = require("../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync");
|
|
24
|
+
const prompts_1 = require("../../prompts");
|
|
25
|
+
const User_1 = require("../../user/User");
|
|
26
|
+
const easCli_1 = require("../../utils/easCli");
|
|
27
|
+
async function promptForTargetDirectoryAsync(targetProjectDirFromArgs) {
|
|
28
|
+
log_1.default.log(`🚚 Let's start by cloning the default Expo template project from GitHub and installing dependencies.`);
|
|
29
|
+
log_1.default.newLine();
|
|
30
|
+
if (targetProjectDirFromArgs) {
|
|
31
|
+
return targetProjectDirFromArgs;
|
|
32
|
+
}
|
|
33
|
+
const result = await (0, prompts_1.promptAsync)({
|
|
34
|
+
type: 'text',
|
|
35
|
+
name: 'targetProjectDir',
|
|
36
|
+
message: 'Where would you like to create your new project directory?',
|
|
37
|
+
initial: path_1.default.join(process.cwd(), 'new-expo-project'),
|
|
38
|
+
});
|
|
39
|
+
return result.targetProjectDir;
|
|
40
|
+
}
|
|
41
|
+
exports.promptForTargetDirectoryAsync = promptForTargetDirectoryAsync;
|
|
42
|
+
async function cloneTemplateAsync(targetProjectDir) {
|
|
43
|
+
const githubUsername = 'expo';
|
|
44
|
+
const githubRepositoryName = 'expo-template-default';
|
|
45
|
+
log_1.default.log(`📂 Cloning the project to ${targetProjectDir}`);
|
|
46
|
+
log_1.default.newLine();
|
|
47
|
+
const cloneMethod = (await (0, git_1.canAccessRepositoryUsingSshAsync)({
|
|
48
|
+
githubUsername,
|
|
49
|
+
githubRepositoryName,
|
|
50
|
+
}))
|
|
51
|
+
? 'ssh'
|
|
52
|
+
: 'https';
|
|
53
|
+
log_1.default.log(chalk_1.default.dim(`We detected that ${cloneMethod} is your preferred git clone method`));
|
|
54
|
+
log_1.default.newLine();
|
|
55
|
+
const { targetProjectDir: finalTargetProjectDirectory } = await (0, git_1.runGitCloneAsync)({
|
|
56
|
+
githubUsername,
|
|
57
|
+
githubRepositoryName,
|
|
58
|
+
targetProjectDir,
|
|
59
|
+
cloneMethod,
|
|
60
|
+
});
|
|
61
|
+
return finalTargetProjectDirectory;
|
|
62
|
+
}
|
|
63
|
+
exports.cloneTemplateAsync = cloneTemplateAsync;
|
|
64
|
+
async function installProjectDependenciesAsync(projectDir) {
|
|
65
|
+
const packageManager = await (0, installDependencies_1.promptForPackageManagerAsync)();
|
|
66
|
+
await (0, installDependencies_1.installDependenciesAsync)({
|
|
67
|
+
projectDir,
|
|
68
|
+
packageManager,
|
|
69
|
+
});
|
|
70
|
+
const dependencies = ['expo-updates', '@expo/metro-runtime'];
|
|
71
|
+
for (const dependency of dependencies) {
|
|
72
|
+
await (0, runCommand_1.runCommandAsync)({
|
|
73
|
+
cwd: projectDir,
|
|
74
|
+
command: 'npx',
|
|
75
|
+
args: ['expo', 'install', dependency],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return packageManager;
|
|
79
|
+
}
|
|
80
|
+
exports.installProjectDependenciesAsync = installProjectDependenciesAsync;
|
|
81
|
+
function getAccountChoices(actor, namesWithSufficientPermissions) {
|
|
82
|
+
const sortedAccounts = actor.accounts.sort((a, _b) => actor.__typename === 'User' ? (a.name === actor.username ? -1 : 1) : 0);
|
|
83
|
+
return sortedAccounts.map(account => {
|
|
84
|
+
const isPersonalAccount = actor.__typename === 'User' && account.name === actor.username;
|
|
85
|
+
const accountDisplayName = isPersonalAccount
|
|
86
|
+
? `${account.name} (personal account)`
|
|
87
|
+
: account.name;
|
|
88
|
+
const disabled = !namesWithSufficientPermissions.has(account.name);
|
|
89
|
+
return {
|
|
90
|
+
title: accountDisplayName,
|
|
91
|
+
value: { name: account.name },
|
|
92
|
+
...(disabled && {
|
|
93
|
+
disabled: true,
|
|
94
|
+
description: 'You do not have the required permissions to create projects on this account.',
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
exports.getAccountChoices = getAccountChoices;
|
|
100
|
+
async function createProjectAsync(graphqlClient, actor, projectDir) {
|
|
101
|
+
const allAccounts = actor.accounts;
|
|
102
|
+
const accountNamesWhereUserHasSufficientPermissionsToCreateApp = new Set(allAccounts
|
|
103
|
+
.filter(a => a.users.find(it => it.actor.id === actor.id)?.role !== generated_1.Role.ViewOnly)
|
|
104
|
+
.map(it => it.name));
|
|
105
|
+
let accountName = allAccounts[0].name;
|
|
106
|
+
if (allAccounts.length > 1) {
|
|
107
|
+
const choices = getAccountChoices(actor, accountNamesWhereUserHasSufficientPermissionsToCreateApp);
|
|
108
|
+
accountName = (await (0, prompts_1.promptAsync)({
|
|
109
|
+
type: 'select',
|
|
110
|
+
name: 'account',
|
|
111
|
+
message: 'Which account should own this project?',
|
|
112
|
+
choices,
|
|
113
|
+
})).account.name;
|
|
114
|
+
}
|
|
115
|
+
const projectName = (0, User_1.getActorUsername)(actor) + '-app';
|
|
116
|
+
const projectFullName = `@${accountName}/${projectName}`;
|
|
117
|
+
const existingProjectIdOnServer = await (0, fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync_1.findProjectIdByAccountNameAndSlugNullableAsync)(graphqlClient, accountName, projectName);
|
|
118
|
+
if (existingProjectIdOnServer) {
|
|
119
|
+
throw new Error(`Existing project found: ${projectFullName} (ID: ${existingProjectIdOnServer}). Project ID configuration canceled. Re-run the command to select a different account/project.`);
|
|
120
|
+
}
|
|
121
|
+
if (!accountNamesWhereUserHasSufficientPermissionsToCreateApp.has(accountName)) {
|
|
122
|
+
throw new Error(`You don't have permission to create a new project on the ${accountName} account and no matching project already exists on the account.`);
|
|
123
|
+
}
|
|
124
|
+
const projectDashboardUrl = (0, url_1.getProjectDashboardUrl)(accountName, projectName);
|
|
125
|
+
const projectLink = (0, log_1.link)(projectDashboardUrl, { text: projectFullName });
|
|
126
|
+
const account = (0, nullthrows_1.default)(allAccounts.find(a => a.name === accountName));
|
|
127
|
+
const spinner = (0, ora_1.ora)(`Creating ${chalk_1.default.bold(projectFullName)}`).start();
|
|
128
|
+
let projectId;
|
|
129
|
+
try {
|
|
130
|
+
projectId = await AppMutation_1.AppMutation.createAppAsync(graphqlClient, {
|
|
131
|
+
accountId: account.id,
|
|
132
|
+
projectName,
|
|
133
|
+
});
|
|
134
|
+
spinner.succeed(`Created ${chalk_1.default.bold(projectLink)}`);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
spinner.fail();
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
const exp = await (0, expoConfig_1.getPrivateExpoConfigAsync)(projectDir, { skipPlugins: true });
|
|
141
|
+
await (0, expoConfig_1.createOrModifyExpoConfigAsync)(projectDir, {
|
|
142
|
+
extra: { ...exp.extra, eas: { ...exp.extra?.eas, projectId } },
|
|
143
|
+
}, { skipSDKVersionRequirement: true });
|
|
144
|
+
log_1.default.withTick(`Project successfully linked (ID: ${chalk_1.default.bold(projectId)}) (modified app.json)`);
|
|
145
|
+
return projectId;
|
|
146
|
+
}
|
|
147
|
+
exports.createProjectAsync = createProjectAsync;
|
|
148
|
+
function stripInvalidCharactersForBundleIdentifier(string) {
|
|
149
|
+
return string.replaceAll(/[^A-Za-z0-9]/g, '');
|
|
150
|
+
}
|
|
151
|
+
exports.stripInvalidCharactersForBundleIdentifier = stripInvalidCharactersForBundleIdentifier;
|
|
152
|
+
async function generateConfigFilesAsync(projectDir, app) {
|
|
153
|
+
await generateAppConfigAsync(projectDir, app);
|
|
154
|
+
await generateEasConfigAsync(projectDir);
|
|
155
|
+
await updatePackageJsonAsync(projectDir);
|
|
156
|
+
await copyProjectTemplatesAsync(projectDir);
|
|
157
|
+
await mergeReadmeAsync(projectDir);
|
|
158
|
+
}
|
|
159
|
+
exports.generateConfigFilesAsync = generateConfigFilesAsync;
|
|
160
|
+
async function generateAppConfigAsync(projectDir, app) {
|
|
161
|
+
// Android package name requires each component to start with a lowercase letter.
|
|
162
|
+
const isUsernameValidSegment = /^[^a-z]/.test(app.ownerAccount.name);
|
|
163
|
+
const userPrefix = isUsernameValidSegment ? 'user' : '';
|
|
164
|
+
const isSlugValidSegment = /^[^a-z]/.test(app.slug);
|
|
165
|
+
const slugPrefix = isSlugValidSegment ? 'app' : '';
|
|
166
|
+
const bundleIdentifier = `com.${userPrefix}${stripInvalidCharactersForBundleIdentifier(app.ownerAccount.name)}.${slugPrefix}${stripInvalidCharactersForBundleIdentifier(app.slug)}`;
|
|
167
|
+
const updateUrl = (0, api_1.getEASUpdateURL)(app.id, /* manifestHostOverride */ null);
|
|
168
|
+
const { expo: baseExpoConfig } = await fs_extra_1.default.readJson(path_1.default.join(projectDir, 'app.json'));
|
|
169
|
+
const expoConfig = {
|
|
170
|
+
name: app.name ?? app.slug,
|
|
171
|
+
slug: app.slug,
|
|
172
|
+
scheme: stripInvalidCharactersForBundleIdentifier(app.name ?? app.slug),
|
|
173
|
+
extra: {
|
|
174
|
+
eas: {
|
|
175
|
+
projectId: app.id,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
owner: app.ownerAccount.name,
|
|
179
|
+
updates: {
|
|
180
|
+
url: updateUrl,
|
|
181
|
+
},
|
|
182
|
+
runtimeVersion: {
|
|
183
|
+
policy: 'appVersion',
|
|
184
|
+
},
|
|
185
|
+
ios: {
|
|
186
|
+
bundleIdentifier,
|
|
187
|
+
},
|
|
188
|
+
android: {
|
|
189
|
+
package: bundleIdentifier,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
const mergedConfig = (0, ts_deepmerge_1.default)(baseExpoConfig, expoConfig);
|
|
193
|
+
const appJsonPath = path_1.default.join(projectDir, 'app.json');
|
|
194
|
+
await fs_extra_1.default.writeJson(appJsonPath, { expo: mergedConfig }, { spaces: 2 });
|
|
195
|
+
log_1.default.withTick(`Generated ${chalk_1.default.bold('app.json')}. ${(0, log_1.learnMore)('https://docs.expo.dev/versions/latest/config/app/')}`);
|
|
196
|
+
log_1.default.log();
|
|
197
|
+
}
|
|
198
|
+
exports.generateAppConfigAsync = generateAppConfigAsync;
|
|
199
|
+
async function generateEasConfigAsync(projectDir) {
|
|
200
|
+
const easBuildGitHubConfig = {
|
|
201
|
+
android: {
|
|
202
|
+
image: 'latest',
|
|
203
|
+
},
|
|
204
|
+
ios: {
|
|
205
|
+
image: 'latest',
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
const easJson = {
|
|
209
|
+
cli: {
|
|
210
|
+
version: `>= ${easCli_1.easCliVersion}`,
|
|
211
|
+
appVersionSource: eas_json_1.AppVersionSource.REMOTE,
|
|
212
|
+
},
|
|
213
|
+
build: {
|
|
214
|
+
development: {
|
|
215
|
+
developmentClient: true,
|
|
216
|
+
distribution: 'internal',
|
|
217
|
+
...easBuildGitHubConfig,
|
|
218
|
+
},
|
|
219
|
+
'development-simulator': {
|
|
220
|
+
extends: 'development',
|
|
221
|
+
ios: {
|
|
222
|
+
simulator: true,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
preview: {
|
|
226
|
+
distribution: 'internal',
|
|
227
|
+
channel: 'main',
|
|
228
|
+
...easBuildGitHubConfig,
|
|
229
|
+
},
|
|
230
|
+
production: {
|
|
231
|
+
channel: 'production',
|
|
232
|
+
autoIncrement: true,
|
|
233
|
+
...easBuildGitHubConfig,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
submit: {
|
|
237
|
+
production: {},
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
const easJsonPath = path_1.default.join(projectDir, 'eas.json');
|
|
241
|
+
await fs_extra_1.default.writeJson(easJsonPath, easJson, { spaces: 2 });
|
|
242
|
+
log_1.default.withTick(`Generated ${chalk_1.default.bold('eas.json')}. ${(0, log_1.learnMore)('https://docs.expo.dev/build-reference/eas-json/')}`);
|
|
243
|
+
log_1.default.log();
|
|
244
|
+
}
|
|
245
|
+
exports.generateEasConfigAsync = generateEasConfigAsync;
|
|
246
|
+
async function updatePackageJsonAsync(projectDir) {
|
|
247
|
+
const packageJsonPath = path_1.default.join(projectDir, 'package.json');
|
|
248
|
+
const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
|
|
249
|
+
if (!packageJson.scripts) {
|
|
250
|
+
packageJson.scripts = {};
|
|
251
|
+
}
|
|
252
|
+
packageJson.scripts.preview = 'npx eas-cli@latest workflow:run publish-preview-update.yml';
|
|
253
|
+
packageJson.scripts['development-builds'] =
|
|
254
|
+
'npx eas-cli@latest workflow:run create-development-builds.yml';
|
|
255
|
+
packageJson.scripts.deploy = 'npx eas-cli@latest workflow:run deploy-to-production.yml';
|
|
256
|
+
await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
257
|
+
log_1.default.withTick('Updated package.json with scripts');
|
|
258
|
+
log_1.default.log();
|
|
259
|
+
}
|
|
260
|
+
exports.updatePackageJsonAsync = updatePackageJsonAsync;
|
|
261
|
+
async function copyProjectTemplatesAsync(projectDir) {
|
|
262
|
+
const templatesSourceDir = path_1.default.join(__dirname, 'templates', '.eas', 'workflows');
|
|
263
|
+
const easWorkflowsTargetDir = path_1.default.join(projectDir, '.eas', 'workflows');
|
|
264
|
+
await fs_extra_1.default.copy(templatesSourceDir, easWorkflowsTargetDir, {
|
|
265
|
+
overwrite: true,
|
|
266
|
+
errorOnExist: false,
|
|
267
|
+
});
|
|
268
|
+
log_1.default.withTick('Created EAS workflow files');
|
|
269
|
+
log_1.default.log();
|
|
270
|
+
}
|
|
271
|
+
exports.copyProjectTemplatesAsync = copyProjectTemplatesAsync;
|
|
272
|
+
async function mergeReadmeAsync(projectDir) {
|
|
273
|
+
const readmeTemplatePath = path_1.default.join(__dirname, 'templates', 'readme-additions.md');
|
|
274
|
+
const projectReadmePath = path_1.default.join(projectDir, 'README.md');
|
|
275
|
+
const readmeAdditions = await fs_extra_1.default.readFile(readmeTemplatePath, 'utf8');
|
|
276
|
+
const existingReadme = await fs_extra_1.default.readFile(projectReadmePath, 'utf8');
|
|
277
|
+
const targetSection = '## Get a fresh project';
|
|
278
|
+
const sectionIndex = existingReadme.indexOf(targetSection);
|
|
279
|
+
let mergedReadme;
|
|
280
|
+
if (sectionIndex !== -1) {
|
|
281
|
+
// Insert before "## Get a fresh project" section
|
|
282
|
+
const beforeSection = existingReadme.substring(0, sectionIndex).trim();
|
|
283
|
+
const afterSection = existingReadme.substring(sectionIndex);
|
|
284
|
+
mergedReadme = beforeSection + '\n\n' + readmeAdditions.trim() + '\n\n' + afterSection;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
// Append to the end if section doesn't exist
|
|
288
|
+
mergedReadme = existingReadme.trim() + '\n\n' + readmeAdditions.trim() + '\n';
|
|
289
|
+
}
|
|
290
|
+
await fs_extra_1.default.writeFile(projectReadmePath, mergedReadme);
|
|
291
|
+
log_1.default.withTick('Updated README.md with EAS configuration details');
|
|
292
|
+
log_1.default.log();
|
|
293
|
+
}
|
|
294
|
+
exports.mergeReadmeAsync = mergeReadmeAsync;
|
|
295
|
+
async function initializeGitRepositoryAsync(projectDir) {
|
|
296
|
+
await fs_extra_1.default.remove(path_1.default.join(projectDir, '.git'));
|
|
297
|
+
const commands = [['init'], ['add', '.'], ['commit', '-m', 'Initial commit']];
|
|
298
|
+
for (const args of commands) {
|
|
299
|
+
await (0, runCommand_1.runCommandAsync)({
|
|
300
|
+
cwd: projectDir,
|
|
301
|
+
command: 'git',
|
|
302
|
+
args,
|
|
303
|
+
});
|
|
304
|
+
log_1.default.log();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
exports.initializeGitRepositoryAsync = initializeGitRepositoryAsync;
|
|
308
|
+
const formatScriptCommand = (script, packageManager) => {
|
|
309
|
+
if (packageManager === 'npm') {
|
|
310
|
+
return `npm run ${script}`;
|
|
311
|
+
}
|
|
312
|
+
return `${packageManager} ${script}`;
|
|
313
|
+
};
|
|
314
|
+
exports.formatScriptCommand = formatScriptCommand;
|
|
315
|
+
class New extends EasCommand_1.default {
|
|
316
|
+
static aliases = ['new'];
|
|
317
|
+
static description = "create a new project set up with Expo's services.";
|
|
318
|
+
static flags = {};
|
|
319
|
+
static hidden = true;
|
|
320
|
+
static args = [{ name: 'TARGET_PROJECT_DIRECTORY' }];
|
|
321
|
+
static contextDefinition = {
|
|
322
|
+
...this.ContextOptions.LoggedIn,
|
|
323
|
+
};
|
|
324
|
+
async runAsync() {
|
|
325
|
+
const { args } = await this.parse(New);
|
|
326
|
+
const { loggedIn: { actor, graphqlClient }, } = await this.getContextAsync(New, { nonInteractive: false });
|
|
327
|
+
if (actor.__typename === 'Robot') {
|
|
328
|
+
throw new Error('This command is not available for robot users. Make sure you are not using a robot token and try again.');
|
|
329
|
+
}
|
|
330
|
+
log_1.default.warn('This command is not yet implemented. It will create a new project, but it will not be fully configured.');
|
|
331
|
+
log_1.default.log(`👋 Welcome to Expo, ${actor.username}!`);
|
|
332
|
+
log_1.default.newLine();
|
|
333
|
+
const targetProjectDirectory = await promptForTargetDirectoryAsync(args.TARGET_PROJECT_DIRECTORY);
|
|
334
|
+
const projectDirectory = await cloneTemplateAsync(targetProjectDirectory);
|
|
335
|
+
const packageManager = await installProjectDependenciesAsync(projectDirectory);
|
|
336
|
+
const projectId = await createProjectAsync(graphqlClient, actor, projectDirectory);
|
|
337
|
+
const app = await AppQuery_1.AppQuery.byIdAsync(graphqlClient, projectId);
|
|
338
|
+
await generateConfigFilesAsync(projectDirectory, app);
|
|
339
|
+
await initializeGitRepositoryAsync(projectDirectory);
|
|
340
|
+
log_1.default.log('🎉 We finished creating your new project.');
|
|
341
|
+
log_1.default.log('Next steps:');
|
|
342
|
+
log_1.default.withInfo(`Run \`cd ${projectDirectory}\` to navigate to your project.`);
|
|
343
|
+
log_1.default.withInfo(`Run \`${(0, exports.formatScriptCommand)('preview', packageManager)}\` to create a preview build on EAS. ${(0, log_1.learnMore)('https://docs.expo.dev/eas/workflows/examples/publish-preview-update/')}`);
|
|
344
|
+
log_1.default.withInfo(`Run \`${(0, exports.formatScriptCommand)('start', packageManager)}\` to start developing locally. ${(0, log_1.learnMore)('https://docs.expo.dev/get-started/start-developing/')}`);
|
|
345
|
+
log_1.default.withInfo(`See the README.md for more information about your project.`);
|
|
346
|
+
log_1.default.newLine();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
exports.default = New;
|
|
@@ -100,7 +100,7 @@ class Onboarding extends EasCommand_1.default {
|
|
|
100
100
|
}))
|
|
101
101
|
? 'ssh'
|
|
102
102
|
: 'https';
|
|
103
|
-
log_1.default.log(chalk_1.default.dim(`We detected that ${cloneMethod} is your
|
|
103
|
+
log_1.default.log(chalk_1.default.dim(`We detected that ${cloneMethod} is your preferred git clone method`));
|
|
104
104
|
log_1.default.log();
|
|
105
105
|
const { targetProjectDir: finalTargetProjectDirectory } = await (0, git_1.runGitCloneAsync)({
|
|
106
106
|
githubUsername,
|
|
@@ -8,7 +8,6 @@ const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
|
8
8
|
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
9
9
|
// import { getExpoWebsiteBaseUrl } from '../../api';
|
|
10
10
|
const queries_1 = require("../../branch/queries");
|
|
11
|
-
const graphql_1 = require("../../build/graphql");
|
|
12
11
|
const repository_1 = require("../../build/utils/repository");
|
|
13
12
|
const url_1 = require("../../build/utils/url");
|
|
14
13
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
@@ -324,10 +323,7 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
324
323
|
const transformedFingerprintInfoGroup = Object.entries(fingerprintInfoGroup).reduce((prev, [platform, fingerprintInfo]) => {
|
|
325
324
|
return {
|
|
326
325
|
...prev,
|
|
327
|
-
[platform]:
|
|
328
|
-
...fingerprintInfo,
|
|
329
|
-
fingerprintSource: (0, graphql_1.transformFingerprintSource)(fingerprintInfo.fingerprintSource),
|
|
330
|
-
},
|
|
326
|
+
[platform]: fingerprintInfo,
|
|
331
327
|
};
|
|
332
328
|
}, {});
|
|
333
329
|
const assetMapGroup = assetMapSource
|
|
@@ -132,8 +132,6 @@ class WorkflowRun extends EasCommand_1.default {
|
|
|
132
132
|
if (error instanceof core_2.CombinedError) {
|
|
133
133
|
workflowFile_1.WorkflowFile.maybePrintWorkflowFileValidationErrors({
|
|
134
134
|
error,
|
|
135
|
-
accountName: account.name,
|
|
136
|
-
projectName,
|
|
137
135
|
});
|
|
138
136
|
throw error;
|
|
139
137
|
}
|
|
@@ -11,6 +11,7 @@ export declare class WorkflowValidate extends EasCommand {
|
|
|
11
11
|
};
|
|
12
12
|
static contextDefinition: {
|
|
13
13
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
14
|
+
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
14
15
|
projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
|
|
15
16
|
getDynamicPublicProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicPublicProjectConfigContextField;
|
|
16
17
|
getDynamicPrivateProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicPrivateProjectConfigContextField;
|
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WorkflowValidate = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const core_1 = require("@urql/core");
|
|
6
|
-
const YAML = tslib_1.__importStar(require("yaml"));
|
|
7
5
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
8
6
|
const flags_1 = require("../../commandUtils/flags");
|
|
9
|
-
const
|
|
7
|
+
const validation_1 = require("../../commandUtils/workflow/validation");
|
|
10
8
|
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
11
9
|
const ora_1 = require("../../ora");
|
|
12
|
-
const projectUtils_1 = require("../../project/projectUtils");
|
|
13
10
|
const workflowFile_1 = require("../../utils/workflowFile");
|
|
14
11
|
class WorkflowValidate extends EasCommand_1.default {
|
|
15
12
|
static description = 'validate a workflow configuration yaml file';
|
|
@@ -26,56 +23,28 @@ class WorkflowValidate extends EasCommand_1.default {
|
|
|
26
23
|
static contextDefinition = {
|
|
27
24
|
...this.ContextOptions.DynamicProjectConfig,
|
|
28
25
|
...this.ContextOptions.ProjectDir,
|
|
26
|
+
...this.ContextOptions.ProjectId,
|
|
29
27
|
...this.ContextOptions.LoggedIn,
|
|
30
28
|
};
|
|
31
29
|
async runAsync() {
|
|
32
30
|
const { args: { path: filePath }, flags, } = await this.parse(WorkflowValidate);
|
|
33
|
-
const { getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, projectDir, } = await this.getContextAsync(WorkflowValidate, {
|
|
34
|
-
nonInteractive: flags['non-interactive'],
|
|
35
|
-
withServerSideEnvironment: null,
|
|
36
|
-
});
|
|
37
|
-
const { projectId, exp: { slug: projectName }, } = await getDynamicPrivateProjectConfigAsync();
|
|
38
|
-
const account = await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId);
|
|
39
31
|
const spinner = (0, ora_1.ora)().start('Validating the workflow YAML file…');
|
|
40
32
|
try {
|
|
33
|
+
const { loggedIn: { graphqlClient }, projectDir, projectId, } = await this.getContextAsync(WorkflowValidate, {
|
|
34
|
+
nonInteractive: flags['non-interactive'],
|
|
35
|
+
withServerSideEnvironment: null,
|
|
36
|
+
});
|
|
41
37
|
const workflowFileContents = await workflowFile_1.WorkflowFile.readWorkflowFileContentsAsync({
|
|
42
38
|
projectDir,
|
|
43
39
|
filePath,
|
|
44
40
|
});
|
|
45
41
|
log_1.default.log(`Using workflow file from ${workflowFileContents.filePath}`);
|
|
46
|
-
|
|
47
|
-
// Check if the parsed result is empty or null
|
|
48
|
-
if (parsedYaml === null ||
|
|
49
|
-
parsedYaml === undefined ||
|
|
50
|
-
(typeof parsedYaml === 'object' && Object.keys(parsedYaml).length === 0)) {
|
|
51
|
-
throw new Error('YAML file is empty or contains only comments.');
|
|
52
|
-
}
|
|
53
|
-
await WorkflowRevisionMutation_1.WorkflowRevisionMutation.validateWorkflowYamlConfigAsync(graphqlClient, {
|
|
54
|
-
appId: projectId,
|
|
55
|
-
yamlConfig: workflowFileContents.yamlConfig,
|
|
56
|
-
});
|
|
42
|
+
await (0, validation_1.validateWorkflowFileAsync)(workflowFileContents, projectDir, graphqlClient, projectId);
|
|
57
43
|
spinner.succeed('Workflow configuration YAML is valid.');
|
|
58
44
|
}
|
|
59
45
|
catch (error) {
|
|
60
46
|
spinner.fail('Workflow configuration YAML is not valid.');
|
|
61
|
-
|
|
62
|
-
log_1.default.error(`YAML syntax error: ${error.message}`);
|
|
63
|
-
}
|
|
64
|
-
else if (error instanceof core_1.CombinedError) {
|
|
65
|
-
workflowFile_1.WorkflowFile.maybePrintWorkflowFileValidationErrors({
|
|
66
|
-
error,
|
|
67
|
-
accountName: account.name,
|
|
68
|
-
projectName,
|
|
69
|
-
});
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
else if (error instanceof Error) {
|
|
73
|
-
log_1.default.error(error.message);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
log_1.default.error(`Unexpected error: ${String(error)}`);
|
|
77
|
-
}
|
|
78
|
-
throw error;
|
|
47
|
+
(0, validation_1.logWorkflowValidationErrors)(error);
|
|
79
48
|
}
|
|
80
49
|
}
|
|
81
50
|
}
|
|
@@ -73,7 +73,7 @@ function getCapabilitiesToEnable(currentRemoteCapabilities, entitlements, additi
|
|
|
73
73
|
});
|
|
74
74
|
const { op } = operation;
|
|
75
75
|
if (log_1.default.isDebug) {
|
|
76
|
-
log_1.default.log(`Will ${op} remote capability: ${key} (${staticCapabilityInfo.name}.`);
|
|
76
|
+
log_1.default.log(`Will ${op} remote capability: ${key} (${staticCapabilityInfo.name}).`);
|
|
77
77
|
}
|
|
78
78
|
if (op === 'enable') {
|
|
79
79
|
enabledCapabilityNames.push(staticCapabilityInfo.name);
|
|
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const apple_utils_1 = require("@expo/apple-utils");
|
|
6
6
|
const invariant_1 = require("graphql/jsutils/invariant");
|
|
7
7
|
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
8
|
+
const log_1 = tslib_1.__importDefault(require("../../../log"));
|
|
8
9
|
const validateBooleanOptions = (options) => {
|
|
9
10
|
return typeof options === 'boolean';
|
|
10
11
|
};
|
|
@@ -51,16 +52,25 @@ const capabilityWithSettingsSyncOperation = ({ existingRemote, entitlementValue,
|
|
|
51
52
|
if (!existingRemote) {
|
|
52
53
|
return enableOp;
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
const { attributes, id } = existingRemote;
|
|
56
|
+
if ('enabled' in attributes) {
|
|
57
|
+
// the `enabled` field should be available as per https://developer.apple.com/documentation/appstoreconnectapi/capabilitysetting
|
|
58
|
+
const existingEnabled = attributes.enabled === true;
|
|
59
|
+
// If both are enabled and the existing one has settings, skip the update
|
|
60
|
+
if (existingEnabled && entitlementValue && attributes.settings) {
|
|
61
|
+
return skipOp;
|
|
62
|
+
}
|
|
63
|
+
const newOption = entitlementValue ? apple_utils_1.CapabilityTypeOption.ON : apple_utils_1.CapabilityTypeOption.OFF;
|
|
64
|
+
// If the states don't match, we need to update
|
|
65
|
+
const newEnabled = newOption === apple_utils_1.CapabilityTypeOption.ON;
|
|
66
|
+
return existingEnabled === newEnabled ? skipOp : { op: 'enable', option: newOption };
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
if (log_1.default.isDebug) {
|
|
70
|
+
log_1.default.log(`Expected the "enabled" attribute in ${id} but it was not present (attributes: ${JSON.stringify(attributes, null, 2)}). Will skip syncing this capability.`);
|
|
71
|
+
}
|
|
59
72
|
return skipOp;
|
|
60
73
|
}
|
|
61
|
-
// If the states don't match, we need to update
|
|
62
|
-
const newEnabled = newOption === apple_utils_1.CapabilityTypeOption.ON;
|
|
63
|
-
return existingEnabled === newEnabled ? skipOp : { op: 'enable', option: newOption };
|
|
64
74
|
};
|
|
65
75
|
// NOTE(Bacon): From manually toggling values in Xcode and checking the git diff and network requests.
|
|
66
76
|
// Last Updated: July 22nd, 2021
|
|
@@ -1093,7 +1093,6 @@ export type AndroidSubmissionConfig = {
|
|
|
1093
1093
|
};
|
|
1094
1094
|
export type AndroidSubmissionConfigInput = {
|
|
1095
1095
|
applicationIdentifier?: InputMaybe<Scalars['String']['input']>;
|
|
1096
|
-
archiveUrl?: InputMaybe<Scalars['String']['input']>;
|
|
1097
1096
|
changelog?: InputMaybe<Scalars['String']['input']>;
|
|
1098
1097
|
changesNotSentForReview?: InputMaybe<Scalars['Boolean']['input']>;
|
|
1099
1098
|
googleServiceAccountKeyId?: InputMaybe<Scalars['String']['input']>;
|
|
@@ -3028,7 +3027,6 @@ export type CreateAndConfigureRepositoryInput = {
|
|
|
3028
3027
|
export type CreateAndroidSubmissionInput = {
|
|
3029
3028
|
appId: Scalars['ID']['input'];
|
|
3030
3029
|
archiveSource?: InputMaybe<SubmissionArchiveSourceInput>;
|
|
3031
|
-
archiveUrl?: InputMaybe<Scalars['String']['input']>;
|
|
3032
3030
|
config: AndroidSubmissionConfigInput;
|
|
3033
3031
|
submittedBuildId?: InputMaybe<Scalars['ID']['input']>;
|
|
3034
3032
|
};
|
|
@@ -3096,7 +3094,6 @@ export type CreateGitHubRepositorySettingsInput = {
|
|
|
3096
3094
|
export type CreateIosSubmissionInput = {
|
|
3097
3095
|
appId: Scalars['ID']['input'];
|
|
3098
3096
|
archiveSource?: InputMaybe<SubmissionArchiveSourceInput>;
|
|
3099
|
-
archiveUrl?: InputMaybe<Scalars['String']['input']>;
|
|
3100
3097
|
config: IosSubmissionConfigInput;
|
|
3101
3098
|
submittedBuildId?: InputMaybe<Scalars['ID']['input']>;
|
|
3102
3099
|
};
|
|
@@ -3108,7 +3105,6 @@ export type CreateSentryProjectInput = {
|
|
|
3108
3105
|
export type CreateSharedEnvironmentVariableInput = {
|
|
3109
3106
|
environments?: InputMaybe<Array<EnvironmentVariableEnvironment>>;
|
|
3110
3107
|
fileName?: InputMaybe<Scalars['String']['input']>;
|
|
3111
|
-
isGlobal?: InputMaybe<Scalars['Boolean']['input']>;
|
|
3112
3108
|
name: Scalars['String']['input'];
|
|
3113
3109
|
overwrite?: InputMaybe<Scalars['Boolean']['input']>;
|
|
3114
3110
|
type?: InputMaybe<EnvironmentSecretType>;
|
|
@@ -4476,7 +4472,6 @@ export type IosSubmissionConfig = {
|
|
|
4476
4472
|
export type IosSubmissionConfigInput = {
|
|
4477
4473
|
appleAppSpecificPassword?: InputMaybe<Scalars['String']['input']>;
|
|
4478
4474
|
appleIdUsername?: InputMaybe<Scalars['String']['input']>;
|
|
4479
|
-
archiveUrl?: InputMaybe<Scalars['String']['input']>;
|
|
4480
4475
|
ascApiKey?: InputMaybe<AscApiKeyInput>;
|
|
4481
4476
|
ascApiKeyId?: InputMaybe<Scalars['String']['input']>;
|
|
4482
4477
|
ascAppIdentifier: Scalars['String']['input'];
|
|
@@ -6193,7 +6188,6 @@ export type UpdateEnvironmentVariableInput = {
|
|
|
6193
6188
|
environments?: InputMaybe<Array<EnvironmentVariableEnvironment>>;
|
|
6194
6189
|
fileName?: InputMaybe<Scalars['String']['input']>;
|
|
6195
6190
|
id: Scalars['ID']['input'];
|
|
6196
|
-
isGlobal?: InputMaybe<Scalars['Boolean']['input']>;
|
|
6197
6191
|
name?: InputMaybe<Scalars['String']['input']>;
|
|
6198
6192
|
type?: InputMaybe<EnvironmentSecretType>;
|
|
6199
6193
|
value?: InputMaybe<Scalars['String']['input']>;
|
|
@@ -1,30 +1,11 @@
|
|
|
1
1
|
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
|
2
|
-
import {
|
|
3
|
-
type CreateVariableArgs = {
|
|
4
|
-
value: string;
|
|
5
|
-
name: string;
|
|
6
|
-
visibility: EnvironmentVariableVisibility;
|
|
7
|
-
environments: EnvironmentVariableEnvironment[];
|
|
8
|
-
type: EnvironmentSecretType;
|
|
9
|
-
isGlobal?: boolean;
|
|
10
|
-
fileName?: string;
|
|
11
|
-
};
|
|
12
|
-
export type EnvironmentVariablePushInput = {
|
|
13
|
-
name: string;
|
|
14
|
-
value: string;
|
|
15
|
-
environments: EnvironmentVariableEnvironment[];
|
|
16
|
-
visibility: EnvironmentVariableVisibility;
|
|
17
|
-
overwrite?: boolean;
|
|
18
|
-
};
|
|
2
|
+
import { CreateEnvironmentVariableInput, CreateSharedEnvironmentVariableInput, EnvironmentVariableFragment, UpdateEnvironmentVariableInput } from '../generated';
|
|
19
3
|
export declare const EnvironmentVariableMutation: {
|
|
20
|
-
createSharedVariableAsync(graphqlClient: ExpoGraphqlClient, input:
|
|
21
|
-
createForAppAsync(graphqlClient: ExpoGraphqlClient, input:
|
|
22
|
-
updateAsync(graphqlClient: ExpoGraphqlClient, input:
|
|
23
|
-
id: string;
|
|
24
|
-
}): Promise<EnvironmentVariableFragment>;
|
|
4
|
+
createSharedVariableAsync(graphqlClient: ExpoGraphqlClient, input: CreateSharedEnvironmentVariableInput, accountId: string): Promise<EnvironmentVariableFragment>;
|
|
5
|
+
createForAppAsync(graphqlClient: ExpoGraphqlClient, input: CreateEnvironmentVariableInput, appId: string): Promise<EnvironmentVariableFragment>;
|
|
6
|
+
updateAsync(graphqlClient: ExpoGraphqlClient, input: UpdateEnvironmentVariableInput): Promise<EnvironmentVariableFragment>;
|
|
25
7
|
deleteAsync(graphqlClient: ExpoGraphqlClient, id: string): Promise<{
|
|
26
8
|
id: string;
|
|
27
9
|
}>;
|
|
28
|
-
createBulkEnvironmentVariablesForAppAsync(graphqlClient: ExpoGraphqlClient, input:
|
|
10
|
+
createBulkEnvironmentVariablesForAppAsync(graphqlClient: ExpoGraphqlClient, input: CreateEnvironmentVariableInput[], appId: string): Promise<boolean>;
|
|
29
11
|
};
|
|
30
|
-
export {};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { FingerprintSource } from '@expo/eas-build-job';
|
|
2
1
|
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
|
3
|
-
import { FingerprintFragment } from '../generated';
|
|
2
|
+
import { FingerprintFragment, FingerprintSourceInput } from '../generated';
|
|
4
3
|
export declare const FingerprintMutation: {
|
|
5
4
|
createFingerprintAsync(graphqlClient: ExpoGraphqlClient, appId: string, fingerprintData: {
|
|
6
5
|
hash: string;
|
|
7
|
-
source?:
|
|
6
|
+
source?: FingerprintSourceInput;
|
|
8
7
|
}): Promise<FingerprintFragment>;
|
|
9
8
|
};
|
package/build/log.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type PackageManager = 'npm' | 'yarn' | 'pnpm';
|
|
2
|
+
export declare function promptForPackageManagerAsync(): Promise<PackageManager>;
|
|
3
|
+
export declare function installDependenciesAsync({ projectDir, packageManager, }: {
|
|
2
4
|
projectDir: string;
|
|
5
|
+
packageManager?: PackageManager;
|
|
3
6
|
}): Promise<void>;
|