deploy-vir 3.0.0 → 3.1.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/dist/config.d.ts CHANGED
@@ -52,6 +52,15 @@ export type SlackNotificationConfig = {
52
52
  * @category Config
53
53
  */
54
54
  export type NotificationConfig = SlackNotificationConfig;
55
+ /**
56
+ * A deploy-level notification override. Same as {@link NotificationConfig} but `webhookUrl` is
57
+ * optional — when omitted, the first matching top-level notification's `webhookUrl` is used.
58
+ *
59
+ * @category Config
60
+ */
61
+ export type DeployNotificationConfig = Omit<SlackNotificationConfig, 'webhookUrl'> & {
62
+ webhookUrl?: string | undefined;
63
+ };
55
64
  /**
56
65
  * A repo config for deploying.
57
66
  *
@@ -96,6 +105,14 @@ export type DeployVirBranchConfig = {
96
105
  toBranch: string;
97
106
  enableNotifications?: boolean | undefined;
98
107
  }[];
108
+ enableNotifications?: boolean | undefined;
109
+ /**
110
+ * Deploy-level notification overrides. When set, these are used instead of the top-level
111
+ * notifications. If a notification here omits `webhookUrl`, the first top-level notification
112
+ * with the same `target` provides it. Setting this also implicitly enables notifications for
113
+ * this deploy.
114
+ */
115
+ notifications?: DeployNotificationConfig[] | undefined;
99
116
  };
