@cmflow/cli 2.2.5 → 2.3.1
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/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/README.md
CHANGED
|
@@ -191,7 +191,7 @@ Notes:
|
|
|
191
191
|
|
|
192
192
|
### Build and E2E Test
|
|
193
193
|
|
|
194
|
-
CmFlow release
|
|
194
|
+
CmFlow release is able to run `build` and `test_e2e` task during the `prepare` step. It's useful when you want to
|
|
195
195
|
deploy a docker image on docker hub with the right revision number in the `package.json`. The only requirement, is to
|
|
196
196
|
have a `build` and `test_e2e` tasks in you npm scripts.
|
|
197
197
|
|
|
@@ -287,3 +287,103 @@ allow git release note deployment, commit push and docker image deployment.
|
|
|
287
287
|
| DOCKER_HUB_ID | The docker hub id |
|
|
288
288
|
| DOCKER_HUB_PWD | The docker password account |
|
|
289
289
|
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
## Jira Releases Command
|
|
293
|
+
|
|
294
|
+
The `jira-releases` command lists and (optionally) updates Jira versions related to an application version, then generates a share-ready delivery note.
|
|
295
|
+
|
|
296
|
+
### Goal
|
|
297
|
+
- For a given Jira project and component, retrieve all versions whose number is less than or equal to a target tag/version.
|
|
298
|
+
- Display the issues associated with those versions and produce a delivery note.
|
|
299
|
+
- Optionally mark the versions as "released" (with today's date) and move the corresponding issues to the "Livré en production" state.
|
|
300
|
+
- Optionally trigger a CMS (Directus) workflow if configuration is provided.
|
|
301
|
+
|
|
302
|
+
### Prerequisites
|
|
303
|
+
- Access to the Jira API via environment variables:
|
|
304
|
+
- `JIRA_API` (e.g., https://your-instance.atlassian.net)
|
|
305
|
+
- `JIRA_EMAIL`
|
|
306
|
+
- `JIRA_TOKEN` (API token)
|
|
307
|
+
- Optional: `HTTPS_PROXY` if you need a proxy
|
|
308
|
+
- Optional (to trigger a Directus workflow):
|
|
309
|
+
- `DIRECTUS_URL`
|
|
310
|
+
- `DIRECTUS_FLOW_ID`
|
|
311
|
+
- `DIRECTUS_ACCESS_TOKEN`
|
|
312
|
+
|
|
313
|
+
If all 3 Directus variables are present, `CmFlow` considers `CAN_TRIGGER_WORKFLOW=true` and will attempt to run the workflow after updating releases.
|
|
314
|
+
|
|
315
|
+
### Usage
|
|
316
|
+
|
|
317
|
+
```sh
|
|
318
|
+
cm jira-releases --project-id <PROJECT_KEY> --component <COMPONENT> --tag <VERSION> [--update] [--verbose]
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Aliases and options (from `bin/cm-jira-releases.js`):
|
|
322
|
+
|
|
323
|
+
- `-c, --component <component>`: Jira component filter (e.g., `API`).
|
|
324
|
+
- `-c, --project-id <projectId>`: Jira project key (e.g., `CMAB`).
|
|
325
|
+
- `-t, --tag <tag>`: target version to analyze (e.g., `0.3241.3`, `v0.3241.3`, or `version-0.3241.3`). `v` and `version-` prefixes are automatically ignored.
|
|
326
|
+
- `-u, --update`: updates Jira (marks the version(s) as released with today's date and moves the issues).
|
|
327
|
+
- `-v, --verbose`: verbose logs.
|
|
328
|
+
|
|
329
|
+
Note: Jira versions are expected to follow `<COMPONENT>-<version>` or similar. For example, if `--component API` and a Jira version is named `API-0.3241.3`, the `0.3241.3` part will be compared to `--tag` using semver.
|
|
330
|
+
|
|
331
|
+
### Detailed behavior
|
|
332
|
+
1. Fetch all Jira versions for the project (`projectId`).
|
|
333
|
+
2. Keep those whose version (after stripping `component-`, `version-`, and `v` prefixes) is semver <= the provided `--tag`.
|
|
334
|
+
3. For each selected version, fetch related issues and prepare a delivery note by type:
|
|
335
|
+
- `Story` → `feat`
|
|
336
|
+
- `Sous-tâche` → `tech`
|
|
337
|
+
- `Tâche` → `chore`
|
|
338
|
+
- `Bug` → `fix`
|
|
339
|
+
4. Print the delivery note to stdout (example below).
|
|
340
|
+
5. If `--update` is provided:
|
|
341
|
+
- Mark the relevant version(s) as `released=true` with `releaseDate=YYYY-MM-DD` (today's date).
|
|
342
|
+
- Move each related issue to the Jira state `id=18`, `name="Livré en production"` (transition errors are logged but won't stop the whole process).
|
|
343
|
+
6. If the Directus config is complete, trigger the workflow by passing the list of `version` values for the processed releases.
|
|
344
|
+
|
|
345
|
+
### Example
|
|
346
|
+
|
|
347
|
+
From this repo, a sample script is provided in `package.json`:
|
|
348
|
+
|
|
349
|
+
```sh
|
|
350
|
+
yarn jira_releases
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
which runs, with `.env` loaded:
|
|
354
|
+
|
|
355
|
+
```sh
|
|
356
|
+
node --env-file=.env bin/cm.js jira-releases \
|
|
357
|
+
--project-id CMAB \
|
|
358
|
+
--component API \
|
|
359
|
+
--tag 0.3241.3 \
|
|
360
|
+
--verbose
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Raw CLI example:
|
|
364
|
+
```
|
|
365
|
+
cm jira-releases --project-id CMAB --component API --tag v0.3241.3 --update
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Example of generated note
|
|
369
|
+
The console output includes a share-ready message (note: the tool prints the message in French by default):
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
Hello,
|
|
373
|
+
We will deploy to production version <TAG> of the <COMPONENT>
|
|
374
|
+
Here is what this delivery includes:
|
|
375
|
+
|
|
376
|
+
- v<version>:
|
|
377
|
+
- feat(CMAB-123): Story title
|
|
378
|
+
- fix(CMAB-456): XYZ fix
|
|
379
|
+
|
|
380
|
+
The API team
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The exact structure may vary depending on how many versions are included and which issues are associated.
|
|
384
|
+
|
|
385
|
+
### Best practices
|
|
386
|
+
- Use a valid semver `--tag` (the `v`/`version-` prefixes are accepted).
|
|
387
|
+
- Ensure your Jira versions follow a consistent schema that includes the component (`<COMPONENT>-<version>`) to allow matching.
|
|
388
|
+
- First run without `--update` to preview the note and verify selected versions and issues.
|
|
389
|
+
- Provide `HTTPS_PROXY` if your environment requires it to reach Jira/Directus.
|
package/package.json
CHANGED
|
@@ -2,9 +2,11 @@ import chalk from "chalk";
|
|
|
2
2
|
import figures from "figures";
|
|
3
3
|
import moment from "moment";
|
|
4
4
|
|
|
5
|
+
import { triggerDirectusFlow } from "../common/back/directus/directus-flow.js";
|
|
5
6
|
import { jiraGetReleasesAndIssues } from "../common/back/jira/jira-get-releases-and-issues.js";
|
|
6
7
|
import { jiraIssueMoveTo } from "../common/back/jira/jira-issue-move-to.js";
|
|
7
8
|
import { updateJiraRelease } from "../common/back/jira/jira-releases.js";
|
|
9
|
+
import { getConfig } from "../common/index.js";
|
|
8
10
|
|
|
9
11
|
const TYPES = {
|
|
10
12
|
Story: "feat",
|
|
@@ -69,6 +71,18 @@ export class JiraReleases {
|
|
|
69
71
|
);
|
|
70
72
|
}
|
|
71
73
|
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
title: `Trigger CMS API Workflow`,
|
|
77
|
+
skip: () => !getConfig().CAN_TRIGGER_WORKFLOW,
|
|
78
|
+
task: () => {
|
|
79
|
+
return triggerDirectusFlow(
|
|
80
|
+
context.projectId,
|
|
81
|
+
context.releases.map(({ version }) => {
|
|
82
|
+
return version;
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
}
|
|
72
86
|
}
|
|
73
87
|
];
|
|
74
88
|
}
|
|
@@ -95,7 +109,7 @@ ${context.releases
|
|
|
95
109
|
.filter((line) => line !== false)
|
|
96
110
|
.join("\n\n")}
|
|
97
111
|
|
|
98
|
-
L'
|
|
112
|
+
L'Equipe API
|
|
99
113
|
`;
|
|
100
114
|
|
|
101
115
|
console.log(note);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getConfig } from "../../get-config.js";
|
|
2
|
+
import { httpRequest } from "../http/http-request.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Trigger a Directus Flow
|
|
6
|
+
*
|
|
7
|
+
* @param {string} projectId - The project identifier to send to the flow
|
|
8
|
+
* @param {string[]} versions - The versions list to send to the flow
|
|
9
|
+
* @param {object} [opts] - Additional request options (e.g., rejectUnauthorized)
|
|
10
|
+
* @returns {Promise<unknown>} The parsed JSON response
|
|
11
|
+
*/
|
|
12
|
+
export async function triggerDirectusFlow(projectId, versions, opts = {}) {
|
|
13
|
+
const { DIRECTUS_URL, DIRECTUS_FLOW_ID, DIRECTUS_ACCESS_TOKEN } = getConfig();
|
|
14
|
+
|
|
15
|
+
const endpoint = `${DIRECTUS_URL}/flows/trigger/${DIRECTUS_FLOW_ID}`;
|
|
16
|
+
|
|
17
|
+
const options = {
|
|
18
|
+
method: "post",
|
|
19
|
+
callee: "DIRECTUS",
|
|
20
|
+
headers: {
|
|
21
|
+
Authorization: `Bearer ${DIRECTUS_ACCESS_TOKEN}`,
|
|
22
|
+
"Content-Type": "application/json"
|
|
23
|
+
},
|
|
24
|
+
body: {
|
|
25
|
+
projectId,
|
|
26
|
+
versions
|
|
27
|
+
},
|
|
28
|
+
...opts
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (options.body && options.headers?.["Content-Type"] === "application/json") {
|
|
32
|
+
options.body = JSON.stringify(options.body);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return httpRequest(endpoint, options);
|
|
36
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Use vi.hoisted to create mock references that are available to mock factories
|
|
2
|
+
const httpRequestMock = vi.hoisted(() => vi.fn());
|
|
3
|
+
|
|
4
|
+
// Mock get-config to provide DIRECTUS_* env values
|
|
5
|
+
vi.mock("../../get-config.js", () => ({
|
|
6
|
+
getConfig: () => ({
|
|
7
|
+
DIRECTUS_URL: "https://directus.example.com",
|
|
8
|
+
DIRECTUS_FLOW_ID: "flow-123",
|
|
9
|
+
DIRECTUS_ACCESS_TOKEN: "token-abc"
|
|
10
|
+
})
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
// Spy/mock for httpRequest
|
|
14
|
+
vi.mock("../http/http-request.js", () => ({
|
|
15
|
+
httpRequest: httpRequestMock
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
// Now import the SUT after mocks are in place
|
|
19
|
+
import { triggerDirectusFlow } from "./directus-flow.js";
|
|
20
|
+
|
|
21
|
+
describe("triggerDirectusFlow", () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
httpRequestMock.mockReset();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should call httpRequest with the expected endpoint, headers and body", async () => {
|
|
27
|
+
httpRequestMock.mockResolvedValue({ ok: true, data: { status: "queued" } });
|
|
28
|
+
|
|
29
|
+
const projectId = "CMAB";
|
|
30
|
+
const versions = ["2.3.0", "2.2.0"];
|
|
31
|
+
const opts = { rejectUnauthorized: true };
|
|
32
|
+
|
|
33
|
+
const result = await triggerDirectusFlow(projectId, versions, opts);
|
|
34
|
+
|
|
35
|
+
expect(result).toEqual({ ok: true, data: { status: "queued" } });
|
|
36
|
+
|
|
37
|
+
expect(httpRequestMock).toHaveBeenCalledTimes(1);
|
|
38
|
+
|
|
39
|
+
const expectedEndpoint = "https://directus.example.com/flows/trigger/flow-123";
|
|
40
|
+
const expectedBody = JSON.stringify({ projectId, versions });
|
|
41
|
+
|
|
42
|
+
expect(httpRequestMock).toHaveBeenCalledWith(
|
|
43
|
+
expectedEndpoint,
|
|
44
|
+
expect.objectContaining({
|
|
45
|
+
method: "post",
|
|
46
|
+
callee: "DIRECTUS",
|
|
47
|
+
rejectUnauthorized: true,
|
|
48
|
+
headers: expect.objectContaining({
|
|
49
|
+
Authorization: "Bearer token-abc",
|
|
50
|
+
"Content-Type": "application/json"
|
|
51
|
+
}),
|
|
52
|
+
body: expectedBody
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should propagate errors from httpRequest", async () => {
|
|
58
|
+
httpRequestMock.mockRejectedValue(new Error("network error"));
|
|
59
|
+
|
|
60
|
+
await expect(triggerDirectusFlow("CMAB", ["1.0.0"])).rejects.toThrow("network error");
|
|
61
|
+
});
|
|
62
|
+
});
|
package/src/common/get-config.js
CHANGED
|
@@ -90,6 +90,9 @@ export function getConfig() {
|
|
|
90
90
|
JIRA_API,
|
|
91
91
|
JIRA_EMAIL,
|
|
92
92
|
JIRA_TOKEN,
|
|
93
|
+
DIRECTUS_URL,
|
|
94
|
+
DIRECTUS_FLOW_ID,
|
|
95
|
+
DIRECTUS_ACCESS_TOKEN,
|
|
93
96
|
HTTPS_PROXY
|
|
94
97
|
} = process.env;
|
|
95
98
|
|
|
@@ -140,6 +143,10 @@ export function getConfig() {
|
|
|
140
143
|
JIRA_EMAIL,
|
|
141
144
|
JIRA_TOKEN,
|
|
142
145
|
HTTPS_PROXY,
|
|
146
|
+
DIRECTUS_URL,
|
|
147
|
+
DIRECTUS_FLOW_ID,
|
|
148
|
+
DIRECTUS_ACCESS_TOKEN,
|
|
149
|
+
CAN_TRIGGER_WORKFLOW: DIRECTUS_ACCESS_TOKEN && DIRECTUS_FLOW_ID && DIRECTUS_URL,
|
|
143
150
|
WORKSPACES: pkg.workspaces || [],
|
|
144
151
|
...getCI()
|
|
145
152
|
};
|