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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/action.yml +53 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +2 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/action-mode.d.ts +1 -0
  8. package/dist/src/action-mode.js +109 -0
  9. package/dist/src/action-mode.js.map +1 -0
  10. package/dist/src/cli.d.ts +2 -0
  11. package/dist/src/cli.js +780 -0
  12. package/dist/src/cli.js.map +1 -0
  13. package/dist/src/config.d.ts +2 -0
  14. package/dist/src/config.js +291 -0
  15. package/dist/src/config.js.map +1 -0
  16. package/dist/src/dependabot.d.ts +1 -0
  17. package/dist/src/dependabot.js +11 -0
  18. package/dist/src/dependabot.js.map +1 -0
  19. package/dist/src/enforcement.d.ts +12 -0
  20. package/dist/src/enforcement.js +238 -0
  21. package/dist/src/enforcement.js.map +1 -0
  22. package/dist/src/github-app.d.ts +6 -0
  23. package/dist/src/github-app.js +4 -0
  24. package/dist/src/github-app.js.map +1 -0
  25. package/dist/src/index.d.ts +2 -0
  26. package/dist/src/index.js +16 -0
  27. package/dist/src/index.js.map +1 -0
  28. package/dist/src/logging.d.ts +8 -0
  29. package/dist/src/logging.js +38 -0
  30. package/dist/src/logging.js.map +1 -0
  31. package/dist/src/multi-repo-scanner.d.ts +69 -0
  32. package/dist/src/multi-repo-scanner.js +121 -0
  33. package/dist/src/multi-repo-scanner.js.map +1 -0
  34. package/dist/src/netrc-auth.d.ts +13 -0
  35. package/dist/src/netrc-auth.js +123 -0
  36. package/dist/src/netrc-auth.js.map +1 -0
  37. package/dist/src/org.d.ts +49 -0
  38. package/dist/src/org.js +162 -0
  39. package/dist/src/org.js.map +1 -0
  40. package/dist/src/pattern-match.d.ts +5 -0
  41. package/dist/src/pattern-match.js +59 -0
  42. package/dist/src/pattern-match.js.map +1 -0
  43. package/dist/src/pinner.d.ts +6 -0
  44. package/dist/src/pinner.js +148 -0
  45. package/dist/src/pinner.js.map +1 -0
  46. package/dist/src/pr.d.ts +87 -0
  47. package/dist/src/pr.js +165 -0
  48. package/dist/src/pr.js.map +1 -0
  49. package/dist/src/report.d.ts +10 -0
  50. package/dist/src/report.js +54 -0
  51. package/dist/src/report.js.map +1 -0
  52. package/dist/src/resolver.d.ts +44 -0
  53. package/dist/src/resolver.js +227 -0
  54. package/dist/src/resolver.js.map +1 -0
  55. package/dist/src/scanner.d.ts +8 -0
  56. package/dist/src/scanner.js +128 -0
  57. package/dist/src/scanner.js.map +1 -0
  58. package/dist/src/types.d.ts +170 -0
  59. package/dist/src/types.js +41 -0
  60. package/dist/src/types.js.map +1 -0
  61. package/dist/src/version.d.ts +1 -0
  62. package/dist/src/version.js +22 -0
  63. package/dist/src/version.js.map +1 -0
  64. package/dist/src/workflow-paths.d.ts +4 -0
  65. package/dist/src/workflow-paths.js +29 -0
  66. package/dist/src/workflow-paths.js.map +1 -0
  67. package/package.json +62 -0
@@ -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":"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"}
@@ -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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"}
@@ -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