100
117
  /**
101
118
  * Define a type safe deploy-vir config.
package/dist/git.js CHANGED
@@ -100,8 +100,14 @@ export async function pushDeploy(git, branchConfig, repoConfig, remoteName, noti
100
100
  after: afterCommit,
101
101
  },
102
102
  };
103
- if (notifications?.length && (repoConfig.enableNotifications || enableNotifications)) {
104
- await sendNotifications(notifications, {
103
+ const resolvedNotifications = resolveNotifications({
104
+ branchConfig,
105
+ enableNotifications,
106
+ globalNotifications: notifications,
107
+ repoConfig,
108
+ });
109
+ if (resolvedNotifications.length) {
110
+ await sendNotifications(resolvedNotifications, {
105
111
  branchConfig,
106
112
  deployResult,
107
113
  repoConfig,
@@ -110,3 +116,28 @@ export async function pushDeploy(git, branchConfig, repoConfig, remoteName, noti
110
116
  return deployResult;
111
117
  });
112
118
  }
119
+ function resolveNotifications({ branchConfig, enableNotifications, globalNotifications, repoConfig, }) {
120
+ if (branchConfig.notifications?.length) {
121
+ return resolveDeployNotifications(branchConfig.notifications, globalNotifications);
122
+ }
123
+ const notificationsEnabled = repoConfig.enableNotifications || enableNotifications || branchConfig.enableNotifications;
124
+ if (notificationsEnabled && globalNotifications?.length) {
125
+ return [...globalNotifications];
126
+ }
127
+ return [];
128
+ }
129
+ function resolveDeployNotifications(deployNotifications, globalNotifications) {
130
+ return deployNotifications.map((deployNotification) => {
131
+ if (deployNotification.webhookUrl) {
132
+ return deployNotification;
133
+ }
134
+ const globalWebhookUrl = globalNotifications?.[0]?.webhookUrl;
135
+ if (!globalWebhookUrl) {
136
+ throw new KnownError(`Deploy notification for target '${deployNotification.target}' is missing webhookUrl and no top-level notification was found to fall back on.`);
137
+ }
138
+ return {
139
+ ...deployNotification,
140
+ webhookUrl: globalWebhookUrl,
141
+ };
142
+ });
143
+ }
@@ -27,7 +27,10 @@ function chunkSectionLines(lines) {
27
27
  const latestSection = sectionLines.slice(-1)[0];
28
28
  const currentSection = latestSection && latestSection.length + line.length < slackMaxSectionLength
29
29
  ? latestSection
30
- : pushAndGet(sectionLines, { length: 0, lines: [] });
30
+ : pushAndGet(sectionLines, {
31
+ length: 0,
32
+ lines: [],
33
+ });
31
34
  currentSection.length += line.length;
32
35
  currentSection.lines.push(line);
33
36
  });
@@ -107,7 +110,10 @@ export async function sendNotificationToSlack({ deployResult: { deployCommits: {
107
110
  ];
108
111
  await sendSlackMessage(notification, {
109
112
  attachments: [],
110
- channel: addPrefix({ value: notification.channelName, prefix: '#' }),
113
+ channel: addPrefix({
114
+ value: notification.channelName,
115
+ prefix: '#',
116
+ }),
111
117
  blocks,
112
118
  });
113
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploy-vir",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Deploy a branch to another branch with notifications.",
5
5
  "keywords": [
6
6
  "deploy",
@@ -41,51 +41,51 @@
41
41
  "test:update": "npm run test update"
42
42
  },
43
43
  "dependencies": {
44
- "@augment-vir/assert": "^31.61.0",
45
- "@augment-vir/common": "^31.61.0",
46
- "@augment-vir/node": "^31.61.0",
47
- "@inquirer/prompts": "^8.2.1",
48
- "@slack/web-api": "^7.14.1",
44
+ "@augment-vir/assert": "^31.68.2",
45
+ "@augment-vir/common": "^31.68.2",
46
+ "@augment-vir/node": "^31.68.2",
47
+ "@inquirer/prompts": "^8.3.2",
48
+ "@slack/web-api": "^7.15.0",
49
49
  "cli-vir": "^1.0.3",
50
- "simple-git": "^3.31.1",
50
+ "simple-git": "^3.33.0",
51
51
  "tsx": "^4.21.0",
52
52
  "url-vir": "^2.1.7"
53
53
  },
54
54
  "devDependencies": {
55
- "@eslint/eslintrc": "^3.3.3",
55
+ "@eslint/eslintrc": "^3.3.5",
56
56
  "@eslint/js": "^9.39.2",
57
- "@stylistic/eslint-plugin": "^5.8.0",
57
+ "@stylistic/eslint-plugin": "^5.10.0",
58
58
  "@stylistic/eslint-plugin-ts": "^4.4.1",
59
- "@types/node": "^25.2.3",
60
- "@typescript-eslint/eslint-plugin": "^8.56.0",
61
- "cspell": "^9.6.4",
62
- "dependency-cruiser": "^17.3.8",
63
- "esbuild": "^0.27.3",
59
+ "@types/node": "^25.5.0",
60
+ "@typescript-eslint/eslint-plugin": "^8.57.1",
61
+ "cspell": "^9.7.0",
62
+ "dependency-cruiser": "^17.3.9",
63
+ "esbuild": "^0.27.4",
64
64
  "eslint": "^9.39.2",
65
65
  "eslint-config-prettier": "^10.1.8",
66
- "eslint-plugin-jsdoc": "^62.5.0",
66
+ "eslint-plugin-jsdoc": "^62.8.0",
67
67
  "eslint-plugin-monorepo-cop": "^1.0.2",
68
- "eslint-plugin-playwright": "^2.5.1",
68
+ "eslint-plugin-playwright": "^2.9.0",
69
69
  "eslint-plugin-prettier": "^5.5.5",
70
70
  "eslint-plugin-require-extensions": "^0.1.3",
71
- "eslint-plugin-sonarjs": "^3.0.6",
71
+ "eslint-plugin-sonarjs": "^4.0.2",
72
72
  "eslint-plugin-unicorn": "^63.0.0",
73
73
  "istanbul-smart-text-reporter": "^1.1.5",
74
74
  "markdown-code-example-inserter": "^3.0.3",
75
- "npm-check-updates": "^19.3.2",
75
+ "npm-check-updates": "^19.6.5",
76
76
  "prettier": "~3.3.3",
77
77
  "prettier-plugin-interpolated-html-tags": "^2.0.1",
78
78
  "prettier-plugin-jsdoc": "^1.8.0",
79
- "prettier-plugin-multiline-arrays": "^4.1.4",
79
+ "prettier-plugin-multiline-arrays": "^4.1.5",
80
80
  "prettier-plugin-organize-imports": "^4.3.0",
81
- "prettier-plugin-packagejson": "^3.0.0",
81
+ "prettier-plugin-packagejson": "^3.0.2",
82
82
  "prettier-plugin-sort-json": "^4.2.0",
83
83
  "prettier-plugin-toml": "^2.0.6",
84
84
  "runstorm": "^1.0.0",
85
85
  "typedoc": "^0.28.17",
86
86
  "typescript": "^5.9.3",
87
- "typescript-eslint": "^8.56.0",
88
- "virmator": "^14.5.2"
87
+ "typescript-eslint": "^8.57.1",
88
+ "virmator": "^14.9.1"
89
89
  },
90
90
  "engines": {
91
91
  "node": ">=22"
package/src/config.ts CHANGED
@@ -57,6 +57,16 @@ export type SlackNotificationConfig = {
57
57
  // eslint-disable-next-line sonarjs/redundant-type-aliases
58
58
  export type NotificationConfig = SlackNotificationConfig;
59
59
 
60
+ /**
61
+ * A deploy-level notification override. Same as {@link NotificationConfig} but `webhookUrl` is
62
+ * optional — when omitted, the first matching top-level notification's `webhookUrl` is used.
63
+ *
64
+ * @category Config
65
+ */
66
+ export type DeployNotificationConfig = Omit<SlackNotificationConfig, 'webhookUrl'> & {
67
+ webhookUrl?: string | undefined;
68
+ };
69
+
60
70
  /**
61
71
  * A repo config for deploying.
62
72
  *
@@ -102,6 +112,14 @@ export type DeployVirBranchConfig = {
102
112
  toBranch: string;
103
113
  enableNotifications?: boolean | undefined;
104
114
  }[];
115
+ enableNotifications?: boolean | undefined;
116
+ /**
117
+ * Deploy-level notification overrides. When set, these are used instead of the top-level
118
+ * notifications. If a notification here omits `webhookUrl`, the first top-level notification
119
+ * with the same `target` provides it. Setting this also implicitly enables notifications for
120
+ * this deploy.
121
+ */
122
+ notifications?: DeployNotificationConfig[] | undefined;
105
123
  };
