@mikitasazan/notify 1.8.0 → 1.9.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 -6
- package/dist/cli.js +69 -60
- package/dist/events.d.ts +127 -117
- package/dist/events.js +30 -24
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/render.d.ts +57 -49
- package/dist/render.js +255 -221
- package/dist/routes.d.ts +31 -27
- package/dist/routes.js +25 -20
- package/dist/send.d.ts +1 -19
- package/dist/send.js +2 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,17 +95,18 @@ notify report --project playhub --json < payload.json # весь объект
|
|
|
95
95
|
|
|
96
96
|
Полные сигнатуры — `src/events.ts`.
|
|
97
97
|
|
|
98
|
-
**Ключ задачи.** Первая строка каждой карточки —
|
|
98
|
+
**Ключ задачи.** Первая строка каждой карточки — три тега: `#тип #ключ
|
|
99
|
+
#итог`.
|
|
99
100
|
По ним дневной разборщик сверяет «это 🔴 уже закрыто более поздней карточкой
|
|
100
101
|
той же задачи?» без сравнения человеческих формулировок. Явный `--key`
|
|
101
102
|
побеждает; без него ключ выводится из заголовка и меняется вместе с ним —
|
|
102
103
|
регулярный отправитель передаёт `--key` явно. В stdout CLI ключ не попадает.
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
**Свободного HTML в пакете нет.** Дверь `sendReport()` удалена 25.08.2026:
|
|
106
|
+
она отдавала произвольную разметку и ставила карточке два тега вместо трёх —
|
|
107
|
+
единственный вид, который нельзя было отфильтровать по итогу. Последний её
|
|
108
|
+
отправитель перешёл на типизированный `report` ещё 25.08; тип события задаёт
|
|
109
|
+
каждый знак карточки, других путей нет.
|
|
109
110
|
|
|
110
111
|
**Неизвестный проект** не роняет вызвавший крон (код возврата 0), но больше и
|
|
111
112
|
не исчезает молча: в mac-config Ops уходит красная карточка «notify: событие
|
package/dist/cli.js
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* `notify <type> [--flag value]...` —
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* `notify <type> [--flag value]...` — a thin dispatcher. Zero dependencies:
|
|
4
|
+
* argument parsing is written by hand (not yargs/commander), because it
|
|
5
|
+
* needs exactly two kinds of flags (a single one and a repeatable
|
|
6
|
+
* `key=value`).
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* (
|
|
10
|
-
*
|
|
8
|
+
* The exit code is ALWAYS 0 — a notification has no right to bring down the
|
|
9
|
+
* deploy or task that called it. All errors go to stderr only. There is
|
|
10
|
+
* deliberately no exception (see docs/rollout.md "what we don't do"): there
|
|
11
|
+
* is no scenario where a deploy should fail because a message did not send.
|
|
11
12
|
*
|
|
12
13
|
* notify deploy --project playhub --status ok --commit "msg" [--commit-url "..."] --url "..."
|
|
13
|
-
* notify job --project playhub --job "
|
|
14
|
-
* notify report --project playhub --title "
|
|
14
|
+
* notify job --project playhub --job "Game import" --status ok --stat "added=5"
|
|
15
|
+
* notify report --project playhub --title "Daily summary" --line "Games=1284"
|
|
15
16
|
* notify ci --project arvent --status fail --branch master --actor saz_sam
|
|
16
17
|
* notify pr --project arvent --action opened --number 142 --title "..."
|
|
17
|
-
* notify incident --project arvent --title "Redis
|
|
18
|
-
* notify file --project arvent --title "
|
|
19
|
-
* notify <type> [--key
|
|
20
|
-
* notify <type> --json < payload.json #
|
|
21
|
-
* notify setup <chat_id
|
|
18
|
+
* notify incident --project arvent --title "Redis is unreachable" --detail "$ERR"
|
|
19
|
+
* notify file --project arvent --title "Full dialogues" --path ./out.txt [--filename name.txt]
|
|
20
|
+
* notify <type> [--key stable-key] # the task's key on the card's last line
|
|
21
|
+
* notify <type> --json < payload.json # the whole event object on stdin
|
|
22
|
+
* notify setup <forum chat_id> <project key> # create the tabs, see setup.ts
|
|
22
23
|
*/
|
|
23
24
|
import { readFileSync } from 'node:fs';
|
|
24
25
|
import { KNOWN_FLAGS } from "./cli-flags.js";
|
|
@@ -52,7 +53,7 @@ if (command === 'setup') {
|
|
|
52
53
|
}
|
|
53
54
|
const flags = new Map();
|
|
54
55
|
const parseErrors = [];
|
|
55
|
-
/**
|
|
56
|
+
/** Flags with no value. Everything else must have one. */
|
|
56
57
|
const BOOLEAN_FLAGS = new Set(['json', 'recovered', 'dry-run']);
|
|
57
58
|
for (let i = 1; i < args.length; i++) {
|
|
58
59
|
const arg = args[i];
|
|
@@ -60,8 +61,8 @@ for (let i = 1; i < args.length; i++) {
|
|
|
60
61
|
parseErrors.push(`stray argument with no flag: "${safe(arg)}"`);
|
|
61
62
|
continue;
|
|
62
63
|
}
|
|
63
|
-
//
|
|
64
|
-
// (
|
|
64
|
+
// The `--key=value` form is required for values that start with `--`
|
|
65
|
+
// (an error message, a diff chunk): otherwise they would get eaten as flags.
|
|
65
66
|
const eq = arg.indexOf('=');
|
|
66
67
|
if (eq !== -1) {
|
|
67
68
|
const key = arg.slice(2, eq);
|
|
@@ -74,10 +75,10 @@ for (let i = 1; i < args.length; i++) {
|
|
|
74
75
|
continue;
|
|
75
76
|
}
|
|
76
77
|
const next = args[i + 1];
|
|
77
|
-
//
|
|
78
|
-
// `--url`
|
|
79
|
-
//
|
|
80
|
-
//
|
|
78
|
+
// A flag with no value used to silently become the string 'true'. That is
|
|
79
|
+
// how `--url` at the end of a command produced `href="true"`, Telegram
|
|
80
|
+
// answered 400 and THE WHOLE MESSAGE WAS LOST, and `--status` with no value
|
|
81
|
+
// painted 🔴 on a successful deploy. Now it is an explicit parse error.
|
|
81
82
|
if (next === undefined || next.startsWith('--')) {
|
|
82
83
|
parseErrors.push(`flag --${safe(key)} with no value`);
|
|
83
84
|
continue;
|
|
@@ -91,7 +92,7 @@ for (const key of flags.keys()) {
|
|
|
91
92
|
parseErrors.push(`unknown flag --${safe(key)}`);
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
|
-
//
|
|
95
|
+
// A number with an explicit parse error, otherwise the render drew "PR #NaN".
|
|
95
96
|
const num = (key) => {
|
|
96
97
|
const raw = one(key);
|
|
97
98
|
const n = Number(raw);
|
|
@@ -102,12 +103,13 @@ const num = (key) => {
|
|
|
102
103
|
return n;
|
|
103
104
|
};
|
|
104
105
|
/**
|
|
105
|
-
* `--item "
|
|
106
|
+
* `--item "text"` or `--item "text|https://link"`.
|
|
106
107
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
108
|
+
* An item's group name cannot be passed the same way `--stat` does it: the
|
|
109
|
+
* bar here is already taken by the link. So `--item-group "Red checks"` is
|
|
110
|
+
* one name for all the items in this call. Senders' lists are homogeneous
|
|
111
|
+
* (red checks, disabled processes), and a mixed list is what `--json` is
|
|
112
|
+
* for.
|
|
111
113
|
*/
|
|
112
114
|
const items = () => {
|
|
113
115
|
const name = flags.get('item-group')?.[0];
|
|
@@ -118,13 +120,13 @@ const items = () => {
|
|
|
118
120
|
});
|
|
119
121
|
};
|
|
120
122
|
/**
|
|
121
|
-
* `--stat "label=value"`,
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
123
|
+
* `--stat "label=value"`, and since 25.08.2026 — `--stat "Group | label=value"`:
|
|
124
|
+
* group name, vertical bar, label. The bar was chosen because it appears in
|
|
125
|
+
* no live label, while a colon does ("Eval: bot answer quality") and equals
|
|
126
|
+
* is taken by the value. Spaces around the bar are optional.
|
|
125
127
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
+
* Without the bar everything works as before — over twenty senders send it
|
|
129
|
+
* that way, and not one of them needs to change.
|
|
128
130
|
*/
|
|
129
131
|
const pairs = (key) => (flags.get(key) ?? []).map((s) => {
|
|
130
132
|
const idx = s.indexOf('=');
|
|
@@ -154,9 +156,10 @@ const ISSUE_ALIASES = {
|
|
|
154
156
|
assigned: 'assigned',
|
|
155
157
|
closed: 'closed'
|
|
156
158
|
};
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
159
|
+
// The error goes into parseErrors — the same path the rest of parsing takes:
|
|
160
|
+
// below, it prints all the errors together and exits BEFORE sending. The
|
|
161
|
+
// placeholder value only needs to satisfy the type; it never lives to reach
|
|
162
|
+
// the network.
|
|
160
163
|
const prAction = (raw) => {
|
|
161
164
|
const hit = PR_ALIASES[(raw ?? '').toLowerCase()];
|
|
162
165
|
if (!hit) {
|
|
@@ -174,19 +177,21 @@ const issueAction = (raw) => {
|
|
|
174
177
|
return hit;
|
|
175
178
|
};
|
|
176
179
|
/**
|
|
177
|
-
*
|
|
180
|
+
* Anything not recognized as a success counts as a failure.
|
|
178
181
|
*
|
|
179
|
-
*
|
|
180
|
-
* (
|
|
181
|
-
*
|
|
182
|
-
*
|
|
182
|
+
* What matters here is not strictness but consistency: `--status success`
|
|
183
|
+
* used to (a natural typo on a manual call) paint 🔴 "failed," but
|
|
184
|
+
* `severity()` saw "not fail" and sent the message WITH NO SOUND. A red
|
|
185
|
+
* card with no sound is the worst outcome: it looks like an incident but
|
|
186
|
+
* does not wake anyone up.
|
|
183
187
|
*/
|
|
184
188
|
const status = () => {
|
|
185
189
|
const raw = (one('status') ?? '').toLowerCase();
|
|
186
190
|
return raw === 'ok' || raw === 'success' || raw === 'passed' || raw === '0' ? 'ok' : 'fail';
|
|
187
191
|
};
|
|
188
|
-
// `job`
|
|
189
|
-
//
|
|
192
|
+
// `job` is the only type with a third state (`disabled`): the task did not
|
|
193
|
+
// fail on its own, someone switched it off from outside (GitHub Actions out
|
|
194
|
+
// of minutes).
|
|
190
195
|
const jobStatus = () => {
|
|
191
196
|
const raw = (one('status') ?? '').toLowerCase();
|
|
192
197
|
if (raw === 'disabled') {
|
|
@@ -201,13 +206,14 @@ let event;
|
|
|
201
206
|
if (flags.has('json')) {
|
|
202
207
|
try {
|
|
203
208
|
const payload = JSON.parse(readFileSync(0, 'utf-8'));
|
|
204
|
-
// type
|
|
205
|
-
//
|
|
209
|
+
// type comes from the command, not from the payload: otherwise --pr
|
|
210
|
+
// <object with type:deploy> would send an event of a different type.
|
|
206
211
|
event = { ...payload, type: command };
|
|
207
212
|
}
|
|
208
213
|
catch (err) {
|
|
209
|
-
//
|
|
210
|
-
//
|
|
214
|
+
// Also into parseErrors: both analytics jobs call the CLI through
|
|
215
|
+
// `|| true`, and a silent JSON parse failure would mean a green cron run
|
|
216
|
+
// with no daily report.
|
|
211
217
|
parseErrors.push(`--json from stdin did not parse: ${safe(err instanceof Error ? err.message : err)}`);
|
|
212
218
|
}
|
|
213
219
|
}
|
|
@@ -356,15 +362,15 @@ else {
|
|
|
356
362
|
};
|
|
357
363
|
break;
|
|
358
364
|
default:
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
//
|
|
365
|
+
// Into parseErrors, not just into the log: otherwise an unknown type
|
|
366
|
+
// went quiet — no event was built, there were no parse errors, and
|
|
367
|
+
// the CLI exited zero, sending nothing and saying nothing about it.
|
|
362
368
|
parseErrors.push(`unknown event type: ${safe(command ?? '(none given)')}`);
|
|
363
369
|
}
|
|
364
370
|
}
|
|
365
|
-
// --key
|
|
366
|
-
// (
|
|
367
|
-
// key
|
|
371
|
+
// --key applies to any type — one spot instead of a line in every case
|
|
372
|
+
// (nine copies would already have lost the tenth). `??`: the --json path
|
|
373
|
+
// may carry key in the object itself, a missing flag must not overwrite it.
|
|
368
374
|
if (event) {
|
|
369
375
|
event.key = one('key') ?? event.key;
|
|
370
376
|
// --path is applicable to any type too, for the same reason --key is.
|
|
@@ -374,8 +380,9 @@ if (event) {
|
|
|
374
380
|
if (event?.filename && !event.path) {
|
|
375
381
|
parseErrors.push('--filename: given without --path, so there is no file to name');
|
|
376
382
|
}
|
|
377
|
-
//
|
|
378
|
-
//
|
|
383
|
+
// Parse errors — before sending: better to say clearly what is wrong with
|
|
384
|
+
// the command than to send a message with "true" instead of a link, or a
|
|
385
|
+
// 🔴 on a successful deploy.
|
|
379
386
|
if (parseErrors.length > 0) {
|
|
380
387
|
for (const err of parseErrors) {
|
|
381
388
|
log(err);
|
|
@@ -403,15 +410,17 @@ if (event && flags.has('dry-run')) {
|
|
|
403
410
|
process.exit(0);
|
|
404
411
|
}
|
|
405
412
|
if (event) {
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
413
|
+
// Catches EVERYTHING: a notification has no right to bring down the
|
|
414
|
+
// deploy or cron job that called it. In bash with `set -e` (or in
|
|
415
|
+
// `trap ... ERR`) a non-zero code here would fail the task itself — the
|
|
416
|
+
// exact thing this package must never do.
|
|
409
417
|
try {
|
|
410
418
|
log(await notify(event));
|
|
411
419
|
}
|
|
412
420
|
catch (err) {
|
|
413
|
-
//
|
|
414
|
-
//
|
|
421
|
+
// The word `failed` is a contract, the same one the parse error above
|
|
422
|
+
// uses. Without it, an exception while sending read to watchdogs as
|
|
423
|
+
// "nothing happened."
|
|
415
424
|
log(`failed: ${safe(err instanceof Error ? err.message : err)}`);
|
|
416
425
|
}
|
|
417
426
|
}
|