deploy-vir 3.4.0 → 3.4.2

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/LICENSE-MIT CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 electrovir
3
+ Copyright (c) 2026 electrovir
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,15 @@
1
+ import { type DefaultLogFields, type ListLogLine } from 'simple-git';
2
+ /**
3
+ * A git commit.
4
+ *
5
+ * @category Internal
6
+ */
7
+ export type Commit = DefaultLogFields & ListLogLine;
8
+ /**
9
+ * Extract the human author name for a commit. Commits authored by a bot (a name with a `[bot]`
10
+ * suffix, like GitHub's merge queue bot) are attributed to their first non-bot `Co-authored-by:`
11
+ * trailer instead.
12
+ *
13
+ * @category Internal
14
+ */
15
+ export declare function getCommitAuthorName(commit: Readonly<Pick<Commit, 'author_name' | 'body'>>): string;
package/dist/commit.js ADDED
@@ -0,0 +1,19 @@
1
+ const coAuthorRegExp = /^co[- \t]*authored[- \t]*by:(.*)$/gim;
2
+ const botNameSuffix = '[bot]';
3
+ /**
4
+ * Extract the human author name for a commit. Commits authored by a bot (a name with a `[bot]`
5
+ * suffix, like GitHub's merge queue bot) are attributed to their first non-bot `Co-authored-by:`
6
+ * trailer instead.
7
+ *
8
+ * @category Internal
9
+ */
10
+ export function getCommitAuthorName(commit) {
11
+ if (!commit.author_name.endsWith(botNameSuffix)) {
12
+ return commit.author_name;
13
+ }
14
+ const coAuthorName = Array.from(commit.body.matchAll(coAuthorRegExp))
15
+ /** Strip the trailing `<email>` from each trailer. */
16
+ .map((match) => (match[1] || '').split('<')[0]?.trim())
17
+ .find((name) => name && !name.endsWith(botNameSuffix));
18
+ return coAuthorName || commit.author_name;
19
+ }
package/dist/config.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type MaybePromise, type PartialWithUndefined } from '@augment-vir/common';
2
- import { type Commit, type DeployResult } from './git.js';
2
+ import { type Commit } from './commit.js';
3
+ import { type DeployResult } from './git.js';
3
4
  /**
4
5
  * The full deploy-vir config object.
5
6
  *
package/dist/git.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type DefaultLogFields, type ListLogLine, type SimpleGit } from 'simple-git';
1
+ import { type SimpleGit } from 'simple-git';
2
+ import { type Commit } from './commit.js';
2
3
  import { type DeployVirBranchConfig, type DeployVirHooks, type DeployVirRepoConfig, type NotificationConfig } from './config.js';
3
4
  /**
4
5
  * Find the git remote's name, or create a new one.
@@ -6,12 +7,6 @@ import { type DeployVirBranchConfig, type DeployVirHooks, type DeployVirRepoConf
6
7
  * @category Internal
7
8
  */
8
9
  export declare function getGitRemoteName(git: Readonly<SimpleGit>, repoConfig: Readonly<Pick<DeployVirRepoConfig, 'name' | 'gitUrl'>>): Promise<string>;
9
- /**
10
- * A git commit.
11
- *
12
- * @category Internal
13
- */
14
- export type Commit = DefaultLogFields & ListLogLine;
15
10
  /**
16
11
  * All commits involved in a deploy.
17
12
  *
package/dist/git.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { assert, assertWrap, check } from '@augment-vir/assert';
2
2
  import { awaitedBlockingMap, log, logColors } from '@augment-vir/common';
3
3
  import { confirm } from '@inquirer/prompts';
4
+ import { getCommitAuthorName } from './commit.js';
4
5
  import { KnownError } from './known.error.js';
5
6
  import { sendNotifications } from './notifications/send-notifications.js';
6
7
  /**
@@ -31,6 +32,7 @@ export async function getGitRemoteName(git, repoConfig) {
31
32
  *
32
33
  * @category Internal
33
34
  */
