deploy-vir 3.5.0 → 3.6.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/commit.d.ts +5 -5
- package/dist/commit.js +35 -41
- package/package.json +22 -20
- package/src/commit.ts +46 -49
package/dist/commit.d.ts
CHANGED
|
@@ -7,11 +7,11 @@ import { type DefaultLogFields, type ListLogLine } from 'simple-git';
|
|
|
7
7
|
*/
|
|
8
8
|
export type Commit = DefaultLogFields & ListLogLine;
|
|
9
9
|
/**
|
|
10
|
-
* Extract the
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Extract the owning person's name for a commit: the first assignee on the commit's pull request,
|
|
11
|
+
* or that pull request's author (requires the `gh` CLI). Whoever merged a pull request is often not
|
|
12
|
+
* whoever owns the change, so the commit's own author is only used when no pull request can be
|
|
13
|
+
* read.
|
|
14
14
|
*
|
|
15
15
|
* @category Internal
|
|
16
16
|
*/
|
|
17
|
-
export declare function getCommitAuthorName(commit: Readonly<Pick<Commit, 'author_name'
|
|
17
|
+
export declare function getCommitAuthorName(commit: Readonly<Pick<Commit, 'author_name'> & PartialWithUndefined<Pick<Commit, 'hash' | 'message'>>>): Promise<string>;
|
package/dist/commit.js
CHANGED
|
@@ -1,75 +1,69 @@
|
|
|
1
1
|
import { check } from '@augment-vir/assert';
|
|
2
2
|
import { extractErrorMessage, log } from '@augment-vir/common';
|
|
3
3
|
import { runShellCommand } from '@augment-vir/node';
|
|
4
|
-
const coAuthorRegExp = /^co[- \t]*authored[- \t]*by:(.*)$/gim;
|
|
5
4
|
const pullRequestNumberRegExp = /#(\d+)/;
|
|
6
|
-
const botNameSuffix = '[bot]';
|
|
7
5
|
/**
|
|
8
6
|
* Each `gh` lookup is a network request and each commit's author name gets read more than once (CLI
|
|
9
7
|
* output and notifications), so results are cached by their lookup key.
|
|
10
8
|
*/
|
|
11
|
-
const
|
|
9
|
+
const pullRequestAuthorCache = new Map();
|
|
12
10
|
/**
|
|
13
|
-
* Extract the
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
11
|
+
* Extract the owning person's name for a commit: the first assignee on the commit's pull request,
|
|
12
|
+
* or that pull request's author (requires the `gh` CLI). Whoever merged a pull request is often not
|
|
13
|
+
* whoever owns the change, so the commit's own author is only used when no pull request can be
|
|
14
|
+
* read.
|
|
17
15
|
*
|
|
18
16
|
* @category Internal
|
|
19
17
|
*/
|
|
20
18
|
export async function getCommitAuthorName(commit) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.map((match) => (match[1] || '').split('<')[0]?.trim())
|
|
27
|
-
.find((name) => name && !name.endsWith(botNameSuffix));
|
|
28
|
-
return coAuthorName || (await getPullRequestAssigneeName(commit)) || commit.author_name;
|
|
19
|
+
const pullRequestAuthorName = await getPullRequestAuthorName({
|
|
20
|
+
pullRequestNumber: commit.message?.match(pullRequestNumberRegExp)?.[1],
|
|
21
|
+
hash: commit.hash,
|
|
22
|
+
});
|
|
23
|
+
return pullRequestAuthorName || commit.author_name;
|
|
29
24
|
}
|
|
30
|
-
function
|
|
31
|
-
const
|
|
32
|
-
const cacheKey = pullRequestNumber || commit.hash;
|
|
25
|
+
function getPullRequestAuthorName({ pullRequestNumber, hash, }) {
|
|
26
|
+
const cacheKey = pullRequestNumber || hash;
|
|
33
27
|
if (!cacheKey) {
|
|
34
28
|
return Promise.resolve(undefined);
|
|
35
29
|
}
|
|
36
|
-
const cached =
|
|
30
|
+
const cached = pullRequestAuthorCache.get(cacheKey);
|
|
37
31
|
if (cached) {
|
|
38
32
|
return cached;
|
|
39
33
|
}
|
|
40
|
-
const pending =
|
|
41
|
-
? `gh pr view ${pullRequestNumber} --json assignees`
|
|
42
|
-
: `gh pr list --search ${cacheKey} --state merged --limit 1 --json assignees`);
|
|
43
|
-
|
|
34
|
+
const pending = fetchPullRequestAuthorName(pullRequestNumber
|
|
35
|
+
? `gh pr view ${pullRequestNumber} --json assignees,author`
|
|
36
|
+
: `gh pr list --search ${cacheKey} --state merged --limit 1 --json assignees,author`);
|
|
37
|
+
pullRequestAuthorCache.set(cacheKey, pending);
|
|
44
38
|
return pending;
|
|
45
39
|
}
|
|
46
|
-
async function
|
|
40
|
+
async function fetchPullRequestAuthorName(command) {
|
|
47
41
|
try {
|
|
48
42
|
const output = await runShellCommand(command, {
|
|
49
43
|
rejectOnError: true,
|
|
50
44
|
});
|
|
51
|
-
return
|
|
45
|
+
return extractPullRequestAuthorName(JSON.parse(output.stdout));
|
|
52
46
|
}
|
|
53
47
|
catch (error) {
|
|
54
|
-
log.faint(`Failed to read a pull request
|
|
48
|
+
log.faint(`Failed to read a pull request author: ${extractErrorMessage(error)}`);
|
|
55
49
|
return undefined;
|
|
56
50
|
}
|
|
57
51
|
}
|
|
58
|
-
function
|
|
52
|
+
function extractPullRequestAuthorName(parsedJson) {
|
|
59
53
|
/** `gh pr list` outputs an array, `gh pr view` outputs a single pull request. */
|
|
60
54
|
const pullRequest = check.isArray(parsedJson) ? parsedJson[0] : parsedJson;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
55
|
+
const assignees = check.hasKey(pullRequest, 'assignees') && check.isArray(pullRequest.assignees)
|
|
56
|
+
? pullRequest.assignees
|
|
57
|
+
: [];
|
|
58
|
+
return [
|
|
59
|
+
...assignees,
|
|
60
|
+
check.hasKey(pullRequest, 'author') ? pullRequest.author : undefined,
|
|
61
|
+
]
|
|
62
|
+
.map(extractUserName)
|
|
63
|
+
.find((name) => name);
|
|
64
|
+
}
|
|
65
|
+
function extractUserName(user) {
|
|
66
|
+
const name = check.hasKey(user, 'name') && check.isString(user.name) ? user.name : '';
|
|
67
|
+
const login = check.hasKey(user, 'login') && check.isString(user.login) ? user.login : '';
|
|
68
|
+
return name || login || undefined;
|
|
75
69
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deploy-vir",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "Deploy a branch to another branch with notifications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deploy",
|
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
"main": "dist/index.js",
|
|
26
26
|
"module": "dist/index.js",
|
|
27
27
|
"types": "dist/index.d.ts",
|
|
28
|
-
"bin":
|
|
28
|
+
"bin": {
|
|
29
|
+
"deploy-vir": "bin.js"
|
|
30
|
+
},
|
|
29
31
|
"scripts": {
|
|
30
32
|
"compile": "virmator compile",
|
|
31
33
|
"docs": "virmator docs",
|
|
@@ -42,39 +44,39 @@
|
|
|
42
44
|
"test:update": "npm run test update"
|
|
43
45
|
},
|
|
44
46
|
"dependencies": {
|
|
45
|
-
"@augment-vir/assert": "^32.2.
|
|
46
|
-
"@augment-vir/common": "^32.2.
|
|
47
|
-
"@augment-vir/node": "^32.2.
|
|
48
|
-
"@inquirer/prompts": "^8.
|
|
49
|
-
"@slack/web-api": "^8.
|
|
47
|
+
"@augment-vir/assert": "^32.2.4",
|
|
48
|
+
"@augment-vir/common": "^32.2.4",
|
|
49
|
+
"@augment-vir/node": "^32.2.4",
|
|
50
|
+
"@inquirer/prompts": "^8.7.0",
|
|
51
|
+
"@slack/web-api": "^8.1.1",
|
|
50
52
|
"cli-vir": "^1.0.5",
|
|
51
53
|
"simple-git": "^3.36.0",
|
|
52
|
-
"tsx": "^4.23.
|
|
53
|
-
"url-vir": "^2.2.
|
|
54
|
+
"tsx": "^4.23.13",
|
|
55
|
+
"url-vir": "^2.2.2"
|
|
54
56
|
},
|
|
55
57
|
"devDependencies": {
|
|
56
|
-
"@augment-vir/test": "^32.2.
|
|
58
|
+
"@augment-vir/test": "^32.2.4",
|
|
57
59
|
"@eslint/eslintrc": "^3.3.6",
|
|
58
60
|
"@eslint/js": "^10.0.1",
|
|
59
61
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
60
62
|
"@stylistic/eslint-plugin-ts": "^4.4.1",
|
|
61
|
-
"@types/node": "^26.
|
|
62
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
63
|
-
"cspell": "^10.
|
|
64
|
-
"dependency-cruiser": "^18.
|
|
63
|
+
"@types/node": "^26.4.0",
|
|
64
|
+
"@typescript-eslint/eslint-plugin": "^8.68.0",
|
|
65
|
+
"cspell": "^10.1.1",
|
|
66
|
+
"dependency-cruiser": "^18.2.0",
|
|
65
67
|
"esbuild": "^0.28.2",
|
|
66
|
-
"eslint": "^10.
|
|
68
|
+
"eslint": "^10.9.1",
|
|
67
69
|
"eslint-config-prettier": "^10.1.8",
|
|
68
|
-
"eslint-plugin-jsdoc": "^64.
|
|
70
|
+
"eslint-plugin-jsdoc": "^64.3.2",
|
|
69
71
|
"eslint-plugin-monorepo-cop": "^1.0.2",
|
|
70
72
|
"eslint-plugin-playwright": "^2.11.0",
|
|
71
73
|
"eslint-plugin-prettier": "^5.5.6",
|
|
72
74
|
"eslint-plugin-require-extensions": "^0.1.3",
|
|
73
75
|
"eslint-plugin-sonarjs": "^4.2.0",
|
|
74
|
-
"eslint-plugin-unicorn": "^
|
|
76
|
+
"eslint-plugin-unicorn": "^74.0.0",
|
|
75
77
|
"istanbul-smart-text-reporter": "^1.1.5",
|
|
76
78
|
"markdown-code-example-inserter": "^3.0.6",
|
|
77
|
-
"npm-check-updates": "^23.0
|
|
79
|
+
"npm-check-updates": "^23.1.0",
|
|
78
80
|
"prettier": "~3.3.3",
|
|
79
81
|
"prettier-plugin-interpolated-html-tags": "^2.0.2",
|
|
80
82
|
"prettier-plugin-jsdoc": "^1.8.1",
|
|
@@ -86,8 +88,8 @@
|
|
|
86
88
|
"runstorm": "^1.0.6",
|
|
87
89
|
"typedoc": "^0.28.20",
|
|
88
90
|
"typescript": "^6.0.3",
|
|
89
|
-
"typescript-eslint": "^8.
|
|
90
|
-
"virmator": "^14.
|
|
91
|
+
"typescript-eslint": "^8.68.0",
|
|
92
|
+
"virmator": "^14.33.0"
|
|
91
93
|
},
|
|
92
94
|
"engines": {
|
|
93
95
|
"node": ">=22"
|
package/src/commit.ts
CHANGED
|
@@ -10,101 +10,98 @@ import {type DefaultLogFields, type ListLogLine} from 'simple-git';
|
|
|
10
10
|
*/
|
|
11
11
|
export type Commit = DefaultLogFields & ListLogLine;
|
|
12
12
|
|
|
13
|
-
const coAuthorRegExp = /^co[- \t]*authored[- \t]*by:(.*)$/gim;
|
|
14
13
|
const pullRequestNumberRegExp = /#(\d+)/;
|
|
15
|
-
const botNameSuffix = '[bot]';
|
|
16
14
|
|
|
17
15
|
/**
|
|
18
16
|
* Each `gh` lookup is a network request and each commit's author name gets read more than once (CLI
|
|
19
17
|
* output and notifications), so results are cached by their lookup key.
|
|
20
18
|
*/
|
|
21
|
-
const
|
|
19
|
+
const pullRequestAuthorCache = new Map<string, Promise<string | undefined>>();
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
|
-
* Extract the
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
* Extract the owning person's name for a commit: the first assignee on the commit's pull request,
|
|
23
|
+
* or that pull request's author (requires the `gh` CLI). Whoever merged a pull request is often not
|
|
24
|
+
* whoever owns the change, so the commit's own author is only used when no pull request can be
|
|
25
|
+
* read.
|
|
28
26
|
*
|
|
29
27
|
* @category Internal
|
|
30
28
|
*/
|
|
31
29
|
export async function getCommitAuthorName(
|
|
32
30
|
commit: Readonly<
|
|
33
|
-
Pick<Commit, 'author_name' | '
|
|
34
|
-
PartialWithUndefined<Pick<Commit, 'hash' | 'message'>>
|
|
31
|
+
Pick<Commit, 'author_name'> & PartialWithUndefined<Pick<Commit, 'hash' | 'message'>>
|
|
35
32
|
>,
|
|
36
33
|
): Promise<string> {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const coAuthorName = Array.from(commit.body.matchAll(coAuthorRegExp))
|
|
42
|
-
/** Strip the trailing `<email>` from each trailer. */
|
|
43
|
-
.map((match) => (match[1] || '').split('<')[0]?.trim())
|
|
44
|
-
.find((name) => name && !name.endsWith(botNameSuffix));
|
|
34
|
+
const pullRequestAuthorName = await getPullRequestAuthorName({
|
|
35
|
+
pullRequestNumber: commit.message?.match(pullRequestNumberRegExp)?.[1],
|
|
36
|
+
hash: commit.hash,
|
|
37
|
+
});
|
|
45
38
|
|
|
46
|
-
return
|
|
39
|
+
return pullRequestAuthorName || commit.author_name;
|
|
47
40
|
}
|
|
48
41
|
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
function getPullRequestAuthorName({
|
|
43
|
+
pullRequestNumber,
|
|
44
|
+
hash,
|
|
45
|
+
}: Readonly<
|
|
46
|
+
PartialWithUndefined<{
|
|
47
|
+
pullRequestNumber: string;
|
|
48
|
+
hash: string;
|
|
49
|
+
}>
|
|
50
|
+
>): Promise<string | undefined> {
|
|
51
|
+
const cacheKey = pullRequestNumber || hash;
|
|
54
52
|
|
|
55
53
|
if (!cacheKey) {
|
|
56
54
|
return Promise.resolve(undefined);
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
const cached =
|
|
57
|
+
const cached = pullRequestAuthorCache.get(cacheKey);
|
|
60
58
|
|
|
61
59
|
if (cached) {
|
|
62
60
|
return cached;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
const pending =
|
|
63
|
+
const pending = fetchPullRequestAuthorName(
|
|
66
64
|
pullRequestNumber
|
|
67
|
-
? `gh pr view ${pullRequestNumber} --json assignees`
|
|
68
|
-
: `gh pr list --search ${cacheKey} --state merged --limit 1 --json assignees`,
|
|
65
|
+
? `gh pr view ${pullRequestNumber} --json assignees,author`
|
|
66
|
+
: `gh pr list --search ${cacheKey} --state merged --limit 1 --json assignees,author`,
|
|
69
67
|
);
|
|
70
|
-
|
|
68
|
+
pullRequestAuthorCache.set(cacheKey, pending);
|
|
71
69
|
|
|
72
70
|
return pending;
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
async function
|
|
73
|
+
async function fetchPullRequestAuthorName(command: string): Promise<string | undefined> {
|
|
76
74
|
try {
|
|
77
75
|
const output = await runShellCommand(command, {
|
|
78
76
|
rejectOnError: true,
|
|
79
77
|
});
|
|
80
78
|
|
|
81
|
-
return
|
|
79
|
+
return extractPullRequestAuthorName(JSON.parse(output.stdout));
|
|
82
80
|
} catch (error) {
|
|
83
|
-
log.faint(`Failed to read a pull request
|
|
81
|
+
log.faint(`Failed to read a pull request author: ${extractErrorMessage(error)}`);
|
|
84
82
|
return undefined;
|
|
85
83
|
}
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
function
|
|
86
|
+
function extractPullRequestAuthorName(parsedJson: unknown): string | undefined {
|
|
89
87
|
/** `gh pr list` outputs an array, `gh pr view` outputs a single pull request. */
|
|
90
88
|
const pullRequest = check.isArray(parsedJson) ? parsedJson[0] : parsedJson;
|
|
89
|
+
const assignees =
|
|
90
|
+
check.hasKey(pullRequest, 'assignees') && check.isArray(pullRequest.assignees)
|
|
91
|
+
? pullRequest.assignees
|
|
92
|
+
: [];
|
|
93
|
+
|
|
94
|
+
return [
|
|
95
|
+
...assignees,
|
|
96
|
+
check.hasKey(pullRequest, 'author') ? pullRequest.author : undefined,
|
|
97
|
+
]
|
|
98
|
+
.map(extractUserName)
|
|
99
|
+
.find((name) => name);
|
|
100
|
+
}
|
|
91
101
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
function extractUserName(user: unknown): string | undefined {
|
|
103
|
+
const name = check.hasKey(user, 'name') && check.isString(user.name) ? user.name : '';
|
|
104
|
+
const login = check.hasKey(user, 'login') && check.isString(user.login) ? user.login : '';
|
|
95
105
|
|
|
96
|
-
return
|
|
97
|
-
.map((assignee) => {
|
|
98
|
-
const name =
|
|
99
|
-
check.hasKey(assignee, 'name') && check.isString(assignee.name)
|
|
100
|
-
? assignee.name
|
|
101
|
-
: '';
|
|
102
|
-
const login =
|
|
103
|
-
check.hasKey(assignee, 'login') && check.isString(assignee.login)
|
|
104
|
-
? assignee.login
|
|
105
|
-
: '';
|
|
106
|
-
|
|
107
|
-
return name || login;
|
|
108
|
-
})
|
|
109
|
-
.find((name) => name && !name.endsWith(botNameSuffix));
|
|
106
|
+
return name || login || undefined;
|
|
110
107
|
}
|