action-pinner 0.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/LICENSE +21 -0
- package/README.md +406 -0
- package/action.yml +53 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/src/action-mode.d.ts +1 -0
- package/dist/src/action-mode.js +109 -0
- package/dist/src/action-mode.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +780 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +291 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/dependabot.d.ts +1 -0
- package/dist/src/dependabot.js +11 -0
- package/dist/src/dependabot.js.map +1 -0
- package/dist/src/enforcement.d.ts +12 -0
- package/dist/src/enforcement.js +238 -0
- package/dist/src/enforcement.js.map +1 -0
- package/dist/src/github-app.d.ts +6 -0
- package/dist/src/github-app.js +4 -0
- package/dist/src/github-app.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +16 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logging.d.ts +8 -0
- package/dist/src/logging.js +38 -0
- package/dist/src/logging.js.map +1 -0
- package/dist/src/multi-repo-scanner.d.ts +69 -0
- package/dist/src/multi-repo-scanner.js +121 -0
- package/dist/src/multi-repo-scanner.js.map +1 -0
- package/dist/src/netrc-auth.d.ts +13 -0
- package/dist/src/netrc-auth.js +123 -0
- package/dist/src/netrc-auth.js.map +1 -0
- package/dist/src/org.d.ts +49 -0
- package/dist/src/org.js +162 -0
- package/dist/src/org.js.map +1 -0
- package/dist/src/pattern-match.d.ts +5 -0
- package/dist/src/pattern-match.js +59 -0
- package/dist/src/pattern-match.js.map +1 -0
- package/dist/src/pinner.d.ts +6 -0
- package/dist/src/pinner.js +148 -0
- package/dist/src/pinner.js.map +1 -0
- package/dist/src/pr.d.ts +87 -0
- package/dist/src/pr.js +165 -0
- package/dist/src/pr.js.map +1 -0
- package/dist/src/report.d.ts +10 -0
- package/dist/src/report.js +54 -0
- package/dist/src/report.js.map +1 -0
- package/dist/src/resolver.d.ts +44 -0
- package/dist/src/resolver.js +227 -0
- package/dist/src/resolver.js.map +1 -0
- package/dist/src/scanner.d.ts +8 -0
- package/dist/src/scanner.js +128 -0
- package/dist/src/scanner.js.map +1 -0
- package/dist/src/types.d.ts +170 -0
- package/dist/src/types.js +41 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +22 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/workflow-paths.d.ts +4 -0
- package/dist/src/workflow-paths.js +29 -0
- package/dist/src/workflow-paths.js.map +1 -0
- package/package.json +62 -0
package/dist/src/org.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { Octokit } from "@octokit/rest";
|
|
3
|
+
import { matchesAnyPattern } from "./pattern-match.js";
|
|
4
|
+
import { normalizeGithubApiUrl } from "./resolver.js";
|
|
5
|
+
const repositoryEnumerationCache = new Map();
|
|
6
|
+
export async function listOrgRepositories(options, token, client = createRepositoryEnumerationClient(options.githubApiUrl, token)) {
|
|
7
|
+
const repositories = await listOwnerRepositories({
|
|
8
|
+
target: options.org,
|
|
9
|
+
targetType: "org",
|
|
10
|
+
includePrivate: options.includePrivate,
|
|
11
|
+
includeArchived: options.includeArchived,
|
|
12
|
+
githubApiUrl: options.githubApiUrl
|
|
13
|
+
}, token, client);
|
|
14
|
+
return repositories.map((repository) => repository.fullName);
|
|
15
|
+
}
|
|
16
|
+
export async function listUserRepositories(options, token, client = createRepositoryEnumerationClient(options.githubApiUrl, token)) {
|
|
17
|
+
const repositories = await listOwnerRepositories({
|
|
18
|
+
target: options.user,
|
|
19
|
+
targetType: "user",
|
|
20
|
+
includePrivate: options.includePrivate,
|
|
21
|
+
includeArchived: options.includeArchived,
|
|
22
|
+
githubApiUrl: options.githubApiUrl
|
|
23
|
+
}, token, client);
|
|
24
|
+
return repositories.map((repository) => repository.fullName);
|
|
25
|
+
}
|
|
26
|
+
export async function listOwnerRepositories(options, token, client = createRepositoryEnumerationClient(options.githubApiUrl, token)) {
|
|
27
|
+
const cacheKey = [
|
|
28
|
+
normalizeGithubApiUrl(options.githubApiUrl),
|
|
29
|
+
options.targetType,
|
|
30
|
+
options.target.toLowerCase(),
|
|
31
|
+
options.includePrivate ? "private" : "public",
|
|
32
|
+
options.includeArchived ? "archived" : "active",
|
|
33
|
+
token
|
|
34
|
+
? `auth:${createHash("sha256").update(token).digest("hex").substring(0, 16)}`
|
|
35
|
+
: "anonymous"
|
|
36
|
+
].join("|");
|
|
37
|
+
const cached = repositoryEnumerationCache.get(cacheKey);
|
|
38
|
+
if (cached) {
|
|
39
|
+
return cached.map((repository) => ({ ...repository }));
|
|
40
|
+
}
|
|
41
|
+
const target = options.target.trim();
|
|
42
|
+
const repositories = options.targetType === "user"
|
|
43
|
+
? await listUserRepositoriesFromClient(client, target, options.includePrivate, token)
|
|
44
|
+
: await client.paginate(client.repos.listForOrg, {
|
|
45
|
+
org: target,
|
|
46
|
+
type: options.includePrivate ? "all" : "public",
|
|
47
|
+
per_page: 100
|
|
48
|
+
});
|
|
49
|
+
const normalized = normalizeAndSortRepositoryMetadata(repositories
|
|
50
|
+
.filter((repo) => options.includeArchived || !repo.archived)
|
|
51
|
+
.map((repo) => ({
|
|
52
|
+
fullName: repo.full_name,
|
|
53
|
+
defaultBranch: repo.default_branch,
|
|
54
|
+
archived: repo.archived
|
|
55
|
+
})));
|
|
56
|
+
repositoryEnumerationCache.set(cacheKey, normalized);
|
|
57
|
+
return normalized.map((repository) => ({ ...repository }));
|
|
58
|
+
}
|
|
59
|
+
export function filterRepositories(repositories, options = {}) {
|
|
60
|
+
const normalized = normalizeAndSortRepositories(repositories);
|
|
61
|
+
const includePatterns = options.includePatterns ?? [];
|
|
62
|
+
const excludePatterns = options.excludePatterns ?? [];
|
|
63
|
+
return normalized.filter((repository) => {
|
|
64
|
+
if (includePatterns.length > 0 && !matchesRepositoryPatterns(repository, includePatterns)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
// Exclusion is applied last so deny rules always win deterministically.
|
|
68
|
+
if (excludePatterns.length > 0 && matchesRepositoryPatterns(repository, excludePatterns)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export function filterRepositoryMetadata(repositories, options = {}) {
|
|
75
|
+
const includePatterns = options.includePatterns ?? [];
|
|
76
|
+
const excludePatterns = options.excludePatterns ?? [];
|
|
77
|
+
return normalizeAndSortRepositoryMetadata(repositories).filter((repository) => {
|
|
78
|
+
if (includePatterns.length > 0 &&
|
|
79
|
+
!matchesRepositoryPatterns(repository.fullName, includePatterns)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
if (excludePatterns.length > 0 &&
|
|
83
|
+
matchesRepositoryPatterns(repository.fullName, excludePatterns)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
export function normalizeAndSortRepositories(repositories) {
|
|
90
|
+
const deduped = new Map();
|
|
91
|
+
for (const repository of repositories) {
|
|
92
|
+
const normalized = normalizeRepository(repository);
|
|
93
|
+
deduped.set(normalized.toLowerCase(), normalized);
|
|
94
|
+
}
|
|
95
|
+
return [...deduped.values()].sort((left, right) => left.localeCompare(right, "en", { sensitivity: "base" }));
|
|
96
|
+
}
|
|
97
|
+
function normalizeAndSortRepositoryMetadata(repositories) {
|
|
98
|
+
const deduped = new Map();
|
|
99
|
+
for (const repository of repositories) {
|
|
100
|
+
const normalized = normalizeRepository(repository.fullName);
|
|
101
|
+
deduped.set(normalized.toLowerCase(), {
|
|
102
|
+
...repository,
|
|
103
|
+
fullName: normalized
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return [...deduped.values()].sort((left, right) => left.fullName.localeCompare(right.fullName, "en", { sensitivity: "base" }));
|
|
107
|
+
}
|
|
108
|
+
function normalizeRepository(repository) {
|
|
109
|
+
const normalized = repository.trim();
|
|
110
|
+
const parts = normalized.split("/");
|
|
111
|
+
if (parts.length !== 2 || parts.some((part) => part.length === 0)) {
|
|
112
|
+
throw new Error(`Invalid repository '${repository}'. Expected format is 'owner/repo'.`);
|
|
113
|
+
}
|
|
114
|
+
return `${parts[0]}/${parts[1]}`;
|
|
115
|
+
}
|
|
116
|
+
function matchesRepositoryPatterns(repository, patterns) {
|
|
117
|
+
const [, repoName] = repository.split("/");
|
|
118
|
+
return patterns.some((pattern) => {
|
|
119
|
+
const trimmed = pattern.trim();
|
|
120
|
+
if (!trimmed) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (trimmed.includes("/")) {
|
|
124
|
+
return matchesAnyPattern(repository, [trimmed], { caseInsensitive: true });
|
|
125
|
+
}
|
|
126
|
+
return matchesAnyPattern(repoName, [trimmed], { caseInsensitive: true });
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async function listUserRepositoriesFromClient(client, username, includePrivate, token) {
|
|
130
|
+
if (includePrivate && token) {
|
|
131
|
+
const authenticated = await getAuthenticatedLogin(client);
|
|
132
|
+
if (authenticated && authenticated.localeCompare(username, "en", { sensitivity: "accent" }) === 0) {
|
|
133
|
+
return client
|
|
134
|
+
.paginate(client.repos.listForAuthenticatedUser, {
|
|
135
|
+
visibility: "all",
|
|
136
|
+
affiliation: "owner",
|
|
137
|
+
per_page: 100
|
|
138
|
+
})
|
|
139
|
+
.then((repositories) => repositories.filter((repository) => repository.owner?.login?.localeCompare(username, "en", { sensitivity: "accent" }) === 0));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return client.paginate(client.repos.listForUser, {
|
|
143
|
+
username,
|
|
144
|
+
per_page: 100
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async function getAuthenticatedLogin(client) {
|
|
148
|
+
try {
|
|
149
|
+
const response = await client.users.getAuthenticated();
|
|
150
|
+
return response.data.login;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function createRepositoryEnumerationClient(githubApiUrl, token) {
|
|
157
|
+
return new Octokit({
|
|
158
|
+
auth: token,
|
|
159
|
+
baseUrl: normalizeGithubApiUrl(githubApiUrl)
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=org.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"org.js","sourceRoot":"","sources":["../../src/org.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AA8CtD,MAAM,0BAA0B,GAAG,IAAI,GAAG,EAAgC,CAAC;AAE3E,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAuB,EACvB,KAAc,EACd,SAAsC,iCAAiC,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;IAEpG,MAAM,YAAY,GAAG,MAAM,qBAAqB,CAC9C;QACE,MAAM,EAAE,OAAO,CAAC,GAAG;QACnB,UAAU,EAAE,KAAK;QACjB,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,YAAY,EAAE,OAAO,CAAC,YAAY;KACnC,EACD,KAAK,EACL,MAAM,CACP,CAAC;IAEF,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAuD,EACvD,KAAc,EACd,SAAsC,iCAAiC,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;IAEpG,MAAM,YAAY,GAAG,MAAM,qBAAqB,CAC9C;QACE,MAAM,EAAE,OAAO,CAAC,IAAI;QACpB,UAAU,EAAE,MAAM;QAClB,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,YAAY,EAAE,OAAO,CAAC,YAAY;KACnC,EACD,KAAK,EACL,MAAM,CACP,CAAC;IAEF,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAyB,EACzB,KAAc,EACd,SAAsC,iCAAiC,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC;IAEpG,MAAM,QAAQ,GAAG;QACf,qBAAqB,CAAC,OAAO,CAAC,YAAY,CAAC;QAC3C,OAAO,CAAC,UAAU;QAClB,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;QAC5B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;QAC7C,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ;QAC/C,KAAK;YACH,CAAC,CAAC,QAAQ,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;YAC7E,CAAC,CAAC,WAAW;KAChB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,MAAM,MAAM,GAAG,0BAA0B,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACrC,MAAM,YAAY,GAChB,OAAO,CAAC,UAAU,KAAK,MAAM;QAC3B,CAAC,CAAC,MAAM,8BAA8B,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC;QACrF,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAqB,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE;YACjE,GAAG,EAAE,MAAM;YACX,IAAI,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;YAC/C,QAAQ,EAAE,GAAG;SACd,CAAC,CAAC;IAET,MAAM,UAAU,GAAG,kCAAkC,CACnD,YAAY;SACT,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;SAC3D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACd,QAAQ,EAAE,IAAI,CAAC,SAAS;QACxB,aAAa,EAAE,IAAI,CAAC,cAAc;QAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC,CAAC,CACN,CAAC;IAEF,0BAA0B,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,YAAsB,EACtB,UAGI,EAAE;IAEN,MAAM,UAAU,GAAG,4BAA4B,CAAC,YAAY,CAAC,CAAC;IAC9D,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;IACtD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;IAEtD,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;QACtC,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,CAAC;YAC1F,OAAO,KAAK,CAAC;QACf,CAAC;QAED,wEAAwE;QACxE,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,IAAI,yBAAyB,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,CAAC;YACzF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,YAAkC,EAClC,UAGI,EAAE;IAEN,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;IACtD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;IAEtD,OAAO,kCAAkC,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE;QAC5E,IACE,eAAe,CAAC,MAAM,GAAG,CAAC;YAC1B,CAAC,yBAAyB,CAAC,UAAU,CAAC,QAAQ,EAAE,eAAe,CAAC,EAChE,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IACE,eAAe,CAAC,MAAM,GAAG,CAAC;YAC1B,yBAAyB,CAAC,UAAU,CAAC,QAAQ,EAAE,eAAe,CAAC,EAC/D,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,YAAsB;IACjE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;QACtC,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAChD,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CACzD,CAAC;AACJ,CAAC;AAED,SAAS,kCAAkC,CACzC,YAAkC;IAElC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IACtD,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;QACtC,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC5D,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE;YACpC,GAAG,UAAU;YACb,QAAQ,EAAE,UAAU;SACrB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAChD,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAC3E,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,UAAkB;IAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CACb,uBAAuB,UAAU,qCAAqC,CACvE,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,yBAAyB,CAAC,UAAkB,EAAE,QAAkB;IACvE,MAAM,CAAC,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE3C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,iBAAiB,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,OAAO,iBAAiB,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,8BAA8B,CAC3C,MAAmC,EACnC,QAAgB,EAChB,cAAuB,EACvB,KAAc;IAEd,IAAI,cAAc,IAAI,KAAK,EAAE,CAAC;QAC5B,MAAM,aAAa,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAC1D,IAAI,aAAa,IAAI,aAAa,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YAClG,OAAO,MAAM;iBACV,QAAQ,CAAqB,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE;gBACnE,UAAU,EAAE,KAAK;gBACjB,WAAW,EAAE,OAAO;gBACpB,QAAQ,EAAE,GAAG;aACd,CAAC;iBACD,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CACrB,YAAY,CAAC,MAAM,CACjB,CAAC,UAAU,EAAE,EAAE,CACb,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,CAC1F,CACF,CAAC;QACN,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,QAAQ,CAAqB,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE;QACnE,QAAQ;QACR,QAAQ,EAAE,GAAG;KACd,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,MAAmC;IAEnC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC;QACvD,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,iCAAiC,CACxC,YAAqB,EACrB,KAAc;IAEd,OAAO,IAAI,OAAO,CAAC;QACjB,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,qBAAqB,CAAC,YAAY,CAAC;KAC7C,CAA2C,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export interface PatternMatchOptions {
|
|
2
|
+
caseInsensitive?: boolean;
|
|
3
|
+
}
|
|
4
|
+
export declare function matchesPattern(value: string, pattern: string, options?: PatternMatchOptions): boolean;
|
|
5
|
+
export declare function matchesAnyPattern(value: string, patterns: string[], options?: PatternMatchOptions): boolean;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const REGEX_SPECIAL = /[|\\{}()[\]^$+?.]/g;
|
|
2
|
+
function toRegex(pattern, options = {}) {
|
|
3
|
+
const flags = options.caseInsensitive === false ? "u" : "iu";
|
|
4
|
+
return new RegExp(`^${globToRegex(pattern)}$`, flags);
|
|
5
|
+
}
|
|
6
|
+
function globToRegex(pattern) {
|
|
7
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
8
|
+
let output = "";
|
|
9
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
10
|
+
const char = normalized[index];
|
|
11
|
+
const next = normalized[index + 1];
|
|
12
|
+
if (char === "*") {
|
|
13
|
+
if (next === "*") {
|
|
14
|
+
const nextNext = normalized[index + 2];
|
|
15
|
+
if (nextNext === "/") {
|
|
16
|
+
output += "(?:.*/)?";
|
|
17
|
+
index += 2;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
output += ".*";
|
|
21
|
+
index += 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
output += "[^/]*";
|
|
26
|
+
}
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (char === "?") {
|
|
30
|
+
output += "[^/]";
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (char === "{") {
|
|
34
|
+
const closeIndex = normalized.indexOf("}", index + 1);
|
|
35
|
+
if (closeIndex !== -1) {
|
|
36
|
+
const body = normalized.slice(index + 1, closeIndex);
|
|
37
|
+
const parts = body.split(",").map((part) => escapeRegex(part));
|
|
38
|
+
output += `(?:${parts.join("|")})`;
|
|
39
|
+
index = closeIndex;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
output += escapeRegex(char);
|
|
44
|
+
}
|
|
45
|
+
return output;
|
|
46
|
+
}
|
|
47
|
+
function escapeRegex(value) {
|
|
48
|
+
return value.replace(REGEX_SPECIAL, "\\$&");
|
|
49
|
+
}
|
|
50
|
+
export function matchesPattern(value, pattern, options = {}) {
|
|
51
|
+
return toRegex(pattern, options).test(value);
|
|
52
|
+
}
|
|
53
|
+
export function matchesAnyPattern(value, patterns, options = {}) {
|
|
54
|
+
if (patterns.length === 0) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return patterns.some((pattern) => matchesPattern(value, pattern, options));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=pattern-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern-match.js","sourceRoot":"","sources":["../../src/pattern-match.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ActionReference, FilePatch, PinActionsConfig } from "./types.js";
|
|
2
|
+
import type { ActionResolver } from "./resolver.js";
|
|
3
|
+
export declare function pinReferences(references: ActionReference[], resolver: Pick<ActionResolver, "resolve">, config: PinActionsConfig, dryRun: boolean, options?: {
|
|
4
|
+
continueOnError?: boolean;
|
|
5
|
+
failOnAmbiguous?: boolean;
|
|
6
|
+
}): Promise<FilePatch[]>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { buildResolutionKey } from "./resolver.js";
|
|
3
|
+
export async function pinReferences(references, resolver, config, dryRun, options) {
|
|
4
|
+
const continueOnError = options?.continueOnError ?? false;
|
|
5
|
+
const failOnAmbiguous = options?.failOnAmbiguous ?? false;
|
|
6
|
+
const resolutions = await resolveReferences(references, resolver, {
|
|
7
|
+
continueOnError,
|
|
8
|
+
failOnAmbiguous
|
|
9
|
+
});
|
|
10
|
+
const grouped = groupByFile(references);
|
|
11
|
+
const patches = [];
|
|
12
|
+
// Process files in sorted order for deterministic output
|
|
13
|
+
const sortedFilePaths = Array.from(grouped.keys()).sort();
|
|
14
|
+
for (const filePath of sortedFilePaths) {
|
|
15
|
+
const refs = grouped.get(filePath) ?? [];
|
|
16
|
+
const original = await readFile(filePath, "utf8");
|
|
17
|
+
const eol = original.includes("\r\n") ? "\r\n" : "\n";
|
|
18
|
+
const lines = original.split(/\r?\n/);
|
|
19
|
+
// Process in reverse line order for safe editing, but track updates for later sorting
|
|
20
|
+
const sorted = [...refs].sort((a, b) => b.line - a.line);
|
|
21
|
+
const updatedRefs = [];
|
|
22
|
+
const evidence = [];
|
|
23
|
+
for (const ref of sorted) {
|
|
24
|
+
if (!shouldResolve(ref)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const resolution = resolutions.get(buildResolutionKey(ref));
|
|
28
|
+
if (!resolution) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const versionComment = renderCommentTemplate(config.dependabot.commentFormat, ref, resolution.sha);
|
|
32
|
+
const lineIndex = ref.line - 1;
|
|
33
|
+
const line = lines[lineIndex] ?? "";
|
|
34
|
+
const updatedLine = rewriteUsesLine(line, resolution.sha, {
|
|
35
|
+
addVersionComment: config.dependabot.addVersionComments,
|
|
36
|
+
comment: versionComment
|
|
37
|
+
});
|
|
38
|
+
lines[lineIndex] = updatedLine;
|
|
39
|
+
updatedRefs.push(ref);
|
|
40
|
+
evidence.push({
|
|
41
|
+
filePath: ref.filePath,
|
|
42
|
+
line: ref.line,
|
|
43
|
+
originalRef: ref.raw,
|
|
44
|
+
resolvedSha: resolution.sha,
|
|
45
|
+
sourceRepo: resolution.sourceRepo,
|
|
46
|
+
resolutionMethod: resolution.resolutionMethod,
|
|
47
|
+
resolvedAt: resolution.resolvedAt
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const updatedContent = lines.join(eol);
|
|
51
|
+
if (updatedContent === original) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!dryRun) {
|
|
55
|
+
await writeFile(filePath, updatedContent, "utf8");
|
|
56
|
+
}
|
|
57
|
+
// Sort refs and evidence by line number for deterministic output
|
|
58
|
+
const sortedUpdatedRefs = updatedRefs.sort((a, b) => a.line - b.line);
|
|
59
|
+
const sortedEvidence = evidence.sort((a, b) => a.line - b.line);
|
|
60
|
+
patches.push({
|
|
61
|
+
filePath,
|
|
62
|
+
originalContent: original,
|
|
63
|
+
updatedContent,
|
|
64
|
+
referencesUpdated: sortedUpdatedRefs,
|
|
65
|
+
evidence: sortedEvidence
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Sort patches by file path for deterministic output
|
|
69
|
+
return patches.sort((a, b) => a.filePath.localeCompare(b.filePath));
|
|
70
|
+
}
|
|
71
|
+
async function resolveReferences(references, resolver, options) {
|
|
72
|
+
const continueOnError = options?.continueOnError ?? false;
|
|
73
|
+
const failOnAmbiguous = options?.failOnAmbiguous ?? false;
|
|
74
|
+
const uniqueRefs = new Map();
|
|
75
|
+
for (const reference of references) {
|
|
76
|
+
if (!shouldResolve(reference)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
uniqueRefs.set(buildResolutionKey(reference), reference);
|
|
80
|
+
}
|
|
81
|
+
const resolutions = new Map();
|
|
82
|
+
const promises = [...uniqueRefs.entries()].map(async ([key, reference]) => {
|
|
83
|
+
try {
|
|
84
|
+
const result = await resolver.resolve(reference);
|
|
85
|
+
resolutions.set(key, result);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
// If failOnAmbiguous is set and we have an AmbiguousRefError, re-throw it
|
|
89
|
+
if (failOnAmbiguous && error instanceof Error && error.name === "AmbiguousRefError") {
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
// If continueOnError is not set and we have an UnresolvedRefError, re-throw it
|
|
93
|
+
if (!continueOnError && error instanceof Error && error.name === "UnresolvedRefError") {
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
// If continueOnError is set, log the warning and continue
|
|
97
|
+
if (continueOnError && error instanceof Error) {
|
|
98
|
+
console.warn(`Skipping ref due to error: ${error.message}`);
|
|
99
|
+
}
|
|
100
|
+
else if (!continueOnError) {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
await Promise.all(promises);
|
|
106
|
+
return resolutions;
|
|
107
|
+
}
|
|
108
|
+
function shouldResolve(reference) {
|
|
109
|
+
const ref = reference.ref;
|
|
110
|
+
return (reference.kind === "tag-or-branch" &&
|
|
111
|
+
typeof ref === "string" &&
|
|
112
|
+
!/^[0-9a-f]{40}$/i.test(ref));
|
|
113
|
+
}
|
|
114
|
+
function groupByFile(references) {
|
|
115
|
+
const grouped = new Map();
|
|
116
|
+
for (const reference of references) {
|
|
117
|
+
const existing = grouped.get(reference.filePath);
|
|
118
|
+
if (existing) {
|
|
119
|
+
existing.push(reference);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
grouped.set(reference.filePath, [reference]);
|
|
123
|
+
}
|
|
124
|
+
return grouped;
|
|
125
|
+
}
|
|
126
|
+
function rewriteUsesLine(line, sha, options) {
|
|
127
|
+
const match = line.match(/^(\s*-?\s*uses:\s*)(['"]?)([^'"#\s@]+)@([^'"#\s]+)(\2)(.*)$/);
|
|
128
|
+
if (!match) {
|
|
129
|
+
return line;
|
|
130
|
+
}
|
|
131
|
+
const [, prefix, quote, action, , closingQuote, suffix] = match;
|
|
132
|
+
const renderedComment = options.comment.trim();
|
|
133
|
+
const comment = options.addVersionComment && renderedComment.length > 0
|
|
134
|
+
? ` # ${renderedComment}`
|
|
135
|
+
: "";
|
|
136
|
+
return `${prefix}${quote}${action}@${sha}${closingQuote}${comment}${suffix}`;
|
|
137
|
+
}
|
|
138
|
+
function renderCommentTemplate(template, reference, sha) {
|
|
139
|
+
const shaShort = sha.slice(0, 7);
|
|
140
|
+
return template.replace(/\{(ref|action|sha_short)\}/g, (_match, token) => {
|
|
141
|
+
if (token === "ref")
|
|
142
|
+
return reference.ref ?? "";
|
|
143
|
+
if (token === "action")
|
|
144
|
+
return reference.action;
|
|
145
|
+
return shaShort;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=pinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinner.js","sourceRoot":"","sources":["../../src/pinner.ts"],"names":[],"mappings":"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"}
|
package/dist/src/pr.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type SimpleGit } from "simple-git";
|
|
2
|
+
import type { FilePatch, PinActionsConfig } from "./types.js";
|
|
3
|
+
export interface CreatePrOptions {
|
|
4
|
+
config: PinActionsConfig;
|
|
5
|
+
patches: FilePatch[];
|
|
6
|
+
branchName?: string;
|
|
7
|
+
git?: Pick<SimpleGit, "branch" | "checkoutLocalBranch" | "add" | "commit">;
|
|
8
|
+
}
|
|
9
|
+
export interface PublishPrOptions {
|
|
10
|
+
config: PinActionsConfig;
|
|
11
|
+
patches: FilePatch[];
|
|
12
|
+
branch: string;
|
|
13
|
+
baseBranch: string;
|
|
14
|
+
commitMessage?: string;
|
|
15
|
+
token?: string;
|
|
16
|
+
git?: Pick<SimpleGit, "raw" | "getRemotes">;
|
|
17
|
+
client?: GitHubPrClient;
|
|
18
|
+
repository?: GitHubRepository;
|
|
19
|
+
}
|
|
20
|
+
export interface GitHubRepository {
|
|
21
|
+
owner: string;
|
|
22
|
+
repo: string;
|
|
23
|
+
}
|
|
24
|
+
export interface GitHubPrClient {
|
|
25
|
+
pulls: {
|
|
26
|
+
create: (args: {
|
|
27
|
+
owner: string;
|
|
28
|
+
repo: string;
|
|
29
|
+
title: string;
|
|
30
|
+
body: string;
|
|
31
|
+
head: string;
|
|
32
|
+
base: string;
|
|
33
|
+
}) => Promise<{
|
|
34
|
+
data: {
|
|
35
|
+
number: number;
|
|
36
|
+
html_url: string;
|
|
37
|
+
};
|
|
38
|
+
}>;
|
|
39
|
+
requestReviewers: (args: {
|
|
40
|
+
owner: string;
|
|
41
|
+
repo: string;
|
|
42
|
+
pull_number: number;
|
|
43
|
+
reviewers: string[];
|
|
44
|
+
}) => Promise<unknown>;
|
|
45
|
+
};
|
|
46
|
+
issues: {
|
|
47
|
+
addLabels: (args: {
|
|
48
|
+
owner: string;
|
|
49
|
+
repo: string;
|
|
50
|
+
issue_number: number;
|
|
51
|
+
labels: string[];
|
|
52
|
+
}) => Promise<unknown>;
|
|
53
|
+
addAssignees: (args: {
|
|
54
|
+
owner: string;
|
|
55
|
+
repo: string;
|
|
56
|
+
issue_number: number;
|
|
57
|
+
assignees: string[];
|
|
58
|
+
}) => Promise<unknown>;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export interface CreatedPrResult {
|
|
62
|
+
number: number;
|
|
63
|
+
htmlUrl: string;
|
|
64
|
+
}
|
|
65
|
+
export declare function createPullRequestBranch({ config, patches, branchName, git }: CreatePrOptions): Promise<{
|
|
66
|
+
branch: string;
|
|
67
|
+
baseBranch: string;
|
|
68
|
+
commitMessage: string;
|
|
69
|
+
}>;
|
|
70
|
+
export declare function publishPullRequest({ config, patches, branch, baseBranch, commitMessage, token, git, client, repository }: PublishPrOptions): Promise<CreatedPrResult | null>;
|
|
71
|
+
export declare function buildPrBody(patches: FilePatch[], template?: string, contextOverrides?: Partial<PrTemplateContext>): string;
|
|
72
|
+
export declare function resolveRepositoryInfo(git: Pick<SimpleGit, "getRemotes">): Promise<GitHubRepository>;
|
|
73
|
+
interface PrTemplateContext {
|
|
74
|
+
summary: string;
|
|
75
|
+
fileCount: number;
|
|
76
|
+
referenceCount: number;
|
|
77
|
+
files: string;
|
|
78
|
+
references: string;
|
|
79
|
+
evidence: string;
|
|
80
|
+
branch: string;
|
|
81
|
+
baseBranch: string;
|
|
82
|
+
commitMessage: string;
|
|
83
|
+
toolVersion: string;
|
|
84
|
+
configHash: string;
|
|
85
|
+
runFingerprint: string;
|
|
86
|
+
}
|
|
87
|
+
export {};
|
package/dist/src/pr.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Octokit } from "@octokit/rest";
|
|
2
|
+
import { simpleGit } from "simple-git";
|
|
3
|
+
import { toDisplayPath } from "./workflow-paths.js";
|
|
4
|
+
import { buildRunFingerprint, formatEvidence } from "./report.js";
|
|
5
|
+
import { getToolVersion } from "./version.js";
|
|
6
|
+
export async function createPullRequestBranch({ config, patches, branchName, git = simpleGit() }) {
|
|
7
|
+
const branchInfo = await git.branch();
|
|
8
|
+
const baseBranch = branchInfo.current;
|
|
9
|
+
const branch = branchName ?? `${config.pr.branchPrefix}-${Date.now()}`;
|
|
10
|
+
const commitMessage = "chore: pin GitHub Actions to commit SHAs";
|
|
11
|
+
await git.checkoutLocalBranch(branch);
|
|
12
|
+
await git.add(patches.map((patch) => patch.filePath));
|
|
13
|
+
await git.commit(commitMessage);
|
|
14
|
+
return { branch, baseBranch, commitMessage };
|
|
15
|
+
}
|
|
16
|
+
export async function publishPullRequest({ config, patches, branch, baseBranch, commitMessage = "chore: pin GitHub Actions to commit SHAs", token, git = simpleGit(), client, repository }) {
|
|
17
|
+
if (!config.pr.create) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (!token) {
|
|
21
|
+
throw new Error("A GitHub token is required to create pull requests. " +
|
|
22
|
+
"Set PIN_ACTIONS_TOKEN or use --token, or ensure GITHUB_TOKEN is available.");
|
|
23
|
+
}
|
|
24
|
+
const repo = repository ?? (await resolveRepositoryInfo(git));
|
|
25
|
+
const octokit = client ?? createPullRequestClient(token);
|
|
26
|
+
const toolVersion = await getToolVersion();
|
|
27
|
+
const runFingerprint = buildRunFingerprint(config, toolVersion);
|
|
28
|
+
const body = buildPrBody(patches, config.pr.bodyTemplate, {
|
|
29
|
+
branch,
|
|
30
|
+
baseBranch,
|
|
31
|
+
commitMessage,
|
|
32
|
+
evidence: formatEvidence(patches),
|
|
33
|
+
toolVersion: runFingerprint.toolVersion,
|
|
34
|
+
configHash: runFingerprint.configHash,
|
|
35
|
+
runFingerprint: runFingerprint.fingerprint
|
|
36
|
+
});
|
|
37
|
+
await git.raw(["push", "-u", "origin", branch]);
|
|
38
|
+
const pullRequest = await octokit.pulls.create({
|
|
39
|
+
owner: repo.owner,
|
|
40
|
+
repo: repo.repo,
|
|
41
|
+
title: config.pr.title,
|
|
42
|
+
body,
|
|
43
|
+
head: branch,
|
|
44
|
+
base: baseBranch
|
|
45
|
+
});
|
|
46
|
+
const issueNumber = pullRequest.data.number;
|
|
47
|
+
if (config.pr.labels.length > 0) {
|
|
48
|
+
await octokit.issues.addLabels({
|
|
49
|
+
owner: repo.owner,
|
|
50
|
+
repo: repo.repo,
|
|
51
|
+
issue_number: issueNumber,
|
|
52
|
+
labels: config.pr.labels
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (config.pr.assignees.length > 0) {
|
|
56
|
+
await octokit.issues.addAssignees({
|
|
57
|
+
owner: repo.owner,
|
|
58
|
+
repo: repo.repo,
|
|
59
|
+
issue_number: issueNumber,
|
|
60
|
+
assignees: config.pr.assignees
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (config.pr.reviewers.length > 0) {
|
|
64
|
+
await octokit.pulls.requestReviewers({
|
|
65
|
+
owner: repo.owner,
|
|
66
|
+
repo: repo.repo,
|
|
67
|
+
pull_number: issueNumber,
|
|
68
|
+
reviewers: config.pr.reviewers
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
number: issueNumber,
|
|
73
|
+
htmlUrl: pullRequest.data.html_url
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function buildPrBody(patches, template = DEFAULT_PR_BODY_TEMPLATE, contextOverrides = {}) {
|
|
77
|
+
const context = buildPrTemplateContext(patches, contextOverrides);
|
|
78
|
+
return renderTemplate(template, context).trim();
|
|
79
|
+
}
|
|
80
|
+
export async function resolveRepositoryInfo(git) {
|
|
81
|
+
const remotes = await git.getRemotes(true);
|
|
82
|
+
const origin = remotes.find((remote) => remote.name === "origin");
|
|
83
|
+
const remoteUrl = origin?.refs.fetch ?? origin?.refs.push;
|
|
84
|
+
if (!remoteUrl) {
|
|
85
|
+
throw new Error("Unable to determine the origin remote URL.");
|
|
86
|
+
}
|
|
87
|
+
return parseRepositoryUrl(remoteUrl);
|
|
88
|
+
}
|
|
89
|
+
function buildPrTemplateContext(patches, contextOverrides) {
|
|
90
|
+
const files = patches.map((patch) => `- ${toDisplayPath(patch.filePath)}`).join("\n");
|
|
91
|
+
const references = patches
|
|
92
|
+
.flatMap((patch) => patch.referencesUpdated.map((reference) => `- ${toDisplayPath(reference.filePath)}:${reference.line} ${reference.raw}`))
|
|
93
|
+
.join("\n");
|
|
94
|
+
const context = {
|
|
95
|
+
summary: `Pinned ${countUpdatedReferences(patches)} action reference(s) across ${patches.length} file(s).`,
|
|
96
|
+
fileCount: patches.length,
|
|
97
|
+
referenceCount: countUpdatedReferences(patches),
|
|
98
|
+
files: files || "- (none)",
|
|
99
|
+
references: references || "- (none)",
|
|
100
|
+
evidence: formatEvidence(patches),
|
|
101
|
+
branch: "",
|
|
102
|
+
baseBranch: "",
|
|
103
|
+
commitMessage: "",
|
|
104
|
+
toolVersion: "",
|
|
105
|
+
configHash: "",
|
|
106
|
+
runFingerprint: "",
|
|
107
|
+
...contextOverrides
|
|
108
|
+
};
|
|
109
|
+
return context;
|
|
110
|
+
}
|
|
111
|
+
function renderTemplate(template, context) {
|
|
112
|
+
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
113
|
+
const value = context[key];
|
|
114
|
+
return value === undefined ? match : String(value);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function countUpdatedReferences(patches) {
|
|
118
|
+
return patches.reduce((count, patch) => count + patch.referencesUpdated.length, 0);
|
|
119
|
+
}
|
|
120
|
+
function createPullRequestClient(token) {
|
|
121
|
+
return new Octokit({ auth: token });
|
|
122
|
+
}
|
|
123
|
+
function parseRepositoryUrl(remoteUrl) {
|
|
124
|
+
const sshMatch = remoteUrl.match(/^git@[^:]+:([^/]+)\/(.+?)(?:\.git)?$/i);
|
|
125
|
+
if (sshMatch) {
|
|
126
|
+
return { owner: sshMatch[1], repo: sshMatch[2] };
|
|
127
|
+
}
|
|
128
|
+
const sshUrlMatch = remoteUrl.match(/^ssh:\/\/git@[^/]+\/([^/]+)\/(.+?)(?:\.git)?$/i);
|
|
129
|
+
if (sshUrlMatch) {
|
|
130
|
+
return { owner: sshUrlMatch[1], repo: sshUrlMatch[2] };
|
|
131
|
+
}
|
|
132
|
+
const httpsMatch = remoteUrl.match(/^https?:\/\/[^/]+\/([^/]+)\/(.+?)(?:\.git)?$/i);
|
|
133
|
+
if (httpsMatch) {
|
|
134
|
+
return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
135
|
+
}
|
|
136
|
+
throw new Error(`Unsupported origin remote URL: ${remoteUrl}`);
|
|
137
|
+
}
|
|
138
|
+
const DEFAULT_PR_BODY_TEMPLATE = [
|
|
139
|
+
"## Summary",
|
|
140
|
+
"",
|
|
141
|
+
"{{summary}}",
|
|
142
|
+
"",
|
|
143
|
+
"## Updated workflows",
|
|
144
|
+
"",
|
|
145
|
+
"{{files}}",
|
|
146
|
+
"",
|
|
147
|
+
"## Updated references",
|
|
148
|
+
"",
|
|
149
|
+
"{{references}}",
|
|
150
|
+
"",
|
|
151
|
+
"## Evidence",
|
|
152
|
+
"",
|
|
153
|
+
"{{evidence}}",
|
|
154
|
+
"",
|
|
155
|
+
"## Run fingerprint",
|
|
156
|
+
"",
|
|
157
|
+
"- Tool version: `{{toolVersion}}`",
|
|
158
|
+
"- Config hash: `{{configHash}}`",
|
|
159
|
+
"- Run fingerprint: `{{runFingerprint}}`",
|
|
160
|
+
"",
|
|
161
|
+
"## Branch",
|
|
162
|
+
"",
|
|
163
|
+
"- `{{branch}}`"
|
|
164
|
+
].join("\n");
|
|
165
|
+
//# sourceMappingURL=pr.js.map
|