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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jira-sprinter",
3
- "version": "2.5.1",
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": "^5.2.1",
38
- "@inquirer/select": "5.2.1",
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": "^7.0.0",
45
- "product-pages": "^1.0.1",
46
- "zod": "4.4.3"
44
+ "kerberos": "7.0.0",
45
+ "product-pages": "1.0.1",
46
+ "zod": "4.6.1"
47
47
  },
48
48
  "devDependencies": {
49
- "@types/node": "26.2.0",
50
- "@vitest/coverage-v8": "4.1.10",
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
- "vitest": "4.1.10"
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(+options.board);
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 [board]', 'Jira Board ID', getDefaultValue('BOARD'))
37
+ .option('-b, --board <board>', 'Jira Board ID', getDefaultValue('BOARD'))
38
38
  .requiredOption(
39
- '-t, --team [assigned team]',
39
+ '-t, --team <assigned team>',
40
40
  'Jira Assigned Team',
41
41
  getDefaultValue('TEAM')
42
42
  )
43
43
  .option(
44
- '-c, --components [components]',
44
+ '-c, --components <components>',
45
45
  'Jira Components',
46
46
  getDefaultValue('COMPONENTS')
47
47
  )
48
48
  .option(
49
- '--deadlines-file [path]',
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(boardId: number): Promise<Sprint | undefined> {
114
+ async getActiveSprint(
115
+ boardId: number,
116
+ prefix?: string
117
+ ): Promise<Sprint | undefined> {
115
118
  const sprints = await this.getSprints(boardId);
116
- return sprints.find(s => s.state === 'active');
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
  }
@@ -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
- export const SCHEDULE_TASK_REGEX =
34
- '.*(Package Advisory REL_PREP Deadline|ITM 26 DevTestDoc).*';
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 = { rel_prep: [], itm_26: null };
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('REL_PREP')) {
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('ITM 26')) {
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
- return twoWeeksStr;
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
- if (deadlines.itm_26 && deadlines.itm_26 >= formatDate(today)) {
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
@@ -30,6 +30,7 @@ export function getDefaultValue(
30
30
  | 'DRY'
31
31
  | 'YOLO'
32
32
  | 'DEADLINES_FILE'
33
+ | 'SPRINT_PREFIX'
33
34
  ) {
34
35
  if (isDefaultValuesDisabled()) {
35
36
  return undefined;
File without changes