106
124
 
107
125
  /**
package/src/git.ts CHANGED
@@ -3,6 +3,7 @@ import {awaitedBlockingMap, log, logColors} from '@augment-vir/common';
3
3
  import {confirm} from '@inquirer/prompts';
4
4
  import {type DefaultLogFields, type ListLogLine, type SimpleGit} from 'simple-git';
5
5
  import {
6
+ type DeployNotificationConfig,
6
7
  type DeployVirBranchConfig,
7
8
  type DeployVirRepoConfig,
8
9
  type NotificationConfig,
@@ -208,8 +209,15 @@ export async function pushDeploy(
208
209
  },
209
210
  };
210
211
 
211
- if (notifications?.length && (repoConfig.enableNotifications || enableNotifications)) {
212
- await sendNotifications(notifications, {
212
+ const resolvedNotifications = resolveNotifications({
213
+ branchConfig,
214
+ enableNotifications,
215
+ globalNotifications: notifications,
216
+ repoConfig,
217
+ });
218
+
219
+ if (resolvedNotifications.length) {
220
+ await sendNotifications(resolvedNotifications, {
213
221
  branchConfig,
214
222
  deployResult,
215
223
  repoConfig,
@@ -220,3 +228,52 @@ export async function pushDeploy(
220
228
  },
221
229
  );
222
230
  }
231
+
232
+ function resolveNotifications({
233
+ branchConfig,
234
+ enableNotifications,
235
+ globalNotifications,
236
+ repoConfig,
237
+ }: Readonly<{
238
+ branchConfig: Readonly<DeployVirBranchConfig>;
239
+ enableNotifications: boolean | undefined;
240
+ globalNotifications: ReadonlyArray<Readonly<NotificationConfig>> | undefined;
241
+ repoConfig: Readonly<DeployVirRepoConfig>;
242
+ }>): NotificationConfig[] {
243
+ if (branchConfig.notifications?.length) {
244
+ return resolveDeployNotifications(branchConfig.notifications, globalNotifications);
245
+ }
246
+
247
+ const notificationsEnabled =
248
+ repoConfig.enableNotifications || enableNotifications || branchConfig.enableNotifications;
249
+
250
+ if (notificationsEnabled && globalNotifications?.length) {
251
+ return [...globalNotifications];
252
+ }
253
+
254
+ return [];
255
+ }
256
+
257
+ function resolveDeployNotifications(
258
+ deployNotifications: ReadonlyArray<Readonly<DeployNotificationConfig>>,
259
+ globalNotifications: ReadonlyArray<Readonly<NotificationConfig>> | undefined,
260
+ ): NotificationConfig[] {
261
+ return deployNotifications.map((deployNotification): NotificationConfig => {
262
+ if (deployNotification.webhookUrl) {
263
+ return deployNotification as NotificationConfig;
264
+ }
265
+
266
+ const globalWebhookUrl = globalNotifications?.[0]?.webhookUrl;
267
+
268
+ if (!globalWebhookUrl) {
269
+ throw new KnownError(
270
+ `Deploy notification for target '${deployNotification.target}' is missing webhookUrl and no top-level notification was found to fall back on.`,
271
+ );
272
+ }
273
+
274
+ return {
275
+ ...deployNotification,
276
+ webhookUrl: globalWebhookUrl,
277
+ };
278
+ });
279
+ }
@@ -39,7 +39,10 @@ function chunkSectionLines(lines: ReadonlyArray<string>): string[][] {
39
39
  const currentSection =
40
40
  latestSection && latestSection.length + line.length < slackMaxSectionLength
41
41
  ? latestSection
42
- : pushAndGet(sectionLines, {length: 0, lines: []});
42
+ : pushAndGet(sectionLines, {
43
+ length: 0,
44
+ lines: [],
45
+ });
43
46
  currentSection.length += line.length;
44
47
  currentSection.lines.push(line);
45
48
  });
@@ -139,7 +142,10 @@ export async function sendNotificationToSlack({
139
142
 
140
143
  await sendSlackMessage(notification, {
141
144
  attachments: [],
142
- channel: addPrefix({value: notification.channelName, prefix: '#'}),
145
+ channel: addPrefix({
146
+ value: notification.channelName,
147
+ prefix: '#',
148
+ }),
143
149
  blocks,
144
150
  });
145
151
  }