@intentius/chant-lexicon-prometheus 0.97.0 → 0.99.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.
Files changed (69) hide show
  1. package/README.md +6 -1
  2. package/dist/alertmanager.d.ts +4 -2
  3. package/dist/alertmanager.d.ts.map +1 -1
  4. package/dist/build.d.ts.map +1 -1
  5. package/dist/codegen/docs.d.ts.map +1 -1
  6. package/dist/import/embedded.d.ts +14 -0
  7. package/dist/import/embedded.d.ts.map +1 -0
  8. package/dist/import/generator.d.ts +50 -0
  9. package/dist/import/generator.d.ts.map +1 -0
  10. package/dist/import/parser.d.ts +52 -0
  11. package/dist/import/parser.d.ts.map +1 -0
  12. package/dist/import/slo.d.ts +15 -0
  13. package/dist/import/slo.d.ts.map +1 -0
  14. package/dist/integrations.d.ts +531 -0
  15. package/dist/integrations.d.ts.map +1 -0
  16. package/dist/integrity.json +7 -7
  17. package/dist/lint/rules/literal-credential.d.ts.map +1 -1
  18. package/dist/lint/rules/prom-ast.d.ts +8 -0
  19. package/dist/lint/rules/prom-ast.d.ts.map +1 -1
  20. package/dist/lint/rules/promql-literal.d.ts.map +1 -1
  21. package/dist/manifest.json +1 -1
  22. package/dist/model.d.ts +77 -121
  23. package/dist/model.d.ts.map +1 -1
  24. package/dist/plugin.d.ts.map +1 -1
  25. package/dist/rules/literal-credential.ts +52 -15
  26. package/dist/rules/prom-ast.ts +36 -0
  27. package/dist/rules/promql-literal.ts +15 -1
  28. package/dist/skills/chant-prometheus-alertmanager.md +4 -0
  29. package/dist/skills/chant-prometheus.md +11 -0
  30. package/package.json +2 -2
  31. package/src/alertmanager.ts +4 -1
  32. package/src/build.ts +2 -0
  33. package/src/codegen/docs.ts +5 -1
  34. package/src/import/cli.test.ts +98 -0
  35. package/src/import/embedded.test.ts +56 -0
  36. package/src/import/embedded.ts +44 -0
  37. package/src/import/generated-types.e2e.test.ts +77 -0
  38. package/src/import/generator.test.ts +138 -0
  39. package/src/import/generator.ts +578 -0
  40. package/src/import/parser.test.ts +133 -0
  41. package/src/import/parser.ts +365 -0
  42. package/src/import/roundtrip.test.ts +358 -0
  43. package/src/import/slo.ts +175 -0
  44. package/src/import/testdata/alertmanager-full.yml +89 -0
  45. package/src/import/testdata/alertmanager-integrations.yml +215 -0
  46. package/src/import/testdata/fixtures.ts +122 -0
  47. package/src/import/testdata/rules-full.yml +42 -0
  48. package/src/import/testdata/upstream/alertmanager-conf-good.yml +120 -0
  49. package/src/import/testdata/upstream/alertmanager-mattermost-default-webhook-url-file.yml +16 -0
  50. package/src/import/testdata/upstream/alertmanager-opsgenie-default-apikey-file.yml +27 -0
  51. package/src/import/testdata/upstream/alertmanager-rocketchat-default-token-file.yml +25 -0
  52. package/src/import/testdata/upstream/alertmanager-route-labels.yml +65 -0
  53. package/src/import/testdata/upstream/alertmanager-simple.yml +130 -0
  54. package/src/import/testdata/upstream/alertmanager-sns-topic-arn.yml +17 -0
  55. package/src/import/testdata/upstream/alertmanager-telegram-default-bot-token-file.yml +16 -0
  56. package/src/import/testdata/upstream/alertmanager-victorops-default-apikey-file.yml +23 -0
  57. package/src/import/testdata/upstream/alertmanager-wechat-default-api-secret-file.yml +18 -0
  58. package/src/import/testdata/upstream/prometheus-alerting-rules.yml +15 -0
  59. package/src/import/testdata/upstream/prometheus-alerting-templates.yml +23 -0
  60. package/src/import/testdata/upstream/prometheus-recording-rules.yml +7 -0
  61. package/src/integrations.ts +538 -0
  62. package/src/lint/rules/literal-credential.ts +52 -15
  63. package/src/lint/rules/prom-ast.ts +36 -0
  64. package/src/lint/rules/promql-literal.ts +15 -1
  65. package/src/lint/rules/rules.test.ts +58 -0
  66. package/src/model.ts +157 -109
  67. package/src/plugin.ts +15 -0
  68. package/src/skills/chant-prometheus-alertmanager.md +4 -0
  69. package/src/skills/chant-prometheus.md +11 -0
