eas-cli 18.6.0 → 18.8.0
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 +149 -91
- package/build/channel/insights/formatInsights.d.ts +47 -0
- package/build/channel/insights/formatInsights.js +108 -0
- package/build/commands/build/dev.d.ts +1 -0
- package/build/commands/build/dev.js +10 -2
- package/build/commands/channel/insights.d.ts +18 -0
- package/build/commands/channel/insights.js +71 -0
- package/build/commands/observe/events.d.ts +1 -0
- package/build/commands/observe/events.js +17 -4
- package/build/commands/observe/metrics.d.ts +1 -0
- package/build/commands/observe/metrics.js +18 -6
- package/build/commands/observe/versions.d.ts +1 -0
- package/build/commands/observe/versions.js +17 -4
- package/build/commands/update/insights.d.ts +19 -0
- package/build/commands/update/insights.js +75 -0
- package/build/commands/update/view.d.ts +4 -0
- package/build/commands/update/view.js +47 -2
- package/build/credentials/ios/appstore/capabilityIdentifiers.js +28 -3
- package/build/graphql/client.d.ts +13 -0
- package/build/graphql/client.js +36 -1
- package/build/graphql/generated.d.ts +193 -0
- package/build/graphql/generated.js +8 -2
- package/build/graphql/queries/ChannelInsightsQuery.d.ts +12 -0
- package/build/graphql/queries/ChannelInsightsQuery.js +81 -0
- package/build/graphql/queries/UpdateInsightsQuery.d.ts +10 -0
- package/build/graphql/queries/UpdateInsightsQuery.js +53 -0
- package/build/graphql/types/Observe.js +1 -0
- package/build/insights/formatTimespan.d.ts +7 -0
- package/build/insights/formatTimespan.js +15 -0
- package/build/insights/timeRange.d.ts +10 -0
- package/build/insights/timeRange.js +10 -0
- package/build/metadata/apple/tasks/previews.js +41 -15
- package/build/observe/formatEvents.d.ts +3 -0
- package/build/observe/formatEvents.js +4 -0
- package/build/observe/formatMetrics.d.ts +3 -2
- package/build/observe/formatMetrics.js +16 -27
- package/build/observe/formatVersions.js +2 -8
- package/build/update/insights/formatInsights.d.ts +34 -0
- package/build/update/insights/formatInsights.js +128 -0
- package/build/utils/renderTextTable.d.ts +6 -0
- package/build/utils/renderTextTable.js +23 -0
- package/oclif.manifest.json +773 -469
- package/package.json +5 -5
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ChannelRuntimeInsights } from '../../graphql/queries/ChannelInsightsQuery';
|
|
2
|
+
export interface ChannelInsightsTimespan {
|
|
3
|
+
startTime: string;
|
|
4
|
+
endTime: string;
|
|
5
|
+
daysBack?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ChannelMostPopularUpdate {
|
|
8
|
+
rank: number;
|
|
9
|
+
groupId: string;
|
|
10
|
+
message: string | null;
|
|
11
|
+
platform: string;
|
|
12
|
+
totalUniqueUsers: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ChannelInsightsSummary {
|
|
15
|
+
channelName: string;
|
|
16
|
+
runtimeVersion: string;
|
|
17
|
+
startTime: string;
|
|
18
|
+
endTime: string;
|
|
19
|
+
daysBack?: number;
|
|
20
|
+
embeddedUpdateTotalUniqueUsers: number;
|
|
21
|
+
otaTotalUniqueUsers: number;
|
|
22
|
+
mostPopularUpdates: ChannelMostPopularUpdate[];
|
|
23
|
+
cumulativeMetricsAtLastTimestamp: {
|
|
24
|
+
id: string;
|
|
25
|
+
label: string;
|
|
26
|
+
data: number;
|
|
27
|
+
}[];
|
|
28
|
+
uniqueUsersOverTime: {
|
|
29
|
+
labels: string[];
|
|
30
|
+
datasets: {
|
|
31
|
+
id: string;
|
|
32
|
+
label: string;
|
|
33
|
+
data: (number | null)[];
|
|
34
|
+
}[];
|
|
35
|
+
};
|
|
36
|
+
cumulativeMetricsOverTime: {
|
|
37
|
+
labels: string[];
|
|
38
|
+
datasets: {
|
|
39
|
+
id: string;
|
|
40
|
+
label: string;
|
|
41
|
+
data: (number | null)[];
|
|
42
|
+
}[];
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export declare function toChannelInsightsSummary(channelName: string, runtimeVersion: string, insights: ChannelRuntimeInsights, timespan: ChannelInsightsTimespan): ChannelInsightsSummary;
|
|
46
|
+
export declare function buildChannelInsightsJson(summary: ChannelInsightsSummary): object;
|
|
47
|
+
export declare function buildChannelInsightsTable(summary: ChannelInsightsSummary): string;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toChannelInsightsSummary = toChannelInsightsSummary;
|
|
4
|
+
exports.buildChannelInsightsJson = buildChannelInsightsJson;
|
|
5
|
+
exports.buildChannelInsightsTable = buildChannelInsightsTable;
|
|
6
|
+
const tslib_1 = require("tslib");
|
|
7
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
8
|
+
const formatTimespan_1 = require("../../insights/formatTimespan");
|
|
9
|
+
const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
|
|
10
|
+
const renderTextTable_1 = tslib_1.__importDefault(require("../../utils/renderTextTable"));
|
|
11
|
+
function toChannelInsightsSummary(channelName, runtimeVersion, insights, timespan) {
|
|
12
|
+
const mostPopular = insights.mostPopularUpdates.map((u, i) => ({
|
|
13
|
+
rank: i + 1,
|
|
14
|
+
groupId: u.group,
|
|
15
|
+
message: u.message ?? null,
|
|
16
|
+
platform: u.platform,
|
|
17
|
+
totalUniqueUsers: u.insights.totalUniqueUsers,
|
|
18
|
+
}));
|
|
19
|
+
const otaTotalUniqueUsers = mostPopular.reduce((sum, u) => sum + u.totalUniqueUsers, 0);
|
|
20
|
+
return {
|
|
21
|
+
channelName,
|
|
22
|
+
runtimeVersion,
|
|
23
|
+
startTime: timespan.startTime,
|
|
24
|
+
endTime: timespan.endTime,
|
|
25
|
+
daysBack: timespan.daysBack,
|
|
26
|
+
embeddedUpdateTotalUniqueUsers: insights.embeddedUpdateTotalUniqueUsers,
|
|
27
|
+
otaTotalUniqueUsers,
|
|
28
|
+
mostPopularUpdates: mostPopular,
|
|
29
|
+
cumulativeMetricsAtLastTimestamp: insights.cumulativeMetricsOverTime.metricsAtLastTimestamp.map(m => ({ id: m.id, label: m.label, data: m.data })),
|
|
30
|
+
uniqueUsersOverTime: {
|
|
31
|
+
labels: insights.uniqueUsersOverTime.data.labels,
|
|
32
|
+
datasets: insights.uniqueUsersOverTime.data.datasets.map(d => ({
|
|
33
|
+
id: d.id,
|
|
34
|
+
label: d.label,
|
|
35
|
+
data: d.data,
|
|
36
|
+
})),
|
|
37
|
+
},
|
|
38
|
+
cumulativeMetricsOverTime: {
|
|
39
|
+
labels: insights.cumulativeMetricsOverTime.data.labels,
|
|
40
|
+
datasets: insights.cumulativeMetricsOverTime.data.datasets.map(d => ({
|
|
41
|
+
id: d.id,
|
|
42
|
+
label: d.label,
|
|
43
|
+
data: d.data,
|
|
44
|
+
})),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function buildChannelInsightsJson(summary) {
|
|
49
|
+
return {
|
|
50
|
+
channel: summary.channelName,
|
|
51
|
+
runtimeVersion: summary.runtimeVersion,
|
|
52
|
+
timespan: {
|
|
53
|
+
start: summary.startTime,
|
|
54
|
+
end: summary.endTime,
|
|
55
|
+
...(summary.daysBack !== undefined ? { daysBack: summary.daysBack } : {}),
|
|
56
|
+
},
|
|
57
|
+
embeddedUpdateTotalUniqueUsers: summary.embeddedUpdateTotalUniqueUsers,
|
|
58
|
+
otaTotalUniqueUsers: summary.otaTotalUniqueUsers,
|
|
59
|
+
mostPopularUpdates: summary.mostPopularUpdates,
|
|
60
|
+
cumulativeMetricsAtLastTimestamp: summary.cumulativeMetricsAtLastTimestamp,
|
|
61
|
+
uniqueUsersOverTime: summary.uniqueUsersOverTime,
|
|
62
|
+
cumulativeMetricsOverTime: summary.cumulativeMetricsOverTime,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function buildChannelInsightsTable(summary) {
|
|
66
|
+
const sections = [];
|
|
67
|
+
sections.push(chalk_1.default.bold('Channel insights:'));
|
|
68
|
+
sections.push((0, formatFields_1.default)([
|
|
69
|
+
{ label: 'Channel', value: summary.channelName },
|
|
70
|
+
{ label: 'Runtime version', value: summary.runtimeVersion },
|
|
71
|
+
{ label: 'Time range', value: (0, formatTimespan_1.formatTimespan)(summary) },
|
|
72
|
+
{
|
|
73
|
+
label: 'Embedded update users',
|
|
74
|
+
value: summary.embeddedUpdateTotalUniqueUsers.toLocaleString(),
|
|
75
|
+
},
|
|
76
|
+
{ label: 'OTA update users', value: summary.otaTotalUniqueUsers.toLocaleString() },
|
|
77
|
+
]));
|
|
78
|
+
if (summary.cumulativeMetricsAtLastTimestamp.length > 0) {
|
|
79
|
+
sections.push('');
|
|
80
|
+
sections.push(chalk_1.default.bold('Cumulative metrics at last timestamp:'));
|
|
81
|
+
sections.push((0, formatFields_1.default)(summary.cumulativeMetricsAtLastTimestamp.map(m => ({
|
|
82
|
+
label: m.label,
|
|
83
|
+
value: m.data.toLocaleString(),
|
|
84
|
+
}))));
|
|
85
|
+
}
|
|
86
|
+
if (summary.mostPopularUpdates.length > 0) {
|
|
87
|
+
sections.push('');
|
|
88
|
+
sections.push(chalk_1.default.bold(`Most popular updates${formatTrailingTimespan(summary)}:`));
|
|
89
|
+
sections.push(renderMostPopularTable(summary.mostPopularUpdates));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
sections.push('');
|
|
93
|
+
sections.push(chalk_1.default.yellow('No update launches recorded for this channel and runtime.'));
|
|
94
|
+
}
|
|
95
|
+
return sections.join('\n');
|
|
96
|
+
}
|
|
97
|
+
function formatTrailingTimespan(summary) {
|
|
98
|
+
return summary.daysBack ? ` (last ${summary.daysBack} days)` : '';
|
|
99
|
+
}
|
|
100
|
+
function renderMostPopularTable(rows) {
|
|
101
|
+
return (0, renderTextTable_1.default)(['#', 'Group ID', 'Platform', 'Unique users', 'Message'], rows.map(r => [
|
|
102
|
+
String(r.rank),
|
|
103
|
+
r.groupId,
|
|
104
|
+
r.platform,
|
|
105
|
+
r.totalUniqueUsers.toLocaleString(),
|
|
106
|
+
r.message ?? '',
|
|
107
|
+
]));
|
|
108
|
+
}
|
|
@@ -7,6 +7,7 @@ export default class BuildDev extends EasCommand {
|
|
|
7
7
|
platform: import("@oclif/core/lib/interfaces").OptionFlag<Platform | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
8
|
profile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
9
|
'skip-build-if-not-found': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
'skip-bundler': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
};
|
|
11
12
|
static contextDefinition: {
|
|
12
13
|
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
@@ -38,6 +38,10 @@ class BuildDev extends EasCommand_1.default {
|
|
|
38
38
|
description: 'Skip build if no successful build with matching fingerprint is found.',
|
|
39
39
|
default: false,
|
|
40
40
|
}),
|
|
41
|
+
'skip-bundler': core_1.Flags.boolean({
|
|
42
|
+
description: 'Install and run the development build without starting the bundler server.',
|
|
43
|
+
default: false,
|
|
44
|
+
}),
|
|
41
45
|
};
|
|
42
46
|
static contextDefinition = {
|
|
43
47
|
...this.ContextOptions.LoggedIn,
|
|
@@ -100,7 +104,9 @@ class BuildDev extends EasCommand_1.default {
|
|
|
100
104
|
log_1.default.succeed(`🎯 Found successful build with matching fingerprint on EAS servers. Running it...`);
|
|
101
105
|
if (build.artifacts?.applicationArchiveUrl) {
|
|
102
106
|
await (0, runBuildAndSubmit_1.downloadAndRunAsync)(build);
|
|
103
|
-
|
|
107
|
+
if (!flags['skip-bundler']) {
|
|
108
|
+
await this.startDevServerAsync({ projectDir, platform });
|
|
109
|
+
}
|
|
104
110
|
return;
|
|
105
111
|
}
|
|
106
112
|
else {
|
|
@@ -143,7 +149,9 @@ class BuildDev extends EasCommand_1.default {
|
|
|
143
149
|
downloadSimBuildAutoConfirm: true,
|
|
144
150
|
envOverride: env,
|
|
145
151
|
});
|
|
146
|
-
|
|
152
|
+
if (!flags['skip-bundler']) {
|
|
153
|
+
await this.startDevServerAsync({ projectDir, platform });
|
|
154
|
+
}
|
|
147
155
|
}
|
|
148
156
|
async selectPlatformAsync(platform) {
|
|
149
157
|
if (platform) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import EasCommand from '../../commandUtils/EasCommand';
|
|
2
|
+
export default class ChannelInsights extends EasCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
6
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
channel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
'runtime-version': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
days: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
start: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
end: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
};
|
|
13
|
+
static contextDefinition: {
|
|
14
|
+
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
15
|
+
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
16
|
+
};
|
|
17
|
+
runAsync(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const formatInsights_1 = require("../../channel/insights/formatInsights");
|
|
6
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
7
|
+
const flags_1 = require("../../commandUtils/flags");
|
|
8
|
+
const ChannelInsightsQuery_1 = require("../../graphql/queries/ChannelInsightsQuery");
|
|
9
|
+
const timeRange_1 = require("../../insights/timeRange");
|
|
10
|
+
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
11
|
+
const json_1 = require("../../utils/json");
|
|
12
|
+
class ChannelInsights extends EasCommand_1.default {
|
|
13
|
+
static description = 'display adoption, crash, and unique-user insights for a channel + runtime version';
|
|
14
|
+
static flags = {
|
|
15
|
+
channel: core_1.Flags.string({
|
|
16
|
+
description: 'Name of the channel.',
|
|
17
|
+
required: true,
|
|
18
|
+
}),
|
|
19
|
+
'runtime-version': core_1.Flags.string({
|
|
20
|
+
description: 'Runtime version to query insights for.',
|
|
21
|
+
required: true,
|
|
22
|
+
}),
|
|
23
|
+
days: core_1.Flags.integer({
|
|
24
|
+
description: 'Show insights from the last N days (default 7, mutually exclusive with --start/--end).',
|
|
25
|
+
min: 1,
|
|
26
|
+
exclusive: ['start', 'end'],
|
|
27
|
+
}),
|
|
28
|
+
start: core_1.Flags.string({
|
|
29
|
+
description: 'Start of insights time range (ISO date).',
|
|
30
|
+
exclusive: ['days'],
|
|
31
|
+
}),
|
|
32
|
+
end: core_1.Flags.string({
|
|
33
|
+
description: 'End of insights time range (ISO date).',
|
|
34
|
+
exclusive: ['days'],
|
|
35
|
+
}),
|
|
36
|
+
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
37
|
+
};
|
|
38
|
+
static contextDefinition = {
|
|
39
|
+
...this.ContextOptions.ProjectId,
|
|
40
|
+
...this.ContextOptions.LoggedIn,
|
|
41
|
+
};
|
|
42
|
+
async runAsync() {
|
|
43
|
+
const { flags } = await this.parse(ChannelInsights);
|
|
44
|
+
const { json, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
|
|
45
|
+
const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(ChannelInsights, { nonInteractive });
|
|
46
|
+
if (json) {
|
|
47
|
+
(0, json_1.enableJsonOutput)();
|
|
48
|
+
}
|
|
49
|
+
const { daysBack, startTime, endTime } = (0, timeRange_1.resolveInsightsTimeRange)(flags);
|
|
50
|
+
const insights = await ChannelInsightsQuery_1.ChannelInsightsQuery.viewChannelRuntimeInsightsAsync(graphqlClient, {
|
|
51
|
+
appId: projectId,
|
|
52
|
+
channelName: flags.channel,
|
|
53
|
+
runtimeVersion: flags['runtime-version'],
|
|
54
|
+
startTime,
|
|
55
|
+
endTime,
|
|
56
|
+
});
|
|
57
|
+
const summary = (0, formatInsights_1.toChannelInsightsSummary)(flags.channel, flags['runtime-version'], insights, {
|
|
58
|
+
startTime,
|
|
59
|
+
endTime,
|
|
60
|
+
daysBack,
|
|
61
|
+
});
|
|
62
|
+
if (json) {
|
|
63
|
+
(0, json_1.printJsonOnlyOutput)((0, formatInsights_1.buildChannelInsightsJson)(summary));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
log_1.default.addNewLineIfNone();
|
|
67
|
+
log_1.default.log((0, formatInsights_1.buildChannelInsightsTable)(summary));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.default = ChannelInsights;
|
|
@@ -23,5 +23,6 @@ export default class ObserveEvents extends EasCommand {
|
|
|
23
23
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
24
24
|
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
25
25
|
};
|
|
26
|
+
private static loggedInOnlyContextDefinition;
|
|
26
27
|
runAsync(): Promise<void>;
|
|
27
28
|
}
|
|
@@ -71,12 +71,25 @@ class ObserveEvents extends EasCommand_1.default {
|
|
|
71
71
|
...this.ContextOptions.ProjectId,
|
|
72
72
|
...this.ContextOptions.LoggedIn,
|
|
73
73
|
};
|
|
74
|
+
static loggedInOnlyContextDefinition = {
|
|
75
|
+
...this.ContextOptions.LoggedIn,
|
|
76
|
+
};
|
|
74
77
|
async runAsync() {
|
|
75
78
|
const { flags, args } = await this.parse(ObserveEvents);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
let projectId;
|
|
80
|
+
let graphqlClient;
|
|
81
|
+
if (flags['project-id']) {
|
|
82
|
+
projectId = flags['project-id'];
|
|
83
|
+
const ctx = await this.getContextAsync({ contextDefinition: ObserveEvents.loggedInOnlyContextDefinition }, { nonInteractive: flags['non-interactive'] });
|
|
84
|
+
graphqlClient = ctx.loggedIn.graphqlClient;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const ctx = await this.getContextAsync(ObserveEvents, {
|
|
88
|
+
nonInteractive: flags['non-interactive'],
|
|
89
|
+
});
|
|
90
|
+
projectId = ctx.projectId;
|
|
91
|
+
graphqlClient = ctx.loggedIn.graphqlClient;
|
|
92
|
+
}
|
|
80
93
|
if (flags.json) {
|
|
81
94
|
(0, json_1.enableJsonOutput)();
|
|
82
95
|
}
|
|
@@ -17,5 +17,6 @@ export default class ObserveMetrics extends EasCommand {
|
|
|
17
17
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
18
18
|
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
19
19
|
};
|
|
20
|
+
private static loggedInOnlyContextDefinition;
|
|
20
21
|
runAsync(): Promise<void>;
|
|
21
22
|
}
|
|
@@ -69,12 +69,25 @@ class ObserveMetrics extends EasCommand_1.default {
|
|
|
69
69
|
...this.ContextOptions.ProjectId,
|
|
70
70
|
...this.ContextOptions.LoggedIn,
|
|
71
71
|
};
|
|
72
|
+
static loggedInOnlyContextDefinition = {
|
|
73
|
+
...this.ContextOptions.LoggedIn,
|
|
74
|
+
};
|
|
72
75
|
async runAsync() {
|
|
73
76
|
const { flags } = await this.parse(ObserveMetrics);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
let projectId;
|
|
78
|
+
let graphqlClient;
|
|
79
|
+
if (flags['project-id']) {
|
|
80
|
+
projectId = flags['project-id'];
|
|
81
|
+
const ctx = await this.getContextAsync({ contextDefinition: ObserveMetrics.loggedInOnlyContextDefinition }, { nonInteractive: flags['non-interactive'] });
|
|
82
|
+
graphqlClient = ctx.loggedIn.graphqlClient;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const ctx = await this.getContextAsync(ObserveMetrics, {
|
|
86
|
+
nonInteractive: flags['non-interactive'],
|
|
87
|
+
});
|
|
88
|
+
projectId = ctx.projectId;
|
|
89
|
+
graphqlClient = ctx.loggedIn.graphqlClient;
|
|
90
|
+
}
|
|
78
91
|
if (flags.json) {
|
|
79
92
|
(0, json_1.enableJsonOutput)();
|
|
80
93
|
}
|
|
@@ -94,7 +107,7 @@ class ObserveMetrics extends EasCommand_1.default {
|
|
|
94
107
|
: undefined;
|
|
95
108
|
if (flags.json) {
|
|
96
109
|
const stats = argumentsStat ?? DEFAULT_STATS_JSON;
|
|
97
|
-
(0, json_1.printJsonOnlyOutput)((0, formatMetrics_1.buildObserveMetricsJson)(metricsMap, metricNames, stats, totalEventCounts));
|
|
110
|
+
(0, json_1.printJsonOnlyOutput)((0, formatMetrics_1.buildObserveMetricsJson)(metricsMap, metricNames, stats, totalEventCounts, buildNumbersMap, updateIdsMap));
|
|
98
111
|
}
|
|
99
112
|
else {
|
|
100
113
|
const stats = argumentsStat ?? DEFAULT_STATS_TABLE;
|
|
@@ -102,7 +115,6 @@ class ObserveMetrics extends EasCommand_1.default {
|
|
|
102
115
|
log_1.default.log((0, formatMetrics_1.buildObserveMetricsTable)(metricsMap, metricNames, stats, {
|
|
103
116
|
daysBack,
|
|
104
117
|
buildNumbersMap,
|
|
105
|
-
updateIdsMap,
|
|
106
118
|
totalEventCounts,
|
|
107
119
|
}));
|
|
108
120
|
}
|
|
@@ -15,5 +15,6 @@ export default class ObserveVersions extends EasCommand {
|
|
|
15
15
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
16
16
|
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
17
17
|
};
|
|
18
|
+
private static loggedInOnlyContextDefinition;
|
|
18
19
|
runAsync(): Promise<void>;
|
|
19
20
|
}
|
|
@@ -40,12 +40,25 @@ class ObserveVersions extends EasCommand_1.default {
|
|
|
40
40
|
...this.ContextOptions.ProjectId,
|
|
41
41
|
...this.ContextOptions.LoggedIn,
|
|
42
42
|
};
|
|
43
|
+
static loggedInOnlyContextDefinition = {
|
|
44
|
+
...this.ContextOptions.LoggedIn,
|
|
45
|
+
};
|
|
43
46
|
async runAsync() {
|
|
44
47
|
const { flags } = await this.parse(ObserveVersions);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
let projectId;
|
|
49
|
+
let graphqlClient;
|
|
50
|
+
if (flags['project-id']) {
|
|
51
|
+
projectId = flags['project-id'];
|
|
52
|
+
const ctx = await this.getContextAsync({ contextDefinition: ObserveVersions.loggedInOnlyContextDefinition }, { nonInteractive: flags['non-interactive'] });
|
|
53
|
+
graphqlClient = ctx.loggedIn.graphqlClient;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const ctx = await this.getContextAsync(ObserveVersions, {
|
|
57
|
+
nonInteractive: flags['non-interactive'],
|
|
58
|
+
});
|
|
59
|
+
projectId = ctx.projectId;
|
|
60
|
+
graphqlClient = ctx.loggedIn.graphqlClient;
|
|
61
|
+
}
|
|
49
62
|
if (flags.json) {
|
|
50
63
|
(0, json_1.enableJsonOutput)();
|
|
51
64
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import EasCommand from '../../commandUtils/EasCommand';
|
|
2
|
+
export default class UpdateInsights extends EasCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
groupId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static flags: {
|
|
8
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
platform: import("@oclif/core/lib/interfaces").OptionFlag<"android" | "ios" | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
days: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
start: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
end: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
|
+
};
|
|
15
|
+
static contextDefinition: {
|
|
16
|
+
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
17
|
+
};
|
|
18
|
+
runAsync(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
6
|
+
const flags_1 = require("../../commandUtils/flags");
|
|
7
|
+
const UpdateInsightsQuery_1 = require("../../graphql/queries/UpdateInsightsQuery");
|
|
8
|
+
const timeRange_1 = require("../../insights/timeRange");
|
|
9
|
+
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
10
|
+
const formatInsights_1 = require("../../update/insights/formatInsights");
|
|
11
|
+
const json_1 = require("../../utils/json");
|
|
12
|
+
class UpdateInsights extends EasCommand_1.default {
|
|
13
|
+
static description = 'display launch, crash, unique-user, and size insights for an update group';
|
|
14
|
+
static args = {
|
|
15
|
+
groupId: core_1.Args.string({
|
|
16
|
+
required: true,
|
|
17
|
+
description: 'The ID of an update group.',
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
static flags = {
|
|
21
|
+
platform: core_1.Flags.option({
|
|
22
|
+
description: 'Filter to a single platform.',
|
|
23
|
+
options: ['ios', 'android'],
|
|
24
|
+
})(),
|
|
25
|
+
days: core_1.Flags.integer({
|
|
26
|
+
description: `Show insights from the last N days (default ${timeRange_1.INSIGHTS_DEFAULT_DAYS_BACK}, mutually exclusive with --start/--end).`,
|
|
27
|
+
min: 1,
|
|
28
|
+
exclusive: ['start', 'end'],
|
|
29
|
+
}),
|
|
30
|
+
start: core_1.Flags.string({
|
|
31
|
+
description: 'Start of insights time range (ISO date).',
|
|
32
|
+
exclusive: ['days'],
|
|
33
|
+
}),
|
|
34
|
+
end: core_1.Flags.string({
|
|
35
|
+
description: 'End of insights time range (ISO date).',
|
|
36
|
+
exclusive: ['days'],
|
|
37
|
+
}),
|
|
38
|
+
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
39
|
+
};
|
|
40
|
+
static contextDefinition = {
|
|
41
|
+
...this.ContextOptions.LoggedIn,
|
|
42
|
+
};
|
|
43
|
+
async runAsync() {
|
|
44
|
+
const { args: { groupId }, flags, } = await this.parse(UpdateInsights);
|
|
45
|
+
const { json, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
|
|
46
|
+
const { loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateInsights, { nonInteractive });
|
|
47
|
+
if (json) {
|
|
48
|
+
(0, json_1.enableJsonOutput)();
|
|
49
|
+
}
|
|
50
|
+
const { daysBack, startTime, endTime } = (0, timeRange_1.resolveInsightsTimeRange)(flags);
|
|
51
|
+
const allUpdates = await UpdateInsightsQuery_1.UpdateInsightsQuery.viewUpdateGroupInsightsAsync(graphqlClient, {
|
|
52
|
+
groupId,
|
|
53
|
+
startTime,
|
|
54
|
+
endTime,
|
|
55
|
+
});
|
|
56
|
+
const updates = flags.platform
|
|
57
|
+
? allUpdates.filter(u => u.platform === flags.platform)
|
|
58
|
+
: allUpdates;
|
|
59
|
+
if (updates.length === 0) {
|
|
60
|
+
throw new Error(`Update group "${groupId}" has no ${flags.platform} update (available platforms: ${allUpdates
|
|
61
|
+
.map(u => u.platform)
|
|
62
|
+
.sort()
|
|
63
|
+
.join(', ')}).`);
|
|
64
|
+
}
|
|
65
|
+
const summary = (0, formatInsights_1.toUpdateInsightsSummary)(groupId, updates, { startTime, endTime, daysBack });
|
|
66
|
+
if (json) {
|
|
67
|
+
(0, json_1.printJsonOnlyOutput)((0, formatInsights_1.buildUpdateInsightsJson)(summary));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
log_1.default.addNewLineIfNone();
|
|
71
|
+
log_1.default.log((0, formatInsights_1.buildUpdateInsightsTable)(summary));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.default = UpdateInsights;
|
|
@@ -6,6 +6,10 @@ export default class UpdateView extends EasCommand {
|
|
|
6
6
|
};
|
|
7
7
|
static flags: {
|
|
8
8
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
insights: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
days: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
start: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
end: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
13
|
};
|
|
10
14
|
static contextDefinition: {
|
|
11
15
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
@@ -5,8 +5,11 @@ const core_1 = require("@oclif/core");
|
|
|
5
5
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
6
6
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
7
7
|
const flags_1 = require("../../commandUtils/flags");
|
|
8
|
+
const UpdateInsightsQuery_1 = require("../../graphql/queries/UpdateInsightsQuery");
|
|
8
9
|
const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
|
|
10
|
+
const timeRange_1 = require("../../insights/timeRange");
|
|
9
11
|
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
12
|
+
const formatInsights_1 = require("../../update/insights/formatInsights");
|
|
10
13
|
const utils_1 = require("../../update/utils");
|
|
11
14
|
const json_1 = require("../../utils/json");
|
|
12
15
|
class UpdateView extends EasCommand_1.default {
|
|
@@ -18,25 +21,67 @@ class UpdateView extends EasCommand_1.default {
|
|
|
18
21
|
}),
|
|
19
22
|
};
|
|
20
23
|
static flags = {
|
|
24
|
+
insights: core_1.Flags.boolean({
|
|
25
|
+
description: 'Also show insights (launches, crash rate, unique users, payload size) for the update group.',
|
|
26
|
+
default: false,
|
|
27
|
+
}),
|
|
28
|
+
days: core_1.Flags.integer({
|
|
29
|
+
description: 'Show insights from the last N days (default 7). Only used with --insights.',
|
|
30
|
+
min: 1,
|
|
31
|
+
exclusive: ['start', 'end'],
|
|
32
|
+
}),
|
|
33
|
+
start: core_1.Flags.string({
|
|
34
|
+
description: 'Start of insights time range (ISO date). Only used with --insights.',
|
|
35
|
+
exclusive: ['days'],
|
|
36
|
+
}),
|
|
37
|
+
end: core_1.Flags.string({
|
|
38
|
+
description: 'End of insights time range (ISO date). Only used with --insights.',
|
|
39
|
+
exclusive: ['days'],
|
|
40
|
+
}),
|
|
21
41
|
...flags_1.EasJsonOnlyFlag,
|
|
22
42
|
};
|
|
23
43
|
static contextDefinition = {
|
|
24
44
|
...this.ContextOptions.LoggedIn,
|
|
25
45
|
};
|
|
26
46
|
async runAsync() {
|
|
27
|
-
const { args: { groupId }, flags: { json: jsonFlag }, } = await this.parse(UpdateView);
|
|
47
|
+
const { args: { groupId }, flags: { json: jsonFlag, insights: insightsFlag, days, start, end }, } = await this.parse(UpdateView);
|
|
48
|
+
if (!insightsFlag && (days !== undefined || start !== undefined || end !== undefined)) {
|
|
49
|
+
throw new Error('--days, --start, and --end can only be used with --insights.');
|
|
50
|
+
}
|
|
28
51
|
const { loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateView, { nonInteractive: true });
|
|
29
52
|
if (jsonFlag) {
|
|
30
53
|
(0, json_1.enableJsonOutput)();
|
|
31
54
|
}
|
|
32
55
|
const updatesByGroup = await UpdateQuery_1.UpdateQuery.viewUpdateGroupAsync(graphqlClient, { groupId });
|
|
56
|
+
let insightsSummary = null;
|
|
57
|
+
if (insightsFlag) {
|
|
58
|
+
const { daysBack, startTime, endTime } = (0, timeRange_1.resolveInsightsTimeRange)({ days, start, end });
|
|
59
|
+
const updatesWithInsights = await UpdateInsightsQuery_1.UpdateInsightsQuery.viewUpdateGroupInsightsAsync(graphqlClient, { groupId, startTime, endTime });
|
|
60
|
+
insightsSummary = (0, formatInsights_1.toUpdateInsightsSummary)(groupId, updatesWithInsights, {
|
|
61
|
+
startTime,
|
|
62
|
+
endTime,
|
|
63
|
+
daysBack,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
33
66
|
if (jsonFlag) {
|
|
34
|
-
(
|
|
67
|
+
if (insightsSummary) {
|
|
68
|
+
(0, json_1.printJsonOnlyOutput)({
|
|
69
|
+
updates: (0, utils_1.getUpdateJsonInfosForUpdates)(updatesByGroup),
|
|
70
|
+
insights: (0, formatInsights_1.buildUpdateInsightsJson)(insightsSummary),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
(0, json_1.printJsonOnlyOutput)((0, utils_1.getUpdateJsonInfosForUpdates)(updatesByGroup));
|
|
75
|
+
}
|
|
35
76
|
}
|
|
36
77
|
else {
|
|
37
78
|
const [updateGroupDescription] = (0, utils_1.getUpdateGroupDescriptions)([updatesByGroup]);
|
|
38
79
|
log_1.default.log(chalk_1.default.bold('Update group:'));
|
|
39
80
|
log_1.default.log((0, utils_1.formatUpdateGroup)(updateGroupDescription));
|
|
81
|
+
if (insightsSummary) {
|
|
82
|
+
log_1.default.addNewLineIfNone();
|
|
83
|
+
log_1.default.log((0, formatInsights_1.buildUpdateInsightsTable)(insightsSummary));
|
|
84
|
+
}
|
|
40
85
|
}
|
|
41
86
|
}
|
|
42
87
|
}
|