eas-cli 16.23.1 → 16.24.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 +87 -87
- package/build/commandUtils/workflow/fetchLogs.d.ts +5 -1
- package/build/commandUtils/workflow/fetchLogs.js +23 -3
- package/build/commandUtils/workflow/inputs.d.ts +40 -0
- package/build/commandUtils/workflow/inputs.js +165 -0
- package/build/commandUtils/workflow/stateMachine.js +1 -1
- package/build/commandUtils/workflow/types.d.ts +1 -0
- package/build/commandUtils/workflow/types.js +1 -1
- package/build/commandUtils/workflow/utils.d.ts +15 -4
- package/build/commandUtils/workflow/utils.js +148 -10
- package/build/commands/workflow/run.d.ts +0 -25
- package/build/commands/workflow/run.js +32 -230
- package/build/credentials/ios/types.d.ts +3 -0
- package/build/credentials/ios/types.js +35 -0
- package/build/graphql/generated.d.ts +80 -2
- package/build/graphql/types/Build.js +1 -0
- package/oclif.manifest.json +2 -2
- package/package.json +4 -4
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { WorkflowJobResult } from './types';
|
|
2
|
-
|
|
2
|
+
import { ExpoGraphqlClient } from '../context/contextUtils/createGraphqlClient';
|
|
3
|
+
export declare function fetchRawLogsForCustomJobAsync(job: WorkflowJobResult): Promise<string | null>;
|
|
4
|
+
export declare function fetchRawLogsForBuildJobAsync(state: {
|
|
5
|
+
graphqlClient: ExpoGraphqlClient;
|
|
6
|
+
}, job: WorkflowJobResult): Promise<string | null>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.fetchRawLogsForBuildJobAsync = exports.fetchRawLogsForCustomJobAsync = void 0;
|
|
4
|
+
const BuildQuery_1 = require("../../graphql/queries/BuildQuery");
|
|
4
5
|
// This function is in a separate module for testing purposes
|
|
5
|
-
async function
|
|
6
|
+
async function fetchRawLogsForCustomJobAsync(job) {
|
|
6
7
|
const firstLogFileUrl = job.turtleJobRun?.logFileUrls?.[0];
|
|
7
8
|
if (!firstLogFileUrl) {
|
|
8
9
|
return null;
|
|
@@ -13,4 +14,23 @@ async function fetchRawLogsForJobAsync(job) {
|
|
|
13
14
|
const rawLogs = await response.text();
|
|
14
15
|
return rawLogs;
|
|
15
16
|
}
|
|
16
|
-
exports.
|
|
17
|
+
exports.fetchRawLogsForCustomJobAsync = fetchRawLogsForCustomJobAsync;
|
|
18
|
+
async function fetchRawLogsForBuildJobAsync(state, job) {
|
|
19
|
+
const buildId = job.outputs?.build_id;
|
|
20
|
+
if (!buildId) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const buildFragment = await BuildQuery_1.BuildQuery.byIdAsync(state.graphqlClient, buildId, {
|
|
24
|
+
useCache: false,
|
|
25
|
+
});
|
|
26
|
+
const firstLogFileUrl = buildFragment.logFiles?.[0];
|
|
27
|
+
if (!firstLogFileUrl) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const response = await fetch(firstLogFileUrl, {
|
|
31
|
+
method: 'GET',
|
|
32
|
+
});
|
|
33
|
+
const rawLogs = await response.text();
|
|
34
|
+
return rawLogs;
|
|
35
|
+
}
|
|
36
|
+
exports.fetchRawLogsForBuildJobAsync = fetchRawLogsForBuildJobAsync;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DefaultEnvironment } from '../../build/utils/environment';
|
|
3
|
+
export declare const WorkflowDispatchInputZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4
|
+
type: z.ZodOptional<z.ZodDefault<z.ZodLiteral<"string">>>;
|
|
5
|
+
default: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
6
|
+
description: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
7
|
+
required: z.ZodOptional<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodNumber, z.ZodBoolean>, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>>;
|
|
8
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
9
|
+
type: z.ZodLiteral<"boolean">;
|
|
10
|
+
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodNumber, z.ZodBoolean>, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>;
|
|
11
|
+
description: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
12
|
+
required: z.ZodOptional<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodNumber, z.ZodBoolean>, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>>;
|
|
13
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
14
|
+
type: z.ZodLiteral<"number">;
|
|
15
|
+
default: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
description: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
17
|
+
required: z.ZodOptional<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodNumber, z.ZodBoolean>, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>>;
|
|
18
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
19
|
+
type: z.ZodLiteral<"choice">;
|
|
20
|
+
default: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
21
|
+
options: z.ZodArray<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
22
|
+
description: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
23
|
+
required: z.ZodOptional<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodNumber, z.ZodBoolean>, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>>;
|
|
24
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
25
|
+
type: z.ZodLiteral<"environment">;
|
|
26
|
+
default: z.ZodOptional<z.ZodEnum<{
|
|
27
|
+
development: DefaultEnvironment.Development;
|
|
28
|
+
preview: DefaultEnvironment.Preview;
|
|
29
|
+
production: DefaultEnvironment.Production;
|
|
30
|
+
}>>;
|
|
31
|
+
description: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
32
|
+
required: z.ZodOptional<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodNumber, z.ZodBoolean>, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>>;
|
|
33
|
+
}, z.core.$strip>], "type">;
|
|
34
|
+
export declare function parseInputs(inputFlags: string[]): Record<string, string>;
|
|
35
|
+
export declare function parseJsonInputs(jsonString: string): Record<string, string>;
|
|
36
|
+
export declare function parseWorkflowInputsFromYaml(yamlConfig: string): Record<string, z.infer<typeof WorkflowDispatchInputZ>>;
|
|
37
|
+
export declare function maybePromptForMissingInputsAsync({ inputSpecs, inputs, }: {
|
|
38
|
+
inputSpecs: Record<string, z.infer<typeof WorkflowDispatchInputZ>>;
|
|
39
|
+
inputs: Record<string, unknown>;
|
|
40
|
+
}): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.maybePromptForMissingInputsAsync = exports.parseWorkflowInputsFromYaml = exports.parseJsonInputs = exports.parseInputs = exports.WorkflowDispatchInputZ = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const YAML = tslib_1.__importStar(require("yaml"));
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const environment_1 = require("../../build/utils/environment");
|
|
8
|
+
const types_1 = require("../../credentials/ios/types");
|
|
9
|
+
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
10
|
+
const prompts_1 = require("../../prompts");
|
|
11
|
+
const inputExtraProperties = {
|
|
12
|
+
description: types_1.stringLike.optional().describe('Description of the input'),
|
|
13
|
+
required: types_1.booleanLike.default(false).optional().describe('Whether the input is required.'),
|
|
14
|
+
};
|
|
15
|
+
// Adapted from the input definition on the server (https://github.com/expo/universe/pull/21950)
|
|
16
|
+
exports.WorkflowDispatchInputZ = zod_1.z.discriminatedUnion('type', [
|
|
17
|
+
zod_1.z.object({
|
|
18
|
+
...inputExtraProperties,
|
|
19
|
+
type: zod_1.z.literal('string').default('string').optional(),
|
|
20
|
+
default: types_1.stringLike.optional().describe('Default value for the input'),
|
|
21
|
+
}),
|
|
22
|
+
zod_1.z.object({
|
|
23
|
+
...inputExtraProperties,
|
|
24
|
+
type: zod_1.z.literal('boolean'),
|
|
25
|
+
default: types_1.booleanLike.optional().describe('Default value for the input'),
|
|
26
|
+
}),
|
|
27
|
+
zod_1.z.object({
|
|
28
|
+
...inputExtraProperties,
|
|
29
|
+
type: zod_1.z.literal('number'),
|
|
30
|
+
default: zod_1.z.number().optional().describe('Default value for the input'),
|
|
31
|
+
}),
|
|
32
|
+
zod_1.z.object({
|
|
33
|
+
...inputExtraProperties,
|
|
34
|
+
type: zod_1.z.literal('choice'),
|
|
35
|
+
default: types_1.stringLike.optional().describe('Default value for the input'),
|
|
36
|
+
options: zod_1.z.array(types_1.stringLike).min(1).describe('Options for choice type inputs'),
|
|
37
|
+
}),
|
|
38
|
+
zod_1.z.object({
|
|
39
|
+
...inputExtraProperties,
|
|
40
|
+
type: zod_1.z.literal('environment'),
|
|
41
|
+
default: zod_1.z
|
|
42
|
+
.enum(Object.values(environment_1.DefaultEnvironment))
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Default value for the input'),
|
|
45
|
+
}),
|
|
46
|
+
]);
|
|
47
|
+
function parseInputs(inputFlags) {
|
|
48
|
+
const inputs = {};
|
|
49
|
+
for (const inputFlag of inputFlags) {
|
|
50
|
+
const equalIndex = inputFlag.indexOf('=');
|
|
51
|
+
if (equalIndex === -1) {
|
|
52
|
+
throw new Error(`Invalid input format: ${inputFlag}. Expected key=value format.`);
|
|
53
|
+
}
|
|
54
|
+
const key = inputFlag.substring(0, equalIndex);
|
|
55
|
+
const value = inputFlag.substring(equalIndex + 1);
|
|
56
|
+
if (!key) {
|
|
57
|
+
throw new Error(`Invalid input format: ${inputFlag}. Key cannot be empty.`);
|
|
58
|
+
}
|
|
59
|
+
inputs[key] = value;
|
|
60
|
+
}
|
|
61
|
+
return inputs;
|
|
62
|
+
}
|
|
63
|
+
exports.parseInputs = parseInputs;
|
|
64
|
+
function parseJsonInputs(jsonString) {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(jsonString);
|
|
67
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
68
|
+
throw new Error('JSON input must be an object.');
|
|
69
|
+
}
|
|
70
|
+
return parsed;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new Error(`Invalid JSON input.`, { cause: error });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.parseJsonInputs = parseJsonInputs;
|
|
77
|
+
function parseWorkflowInputsFromYaml(yamlConfig) {
|
|
78
|
+
try {
|
|
79
|
+
const parsed = YAML.parse(yamlConfig);
|
|
80
|
+
return zod_1.z
|
|
81
|
+
.record(zod_1.z.string(), exports.WorkflowDispatchInputZ)
|
|
82
|
+
.default({})
|
|
83
|
+
.parse(parsed?.on?.workflow_dispatch?.inputs);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
log_1.default.warn('Failed to parse workflow inputs from YAML:', error);
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.parseWorkflowInputsFromYaml = parseWorkflowInputsFromYaml;
|
|
91
|
+
async function maybePromptForMissingInputsAsync({ inputSpecs, inputs, }) {
|
|
92
|
+
const requiredInputs = Object.entries(inputSpecs).filter(([_, spec]) => spec.required);
|
|
93
|
+
const missingRequiredInputs = requiredInputs.filter(([key]) => inputs[key] === undefined);
|
|
94
|
+
if (missingRequiredInputs.length === 0) {
|
|
95
|
+
return inputs;
|
|
96
|
+
}
|
|
97
|
+
log_1.default.addNewLineIfNone();
|
|
98
|
+
log_1.default.log('Some required inputs are missing. Please provide them:');
|
|
99
|
+
const nextInputs = { ...inputs };
|
|
100
|
+
for (const [key, spec] of missingRequiredInputs) {
|
|
101
|
+
const value = await promptForMissingInputAsync({ key, spec });
|
|
102
|
+
nextInputs[key] = value;
|
|
103
|
+
}
|
|
104
|
+
return nextInputs;
|
|
105
|
+
}
|
|
106
|
+
exports.maybePromptForMissingInputsAsync = maybePromptForMissingInputsAsync;
|
|
107
|
+
async function promptForMissingInputAsync({ key, spec, }) {
|
|
108
|
+
const message = spec.description ? `${key} (${spec.description})` : key;
|
|
109
|
+
switch (spec.type) {
|
|
110
|
+
case 'boolean': {
|
|
111
|
+
const { value } = await (0, prompts_1.promptAsync)({
|
|
112
|
+
type: 'confirm',
|
|
113
|
+
name: 'value',
|
|
114
|
+
message,
|
|
115
|
+
initial: spec.default,
|
|
116
|
+
});
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
case 'number': {
|
|
120
|
+
const { value } = await (0, prompts_1.promptAsync)({
|
|
121
|
+
type: 'number',
|
|
122
|
+
name: 'value',
|
|
123
|
+
message,
|
|
124
|
+
initial: spec.default,
|
|
125
|
+
validate: (val) => {
|
|
126
|
+
if (isNaN(val)) {
|
|
127
|
+
return 'Please enter a valid number';
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
case 'choice': {
|
|
135
|
+
const { value } = await (0, prompts_1.promptAsync)({
|
|
136
|
+
type: 'select',
|
|
137
|
+
name: 'value',
|
|
138
|
+
message,
|
|
139
|
+
choices: spec.options.map(option => ({
|
|
140
|
+
title: option,
|
|
141
|
+
value: option,
|
|
142
|
+
})),
|
|
143
|
+
initial: spec.default,
|
|
144
|
+
});
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
case 'string':
|
|
148
|
+
case 'environment':
|
|
149
|
+
default: {
|
|
150
|
+
const { value } = await (0, prompts_1.promptAsync)({
|
|
151
|
+
type: 'text',
|
|
152
|
+
name: 'value',
|
|
153
|
+
message,
|
|
154
|
+
initial: spec.default,
|
|
155
|
+
validate: (val) => {
|
|
156
|
+
if (spec.required && (!val || val.trim() === '')) {
|
|
157
|
+
return 'This field is required';
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -161,7 +161,7 @@ const workflowStepSelectionAction = async (prevState) => {
|
|
|
161
161
|
if (!job) {
|
|
162
162
|
return moveToWorkflowSelectionErrorState(prevState, 'No job found');
|
|
163
163
|
}
|
|
164
|
-
const logs = await (0, utils_1.
|
|
164
|
+
const logs = await (0, utils_1.fetchAndProcessLogsFromJobAsync)(prevState, job);
|
|
165
165
|
if (!logs) {
|
|
166
166
|
return moveToWorkflowSelectionErrorState(prevState, 'No logs found');
|
|
167
167
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WorkflowTriggerType = void 0;
|
|
4
4
|
/*
|
|
5
|
-
* Utility types for workflow commands
|
|
5
|
+
* Utility types and Zod definitions for workflow commands
|
|
6
6
|
*/
|
|
7
7
|
var WorkflowTriggerType;
|
|
8
8
|
(function (WorkflowTriggerType) {
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
import { WorkflowJobResult, WorkflowLogs, WorkflowRunResult, WorkflowTriggerType } from './types';
|
|
2
|
-
import { WorkflowRunFragment } from '../../graphql/generated';
|
|
1
|
+
import { WorkflowJobResult, WorkflowLogLine, WorkflowLogs, WorkflowRunResult, WorkflowTriggerType } from './types';
|
|
2
|
+
import { WorkflowRunByIdWithJobsQuery, WorkflowRunFragment } from '../../graphql/generated';
|
|
3
3
|
import { Choice } from '../../prompts';
|
|
4
|
+
import { ExpoGraphqlClient } from '../context/contextUtils/createGraphqlClient';
|
|
4
5
|
export declare function computeTriggerInfoForWorkflowRun(run: WorkflowRunFragment): {
|
|
5
6
|
triggerType: WorkflowTriggerType;
|
|
6
7
|
trigger: string | null;
|
|
7
8
|
};
|
|
8
9
|
export declare function choiceFromWorkflowRun(run: WorkflowRunResult): Choice;
|
|
9
10
|
export declare function choiceFromWorkflowJob(job: WorkflowJobResult, index: number): Choice;
|
|
10
|
-
export declare function choicesFromWorkflowLogs(logs: WorkflowLogs): Choice
|
|
11
|
+
export declare function choicesFromWorkflowLogs(logs: WorkflowLogs): (Choice & {
|
|
12
|
+
name: string;
|
|
13
|
+
status: string;
|
|
14
|
+
logLines: WorkflowLogLine[] | undefined;
|
|
15
|
+
})[];
|
|
11
16
|
export declare function processWorkflowRuns(runs: WorkflowRunFragment[]): WorkflowRunResult[];
|
|
12
|
-
export declare function
|
|
17
|
+
export declare function fetchAndProcessLogsFromJobAsync(state: {
|
|
18
|
+
graphqlClient: ExpoGraphqlClient;
|
|
19
|
+
}, job: WorkflowJobResult): Promise<WorkflowLogs | null>;
|
|
20
|
+
export declare function infoForActiveWorkflowRunAsync(graphqlClient: ExpoGraphqlClient, workflowRun: WorkflowRunByIdWithJobsQuery['workflowRuns']['byId'], maxLogLines?: number): Promise<string>;
|
|
21
|
+
export declare function infoForFailedWorkflowRunAsync(graphqlClient: ExpoGraphqlClient, workflowRun: WorkflowRunByIdWithJobsQuery['workflowRuns']['byId']): Promise<string>;
|
|
22
|
+
export declare function fileExistsAsync(filePath: string): Promise<boolean>;
|
|
23
|
+
export declare function maybeReadStdinAsync(): Promise<string | null>;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.maybeReadStdinAsync = exports.fileExistsAsync = exports.infoForFailedWorkflowRunAsync = exports.infoForActiveWorkflowRunAsync = exports.fetchAndProcessLogsFromJobAsync = exports.processWorkflowRuns = exports.choicesFromWorkflowLogs = exports.choiceFromWorkflowJob = exports.choiceFromWorkflowRun = exports.computeTriggerInfoForWorkflowRun = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("node:fs"));
|
|
4
6
|
const fetchLogs_1 = require("./fetchLogs");
|
|
5
7
|
const types_1 = require("./types");
|
|
6
8
|
const generated_1 = require("../../graphql/generated");
|
|
9
|
+
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
10
|
+
const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
|
|
7
11
|
function computeTriggerInfoForWorkflowRun(run) {
|
|
8
12
|
let triggerType = types_1.WorkflowTriggerType.OTHER;
|
|
9
13
|
let trigger = '';
|
|
@@ -58,14 +62,19 @@ function choiceFromWorkflowJob(job, index) {
|
|
|
58
62
|
}
|
|
59
63
|
exports.choiceFromWorkflowJob = choiceFromWorkflowJob;
|
|
60
64
|
function choicesFromWorkflowLogs(logs) {
|
|
61
|
-
return Array.from(logs.keys())
|
|
65
|
+
return Array.from(logs.keys())
|
|
66
|
+
.map(step => {
|
|
62
67
|
const logLines = logs.get(step);
|
|
63
68
|
const stepStatus = logLines?.filter((line) => line.marker === 'end-step')[0]?.result ?? '';
|
|
64
69
|
return {
|
|
65
70
|
title: `${step} - ${stepStatus}`,
|
|
71
|
+
name: step,
|
|
72
|
+
status: stepStatus,
|
|
66
73
|
value: step,
|
|
74
|
+
logLines,
|
|
67
75
|
};
|
|
68
|
-
})
|
|
76
|
+
})
|
|
77
|
+
.filter(step => step.status !== 'skipped');
|
|
69
78
|
}
|
|
70
79
|
exports.choicesFromWorkflowLogs = choicesFromWorkflowLogs;
|
|
71
80
|
function processWorkflowRuns(runs) {
|
|
@@ -88,29 +97,158 @@ function processWorkflowRuns(runs) {
|
|
|
88
97
|
});
|
|
89
98
|
}
|
|
90
99
|
exports.processWorkflowRuns = processWorkflowRuns;
|
|
91
|
-
async function
|
|
92
|
-
|
|
100
|
+
async function fetchAndProcessLogsFromJobAsync(state, job) {
|
|
101
|
+
let rawLogs;
|
|
102
|
+
switch (job.type) {
|
|
103
|
+
case generated_1.WorkflowJobType.Build:
|
|
104
|
+
case generated_1.WorkflowJobType.Repack:
|
|
105
|
+
rawLogs = await (0, fetchLogs_1.fetchRawLogsForBuildJobAsync)(state, job);
|
|
106
|
+
break;
|
|
107
|
+
default:
|
|
108
|
+
rawLogs = await (0, fetchLogs_1.fetchRawLogsForCustomJobAsync)(job);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
93
111
|
if (!rawLogs) {
|
|
94
112
|
return null;
|
|
95
113
|
}
|
|
114
|
+
log_1.default.debug(`rawLogs = ${JSON.stringify(rawLogs, null, 2)}`);
|
|
96
115
|
const logs = new Map();
|
|
97
116
|
const logKeys = new Set();
|
|
98
|
-
rawLogs.split('\n').forEach(line => {
|
|
117
|
+
rawLogs.split('\n').forEach((line, index) => {
|
|
118
|
+
log_1.default.debug(`line ${index} = ${JSON.stringify(line, null, 2)}`);
|
|
99
119
|
try {
|
|
100
120
|
const parsedLine = JSON.parse(line);
|
|
101
|
-
const { result, marker } = parsedLine;
|
|
102
|
-
const { buildStepDisplayName, buildStepInternalId, time, msg } = parsedLine;
|
|
121
|
+
const { buildStepDisplayName, buildStepInternalId, time, msg, result, marker, err } = parsedLine;
|
|
103
122
|
const stepId = buildStepDisplayName ?? buildStepInternalId;
|
|
104
123
|
if (stepId) {
|
|
105
124
|
if (!logKeys.has(stepId)) {
|
|
106
125
|
logKeys.add(stepId);
|
|
107
126
|
logs.set(stepId, []);
|
|
108
127
|
}
|
|
109
|
-
logs.get(stepId)?.push({ time, msg, result, marker });
|
|
128
|
+
logs.get(stepId)?.push({ time, msg, result, marker, err });
|
|
110
129
|
}
|
|
111
130
|
}
|
|
112
131
|
catch { }
|
|
113
132
|
});
|
|
114
133
|
return logs;
|
|
115
134
|
}
|
|
116
|
-
exports.
|
|
135
|
+
exports.fetchAndProcessLogsFromJobAsync = fetchAndProcessLogsFromJobAsync;
|
|
136
|
+
function descriptionForJobStatus(status) {
|
|
137
|
+
switch (status) {
|
|
138
|
+
case generated_1.WorkflowJobStatus.New:
|
|
139
|
+
return 'Waiting for worker';
|
|
140
|
+
case generated_1.WorkflowJobStatus.InProgress:
|
|
141
|
+
return 'In progress';
|
|
142
|
+
case generated_1.WorkflowJobStatus.Success:
|
|
143
|
+
return 'Completed successfully';
|
|
144
|
+
case generated_1.WorkflowJobStatus.Failure:
|
|
145
|
+
return 'Failed';
|
|
146
|
+
case generated_1.WorkflowJobStatus.Canceled:
|
|
147
|
+
return 'Canceled';
|
|
148
|
+
case generated_1.WorkflowJobStatus.Skipped:
|
|
149
|
+
return 'Skipped';
|
|
150
|
+
case generated_1.WorkflowJobStatus.ActionRequired:
|
|
151
|
+
return 'Waiting for action';
|
|
152
|
+
case generated_1.WorkflowJobStatus.PendingCancel:
|
|
153
|
+
return 'Pending cancel';
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function infoForActiveWorkflowRunAsync(graphqlClient, workflowRun, maxLogLines = 5 // -1 means no limit
|
|
157
|
+
) {
|
|
158
|
+
const statusLines = [];
|
|
159
|
+
const statusValues = [];
|
|
160
|
+
for (const job of workflowRun.jobs) {
|
|
161
|
+
statusValues.push({ label: '', value: '' });
|
|
162
|
+
statusValues.push({ label: ' Job', value: job.name });
|
|
163
|
+
statusValues.push({ label: ' Status', value: descriptionForJobStatus(job.status) });
|
|
164
|
+
if (job.status !== generated_1.WorkflowJobStatus.InProgress) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const logs = await fetchAndProcessLogsFromJobAsync({ graphqlClient }, job);
|
|
168
|
+
const steps = logs ? choicesFromWorkflowLogs(logs) : [];
|
|
169
|
+
if (steps.length > 0) {
|
|
170
|
+
const currentStep = steps[steps.length - 1];
|
|
171
|
+
statusValues.push({ label: ' Current step', value: currentStep.name });
|
|
172
|
+
if (currentStep?.logLines?.length) {
|
|
173
|
+
statusValues.push({ label: ' Current logs', value: '' });
|
|
174
|
+
const currentLogs = currentStep.logLines
|
|
175
|
+
?.map(line => line.msg)
|
|
176
|
+
.filter((_, index) => {
|
|
177
|
+
if (maxLogLines === -1) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return index > (currentStep.logLines?.length ?? 0) - maxLogLines;
|
|
181
|
+
}) ?? [];
|
|
182
|
+
for (const log of currentLogs) {
|
|
183
|
+
statusValues.push({ label: '', value: log });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
statusValues.push({ label: '', value: '' });
|
|
189
|
+
statusLines.push((0, formatFields_1.default)(statusValues));
|
|
190
|
+
return statusLines.join('\n');
|
|
191
|
+
}
|
|
192
|
+
exports.infoForActiveWorkflowRunAsync = infoForActiveWorkflowRunAsync;
|
|
193
|
+
async function infoForFailedWorkflowRunAsync(graphqlClient, workflowRun) {
|
|
194
|
+
const statusLines = [];
|
|
195
|
+
const statusValues = [];
|
|
196
|
+
for (const job of workflowRun.jobs) {
|
|
197
|
+
if (job.status !== generated_1.WorkflowJobStatus.Failure) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const logs = await fetchAndProcessLogsFromJobAsync({ graphqlClient }, job);
|
|
201
|
+
const steps = logs ? choicesFromWorkflowLogs(logs) : [];
|
|
202
|
+
statusValues.push({ label: '', value: '' });
|
|
203
|
+
statusValues.push({ label: ' Failed job', value: job.name });
|
|
204
|
+
if (steps.length > 0) {
|
|
205
|
+
const failedStep = steps.find(step => step.status === 'fail');
|
|
206
|
+
if (failedStep) {
|
|
207
|
+
const logs = failedStep.logLines?.map(line => line.msg) ?? [];
|
|
208
|
+
statusValues.push({ label: ' Failed step', value: failedStep.name });
|
|
209
|
+
statusValues.push({
|
|
210
|
+
label: ' Logs for failed step',
|
|
211
|
+
value: '',
|
|
212
|
+
});
|
|
213
|
+
for (const log of logs) {
|
|
214
|
+
statusValues.push({ label: '', value: log });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
statusValues.push({ label: '', value: '' });
|
|
220
|
+
statusLines.push((0, formatFields_1.default)(statusValues));
|
|
221
|
+
return statusLines.join('\n');
|
|
222
|
+
}
|
|
223
|
+
exports.infoForFailedWorkflowRunAsync = infoForFailedWorkflowRunAsync;
|
|
224
|
+
async function fileExistsAsync(filePath) {
|
|
225
|
+
return await fs.promises
|
|
226
|
+
.access(filePath, fs.constants.F_OK)
|
|
227
|
+
.then(() => true)
|
|
228
|
+
.catch(() => false);
|
|
229
|
+
}
|
|
230
|
+
exports.fileExistsAsync = fileExistsAsync;
|
|
231
|
+
async function maybeReadStdinAsync() {
|
|
232
|
+
// Check if there's data on stdin
|
|
233
|
+
if (process.stdin.isTTY) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
return await new Promise((resolve, reject) => {
|
|
237
|
+
let data = '';
|
|
238
|
+
process.stdin.setEncoding('utf8');
|
|
239
|
+
process.stdin.on('readable', () => {
|
|
240
|
+
let chunk;
|
|
241
|
+
while ((chunk = process.stdin.read()) !== null) {
|
|
242
|
+
data += chunk;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
process.stdin.on('end', () => {
|
|
246
|
+
const trimmedData = data.trim();
|
|
247
|
+
resolve(trimmedData || null);
|
|
248
|
+
});
|
|
249
|
+
process.stdin.on('error', err => {
|
|
250
|
+
reject(err);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
exports.maybeReadStdinAsync = maybeReadStdinAsync;
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
* description: "Type of deployment"
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
import { z } from 'zod';
|
|
43
42
|
import EasCommand from '../../commandUtils/EasCommand';
|
|
44
43
|
export default class WorkflowRun extends EasCommand {
|
|
45
44
|
static description: string;
|
|
@@ -63,27 +62,3 @@ export default class WorkflowRun extends EasCommand {
|
|
|
63
62
|
};
|
|
64
63
|
runAsync(): Promise<void>;
|
|
65
64
|
}
|
|
66
|
-
export declare function parseInputs(inputFlags: string[]): Record<string, string>;
|
|
67
|
-
export declare function maybeReadStdinAsync(): Promise<string | null>;
|
|
68
|
-
export declare function parseJsonInputs(jsonString: string): Record<string, string>;
|
|
69
|
-
export declare const WorkflowDispatchInputZ: z.ZodIntersection<z.ZodObject<{
|
|
70
|
-
description: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
71
|
-
required: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodNumber, z.ZodBoolean>, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>;
|
|
72
|
-
}, z.core.$strip>, z.ZodUnion<readonly [z.ZodObject<{
|
|
73
|
-
type: z.ZodDefault<z.ZodLiteral<"string">>;
|
|
74
|
-
default: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
75
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
76
|
-
type: z.ZodLiteral<"boolean">;
|
|
77
|
-
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodNumber, z.ZodBoolean>, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>;
|
|
78
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
79
|
-
type: z.ZodLiteral<"number">;
|
|
80
|
-
default: z.ZodOptional<z.ZodNumber>;
|
|
81
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
82
|
-
type: z.ZodLiteral<"choice">;
|
|
83
|
-
default: z.ZodOptional<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
84
|
-
options: z.ZodArray<z.ZodCodec<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodString>>;
|
|
85
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
86
|
-
type: z.ZodLiteral<"environment">;
|
|
87
|
-
default: z.ZodOptional<z.ZodString>;
|
|
88
|
-
}, z.core.$strip>]>>;
|
|
89
|
-
export declare function parseWorkflowInputsFromYaml(yamlConfig: string): Record<string, z.infer<typeof WorkflowDispatchInputZ>>;
|