@uxf/scripts 10.0.0 → 11.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.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ require("../src/uxf-merge-requests-notifier/cli")()
3
+ .then((exitCode) => {
4
+ process.exitCode = exitCode;
5
+ })
6
+ .catch(() => {
7
+ process.exitCode = 1;
8
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxf/scripts",
3
- "version": "10.0.0",
3
+ "version": "11.7.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -13,7 +13,8 @@
13
13
  "scripts": {
14
14
  "build": "",
15
15
  "typecheck": "",
16
- "test": "./node_modules/.bin/eslint ./bin"
16
+ "test": "./node_modules/.bin/eslint ./bin",
17
+ "run:mr-notifier": "GOOGLE_WEBHOOK_URL=<<TODO>> GITLAB_TOKEN=<<TODO>> CI_SERVER_URL=https://gitlab.uxf.cz node ./bin/uxf-merge-requests-notifier.js"
17
18
  },
18
19
  "publishConfig": {
19
20
  "access": "public"
package/src/GitLab.js CHANGED
@@ -5,12 +5,35 @@ const parse = require("@commitlint/parse").default;
5
5
 
6
6
  const axios = create({
7
7
  baseURL: `${env.CI_SERVER_URL}/api/v4`,
8
-
9
8
  headers: {
10
9
  Authorization: `Bearer ${env.GITLAB_TOKEN}`,
11
10
  },
12
11
  });
13
12
 
13
+ /**
14
+ *
15
+ * @param {string} url
16
+ * @param {axios.AxiosRequestConfig<any>} config
17
+ */
18
+ async function getAll(url, config) {
19
+ let nextPage = "1";
20
+ const data = [];
21
+ do {
22
+ const response = await axios.get(url, {
23
+ ...config,
24
+ params: {
25
+ ...(config.params ?? {}),
26
+ per_page: "100",
27
+ page: nextPage,
28
+ },
29
+ });
30
+ nextPage = response.headers["x-next-page"];
31
+ data.push(...response.data);
32
+ } while (nextPage);
33
+
34
+ return data;
35
+ }
36
+
14
37
  async function loadCommits(from) {
15
38
  const commits = [];
16
39
  console.log(`- start date: ${from}`);
@@ -94,9 +117,33 @@ async function createRelease(description, dryRun = false) {
94
117
  console.log(`\n🎉🎉🎉 Release "${tag}" published\n\n${description}`);
95
118
  }
96
119
 
120
+ function getSingleMergeRequestChanges(projectId, mr_iid) {
121
+ return axios.get(`/projects/${projectId}/merge_requests/${mr_iid}/changes`).then((r) => r.data);
122
+ }
123
+
124
+ function getAllMergeRequests() {
125
+ return getAll("/merge_requests", {
126
+ params: {
127
+ approwed: "no",
128
+ wip: "no",
129
+ sort: "asc",
130
+ scope: "all",
131
+ state: "opened",
132
+ order_by: "updated_at",
133
+ },
134
+ });
135
+ }
136
+
137
+ function getAllProjects() {
138
+ return getAll("/projects", { params: { simple: false } });
139
+ }
140
+
97
141
  module.exports = {
98
142
  loadCommits,
99
143
  loadIssues,
100
144
  getLastTag,
101
145
  createRelease,
146
+ getAllMergeRequests,
147
+ getAllProjects,
148
+ getSingleMergeRequestChanges,
102
149
  };
package/src/GoogleChat.js CHANGED
@@ -15,6 +15,13 @@ async function chatPostMessage(data, dryRun) {
15
15
  }
16
16
  }
17
17
 
18
+ async function messageCardsV2(cardsV2) {
19
+ return axios
20
+ .post(env.GOOGLE_WEBHOOK_URL, { cardsV2 }, { headers: { "Content-Type": "application/json; charset=UTF-8" } })
21
+ .then((r) => r.data);
22
+ }
23
+
18
24
  module.exports = {
19
25
  chatPostMessage,
26
+ messageCardsV2,
20
27
  };
@@ -1,5 +1,5 @@
1
1
  function findTFunctionNamespaces(pageContent) {
2
- const regex = /\bt\("([a-zA-Z0-9\-]+):([a-zA-Z0-9\-\.]+)"/g;
2
+ const regex = /\bt\("([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_\.]+)"/g;
3
3
  const matches = pageContent.matchAll(regex);
4
4
  const namespaces = new Set();
5
5
 
@@ -6,6 +6,7 @@ const content = `
6
6
  {t("basic:title")}
7
7
  {t("basic-dash:title-dash")}
8
8
  {t("basicCamel:titleCamel")}
9
+ {t("basic_underscore:title_underscore")}
9
10
  </div>
10
11
  <div title={
11
12
  condition
@@ -24,6 +25,7 @@ describe("find namespaces in t functions", function () {
24
25
  "basic",
25
26
  "basic-dash",
26
27
  "basicCamel",
28
+ "basic_underscore",
27
29
  "multiline-with-parameters",
28
30
  "with-parameters",
29
31
  "in-translation-parameter",
@@ -1,5 +1,5 @@
1
1
  function findTransComponentNamespaces(pageContent) {
2
- const regex = /<Trans[^>]*?i18nKey="([a-zA-Z0-9\-]+):([a-zA-Z0-9\-\.]+)"/g;
2
+ const regex = /<Trans[^>]*?i18nKey="([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_\.]+)"/g;
3
3
  const matches = pageContent.matchAll(regex);
4
4
  const namespaces = new Set();
5
5
 
@@ -5,11 +5,17 @@ const content = `
5
5
  <Trans i18nKey="basic:title" />
6
6
  <Trans i18nKey="basic-dash:title-dash" />
7
7
  <Trans i18nKey="basicCamel:titleCamel" />
8
+ <Trans i18nKey="basic_underscore:title_underscore" />
8
9
  </div>
9
10
  `;
10
11
 
11
12
  describe("find namespaces in trans component", function () {
12
13
  it("should find namespaces", function () {
13
- expect(findTransComponentNamespaces(content)).toStrictEqual(["basic", "basic-dash", "basicCamel"]);
14
+ expect(findTransComponentNamespaces(content)).toStrictEqual([
15
+ "basic",
16
+ "basic-dash",
17
+ "basicCamel",
18
+ "basic_underscore",
19
+ ]);
14
20
  });
15
21
  });
@@ -0,0 +1,40 @@
1
+ const { argv, env } = require("process");
2
+
3
+ module.exports = async () => {
4
+ const cli = require("yargs")
5
+ .command("$0", "UXF merge requests notifier", (yargs) => {
6
+ yargs.demandCommand(0, 0).usage(`Usage:
7
+ uxf-merge-requests-notifier [options]
8
+
9
+ Environment variables:
10
+ GITLAB_TOKEN - required
11
+ GOOGLE_WEBHOOK_URL - required
12
+ CI_SERVER_URL - required - setting by GitLab CI`);
13
+ })
14
+ .option("h", { alias: "help", group: "Options" })
15
+ .strict(false)
16
+ .exitProcess(false);
17
+
18
+ try {
19
+ const { help, ...options } = cli.parse(argv.slice(2));
20
+
21
+ if (Boolean(help)) {
22
+ return 0;
23
+ }
24
+
25
+ if (!env.GITLAB_TOKEN) {
26
+ console.log("GitLab token must be set. Use environment variable GITLAB_TOKEN.");
27
+ return 1;
28
+ }
29
+
30
+ if (!env.GOOGLE_WEBHOOK_URL) {
31
+ console.log("Google webhook url must be set. Use environment variable GOOGLE_WEBHOOK_URL.");
32
+ return 1;
33
+ }
34
+
35
+ await require("./index")();
36
+ } catch (e) {
37
+ console.error(e);
38
+ return 1;
39
+ }
40
+ };
@@ -0,0 +1,82 @@
1
+ const GitLab = require("../GitLab");
2
+ const GoogleChat = require("../GoogleChat");
3
+ const relativeTime = require("dayjs/plugin/relativeTime");
4
+ const dayjs = require("dayjs");
5
+ require("dayjs/locale/cs");
6
+ dayjs.locale("cs");
7
+ dayjs.extend(relativeTime);
8
+
9
+ function inflect(value, word1, word2, word3) {
10
+ return `${value} ${value === 1 ? word1 : value <= 4 ? word2 : word3}`;
11
+ }
12
+
13
+ module.exports = async function run() {
14
+ const projects = await GitLab.getAllProjects();
15
+ const allMergeRequests = await GitLab.getAllMergeRequests();
16
+
17
+ const result = allMergeRequests
18
+ .map((mr) => ({
19
+ id: mr.id,
20
+ iid: mr.iid,
21
+ reviewers: mr.reviewers ?? [],
22
+ project: projects.find((project) => project.id === mr.project_id),
23
+ title: mr.title,
24
+ author: mr.author,
25
+ createdAt: mr.created_at,
26
+ updatedAt: mr.updated_at,
27
+ webUrl: mr.web_url,
28
+ state: mr.state.toUpperCase(),
29
+ targetBranch: mr.target_branch,
30
+ sourceBranch: mr.source_branch,
31
+ }))
32
+ .filter((mr) => mr.reviewers.length === 0)
33
+ .filter((mr) => mr.sourceBranch !== "develop" || mr.targetBranch !== "master")
34
+ .filter((mr) => !mr.project.archived);
35
+
36
+ if (result.length === 0) {
37
+ console.log("No merge requests to code review.");
38
+ return;
39
+ }
40
+
41
+ await GoogleChat.chatPostMessage({
42
+ text: `🔥🔥🔥 ${inflect(
43
+ result.length,
44
+ "MR čekající na code review",
45
+ "MR čekající na code review",
46
+ "MR čekajících na code review",
47
+ )}`,
48
+ });
49
+
50
+ for (const mr of result) {
51
+ const changes = await GitLab.getSingleMergeRequestChanges(mr.project.id, mr.iid);
52
+ const changesCount = Number.parseInt(changes.changes_count);
53
+
54
+ await GoogleChat.messageCardsV2([
55
+ {
56
+ cardId: `mr-card-${mr.id}`,
57
+ card: {
58
+ header: {
59
+ title: `${mr.project.name} | ${mr.title}`,
60
+ subtitle: `${mr.author.name} | ${dayjs(mr.updatedAt).fromNow()} | ${inflect(
61
+ changesCount,
62
+ "soubor",
63
+ "soubory",
64
+ "souborů",
65
+ )}`,
66
+ },
67
+ sections: [
68
+ {
69
+ widgets: [
70
+ {
71
+ buttonList: {
72
+ buttons: [{ text: "Otevřít MR", onClick: { openLink: { url: mr.webUrl } } }],
73
+ },
74
+ },
75
+ ],
76
+ },
77
+ ],
78
+ },
79
+ },
80
+ ]);
81
+ }
82
+ };