Skip to content

Commit 032c99f

Browse files
authored
Align issue ID matching with Linear matching logic (#11)
* Align issue ID matching with Linear matching logic * Bump package.json to 0.4.0
1 parent 0e13a83 commit 032c99f

3 files changed

Lines changed: 331 additions & 14 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "linear-release",
3-
"version": "0.3.0",
3+
"version": "0.4.0",
44
"private": true,
55
"description": "CLI tool to integrate CI/CD pipelines with Linear releases",
66
"homepage": "https://github.com/linear/linear-release",

src/extractors.test.ts

Lines changed: 237 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ describe("extractLinearIssueIdentifiersForCommit", () => {
1515
expect(result).toEqual(["ENG-123"]);
1616
});
1717

18-
it("extracts identifiers from commit message", () => {
18+
it("extracts identifiers from commit message with magic words", () => {
1919
const commit: CommitContext = {
2020
sha: "abc123",
2121
branchName: "feature/no-key-here",
22-
message: "Implements PLAT-42 and ENG-7 in one go",
22+
message: "Fixes PLAT-42 and ENG-7 in one go",
2323
};
2424

2525
const result = extractLinearIssueIdentifiersForCommit(commit);
@@ -31,7 +31,7 @@ describe("extractLinearIssueIdentifiersForCommit", () => {
3131
const commit: CommitContext = {
3232
sha: "abc123",
3333
branchName: "feature/eng-123-awesome-change",
34-
message: "ENG-123 fixed, see ENG-123",
34+
message: "Fixed ENG-123, see ENG-123",
3535
};
3636

3737
const result = extractLinearIssueIdentifiersForCommit(commit);
@@ -63,6 +63,18 @@ describe("extractLinearIssueIdentifiersForCommit", () => {
6363
expect(result.sort()).toEqual(["A-1", "ABCDEFG-999", "X1Y2Z3A-100"].sort());
6464
});
6565

66+
it("does not extract identifiers from commit message without magic words", () => {
67+
const commit: CommitContext = {
68+
sha: "abc123",
69+
branchName: "feature/no-key-here",
70+
message: "See LIN-123 for details",
71+
};
72+
73+
const result = extractLinearIssueIdentifiersForCommit(commit);
74+
75+
expect(result).toEqual([]);
76+
});
77+
6678
it("does not match team keys longer than 7 characters", () => {
6779
const commit: CommitContext = {
6880
sha: "abc123",
@@ -140,8 +152,8 @@ describe("underscore handling ", () => {
140152

141153
describe("multiple identifiers ", () => {
142154
it.each([
143-
["LIN-123 LIN-321", ["LIN-123", "LIN-321"]],
144-
["Closes issues LIN-123 and LIN-321", ["LIN-123", "LIN-321"]],
155+
["Fixes LIN-123 and LIN-321", ["LIN-123", "LIN-321"]],
156+
["Closes LIN-123, LIN-321", ["LIN-123", "LIN-321"]],
145157
])("message %s should yield %j", (message, expected) => {
146158
const result = extractLinearIssueIdentifiersForCommit({
147159
sha: "abc",
@@ -152,6 +164,226 @@ describe("multiple identifiers ", () => {
152164
});
153165
});
154166

167+
describe("commit message magic word behavior", () => {
168+
it("extracts with closing keyword", () => {
169+
const result = extractLinearIssueIdentifiersForCommit({
170+
sha: "abc",
171+
branchName: null,
172+
message: "Fixes LIN-123",
173+
});
174+
expect(result).toEqual(["LIN-123"]);
175+
});
176+
177+
it("extracts with contributing phrase", () => {
178+
const result = extractLinearIssueIdentifiersForCommit({
179+
sha: "abc",
180+
branchName: null,
181+
message: "Related to LIN-123",
182+
});
183+
expect(result).toEqual(["LIN-123"]);
184+
});
185+
186+
it("does not extract without magic words", () => {
187+
const result = extractLinearIssueIdentifiersForCommit({
188+
sha: "abc",
189+
branchName: null,
190+
message: "See LIN-123 for details",
191+
});
192+
expect(result).toEqual([]);
193+
});
194+
195+
it("extracts multiple keys after keyword", () => {
196+
const result = extractLinearIssueIdentifiersForCommit({
197+
sha: "abc",
198+
branchName: null,
199+
message: "Fixes LIN-123, LIN-456 and ENG-789",
200+
});
201+
expect(result.sort()).toEqual(["ENG-789", "LIN-123", "LIN-456"]);
202+
});
203+
204+
it("extracts magic word in title line", () => {
205+
const result = extractLinearIssueIdentifiersForCommit({
206+
sha: "abc",
207+
branchName: null,
208+
message: "Fix LIN-123: something",
209+
});
210+
expect(result).toEqual(["LIN-123"]);
211+
});
212+
213+
it("does not extract key in title without keyword", () => {
214+
const result = extractLinearIssueIdentifiersForCommit({
215+
sha: "abc",
216+
branchName: null,
217+
message: "LIN-123: Fix something",
218+
});
219+
expect(result).toEqual([]);
220+
});
221+
222+
it.each([
223+
"close",
224+
"closes",
225+
"closed",
226+
"closing",
227+
"fix",
228+
"fixes",
229+
"fixed",
230+
"fixing",
231+
"resolve",
232+
"resolves",
233+
"resolved",
234+
"resolving",
235+
"complete",
236+
"completes",
237+
"completed",
238+
"completing",
239+
])("closing keyword '%s' extracts issue", (keyword) => {
240+
const result = extractLinearIssueIdentifiersForCommit({
241+
sha: "abc",
242+
branchName: null,
243+
message: `${keyword} LIN-100`,
244+
});
245+
expect(result).toEqual(["LIN-100"]);
246+
});
247+
248+
it.each(["ref", "refs", "references", "part of", "related to", "relates to", "contributes to", "towards", "toward"])(
249+
"contributing phrase '%s' extracts issue",
250+
(phrase) => {
251+
const result = extractLinearIssueIdentifiersForCommit({
252+
sha: "abc",
253+
branchName: null,
254+
message: `${phrase} LIN-200`,
255+
});
256+
expect(result).toEqual(["LIN-200"]);
257+
},
258+
);
259+
260+
it("supports keyword with colon separator", () => {
261+
const result = extractLinearIssueIdentifiersForCommit({
262+
sha: "abc",
263+
branchName: null,
264+
message: "Closes: LIN-123",
265+
});
266+
expect(result).toEqual(["LIN-123"]);
267+
});
268+
269+
it("only extracts keys preceded by magic word on same line", () => {
270+
const result = extractLinearIssueIdentifiersForCommit({
271+
sha: "abc",
272+
branchName: null,
273+
message: "See LIN-111, fixes LIN-222",
274+
});
275+
expect(result).toEqual(["LIN-222"]);
276+
});
277+
278+
it("does not extract from the original bug scenario", () => {
279+
const result = extractLinearIssueIdentifiersForCommit({
280+
sha: "abc",
281+
branchName: null,
282+
message: "Title\nSeparate issue to follow up on that here LIN-60064",
283+
});
284+
expect(result).toEqual([]);
285+
});
286+
287+
it("branch provides keys independently of message magic words", () => {
288+
const result = extractLinearIssueIdentifiersForCommit({
289+
sha: "abc",
290+
branchName: "feature/LIN-100-something",
291+
message: "See LIN-200 for details",
292+
});
293+
expect(result).toEqual(["LIN-100"]);
294+
});
295+
296+
it("is case insensitive for magic words", () => {
297+
const result = extractLinearIssueIdentifiersForCommit({
298+
sha: "abc",
299+
branchName: null,
300+
message: "fIXES LIN-123",
301+
});
302+
expect(result).toEqual(["LIN-123"]);
303+
});
304+
305+
it("extracts with multi-word phrase 'Part of'", () => {
306+
const result = extractLinearIssueIdentifiersForCommit({
307+
sha: "abc",
308+
branchName: null,
309+
message: "Part of LIN-123",
310+
});
311+
expect(result).toEqual(["LIN-123"]);
312+
});
313+
314+
it("extracts with multi-word phrase 'Related to'", () => {
315+
const result = extractLinearIssueIdentifiersForCommit({
316+
sha: "abc",
317+
branchName: null,
318+
message: "Related to LIN-456",
319+
});
320+
expect(result).toEqual(["LIN-456"]);
321+
});
322+
323+
it("extracts issue from Linear URL with slug after magic word", () => {
324+
const result = extractLinearIssueIdentifiersForCommit({
325+
sha: "abc",
326+
branchName: null,
327+
message: "Fixes https://linear.app/myorg/issue/LIN-123/fix-auth",
328+
});
329+
expect(result).toEqual(["LIN-123"]);
330+
});
331+
332+
it("extracts issue from Linear URL without slug after magic word", () => {
333+
const result = extractLinearIssueIdentifiersForCommit({
334+
sha: "abc",
335+
branchName: null,
336+
message: "Fixes https://linear.app/myorg/issue/LIN-123",
337+
});
338+
expect(result).toEqual(["LIN-123"]);
339+
});
340+
341+
it("does not extract Linear URL without magic word", () => {
342+
const result = extractLinearIssueIdentifiersForCommit({
343+
sha: "abc",
344+
branchName: null,
345+
message: "See https://linear.app/myorg/issue/LIN-123/fix",
346+
});
347+
expect(result).toEqual([]);
348+
});
349+
350+
it("extracts mixed Linear URLs and raw IDs after magic word", () => {
351+
const result = extractLinearIssueIdentifiersForCommit({
352+
sha: "abc",
353+
branchName: null,
354+
message: "Fixes https://linear.app/myorg/issue/LIN-123/slug, ENG-456 and LIN-789",
355+
});
356+
expect(result.sort()).toEqual(["ENG-456", "LIN-123", "LIN-789"]);
357+
});
358+
359+
it("extracts issue from http Linear URL after magic word", () => {
360+
const result = extractLinearIssueIdentifiersForCommit({
361+
sha: "abc",
362+
branchName: null,
363+
message: "Fixes http://linear.app/myorg/issue/LIN-123",
364+
});
365+
expect(result).toEqual(["LIN-123"]);
366+
});
367+
368+
it("extracts issue from Linear URL with trailing slash", () => {
369+
const result = extractLinearIssueIdentifiersForCommit({
370+
sha: "abc",
371+
branchName: null,
372+
message: "Fixes https://linear.app/my-org/issue/LIN-213/",
373+
});
374+
expect(result).toEqual(["LIN-213"]);
375+
});
376+
377+
it("extracts issue from Linear URL with contributing phrase", () => {
378+
const result = extractLinearIssueIdentifiersForCommit({
379+
sha: "abc",
380+
branchName: null,
381+
message: "Part of https://linear.app/myorg/issue/LIN-213/some-slug",
382+
});
383+
expect(result).toEqual(["LIN-213"]);
384+
});
385+
});
386+
155387
describe("extractPullRequestNumbersForCommit", () => {
156388
// Messages that should extract PR numbers
157389
it.each([

0 commit comments

Comments
 (0)