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,326 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
5
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
6
|
+
const flags_1 = require("../../commandUtils/flags");
|
|
7
|
+
const usageUtils_1 = require("../../commandUtils/usageUtils");
|
|
8
|
+
const generated_1 = require("../../graphql/generated");
|
|
9
|
+
const AccountQuery_1 = require("../../graphql/queries/AccountQuery");
|
|
10
|
+
const log_1 = tslib_1.__importStar(require("../../log"));
|
|
11
|
+
const ora_1 = require("../../ora");
|
|
12
|
+
const prompts_1 = require("../../prompts");
|
|
13
|
+
const json_1 = require("../../utils/json");
|
|
14
|
+
const checkForOverages_1 = require("../../utils/usage/checkForOverages");
|
|
15
|
+
const MiB = 1024 * 1024;
|
|
16
|
+
const GiB = 1024 * MiB;
|
|
17
|
+
const TiB = 1024 * GiB;
|
|
18
|
+
function formatBytes(bytes) {
|
|
19
|
+
if (bytes >= TiB) {
|
|
20
|
+
return { value: bytes / TiB, unit: 'TiB' };
|
|
21
|
+
}
|
|
22
|
+
else if (bytes >= GiB) {
|
|
23
|
+
return { value: bytes / GiB, unit: 'GiB' };
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return { value: bytes / MiB, unit: 'MiB' };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function formatBytesDisplay(bytes) {
|
|
30
|
+
const { value, unit } = formatBytes(bytes);
|
|
31
|
+
return `${(0, usageUtils_1.formatNumber)(value, value < 10 ? 2 : value < 100 ? 1 : 0)} ${unit}`;
|
|
32
|
+
}
|
|
33
|
+
function displayBandwidthMetric(metric, indent = ' ') {
|
|
34
|
+
const progressBar = (0, checkForOverages_1.createProgressBar)(metric.percentUsed, 20);
|
|
35
|
+
const percentStr = `${metric.percentUsed}%`;
|
|
36
|
+
const planUsageStr = `${formatBytesDisplay(metric.planValue)}/${formatBytesDisplay(metric.limit)}`;
|
|
37
|
+
let color = chalk_1.default.green;
|
|
38
|
+
if (metric.percentUsed >= 100) {
|
|
39
|
+
color = chalk_1.default.red;
|
|
40
|
+
}
|
|
41
|
+
else if (metric.percentUsed >= 85) {
|
|
42
|
+
color = chalk_1.default.yellow;
|
|
43
|
+
}
|
|
44
|
+
log_1.default.log(`${indent}${metric.name} (plan): ${color(planUsageStr)}`);
|
|
45
|
+
log_1.default.log(`${indent}${progressBar} ${color(percentStr)}`);
|
|
46
|
+
if (metric.overageValue > 0) {
|
|
47
|
+
log_1.default.log(`${indent}${metric.name} (overage): ${chalk_1.default.red(formatBytesDisplay(metric.overageValue))} (${(0, usageUtils_1.formatCurrency)(metric.overageCost)})`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function displayMetric(metric, indent = ' ') {
|
|
51
|
+
const progressBar = (0, checkForOverages_1.createProgressBar)(metric.percentUsed, 20);
|
|
52
|
+
const percentStr = `${metric.percentUsed}%`;
|
|
53
|
+
const planUsageStr = `${(0, usageUtils_1.formatNumber)(metric.planValue)}/${(0, usageUtils_1.formatNumber)(metric.limit)} ${metric.unit ?? ''}`;
|
|
54
|
+
let color = chalk_1.default.green;
|
|
55
|
+
if (metric.percentUsed >= 100) {
|
|
56
|
+
color = chalk_1.default.red;
|
|
57
|
+
}
|
|
58
|
+
else if (metric.percentUsed >= 85) {
|
|
59
|
+
color = chalk_1.default.yellow;
|
|
60
|
+
}
|
|
61
|
+
log_1.default.log(`${indent}${metric.name} (plan): ${color(planUsageStr)}`);
|
|
62
|
+
log_1.default.log(`${indent}${progressBar} ${color(percentStr)}`);
|
|
63
|
+
if (metric.overageValue > 0) {
|
|
64
|
+
log_1.default.log(`${indent}${metric.name} (overage): ${chalk_1.default.red(`${(0, usageUtils_1.formatNumber)(metric.overageValue)} ${metric.unit ?? ''}`)} (${(0, usageUtils_1.formatCurrency)(metric.overageCost)})`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function billingUrl(accountName) {
|
|
68
|
+
return `https://expo.dev/accounts/${accountName}/settings/billing`;
|
|
69
|
+
}
|
|
70
|
+
function displayUsage(data, usageData) {
|
|
71
|
+
const subscription = usageData.subscription;
|
|
72
|
+
log_1.default.newLine();
|
|
73
|
+
log_1.default.log(chalk_1.default.bold(`Account: ${data.accountName}`));
|
|
74
|
+
log_1.default.log(`Plan: ${data.subscriptionPlan}`);
|
|
75
|
+
if (subscription?.concurrencies) {
|
|
76
|
+
log_1.default.log(`Concurrencies: ${subscription.concurrencies.total} total (iOS: ${subscription.concurrencies.ios}, Android: ${subscription.concurrencies.android})`);
|
|
77
|
+
}
|
|
78
|
+
log_1.default.log(`Billing Period: ${(0, usageUtils_1.formatDate)(data.billingPeriod.start)} - ${(0, usageUtils_1.formatDate)(data.billingPeriod.end)}`);
|
|
79
|
+
const periodInfo = (0, usageUtils_1.calculateBillingPeriodInfo)(data);
|
|
80
|
+
log_1.default.log(`Days: ${periodInfo.daysElapsed} elapsed / ${periodInfo.totalDays} total (${periodInfo.daysRemaining} remaining)`);
|
|
81
|
+
log_1.default.newLine();
|
|
82
|
+
log_1.default.log(chalk_1.default.bold.underline('EAS Build'));
|
|
83
|
+
displayMetric(data.builds.total);
|
|
84
|
+
if (data.builds.ios) {
|
|
85
|
+
displayMetric(data.builds.ios, ' ');
|
|
86
|
+
}
|
|
87
|
+
if (data.builds.android) {
|
|
88
|
+
displayMetric(data.builds.android, ' ');
|
|
89
|
+
}
|
|
90
|
+
// Show build counts by platform and worker size
|
|
91
|
+
if (data.builds.countsByPlatformAndSize.length > 0) {
|
|
92
|
+
log_1.default.log(' Breakdown by platform/worker:');
|
|
93
|
+
for (const item of data.builds.countsByPlatformAndSize) {
|
|
94
|
+
const platformName = item.platform === 'ios' ? 'iOS' : 'Android';
|
|
95
|
+
log_1.default.log(` ${platformName} ${item.resourceClass}: ${(0, usageUtils_1.formatNumber)(item.count)} builds`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
log_1.default.newLine();
|
|
99
|
+
log_1.default.log(chalk_1.default.bold.underline('EAS Update'));
|
|
100
|
+
displayMetric(data.updates.mau);
|
|
101
|
+
displayBandwidthMetric(data.updates.bandwidth);
|
|
102
|
+
log_1.default.newLine();
|
|
103
|
+
log_1.default.log(chalk_1.default.bold.underline('Billing'));
|
|
104
|
+
const upcomingInvoice = subscription?.upcomingInvoice;
|
|
105
|
+
// Show subscription addons
|
|
106
|
+
if (subscription?.addons && subscription.addons.length > 0) {
|
|
107
|
+
log_1.default.log(' Addons:');
|
|
108
|
+
for (const addon of subscription.addons) {
|
|
109
|
+
log_1.default.log(` ${addon.name}: ${addon.quantity}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Show upcoming invoice line items
|
|
113
|
+
if (upcomingInvoice?.lineItems && upcomingInvoice.lineItems.length > 0) {
|
|
114
|
+
log_1.default.newLine();
|
|
115
|
+
log_1.default.log(' Upcoming invoice:');
|
|
116
|
+
for (const lineItem of upcomingInvoice.lineItems) {
|
|
117
|
+
const amount = lineItem.amount;
|
|
118
|
+
const amountStr = amount < 0 ? chalk_1.default.green((0, usageUtils_1.formatCurrency)(amount)) : (0, usageUtils_1.formatCurrency)(amount);
|
|
119
|
+
log_1.default.log(` ${lineItem.description}: ${amountStr}`);
|
|
120
|
+
}
|
|
121
|
+
log_1.default.newLine();
|
|
122
|
+
log_1.default.log(` Invoice total: ${chalk_1.default.bold((0, usageUtils_1.formatCurrency)(upcomingInvoice.total))}`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Fallback to the calculated estimate if no upcoming invoice
|
|
126
|
+
if (data.recurringCents !== null && data.recurringCents > 0) {
|
|
127
|
+
log_1.default.log(` Base subscription: ${(0, usageUtils_1.formatCurrency)(data.recurringCents)}`);
|
|
128
|
+
}
|
|
129
|
+
if (data.totalOverageCostCents > 0) {
|
|
130
|
+
log_1.default.log(` Current overages: ${chalk_1.default.yellow((0, usageUtils_1.formatCurrency)(data.totalOverageCostCents))}`);
|
|
131
|
+
if (data.builds.overageCostCents > 0) {
|
|
132
|
+
log_1.default.log(` Build overages: ${(0, usageUtils_1.formatCurrency)(data.builds.overageCostCents)}`);
|
|
133
|
+
// Show breakdown by worker size
|
|
134
|
+
for (const overage of data.builds.overagesByWorkerSize) {
|
|
135
|
+
const platformName = overage.platform === generated_1.AppPlatform.Ios ? 'iOS' : 'Android';
|
|
136
|
+
const sizeName = overage.resourceClass === generated_1.EasBuildBillingResourceClass.Large ? 'large' : 'medium';
|
|
137
|
+
log_1.default.log(` ${platformName} ${sizeName}: ${(0, usageUtils_1.formatNumber)(overage.count)} builds (${(0, usageUtils_1.formatCurrency)(overage.costCents)})`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (data.updates.overageCostCents > 0) {
|
|
141
|
+
log_1.default.log(` Update overages: ${(0, usageUtils_1.formatCurrency)(data.updates.overageCostCents)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
log_1.default.log(` Estimated bill: ${chalk_1.default.bold((0, usageUtils_1.formatCurrency)(data.estimatedBillCents))}`);
|
|
145
|
+
}
|
|
146
|
+
log_1.default.newLine();
|
|
147
|
+
log_1.default.log(chalk_1.default.dim(`View detailed billing: ${(0, log_1.link)(billingUrl(data.accountName))}`));
|
|
148
|
+
}
|
|
149
|
+
class AccountUsage extends EasCommand_1.default {
|
|
150
|
+
static description = 'view account usage and billing for the current cycle';
|
|
151
|
+
static args = [
|
|
152
|
+
{
|
|
153
|
+
name: 'ACCOUNT_NAME',
|
|
154
|
+
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)',
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
static flags = {
|
|
158
|
+
...flags_1.EasJsonOnlyFlag,
|
|
159
|
+
...flags_1.EASNonInteractiveFlag,
|
|
160
|
+
};
|
|
161
|
+
static contextDefinition = {
|
|
162
|
+
...this.ContextOptions.LoggedIn,
|
|
163
|
+
};
|
|
164
|
+
async runAsync() {
|
|
165
|
+
const { args: { ACCOUNT_NAME: accountName }, flags: { json: jsonFlag, 'non-interactive': nonInteractive }, } = await this.parse(AccountUsage);
|
|
166
|
+
// Enable JSON output if either --json or --non-interactive is passed
|
|
167
|
+
const json = jsonFlag || nonInteractive;
|
|
168
|
+
if (json) {
|
|
169
|
+
(0, json_1.enableJsonOutput)();
|
|
170
|
+
}
|
|
171
|
+
const { loggedIn: { graphqlClient, actor }, } = await this.getContextAsync(AccountUsage, {
|
|
172
|
+
nonInteractive,
|
|
173
|
+
});
|
|
174
|
+
// Find the target account
|
|
175
|
+
const defaultAccount = actor.accounts[0];
|
|
176
|
+
let targetAccount;
|
|
177
|
+
const availableAccounts = actor.accounts.map(a => a.name).join(', ');
|
|
178
|
+
if (accountName) {
|
|
179
|
+
// First check if it's one of the user's accounts
|
|
180
|
+
const found = actor.accounts.find(a => a.name === accountName);
|
|
181
|
+
if (found) {
|
|
182
|
+
targetAccount = found;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
// Try to look up the account by name (user may have access via organization)
|
|
186
|
+
try {
|
|
187
|
+
const account = await AccountQuery_1.AccountQuery.getByNameAsync(graphqlClient, accountName);
|
|
188
|
+
if (!account) {
|
|
189
|
+
throw new Error(`Account "${accountName}" not found. Available accounts: ${availableAccounts}`);
|
|
190
|
+
}
|
|
191
|
+
targetAccount = account;
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
throw new Error(`Account "${accountName}" not found or you don't have access. Available accounts: ${availableAccounts}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else if (nonInteractive) {
|
|
199
|
+
throw new Error('ACCOUNT_NAME argument must be provided when running in `--non-interactive` mode.');
|
|
200
|
+
}
|
|
201
|
+
else if (actor.accounts.length === 1) {
|
|
202
|
+
// Only one account, use it directly
|
|
203
|
+
targetAccount = defaultAccount;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// Prompt user to select an account
|
|
207
|
+
targetAccount = await (0, prompts_1.selectAsync)('Select account to view usage for:', actor.accounts.map(account => ({
|
|
208
|
+
title: account.name,
|
|
209
|
+
value: account,
|
|
210
|
+
})), { initial: defaultAccount });
|
|
211
|
+
}
|
|
212
|
+
const spinner = (0, ora_1.ora)(`Fetching usage data for account ${targetAccount.name}`).start();
|
|
213
|
+
try {
|
|
214
|
+
const currentDate = new Date();
|
|
215
|
+
const { start, end } = await AccountQuery_1.AccountQuery.getBillingPeriodAsync(graphqlClient, targetAccount.id, currentDate);
|
|
216
|
+
const usageData = await AccountQuery_1.AccountQuery.getFullUsageAsync(graphqlClient, targetAccount.id, currentDate, start, end);
|
|
217
|
+
log_1.default.debug(JSON.stringify(usageData, null, 2));
|
|
218
|
+
spinner.succeed(`Usage data loaded for ${targetAccount.name}`);
|
|
219
|
+
const displayData = (0, usageUtils_1.extractUsageData)(usageData);
|
|
220
|
+
const periodInfo = (0, usageUtils_1.calculateBillingPeriodInfo)(displayData);
|
|
221
|
+
if (json) {
|
|
222
|
+
const subscription = usageData.subscription;
|
|
223
|
+
(0, json_1.printJsonOnlyOutput)({
|
|
224
|
+
account: {
|
|
225
|
+
name: displayData.accountName,
|
|
226
|
+
plan: displayData.subscriptionPlan,
|
|
227
|
+
concurrencies: subscription?.concurrencies ?? null,
|
|
228
|
+
billingPeriod: {
|
|
229
|
+
start: displayData.billingPeriod.start,
|
|
230
|
+
end: displayData.billingPeriod.end,
|
|
231
|
+
daysElapsed: periodInfo.daysElapsed,
|
|
232
|
+
daysRemaining: periodInfo.daysRemaining,
|
|
233
|
+
totalDays: periodInfo.totalDays,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
builds: {
|
|
237
|
+
plan: {
|
|
238
|
+
used: displayData.builds.total.planValue,
|
|
239
|
+
limit: displayData.builds.total.limit,
|
|
240
|
+
percentUsed: displayData.builds.total.percentUsed,
|
|
241
|
+
},
|
|
242
|
+
overage: {
|
|
243
|
+
count: displayData.builds.total.overageValue,
|
|
244
|
+
costCents: displayData.builds.total.overageCost,
|
|
245
|
+
byWorkerSize: displayData.builds.overagesByWorkerSize.map(o => ({
|
|
246
|
+
platform: o.platform.toLowerCase(),
|
|
247
|
+
resourceClass: o.resourceClass.toLowerCase(),
|
|
248
|
+
count: o.count,
|
|
249
|
+
costCents: o.costCents,
|
|
250
|
+
})),
|
|
251
|
+
},
|
|
252
|
+
byPlatformAndSize: displayData.builds.countsByPlatformAndSize.map(item => ({
|
|
253
|
+
platform: item.platform,
|
|
254
|
+
resourceClass: item.resourceClass,
|
|
255
|
+
count: item.count,
|
|
256
|
+
})),
|
|
257
|
+
ios: displayData.builds.ios
|
|
258
|
+
? {
|
|
259
|
+
plan: {
|
|
260
|
+
used: displayData.builds.ios.planValue,
|
|
261
|
+
limit: displayData.builds.ios.limit,
|
|
262
|
+
percentUsed: displayData.builds.ios.percentUsed,
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
: null,
|
|
266
|
+
android: displayData.builds.android
|
|
267
|
+
? {
|
|
268
|
+
plan: {
|
|
269
|
+
used: displayData.builds.android.planValue,
|
|
270
|
+
limit: displayData.builds.android.limit,
|
|
271
|
+
percentUsed: displayData.builds.android.percentUsed,
|
|
272
|
+
},
|
|
273
|
+
}
|
|
274
|
+
: null,
|
|
275
|
+
},
|
|
276
|
+
updates: {
|
|
277
|
+
uniqueUpdaters: {
|
|
278
|
+
plan: {
|
|
279
|
+
used: displayData.updates.mau.planValue,
|
|
280
|
+
limit: displayData.updates.mau.limit,
|
|
281
|
+
percentUsed: displayData.updates.mau.percentUsed,
|
|
282
|
+
},
|
|
283
|
+
overage: {
|
|
284
|
+
count: displayData.updates.mau.overageValue,
|
|
285
|
+
costCents: displayData.updates.mau.overageCost,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
bandwidth: {
|
|
289
|
+
plan: {
|
|
290
|
+
usedBytes: displayData.updates.bandwidth.planValue,
|
|
291
|
+
usedFormatted: formatBytesDisplay(displayData.updates.bandwidth.planValue),
|
|
292
|
+
limitBytes: displayData.updates.bandwidth.limit,
|
|
293
|
+
limitFormatted: formatBytesDisplay(displayData.updates.bandwidth.limit),
|
|
294
|
+
percentUsed: displayData.updates.bandwidth.percentUsed,
|
|
295
|
+
},
|
|
296
|
+
overage: {
|
|
297
|
+
usedBytes: displayData.updates.bandwidth.overageValue,
|
|
298
|
+
usedFormatted: formatBytesDisplay(displayData.updates.bandwidth.overageValue),
|
|
299
|
+
costCents: displayData.updates.bandwidth.overageCost,
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
overageCostCents: displayData.updates.overageCostCents,
|
|
303
|
+
},
|
|
304
|
+
billing: {
|
|
305
|
+
addons: subscription?.addons ?? [],
|
|
306
|
+
upcomingInvoice: subscription?.upcomingInvoice ?? null,
|
|
307
|
+
estimated: {
|
|
308
|
+
recurringCents: displayData.recurringCents,
|
|
309
|
+
overageCents: displayData.totalOverageCostCents,
|
|
310
|
+
totalCents: displayData.estimatedBillCents,
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
billingUrl: billingUrl(displayData.accountName),
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
displayUsage(displayData, usageData);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
spinner.fail('Failed to fetch usage data');
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
exports.default = AccountUsage;
|
|
@@ -54,7 +54,10 @@ class EnvCreate extends EasCommand_1.default {
|
|
|
54
54
|
const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(EnvCreate, {
|
|
55
55
|
nonInteractive: validatedFlags['non-interactive'],
|
|
56
56
|
});
|
|
57
|
-
const { name, value, scope, 'non-interactive': nonInteractive, environment: environments, visibility, force, type, fileName, } = await this.promptForMissingFlagsAsync(validatedFlags, args, {
|
|
57
|
+
const { name, value, scope, 'non-interactive': nonInteractive, environment: environments, visibility, force, type, fileName, } = await this.promptForMissingFlagsAsync(validatedFlags, args, {
|
|
58
|
+
graphqlClient,
|
|
59
|
+
projectId,
|
|
60
|
+
});
|
|
58
61
|
const [projectDisplayName, ownerAccount] = await Promise.all([
|
|
59
62
|
(0, projectUtils_1.getDisplayNameForProjectIdAsync)(graphqlClient, projectId),
|
|
60
63
|
(0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId),
|
|
@@ -186,7 +189,7 @@ class EnvCreate extends EasCommand_1.default {
|
|
|
186
189
|
fileName = path_1.default.basename(environmentFilePath);
|
|
187
190
|
}
|
|
188
191
|
value = environmentFilePath ? await fs_extra_1.default.readFile(environmentFilePath, 'base64') : value;
|
|
189
|
-
let newEnvironments = environments
|
|
192
|
+
let newEnvironments = environments ?? (environment ? [environment] : undefined);
|
|
190
193
|
if (!newEnvironments) {
|
|
191
194
|
newEnvironments = await (0, prompts_2.promptVariableEnvironmentAsync)({
|
|
192
195
|
nonInteractive,
|
|
@@ -110,11 +110,7 @@ class EnvList extends EasCommand_1.default {
|
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
112
|
sanitizeInputs(flags, { environment }) {
|
|
113
|
-
const environments = flags.environment
|
|
114
|
-
? flags.environment
|
|
115
|
-
: environment
|
|
116
|
-
? [environment]
|
|
117
|
-
: undefined;
|
|
113
|
+
const environments = flags.environment ?? (environment ? [environment] : undefined);
|
|
118
114
|
return {
|
|
119
115
|
...flags,
|
|
120
116
|
'non-interactive': flags['non-interactive'] ?? false,
|
|
@@ -313,11 +313,11 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
313
313
|
return {
|
|
314
314
|
...info,
|
|
315
315
|
expoUpdatesRuntimeFingerprintSource: info.expoUpdatesRuntimeFingerprint
|
|
316
|
-
? (await (0, maybeUploadFingerprintAsync_1.maybeUploadFingerprintAsync)({
|
|
316
|
+
? ((await (0, maybeUploadFingerprintAsync_1.maybeUploadFingerprintAsync)({
|
|
317
317
|
hash: info.runtimeVersion,
|
|
318
318
|
fingerprint: info.expoUpdatesRuntimeFingerprint,
|
|
319
319
|
graphqlClient,
|
|
320
|
-
})).fingerprintSource ?? null
|
|
320
|
+
})).fingerprintSource ?? null)
|
|
321
321
|
: null,
|
|
322
322
|
};
|
|
323
323
|
}));
|
package/build/commands/upload.js
CHANGED
|
@@ -8,7 +8,7 @@ const fast_glob_1 = tslib_1.__importDefault(require("fast-glob"));
|
|
|
8
8
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
9
9
|
const node_stream_zip_1 = tslib_1.__importDefault(require("node-stream-zip"));
|
|
10
10
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
11
|
-
const
|
|
11
|
+
const tar = tslib_1.__importStar(require("tar"));
|
|
12
12
|
const uuid_1 = require("uuid");
|
|
13
13
|
const url_1 = require("../build/utils/url");
|
|
14
14
|
const EasCommand_1 = tslib_1.__importDefault(require("../commandUtils/EasCommand"));
|
|
@@ -214,7 +214,7 @@ async function uploadAppArchiveAsync(graphqlClient, originalPath) {
|
|
|
214
214
|
const tarPath = path_1.default.join((0, paths_1.getTmpDirectory)(), `${(0, uuid_1.v4)()}.tar.gz`);
|
|
215
215
|
const parentPath = path_1.default.dirname(originalPath);
|
|
216
216
|
const folderName = path_1.default.basename(originalPath);
|
|
217
|
-
await
|
|
217
|
+
await tar.create({ cwd: parentPath, file: tarPath, gzip: true }, [folderName]);
|
|
218
218
|
filePath = tarPath;
|
|
219
219
|
}
|
|
220
220
|
const fileSize = (await fs_extra_1.default.stat(filePath)).size;
|
|
@@ -306,7 +306,7 @@ async function extractAppMetadataAsync(buildPath, platform) {
|
|
|
306
306
|
try {
|
|
307
307
|
let fingerprintHashPromise;
|
|
308
308
|
let infoPlistPromise;
|
|
309
|
-
await
|
|
309
|
+
await tar.list({
|
|
310
310
|
file: buildPath,
|
|
311
311
|
// eslint-disable-next-line async-protect/async-suffix
|
|
312
312
|
onentry: entry => {
|
|
@@ -43,9 +43,7 @@ exports.AppleDistributionCertificateMutation = {
|
|
|
43
43
|
async deleteAppleDistributionCertificateAsync(graphqlClient, appleDistributionCertificateId) {
|
|
44
44
|
await (0, client_1.withErrorHandlingAsync)(graphqlClient
|
|
45
45
|
.mutation((0, graphql_tag_1.default) `
|
|
46
|
-
mutation DeleteAppleDistributionCertificateMutation(
|
|
47
|
-
$appleDistributionCertificateId: ID!
|
|
48
|
-
) {
|
|
46
|
+
mutation DeleteAppleDistributionCertificateMutation($appleDistributionCertificateId: ID!) {
|
|
49
47
|
appleDistributionCertificate {
|
|
50
48
|
deleteAppleDistributionCertificate(id: $appleDistributionCertificateId) {
|
|
51
49
|
id
|
package/build/devices/queries.js
CHANGED
|
@@ -18,7 +18,18 @@ exports.TEAMS_LIMIT = 50;
|
|
|
18
18
|
exports.DEVICES_LIMIT = 50;
|
|
19
19
|
async function selectAppleTeamOnAccountAsync(graphqlClient, { accountName, selectionPromptTitle, paginatedQueryOptions, }) {
|
|
20
20
|
if (paginatedQueryOptions.nonInteractive) {
|
|
21
|
-
|
|
21
|
+
const teams = await AppleTeamQuery_1.AppleTeamQuery.getAllForAccountAsync(graphqlClient, {
|
|
22
|
+
accountName,
|
|
23
|
+
limit: paginatedQueryOptions.limit ?? exports.TEAMS_LIMIT,
|
|
24
|
+
offset: paginatedQueryOptions.offset,
|
|
25
|
+
});
|
|
26
|
+
if (teams.length === 0) {
|
|
27
|
+
throw new Error(`No Apple teams found for account ${accountName}.`);
|
|
28
|
+
}
|
|
29
|
+
const teamList = teams
|
|
30
|
+
.map(t => t.appleTeamName ? `${t.appleTeamName} (${t.appleTeamIdentifier})` : t.appleTeamIdentifier)
|
|
31
|
+
.join('\n ');
|
|
32
|
+
throw new Error(`Unable to select an Apple team in non-interactive mode. Use the --apple-team-id flag to specify the team. Available Apple teams for account ${accountName}:\n ${teamList}`);
|
|
22
33
|
}
|
|
23
34
|
else {
|
|
24
35
|
const selectedAppleTeam = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
|
|
@@ -47,7 +58,17 @@ async function selectAppleTeamOnAccountAsync(graphqlClient, { accountName, selec
|
|
|
47
58
|
}
|
|
48
59
|
async function selectAppleDeviceOnAppleTeamAsync(graphqlClient, { accountName, appleTeamIdentifier, selectionPromptTitle, paginatedQueryOptions, }) {
|
|
49
60
|
if (paginatedQueryOptions.nonInteractive) {
|
|
50
|
-
|
|
61
|
+
const devices = await AppleDeviceQuery_1.AppleDeviceQuery.getAllForAppleTeamAsync(graphqlClient, {
|
|
62
|
+
accountName,
|
|
63
|
+
appleTeamIdentifier,
|
|
64
|
+
limit: paginatedQueryOptions.limit ?? exports.DEVICES_LIMIT,
|
|
65
|
+
offset: paginatedQueryOptions.offset,
|
|
66
|
+
});
|
|
67
|
+
if (devices.length === 0) {
|
|
68
|
+
throw new Error(`No devices found on Apple team ${appleTeamIdentifier} for account ${accountName}.`);
|
|
69
|
+
}
|
|
70
|
+
const deviceList = devices.map(d => (0, DeviceUtils_1.formatDeviceLabel)(d)).join('\n ');
|
|
71
|
+
throw new Error(`Unable to select an Apple device in non-interactive mode. Available devices on Apple team ${appleTeamIdentifier}:\n ${deviceList}`);
|
|
51
72
|
}
|
|
52
73
|
else {
|
|
53
74
|
const selectedAppleDevice = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
|