@@ -1,6 +1,6 @@
1
1
  import * as ts from "typescript";
2
2
  import type { LintRule, LintDiagnostic, LintContext } from "@intentius/chant/lint/rule";
3
- import { calleeName, literalText, position, propertyName } from "./prom-ast";
3
+ import { calleeName, constInitializers, literalText, position, propertyName, resolveConst } from "./prom-ast";
4
4
  import { checkPromql } from "../../promql";
5
5
 
6
6
  /**
@@ -21,7 +21,21 @@ export const promqlLiteralRule: LintRule = {
21
21
  const diagnostics: LintDiagnostic[] = [];
22
22
  const source = context.sourceFile;
23
23
 
24
+ const consts = constInitializers(source);
25
+ const scanned = new Set<ts.Node>();
26
+
27
+ // Rules lifted into a const (`rules: apiRules`) are followed to it and
28
+ // checked where the group uses them.
29
+ const follow = (value: ts.Expression) => {
30
+ const init = resolveConst(value, consts);
31
+ if (init !== value && !scanned.has(init)) {
32
+ scanned.add(init);
33
+ scan(init);
34
+ }
35
+ };
24
36
  const scan = (node: ts.Node) => {
37
+ if (ts.isShorthandPropertyAssignment(node)) follow(node.name);
38
+ if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer)) follow(node.initializer);
25
39
  if (ts.isPropertyAssignment(node) && propertyName(node) === "expr") {
26
40
  const text = literalText(node.initializer);
27
41
  if (text !== undefined) {
@@ -44,6 +44,52 @@ describe("PROM001 literal credential", () => {
44
44
  );
45
45
  expect(diags).toEqual([]);
46
46
  });
47
+
48
+ test("follows values lifted into named consts, shorthand props and spreads", () => {
49
+ const diags = literalCredentialRule.check(
50
+ ctx(`
51
+ const heartbeatEmail: EmailConfig[] = [{ to: "a@example.com", auth_password: "hunter2" }];
52
+ const heartbeat = new Receiver({ name: "heartbeat", email_configs: heartbeatEmail });
53
+ const oncallUntyped = { opsgenie_configs: [{ api_key: "x" }], pagerduty_configs: [{ service_key: "k" }] };
54
+ const oncall = new Receiver({ name: "oncall", ...oncallUntyped });
55
+ const global: AlertmanagerGlobalConfig = { smtp_auth_password: "p" };
56
+ const settings = new AlertmanagerSettings({ global });
57
+ const safe: PagerDutyConfig[] = [{ routing_key_file: "/etc/pd" }];
58
+ const pager = new Receiver({ name: "pager", pagerduty_configs: safe });
59
+ `),
60
+ );
61
+ expect(diags.map((d) => d.message.split("`")[1])).toEqual(["auth_password", "api_key", "service_key", "smtp_auth_password"]);
62
+ });
63
+
64
+ test("covers every integration's credential, and api_url and routing_key only where they are one", () => {
65
+ const diags = literalCredentialRule.check(
66
+ ctx(`
67
+ const chatSlack: SlackConfig[] = [{ api_url: "https://hooks.slack.com/services/T/B/X", app_token: "xoxb-1" }];
68
+ new Receiver({
69
+ name: "chat",
70
+ slack_configs: chatSlack,
71
+ discord_configs: [{ webhook_url: "https://discord.com/api/webhooks/1/x" }],
72
+ telegram_configs: [{ bot_token: "123:abc", chat_id: 1 }],
73
+ webex_configs: [{ api_url: "https://webexapis.com/v1/messages", room_id: "r" }],
74
+ victorops_configs: [{ api_key: "k", routing_key: "ops" }],
75
+ pushover_configs: [{ user_key: "u", token: "t" }],
76
+ rocketchat_configs: [{ token_id: "i", token_file: "/etc/rc" }],
77
+ });
78
+ new AlertmanagerSettings({ global: { telegram_bot_token: "123:abc", webex_api_url: "https://webexapis.com/v1/messages" } });
79
+ `),
80
+ );
81
+ expect(diags.map((d) => d.message.split("`")[1])).toEqual([
82
+ "api_url",
83
+ "app_token",
84
+ "webhook_url",
85
+ "bot_token",
86
+ "api_key",
87
+ "user_key",
88
+ "token",
89
+ "token_id",
90
+ "telegram_bot_token",
91
+ ]);
92
+ });
47
93
  });
48
94
 
49
95
  describe("PROM002 PromQL literal", () => {
@@ -61,6 +107,18 @@ describe("PROM002 PromQL literal", () => {
61
107
  expect(diags[0].line).toBe(3);
62
108
  });
63
109
 
110
+ test("follows rules lifted into a named const", () => {
111
+ const diags = promqlLiteralRule.check(
112
+ ctx(`
113
+ const apiRules: Rule[] = [{ record: "a:b", expr: "sum(" }, { alert: "A", expr: "up == 0" }];
114
+ const api = new RuleGroup({ name: "api", rules: apiRules });
115
+ const rules: Rule[] = [{ record: "c:d", expr: "rate(x[5m]" }];
116
+ const other = new RuleGroup({ name: "other", rules });
117
+ `),
118
+ );
119
+ expect(diags.map((d) => d.line)).toEqual([2, 4]);
120
+ });
121
+
64
122
  test("ignores built expressions and exprs outside a RuleGroup", () => {
65
123
  const diags = promqlLiteralRule.check(
66
124
  ctx(`
package/src/model.ts CHANGED
@@ -13,6 +13,31 @@
13
13
  * docs apply word for word.
14
14
  */
