jira-sprinter 2.5.1 → 2.7.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 +7 -3
- package/dist/main.js +26735 -6439
- package/package.json +10 -9
- package/src/auto.ts +4 -1
- package/src/cli.ts +9 -4
- package/src/jira.ts +18 -2
- package/src/pp-sync.ts +4 -0
- package/src/schema/deadlines.ts +48 -8
- package/src/util.ts +1 -0
- /package/{vitest.config.ts → vitest.config.mts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jira-sprinter",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Small CLI tool to manage sprints in JIRA Board",
|
|
5
5
|
"main": "src/main.ts",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -34,24 +34,25 @@
|
|
|
34
34
|
"scrum"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@inquirer/checkbox": "
|
|
38
|
-
"@inquirer/select": "5.2.
|
|
37
|
+
"@inquirer/checkbox": "5.2.5",
|
|
38
|
+
"@inquirer/select": "5.2.5",
|
|
39
39
|
"@total-typescript/ts-reset": "0.6.1",
|
|
40
40
|
"chalk": "6.0.0",
|
|
41
41
|
"commander": "15.0.0",
|
|
42
42
|
"dotenv": "17.4.2",
|
|
43
43
|
"jira.js": "5.4.0",
|
|
44
|
-
"kerberos": "
|
|
45
|
-
"product-pages": "
|
|
46
|
-
"zod": "4.
|
|
44
|
+
"kerberos": "7.0.0",
|
|
45
|
+
"product-pages": "1.0.1",
|
|
46
|
+
"zod": "4.6.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@types/node": "
|
|
50
|
-
"@vitest/coverage-v8": "
|
|
49
|
+
"@types/node": "22.20.2",
|
|
50
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
51
51
|
"esbuild": "0.28.2",
|
|
52
52
|
"prettier": "3.9.6",
|
|
53
53
|
"ts-node": "10.9.2",
|
|
54
54
|
"typescript": "7.0.2",
|
|
55
|
-
"
|
|
55
|
+
"vite": "8.2.2",
|
|
56
|
+
"vitest": "5.0.0"
|
|
56
57
|
}
|
|
57
58
|
}
|
package/src/auto.ts
CHANGED
|
@@ -85,7 +85,10 @@ export async function runAuto(options: OptionValues): Promise<void> {
|
|
|
85
85
|
)
|
|
86
86
|
);
|
|
87
87
|
|
|
88
|
-
const activeSprint = await jira.getActiveSprint(
|
|
88
|
+
const activeSprint = await jira.getActiveSprint(
|
|
89
|
+
+options.board,
|
|
90
|
+
options.prefix
|
|
91
|
+
);
|
|
89
92
|
let tasksWithoutSprint: { key?: string; fields?: Record<string, any> }[] = [];
|
|
90
93
|
|
|
91
94
|
if (!activeSprint) {
|
package/src/cli.ts
CHANGED
|
@@ -34,22 +34,27 @@ export function cli(): Command {
|
|
|
34
34
|
.description(
|
|
35
35
|
'Automatically manages split tasks (Preliminary Testing and QE) based on ticket state and status'
|
|
36
36
|
)
|
|
37
|
-
.option('-b, --board
|
|
37
|
+
.option('-b, --board <board>', 'Jira Board ID', getDefaultValue('BOARD'))
|
|
38
38
|
.requiredOption(
|
|
39
|
-
'-t, --team
|
|
39
|
+
'-t, --team <assigned team>',
|
|
40
40
|
'Jira Assigned Team',
|
|
41
41
|
getDefaultValue('TEAM')
|
|
42
42
|
)
|
|
43
43
|
.option(
|
|
44
|
-
'-c, --components
|
|
44
|
+
'-c, --components <components>',
|
|
45
45
|
'Jira Components',
|
|
46
46
|
getDefaultValue('COMPONENTS')
|
|
47
47
|
)
|
|
48
48
|
.option(
|
|
49
|
-
'--deadlines-file
|
|
49
|
+
'--deadlines-file <path>',
|
|
50
50
|
'Path to deadlines JSON file',
|
|
51
51
|
getDefaultValue('DEADLINES_FILE')
|
|
52
52
|
)
|
|
53
|
+
.option(
|
|
54
|
+
'-p, --prefix <prefix>',
|
|
55
|
+
'Sprint name prefix for matching the active sprint',
|
|
56
|
+
getDefaultValue('SPRINT_PREFIX')
|
|
57
|
+
)
|
|
53
58
|
.action(async (_opts, command) => {
|
|
54
59
|
await runAuto(command.optsWithGlobals());
|
|
55
60
|
})
|
package/src/jira.ts
CHANGED
|
@@ -111,9 +111,18 @@ export class Jira {
|
|
|
111
111
|
return response.values;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
async getActiveSprint(
|
|
114
|
+
async getActiveSprint(
|
|
115
|
+
boardId: number,
|
|
116
|
+
prefix?: string
|
|
117
|
+
): Promise<Sprint | undefined> {
|
|
115
118
|
const sprints = await this.getSprints(boardId);
|
|
116
|
-
|
|
119
|
+
const activeSprints = sprints.filter(s => s.state === 'active');
|
|
120
|
+
|
|
121
|
+
if (prefix) {
|
|
122
|
+
return activeSprints.find(s => s.name?.startsWith(prefix));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return activeSprints[0];
|
|
117
126
|
}
|
|
118
127
|
|
|
119
128
|
async getIssuesInSprint(
|
|
@@ -286,6 +295,13 @@ export class Jira {
|
|
|
286
295
|
? { [this.fields.sprint]: values.sprint }
|
|
287
296
|
: {};
|
|
288
297
|
|
|
298
|
+
if (this.dry) {
|
|
299
|
+
this.logger.log(
|
|
300
|
+
` ${chalk.dim(`Setting values ${JSON.stringify(values)} on ${issue} (dry-run)`)}`
|
|
301
|
+
);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
289
305
|
await this.api.issues.editIssue({
|
|
290
306
|
issueIdOrKey: issue,
|
|
291
307
|
fields: { ...assigneeValue, ...storyPointsValue, ...sprintValue },
|
package/src/pp-sync.ts
CHANGED
|
@@ -53,6 +53,10 @@ export async function runPpSync(
|
|
|
53
53
|
? chalk.bold(deadlines.itm_26)
|
|
54
54
|
: chalk.dim('not found');
|
|
55
55
|
logger.log(` ITM 26: ${itm26}`);
|
|
56
|
+
const allBuiltRelPrep = deadlines.all_built_rel_prep
|
|
57
|
+
? chalk.bold(deadlines.all_built_rel_prep)
|
|
58
|
+
: chalk.dim('not found');
|
|
59
|
+
logger.log(` All built REL_PREP: ${allBuiltRelPrep}`);
|
|
56
60
|
} catch (error) {
|
|
57
61
|
logger.log(chalk.yellow(` Failed to sync ${release}: ${error}`));
|
|
58
62
|
}
|
package/src/schema/deadlines.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type RelPrepEntry = z.infer<typeof relPrepEntrySchema>;
|
|
|
13
13
|
const releaseDeadlinesSchema = z.object({
|
|
14
14
|
rel_prep: z.array(relPrepEntrySchema),
|
|
15
15
|
itm_26: z.string().nullable(),
|
|
16
|
+
all_built_rel_prep: z.string().nullable().default(null),
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
const deadlinesFileSchema = z.object({
|
|
@@ -30,8 +31,16 @@ export const DEFAULT_DEADLINES_PATH = path.resolve(
|
|
|
30
31
|
'deadlines.json'
|
|
31
32
|
);
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
const TASK_NAME_REL_PREP = 'Package Advisory REL_PREP Deadline';
|
|
35
|
+
const TASK_NAME_ITM_26 = 'ITM 26 DevTestDoc';
|
|
36
|
+
const TASK_NAME_ALL_BUILT_REL_PREP =
|
|
37
|
+
'All packages built & All Errata in REL_PREP (non-container)';
|
|
38
|
+
|
|
39
|
+
export function escapeRegex(str: string): string {
|
|
40
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const SCHEDULE_TASK_REGEX = `.*(${escapeRegex(TASK_NAME_REL_PREP)}|${escapeRegex(TASK_NAME_ITM_26)}|${escapeRegex(TASK_NAME_ALL_BUILT_REL_PREP)}).*`;
|
|
35
44
|
|
|
36
45
|
export function readDeadlines(
|
|
37
46
|
filePath: string = DEFAULT_DEADLINES_PATH
|
|
@@ -54,14 +63,20 @@ function formatDate(date: Date): string {
|
|
|
54
63
|
export function classifyScheduleTasks(
|
|
55
64
|
tasks: { name: string; date_finish: string }[]
|
|
56
65
|
): ReleaseDeadlines {
|
|
57
|
-
const deadlines: ReleaseDeadlines = {
|
|
66
|
+
const deadlines: ReleaseDeadlines = {
|
|
67
|
+
rel_prep: [],
|
|
68
|
+
itm_26: null,
|
|
69
|
+
all_built_rel_prep: null,
|
|
70
|
+
};
|
|
58
71
|
for (const task of tasks) {
|
|
59
|
-
if (task.name.includes(
|
|
72
|
+
if (task.name.includes(TASK_NAME_ALL_BUILT_REL_PREP)) {
|
|
73
|
+
deadlines.all_built_rel_prep = task.date_finish;
|
|
74
|
+
} else if (task.name.includes(TASK_NAME_REL_PREP)) {
|
|
60
75
|
deadlines.rel_prep.push({
|
|
61
76
|
name: task.name,
|
|
62
77
|
date_finish: task.date_finish,
|
|
63
78
|
});
|
|
64
|
-
} else if (task.name.includes(
|
|
79
|
+
} else if (task.name.includes(TASK_NAME_ITM_26)) {
|
|
65
80
|
deadlines.itm_26 = task.date_finish;
|
|
66
81
|
}
|
|
67
82
|
}
|
|
@@ -95,14 +110,30 @@ export function computePreliminaryTestingDueDate(
|
|
|
95
110
|
return closest < twoWeeksStr ? closest : twoWeeksStr;
|
|
96
111
|
}
|
|
97
112
|
|
|
113
|
+
const todayStr = formatDate(today);
|
|
114
|
+
|
|
98
115
|
if (deadlines.itm_26) {
|
|
99
|
-
const todayStr = formatDate(today);
|
|
100
116
|
if (deadlines.itm_26 >= todayStr && deadlines.itm_26 < twoWeeksStr) {
|
|
101
117
|
return deadlines.itm_26;
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
120
|
|
|
105
|
-
|
|
121
|
+
const pastItm26 = !deadlines.itm_26 || deadlines.itm_26 < todayStr;
|
|
122
|
+
const oneWeek = new Date(today);
|
|
123
|
+
oneWeek.setDate(oneWeek.getDate() + 7);
|
|
124
|
+
const oneWeekStr = formatDate(oneWeek);
|
|
125
|
+
const maxCap = pastItm26 ? oneWeekStr : twoWeeksStr;
|
|
126
|
+
|
|
127
|
+
if (deadlines.all_built_rel_prep) {
|
|
128
|
+
if (
|
|
129
|
+
deadlines.all_built_rel_prep >= todayStr &&
|
|
130
|
+
deadlines.all_built_rel_prep < maxCap
|
|
131
|
+
) {
|
|
132
|
+
return deadlines.all_built_rel_prep;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return maxCap;
|
|
106
137
|
}
|
|
107
138
|
|
|
108
139
|
export function computeQeTaskDueDate(
|
|
@@ -117,10 +148,19 @@ export function computeQeTaskDueDate(
|
|
|
117
148
|
);
|
|
118
149
|
}
|
|
119
150
|
|
|
120
|
-
|
|
151
|
+
const todayStr = formatDate(today);
|
|
152
|
+
|
|
153
|
+
if (deadlines.itm_26 && deadlines.itm_26 >= todayStr) {
|
|
121
154
|
return deadlines.itm_26;
|
|
122
155
|
}
|
|
123
156
|
|
|
157
|
+
if (
|
|
158
|
+
deadlines.all_built_rel_prep &&
|
|
159
|
+
deadlines.all_built_rel_prep >= todayStr
|
|
160
|
+
) {
|
|
161
|
+
return deadlines.all_built_rel_prep;
|
|
162
|
+
}
|
|
163
|
+
|
|
124
164
|
return null;
|
|
125
165
|
}
|
|
126
166
|
|
package/src/util.ts
CHANGED
|
File without changes
|