35
+ // eslint-disable-next-line @virmator/prefer-params-object
34
36
  export async function pushDeploy(git, branchConfig, repoConfig, remoteName, notifications, bypassConfirmation = false, hooks) {
35
37
  const { deployName, branches } = branchConfig;
36
38
  return await awaitedBlockingMap(branches, async ({ fromBranch, toBranch, enableNotifications }) => {
@@ -64,12 +66,12 @@ export async function pushDeploy(git, branchConfig, repoConfig, remoteName, noti
64
66
  const requiresForcePush = commitsBehind.length > 0;
65
67
  log.info(`\n${requiresForcePush ? 'Only on' : 'Releasing from'} ${logColors.bold}${fromBranch}${logColors.reset}:`);
66
68
  commitsAhead.forEach((commit, index) => {
67
- log.faint(` ${index + 1}. ${commit.hash.slice(0, 7)} (${commit.author_name}) - ${commit.message}`);
69
+ log.faint(` ${index + 1}. ${commit.hash.slice(0, 7)} (${getCommitAuthorName(commit)}) - ${commit.message}`);
68
70
  });
69
71
  if (requiresForcePush) {
70
72
  log.info(`\nOnly on ${logColors.bold}${toBranch}${logColors.reset}:`);
71
73
  commitsBehind.forEach((commit, index) => {
72
- log.faint(` ${index + 1}. ${commit.hash.slice(0, 7)} (${commit.author_name}) - ${commit.message}`);
74
+ log.faint(` ${index + 1}. ${commit.hash.slice(0, 7)} (${getCommitAuthorName(commit)}) - ${commit.message}`);
73
75
  });
74
76
  }
75
77
  const shouldPush = bypassConfirmation ||
@@ -80,7 +82,7 @@ export async function pushDeploy(git, branchConfig, repoConfig, remoteName, noti
80
82
  default: false,
81
83
  }));
82
84
  if (!shouldPush) {
83
- throw new KnownError(`Deploy aborted.`);
85
+ throw new KnownError('Deploy aborted.');
84
86
  }
85
87
  if (requiresForcePush) {
86
88
  await git.push(remoteName, pushString, ['--force']);
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './cli/cli.js';
2
+ export * from './commit.js';
2
3
  export * from './config.js';
3
4
  export * from './git.js';
4
5
  export * from './known.error.js';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './cli/cli.js';
2
+ export * from './commit.js';
2
3
  export * from './config.js';
3
4
  export * from './git.js';
4
5
  export * from './known.error.js';
@@ -1,5 +1,6 @@
1
1
  import { addPrefix, log, setFirstLetterCasing, StringCase } from '@augment-vir/common';
2
2
  import { joinUrlPaths } from 'url-vir';
3
+ import { getCommitAuthorName } from '../commit.js';
3
4
  function truncateString(value, truncateAt = 100) {
4
5
  if (value.length > truncateAt) {
5
6
  return `${value.slice(0, Math.max(0, truncateAt))}...`;
@@ -10,10 +11,10 @@ function truncateString(value, truncateAt = 100) {
10
11
  }
11
12
  function formatCommit(baseCommitUrl, commit) {
12
13
  if (baseCommitUrl) {
13
- return `<${joinUrlPaths(baseCommitUrl, commit.hash)}|${commit.hash.slice(0, 7)}> (${commit.author_name}) ${truncateString(commit.message)}`;
14
+ return `<${joinUrlPaths(baseCommitUrl, commit.hash)}|${commit.hash.slice(0, 7)}> (${getCommitAuthorName(commit)}) ${truncateString(commit.message)}`;
14
15
  }
15
16
  else {
16
- return `${commit.hash.slice(0, 7)} (${commit.author_name}) ${truncateString(commit.message)}`;
17
+ return `${commit.hash.slice(0, 7)} (${getCommitAuthorName(commit)}) ${truncateString(commit.message)}`;
17
18
  }
18
19
  }
19
20
  const slackMaxSectionLength = 2500;
@@ -86,7 +87,7 @@ export async function sendNotificationToSlack({ deployResult: { deployCommits: {
86
87
  type: 'section',
87
88
  text: {
88
89
  type: 'mrkdwn',
89
- text: `\n*Deployed Commits*\n`,
90
+ text: '\n*Deployed Commits*\n',
90
91
  },
91
92
  },
92
93
  ...chunkSectionLines(deployedCommitBulletLines).map((lines) => {
@@ -104,7 +105,7 @@ export async function sendNotificationToSlack({ deployResult: { deployCommits: {
104
105
  type: 'section',
105
106
  text: {
106
107
  type: 'mrkdwn',
107
- text: `\n*Overwritten Commits*\n`,
108
+ text: '\n*Overwritten Commits*\n',
108
109
  },
109
110
  },
110
111
  ...chunkSectionLines(overwrittenCommitBulletLines).map((lines) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploy-vir",
3
- "version": "3.4.0",
3
+ "version": "3.4.2",
4
4
  "description": "Deploy a branch to another branch with notifications.",
5
5
  "keywords": [
6
6
  "deploy",
@@ -15,7 +15,7 @@
15
15
  "type": "git",
16
16
  "url": "git+https://github.com/electrovir/deploy-vir.git"
17
17
  },
18
- "license": "(MIT or CC0 1.0)",
18
+ "license": "(MIT OR CC0-1.0)",
19
19
  "author": {
20
20
  "name": "electrovir",
21
21
  "url": "https://github.com/electrovir"
@@ -32,7 +32,8 @@
32
32
  "format": "virmator format",
33
33
  "lint": "virmator lint fix",
34
34
  "publish": "virmator publish npm run test:all",
35
- "test:all": "npm run compile && runstorm --names spelling,format,docs,deps,lint \"npm run test:spelling\" \"npm run test:format\" \"npm run test:docs\" \"npm run test:deps\" \"npm run test:lint\"",
35
+ "test": "virmator test node",
36
+ "test:all": "npm run compile && runstorm --names spelling,format,docs,deps,lint,test \"npm run test:spelling\" \"npm run test:format\" \"npm run test:docs\" \"npm run test:deps\" \"npm run test:lint\" \"npm test\"",
36
37
  "test:deps": "virmator deps check",
37
38
  "test:docs": "virmator docs check",
38
39
  "test:format": "virmator format check",
@@ -41,51 +42,52 @@
41
42
  "test:update": "npm run test update"
42
43
  },
43
44
  "dependencies": {
44
- "@augment-vir/assert": "^31.68.4",
45
- "@augment-vir/common": "^31.68.4",
46
- "@augment-vir/node": "^31.68.4",
47
- "@inquirer/prompts": "^8.4.2",
48
- "@slack/web-api": "^7.15.1",
49
- "cli-vir": "^1.0.3",
45
+ "@augment-vir/assert": "^32.2.1",
46
+ "@augment-vir/common": "^32.2.1",
47
+ "@augment-vir/node": "^32.2.1",
48
+ "@inquirer/prompts": "^8.5.2",
49
+ "@slack/web-api": "^8.0.0",
50
+ "cli-vir": "^1.0.4",
50
51
  "simple-git": "^3.36.0",
51
- "tsx": "^4.21.0",
52
- "url-vir": "^2.1.8"
52
+ "tsx": "^4.23.1",
53
+ "url-vir": "^2.1.10"
53
54
  },
54
55
  "devDependencies": {
55
- "@eslint/eslintrc": "^3.3.5",
56
- "@eslint/js": "^9.39.2",
56
+ "@augment-vir/test": "^32.2.1",
57
+ "@eslint/eslintrc": "^3.3.6",
58
+ "@eslint/js": "^10.0.1",
57
59
  "@stylistic/eslint-plugin": "^5.10.0",
58
60
  "@stylistic/eslint-plugin-ts": "^4.4.1",
59
- "@types/node": "^25.6.0",
60
- "@typescript-eslint/eslint-plugin": "^8.59.0",
61
- "cspell": "^10.0.0",
62
- "dependency-cruiser": "^17.3.10",
63
- "esbuild": "^0.28.0",
64
- "eslint": "^9.39.2",
61
+ "@types/node": "^26.1.1",
62
+ "@typescript-eslint/eslint-plugin": "^8.65.0",
63
+ "cspell": "^10.0.1",
64
+ "dependency-cruiser": "^18.1.0",
65
+ "esbuild": "^0.28.1",
66
+ "eslint": "^10.8.0",
65
67
  "eslint-config-prettier": "^10.1.8",
66
- "eslint-plugin-jsdoc": "^62.9.0",
68
+ "eslint-plugin-jsdoc": "^63.2.2",
67
69
  "eslint-plugin-monorepo-cop": "^1.0.2",
68
- "eslint-plugin-playwright": "^2.10.2",
69
- "eslint-plugin-prettier": "^5.5.5",
70
+ "eslint-plugin-playwright": "^2.10.5",
71
+ "eslint-plugin-prettier": "^5.5.6",
70
72
  "eslint-plugin-require-extensions": "^0.1.3",
71
- "eslint-plugin-sonarjs": "^4.0.3",
72
- "eslint-plugin-unicorn": "^64.0.0",
73
+ "eslint-plugin-sonarjs": "^4.2.0",
74
+ "eslint-plugin-unicorn": "^72.0.0",
73
75
  "istanbul-smart-text-reporter": "^1.1.5",
74
- "markdown-code-example-inserter": "^3.0.5",
75
- "npm-check-updates": "^21.0.3",
76
+ "markdown-code-example-inserter": "^3.0.6",
77
+ "npm-check-updates": "^22.2.9",
76
78
  "prettier": "~3.3.3",
77
- "prettier-plugin-interpolated-html-tags": "^2.0.1",
78
- "prettier-plugin-jsdoc": "^1.8.0",
79
- "prettier-plugin-multiline-arrays": "^4.1.7",
79
+ "prettier-plugin-interpolated-html-tags": "^2.0.2",
80
+ "prettier-plugin-jsdoc": "^1.8.1",
81
+ "prettier-plugin-multiline-arrays": "^4.1.11",
80
82
  "prettier-plugin-organize-imports": "^4.3.0",
81
83
  "prettier-plugin-packagejson": "^3.0.2",
82
84
  "prettier-plugin-sort-json": "^4.2.0",
83
85
  "prettier-plugin-toml": "^2.0.6",
84
- "runstorm": "^1.0.0",
85
- "typedoc": "^0.28.19",
86
- "typescript": "^5.9.3",
87
- "typescript-eslint": "^8.57.1",
88
- "virmator": "^14.12.2"
86
+ "runstorm": "^1.0.6",
87
+ "typedoc": "^0.28.20",
88
+ "typescript": "^6.0.3",
89
+ "typescript-eslint": "^8.65.0",
90
+ "virmator": "^14.30.1"
89
91
  },
90
92
  "engines": {
91
93
  "node": ">=22"
package/src/commit.ts ADDED
@@ -0,0 +1,33 @@
1
+ import {type DefaultLogFields, type ListLogLine} from 'simple-git';
2
+
3
+ /**
4
+ * A git commit.
5
+ *
6
+ * @category Internal
7
+ */
8
+ export type Commit = DefaultLogFields & ListLogLine;
9
+
10
+ const coAuthorRegExp = /^co[- \t]*authored[- \t]*by:(.*)$/gim;
11
+ const botNameSuffix = '[bot]';
12
+
13
+ /**
14
+ * Extract the human author name for a commit. Commits authored by a bot (a name with a `[bot]`
15
+ * suffix, like GitHub's merge queue bot) are attributed to their first non-bot `Co-authored-by:`
16
+ * trailer instead.
17
+ *
18
+ * @category Internal
19
+ */
20
+ export function getCommitAuthorName(
21
+ commit: Readonly<Pick<Commit, 'author_name' | 'body'>>,
22
+ ): string {
23
+ if (!commit.author_name.endsWith(botNameSuffix)) {
24
+ return commit.author_name;
25
+ }
26
+
27
+ const coAuthorName = Array.from(commit.body.matchAll(coAuthorRegExp))
28
+ /** Strip the trailing `<email>` from each trailer. */
29
+ .map((match) => (match[1] || '').split('<')[0]?.trim())
30
+ .find((name) => name && !name.endsWith(botNameSuffix));
31
+
32
+ return coAuthorName || commit.author_name;
33
+ }
package/src/config.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {type MaybePromise, type PartialWithUndefined} from '@augment-vir/common';
2
- import {type Commit, type DeployResult} from './git.js';
2
+ import {type Commit} from './commit.js';
3
+ import {type DeployResult} from './git.js';
3
4
 
4
5
  /**
5
6
  * The full deploy-vir config object.
package/src/git.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import {assert, assertWrap, check} from '@augment-vir/assert';
2
2
  import {awaitedBlockingMap, log, logColors} from '@augment-vir/common';
3
3
  import {confirm} from '@inquirer/prompts';
4
- import {type DefaultLogFields, type ListLogLine, type SimpleGit} from 'simple-git';
4
+ import {type SimpleGit} from 'simple-git';
5
+ import {getCommitAuthorName, type Commit} from './commit.js';
5
6
  import {
6
7
  type DeployNotificationConfig,
7
8
  type DeployVirBranchConfig,
@@ -52,13 +53,6 @@ export async function getGitRemoteName(
52
53
  return newRemoteName;
53
54
  }
54
55
 
55
- /**
56
- * A git commit.
57
- *
58
- * @category Internal
59
- */
60
- export type Commit = DefaultLogFields & ListLogLine;
61
-
62
56
  /**
63
57
  * All commits involved in a deploy.
64
58
  *
@@ -94,6 +88,7 @@ export type DeployResult = {
94
88
  *
95
89
  * @category Internal
96
90
  */
91
+ // eslint-disable-next-line @virmator/prefer-params-object
97
92
  export async function pushDeploy(
98
93
  git: Readonly<SimpleGit>,
99
94
  branchConfig: Readonly<DeployVirBranchConfig>,
@@ -164,7 +159,7 @@ export async function pushDeploy(
164
159
  );
165
160
  commitsAhead.forEach((commit, index) => {
166
161
  log.faint(
167
- ` ${index + 1}. ${commit.hash.slice(0, 7)} (${commit.author_name}) - ${commit.message}`,
162
+ ` ${index + 1}. ${commit.hash.slice(0, 7)} (${getCommitAuthorName(commit)}) - ${commit.message}`,
168
163
  );
169
164
  });
170
165
 
@@ -172,7 +167,7 @@ export async function pushDeploy(
172
167
  log.info(`\nOnly on ${logColors.bold}${toBranch}${logColors.reset}:`);
173
168
  commitsBehind.forEach((commit, index) => {
174
169
  log.faint(
175
- ` ${index + 1}. ${commit.hash.slice(0, 7)} (${commit.author_name}) - ${commit.message}`,
170
+ ` ${index + 1}. ${commit.hash.slice(0, 7)} (${getCommitAuthorName(commit)}) - ${commit.message}`,
176
171
  );
177
172
  });
178
173
  }
@@ -187,7 +182,7 @@ export async function pushDeploy(
187
182
  }));
188
183
 
189
184
  if (!shouldPush) {
190
- throw new KnownError(`Deploy aborted.`);
185
+ throw new KnownError('Deploy aborted.');
191
186
  }
192
187
 
193
188
  if (requiresForcePush) {
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './cli/cli.js';
2
+ export * from './commit.js';
2
3
  export * from './config.js';
3
4
  export * from './git.js';
4
5
  export * from './known.error.js';
@@ -1,8 +1,8 @@
1
1
  import {addPrefix, log, setFirstLetterCasing, StringCase} from '@augment-vir/common';
2
2
  import {type ChatPostMessageArguments, type KnownBlock} from '@slack/web-api';
3
3
  import {joinUrlPaths} from 'url-vir';
4
+ import {getCommitAuthorName, type Commit} from '../commit.js';
4
5
  import {type SlackNotificationConfig} from '../config.js';
5
- import {type Commit} from '../git.js';
6
6
  import {type NotificationParams} from './notification-params.js';
7
7
 
8
8
  export {type ChatPostMessageArguments} from '@slack/web-api';
@@ -17,9 +17,9 @@ function truncateString(value: string, truncateAt = 100): string {
17
17
 
18
18
  function formatCommit(baseCommitUrl: string | undefined, commit: Readonly<Commit>) {
19
19
  if (baseCommitUrl) {
20
- return `<${joinUrlPaths(baseCommitUrl, commit.hash)}|${commit.hash.slice(0, 7)}> (${commit.author_name}) ${truncateString(commit.message)}`;
20
+ return `<${joinUrlPaths(baseCommitUrl, commit.hash)}|${commit.hash.slice(0, 7)}> (${getCommitAuthorName(commit)}) ${truncateString(commit.message)}`;
21
21
  } else {
22
- return `${commit.hash.slice(0, 7)} (${commit.author_name}) ${truncateString(commit.message)}`;
22
+ return `${commit.hash.slice(0, 7)} (${getCommitAuthorName(commit)}) ${truncateString(commit.message)}`;
23
23
  }
24
24
  }
25
25
 
@@ -117,7 +117,7 @@ export async function sendNotificationToSlack({
117
117
  type: 'section',
118
118
  text: {
119
119
  type: 'mrkdwn',
120
- text: `\n*Deployed Commits*\n`,
120
+ text: '\n*Deployed Commits*\n',
121
121
  },
122
122
  },
123
123
  ...chunkSectionLines(deployedCommitBulletLines).map((lines): KnownBlock => {
@@ -135,7 +135,7 @@ export async function sendNotificationToSlack({
135
135
  type: 'section',
136
136
  text: {
137
137
  type: 'mrkdwn',
138
- text: `\n*Overwritten Commits*\n`,
138
+ text: '\n*Overwritten Commits*\n',
139
139
  },
140
140
  },
141
141