15
15
 
16
+ import {
17
+ RECEIVER_INTEGRATION_TYPES,
18
+ type DiscordConfig,
19
+ type EmailConfig,
20
+ type HttpClientConfig,
21
+ type IncidentioConfig,
22
+ type JiraConfig,
23
+ type MattermostConfig,
24
+ type MSTeamsConfig,
25
+ type MSTeamsV2Config,
26
+ type OpsGenieConfig,
27
+ type PagerDutyConfig,
28
+ type PushoverConfig,
29
+ type ReceiverIntegration,
30
+ type RocketchatConfig,
31
+ type SlackConfig,
32
+ type SNSConfig,
33
+ type TelegramConfig,
34
+ type TlsConfig,
35
+ type VictorOpsConfig,
36
+ type WebexConfig,
37
+ type WebhookConfig,
38
+ type WechatConfig,
39
+ } from "./integrations";
40
+
16
41
  /** Labels or annotations: string keys to string values. */
17
42
  export type LabelSet = Record<string, string>;
18
43
 
@@ -78,112 +103,31 @@ export function ruleName(rule: RuleConfig): string {
78
103
 
79
104
  // ── Alertmanager ────────────────────────────────────────────────────
80
105
 
81
- /** HTTP client settings shared by the receivers that make HTTP calls. */
82
- export interface HttpClientConfig {
83
- basic_auth?: { username?: string; username_file?: string; password?: string; password_file?: string };
84
- authorization?: { type?: string; credentials?: string; credentials_file?: string };
85
- bearer_token?: string;
86
- bearer_token_file?: string;
87
- oauth2?: Record<string, unknown>;
88
- proxy_url?: string;
89
- follow_redirects?: boolean;
90
- enable_http2?: boolean;
91
- tls_config?: {
92
- ca_file?: string;
93
- cert_file?: string;
94
- key_file?: string;
95
- server_name?: string;
96
- insecure_skip_verify?: boolean;
97
- min_version?: string;
98
- };
99
- }
100
-
101
- interface NotifierBase {
102
- /** Whether to notify about resolved alerts too. */
103
- send_resolved?: boolean;
104
- }
105
-
106
- export interface WebhookConfig extends NotifierBase {
107
- /** Where alerts are POSTed. Set this or `url_file`. */
108
- url?: string;
109
- url_file?: string;
110
- http_config?: HttpClientConfig;
111
- /** Most alerts per message; 0 sends them all. */
112
- max_alerts?: number;
113
- timeout?: string;
114
- }
115
-
116
- export interface EmailConfig extends NotifierBase {
117
- to: string;
118
- from?: string;
119
- /** `host:port` of the SMTP server. Falls back to `global.smtp_smarthost`. */
120
- smarthost?: string;
121
- hello?: string;
122
- auth_username?: string;
123
- auth_password?: string;
124
- auth_password_file?: string;
125
- auth_secret?: string;
126
- auth_secret_file?: string;
127
- auth_identity?: string;
128
- require_tls?: boolean;
129
- tls_config?: HttpClientConfig["tls_config"];
130
- html?: string;
131
- text?: string;
132
- headers?: Record<string, string>;
133
- }
134
-
135
- export interface SlackConfig extends NotifierBase {
136
- /** The incoming-webhook URL. It is a credential: prefer `api_url_file`. */
137
- api_url?: string;
138
- api_url_file?: string;
139
- channel?: string;
140
- username?: string;
141
- color?: string;
142
- title?: string;
143
- title_link?: string;
144
- pretext?: string;
145
- text?: string;
146
- footer?: string;
147
- fallback?: string;
148
- icon_emoji?: string;
149
- icon_url?: string;
150
- link_names?: boolean;
151
- short_fields?: boolean;
152
- mrkdwn_in?: string[];
153
- fields?: Array<{ title: string; value: string; short?: boolean }>;
154
- actions?: Array<Record<string, unknown>>;
155
- http_config?: HttpClientConfig;
156
- }
157
-
158
- export interface PagerDutyConfig extends NotifierBase {
159
- /** Events API v2 integration key. A credential: prefer `routing_key_file`. */
160
- routing_key?: string;
161
- routing_key_file?: string;
162
- /** Events API v1 integration key. A credential: prefer `service_key_file`. */
163
- service_key?: string;
164
- service_key_file?: string;
165
- url?: string;
166
- client?: string;
167
- client_url?: string;
168
- description?: string;
169
- severity?: string;
170
- class?: string;
171
- component?: string;
172
- group?: string;
173
- source?: string;
174
- details?: Record<string, string>;
175
- links?: Array<{ href: string; text?: string }>;
176
- images?: Array<{ src: string; alt?: string; href?: string }>;
177
- http_config?: HttpClientConfig;
178
- }
106
+ export * from "./integrations";
179
107
 
180
108
  /** One entry under `receivers:`. */
181
109
  export interface ReceiverConfig {
182
110
  name: string;
183
- webhook_configs?: WebhookConfig[];
111
+ /** Labels on the receiver, exposed to notification templates. */
112
+ labels?: LabelSet;
113
+ discord_configs?: DiscordConfig[];
184
114
  email_configs?: EmailConfig[];
185
- slack_configs?: SlackConfig[];
115
+ incidentio_configs?: IncidentioConfig[];
186
116
  pagerduty_configs?: PagerDutyConfig[];
117
+ slack_configs?: SlackConfig[];
118
+ webhook_configs?: WebhookConfig[];
119
+ opsgenie_configs?: OpsGenieConfig[];
120
+ wechat_configs?: WechatConfig[];
121
+ pushover_configs?: PushoverConfig[];
122
+ victorops_configs?: VictorOpsConfig[];
123
+ sns_configs?: SNSConfig[];
124
+ telegram_configs?: TelegramConfig[];
125
+ webex_configs?: WebexConfig[];
126
+ msteams_configs?: MSTeamsConfig[];
127
+ msteamsv2_configs?: MSTeamsV2Config[];
128
+ jira_configs?: JiraConfig[];
129
+ rocketchat_configs?: RocketchatConfig[];
130
+ mattermost_configs?: MattermostConfig[];
187
131
  }
188
132
 
189
133
  /** A route in the routing tree. The top-level `route:` is the root. */
@@ -198,11 +142,18 @@ export interface RouteConfig {
198
142
  repeat_interval?: string;
199
143
  mute_time_intervals?: string[];
200
144
  active_time_intervals?: string[];
145
+ /**
146
+ * Labels on the route, inherited by child routes and exposed to
147
+ * notification templates as `routeLabels`. Values may be Go templates.
148
+ */
149
+ labels?: LabelSet;
201
150
  routes?: RouteConfig[];
202
151
  }
203
152
 
204
153
  /** One entry under `inhibit_rules:`. */
205
154
  export interface InhibitRuleConfig {
155
+ /** A name for the rule, shown in Alertmanager's logs and metrics. */
156
+ name?: string;
206
157
  source_matchers?: string[];
207
158
  target_matchers?: string[];
208
159
  /** Labels that must be equal on source and target for the inhibition to apply. */
@@ -235,12 +186,18 @@ export interface TimeIntervalConfig {
235
186
  time_intervals: TimePeriodConfig[];
236
187
  }
237
188
 
238
- /** `global:` settings. */
189
+ /**
190
+ * `global:` settings: defaults the receivers fall back to. Credentials with
191
+ * a `*_file` sibling are best set through it; PROM001 flags a literal.
192
+ */
239
193
  export interface AlertmanagerGlobalConfig {
194
+ /** How long an alert that stops being updated stays firing. */
240
195
  resolve_timeout?: string;
196
+ http_config?: HttpClientConfig;
197
+ jira_api_url?: string;
241
198
  smtp_from?: string;
242
- smtp_smarthost?: string;
243
199
  smtp_hello?: string;
200
+ smtp_smarthost?: string;
244
201
  smtp_auth_username?: string;
245
202
  smtp_auth_password?: string;
246
203
  smtp_auth_password_file?: string;
@@ -248,10 +205,93 @@ export interface AlertmanagerGlobalConfig {
248
205
  smtp_auth_secret_file?: string;
249
206
  smtp_auth_identity?: string;
250
207
  smtp_require_tls?: boolean;
208
+ smtp_tls_config?: TlsConfig;
209
+ smtp_force_implicit_tls?: boolean;
251
210
  slack_api_url?: string;
252
211
  slack_api_url_file?: string;
212
+ slack_app_token?: string;
213
+ slack_app_token_file?: string;
214
+ slack_app_url?: string;
253
215
  pagerduty_url?: string;
254
- http_config?: HttpClientConfig;
216
+ opsgenie_api_url?: string;
217
+ opsgenie_api_key?: string;
218
+ opsgenie_api_key_file?: string;
219
+ wechat_api_url?: string;
220
+ wechat_api_secret?: string;
221
+ wechat_api_secret_file?: string;
222
+ wechat_api_corp_id?: string;
223
+ victorops_api_url?: string;
224
+ victorops_api_key?: string;
225
+ victorops_api_key_file?: string;
226
+ telegram_api_url?: string;
227
+ telegram_bot_token?: string;
228
+ telegram_bot_token_file?: string;
229
+ webex_api_url?: string;
230
+ rocketchat_api_url?: string;
231
+ rocketchat_token?: string;
232
+ rocketchat_token_file?: string;
233
+ rocketchat_token_id?: string;
234
+ rocketchat_token_id_file?: string;
235
+ mattermost_webhook_url?: string;
236
+ mattermost_webhook_url_file?: string;
237
+ }
238
+
239
+ /** Every `global:` field, as a list, for code that reads a parsed file. */
240
+ export const ALERTMANAGER_GLOBAL_FIELDS = Object.freeze(Object.keys({
241
+ resolve_timeout: true,
242
+ http_config: true,
243
+ jira_api_url: true,
244
+ smtp_from: true,
245
+ smtp_hello: true,
246
+ smtp_smarthost: true,
247
+ smtp_auth_username: true,
248
+ smtp_auth_password: true,
249
+ smtp_auth_password_file: true,
250
+ smtp_auth_secret: true,
251
+ smtp_auth_secret_file: true,
252
+ smtp_auth_identity: true,
253
+ smtp_require_tls: true,
254
+ smtp_tls_config: true,
255
+ smtp_force_implicit_tls: true,
256
+ slack_api_url: true,
257
+ slack_api_url_file: true,
258
+ slack_app_token: true,
259
+ slack_app_token_file: true,
260
+ slack_app_url: true,
261
+ pagerduty_url: true,
262
+ opsgenie_api_url: true,
263
+ opsgenie_api_key: true,
264
+ opsgenie_api_key_file: true,
265
+ wechat_api_url: true,
266
+ wechat_api_secret: true,
267
+ wechat_api_secret_file: true,
268
+ wechat_api_corp_id: true,
269
+ victorops_api_url: true,
270
+ victorops_api_key: true,
271
+ victorops_api_key_file: true,
272
+ telegram_api_url: true,
273
+ telegram_bot_token: true,
274
+ telegram_bot_token_file: true,
275
+ webex_api_url: true,
276
+ rocketchat_api_url: true,
277
+ rocketchat_token: true,
278
+ rocketchat_token_file: true,
279
+ rocketchat_token_id: true,
280
+ rocketchat_token_id_file: true,
281
+ mattermost_webhook_url: true,
282
+ mattermost_webhook_url_file: true,
283
+ } satisfies Record<keyof AlertmanagerGlobalConfig, true>)) as readonly (keyof AlertmanagerGlobalConfig)[];
284
+
285
+ /** `tracing:` settings: where Alertmanager sends its own traces. */
286
+ export interface AlertmanagerTracingConfig {
287
+ client_type?: "grpc" | "http";
288
+ endpoint?: string;
289
+ sampling_fraction?: number;
290
+ insecure?: boolean;
291
+ headers?: Record<string, string>;
292
+ compression?: string;
293
+ timeout?: string;
294
+ tls_config?: TlsConfig;
255
295
  }
256
296
 
257
297
  /** A whole `alertmanager.yml`, the file `amtool check-config` reads. */
@@ -262,19 +302,27 @@ export interface AlertmanagerConfig {
262
302
  inhibit_rules?: InhibitRuleConfig[];
263
303
  receivers?: ReceiverConfig[];
264
304
  time_intervals?: TimeIntervalConfig[];
305
+ tracing?: AlertmanagerTracingConfig;
265
306
  }
266
307
 
267
- /** The four receiver integrations this lexicon types. */
268
- export const RECEIVER_INTEGRATIONS = ["webhook_configs", "email_configs", "slack_configs", "pagerduty_configs"] as const;
308
+ /** The receiver integrations, every one Alertmanager defines. */
309
+ export const RECEIVER_INTEGRATIONS = Object.keys(RECEIVER_INTEGRATION_TYPES) as readonly ReceiverIntegration[];
269
310
 
270
311
  /** True when a parsed document has the shape of a Prometheus rule file. */
271
312
  export function looksLikeRuleFile(value: unknown): value is RuleFileConfig {
272
313
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
273
- const groups = (value as Record<string, unknown>).groups;
314
+ const v = value as Record<string, unknown>;
315
+ const groups = v.groups;
274
316
  if (!Array.isArray(groups)) return false;
275
- return groups.every(
276
- (g) => typeof g === "object" && g !== null && typeof (g as Record<string, unknown>).name === "string" && Array.isArray((g as Record<string, unknown>).rules),
277
- );
317
+ // Grafana's alerting provisioning files also hold `groups:` of named rule lists. Prometheus reads rule
318
+ // files strictly (unknown keys are errors), so `apiVersion`, a group `folder` or a rule's `data` means Grafana.
319
+ if ("apiVersion" in v) return false;
320
+ return groups.every((g) => {
321
+ if (typeof g !== "object" || g === null) return false;
322
+ const group = g as Record<string, unknown>;
323
+ if (typeof group.name !== "string" || !Array.isArray(group.rules) || "folder" in group) return false;
324
+ return !group.rules.some((r) => typeof r === "object" && r !== null && "data" in r);
325
+ });
278
326
  }
279
327
 
280
328
  /** True when a parsed document has the shape of an `alertmanager.yml`. */
package/src/plugin.ts CHANGED
@@ -9,6 +9,9 @@ import { prometheusAuditCatalog } from "./lint/audit-catalog";
9
9
  import { completions } from "./lsp/completions";
10
10
  import { hover } from "./lsp/hover";
11
11
  import { detectTemplate } from "./detect";
12
+ import { PrometheusParser } from "./import/parser";
13
+ import { PrometheusGenerator } from "./import/generator";
14
+ import { ruleGroupsImporter } from "./import/embedded";
12
15
  import { initTemplates } from "./init-templates";
13
16
  import { prometheusSkills } from "./skill-defs";
14
17
  import { CATALOG } from "./catalog";
@@ -110,6 +113,18 @@ export const prometheusPlugin: LexiconPlugin = {
110
113
  return detectTemplate(data);
111
114
  },
112
115
 
116
+ templateParser() {
117
+ return new PrometheusParser();
118
+ },
119
+
120
+ templateGenerator() {
121
+ return new PrometheusGenerator();
122
+ },
123
+
124
+ embeddedImporters() {
125
+ return [ruleGroupsImporter];
126
+ },
127
+
113
128
  initTemplates(template?: string) {
114
129
  return initTemplates(template);
115
130
  },
@@ -47,6 +47,10 @@ export const pageMutesTicket = new InhibitRule({
47
47
  });
48
48
  ```
49
49
 
50
+ ## Starting from an existing alertmanager.yml
51
+
52
+ Import it rather than retyping it: `chant import alertmanager.yml --output src` (with `--lexicon prometheus` outside a chant project). The importer writes `receivers.ts`, `time-intervals.ts`, `routes.ts` (the root `Route`, children as `RouteProps` that reference receivers and time intervals by variable), `inhibit-rules.ts` and `settings.ts`. `*_file` paths and Go templates are kept as written; a literal credential is imported as found and PROM001 reports it, so offer to move it to the `*_file` field. Every integration comes back typed (`OpsGenieConfig[]`, `MSTeamsV2Config[]`, ...); a receiver or `global` key Alertmanager doesn't define is spread in from an untyped const, with a warning. `match`/`match_re` and the top-level `mute_time_intervals` come back in their current spelling (`matchers`, `time_intervals`), and `chant import` prints a warning for each.
53
+
50
54
  ## What the checks hold you to
51
55
 
52
56
  - One root route (the `Route` no other route nests), with a receiver and no matchers (PROM205).
@@ -15,6 +15,17 @@ The prometheus lexicon (`@intentius/chant-lexicon-prometheus`) types Prometheus
15
15
  export default { lexicons: ["prometheus"] };
16
16
  ```
17
17
 
18
+ ## Starting from an existing rule file
19
+
20
+ When the user already has a rule file, import it rather than retyping it:
21
+
22
+ ```bash
23
+ chant import rules.yml --output src # add --lexicon prometheus outside a chant project
24
+ chant build src --lexicon prometheus -o rules.yml # the same groups back
25
+ ```
26
+
27
+ The importer writes `rules.ts` (per group, a `Rule[]` const of plain objects and a `RuleGroup`) and `slos.ts` for any group an `Slo()` built, as the `Slo` call. Multi-line expressions become template literals and Go templates in annotations stay as written. Read the warnings `chant import` prints: they name any field Prometheus doesn't define, which is not carried.
28
+
18
29
  ## Declaring a group
19
30
 
20
31
  A group takes the rule file's own keys (`name`, `interval`, `query_offset`, `limit`, `labels`, `rules`). Rules are plain objects: `record` + `expr` for a recording rule, `alert` + `expr` (+ `for`, `keep_firing_for`, `labels`, `annotations`) for an alerting rule.