jira-sprinter 1.0.3 → 2.0.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 +3 -1
- package/dist/main.js +23322 -27282
- package/package.json +13 -13
- package/src/cli.ts +153 -85
- package/src/jira.ts +58 -27
- package/src/util.ts +5 -1
- package/vitest.config.ts +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jira-sprinter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Small CLI tool to manage sprints in JIRA Board",
|
|
5
5
|
"main": "src/main.ts",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"update-snapshots": "vitest run --update",
|
|
17
17
|
"all": "yarn && yarn run build && yarn run format && yarn test"
|
|
18
18
|
},
|
|
19
|
-
"packageManager": "yarn@4.
|
|
19
|
+
"packageManager": "yarn@4.12.0",
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
22
22
|
"url": "git+https://github.com/redhat-plumbers-in-action/sprinter.git"
|
|
@@ -34,22 +34,22 @@
|
|
|
34
34
|
"scrum"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@inquirer/checkbox": "^
|
|
38
|
-
"@inquirer/select": "
|
|
37
|
+
"@inquirer/checkbox": "^5.1.2",
|
|
38
|
+
"@inquirer/select": "5.1.2",
|
|
39
39
|
"@total-typescript/ts-reset": "0.6.1",
|
|
40
40
|
"chalk": "5.6.2",
|
|
41
|
-
"commander": "14.0.
|
|
42
|
-
"dotenv": "17.
|
|
43
|
-
"jira.js": "5.
|
|
44
|
-
"zod": "4.
|
|
41
|
+
"commander": "14.0.3",
|
|
42
|
+
"dotenv": "17.3.1",
|
|
43
|
+
"jira.js": "5.3.1",
|
|
44
|
+
"zod": "4.3.6"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@types/node": "
|
|
48
|
-
"@vitest/coverage-v8": "
|
|
49
|
-
"esbuild": "0.
|
|
50
|
-
"prettier": "3.
|
|
47
|
+
"@types/node": "25.5.0",
|
|
48
|
+
"@vitest/coverage-v8": "4.1.0",
|
|
49
|
+
"esbuild": "0.27.4",
|
|
50
|
+
"prettier": "3.8.1",
|
|
51
51
|
"ts-node": "10.9.2",
|
|
52
52
|
"typescript": "5.9.3",
|
|
53
|
-
"vitest": "
|
|
53
|
+
"vitest": "4.1.0"
|
|
54
54
|
}
|
|
55
55
|
}
|
package/src/cli.ts
CHANGED
|
@@ -5,7 +5,12 @@ import checkbox from '@inquirer/checkbox';
|
|
|
5
5
|
|
|
6
6
|
import { Jira } from './jira';
|
|
7
7
|
import { Logger } from './logger';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getDefaultValue,
|
|
10
|
+
getOptions,
|
|
11
|
+
getUserFromLogin,
|
|
12
|
+
tokenUnavailable,
|
|
13
|
+
} from './util';
|
|
9
14
|
|
|
10
15
|
import { SearchResults } from 'jira.js/dist/esm/types/agile/models';
|
|
11
16
|
import {
|
|
@@ -15,7 +20,7 @@ import {
|
|
|
15
20
|
colorSizeSchema,
|
|
16
21
|
Size,
|
|
17
22
|
} from './schema/jira';
|
|
18
|
-
import { Issue } from 'jira.js/dist/esm/types/
|
|
23
|
+
import { Issue } from 'jira.js/dist/esm/types/version3/models/issue';
|
|
19
24
|
|
|
20
25
|
export function cli(): Command {
|
|
21
26
|
const program = new Command();
|
|
@@ -23,7 +28,7 @@ export function cli(): Command {
|
|
|
23
28
|
program
|
|
24
29
|
.name('jira-sprinter')
|
|
25
30
|
.description('🏃 Small CLI tool to manage sprints in JIRA Board')
|
|
26
|
-
.version('
|
|
31
|
+
.version('2.0.0');
|
|
27
32
|
|
|
28
33
|
program
|
|
29
34
|
.option('-b, --board [board]', 'Jira Board ID', getDefaultValue('BOARD'))
|
|
@@ -32,6 +37,11 @@ export function cli(): Command {
|
|
|
32
37
|
'Jira Assignee',
|
|
33
38
|
getDefaultValue('ASSIGNEE')
|
|
34
39
|
)
|
|
40
|
+
.option(
|
|
41
|
+
'-y, --yolo',
|
|
42
|
+
'YOLO mode, dangerously skip all questions, apply default values (use with caution!)',
|
|
43
|
+
getDefaultValue('YOLO')
|
|
44
|
+
)
|
|
35
45
|
.option('-n, --nocolor', 'Disable color output', getDefaultValue('NOCOLOR'))
|
|
36
46
|
.option('-x, --dry', 'dry run', getDefaultValue('DRY'));
|
|
37
47
|
|
|
@@ -47,10 +57,11 @@ const runProgram = async () => {
|
|
|
47
57
|
|
|
48
58
|
const token = process.env.JIRA_API_TOKEN ?? tokenUnavailable();
|
|
49
59
|
const jira = new Jira(
|
|
50
|
-
'https://
|
|
60
|
+
'https://redhat.atlassian.net',
|
|
51
61
|
token,
|
|
52
62
|
options.dry,
|
|
53
|
-
logger
|
|
63
|
+
logger,
|
|
64
|
+
getUserFromLogin()
|
|
54
65
|
);
|
|
55
66
|
|
|
56
67
|
const version = await jira.getVersion();
|
|
@@ -88,11 +99,6 @@ const runProgram = async () => {
|
|
|
88
99
|
process.exit(0);
|
|
89
100
|
}
|
|
90
101
|
|
|
91
|
-
// TODO:
|
|
92
|
-
// Show issue - allow to split it into tasks
|
|
93
|
-
// Allow to set story points and assignee
|
|
94
|
-
// add task into sprint
|
|
95
|
-
|
|
96
102
|
for (const issue of issues) {
|
|
97
103
|
logger.log(
|
|
98
104
|
`\n${issueTypeSchema.parse(issue.fields?.issuetype.name)} ${issue.key} - ${chalk.bold(issueStatusSchema.parse(issue.fields?.status.name))} - ${chalk.italic(issue.fields?.assignee?.displayName ?? '')}`
|
|
@@ -105,36 +111,87 @@ const runProgram = async () => {
|
|
|
105
111
|
);
|
|
106
112
|
|
|
107
113
|
const availableTasks = [
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
{
|
|
115
|
+
name: 'DEV Task',
|
|
116
|
+
summary: '[DEV Task]:',
|
|
117
|
+
label: 'dev_task',
|
|
118
|
+
value: 14476,
|
|
119
|
+
checked: true,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'QE Task',
|
|
123
|
+
summary: '[QE Task]:',
|
|
124
|
+
label: 'qe_task',
|
|
125
|
+
value: 14480,
|
|
126
|
+
checked: true,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'Upstream',
|
|
130
|
+
summary: '[Upstream]:',
|
|
131
|
+
label: 'upstream_task',
|
|
132
|
+
value: 14475,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'Root Cause Analysis Task',
|
|
136
|
+
summary: '[Root Cause Analysis Task]:',
|
|
137
|
+
label: 'root_cause_analysis_task',
|
|
138
|
+
value: 14483,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'Preliminary Testing Task',
|
|
142
|
+
summary: '[Preliminary Testing Task]:',
|
|
143
|
+
label: 'preliminary_testing_task',
|
|
144
|
+
value: 14478,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'Integration Testing',
|
|
148
|
+
summary: '[Integration Testing Task]:',
|
|
149
|
+
label: 'integration_testing_task',
|
|
150
|
+
value: 14481,
|
|
151
|
+
},
|
|
114
152
|
];
|
|
115
153
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
154
|
+
let answer: number[] = [];
|
|
155
|
+
if (!options.yolo) {
|
|
156
|
+
answer = await checkbox({
|
|
157
|
+
message: `Split ${chalk.bold(issue.key)} into following tasks:\n`,
|
|
158
|
+
choices: [
|
|
159
|
+
...availableTasks.map(task => {
|
|
160
|
+
const isTaskOpen = issue.fields?.issuelinks?.some(
|
|
161
|
+
l =>
|
|
162
|
+
l.type?.outward === 'split to' &&
|
|
163
|
+
l.outwardIssue?.fields?.summary.startsWith(task.summary) &&
|
|
164
|
+
l.outwardIssue?.fields?.status.name !== 'Closed'
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
name: colorTaskSchema(task.name),
|
|
169
|
+
value: task.value,
|
|
170
|
+
checked: task.checked && !isTaskOpen,
|
|
171
|
+
disabled: isTaskOpen,
|
|
172
|
+
};
|
|
173
|
+
}),
|
|
174
|
+
new Separator(),
|
|
175
|
+
{ name: 'SKIP', value: -1 },
|
|
176
|
+
{ name: 'EXIT', value: -2 },
|
|
177
|
+
],
|
|
178
|
+
loop: false,
|
|
179
|
+
pageSize: 10,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
if (answer.includes(-1)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (answer.includes(-2)) {
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
134
189
|
}
|
|
135
190
|
|
|
136
|
-
if (
|
|
137
|
-
|
|
191
|
+
if (options.yolo) {
|
|
192
|
+
answer = availableTasks
|
|
193
|
+
.filter(task => task.checked ?? false)
|
|
194
|
+
.map(task => task.value);
|
|
138
195
|
}
|
|
139
196
|
|
|
140
197
|
// Create tasks
|
|
@@ -157,7 +214,6 @@ const runProgram = async () => {
|
|
|
157
214
|
}
|
|
158
215
|
}
|
|
159
216
|
|
|
160
|
-
// loop through tasks and and set sprint, assignee and story points
|
|
161
217
|
for (const task of tasks) {
|
|
162
218
|
// Skip QE Task
|
|
163
219
|
if (task.fields.summary.includes('QE Task')) {
|
|
@@ -165,57 +221,69 @@ const runProgram = async () => {
|
|
|
165
221
|
}
|
|
166
222
|
|
|
167
223
|
logger.log(`${chalk.italic(task.fields.summary)}`);
|
|
168
|
-
const storyPointsAnswer: Size = await select({
|
|
169
|
-
message: 'Story Points',
|
|
170
|
-
choices: [
|
|
171
|
-
{
|
|
172
|
-
name: colorSizeSchema.parse(0),
|
|
173
|
-
value: 0,
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
name: colorSizeSchema.parse(1),
|
|
177
|
-
value: 1,
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
name: colorSizeSchema.parse(2),
|
|
181
|
-
value: 2,
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
name: colorSizeSchema.parse(3),
|
|
185
|
-
value: 3,
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
name: colorSizeSchema.parse(5),
|
|
189
|
-
value: 5,
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
name: colorSizeSchema.parse(8),
|
|
193
|
-
value: 8,
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
name: colorSizeSchema.parse(13),
|
|
197
|
-
value: 13,
|
|
198
|
-
},
|
|
199
|
-
],
|
|
200
|
-
default: issue.fields?.[jira.fields.storyPoints] ?? 3,
|
|
201
|
-
pageSize: 6,
|
|
202
|
-
loop: false,
|
|
203
|
-
});
|
|
204
224
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
225
|
+
let storyPointsAnswer: Size | undefined;
|
|
226
|
+
let addToSprintAnswer: boolean | undefined;
|
|
227
|
+
if (!options.yolo) {
|
|
228
|
+
storyPointsAnswer = await select({
|
|
229
|
+
message: 'Story Points',
|
|
230
|
+
choices: [
|
|
231
|
+
{
|
|
232
|
+
name: colorSizeSchema.parse(0),
|
|
233
|
+
value: 0,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: colorSizeSchema.parse(1),
|
|
237
|
+
value: 1,
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: colorSizeSchema.parse(2),
|
|
241
|
+
value: 2,
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: colorSizeSchema.parse(3),
|
|
245
|
+
value: 3,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: colorSizeSchema.parse(5),
|
|
249
|
+
value: 5,
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: colorSizeSchema.parse(8),
|
|
253
|
+
value: 8,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: colorSizeSchema.parse(13),
|
|
257
|
+
value: 13,
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
default: issue.fields?.[jira.fields.storyPoints] ?? 3,
|
|
261
|
+
pageSize: 6,
|
|
262
|
+
loop: false,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
addToSprintAnswer = await select({
|
|
266
|
+
message: 'Add to sprint',
|
|
267
|
+
choices: [
|
|
268
|
+
{ name: `${chalk.green('Yes')}`, value: true },
|
|
269
|
+
{ name: `${chalk.red('No')}`, value: false },
|
|
270
|
+
],
|
|
271
|
+
default: true,
|
|
272
|
+
pageSize: 2,
|
|
273
|
+
loop: false,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (options.yolo) {
|
|
278
|
+
storyPointsAnswer = issue.fields?.[jira.fields.storyPoints];
|
|
279
|
+
addToSprintAnswer = sprintOrBacklog != -1;
|
|
280
|
+
logger.log(
|
|
281
|
+
`Setting story points to ${colorSizeSchema.parse(storyPointsAnswer)} and adding it to the sprint.`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
215
284
|
|
|
216
|
-
// update task
|
|
217
285
|
await jira.setValues(task.key, {
|
|
218
|
-
assignee: issue.fields?.assignee?.
|
|
286
|
+
assignee: issue.fields?.assignee?.accountId,
|
|
219
287
|
size: storyPointsAnswer,
|
|
220
288
|
sprint:
|
|
221
289
|
addToSprintAnswer && sprintOrBacklog != -1
|
package/src/jira.ts
CHANGED
|
@@ -1,35 +1,38 @@
|
|
|
1
|
-
import { AgileClient,
|
|
1
|
+
import { AgileClient, Version3Client } from 'jira.js';
|
|
2
2
|
import { SearchResults, Sprint } from 'jira.js/dist/esm/types/agile/models';
|
|
3
|
+
import { Issue } from 'jira.js/dist/esm/types/version3/models/issue';
|
|
3
4
|
|
|
4
5
|
import { raise } from './util';
|
|
5
6
|
import { Size } from './schema/jira';
|
|
6
7
|
import { Logger } from './logger';
|
|
7
8
|
|
|
8
9
|
export class Jira {
|
|
9
|
-
readonly api:
|
|
10
|
+
readonly api: Version3Client;
|
|
10
11
|
readonly agile: AgileClient;
|
|
11
12
|
readonly fields = {
|
|
12
|
-
automation: '
|
|
13
|
+
automation: 'customfield_10553',
|
|
13
14
|
assignee: 'assignee',
|
|
14
15
|
priority: 'priority',
|
|
15
|
-
severity: '
|
|
16
|
-
sprint: '
|
|
17
|
-
storyPoints: '
|
|
16
|
+
severity: 'customfield_10840',
|
|
17
|
+
sprint: 'customfield_10020',
|
|
18
|
+
storyPoints: 'customfield_10028',
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
readonly issuesWithoutTasksJQL = `
|
|
21
|
+
readonly issuesWithoutTasksJQL = `(labels not in (upstream_task, dev_task, qe_task, root_cause_analysis_task, preliminary_testing_task, integration_testing_task) OR labels is EMPTY) AND type in (Bug, "Story", Vulnerability) AND Project = RHEL AND statusCategory != Done`;
|
|
21
22
|
|
|
22
23
|
constructor(
|
|
23
24
|
readonly instance: string,
|
|
24
25
|
apiToken: string,
|
|
25
26
|
readonly dry: boolean,
|
|
26
|
-
readonly logger: Logger
|
|
27
|
+
readonly logger: Logger,
|
|
28
|
+
email: string
|
|
27
29
|
) {
|
|
28
|
-
this.api = new
|
|
30
|
+
this.api = new Version3Client({
|
|
29
31
|
host: instance,
|
|
30
32
|
authentication: {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
basic: {
|
|
34
|
+
email,
|
|
35
|
+
apiToken,
|
|
33
36
|
},
|
|
34
37
|
},
|
|
35
38
|
});
|
|
@@ -37,8 +40,9 @@ export class Jira {
|
|
|
37
40
|
this.agile = new AgileClient({
|
|
38
41
|
host: instance,
|
|
39
42
|
authentication: {
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
basic: {
|
|
44
|
+
email,
|
|
45
|
+
apiToken,
|
|
42
46
|
},
|
|
43
47
|
},
|
|
44
48
|
});
|
|
@@ -78,11 +82,12 @@ export class Jira {
|
|
|
78
82
|
'assignee',
|
|
79
83
|
'priority',
|
|
80
84
|
'components',
|
|
81
|
-
// sprint
|
|
82
85
|
this.fields.storyPoints,
|
|
83
86
|
this.fields.severity,
|
|
87
|
+
'issuelinks',
|
|
84
88
|
],
|
|
85
89
|
});
|
|
90
|
+
|
|
86
91
|
return response.issues;
|
|
87
92
|
}
|
|
88
93
|
|
|
@@ -115,18 +120,43 @@ export class Jira {
|
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
async getlinkedTasks(issue: string, expectedTasks: string[]) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
if (this.dry) {
|
|
124
|
+
this.logger.log(`Would get linked tasks for issue: ${issue}`);
|
|
125
|
+
return [
|
|
126
|
+
{
|
|
127
|
+
key: 'RHEL-1234',
|
|
128
|
+
fields: {
|
|
129
|
+
summary: '[DEV Task] Test Task',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
key: 'RHEL-1235',
|
|
134
|
+
fields: {
|
|
135
|
+
summary: '[QE Task] Test Task',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
key: 'RHEL-1236',
|
|
140
|
+
fields: {
|
|
141
|
+
summary: '[Upstream] Test Task',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
] as unknown as Issue[];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const response =
|
|
148
|
+
await this.api.issueSearch.searchForIssuesUsingJqlEnhancedSearchPost({
|
|
149
|
+
jql: `issue in linkedIssues("${issue}") AND type = Task AND status = New AND (${this.composeTaskSummaryJQL(expectedTasks)})`,
|
|
150
|
+
fields: [
|
|
151
|
+
'id',
|
|
152
|
+
'issuetype',
|
|
153
|
+
'status',
|
|
154
|
+
'components',
|
|
155
|
+
'summary',
|
|
156
|
+
'assignee',
|
|
157
|
+
this.fields.storyPoints,
|
|
158
|
+
],
|
|
159
|
+
});
|
|
130
160
|
|
|
131
161
|
return response.issues ?? [];
|
|
132
162
|
}
|
|
@@ -139,6 +169,7 @@ export class Jira {
|
|
|
139
169
|
return;
|
|
140
170
|
}
|
|
141
171
|
|
|
172
|
+
this.logger.log(`Creating tasks: ${tasks.join(', ')} for issue: ${issue}`);
|
|
142
173
|
await this.api.issues.editIssue({
|
|
143
174
|
issueIdOrKey: issue,
|
|
144
175
|
fields: {
|
|
@@ -157,7 +188,7 @@ export class Jira {
|
|
|
157
188
|
}
|
|
158
189
|
) {
|
|
159
190
|
const assigneeValue = values.assignee
|
|
160
|
-
? { [this.fields.assignee]: {
|
|
191
|
+
? { [this.fields.assignee]: { accountId: values.assignee } }
|
|
161
192
|
: {};
|
|
162
193
|
const storyPointsValue =
|
|
163
194
|
values.size !== undefined
|
package/src/util.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function isDefaultValuesDisabled(): boolean {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function getDefaultValue(
|
|
24
|
-
envName: 'ASSIGNEE' | 'BOARD' | 'NOCOLOR' | 'DRY'
|
|
24
|
+
envName: 'ASSIGNEE' | 'BOARD' | 'NOCOLOR' | 'DRY' | 'YOLO'
|
|
25
25
|
) {
|
|
26
26
|
if (isDefaultValuesDisabled()) {
|
|
27
27
|
return undefined;
|
|
@@ -37,6 +37,10 @@ export function getDefaultValue(
|
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
if (envName === 'YOLO' && !value) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
return value;
|
|
41
45
|
}
|
|
42
46
|
|
package/vitest.config.ts
ADDED