actions-up 1.14.2 → 1.15.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/cli/anchor-directory-inputs.d.ts +30 -0
- package/dist/cli/anchor-directory-inputs.js +13 -0
- package/dist/cli/index.js +196 -181
- package/dist/cli/normalize-update-mode.js +5 -1
- package/dist/cli/parse-arguments.d.ts +78 -0
- package/dist/cli/parse-arguments.js +81 -0
- package/dist/core/api/check-updates.js +125 -114
- package/dist/core/api/internal-rate-limit-error.d.ts +1 -1
- package/dist/core/api/internal-rate-limit-error.js +3 -3
- package/dist/core/api/make-request.js +1 -2
- package/dist/core/api/resolve-github-token-sync.js +1 -2
- package/dist/core/ast/update/apply-updates.js +4 -3
- package/dist/core/fs/find-repo-root.d.ts +12 -0
- package/dist/core/fs/find-repo-root.js +17 -0
- package/dist/core/interactive/format-version.js +1 -1
- package/dist/core/interactive/prompt-update-selection.js +5 -5
- package/dist/core/scan-github-actions.js +3 -1
- package/dist/core/scan-recursive.js +8 -5
- package/dist/package.js +1 -1
- package/package.json +3 -4
- package/readme.md +10 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for anchoring directory inputs at the repository root.
|
|
3
|
+
*/
|
|
4
|
+
interface AnchorDirectoryInputsOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Raw --dir value(s), or undefined when the flag was not provided.
|
|
7
|
+
*/
|
|
8
|
+
dir?: string[] | string;
|
|
9
|
+
/**
|
|
10
|
+
* Repository root detected by walking up, or null when none was found.
|
|
11
|
+
*/
|
|
12
|
+
root: string | null;
|
|
13
|
+
/**
|
|
14
|
+
* Current working directory the CLI was invoked from.
|
|
15
|
+
*/
|
|
16
|
+
cwd: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Anchors relative scan directories at the detected repository root.
|
|
20
|
+
*
|
|
21
|
+
* When the CLI runs from a nested subdirectory, the default `.github` and any
|
|
22
|
+
* simple relative `--dir` values are resolved against the repository root
|
|
23
|
+
* instead of the current directory. Absolute and parent-relative (`..`) values
|
|
24
|
+
* are intentional and left untouched.
|
|
25
|
+
*
|
|
26
|
+
* @param options - Detected root, current directory, and raw --dir value(s).
|
|
27
|
+
* @returns The directory input(s) to feed into resolveScanDirectories.
|
|
28
|
+
*/
|
|
29
|
+
export declare function anchorDirectoryInputs(options: AnchorDirectoryInputsOptions): undefined | string[] | string;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GITHUB_DIRECTORY as e } from "../core/constants.js";
|
|
2
|
+
import { isAbsolute as t, join as n } from "node:path";
|
|
3
|
+
function r(r) {
|
|
4
|
+
let { root: i, cwd: a, dir: o } = r;
|
|
5
|
+
if (!i || i === a) return o;
|
|
6
|
+
let s = [];
|
|
7
|
+
return Array.isArray(o) ? s.push(...o) : typeof o == "string" && s.push(o), s.length === 0 && (s = ["."]), s.map((r) => {
|
|
8
|
+
if (t(r) || r.startsWith("..")) return r;
|
|
9
|
+
let a = n(i, r);
|
|
10
|
+
return a === i ? n(i, e) : a;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export { r as anchorDirectoryInputs };
|
package/dist/cli/index.js
CHANGED
|
@@ -6,203 +6,218 @@ import { getCompatibleUpdate as i } from "../core/api/get-compatible-update.js";
|
|
|
6
6
|
import { createGitHubClient as a } from "../core/api/create-github-client.js";
|
|
7
7
|
import { resolveScanDirectories as o } from "./resolve-scan-directories.js";
|
|
8
8
|
import { getUpdateLevel as s } from "../core/versions/get-update-level.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
9
|
+
import { anchorDirectoryInputs as c } from "./anchor-directory-inputs.js";
|
|
10
|
+
import { applyUpdates as l } from "../core/ast/update/apply-updates.js";
|
|
11
|
+
import { normalizeUpdateStyle as u } from "./normalize-update-style.js";
|
|
12
|
+
import { printSkippedWarning as d } from "./print-skipped-warning.js";
|
|
13
|
+
import { normalizeUpdateMode as f } from "./normalize-update-mode.js";
|
|
14
|
+
import { validateCliOptions as p } from "./validate-cli-options.js";
|
|
15
|
+
import { shouldIgnore as m } from "../core/ignore/should-ignore.js";
|
|
16
|
+
import { findRepoRoot as h } from "../core/fs/find-repo-root.js";
|
|
17
|
+
import { checkUpdates as g } from "../core/api/check-updates.js";
|
|
18
|
+
import { mergeScanResults as _ } from "./merge-scan-results.js";
|
|
19
|
+
import { printModeWarning as v } from "./print-mode-warning.js";
|
|
20
|
+
import { scanRecursive as y } from "../core/scan-recursive.js";
|
|
21
|
+
import { buildJsonReport as b } from "./build-json-report.js";
|
|
22
|
+
import { parseArguments as x } from "./parse-arguments.js";
|
|
23
|
+
import { scanGitHubActions as S } from "../core/scan-github-actions.js";
|
|
21
24
|
import "../core/index.js";
|
|
22
|
-
import { version as
|
|
23
|
-
import { createSpinner as
|
|
24
|
-
import { resolve as
|
|
25
|
+
import { version as C } from "../package.js";
|
|
26
|
+
import { createSpinner as w } from "nanospinner";
|
|
27
|
+
import { resolve as T } from "node:path";
|
|
25
28
|
import "node:worker_threads";
|
|
26
|
-
import
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
29
|
+
import E from "picocolors";
|
|
30
|
+
function D() {
|
|
31
|
+
let e = x(process.argv.slice(2), C);
|
|
32
|
+
if (e.kind === "help" || e.kind === "version") {
|
|
33
|
+
console.info(e.text);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
e.kind === "error" && (console.error(E.redBright("\nError:"), e.message), process.exit(1)), O(e.options);
|
|
37
|
+
}
|
|
38
|
+
async function O(x) {
|
|
39
|
+
let C = x.json ?? !1, D = x.quiet ?? !1, O = null, k = process.cwd(), A = x.recursive ? null : await h(k), j = o({
|
|
40
|
+
dir: c({
|
|
41
|
+
dir: x.dir,
|
|
42
|
+
root: A,
|
|
43
|
+
cwd: k
|
|
44
|
+
}),
|
|
45
|
+
recursive: x.recursive,
|
|
46
|
+
cwd: k
|
|
47
|
+
}), M = j.map(({ root: e, dir: t }) => T(e, t)), N = x.includeBranches ?? !1, P = f(x.mode), F = u(x.style), I = [];
|
|
48
|
+
Array.isArray(x.exclude) ? I.push(...x.exclude) : typeof x.exclude == "string" && I.push(x.exclude);
|
|
49
|
+
let L = I.flatMap((e) => e.split(",")).map((e) => e.trim()).filter(Boolean);
|
|
50
|
+
try {
|
|
51
|
+
p({
|
|
52
|
+
yes: x.yes,
|
|
53
|
+
json: C
|
|
54
|
+
}), C || (console.info(E.cyan("\n🚀 Actions Up!\n")), O = w("Scanning GitHub Actions...").start());
|
|
55
|
+
function o({ actionsToCheckCount: e, blockedByMode: t = [], outdated: n = [], skipped: r = [], scanResult: i, status: a }) {
|
|
56
|
+
process.stdout.write(`${JSON.stringify(b({
|
|
57
|
+
recursive: x.recursive ?? !1,
|
|
58
|
+
excludePatterns: L,
|
|
59
|
+
directories: M,
|
|
60
|
+
minAge: x.minAge,
|
|
61
|
+
actionsToCheckCount: e,
|
|
62
|
+
includeBranches: N,
|
|
63
|
+
blockedByMode: t,
|
|
64
|
+
scanResult: i,
|
|
65
|
+
outdated: n,
|
|
66
|
+
skipped: r,
|
|
67
|
+
status: a,
|
|
68
|
+
style: F,
|
|
69
|
+
mode: P
|
|
70
|
+
}), null, 2)}\n`);
|
|
71
|
+
}
|
|
72
|
+
let c = _(x.recursive ? await Promise.all(j.map(({ root: e, dir: t }) => y(e, t))) : await Promise.all(j.map(({ root: e, dir: t }) => S(e, t)))), u = c.actions.length, f = c.workflows.size, h = c.compositeActions.size;
|
|
73
|
+
if (O?.success(`Found ${E.yellow(u)} actions in ${E.yellow(f)} workflows and ${E.yellow(h)} composite actions`), u === 0) {
|
|
74
|
+
if (C) {
|
|
75
|
+
o({
|
|
76
|
+
status: "no-actions-found",
|
|
77
|
+
actionsToCheckCount: 0,
|
|
78
|
+
scanResult: c
|
|
79
|
+
});
|
|
71
80
|
return;
|
|
72
81
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
console.info(E.green("\n✨ No GitHub Actions found in this repository"));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let T = c.actions;
|
|
86
|
+
if (L.length > 0) {
|
|
87
|
+
let { parseExcludePatterns: e } = await import("../core/filters/parse-exclude-patterns.js"), t = e(L);
|
|
88
|
+
t.length > 0 && (T = T.filter((e) => {
|
|
89
|
+
let { name: n } = e;
|
|
90
|
+
for (let e of t) if (e.test(n)) return !1;
|
|
91
|
+
return !0;
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
if (C || (O = w("Checking for updates...").start()), T.length === 0) {
|
|
95
|
+
if (O?.success("No actions to check after excludes"), C) {
|
|
96
|
+
o({
|
|
97
|
+
status: "nothing-to-check",
|
|
98
|
+
actionsToCheckCount: 0,
|
|
99
|
+
scanResult: c
|
|
100
|
+
});
|
|
92
101
|
return;
|
|
93
102
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
let
|
|
113
|
-
|
|
114
|
-
effectiveCurrentVersion: r,
|
|
115
|
-
allowed: k === "minor" ? i === "minor" || i === "patch" || i === "none" : i === "patch" || i === "none",
|
|
116
|
-
update: n
|
|
117
|
-
};
|
|
118
|
-
})), c = [], l = await Promise.all(o.map(async (e) => {
|
|
119
|
-
if (e.allowed) return { update: e.update };
|
|
120
|
-
let t = await i(F, {
|
|
121
|
-
currentVersion: e.effectiveCurrentVersion,
|
|
122
|
-
actionName: e.update.action.name,
|
|
123
|
-
tagsCache: n,
|
|
124
|
-
shaCache: r,
|
|
125
|
-
mode: k
|
|
126
|
-
});
|
|
127
|
-
return t ? { update: {
|
|
128
|
-
...e.update,
|
|
129
|
-
latestVersion: t.version,
|
|
130
|
-
latestSha: t.sha,
|
|
131
|
-
isBreaking: !1,
|
|
132
|
-
hasUpdate: !0
|
|
133
|
-
} } : { blocked: e.update };
|
|
134
|
-
}));
|
|
135
|
-
for (let e of l) {
|
|
136
|
-
if (e.update) {
|
|
137
|
-
c.push(e.update);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
H.push(e.blocked);
|
|
103
|
+
console.info(E.green("\n✨ Nothing to check after excludes\n"));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
let k = process.env.GITHUB_TOKEN, A = a(k), I = await g(T, k, {
|
|
107
|
+
client: A,
|
|
108
|
+
includeBranches: N,
|
|
109
|
+
style: F
|
|
110
|
+
}), R = [];
|
|
111
|
+
await Promise.all(I.map(async (e) => {
|
|
112
|
+
await m(e.action.file, e.action.line) || R.push(e);
|
|
113
|
+
}));
|
|
114
|
+
let z = R.filter((e) => e.status === "skipped"), B = R.filter((e) => e.hasUpdate), V = x.minAge * 24 * 60 * 60 * 1e3, H = Date.now();
|
|
115
|
+
B = B.filter((e) => e.publishedAt ? H - e.publishedAt.getTime() >= V : !0);
|
|
116
|
+
let U = [];
|
|
117
|
+
if (P !== "major") {
|
|
118
|
+
let n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map(), o = await Promise.all(B.map(async (n) => {
|
|
119
|
+
let r = n.currentVersion;
|
|
120
|
+
if (t(n.currentVersion)) {
|
|
121
|
+
let t = await e(n.action.file, n.action.line, a);
|
|
122
|
+
t && (r = t);
|
|
141
123
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
124
|
+
let i = s(r, n.latestVersion), o = (P === "minor" ? [
|
|
125
|
+
"minor",
|
|
126
|
+
"patch",
|
|
127
|
+
"none"
|
|
128
|
+
] : ["patch", "none"]).includes(i);
|
|
129
|
+
return {
|
|
130
|
+
effectiveCurrentVersion: r,
|
|
131
|
+
allowed: o,
|
|
132
|
+
update: n
|
|
133
|
+
};
|
|
134
|
+
})), c = [], l = await Promise.all(o.map(async (e) => {
|
|
135
|
+
if (e.allowed) return { update: e.update };
|
|
136
|
+
let t = await i(A, {
|
|
137
|
+
currentVersion: e.effectiveCurrentVersion,
|
|
138
|
+
actionName: e.update.action.name,
|
|
139
|
+
tagsCache: n,
|
|
140
|
+
shaCache: r,
|
|
141
|
+
mode: P
|
|
142
|
+
});
|
|
143
|
+
return t ? { update: {
|
|
144
|
+
...e.update,
|
|
145
|
+
latestVersion: t.version,
|
|
146
|
+
latestSha: t.sha,
|
|
147
|
+
isBreaking: !1,
|
|
148
|
+
hasUpdate: !0
|
|
149
|
+
} } : { blocked: e.update };
|
|
150
150
|
}));
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
o({
|
|
156
|
-
actionsToCheckCount: N.length,
|
|
157
|
-
status: "up-to-date",
|
|
158
|
-
blockedByMode: H,
|
|
159
|
-
scanResult: l,
|
|
160
|
-
skipped: R
|
|
161
|
-
});
|
|
162
|
-
return;
|
|
151
|
+
for (let e of l) {
|
|
152
|
+
if (e.update) {
|
|
153
|
+
c.push(e.update);
|
|
154
|
+
continue;
|
|
163
155
|
}
|
|
164
|
-
|
|
165
|
-
return;
|
|
156
|
+
U.push(e.blocked);
|
|
166
157
|
}
|
|
167
|
-
|
|
158
|
+
B = c;
|
|
159
|
+
}
|
|
160
|
+
B = B.map((e) => r(e, F));
|
|
161
|
+
let W = B.filter((e) => !e.targetRef).map((e) => ({
|
|
162
|
+
...e,
|
|
163
|
+
skipReason: "unsupported-style",
|
|
164
|
+
status: "skipped",
|
|
165
|
+
hasUpdate: !1
|
|
166
|
+
}));
|
|
167
|
+
if (z.push(...W), B = B.filter((e) => e.targetRef), B.length === 0) {
|
|
168
|
+
if (O?.success("All actions are up to date!"), C) {
|
|
168
169
|
o({
|
|
169
|
-
actionsToCheckCount:
|
|
170
|
-
status: "
|
|
171
|
-
blockedByMode:
|
|
172
|
-
scanResult:
|
|
173
|
-
|
|
174
|
-
skipped: R
|
|
170
|
+
actionsToCheckCount: T.length,
|
|
171
|
+
status: "up-to-date",
|
|
172
|
+
blockedByMode: U,
|
|
173
|
+
scanResult: c,
|
|
174
|
+
skipped: z
|
|
175
175
|
});
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
178
|
+
!D && z.length > 0 && d(z, N, F), !D && U.length > 0 && v(U, P), console.info(E.green("\n✨ Everything is already at the latest version!\n"));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
let G = B.filter((e) => e.isBreaking);
|
|
182
|
+
if (O?.success(`Found ${E.yellow(B.length)} updates available${G.length > 0 ? ` (${E.redBright(G.length)} breaking)` : ""}`), C) {
|
|
183
|
+
o({
|
|
184
|
+
actionsToCheckCount: T.length,
|
|
185
|
+
status: "updates-available",
|
|
186
|
+
blockedByMode: U,
|
|
187
|
+
scanResult: c,
|
|
188
|
+
outdated: B,
|
|
189
|
+
skipped: z
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (!D && z.length > 0 && d(z, N, F), !D && U.length > 0 && v(U, P), x.dryRun) {
|
|
194
|
+
console.info(E.yellow("\n📋 Dry Run - No changes will be made\n"));
|
|
195
|
+
for (let e of B) {
|
|
196
|
+
let t = e.targetRefStyle === "sha" && e.targetRef ? `${e.latestVersion} ${E.gray(`(${e.targetRef.slice(0, 7)})`)}` : e.targetRef ?? e.latestVersion;
|
|
197
|
+
console.info(`${E.cyan(e.action.file ?? "unknown")}:\n${e.action.name}: ${E.redBright(e.currentVersion)} → ${E.green(t)}\n`);
|
|
198
|
+
}
|
|
199
|
+
console.info(E.gray(`\n${B.length} actions would be updated\n`));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (x.yes) {
|
|
203
|
+
let e = B.filter((e) => e.targetRef);
|
|
204
|
+
if (e.length === 0) {
|
|
205
|
+
console.info(E.yellow("\n⚠️ No actionable updates available\n"));
|
|
185
206
|
return;
|
|
186
207
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
} else {
|
|
195
|
-
(R.length > 0 || H.length > 0) && console.info("");
|
|
196
|
-
let e = await n(z, { showAge: b.minAge > 0 });
|
|
197
|
-
if (!e || e.length === 0) {
|
|
198
|
-
console.info(C.gray("\nNo updates applied"));
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
console.info(C.yellow(`\n🔄 Updating ${e.length} selected actions...\n`)), await c(e), console.info(C.green("\n✓ Updates applied successfully!"));
|
|
208
|
+
console.info(E.yellow(`\n🔄 Updating ${e.length} actions...\n`)), await l(e);
|
|
209
|
+
} else {
|
|
210
|
+
!D && (z.length > 0 || U.length > 0) && console.info("");
|
|
211
|
+
let e = await n(B, { showAge: x.minAge > 0 });
|
|
212
|
+
if (!e || e.length === 0) {
|
|
213
|
+
console.info(E.gray("\nNo updates applied"));
|
|
214
|
+
return;
|
|
202
215
|
}
|
|
203
|
-
|
|
204
|
-
T?.error("Failed"), e instanceof Error && e.name === "GitHubRateLimitError" ? (console.error(C.yellow("\n⚠️ Rate Limit Exceeded\n")), console.error(e.message), console.error(C.gray("\nExample: GITHUB_TOKEN=ghp_xxxx actions-up\n"))) : console.error(C.redBright("\nError:"), e instanceof Error ? e.message : String(e)), process.exit(1);
|
|
216
|
+
console.info(E.yellow(`\n🔄 Updating ${e.length} selected actions...\n`)), await l(e);
|
|
205
217
|
}
|
|
206
|
-
|
|
218
|
+
console.info(E.green("\n✓ Updates applied successfully!"));
|
|
219
|
+
} catch (e) {
|
|
220
|
+
O?.error("Failed"), e instanceof Error && e.name === "GitHubRateLimitError" ? (console.error(E.yellow("\n⚠️ Rate Limit Exceeded\n")), console.error(e.message), console.error(E.gray("\nExample: GITHUB_TOKEN=ghp_xxxx actions-up\n"))) : console.error(E.redBright("\nError:"), e instanceof Error ? e.message : String(e)), process.exit(1);
|
|
221
|
+
}
|
|
207
222
|
}
|
|
208
|
-
export {
|
|
223
|
+
export { D as run };
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
function e(e) {
|
|
2
2
|
let t = (e ?? "major").toLowerCase();
|
|
3
|
-
if (
|
|
3
|
+
if ([
|
|
4
|
+
"major",
|
|
5
|
+
"minor",
|
|
6
|
+
"patch"
|
|
7
|
+
].includes(t)) return t;
|
|
4
8
|
throw Error(`Invalid mode "${e}". Expected "major", "minor", or "patch".`);
|
|
5
9
|
}
|
|
6
10
|
export { e as normalizeUpdateMode };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Options.
|
|
3
|
+
*/
|
|
4
|
+
export interface CLIOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Regex patterns to exclude actions by name (repeatable).
|
|
7
|
+
*/
|
|
8
|
+
exclude?: string[] | string;
|
|
9
|
+
/**
|
|
10
|
+
* Whether to include branch references in update checks.
|
|
11
|
+
*/
|
|
12
|
+
includeBranches?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Custom directory name (e.g., '.gitea' instead of '.github').
|
|
15
|
+
*/
|
|
16
|
+
dir?: string[] | string;
|
|
17
|
+
/**
|
|
18
|
+
* Recursively scan directories for YAML files.
|
|
19
|
+
*/
|
|
20
|
+
recursive?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Suppress skipped and blocked-update warnings.
|
|
23
|
+
*/
|
|
24
|
+
quiet?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Preview changes without applying them.
|
|
27
|
+
*/
|
|
28
|
+
dryRun: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Update style (sha or preserve).
|
|
31
|
+
*/
|
|
32
|
+
style?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Output a machine-readable JSON report.
|
|
35
|
+
*/
|
|
36
|
+
json?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Minimum age in days for updates.
|
|
39
|
+
*/
|
|
40
|
+
minAge: number;
|
|
41
|
+
/**
|
|
42
|
+
* Update mode (major, minor, patch).
|
|
43
|
+
*/
|
|
44
|
+
mode?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Skip all confirmations.
|
|
47
|
+
*/
|
|
48
|
+
yes: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Result of parsing CLI arguments. `kind` discriminates what the caller should
|
|
52
|
+
* do next.
|
|
53
|
+
*/
|
|
54
|
+
export type ParseArgumentsResult = {
|
|
55
|
+
options: CLIOptions;
|
|
56
|
+
kind: 'options';
|
|
57
|
+
} | {
|
|
58
|
+
message: string;
|
|
59
|
+
kind: 'error';
|
|
60
|
+
} | {
|
|
61
|
+
kind: 'version';
|
|
62
|
+
text: string;
|
|
63
|
+
} | {
|
|
64
|
+
kind: 'help';
|
|
65
|
+
text: string;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Parse CLI arguments into normalized options.
|
|
69
|
+
*
|
|
70
|
+
* Reproduces the previous cac behavior without its runtime magic: numeric
|
|
71
|
+
* coercion for `--min-age` and the option defaults are applied manually here,
|
|
72
|
+
* and kebab-case flags are mapped to the camelCase option shape.
|
|
73
|
+
*
|
|
74
|
+
* @param argv - Raw arguments, typically `process.argv.slice(2)`.
|
|
75
|
+
* @param appVersion - Version string used for `--version`.
|
|
76
|
+
* @returns A discriminated result describing what the caller should do.
|
|
77
|
+
*/
|
|
78
|
+
export declare function parseArguments(argv: string[], appVersion: string): ParseArgumentsResult;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { parseArgs as e } from "node:util";
|
|
2
|
+
var t = "Usage:\n $ actions-up [options]\n\nOptions:\n --dir <directory> Directory to scan (repeatable). Default: .github, or . with --recursive\n --dry-run Preview changes without applying them\n --exclude <regex> Exclude actions by regex (repeatable)\n --include-branches Also check actions pinned to branches (default: false)\n --json Output update information as machine-readable JSON\n --min-age <days> Minimum age in days for updates (default: 0)\n --mode <mode> Update mode: major, minor, or patch (default: major)\n --style <style> Update style: sha or preserve (default: sha)\n -r, --recursive Recursively scan directories for YAML files\n -y, --yes Skip all confirmations\n -q, --quiet Suppress skipped/blocked warnings\n -h, --help Display this message\n -v, --version Display version number", n = {
|
|
3
|
+
exclude: {
|
|
4
|
+
type: "string",
|
|
5
|
+
multiple: !0
|
|
6
|
+
},
|
|
7
|
+
recursive: {
|
|
8
|
+
type: "boolean",
|
|
9
|
+
short: "r"
|
|
10
|
+
},
|
|
11
|
+
version: {
|
|
12
|
+
type: "boolean",
|
|
13
|
+
short: "v"
|
|
14
|
+
},
|
|
15
|
+
"include-branches": { type: "boolean" },
|
|
16
|
+
dir: {
|
|
17
|
+
type: "string",
|
|
18
|
+
multiple: !0
|
|
19
|
+
},
|
|
20
|
+
quiet: {
|
|
21
|
+
type: "boolean",
|
|
22
|
+
short: "q"
|
|
23
|
+
},
|
|
24
|
+
help: {
|
|
25
|
+
type: "boolean",
|
|
26
|
+
short: "h"
|
|
27
|
+
},
|
|
28
|
+
yes: {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
short: "y"
|
|
31
|
+
},
|
|
32
|
+
"dry-run": { type: "boolean" },
|
|
33
|
+
"min-age": { type: "string" },
|
|
34
|
+
style: { type: "string" },
|
|
35
|
+
json: { type: "boolean" },
|
|
36
|
+
mode: { type: "string" }
|
|
37
|
+
};
|
|
38
|
+
function r(r, i) {
|
|
39
|
+
try {
|
|
40
|
+
let { values: a } = e({
|
|
41
|
+
allowPositionals: !1,
|
|
42
|
+
options: n,
|
|
43
|
+
strict: !0,
|
|
44
|
+
args: r
|
|
45
|
+
});
|
|
46
|
+
if (a.help) return {
|
|
47
|
+
text: t,
|
|
48
|
+
kind: "help"
|
|
49
|
+
};
|
|
50
|
+
if (a.version) return {
|
|
51
|
+
text: `actions-up/${i} ${process.platform}-${process.arch} node-${process.version}`,
|
|
52
|
+
kind: "version"
|
|
53
|
+
};
|
|
54
|
+
let o = a["min-age"], s = o === void 0 ? 0 : Number(o);
|
|
55
|
+
return !Number.isFinite(s) || s < 0 ? {
|
|
56
|
+
message: `Invalid --min-age "${o}". Expected a non-negative number.`,
|
|
57
|
+
kind: "error"
|
|
58
|
+
} : {
|
|
59
|
+
options: {
|
|
60
|
+
includeBranches: a["include-branches"],
|
|
61
|
+
dryRun: a["dry-run"] ?? !1,
|
|
62
|
+
style: a.style ?? "sha",
|
|
63
|
+
mode: a.mode ?? "major",
|
|
64
|
+
recursive: a.recursive,
|
|
65
|
+
yes: a.yes ?? !1,
|
|
66
|
+
exclude: a.exclude,
|
|
67
|
+
quiet: a.quiet,
|
|
68
|
+
json: a.json,
|
|
69
|
+
dir: a.dir,
|
|
70
|
+
minAge: s
|
|
71
|
+
},
|
|
72
|
+
kind: "options"
|
|
73
|
+
};
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return {
|
|
76
|
+
message: e.message,
|
|
77
|
+
kind: "error"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export { r as parseArguments };
|
|
@@ -3,173 +3,183 @@ import { preserveTagFormat as t } from "../versions/preserve-tag-format.js";
|
|
|
3
3
|
import { normalizeVersion as n } from "../versions/normalize-version.js";
|
|
4
4
|
import { createGitHubClient as r } from "./create-github-client.js";
|
|
5
5
|
import i from "semver";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let h = /* @__PURE__ */ new Map();
|
|
10
|
-
for (let e of m) {
|
|
11
|
-
let t = h.get(e.name) ?? [];
|
|
12
|
-
t.push(e), h.set(e.name, t);
|
|
6
|
+
var a = class extends Error {
|
|
7
|
+
constructor(e, t) {
|
|
8
|
+
super(e, t), this.name = "GitHubRateLimitError";
|
|
13
9
|
}
|
|
14
|
-
|
|
10
|
+
};
|
|
11
|
+
async function o(t, o, c) {
|
|
12
|
+
let f = c?.client ?? r(o), p = c?.includeBranches ?? !1, m = c?.style ?? "sha", h = t.filter((e) => e.type === "external" || e.type === "reusable-workflow");
|
|
13
|
+
if (h.length === 0) return [];
|
|
14
|
+
let g = /* @__PURE__ */ new Map();
|
|
15
|
+
for (let e of h) {
|
|
16
|
+
let t = g.get(e.name) ?? [];
|
|
17
|
+
t.push(e), g.set(e.name, t);
|
|
18
|
+
}
|
|
19
|
+
let _ = {
|
|
15
20
|
rateLimitError: null,
|
|
16
21
|
rateLimitHit: !1
|
|
17
|
-
},
|
|
18
|
-
|
|
22
|
+
}, v = [];
|
|
23
|
+
async function y(t) {
|
|
24
|
+
if (_.rateLimitHit) return {
|
|
19
25
|
currentRefType: "unknown",
|
|
20
26
|
publishedAt: null,
|
|
21
27
|
version: null,
|
|
22
|
-
actionName:
|
|
28
|
+
actionName: t,
|
|
23
29
|
sha: null
|
|
24
|
-
}
|
|
25
|
-
let
|
|
26
|
-
if (
|
|
30
|
+
};
|
|
31
|
+
let r = t.split("/");
|
|
32
|
+
if (r.length < 2) return {
|
|
27
33
|
currentRefType: "unknown",
|
|
28
34
|
publishedAt: null,
|
|
29
35
|
version: null,
|
|
30
|
-
actionName:
|
|
36
|
+
actionName: t,
|
|
31
37
|
sha: null
|
|
32
|
-
}
|
|
33
|
-
let [
|
|
34
|
-
if (!
|
|
38
|
+
};
|
|
39
|
+
let [a, o] = r;
|
|
40
|
+
if (!a || !o) return {
|
|
35
41
|
currentRefType: "unknown",
|
|
36
42
|
publishedAt: null,
|
|
37
43
|
version: null,
|
|
38
|
-
actionName:
|
|
44
|
+
actionName: t,
|
|
39
45
|
sha: null
|
|
40
|
-
}
|
|
46
|
+
};
|
|
41
47
|
try {
|
|
42
|
-
let
|
|
43
|
-
if (
|
|
44
|
-
let e = await
|
|
45
|
-
if (
|
|
46
|
-
currentRefType:
|
|
48
|
+
let r = g.get(t)[0]?.version, s = u(r);
|
|
49
|
+
if (r && !l(r) && !e(r)) {
|
|
50
|
+
let e = await f.getRefType(a, o, r);
|
|
51
|
+
if (s = e === "branch" || e === "tag" ? e : s, e === "branch" && !p) return {
|
|
52
|
+
currentRefType: s,
|
|
47
53
|
skipReason: "branch",
|
|
48
54
|
status: "skipped",
|
|
49
55
|
publishedAt: null,
|
|
50
56
|
version: null,
|
|
51
|
-
actionName:
|
|
57
|
+
actionName: t,
|
|
52
58
|
sha: null
|
|
53
|
-
}
|
|
59
|
+
};
|
|
54
60
|
}
|
|
55
|
-
let
|
|
56
|
-
if (!
|
|
57
|
-
let e = await
|
|
58
|
-
|
|
61
|
+
let c = await f.getLatestRelease(a, o);
|
|
62
|
+
if (!c) {
|
|
63
|
+
let e = await f.getAllReleases(a, o, 1);
|
|
64
|
+
c = e.find((e) => !e.isPrerelease) ?? e[0] ?? null;
|
|
59
65
|
}
|
|
60
|
-
if (
|
|
61
|
-
let { publishedAt:
|
|
66
|
+
if (c) {
|
|
67
|
+
let { publishedAt: r, version: l, sha: u } = c, p = !1;
|
|
62
68
|
{
|
|
63
|
-
let t = n(
|
|
64
|
-
|
|
69
|
+
let t = n(l), r = !!(l && l.trim() !== ""), a = r && /^v?\d+$/u.test(l.trim()), o = i.valid(t);
|
|
70
|
+
p = !r || a || !o || !e(l);
|
|
65
71
|
}
|
|
66
|
-
if (
|
|
67
|
-
let
|
|
68
|
-
if (
|
|
69
|
-
let
|
|
72
|
+
if (p) {
|
|
73
|
+
let r = await f.getAllTags(a, o, 30);
|
|
74
|
+
if (r.length > 0) {
|
|
75
|
+
let c = r.filter((t) => e(t.tag)).map((e) => ({
|
|
70
76
|
v: i.valid(n(e.tag)),
|
|
71
77
|
raw: e
|
|
72
78
|
}));
|
|
73
|
-
if (
|
|
74
|
-
|
|
79
|
+
if (c.length > 0) {
|
|
80
|
+
c.sort((e, t) => {
|
|
75
81
|
let n = i.rcompare(e.v, t.v);
|
|
76
82
|
if (n !== 0) return n;
|
|
77
83
|
let r = +!!/\d+\.\d+/u.test(e.raw.tag);
|
|
78
84
|
return +!!/\d+\.\d+/u.test(t.raw.tag) - r;
|
|
79
85
|
});
|
|
80
|
-
let e =
|
|
81
|
-
if (!
|
|
82
|
-
let n = e.tag,
|
|
83
|
-
if (!
|
|
84
|
-
|
|
86
|
+
let e = c[0].raw, r = i.valid(n(l) ?? void 0);
|
|
87
|
+
if (!r || i.gt(c[0].v, r) || i.eq(c[0].v, r) && /\d+\.\d+/u.test(e.tag)) {
|
|
88
|
+
let n = e.tag, r = e.sha?.length ? e.sha : null;
|
|
89
|
+
if (!r && n) try {
|
|
90
|
+
r = await f.getTagSha(a, o, n);
|
|
85
91
|
} catch (e) {
|
|
86
|
-
if (
|
|
92
|
+
if (d(e)) throw e;
|
|
87
93
|
}
|
|
88
|
-
return
|
|
89
|
-
currentRefType:
|
|
94
|
+
return {
|
|
95
|
+
currentRefType: s,
|
|
90
96
|
version: n,
|
|
91
97
|
publishedAt: null,
|
|
92
|
-
sha:
|
|
93
|
-
actionName:
|
|
94
|
-
}
|
|
98
|
+
sha: r,
|
|
99
|
+
actionName: t
|
|
100
|
+
};
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
103
|
}
|
|
98
104
|
}
|
|
99
|
-
if (
|
|
100
|
-
let e =
|
|
105
|
+
if (l) {
|
|
106
|
+
let e = u;
|
|
101
107
|
try {
|
|
102
|
-
|
|
108
|
+
u = await f.getTagSha(a, o, l) ?? e;
|
|
103
109
|
} catch (t) {
|
|
104
|
-
if (
|
|
105
|
-
|
|
110
|
+
if (d(t)) throw t;
|
|
111
|
+
u = e;
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
|
-
return
|
|
109
|
-
currentRefType:
|
|
114
|
+
return {
|
|
115
|
+
currentRefType: s,
|
|
110
116
|
status: "ok",
|
|
111
|
-
publishedAt:
|
|
112
|
-
actionName:
|
|
113
|
-
version:
|
|
114
|
-
sha:
|
|
115
|
-
}
|
|
117
|
+
publishedAt: r,
|
|
118
|
+
actionName: t,
|
|
119
|
+
version: l,
|
|
120
|
+
sha: u
|
|
121
|
+
};
|
|
116
122
|
}
|
|
117
|
-
let
|
|
118
|
-
if (
|
|
119
|
-
let
|
|
123
|
+
let m = await f.getAllTags(a, o, 30);
|
|
124
|
+
if (m.length > 0) {
|
|
125
|
+
let r = m.filter((t) => e(t.tag)).map((e) => ({
|
|
120
126
|
v: i.valid(n(e.tag)),
|
|
121
127
|
raw: e
|
|
122
128
|
})), c;
|
|
123
|
-
|
|
129
|
+
r.length > 0 ? (r.sort((e, t) => {
|
|
124
130
|
let n = i.rcompare(e.v, t.v);
|
|
125
131
|
if (n !== 0) return n;
|
|
126
132
|
let r = +!!/\d+\.\d+/u.test(e.raw.tag);
|
|
127
133
|
return +!!/\d+\.\d+/u.test(t.raw.tag) - r;
|
|
128
|
-
}), c =
|
|
129
|
-
let l = c.tag,
|
|
130
|
-
if (!
|
|
131
|
-
|
|
134
|
+
}), c = r[0].raw) : c = m[0];
|
|
135
|
+
let l = c.tag, u = c.sha?.length ? c.sha : null;
|
|
136
|
+
if (!u && l) try {
|
|
137
|
+
u = await f.getTagSha(a, o, l);
|
|
132
138
|
} catch (e) {
|
|
133
|
-
if (
|
|
139
|
+
if (d(e)) throw e;
|
|
134
140
|
}
|
|
135
|
-
return
|
|
136
|
-
currentRefType:
|
|
141
|
+
return {
|
|
142
|
+
currentRefType: s,
|
|
137
143
|
status: "ok",
|
|
138
144
|
publishedAt: null,
|
|
139
|
-
actionName:
|
|
145
|
+
actionName: t,
|
|
140
146
|
version: l,
|
|
141
|
-
sha:
|
|
142
|
-
}
|
|
147
|
+
sha: u
|
|
148
|
+
};
|
|
143
149
|
}
|
|
144
|
-
return
|
|
145
|
-
currentRefType:
|
|
150
|
+
return {
|
|
151
|
+
currentRefType: s,
|
|
146
152
|
publishedAt: null,
|
|
147
153
|
version: null,
|
|
148
|
-
actionName:
|
|
154
|
+
actionName: t,
|
|
149
155
|
sha: null
|
|
150
|
-
}
|
|
156
|
+
};
|
|
151
157
|
} catch (e) {
|
|
152
|
-
return e instanceof Error && e.name === "GitHubRateLimitError" ? (
|
|
158
|
+
return e instanceof Error && e.name === "GitHubRateLimitError" ? (_.rateLimitHit = !0, _.rateLimitError = e, {
|
|
153
159
|
currentRefType: "unknown",
|
|
154
160
|
publishedAt: null,
|
|
155
161
|
version: null,
|
|
156
|
-
actionName:
|
|
162
|
+
actionName: t,
|
|
157
163
|
sha: null
|
|
158
|
-
}
|
|
164
|
+
}) : (console.warn(`Failed to check ${t}:`, e), {
|
|
159
165
|
currentRefType: "unknown",
|
|
160
166
|
publishedAt: null,
|
|
161
167
|
version: null,
|
|
162
|
-
actionName:
|
|
168
|
+
actionName: t,
|
|
163
169
|
sha: null
|
|
164
|
-
}
|
|
170
|
+
});
|
|
165
171
|
}
|
|
166
|
-
}), Promise.resolve([]));
|
|
167
|
-
if (g.rateLimitError) {
|
|
168
|
-
let e = !!(a ?? process.env.GITHUB_TOKEN), t = `${g.rateLimitError.message || "GitHub API rate limit exceeded."}\n${e ? "Wait for reset or reduce request rate." : "Please set GITHUB_TOKEN environment variable to increase the limit.\nSee: https://github.com/azat-io/actions-up?tab=readme-ov-file#github-token"}`, n = Error(t);
|
|
169
|
-
throw n.name = "GitHubRateLimitError", n;
|
|
170
172
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
async function b(e) {
|
|
174
|
+
let t = e.next();
|
|
175
|
+
t.done || (v.push(await y(t.value)), await b(e));
|
|
176
|
+
}
|
|
177
|
+
if (await b(g.keys()), _.rateLimitError) {
|
|
178
|
+
let e = !!(o ?? process.env.GITHUB_TOKEN);
|
|
179
|
+
throw new a(`${_.rateLimitError.message || "GitHub API rate limit exceeded."}\n${e ? "Wait for reset or reduce request rate." : "Please set GITHUB_TOKEN environment variable to increase the limit.\nSee: https://github.com/azat-io/actions-up?tab=readme-ov-file#github-token"}`);
|
|
180
|
+
}
|
|
181
|
+
let x = /* @__PURE__ */ new Map();
|
|
182
|
+
for (let e of v) x.set(e.actionName, {
|
|
173
183
|
currentRefType: e.currentRefType,
|
|
174
184
|
publishedAt: e.publishedAt,
|
|
175
185
|
actionName: e.actionName,
|
|
@@ -178,10 +188,10 @@ async function a(t, a, s) {
|
|
|
178
188
|
status: e.status,
|
|
179
189
|
sha: e.sha
|
|
180
190
|
});
|
|
181
|
-
let
|
|
182
|
-
for (let e of
|
|
183
|
-
let t =
|
|
184
|
-
t ?
|
|
191
|
+
let S = [];
|
|
192
|
+
for (let e of h) {
|
|
193
|
+
let t = x.get(e.name);
|
|
194
|
+
t ? S.push(s(e, {
|
|
185
195
|
publishedAt: t.publishedAt,
|
|
186
196
|
version: t.version,
|
|
187
197
|
sha: t.sha
|
|
@@ -189,20 +199,20 @@ async function a(t, a, s) {
|
|
|
189
199
|
currentRefType: t.currentRefType,
|
|
190
200
|
skipReason: t.skipReason,
|
|
191
201
|
status: t.status,
|
|
192
|
-
style:
|
|
193
|
-
})) :
|
|
202
|
+
style: m
|
|
203
|
+
})) : S.push(s(e, {
|
|
194
204
|
publishedAt: null,
|
|
195
205
|
version: null,
|
|
196
206
|
sha: null
|
|
197
207
|
}, {
|
|
198
|
-
currentRefType:
|
|
199
|
-
style:
|
|
208
|
+
currentRefType: u(e.version),
|
|
209
|
+
style: m
|
|
200
210
|
}));
|
|
201
211
|
}
|
|
202
|
-
return
|
|
212
|
+
return S;
|
|
203
213
|
}
|
|
204
|
-
function
|
|
205
|
-
let { version: o, sha:
|
|
214
|
+
function s(e, r, a) {
|
|
215
|
+
let { version: o, sha: s, publishedAt: u } = r, d = e.version ?? "unknown", f = n(d), p = a.currentRefType, { style: m } = a, h = (m === "preserve" && p === "tag" ? t(d, o) : null) ?? o, g = h ? n(h) : null, _ = a.status ?? "ok", v = a.skipReason;
|
|
206
216
|
if (_ === "skipped") return {
|
|
207
217
|
currentRefType: p,
|
|
208
218
|
currentVersion: d,
|
|
@@ -211,11 +221,12 @@ function o(e, r, a) {
|
|
|
211
221
|
latestVersion: o,
|
|
212
222
|
publishedAt: u,
|
|
213
223
|
skipReason: v,
|
|
214
|
-
latestSha:
|
|
224
|
+
latestSha: s,
|
|
215
225
|
action: e,
|
|
216
226
|
status: _
|
|
217
227
|
};
|
|
218
|
-
|
|
228
|
+
let y = !1, b = !1;
|
|
229
|
+
if (f && l(f)) s ? y = !c(f, s) : g && (y = !0);
|
|
219
230
|
else if (f && g) {
|
|
220
231
|
let t = i.valid(f), n = i.valid(g);
|
|
221
232
|
if (t && n) {
|
|
@@ -223,7 +234,7 @@ function o(e, r, a) {
|
|
|
223
234
|
let e = i.major(t);
|
|
224
235
|
b = i.major(n) > e;
|
|
225
236
|
}
|
|
226
|
-
!y && i.eq(t, n) && !
|
|
237
|
+
!y && i.eq(t, n) && !l(e.version) && s && m === "sha" && (y = !0, b = !1);
|
|
227
238
|
} else f !== g && (y = !0);
|
|
228
239
|
}
|
|
229
240
|
return {
|
|
@@ -233,25 +244,25 @@ function o(e, r, a) {
|
|
|
233
244
|
publishedAt: u,
|
|
234
245
|
isBreaking: b,
|
|
235
246
|
skipReason: v,
|
|
236
|
-
latestSha:
|
|
247
|
+
latestSha: s,
|
|
237
248
|
hasUpdate: y,
|
|
238
249
|
action: e,
|
|
239
250
|
status: _
|
|
240
251
|
};
|
|
241
252
|
}
|
|
242
|
-
function
|
|
253
|
+
function c(e, t) {
|
|
243
254
|
let n = e.replace(/^v/u, ""), r = t.replace(/^v/u, ""), i = Math.min(n.length, r.length);
|
|
244
255
|
return i < 7 ? !1 : n.slice(0, Math.max(0, i)).toLowerCase() === r.slice(0, Math.max(0, i)).toLowerCase();
|
|
245
256
|
}
|
|
246
|
-
function
|
|
257
|
+
function l(e) {
|
|
247
258
|
if (!e) return !1;
|
|
248
259
|
let t = e.replace(/^v/u, "");
|
|
249
260
|
return /^[0-9a-f]{7,40}$/iu.test(t);
|
|
250
261
|
}
|
|
251
|
-
function
|
|
252
|
-
return t ?
|
|
262
|
+
function u(t) {
|
|
263
|
+
return t ? l(t) ? "sha" : e(t) ? "tag" : "unknown" : "unknown";
|
|
253
264
|
}
|
|
254
|
-
function
|
|
265
|
+
function d(e) {
|
|
255
266
|
return e instanceof Error && e.name === "GitHubRateLimitError";
|
|
256
267
|
}
|
|
257
|
-
export {
|
|
268
|
+
export { o as checkUpdates };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var e = class extends Error {
|
|
2
|
-
constructor(e) {
|
|
3
|
-
let
|
|
4
|
-
super(`GitHub API rate limit exceeded. Resets at ${t
|
|
2
|
+
constructor(e, t) {
|
|
3
|
+
let n = e.toLocaleTimeString();
|
|
4
|
+
super(`GitHub API rate limit exceeded. Resets at ${n}`, t), this.name = "GitHubRateLimitError";
|
|
5
5
|
}
|
|
6
6
|
};
|
|
7
7
|
export { e as GitHubRateLimitError };
|
|
@@ -9,8 +9,7 @@ async function t(t, n, r = {}) {
|
|
|
9
9
|
let a = await fetch(`${t.baseUrl}${n}`, {
|
|
10
10
|
...r,
|
|
11
11
|
headers: i
|
|
12
|
-
}), o =
|
|
13
|
-
for (let [e, t] of a.headers.entries()) o[e] = t;
|
|
12
|
+
}), o = Object.fromEntries(a.headers.entries());
|
|
14
13
|
if (e(t, o), !a.ok) {
|
|
15
14
|
let e = /* @__PURE__ */ Error(`GitHub API error: ${a.status} ${a.statusText}`);
|
|
16
15
|
if (e.status = a.status, a.status === 403) {
|
|
@@ -31,8 +31,7 @@ function r() {
|
|
|
31
31
|
if (i === "github") {
|
|
32
32
|
let e = t.match(/^(?:oauth-token|token)\s*=\s*(?<val>\S[^\n\r]*)$/u);
|
|
33
33
|
if (e?.groups?.val) return e.groups.val.trim();
|
|
34
|
-
}
|
|
35
|
-
if (i === "hub") {
|
|
34
|
+
} else if (i === "hub") {
|
|
36
35
|
let e = t.match(/^oauthtoken\s*=\s*(?<val>\S[^\n\r]*)$/u);
|
|
37
36
|
if (e?.groups?.val) return e.groups.val.trim();
|
|
38
37
|
}
|
|
@@ -7,7 +7,7 @@ async function n(n) {
|
|
|
7
7
|
let n = i.get(t) ?? [];
|
|
8
8
|
n.push(e), i.set(t, n);
|
|
9
9
|
}
|
|
10
|
-
let a = [...i
|
|
10
|
+
let a = [...i].map(async ([n, i]) => {
|
|
11
11
|
let a = await e(n, "utf8");
|
|
12
12
|
for (let e of i) {
|
|
13
13
|
let t = e.targetRef ?? e.latestSha, n = e.targetRefStyle ?? (e.latestSha ? "sha" : null);
|
|
@@ -15,11 +15,12 @@ async function n(n) {
|
|
|
15
15
|
function i(e) {
|
|
16
16
|
return e.replaceAll(/[$()*+\-./?[\\\]^{|}]/gu, String.raw`\$&`);
|
|
17
17
|
}
|
|
18
|
-
let o = i(e.action.name)
|
|
18
|
+
let o = i(e.action.name);
|
|
19
19
|
if (o.includes("\n") || o.includes("\r")) {
|
|
20
20
|
console.error(`Invalid action name: ${e.action.name}`);
|
|
21
21
|
continue;
|
|
22
22
|
}
|
|
23
|
+
let s = e.currentVersion ? i(e.currentVersion) : "";
|
|
23
24
|
if (s && (s.includes("\n") || s.includes("\r"))) {
|
|
24
25
|
console.error(`Invalid version: ${e.currentVersion}`);
|
|
25
26
|
continue;
|
|
@@ -32,7 +33,7 @@ async function n(n) {
|
|
|
32
33
|
console.error(`Invalid SHA format: ${t}`);
|
|
33
34
|
continue;
|
|
34
35
|
}
|
|
35
|
-
let l = String.raw`['"]?\buses\b['"]?\s*:\s*`, u = String.raw`(?:^[^\S\n]*(?:-[^\S\n]*)?|[{\[,][^\S\n]*)` + l, d =
|
|
36
|
+
let c = s ? String.raw`(?=(?:['"]|[ \t\]}{,#]|$))` : "", l = String.raw`['"]?\buses\b['"]?\s*:\s*`, u = String.raw`(?:^[^\S\n]*(?:-[^\S\n]*)?|[{\[,][^\S\n]*)` + l, d = RegExp(`(?<prefix>${u})(?<quote>['"]?)(?<name>${o})@${s}${c}${String.raw`\k<quote>`}${String.raw`(?<after>[ \t\]}{,]*)`}${String.raw`(?<comment>[^\S\r\n]*#[^\r\n]*)?`}`, "gm");
|
|
36
37
|
a = a.replace(d, (i, ...a) => {
|
|
37
38
|
let o = a.at(-3), c = a.at(-2), l = a.at(-1), u = c.indexOf("\n", o + i.length), d = (u === -1 ? c.slice(o + i.length) : c.slice(o + i.length, u)).trim().length > 0, f = l.after.endsWith(" ") ? "" : " ", p = "";
|
|
38
39
|
if (n === "sha") p = d && !l.comment && s !== "" ? "" : `${f}# ${e.latestVersion}`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walks up from a starting directory to find the repository root.
|
|
3
|
+
*
|
|
4
|
+
* The root is the nearest ancestor (including the start) that contains a `.git`
|
|
5
|
+
* entry — a directory in a normal clone or a file in a git worktree — or, as a
|
|
6
|
+
* fallback for non-git checkouts, a `.github` directory. Returns null when no
|
|
7
|
+
* such ancestor exists up to the filesystem root.
|
|
8
|
+
*
|
|
9
|
+
* @param startDirectory - Absolute path to start searching from.
|
|
10
|
+
* @returns The repository root directory, or null when none is found.
|
|
11
|
+
*/
|
|
12
|
+
export declare function findRepoRoot(startDirectory: string): Promise<string | null>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "../constants.js";
|
|
2
|
+
import { dirname as e, join as t, resolve as n } from "node:path";
|
|
3
|
+
import { stat as r } from "node:fs/promises";
|
|
4
|
+
async function i(r) {
|
|
5
|
+
let o = n(r);
|
|
6
|
+
if (await a(t(o, ".git")) || await a(t(o, ".github"))) return o;
|
|
7
|
+
let s = e(o);
|
|
8
|
+
return s === o ? null : await i(s);
|
|
9
|
+
}
|
|
10
|
+
async function a(e) {
|
|
11
|
+
try {
|
|
12
|
+
return await r(e), !0;
|
|
13
|
+
} catch {
|
|
14
|
+
return !1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export { i as findRepoRoot };
|
|
@@ -4,7 +4,7 @@ function n(n, a) {
|
|
|
4
4
|
if (!n) return e.gray("unknown");
|
|
5
5
|
let o = t.parse(n), s = a ? t.parse(r(a)) : null;
|
|
6
6
|
if (!s || !o) return n;
|
|
7
|
-
let c = t.diff(r(a), n), l = s.major === 0 ?
|
|
7
|
+
let c = t.diff(r(a), n), l = e[s.major === 0 ? "yellowBright" : "gray"], u = [
|
|
8
8
|
o.major,
|
|
9
9
|
o.minor,
|
|
10
10
|
o.patch
|
|
@@ -43,13 +43,13 @@ async function p(p, T = {}) {
|
|
|
43
43
|
for (let [e, n] of D.entries()) {
|
|
44
44
|
let r = n.action.name, o = k[e], c = o.display, l = n.action.job ?? "–";
|
|
45
45
|
if (j = Math.max(j, r.length), M = Math.max(M, i(c).length, o.versionForPadding && o.shortSha ? i(`${a(o.versionForPadding, P + 1)}${s.gray(`(${o.shortSha})`)}`).length : 0), N = Math.max(N, l.length), n.latestVersion) {
|
|
46
|
-
let r =
|
|
47
|
-
P = Math.max(P, i(
|
|
46
|
+
let r = n[n.targetRefStyle === "tag" && n.targetRef ? "targetRef" : "latestVersion"], a = t(r, k[e]?.effectiveForDiff ?? n.currentVersion);
|
|
47
|
+
P = Math.max(P, i(a).length);
|
|
48
48
|
}
|
|
49
49
|
let u = k[e]?.versionForPadding;
|
|
50
50
|
u && (P = Math.max(P, i(u).length)), n.publishedAt && (F = !0);
|
|
51
51
|
}
|
|
52
|
-
let I = Math.max(j, l), L = Math.max(M, d), R = Math.max(N, u), z = Math.min(P, f), B = z + 1 + 9, V = E && F ? 6 : 0, H =
|
|
52
|
+
let I = Math.max(j, l), L = Math.max(M, d), R = Math.max(N, u), z = Math.min(P, f), B = z + 1 + 9, V = E && F ? 6 : 0, H = O.keys().toArray().toSorted();
|
|
53
53
|
for (let [e, n] of H.entries()) {
|
|
54
54
|
let r = O.get(n);
|
|
55
55
|
if (!r) {
|
|
@@ -129,7 +129,7 @@ async function p(p, T = {}) {
|
|
|
129
129
|
let e = {
|
|
130
130
|
indicator(e, t) {
|
|
131
131
|
if (t.isGroupLabel) {
|
|
132
|
-
let e = (t.choices ?? []).filter((e) => !("role" in e)), n = e.length, r = e.filter((e) =>
|
|
132
|
+
let e = (t.choices ?? []).filter((e) => !("role" in e)), n = e.length, r = e.filter((e) => e.enabled).length === n ? "●" : "○";
|
|
133
133
|
return ` ${s.gray(r)}`;
|
|
134
134
|
}
|
|
135
135
|
return ` ${t.enabled ? "●" : "○"}`;
|
|
@@ -215,7 +215,7 @@ function S(e) {
|
|
|
215
215
|
return e.targetRef ? e.targetRef : e.latestSha;
|
|
216
216
|
}
|
|
217
217
|
function C() {
|
|
218
|
-
console.info(`\r\
|
|
218
|
+
console.info(`\r\u{1B}[K${s.yellow("Selection cancelled")}`);
|
|
219
219
|
}
|
|
220
220
|
function w(e) {
|
|
221
221
|
return !!S(e);
|
|
@@ -110,7 +110,9 @@ async function p(u = process.cwd(), p = t) {
|
|
|
110
110
|
}
|
|
111
111
|
async function r() {
|
|
112
112
|
if (n.length === 0) return;
|
|
113
|
-
let i = n
|
|
113
|
+
let i = [...n];
|
|
114
|
+
n.length = 0;
|
|
115
|
+
let o = await Promise.all(i.map(async (n) => {
|
|
114
116
|
try {
|
|
115
117
|
let r = s(n, "action.yml"), i = s(n, "action.yaml"), o = r;
|
|
116
118
|
try {
|
|
@@ -33,14 +33,17 @@ async function l(l, d) {
|
|
|
33
33
|
} catch {}
|
|
34
34
|
return null;
|
|
35
35
|
}), _ = await Promise.all(g);
|
|
36
|
-
for (let e of _) if (e)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
for (let e of _) if (e) {
|
|
37
|
+
if (e.type === "workflow") f.workflows.set(e.path, e.actions);
|
|
38
|
+
else {
|
|
39
|
+
let t = o(e.path), n = t === "." || t === "" ? e.path : t;
|
|
40
|
+
f.compositeActions.set(n, e.path);
|
|
41
|
+
}
|
|
42
|
+
f.actions.push(...e.actions);
|
|
40
43
|
}
|
|
41
44
|
return f;
|
|
42
45
|
}
|
|
43
46
|
function u(e, t) {
|
|
44
|
-
return typeof e == "object" && !!e && t
|
|
47
|
+
return typeof e == "object" && !!e && Object.hasOwn(e, t);
|
|
45
48
|
}
|
|
46
49
|
export { l as scanRecursive };
|
package/dist/package.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e = "1.
|
|
1
|
+
var e = "1.15.0";
|
|
2
2
|
export { e as version };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "actions-up",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "Interactive CLI tool to update GitHub Actions with SHA pinning or preserved refs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github-actions",
|
|
@@ -36,14 +36,13 @@
|
|
|
36
36
|
"./dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"cac": "^7.0.0",
|
|
40
39
|
"enquirer": "^2.4.1",
|
|
41
40
|
"nanospinner": "^1.2.2",
|
|
42
41
|
"picocolors": "^1.1.1",
|
|
43
|
-
"semver": "^7.8.
|
|
42
|
+
"semver": "^7.8.5",
|
|
44
43
|
"yaml": "^2.9.0"
|
|
45
44
|
},
|
|
46
45
|
"engines": {
|
|
47
|
-
"node": "^18.
|
|
46
|
+
"node": "^18.3.0 || >=20.0.0"
|
|
48
47
|
}
|
|
49
48
|
}
|
package/readme.md
CHANGED
|
@@ -187,6 +187,16 @@ skipped to avoid changing intentionally floating references. Skipped entries are
|
|
|
187
187
|
listed in the output. To include them in update checks, pass
|
|
188
188
|
`--include-branches`.
|
|
189
189
|
|
|
190
|
+
### Quiet Mode
|
|
191
|
+
|
|
192
|
+
Use `--quiet` (`-q`) to hide the skipped and blocked-update warnings (for
|
|
193
|
+
example, actions intentionally pinned to branches). Other output — results,
|
|
194
|
+
applied updates, and errors — is unchanged.
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npx actions-up --yes --quiet
|
|
198
|
+
```
|
|
199
|
+
|
|
190
200
|
### Update Mode
|
|
191
201
|
|
|
192
202
|
By default, Actions Up allows major updates. Use `--mode` to limit updates:
|