eas-cli 16.31.0 → 18.0.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 +116 -91
- package/build/branch/queries.js +6 -1
- package/build/build/createContext.js +5 -3
- package/build/build/queries.js +13 -1
- package/build/build/runBuildAndSubmit.js +2 -0
- package/build/build/utils/devClient.d.ts +1 -0
- package/build/build/utils/devClient.js +3 -2
- package/build/build/utils/repository.js +3 -3
- package/build/build/utils/resourceClass.d.ts +1 -1
- package/build/build/utils/resourceClass.js +2 -2
- package/build/channel/queries.js +10 -1
- package/build/commandUtils/context/contextUtils/createGraphqlClient.d.ts +1 -5
- package/build/commandUtils/new/templates/AGENTS.md +0 -1
- package/build/commandUtils/usageUtils.d.ts +58 -0
- package/build/commandUtils/usageUtils.js +204 -0
- package/build/commands/account/login.d.ts +1 -0
- package/build/commands/account/login.js +7 -2
- package/build/commands/account/usage.d.ts +16 -0
- package/build/commands/account/usage.js +326 -0
- package/build/commands/env/create.js +5 -2
- package/build/commands/env/list.js +1 -5
- package/build/commands/update/index.js +2 -2
- package/build/commands/upload.js +3 -3
- package/build/credentials/ios/api/graphql/mutations/AppleDistributionCertificateMutation.js +1 -3
- package/build/devices/queries.js +23 -2
- package/build/graphql/generated.d.ts +1063 -39
- package/build/graphql/generated.js +78 -11
- package/build/graphql/mutations/EnvironmentVariableMutation.js +1 -4
- package/build/graphql/mutations/PublishMutation.js +1 -4
- package/build/graphql/mutations/UserPreferencesMutation.js +1 -3
- package/build/graphql/mutations/WorkflowRevisionMutation.js +2 -10
- package/build/graphql/queries/AccountQuery.d.ts +15 -0
- package/build/graphql/queries/AccountQuery.js +197 -0
- package/build/graphql/queries/AppVersionQuery.js +1 -5
- package/build/graphql/types/Account.d.ts +11 -0
- package/build/graphql/types/Account.js +142 -1
- package/build/log.js +1 -1
- package/build/ora.js +1 -1
- package/build/project/discourageExpoGoForProdAsync.d.ts +4 -0
- package/build/project/discourageExpoGoForProdAsync.js +46 -0
- package/build/rollout/actions/ManageRollout.js +5 -1
- package/build/rollout/actions/SelectRuntime.js +10 -1
- package/build/update/queries.js +28 -2
- package/build/user/SessionManager.d.ts +3 -2
- package/build/user/SessionManager.js +6 -6
- package/build/user/expoBrowserAuthFlowLauncher.d.ts +3 -0
- package/build/user/{expoSsoLauncher.js → expoBrowserAuthFlowLauncher.js} +21 -47
- package/build/user/fetchSessionSecretAndUserFromBrowserAuthFlow.d.ts +7 -0
- package/build/user/fetchSessionSecretAndUserFromBrowserAuthFlow.js +14 -0
- package/build/utils/prompts.js +3 -3
- package/build/utils/usage/checkForOverages.js +6 -4
- package/build/worker/mutations.js +2 -8
- package/oclif.manifest.json +40 -1
- package/package.json +60 -63
- package/build/graphql/queries/AccountUsageQuery.d.ts +0 -5
- package/build/graphql/queries/AccountUsageQuery.js +0 -40
- package/build/user/expoSsoLauncher.d.ts +0 -3
- package/build/user/fetchSessionSecretAndSsoUser.d.ts +0 -5
- package/build/user/fetchSessionSecretAndSsoUser.js +0 -17
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.discourageExpoGoForProdAsync = discourageExpoGoForProdAsync;
|
|
4
|
+
exports.detectExpoGoProdBuildAsync = detectExpoGoProdBuildAsync;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
7
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
8
|
+
const getenv_1 = tslib_1.__importDefault(require("getenv"));
|
|
9
|
+
const workflow_1 = require("./workflow");
|
|
10
|
+
const devClient_1 = require("../build/utils/devClient");
|
|
11
|
+
const log_1 = tslib_1.__importStar(require("../log"));
|
|
12
|
+
const suppressionEnvVarName = 'EAS_BUILD_NO_EXPO_GO_WARNING';
|
|
13
|
+
async function discourageExpoGoForProdAsync(buildProfiles, projectDir, vcsClient) {
|
|
14
|
+
try {
|
|
15
|
+
const isExpoGoProdBuild = await detectExpoGoProdBuildAsync(buildProfiles, projectDir, vcsClient);
|
|
16
|
+
if (!isExpoGoProdBuild) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
log_1.default.newLine();
|
|
20
|
+
log_1.default.warn(`⚠️ Detected that your app uses Expo Go for development, this is not recommended when building production apps.`);
|
|
21
|
+
log_1.default.warn((0, log_1.learnMore)('https://expo.fyi/why-not-build-expo-go-for-production', {
|
|
22
|
+
dim: false,
|
|
23
|
+
}));
|
|
24
|
+
log_1.default.warn(chalk_1.default.dim(`To suppress this warning, set ${chalk_1.default.bold(`${suppressionEnvVarName}=true`)}.`));
|
|
25
|
+
log_1.default.newLine();
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
log_1.default.isDebug && log_1.default.warn('Error detecting whether Expo Go is used:', err);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function detectExpoGoProdBuildAsync(buildProfiles, projectDir, vcsClient) {
|
|
32
|
+
const shouldSuppressWarning = getenv_1.default.boolish(suppressionEnvVarName, false);
|
|
33
|
+
const isProductionBuild = buildProfiles?.map(it => it.profileName).includes('production');
|
|
34
|
+
if (shouldSuppressWarning || !isProductionBuild) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const hasExpoDevClient = (0, devClient_1.isExpoDevClientInstalled)(projectDir);
|
|
38
|
+
if (hasExpoDevClient) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return await checkIfManagedWorkflowAsync(projectDir, vcsClient);
|
|
42
|
+
}
|
|
43
|
+
async function checkIfManagedWorkflowAsync(projectDir, vcsClient) {
|
|
44
|
+
const workflows = await (0, workflow_1.resolveWorkflowPerPlatformAsync)(projectDir, vcsClient);
|
|
45
|
+
return workflows.android === eas_build_job_1.Workflow.MANAGED && workflows.ios === eas_build_job_1.Workflow.MANAGED;
|
|
46
|
+
}
|
|
@@ -30,7 +30,11 @@ class ManageRollout {
|
|
|
30
30
|
async runAsync(ctx) {
|
|
31
31
|
const { nonInteractive } = ctx;
|
|
32
32
|
if (nonInteractive) {
|
|
33
|
-
throw new Error(`
|
|
33
|
+
throw new Error(`Rollout selection cannot be run in non-interactive mode. Available actions: ${[
|
|
34
|
+
ManageRolloutActions.EDIT,
|
|
35
|
+
ManageRolloutActions.END,
|
|
36
|
+
ManageRolloutActions.VIEW,
|
|
37
|
+
].join(', ')}.`);
|
|
34
38
|
}
|
|
35
39
|
const channelObject = await this.getChannelObjectAsync(ctx);
|
|
36
40
|
(0, utils_1.printRollout)(channelObject);
|
|
@@ -43,7 +43,16 @@ class SelectRuntime {
|
|
|
43
43
|
const { nonInteractive, graphqlClient, app } = ctx;
|
|
44
44
|
const { projectId } = app;
|
|
45
45
|
if (nonInteractive) {
|
|
46
|
-
|
|
46
|
+
const runtimes = await this.getNewestRuntimeAsync(graphqlClient, {
|
|
47
|
+
appId: projectId,
|
|
48
|
+
branchName: this.branchInfo.name,
|
|
49
|
+
anotherBranchIdToIntersectRuntimesBy: this.options.anotherBranchToIntersectRuntimesBy?.id,
|
|
50
|
+
});
|
|
51
|
+
if (runtimes.edges.length === 0) {
|
|
52
|
+
throw new utils_1.NonInteractiveError(`No ${this.printedType} versions found on branch "${this.branchInfo.name}".`);
|
|
53
|
+
}
|
|
54
|
+
const runtimeList = runtimes.edges.map(e => e.node.version).join('\n ');
|
|
55
|
+
throw new utils_1.NonInteractiveError(`Runtime selection cannot be run in non-interactive mode. Available ${this.printedType} versions on branch "${this.branchInfo.name}":\n ${runtimeList}`);
|
|
47
56
|
}
|
|
48
57
|
const newestRuntimeConnection = await this.getNewestRuntimeAsync(graphqlClient, {
|
|
49
58
|
appId: projectId,
|
package/build/update/queries.js
CHANGED
|
@@ -78,7 +78,16 @@ async function listAndRenderUpdateGroupsOnBranchAsync(graphqlClient, { projectId
|
|
|
78
78
|
}
|
|
79
79
|
async function selectRuntimeAndGetLatestUpdateGroupForEachPublishPlatformOnBranchAsync(graphqlClient, { projectId, branchName, paginatedQueryOptions, }) {
|
|
80
80
|
if (paginatedQueryOptions.nonInteractive) {
|
|
81
|
-
|
|
81
|
+
const runtimes = await RuntimeQuery_1.RuntimeQuery.getRuntimesOnBranchAsync(graphqlClient, {
|
|
82
|
+
appId: projectId,
|
|
83
|
+
name: branchName,
|
|
84
|
+
first: exports.RUNTIME_VERSIONS_LIMIT,
|
|
85
|
+
});
|
|
86
|
+
if (runtimes.edges.length === 0) {
|
|
87
|
+
throw new Error(`No runtime versions found on branch "${branchName}".`);
|
|
88
|
+
}
|
|
89
|
+
const runtimeList = runtimes.edges.map(e => e.node.version).join('\n ');
|
|
90
|
+
throw new Error(`Unable to select a runtime in non-interactive mode. Available runtime versions on branch "${branchName}":\n ${runtimeList}`);
|
|
82
91
|
}
|
|
83
92
|
const runtimeVersion = await selectRuntimeOnBranchAsync(graphqlClient, {
|
|
84
93
|
appId: projectId,
|
|
@@ -112,7 +121,24 @@ async function selectRuntimeAndGetLatestUpdateGroupForEachPublishPlatformOnBranc
|
|
|
112
121
|
}
|
|
113
122
|
async function selectUpdateGroupOnBranchAsync(graphqlClient, { projectId, branchName, paginatedQueryOptions, }) {
|
|
114
123
|
if (paginatedQueryOptions.nonInteractive) {
|
|
115
|
-
|
|
124
|
+
const updateGroups = await queryUpdateGroupsOnBranchAsync(graphqlClient, {
|
|
125
|
+
appId: projectId,
|
|
126
|
+
branchName,
|
|
127
|
+
limit: paginatedQueryOptions.limit ?? exports.UPDATE_GROUPS_LIMIT,
|
|
128
|
+
offset: paginatedQueryOptions.offset,
|
|
129
|
+
});
|
|
130
|
+
if (updateGroups.length === 0) {
|
|
131
|
+
throw new Error(`No update groups found on branch "${branchName}".`);
|
|
132
|
+
}
|
|
133
|
+
const updateList = updateGroups
|
|
134
|
+
.map(group => {
|
|
135
|
+
const first = group[0];
|
|
136
|
+
return first
|
|
137
|
+
? `${first.group} (runtime: ${first.runtimeVersion}, message: ${first.message ?? 'N/A'})`
|
|
138
|
+
: 'unknown';
|
|
139
|
+
})
|
|
140
|
+
.join('\n ');
|
|
141
|
+
throw new Error(`Unable to select an update in non-interactive mode. Use the --group flag to specify the update group. Available update groups on branch "${branchName}":\n ${updateList}`);
|
|
116
142
|
}
|
|
117
143
|
const updateGroup = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
|
|
118
144
|
limit: paginatedQueryOptions.limit ?? exports.UPDATE_GROUPS_LIMIT,
|
|
@@ -39,12 +39,13 @@ export default class SessionManager {
|
|
|
39
39
|
*
|
|
40
40
|
* @deprecated Should not be used outside of context functions, except in the AccountLogin command.
|
|
41
41
|
*/
|
|
42
|
-
showLoginPromptAsync({ nonInteractive, printNewLine, sso, }?: {
|
|
42
|
+
showLoginPromptAsync({ nonInteractive, printNewLine, sso, browser, }?: {
|
|
43
43
|
nonInteractive?: boolean | undefined;
|
|
44
44
|
printNewLine?: boolean | undefined;
|
|
45
45
|
sso?: boolean | undefined;
|
|
46
|
+
browser?: boolean | undefined;
|
|
46
47
|
}): Promise<void>;
|
|
47
|
-
private
|
|
48
|
+
private browserLoginAsync;
|
|
48
49
|
private loginAsync;
|
|
49
50
|
/**
|
|
50
51
|
* Prompt for an OTP with the option to cancel the question by answering empty (pressing return key).
|
|
@@ -7,8 +7,8 @@ const core_1 = require("@oclif/core");
|
|
|
7
7
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
8
8
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
9
9
|
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
10
|
-
const fetchSessionSecretAndSsoUser_1 = require("./fetchSessionSecretAndSsoUser");
|
|
11
10
|
const fetchSessionSecretAndUser_1 = require("./fetchSessionSecretAndUser");
|
|
11
|
+
const fetchSessionSecretAndUserFromBrowserAuthFlow_1 = require("./fetchSessionSecretAndUserFromBrowserAuthFlow");
|
|
12
12
|
const ApiV2Error_1 = require("../ApiV2Error");
|
|
13
13
|
const api_1 = require("../api");
|
|
14
14
|
const createGraphqlClient_1 = require("../commandUtils/context/contextUtils/createGraphqlClient");
|
|
@@ -102,15 +102,15 @@ class SessionManager {
|
|
|
102
102
|
*
|
|
103
103
|
* @deprecated Should not be used outside of context functions, except in the AccountLogin command.
|
|
104
104
|
*/
|
|
105
|
-
async showLoginPromptAsync({ nonInteractive = false, printNewLine = false, sso = false, } = {}) {
|
|
105
|
+
async showLoginPromptAsync({ nonInteractive = false, printNewLine = false, sso = false, browser = false, } = {}) {
|
|
106
106
|
if (nonInteractive) {
|
|
107
107
|
core_1.Errors.error(`Either log in with ${chalk_1.default.bold('eas login')} or set the ${chalk_1.default.bold('EXPO_TOKEN')} environment variable if you're using EAS CLI on CI (${(0, log_1.learnMore)('https://docs.expo.dev/accounts/programmatic-access/', { dim: false })})`);
|
|
108
108
|
}
|
|
109
109
|
if (printNewLine) {
|
|
110
110
|
log_1.default.newLine();
|
|
111
111
|
}
|
|
112
|
-
if (sso) {
|
|
113
|
-
await this.
|
|
112
|
+
if (sso || browser) {
|
|
113
|
+
await this.browserLoginAsync({ sso });
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
log_1.default.log(`Log in to EAS with email or username (exit and run ${chalk_1.default.bold('eas login --help')} to see other login options)`);
|
|
@@ -141,8 +141,8 @@ class SessionManager {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
|
-
async
|
|
145
|
-
const { sessionSecret, id, username } = await (0,
|
|
144
|
+
async browserLoginAsync({ sso = false }) {
|
|
145
|
+
const { sessionSecret, id, username } = await (0, fetchSessionSecretAndUserFromBrowserAuthFlow_1.fetchSessionSecretAndUserFromBrowserAuthFlowAsync)({ sso });
|
|
146
146
|
await this.setSessionAsync({
|
|
147
147
|
sessionSecret,
|
|
148
148
|
userId: id,
|
|
@@ -6,55 +6,37 @@ const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
|
6
6
|
const better_opn_1 = tslib_1.__importDefault(require("better-opn"));
|
|
7
7
|
const http_1 = tslib_1.__importDefault(require("http"));
|
|
8
8
|
const querystring_1 = tslib_1.__importDefault(require("querystring"));
|
|
9
|
+
const api_1 = require("../api");
|
|
9
10
|
const log_1 = tslib_1.__importDefault(require("../log"));
|
|
10
|
-
|
|
11
|
-
<!DOCTYPE html>
|
|
12
|
-
<html lang="en">
|
|
13
|
-
<head>
|
|
14
|
-
<title>Expo SSO Login</title>
|
|
15
|
-
<meta charset="utf-8">
|
|
16
|
-
<style type="text/css">
|
|
17
|
-
html {
|
|
18
|
-
margin: 0;
|
|
19
|
-
padding: 0
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
body {
|
|
23
|
-
background-color: #fff;
|
|
24
|
-
font-family: Tahoma,Verdana;
|
|
25
|
-
font-size: 16px;
|
|
26
|
-
color: #000;
|
|
27
|
-
max-width: 100%;
|
|
28
|
-
box-sizing: border-box;
|
|
29
|
-
padding: .5rem;
|
|
30
|
-
margin: 1em;
|
|
31
|
-
overflow-wrap: break-word
|
|
32
|
-
}
|
|
33
|
-
</style>
|
|
34
|
-
</head>
|
|
35
|
-
<body>
|
|
36
|
-
SSO login complete. You may now close this tab and return to the command prompt.
|
|
37
|
-
</body>
|
|
38
|
-
</html>`;
|
|
39
|
-
async function getSessionUsingBrowserAuthFlowAsync({ expoWebsiteUrl, }) {
|
|
11
|
+
async function getSessionUsingBrowserAuthFlowAsync({ sso = false }) {
|
|
40
12
|
const scheme = 'http';
|
|
41
13
|
const hostname = 'localhost';
|
|
42
14
|
const path = '/auth/callback';
|
|
43
|
-
const
|
|
44
|
-
|
|
15
|
+
const expoWebsiteUrl = (0, api_1.getExpoWebsiteBaseUrl)();
|
|
16
|
+
const buildExpoLoginUrl = (port, sso) => {
|
|
17
|
+
const params = querystring_1.default.stringify({
|
|
18
|
+
confirm_account: true,
|
|
45
19
|
app_redirect_uri: `${scheme}://${hostname}:${port}${path}`,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
return `${expoWebsiteUrl}/sso-login?${params}`;
|
|
20
|
+
});
|
|
21
|
+
return `${expoWebsiteUrl}${sso ? '/sso-login' : '/login'}?${params}`;
|
|
49
22
|
};
|
|
50
23
|
// Start server and begin auth flow
|
|
51
24
|
const executeAuthFlow = () => {
|
|
52
25
|
return new Promise(async (resolve, reject) => {
|
|
53
26
|
const connections = new Set();
|
|
54
27
|
const server = http_1.default.createServer((request, response) => {
|
|
28
|
+
const redirectAndCleanup = (result) => {
|
|
29
|
+
const redirectUrl = `${expoWebsiteUrl}/oauth/expo-cli?result=${result}`;
|
|
30
|
+
response.writeHead(302, { Location: redirectUrl });
|
|
31
|
+
response.end();
|
|
32
|
+
server.close();
|
|
33
|
+
for (const connection of connections) {
|
|
34
|
+
connection.destroy();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
55
37
|
try {
|
|
56
38
|
if (!(request.method === 'GET' && request.url?.includes('/auth/callback'))) {
|
|
57
|
-
throw new Error('Unexpected
|
|
39
|
+
throw new Error('Unexpected login response.');
|
|
58
40
|
}
|
|
59
41
|
const url = new URL(request.url, `http:${request.headers.host}`);
|
|
60
42
|
const sessionSecret = url.searchParams.get('session_secret');
|
|
@@ -62,27 +44,19 @@ async function getSessionUsingBrowserAuthFlowAsync({ expoWebsiteUrl, }) {
|
|
|
62
44
|
throw new Error('Request missing session_secret search parameter.');
|
|
63
45
|
}
|
|
64
46
|
resolve(sessionSecret);
|
|
65
|
-
|
|
66
|
-
response.write(successBody);
|
|
67
|
-
response.end();
|
|
47
|
+
redirectAndCleanup('success');
|
|
68
48
|
}
|
|
69
49
|
catch (error) {
|
|
50
|
+
redirectAndCleanup('error');
|
|
70
51
|
reject(error);
|
|
71
52
|
}
|
|
72
|
-
finally {
|
|
73
|
-
server.close();
|
|
74
|
-
// Ensure that the server shuts down
|
|
75
|
-
for (const connection of connections) {
|
|
76
|
-
connection.destroy();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
53
|
});
|
|
80
54
|
server.listen(0, hostname, () => {
|
|
81
55
|
log_1.default.log('Waiting for browser login...');
|
|
82
56
|
const address = server.address();
|
|
83
57
|
(0, assert_1.default)(address !== null && typeof address === 'object', 'Server address and port should be set after listening has begun');
|
|
84
58
|
const port = address.port;
|
|
85
|
-
const authorizeUrl =
|
|
59
|
+
const authorizeUrl = buildExpoLoginUrl(port, sso);
|
|
86
60
|
log_1.default.log(`If your browser doesn't automatically open, visit this link to log in: ${authorizeUrl}`);
|
|
87
61
|
void (0, better_opn_1.default)(authorizeUrl);
|
|
88
62
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchSessionSecretAndUserFromBrowserAuthFlowAsync = fetchSessionSecretAndUserFromBrowserAuthFlowAsync;
|
|
4
|
+
const expoBrowserAuthFlowLauncher_1 = require("./expoBrowserAuthFlowLauncher");
|
|
5
|
+
const fetchUser_1 = require("./fetchUser");
|
|
6
|
+
async function fetchSessionSecretAndUserFromBrowserAuthFlowAsync({ sso = false }) {
|
|
7
|
+
const sessionSecret = await (0, expoBrowserAuthFlowLauncher_1.getSessionUsingBrowserAuthFlowAsync)({ sso });
|
|
8
|
+
const userData = await (0, fetchUser_1.fetchUserAsync)({ sessionSecret });
|
|
9
|
+
return {
|
|
10
|
+
sessionSecret,
|
|
11
|
+
id: userData.id,
|
|
12
|
+
username: userData.username,
|
|
13
|
+
};
|
|
14
|
+
}
|
package/build/utils/prompts.js
CHANGED
|
@@ -28,7 +28,7 @@ async function getProjectEnvironmentVariableEnvironmentsAsync(graphqlClient, pro
|
|
|
28
28
|
const CUSTOM_ENVIRONMENT_VALUE = '~~CUSTOM~~';
|
|
29
29
|
async function promptVariableTypeAsync(nonInteractive, initialType) {
|
|
30
30
|
if (nonInteractive) {
|
|
31
|
-
throw new Error('The `--type` flag must be set when running in `--non-interactive` mode.');
|
|
31
|
+
throw new Error('The `--type` flag must be set when running in `--non-interactive` mode. Valid values: string, file.');
|
|
32
32
|
}
|
|
33
33
|
const options = [
|
|
34
34
|
{
|
|
@@ -75,7 +75,7 @@ async function promptCustomEnvironmentAsync() {
|
|
|
75
75
|
}
|
|
76
76
|
async function promptVariableVisibilityAsync(nonInteractive, selectedVisibility) {
|
|
77
77
|
if (nonInteractive) {
|
|
78
|
-
throw new Error('The `--visibility` flag must be set when running in `--non-interactive` mode.');
|
|
78
|
+
throw new Error('The `--visibility` flag must be set when running in `--non-interactive` mode. Valid values: plaintext, sensitive, secret.');
|
|
79
79
|
}
|
|
80
80
|
return await (0, prompts_1.selectAsync)('Select visibility:', [
|
|
81
81
|
{
|
|
@@ -97,7 +97,7 @@ async function promptVariableVisibilityAsync(nonInteractive, selectedVisibility)
|
|
|
97
97
|
}
|
|
98
98
|
async function promptVariableEnvironmentAsync({ nonInteractive, selectedEnvironments, multiple = false, canEnterCustomEnvironment = false, graphqlClient, projectId, }) {
|
|
99
99
|
if (nonInteractive) {
|
|
100
|
-
throw new Error(
|
|
100
|
+
throw new Error(`The \`--environment\` flag must be set when running in \`--non-interactive\` mode. Default environments: ${DEFAULT_ENVIRONMENTS.join(', ')}.`);
|
|
101
101
|
}
|
|
102
102
|
let allEnvironments = DEFAULT_ENVIRONMENTS;
|
|
103
103
|
if (graphqlClient && projectId) {
|
|
@@ -6,13 +6,13 @@ exports.createProgressBar = createProgressBar;
|
|
|
6
6
|
exports.displayOverageWarning = displayOverageWarning;
|
|
7
7
|
const tslib_1 = require("tslib");
|
|
8
8
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
9
|
-
const
|
|
9
|
+
const AccountQuery_1 = require("../../graphql/queries/AccountQuery");
|
|
10
10
|
const log_1 = tslib_1.__importStar(require("../../log"));
|
|
11
11
|
const THRESHOLD_PERCENT = 85;
|
|
12
12
|
async function maybeWarnAboutUsageOveragesAsync({ graphqlClient, accountId, }) {
|
|
13
13
|
try {
|
|
14
14
|
const currentDate = new Date();
|
|
15
|
-
const { name, subscription, usageMetrics: { EAS_BUILD }, } = await
|
|
15
|
+
const { name, subscription, usageMetrics: { EAS_BUILD }, } = await AccountQuery_1.AccountQuery.getUsageForOverageWarningAsync(graphqlClient, accountId, currentDate);
|
|
16
16
|
const planMetric = EAS_BUILD?.planMetrics?.[0];
|
|
17
17
|
if (!planMetric || !subscription) {
|
|
18
18
|
return;
|
|
@@ -42,8 +42,10 @@ function createProgressBar(percentUsed, width = 30) {
|
|
|
42
42
|
return `${filled}${empty}`;
|
|
43
43
|
}
|
|
44
44
|
function displayOverageWarning({ percentUsed, hasFreePlan, name, }) {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const message = chalk_1.default.bold(`You've used ${percentUsed}% of your included build credits for this month.`);
|
|
46
|
+
// Don't show progress bar at 100% - it's redundant when the limit is reached
|
|
47
|
+
const progressBar = percentUsed < 100 ? ' ' + createProgressBar(percentUsed) : '';
|
|
48
|
+
log_1.default.warn(message + progressBar);
|
|
47
49
|
const billingUrl = `https://expo.dev/accounts/${name}/settings/billing`;
|
|
48
50
|
const warning = hasFreePlan
|
|
49
51
|
? "You won't be able to start new builds once you reach the limit. " +
|
|
@@ -14,10 +14,7 @@ exports.DeploymentsMutation = {
|
|
|
14
14
|
.mutation((0, graphql_tag_1.default) `
|
|
15
15
|
mutation createDeploymentUrlMutation($appId: ID!, $deploymentIdentifier: ID) {
|
|
16
16
|
deployments {
|
|
17
|
-
createSignedDeploymentUrl(
|
|
18
|
-
appId: $appId
|
|
19
|
-
deploymentIdentifier: $deploymentIdentifier
|
|
20
|
-
) {
|
|
17
|
+
createSignedDeploymentUrl(appId: $appId, deploymentIdentifier: $deploymentIdentifier) {
|
|
21
18
|
pendingWorkerDeploymentId
|
|
22
19
|
deploymentIdentifier
|
|
23
20
|
url
|
|
@@ -95,10 +92,7 @@ exports.DeploymentsMutation = {
|
|
|
95
92
|
.mutation((0, graphql_tag_1.default) `
|
|
96
93
|
mutation DeleteDeployment($appId: ID!, $deploymentIdentifier: ID!) {
|
|
97
94
|
deployments {
|
|
98
|
-
deleteWorkerDeploymentByIdentifier(
|
|
99
|
-
appId: $appId
|
|
100
|
-
deploymentIdentifier: $deploymentIdentifier
|
|
101
|
-
) {
|
|
95
|
+
deleteWorkerDeploymentByIdentifier(appId: $appId, deploymentIdentifier: $deploymentIdentifier) {
|
|
102
96
|
id
|
|
103
97
|
deploymentIdentifier
|
|
104
98
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "18.0.1",
|
|
3
3
|
"commands": {
|
|
4
4
|
"analytics": {
|
|
5
5
|
"id": "analytics",
|
|
@@ -298,6 +298,13 @@
|
|
|
298
298
|
"char": "s",
|
|
299
299
|
"description": "Login with SSO",
|
|
300
300
|
"allowNo": false
|
|
301
|
+
},
|
|
302
|
+
"browser": {
|
|
303
|
+
"name": "browser",
|
|
304
|
+
"type": "boolean",
|
|
305
|
+
"char": "b",
|
|
306
|
+
"description": "Login with your browser",
|
|
307
|
+
"allowNo": false
|
|
301
308
|
}
|
|
302
309
|
},
|
|
303
310
|
"args": {},
|
|
@@ -322,6 +329,38 @@
|
|
|
322
329
|
"sessionManager": {}
|
|
323
330
|
}
|
|
324
331
|
},
|
|
332
|
+
"account:usage": {
|
|
333
|
+
"id": "account:usage",
|
|
334
|
+
"description": "view account usage and billing for the current cycle",
|
|
335
|
+
"strict": true,
|
|
336
|
+
"pluginName": "eas-cli",
|
|
337
|
+
"pluginAlias": "eas-cli",
|
|
338
|
+
"pluginType": "core",
|
|
339
|
+
"aliases": [],
|
|
340
|
+
"flags": {
|
|
341
|
+
"json": {
|
|
342
|
+
"name": "json",
|
|
343
|
+
"type": "boolean",
|
|
344
|
+
"description": "Enable JSON output, non-JSON messages will be printed to stderr.",
|
|
345
|
+
"allowNo": false
|
|
346
|
+
},
|
|
347
|
+
"non-interactive": {
|
|
348
|
+
"name": "non-interactive",
|
|
349
|
+
"type": "boolean",
|
|
350
|
+
"description": "Run the command in non-interactive mode.",
|
|
351
|
+
"allowNo": false
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
"args": {
|
|
355
|
+
"ACCOUNT_NAME": {
|
|
356
|
+
"name": "ACCOUNT_NAME",
|
|
357
|
+
"description": "Account name to view usage for. If not provided, the account will be selected interactively (or defaults to the only account if there is just one)"
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
"contextDefinition": {
|
|
361
|
+
"loggedIn": {}
|
|
362
|
+
}
|
|
363
|
+
},
|
|
325
364
|
"account:view": {
|
|
326
365
|
"id": "account:view",
|
|
327
366
|
"description": "show the username you are logged in as",
|