actions-up 1.12.0 → 1.12.1
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/index.d.ts +3 -1
- package/dist/cli/resolve-scan-directories.d.ts +6 -2
- package/dist/core/api/check-updates.js +68 -55
- package/dist/core/api/get-all-releases.d.ts +2 -1
- package/dist/core/api/get-compatible-update.d.ts +15 -5
- package/dist/core/api/get-latest-release.d.ts +3 -3
- package/dist/core/ast/utils/extract-uses-from-steps.d.ts +12 -4
- package/dist/core/constants.d.ts +3 -1
- package/dist/core/interactive/prompt-update-selection.d.ts +3 -1
- package/dist/core/parsing/parse-action-reference.d.ts +16 -12
- package/dist/core/scan-github-actions.d.ts +4 -1
- package/dist/core/versions/get-update-level.d.ts +3 -1
- package/dist/package.js +1 -1
- package/dist/types/action-update.d.ts +30 -10
- package/dist/types/composite-action-runs.d.ts +12 -4
- package/dist/types/composite-action-step.d.ts +24 -8
- package/dist/types/composite-action-structure.d.ts +21 -7
- package/dist/types/github-action.d.ts +27 -9
- package/dist/types/github-client-context.d.ts +24 -8
- package/dist/types/github-client.d.ts +24 -8
- package/dist/types/release-info.d.ts +24 -8
- package/dist/types/scan-result.d.ts +12 -4
- package/dist/types/tag-info.d.ts +15 -5
- package/dist/types/update-mode.d.ts +3 -1
- package/dist/types/workflow-job.d.ts +27 -9
- package/dist/types/workflow-step.d.ts +21 -7
- package/dist/types/workflow-structure.d.ts +15 -5
- package/package.json +3 -8
- package/readme.md +49 -21
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Options for resolving scan directories from CLI flags.
|
|
3
|
+
*/
|
|
2
4
|
interface ResolveScanDirectoriesOptions {
|
|
3
5
|
dir?: string[] | string;
|
|
4
6
|
recursive?: boolean;
|
|
5
7
|
cwd: string;
|
|
6
8
|
}
|
|
7
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Resolved directory with root and relative directory.
|
|
11
|
+
*/
|
|
8
12
|
interface ResolvedDirectory {
|
|
9
13
|
root: string;
|
|
10
14
|
dir: string;
|
|
@@ -2,19 +2,19 @@ import { normalizeVersion } from "../versions/normalize-version.js";
|
|
|
2
2
|
import { isSemverLike } from "../versions/is-semver-like.js";
|
|
3
3
|
import { createGitHubClient } from "./create-github-client.js";
|
|
4
4
|
import semver from "semver";
|
|
5
|
-
async function checkUpdates(i, o,
|
|
6
|
-
let
|
|
7
|
-
if (
|
|
8
|
-
let
|
|
9
|
-
for (let e of
|
|
10
|
-
let t =
|
|
11
|
-
t.push(e),
|
|
5
|
+
async function checkUpdates(i, o, l) {
|
|
6
|
+
let u = l?.client ?? createGitHubClient(o), d = l?.includeBranches ?? !1, f = i.filter((e) => e.type === "external" || e.type === "reusable-workflow");
|
|
7
|
+
if (f.length === 0) return [];
|
|
8
|
+
let p = /* @__PURE__ */ new Map();
|
|
9
|
+
for (let e of f) {
|
|
10
|
+
let t = p.get(e.name) ?? [];
|
|
11
|
+
t.push(e), p.set(e.name, t);
|
|
12
12
|
}
|
|
13
|
-
let
|
|
13
|
+
let m = {
|
|
14
14
|
rateLimitError: null,
|
|
15
15
|
rateLimitHit: !1
|
|
16
|
-
},
|
|
17
|
-
if (
|
|
16
|
+
}, h = await [...p.keys()].reduce((n, i) => n.then(async (n) => {
|
|
17
|
+
if (m.rateLimitHit) return [...n, {
|
|
18
18
|
publishedAt: null,
|
|
19
19
|
version: null,
|
|
20
20
|
actionName: i,
|
|
@@ -27,16 +27,16 @@ async function checkUpdates(i, o, c) {
|
|
|
27
27
|
actionName: i,
|
|
28
28
|
sha: null
|
|
29
29
|
}];
|
|
30
|
-
let [o,
|
|
31
|
-
if (!o || !
|
|
30
|
+
let [o, l] = a;
|
|
31
|
+
if (!o || !l) return [...n, {
|
|
32
32
|
publishedAt: null,
|
|
33
33
|
version: null,
|
|
34
34
|
actionName: i,
|
|
35
35
|
sha: null
|
|
36
36
|
}];
|
|
37
37
|
try {
|
|
38
|
-
let a =
|
|
39
|
-
if (a && !isSha(a) && !isSemverLike(a) && await
|
|
38
|
+
let a = p.get(i)[0]?.version;
|
|
39
|
+
if (a && !isSha(a) && !isSemverLike(a) && await u.getRefType(o, l, a) === "branch" && !d) return [...n, {
|
|
40
40
|
skipReason: "branch",
|
|
41
41
|
status: "skipped",
|
|
42
42
|
publishedAt: null,
|
|
@@ -44,37 +44,39 @@ async function checkUpdates(i, o, c) {
|
|
|
44
44
|
actionName: i,
|
|
45
45
|
sha: null
|
|
46
46
|
}];
|
|
47
|
-
let
|
|
48
|
-
if (!
|
|
49
|
-
let e = await
|
|
50
|
-
|
|
47
|
+
let f = await u.getLatestRelease(o, l);
|
|
48
|
+
if (!f) {
|
|
49
|
+
let e = await u.getAllReleases(o, l, 1);
|
|
50
|
+
f = e.find((e) => !e.isPrerelease) ?? e[0] ?? null;
|
|
51
51
|
}
|
|
52
|
-
if (
|
|
53
|
-
let { publishedAt: a, version: s, sha:
|
|
52
|
+
if (f) {
|
|
53
|
+
let { publishedAt: a, version: s, sha: d } = f, p = !1;
|
|
54
54
|
{
|
|
55
55
|
let n = normalizeVersion(s), i = !!(s && s.trim() !== ""), a = i && /^v?\d+$/u.test(s.trim()), o = semver.valid(n);
|
|
56
|
-
|
|
56
|
+
p = !i || a || !o || !isSemverLike(s);
|
|
57
57
|
}
|
|
58
|
-
if (
|
|
59
|
-
let a = await
|
|
58
|
+
if (p) {
|
|
59
|
+
let a = await u.getAllTags(o, l, 30);
|
|
60
60
|
if (a.length > 0) {
|
|
61
|
-
let
|
|
61
|
+
let d = a.filter((e) => isSemverLike(e.tag)).map((t) => ({
|
|
62
62
|
v: semver.valid(normalizeVersion(t.tag)),
|
|
63
63
|
raw: t
|
|
64
64
|
}));
|
|
65
|
-
if (
|
|
66
|
-
|
|
65
|
+
if (d.length > 0) {
|
|
66
|
+
d.sort((e, t) => {
|
|
67
67
|
let n = semver.rcompare(e.v, t.v);
|
|
68
68
|
if (n !== 0) return n;
|
|
69
69
|
let i = /\d+\.\d+/u.test(e.raw.tag) ? 1 : 0;
|
|
70
70
|
return (/\d+\.\d+/u.test(t.raw.tag) ? 1 : 0) - i;
|
|
71
71
|
});
|
|
72
|
-
let t =
|
|
73
|
-
if (!a || semver.gt(
|
|
72
|
+
let t = d[0].raw, a = semver.valid(normalizeVersion(s) ?? void 0);
|
|
73
|
+
if (!a || semver.gt(d[0].v, a) || semver.eq(d[0].v, a) && /\d+\.\d+/u.test(t.tag)) {
|
|
74
74
|
let e = t.tag, r = t.sha?.length ? t.sha : null;
|
|
75
75
|
if (!r && e) try {
|
|
76
|
-
r = await
|
|
77
|
-
} catch {
|
|
76
|
+
r = await u.getTagSha(o, l, e);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
if (isRateLimitError(e)) throw e;
|
|
79
|
+
}
|
|
78
80
|
return [...n, {
|
|
79
81
|
version: e,
|
|
80
82
|
publishedAt: null,
|
|
@@ -85,20 +87,26 @@ async function checkUpdates(i, o, c) {
|
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
if (s) {
|
|
91
|
+
let e = d;
|
|
92
|
+
try {
|
|
93
|
+
d = await u.getTagSha(o, l, s) ?? e;
|
|
94
|
+
} catch (t) {
|
|
95
|
+
if (isRateLimitError(t)) throw t;
|
|
96
|
+
d = e;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
91
99
|
return [...n, {
|
|
92
100
|
status: "ok",
|
|
93
101
|
publishedAt: a,
|
|
94
102
|
actionName: i,
|
|
95
103
|
version: s,
|
|
96
|
-
sha:
|
|
104
|
+
sha: d
|
|
97
105
|
}];
|
|
98
106
|
}
|
|
99
|
-
let
|
|
100
|
-
if (
|
|
101
|
-
let a =
|
|
107
|
+
let m = await u.getAllTags(o, l, 30);
|
|
108
|
+
if (m.length > 0) {
|
|
109
|
+
let a = m.filter((e) => isSemverLike(e.tag)).map((t) => ({
|
|
102
110
|
v: semver.valid(normalizeVersion(t.tag)),
|
|
103
111
|
raw: t
|
|
104
112
|
})), s;
|
|
@@ -107,17 +115,19 @@ async function checkUpdates(i, o, c) {
|
|
|
107
115
|
if (n !== 0) return n;
|
|
108
116
|
let i = /\d+\.\d+/u.test(e.raw.tag) ? 1 : 0;
|
|
109
117
|
return (/\d+\.\d+/u.test(t.raw.tag) ? 1 : 0) - i;
|
|
110
|
-
}), s = a[0].raw) : s =
|
|
111
|
-
let
|
|
112
|
-
if (!
|
|
113
|
-
|
|
114
|
-
} catch {
|
|
118
|
+
}), s = a[0].raw) : s = m[0];
|
|
119
|
+
let d = s.tag, f = s.sha?.length ? s.sha : null;
|
|
120
|
+
if (!f && d) try {
|
|
121
|
+
f = await u.getTagSha(o, l, d);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
if (isRateLimitError(e)) throw e;
|
|
124
|
+
}
|
|
115
125
|
return [...n, {
|
|
116
126
|
status: "ok",
|
|
117
127
|
publishedAt: null,
|
|
118
128
|
actionName: i,
|
|
119
|
-
version:
|
|
120
|
-
sha:
|
|
129
|
+
version: d,
|
|
130
|
+
sha: f
|
|
121
131
|
}];
|
|
122
132
|
}
|
|
123
133
|
return [...n, {
|
|
@@ -127,7 +137,7 @@ async function checkUpdates(i, o, c) {
|
|
|
127
137
|
sha: null
|
|
128
138
|
}];
|
|
129
139
|
} catch (e) {
|
|
130
|
-
return e instanceof Error && e.name === "GitHubRateLimitError" ? (
|
|
140
|
+
return e instanceof Error && e.name === "GitHubRateLimitError" ? (m.rateLimitHit = !0, m.rateLimitError = e, [...n, {
|
|
131
141
|
publishedAt: null,
|
|
132
142
|
version: null,
|
|
133
143
|
actionName: i,
|
|
@@ -140,12 +150,12 @@ async function checkUpdates(i, o, c) {
|
|
|
140
150
|
}]);
|
|
141
151
|
}
|
|
142
152
|
}), Promise.resolve([]));
|
|
143
|
-
if (
|
|
144
|
-
let e = !!(o ?? process.env.GITHUB_TOKEN), t = `${
|
|
153
|
+
if (m.rateLimitError) {
|
|
154
|
+
let e = !!(o ?? process.env.GITHUB_TOKEN), t = `${m.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);
|
|
145
155
|
throw n.name = "GitHubRateLimitError", n;
|
|
146
156
|
}
|
|
147
|
-
let
|
|
148
|
-
for (let e of
|
|
157
|
+
let g = /* @__PURE__ */ new Map();
|
|
158
|
+
for (let e of h) g.set(e.actionName, {
|
|
149
159
|
publishedAt: e.publishedAt,
|
|
150
160
|
actionName: e.actionName,
|
|
151
161
|
skipReason: e.skipReason,
|
|
@@ -153,23 +163,23 @@ async function checkUpdates(i, o, c) {
|
|
|
153
163
|
status: e.status,
|
|
154
164
|
sha: e.sha
|
|
155
165
|
});
|
|
156
|
-
let
|
|
157
|
-
for (let e of
|
|
158
|
-
let t =
|
|
159
|
-
t ?
|
|
166
|
+
let _ = [];
|
|
167
|
+
for (let e of f) {
|
|
168
|
+
let t = g.get(e.name);
|
|
169
|
+
t ? _.push(createUpdate(e, {
|
|
160
170
|
publishedAt: t.publishedAt,
|
|
161
171
|
version: t.version,
|
|
162
172
|
sha: t.sha
|
|
163
173
|
}, {
|
|
164
174
|
skipReason: t.skipReason,
|
|
165
175
|
status: t.status
|
|
166
|
-
})) :
|
|
176
|
+
})) : _.push(createUpdate(e, {
|
|
167
177
|
publishedAt: null,
|
|
168
178
|
version: null,
|
|
169
179
|
sha: null
|
|
170
180
|
}));
|
|
171
181
|
}
|
|
172
|
-
return
|
|
182
|
+
return _;
|
|
173
183
|
}
|
|
174
184
|
function createUpdate(t, n, i = {}) {
|
|
175
185
|
let { version: a, sha: c, publishedAt: l } = n, u = t.version ?? "unknown", d = normalizeVersion(u), f = a ? normalizeVersion(a) : null, p = i.status ?? "ok", m = i.skipReason, h = !1, g = !1;
|
|
@@ -216,4 +226,7 @@ function isSha(e) {
|
|
|
216
226
|
let t = e.replace(/^v/u, "");
|
|
217
227
|
return /^[0-9a-f]{7,40}$/iu.test(t);
|
|
218
228
|
}
|
|
229
|
+
function isRateLimitError(e) {
|
|
230
|
+
return e instanceof Error && e.name === "GitHubRateLimitError";
|
|
231
|
+
}
|
|
219
232
|
export { checkUpdates };
|
|
@@ -4,7 +4,8 @@ import { ReleaseInfo } from '../../types/release-info';
|
|
|
4
4
|
* Fetch releases for a repository.
|
|
5
5
|
*
|
|
6
6
|
* Resolves SHA only for the first returned release via target_commitish when it
|
|
7
|
-
* looks like a SHA;
|
|
7
|
+
* looks like a SHA; callers can resolve the tag via git refs later when
|
|
8
|
+
* pinning.
|
|
8
9
|
*
|
|
9
10
|
* @param context - Client context.
|
|
10
11
|
* @param parameters - Request parameters.
|
|
@@ -2,15 +2,25 @@ import { GitHubClient } from '../../types/github-client';
|
|
|
2
2
|
import { UpdateMode } from '../../types/update-mode';
|
|
3
3
|
import { TagInfo } from '../../types/tag-info';
|
|
4
4
|
interface GetCompatibleUpdateParameters {
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Optional in-memory cache for resolved tag SHAs.
|
|
7
|
+
*/
|
|
6
8
|
shaCache?: Map<string, string | null>;
|
|
7
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Update mode that limits which tag can be selected.
|
|
11
|
+
*/
|
|
8
12
|
mode: Exclude<UpdateMode, 'major'>;
|
|
9
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Optional in-memory cache for action tags.
|
|
15
|
+
*/
|
|
10
16
|
tagsCache?: Map<string, TagInfo[]>;
|
|
11
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Current action version used as compatibility baseline.
|
|
19
|
+
*/
|
|
12
20
|
currentVersion: string | null;
|
|
13
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Action name in `owner/repo` format (path suffix is allowed).
|
|
23
|
+
*/
|
|
14
24
|
actionName: string;
|
|
15
25
|
}
|
|
16
26
|
/**
|
|
@@ -3,9 +3,9 @@ import { ReleaseInfo } from '../../types/release-info';
|
|
|
3
3
|
/**
|
|
4
4
|
* Fetch the latest release for a repository.
|
|
5
5
|
*
|
|
6
|
-
* If the latest release does not exist (404), returns null. The commit SHA
|
|
7
|
-
* taken from target_commitish
|
|
8
|
-
*
|
|
6
|
+
* If the latest release does not exist (404), returns null. The commit SHA may
|
|
7
|
+
* be taken from target_commitish when it looks like a SHA; callers can resolve
|
|
8
|
+
* the tag via git refs later when pinning.
|
|
9
9
|
*
|
|
10
10
|
* @param context - Client context.
|
|
11
11
|
* @param owner - Repository owner.
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { GitHubAction } from '../../../types/github-action';
|
|
2
2
|
interface ExtractUsesOptions {
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* YAML sequence node containing workflow/action steps.
|
|
5
|
+
*/
|
|
4
6
|
stepsNode: unknown;
|
|
5
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Path of the file being scanned (for metadata).
|
|
9
|
+
*/
|
|
6
10
|
filePath: string;
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Name of the job containing these steps (for workflows).
|
|
13
|
+
*/
|
|
8
14
|
jobName?: string;
|
|
9
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Original YAML file content (for line number calculation).
|
|
17
|
+
*/
|
|
10
18
|
content: string;
|
|
11
19
|
}
|
|
12
20
|
/**
|
package/dist/core/constants.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Constants for directory names used in the project.
|
|
3
|
+
*/
|
|
2
4
|
export declare const GITHUB_DIRECTORY: ".github";
|
|
3
5
|
export declare const WORKFLOWS_DIRECTORY: "workflows";
|
|
4
6
|
export declare const ACTIONS_DIRECTORY: "actions";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ActionUpdate } from '../../types/action-update';
|
|
2
2
|
interface PromptUpdateSelectionOptions {
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Whether to show the Age column.
|
|
5
|
+
*/
|
|
4
6
|
showAge?: boolean;
|
|
5
7
|
}
|
|
6
8
|
export declare function promptUpdateSelection(updates: ActionUpdate[], options?: PromptUpdateSelectionOptions): Promise<ActionUpdate[] | null>;
|
|
@@ -4,18 +4,22 @@ import { GitHubAction } from '../../types/github-action';
|
|
|
4
4
|
* object.
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const action = parseActionReference(
|
|
10
|
+
* 'actions/checkout@v3',
|
|
11
|
+
* 'workflow.yml',
|
|
12
|
+
* 10,
|
|
13
|
+
* )
|
|
14
|
+
* // Returns:
|
|
15
|
+
* // {
|
|
16
|
+
* // type: 'external',
|
|
17
|
+
* // name: 'actions/checkout',
|
|
18
|
+
* // version: 'v3',
|
|
19
|
+
* // file: 'workflow.yml',
|
|
20
|
+
* // line: 10,
|
|
21
|
+
* // }
|
|
22
|
+
* ```
|
|
19
23
|
*
|
|
20
24
|
* @param reference - The action reference string to parse. Can be:
|
|
21
25
|
*
|
|
@@ -4,7 +4,10 @@ import { ScanResult } from '../types/scan-result';
|
|
|
4
4
|
* actions.
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const result = await scanGitHubActions('/path/to/repo')
|
|
10
|
+
* ```
|
|
8
11
|
*
|
|
9
12
|
* @param rootPath - The root path of the repository to scan. Defaults to
|
|
10
13
|
* current working directory.
|
package/dist/package.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const version = "1.12.
|
|
1
|
+
const version = "1.12.1";
|
|
2
2
|
export { version };
|
|
@@ -1,30 +1,50 @@
|
|
|
1
1
|
import { GitHubAction } from './github-action';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Update information for a GitHub Action.
|
|
4
|
+
*/
|
|
3
5
|
export interface ActionUpdate {
|
|
4
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Reason for skipping the update check.
|
|
8
|
+
*/
|
|
5
9
|
skipReason?: 'unknown' | 'branch'
|
|
6
10
|
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Current version string.
|
|
13
|
+
*/
|
|
8
14
|
currentVersion: string | null
|
|
9
15
|
|
|
10
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Latest available version.
|
|
18
|
+
*/
|
|
11
19
|
latestVersion: string | null
|
|
12
20
|
|
|
13
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Status of the check for this action.
|
|
23
|
+
*/
|
|
14
24
|
status?: 'skipped' | 'ok'
|
|
15
25
|
|
|
16
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* SHA hash of the latest version.
|
|
28
|
+
*/
|
|
17
29
|
latestSha: string | null
|
|
18
30
|
|
|
19
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Publication date of the latest version (null if unknown).
|
|
33
|
+
*/
|
|
20
34
|
publishedAt: Date | null
|
|
21
35
|
|
|
22
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* The original action from scanning.
|
|
38
|
+
*/
|
|
23
39
|
action: GitHubAction
|
|
24
40
|
|
|
25
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Whether this is a major version change.
|
|
43
|
+
*/
|
|
26
44
|
isBreaking: boolean
|
|
27
45
|
|
|
28
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Whether an update is available.
|
|
48
|
+
*/
|
|
29
49
|
hasUpdate: boolean
|
|
30
50
|
}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { CompositeActionStep } from './composite-action-step';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Represents the runs configuration for a composite action.
|
|
4
|
+
*/
|
|
3
5
|
export interface CompositeActionRuns {
|
|
4
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Array of steps to execute.
|
|
8
|
+
*/
|
|
5
9
|
steps?: CompositeActionStep[]
|
|
6
10
|
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Allow additional properties.
|
|
13
|
+
*/
|
|
8
14
|
[key: string]: unknown
|
|
9
15
|
|
|
10
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Must be 'composite' for composite actions.
|
|
18
|
+
*/
|
|
11
19
|
using?: string
|
|
12
20
|
}
|
|
@@ -1,23 +1,39 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Represents a step in a composite GitHub Action.
|
|
3
|
+
*/
|
|
2
4
|
export interface CompositeActionStep {
|
|
3
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Environment variables for this step.
|
|
7
|
+
*/
|
|
4
8
|
env?: Record<string, unknown>
|
|
5
9
|
|
|
6
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Working directory for the step.
|
|
12
|
+
*/
|
|
7
13
|
'working-directory'?: string
|
|
8
14
|
|
|
9
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Allow additional properties.
|
|
17
|
+
*/
|
|
10
18
|
[key: string]: unknown
|
|
11
19
|
|
|
12
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Shell to use for the run command.
|
|
22
|
+
*/
|
|
13
23
|
shell?: string
|
|
14
24
|
|
|
15
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Action to use for this step.
|
|
27
|
+
*/
|
|
16
28
|
uses?: string
|
|
17
29
|
|
|
18
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Display name for this step.
|
|
32
|
+
*/
|
|
19
33
|
name?: string
|
|
20
34
|
|
|
21
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Shell command to run for this step.
|
|
37
|
+
*/
|
|
22
38
|
run?: string
|
|
23
39
|
}
|
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
import { CompositeActionRuns } from './composite-action-runs';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Represents the structure of a composite GitHub Action file.
|
|
4
|
+
*/
|
|
3
5
|
export interface CompositeActionStructure {
|
|
4
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Output values from the action.
|
|
8
|
+
*/
|
|
5
9
|
outputs?: Record<string, unknown>
|
|
6
10
|
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Input parameters for the action.
|
|
13
|
+
*/
|
|
8
14
|
inputs?: Record<string, unknown>
|
|
9
15
|
|
|
10
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Runs configuration for composite actions.
|
|
18
|
+
*/
|
|
11
19
|
runs?: CompositeActionRuns
|
|
12
20
|
|
|
13
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Allow additional properties.
|
|
23
|
+
*/
|
|
14
24
|
[key: string]: unknown
|
|
15
25
|
|
|
16
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Description of what the action does.
|
|
28
|
+
*/
|
|
17
29
|
description?: string
|
|
18
30
|
|
|
19
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Display name of the action.
|
|
33
|
+
*/
|
|
20
34
|
name?: string
|
|
21
35
|
}
|
|
@@ -1,26 +1,44 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Represents a GitHub Action used in workflows or composite actions.
|
|
3
|
+
*/
|
|
2
4
|
export interface GitHubAction {
|
|
3
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Type of the GitHub Action.
|
|
7
|
+
*/
|
|
4
8
|
type: 'reusable-workflow' | 'composite' | 'external' | 'docker' | 'local'
|
|
5
9
|
|
|
6
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Version or tag of the action (e.g., 'v1', 'main', commit SHA).
|
|
12
|
+
*/
|
|
7
13
|
version?: string | null
|
|
8
14
|
|
|
9
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Line number where the action is used in the file.
|
|
17
|
+
*/
|
|
10
18
|
line?: number
|
|
11
19
|
|
|
12
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Path to the file where this action is used.
|
|
22
|
+
*/
|
|
13
23
|
file?: string
|
|
14
24
|
|
|
15
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Original `uses` string from workflow, if available.
|
|
27
|
+
*/
|
|
16
28
|
uses?: string
|
|
17
29
|
|
|
18
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Name of the job where this action is used (for workflows).
|
|
32
|
+
*/
|
|
19
33
|
job?: string
|
|
20
34
|
|
|
21
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Full name of the action (e.g., 'actions/checkout').
|
|
37
|
+
*/
|
|
22
38
|
name: string
|
|
23
39
|
|
|
24
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Original `ref` string from workflow, if available.
|
|
42
|
+
*/
|
|
25
43
|
ref?: string
|
|
26
44
|
}
|
|
@@ -6,27 +6,43 @@ import { TagInfo } from './tag-info';
|
|
|
6
6
|
* number of API requests during a single run.
|
|
7
7
|
*/
|
|
8
8
|
export interface GitHubClientContext {
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Lightweight caches keyed by owner/repo (+ extra payload).
|
|
11
|
+
*/
|
|
10
12
|
caches: {
|
|
11
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Cache of reference type detections (branch/tag/null).
|
|
15
|
+
*/
|
|
12
16
|
refType: Map<string, 'branch' | 'tag' | null>
|
|
13
17
|
|
|
14
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Cache of resolved tag metadata (message/date/SHA).
|
|
20
|
+
*/
|
|
15
21
|
tagInfo: Map<string, TagInfo | null>
|
|
16
22
|
|
|
17
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Cache of resolved tag commit SHAs.
|
|
25
|
+
*/
|
|
18
26
|
tagSha: Map<string, string | null>
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Remaining requests available per current rate-limit window.
|
|
31
|
+
*/
|
|
22
32
|
rateLimitRemaining: number
|
|
23
33
|
|
|
24
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* GitHub token, if available.
|
|
36
|
+
*/
|
|
25
37
|
token: undefined | string
|
|
26
38
|
|
|
27
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Scheduled time when rate limit resets.
|
|
41
|
+
*/
|
|
28
42
|
rateLimitReset: Date
|
|
29
43
|
|
|
30
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* GitHub REST API base URL.
|
|
46
|
+
*/
|
|
31
47
|
baseUrl: string
|
|
32
48
|
}
|
|
@@ -8,35 +8,51 @@ import { TagInfo } from './tag-info';
|
|
|
8
8
|
* normalized, serializable data structures.
|
|
9
9
|
*/
|
|
10
10
|
export interface GitHubClient {
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Detect whether a reference is a tag or a branch (or unknown).
|
|
13
|
+
*/
|
|
12
14
|
getRefType(
|
|
13
15
|
owner: string,
|
|
14
16
|
repo: string,
|
|
15
17
|
reference: string,
|
|
16
18
|
): Promise<'branch' | 'tag' | null>
|
|
17
19
|
|
|
18
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* List releases with minimal enrichment.
|
|
22
|
+
*/
|
|
19
23
|
getAllReleases(
|
|
20
24
|
owner: string,
|
|
21
25
|
repo: string,
|
|
22
26
|
limit?: number,
|
|
23
27
|
): Promise<ReleaseInfo[]>
|
|
24
28
|
|
|
25
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Fetch tag metadata (message/date) and the resolved commit SHA.
|
|
31
|
+
*/
|
|
26
32
|
getTagInfo(owner: string, repo: string, tag: string): Promise<TagInfo | null>
|
|
27
33
|
|
|
28
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Resolve commit SHA for a tag without fetching commit data.
|
|
36
|
+
*/
|
|
29
37
|
getTagSha(owner: string, repo: string, tag: string): Promise<string | null>
|
|
30
38
|
|
|
31
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* List repository tags (name + commit SHA).
|
|
41
|
+
*/
|
|
32
42
|
getAllTags(owner: string, repo: string, limit?: number): Promise<TagInfo[]>
|
|
33
43
|
|
|
34
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Fetch the latest release or null when no latest release exists.
|
|
46
|
+
*/
|
|
35
47
|
getLatestRelease(owner: string, repo: string): Promise<ReleaseInfo | null>
|
|
36
48
|
|
|
37
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Current rate limit snapshot.
|
|
51
|
+
*/
|
|
38
52
|
getRateLimitStatus(): { remaining: number; resetAt: Date }
|
|
39
53
|
|
|
40
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* True when remaining requests are below a threshold.
|
|
56
|
+
*/
|
|
41
57
|
shouldWaitForRateLimit(threshold?: number): boolean
|
|
42
58
|
}
|
|
@@ -1,23 +1,39 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Normalized release information used across the tool.
|
|
3
|
+
*/
|
|
2
4
|
export interface ReleaseInfo {
|
|
3
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Release description (body) or null when absent.
|
|
7
|
+
*/
|
|
4
8
|
description: string | null
|
|
5
9
|
|
|
6
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* True when the release is marked as prerelease.
|
|
12
|
+
*/
|
|
7
13
|
isPrerelease: boolean
|
|
8
14
|
|
|
9
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Commit SHA associated with the release tag when known (may be provisional).
|
|
17
|
+
*/
|
|
10
18
|
sha: string | null
|
|
11
19
|
|
|
12
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Publication date of the release.
|
|
22
|
+
*/
|
|
13
23
|
publishedAt: Date
|
|
14
24
|
|
|
15
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Tag name (e.g. V1.2.3).
|
|
27
|
+
*/
|
|
16
28
|
version: string
|
|
17
29
|
|
|
18
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Release name or tag name when name is not provided.
|
|
32
|
+
*/
|
|
19
33
|
name: string
|
|
20
34
|
|
|
21
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* HTML URL of the release page.
|
|
37
|
+
*/
|
|
22
38
|
url: string
|
|
23
39
|
}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { GitHubAction } from './github-action';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Result of scanning a repository for GitHub Actions usage.
|
|
4
|
+
*/
|
|
3
5
|
export interface ScanResult {
|
|
4
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Map of workflow files to their used GitHub Actions.
|
|
8
|
+
*/
|
|
5
9
|
workflows: Map<string, GitHubAction[]>
|
|
6
10
|
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Map of composite action names to their file paths.
|
|
13
|
+
*/
|
|
8
14
|
compositeActions: Map<string, string>
|
|
9
15
|
|
|
10
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* List of all unique GitHub Actions found in the repository.
|
|
18
|
+
*/
|
|
11
19
|
actions: GitHubAction[]
|
|
12
20
|
}
|
package/dist/types/tag-info.d.ts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Normalized tag information (message/date) and the resolved commit SHA.
|
|
3
|
+
*/
|
|
2
4
|
export interface TagInfo {
|
|
3
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Tag or commit message, null when absent.
|
|
7
|
+
*/
|
|
4
8
|
message: string | null
|
|
5
9
|
|
|
6
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Commit SHA the tag ultimately points to (may be null).
|
|
12
|
+
*/
|
|
7
13
|
sha: string | null
|
|
8
14
|
|
|
9
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Date associated with the tag (from release, tagger or commit).
|
|
17
|
+
*/
|
|
10
18
|
date: Date | null
|
|
11
19
|
|
|
12
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Tag name (e.g. V1.2.3).
|
|
22
|
+
*/
|
|
13
23
|
tag: string
|
|
14
24
|
}
|
|
@@ -1,27 +1,45 @@
|
|
|
1
1
|
import { WorkflowStep } from './workflow-step';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Represents a job in a GitHub Actions workflow.
|
|
4
|
+
*/
|
|
3
5
|
export interface WorkflowJob {
|
|
4
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Secrets passed to the reusable workflow ('inherit' or specific secrets).
|
|
8
|
+
*/
|
|
5
9
|
secrets?: Record<string, unknown> | 'inherit'
|
|
6
10
|
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Input parameters passed to the reusable workflow.
|
|
13
|
+
*/
|
|
8
14
|
with?: Record<string, unknown>
|
|
9
15
|
|
|
10
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Runner environment(s) to execute this job on (e.g., 'ubuntu-latest').
|
|
18
|
+
*/
|
|
11
19
|
'runs-on'?: string[] | string
|
|
12
20
|
|
|
13
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Job IDs that must complete successfully before this job runs.
|
|
23
|
+
*/
|
|
14
24
|
needs?: string[] | string
|
|
15
25
|
|
|
16
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Array of steps to execute in this job.
|
|
28
|
+
*/
|
|
17
29
|
steps?: WorkflowStep[]
|
|
18
30
|
|
|
19
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Allow additional properties for job configuration.
|
|
33
|
+
*/
|
|
20
34
|
[key: string]: unknown
|
|
21
35
|
|
|
22
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Reusable workflow reference (mutually exclusive with 'steps').
|
|
38
|
+
*/
|
|
23
39
|
uses?: string
|
|
24
40
|
|
|
25
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Conditional expression to determine if the job should run.
|
|
43
|
+
*/
|
|
26
44
|
if?: string
|
|
27
45
|
}
|
|
@@ -1,20 +1,34 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Represents a single step in a GitHub Actions workflow job.
|
|
3
|
+
*/
|
|
2
4
|
export interface WorkflowStep {
|
|
3
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Input parameters to pass to the action.
|
|
7
|
+
*/
|
|
4
8
|
with?: Record<string, unknown>
|
|
5
9
|
|
|
6
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Environment variables to set for this step.
|
|
12
|
+
*/
|
|
7
13
|
env?: Record<string, unknown>
|
|
8
14
|
|
|
9
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Allow additional properties for step configuration.
|
|
17
|
+
*/
|
|
10
18
|
[key: string]: unknown
|
|
11
19
|
|
|
12
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Action to use for this step (e.g., 'actions/checkout@v4').
|
|
22
|
+
*/
|
|
13
23
|
uses?: string
|
|
14
24
|
|
|
15
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Display name for this step.
|
|
27
|
+
*/
|
|
16
28
|
name?: string
|
|
17
29
|
|
|
18
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Shell command to run for this step.
|
|
32
|
+
*/
|
|
19
33
|
run?: string
|
|
20
34
|
}
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import { WorkflowJob } from './workflow-job';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Represents the root structure of a GitHub Actions workflow file.
|
|
4
|
+
*/
|
|
3
5
|
export interface WorkflowStructure {
|
|
4
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Map of job IDs to job configurations.
|
|
8
|
+
*/
|
|
5
9
|
jobs?: Record<string, WorkflowJob>
|
|
6
10
|
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Allow additional properties for workflow configuration.
|
|
13
|
+
*/
|
|
8
14
|
[key: string]: unknown
|
|
9
15
|
|
|
10
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Display name for the workflow.
|
|
18
|
+
*/
|
|
11
19
|
name?: string
|
|
12
20
|
|
|
13
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Events that trigger the workflow (push, pull_request, etc.).
|
|
23
|
+
*/
|
|
14
24
|
on?: unknown
|
|
15
25
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "actions-up",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"description": "Interactive CLI tool to update GitHub Actions to latest versions with SHA pinning",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github-actions",
|
|
@@ -36,19 +36,14 @@
|
|
|
36
36
|
"./dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"cac": "^
|
|
39
|
+
"cac": "^7.0.0",
|
|
40
40
|
"enquirer": "^2.4.1",
|
|
41
41
|
"nanospinner": "^1.2.2",
|
|
42
42
|
"picocolors": "^1.1.1",
|
|
43
43
|
"semver": "^7.7.4",
|
|
44
|
-
"yaml": "^2.8.
|
|
44
|
+
"yaml": "^2.8.3"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": "^18.0.0 || >=20.0.0"
|
|
48
|
-
},
|
|
49
|
-
"pnpm": {
|
|
50
|
-
"overrides": {
|
|
51
|
-
"vite": "npm:rolldown-vite@latest"
|
|
52
|
-
}
|
|
53
48
|
}
|
|
54
49
|
}
|
package/readme.md
CHANGED
|
@@ -12,15 +12,21 @@
|
|
|
12
12
|
[](https://codecov.io/gh/azat-io/actions-up)
|
|
13
13
|
[](https://github.com/azat-io/actions-up/blob/main/license.md)
|
|
14
14
|
|
|
15
|
-
Actions Up scans your workflows and composite actions to discover every
|
|
15
|
+
Actions Up scans your workflows and composite actions to discover every
|
|
16
|
+
referenced GitHub Action, then checks for newer releases.
|
|
16
17
|
|
|
17
|
-
Interactively upgrade and pin actions to exact commit SHAs for secure,
|
|
18
|
+
Interactively upgrade and pin actions to exact commit SHAs for secure,
|
|
19
|
+
reproducible CI and low-friction maintenance.
|
|
18
20
|
|
|
19
21
|
## Features
|
|
20
22
|
|
|
21
|
-
- **Auto-discovery**: Scans all workflows (`.github/workflows/*.yml`) and
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
- **Auto-discovery**: Scans all workflows (`.github/workflows/*.yml`) and
|
|
24
|
+
composite actions (`.github/actions/*/action.yml` and root
|
|
25
|
+
`action.yml`/`action.yaml`)
|
|
26
|
+
- **Reusable Workflows**: Detects and updates reusable workflow calls at the job
|
|
27
|
+
level
|
|
28
|
+
- **SHA pinning**: Updates actions to use commit SHA instead of tags for better
|
|
29
|
+
security
|
|
24
30
|
- **Batch Updates**: Update multiple actions at once
|
|
25
31
|
- **Interactive Selection**: Choose which actions to update
|
|
26
32
|
- **Breaking Changes Detection**: Warns about major version updates
|
|
@@ -49,7 +55,9 @@ Interactively upgrade and pin actions to exact commit SHAs for secure, reproduci
|
|
|
49
55
|
|
|
50
56
|
## Why
|
|
51
57
|
|
|
52
|
-
Keeping GitHub Actions updated is critical and time-consuming. Actions Up scans
|
|
58
|
+
Keeping GitHub Actions updated is critical and time-consuming. Actions Up scans
|
|
59
|
+
all workflows, highlights available updates, and can pin actions to SHAs for
|
|
60
|
+
reproducibility.
|
|
53
61
|
|
|
54
62
|
| Without Actions Up | With Actions Up |
|
|
55
63
|
| :----------------------------- | :------------------------------- |
|
|
@@ -59,7 +67,10 @@ Keeping GitHub Actions updated is critical and time-consuming. Actions Up scans
|
|
|
59
67
|
|
|
60
68
|
### Security Motivation
|
|
61
69
|
|
|
62
|
-
GitHub Actions run arbitrary code in your CI. If a job has secrets available,
|
|
70
|
+
GitHub Actions run arbitrary code in your CI. If a job has secrets available,
|
|
71
|
+
any action used in that job can read the environment and exfiltrate those
|
|
72
|
+
secrets. A compromised action or a mutable version tag is a direct path to
|
|
73
|
+
leakage.
|
|
63
74
|
|
|
64
75
|
Actions Up reduces risk by:
|
|
65
76
|
|
|
@@ -67,7 +78,9 @@ Actions Up reduces risk by:
|
|
|
67
78
|
- Making outdated actions visible and showing exactly what runs in CI
|
|
68
79
|
- Warning about major updates so you can review changes before applying them
|
|
69
80
|
|
|
70
|
-
Note: secrets are available on `push`, `workflow_dispatch`, `schedule`, and
|
|
81
|
+
Note: secrets are available on `push`, `workflow_dispatch`, `schedule`, and
|
|
82
|
+
`pull_request_target` triggers (and on fork PRs if explicitly enabled). Always
|
|
83
|
+
scope workflow permissions to the minimum required.
|
|
71
84
|
|
|
72
85
|
## Installation
|
|
73
86
|
|
|
@@ -107,7 +120,8 @@ npx actions-up
|
|
|
107
120
|
|
|
108
121
|
This will:
|
|
109
122
|
|
|
110
|
-
1. Scan all `.github/workflows/*.yml` and `.github/actions/*/action.yml` files,
|
|
123
|
+
1. Scan all `.github/workflows/*.yml` and `.github/actions/*/action.yml` files,
|
|
124
|
+
plus root `action.yml`/`action.yaml`
|
|
111
125
|
2. Check for available updates
|
|
112
126
|
3. Show an interactive list to select updates
|
|
113
127
|
4. Apply selected updates with SHA pinning
|
|
@@ -134,7 +148,8 @@ npx actions-up --dry-run
|
|
|
134
148
|
|
|
135
149
|
By default, Actions Up scans `.github`.
|
|
136
150
|
|
|
137
|
-
Use `--dir` to choose another directory, and pass it multiple times to scan
|
|
151
|
+
Use `--dir` to choose another directory, and pass it multiple times to scan
|
|
152
|
+
several directories:
|
|
138
153
|
|
|
139
154
|
```bash
|
|
140
155
|
npx actions-up --dir .gitea
|
|
@@ -143,18 +158,23 @@ npx actions-up --dir .github --dir ./other/.github
|
|
|
143
158
|
|
|
144
159
|
### Recursive Scanning
|
|
145
160
|
|
|
146
|
-
Use `--recursive` (`-r`) to scan YAML workflow/composite-action files
|
|
161
|
+
Use `--recursive` (`-r`) to scan YAML workflow/composite-action files
|
|
162
|
+
recursively in the selected directories:
|
|
147
163
|
|
|
148
164
|
```bash
|
|
149
165
|
npx actions-up -r
|
|
150
166
|
npx actions-up --dir ./gh-repo-defaults -r
|
|
151
167
|
```
|
|
152
168
|
|
|
153
|
-
When `--recursive` is used without `--dir`, Actions Up scans from the current
|
|
169
|
+
When `--recursive` is used without `--dir`, Actions Up scans from the current
|
|
170
|
+
directory (`.`).
|
|
154
171
|
|
|
155
172
|
### Branch References
|
|
156
173
|
|
|
157
|
-
By default, actions pinned to branch refs (e.g., `@main`, `@release/v1`) are
|
|
174
|
+
By default, actions pinned to branch refs (e.g., `@main`, `@release/v1`) are
|
|
175
|
+
skipped to avoid changing intentionally floating references. Skipped entries are
|
|
176
|
+
listed in the output. To include them in update checks, pass
|
|
177
|
+
`--include-branches`.
|
|
158
178
|
|
|
159
179
|
### Update Mode
|
|
160
180
|
|
|
@@ -165,15 +185,17 @@ npx actions-up --mode minor
|
|
|
165
185
|
npx actions-up --mode patch
|
|
166
186
|
```
|
|
167
187
|
|
|
168
|
-
In `minor` and `patch` modes, Actions Up tries to find the newest compatible
|
|
169
|
-
|
|
188
|
+
In `minor` and `patch` modes, Actions Up tries to find the newest compatible tag
|
|
189
|
+
first (for example, from `@v4` in `minor` mode it will choose the latest
|
|
170
190
|
`v4.x.y`). If no compatible version exists, that action is skipped.
|
|
171
191
|
|
|
172
192
|
## GitHub Actions Integration
|
|
173
193
|
|
|
174
194
|
### Automated PR Checks
|
|
175
195
|
|
|
176
|
-
You can integrate Actions Up into your CI/CD pipeline to automatically check for
|
|
196
|
+
You can integrate Actions Up into your CI/CD pipeline to automatically check for
|
|
197
|
+
outdated actions on every pull request. This helps maintain security and ensures
|
|
198
|
+
your team stays aware of available updates.
|
|
177
199
|
|
|
178
200
|
<details>
|
|
179
201
|
<summary>Create <code>.github/workflows/check-actions-updates.yml</code>.</summary>
|
|
@@ -295,7 +317,9 @@ jobs:
|
|
|
295
317
|
fi
|
|
296
318
|
|
|
297
319
|
- name: Comment PR with updates
|
|
298
|
-
if:
|
|
320
|
+
if:
|
|
321
|
+
github.event_name == 'pull_request' &&
|
|
322
|
+
github.event.pull_request.head.repo.full_name == github.repository
|
|
299
323
|
uses: actions/github-script@v7
|
|
300
324
|
with:
|
|
301
325
|
script: |
|
|
@@ -433,7 +457,8 @@ jobs:
|
|
|
433
457
|
|
|
434
458
|
### GitHub Token
|
|
435
459
|
|
|
436
|
-
Use `GITHUB_TOKEN` (or a PAT) to raise API rate limits from 60 to 5000
|
|
460
|
+
Use `GITHUB_TOKEN` (or a PAT) to raise API rate limits from 60 to 5000
|
|
461
|
+
requests/hour.
|
|
437
462
|
|
|
438
463
|
```bash
|
|
439
464
|
GITHUB_TOKEN=your_token_here npx actions-up
|
|
@@ -479,14 +504,17 @@ Ignore comments (file/block/next-line/inline):
|
|
|
479
504
|
|
|
480
505
|
Interactive CLI for developers who want control over GitHub Actions updates.
|
|
481
506
|
|
|
482
|
-
- **vs. Dependabot/Renovate:** Dependabot and Renovate update via pull requests;
|
|
483
|
-
|
|
507
|
+
- **vs. Dependabot/Renovate:** Dependabot and Renovate update via pull requests;
|
|
508
|
+
Actions Up is an interactive CLI with explicit SHA pinning.
|
|
509
|
+
- **vs. pinact:** pinact is a CLI to pin and update Actions and reusable
|
|
510
|
+
workflows; Actions Up adds interactive selection and major update warnings.
|
|
484
511
|
- **Zero-config:** `npx actions-up` runs immediately.
|
|
485
512
|
- **Breaking change warnings:** Major updates are flagged before applying.
|
|
486
513
|
|
|
487
514
|
## Contributing
|
|
488
515
|
|
|
489
|
-
See
|
|
516
|
+
See
|
|
517
|
+
[Contributing Guide](https://github.com/azat-io/actions-up/blob/main/contributing.md).
|
|
490
518
|
|
|
491
519
|
## License
|
|
492
520
|
|