actions-up 1.4.2 ā 1.5.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/index.js +2 -2
- package/dist/core/api/check-updates.js +0 -1
- package/dist/core/ast/update/apply-updates.js +1 -1
- package/dist/core/interactive/prompt-update-selection.js +5 -8
- package/dist/core/scan-github-actions.d.ts +3 -1
- package/dist/core/scan-github-actions.js +39 -50
- package/dist/package.js +1 -1
- package/package.json +1 -1
- package/readme.md +8 -0
package/dist/cli/index.js
CHANGED
|
@@ -11,11 +11,11 @@ import pc from "picocolors";
|
|
|
11
11
|
import cac from "cac";
|
|
12
12
|
function run() {
|
|
13
13
|
let l = cac("actions-up");
|
|
14
|
-
l.help().version(version).option("--dry-run", "Preview changes without applying them").option("--exclude <regex>", "Exclude actions by regex (repeatable)").option("--yes, -y", "Skip all confirmations").command("", "Update GitHub Actions").action(async (s) => {
|
|
14
|
+
l.help().version(version).option("--dir <directory>", "Custom directory name (default: .github)").option("--dry-run", "Preview changes without applying them").option("--exclude <regex>", "Exclude actions by regex (repeatable)").option("--yes, -y", "Skip all confirmations").command("", "Update GitHub Actions").action(async (s) => {
|
|
15
15
|
console.info(pc.cyan("\nš Actions Up!\n"));
|
|
16
16
|
let c = createSpinner("Scanning GitHub Actions...").start();
|
|
17
17
|
try {
|
|
18
|
-
let l = await scanGitHubActions(process.cwd()), u = l.actions.length, d = l.workflows.size, f = l.compositeActions.size;
|
|
18
|
+
let l = await scanGitHubActions(process.cwd(), s.dir), u = l.actions.length, d = l.workflows.size, f = l.compositeActions.size;
|
|
19
19
|
if (c.success(`Found ${pc.yellow(u)} actions in ${pc.yellow(d)} workflows and ${pc.yellow(f)} composite actions`), u === 0) {
|
|
20
20
|
console.info(pc.green("\n⨠No GitHub Actions found in this repository"));
|
|
21
21
|
return;
|
|
@@ -163,7 +163,6 @@ function createUpdate(e, r, i) {
|
|
|
163
163
|
};
|
|
164
164
|
}
|
|
165
165
|
function compareSha(e, n) {
|
|
166
|
-
if (!e || !n) return !1;
|
|
167
166
|
let r = e.replace(/^v/u, ""), i = n.replace(/^v/u, ""), a = Math.min(r.length, i.length);
|
|
168
167
|
return a < 7 ? !1 : r.slice(0, Math.max(0, a)).toLowerCase() === i.slice(0, Math.max(0, a)).toLowerCase();
|
|
169
168
|
}
|
|
@@ -27,7 +27,7 @@ async function applyUpdates(n) {
|
|
|
27
27
|
console.error(`Invalid SHA format: ${e.latestSha}`);
|
|
28
28
|
continue;
|
|
29
29
|
}
|
|
30
|
-
let a = r ? String.raw`(?=[^\S\r\n]|$|#)` : "", o = RegExp(`(
|
|
30
|
+
let a = r ? String.raw`(?=[^\S\r\n]|$|#)` : "", o = new RegExp(String.raw`(^\s*-?\s*uses:\s*)(['"]?)(${n})@${r}\2${a}([^\S\r\n]*#[^\r\n]*)?`, "gm"), s = `$1$2$3@${e.latestSha}$2 # ${e.latestVersion}`;
|
|
31
31
|
i = i.replace(o, s);
|
|
32
32
|
}
|
|
33
33
|
await writeFile(n, i, "utf8");
|
|
@@ -79,16 +79,14 @@ async function promptUpdateSelection(o) {
|
|
|
79
79
|
name: ""
|
|
80
80
|
});
|
|
81
81
|
else {
|
|
82
|
-
let i = l[e - 1];
|
|
83
|
-
if (!i) continue;
|
|
84
|
-
let { update: a, index: s } = i, c = !!a.latestSha, u = c && !a.isBreaking;
|
|
82
|
+
let { update: i, index: a } = l[e - 1], s = !!i.latestSha, c = s && !i.isBreaking;
|
|
85
83
|
h.push({
|
|
86
84
|
message: o,
|
|
87
|
-
value: String(
|
|
88
|
-
name: String(
|
|
89
|
-
disabled: !
|
|
85
|
+
value: String(a),
|
|
86
|
+
name: String(a),
|
|
87
|
+
disabled: !s,
|
|
90
88
|
indent: "",
|
|
91
|
-
enabled:
|
|
89
|
+
enabled: c
|
|
92
90
|
});
|
|
93
91
|
}
|
|
94
92
|
}
|
|
@@ -171,7 +169,6 @@ function formatTableRow(e, i, a) {
|
|
|
171
169
|
].join(" ").replace(/\s+$/u, "");
|
|
172
170
|
}
|
|
173
171
|
function isSha(e) {
|
|
174
|
-
if (!e) return !1;
|
|
175
172
|
let i = e.replace(/^v/u, "");
|
|
176
173
|
return /^[0-9a-f]{7,40}$/iu.test(i);
|
|
177
174
|
}
|
|
@@ -8,10 +8,12 @@ import { ScanResult } from '../types/scan-result';
|
|
|
8
8
|
*
|
|
9
9
|
* @param rootPath - The root path of the repository to scan. Defaults to
|
|
10
10
|
* current working directory.
|
|
11
|
+
* @param ciDirectory - The CI directory name (e.g., '.github' or '.gitea').
|
|
12
|
+
* Defaults to '.github'.
|
|
11
13
|
* @returns A promise that resolves to a ScanResult containing:
|
|
12
14
|
*
|
|
13
15
|
* - Workflows: Map of workflow file paths to their referenced actions
|
|
14
16
|
* - CompositeActions: Map of composite action names to their directory paths
|
|
15
17
|
* - Actions: Flat array of all discovered GitHub Actions.
|
|
16
18
|
*/
|
|
17
|
-
export declare function scanGitHubActions(rootPath?: string): Promise<ScanResult>;
|
|
19
|
+
export declare function scanGitHubActions(rootPath?: string, ciDirectory?: string): Promise<ScanResult>;
|
|
@@ -4,96 +4,85 @@ import { scanActionFile } from "./scan-action-file.js";
|
|
|
4
4
|
import { isYamlFile } from "./fs/is-yaml-file.js";
|
|
5
5
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
6
6
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
7
|
-
async function scanGitHubActions(d = process.cwd()) {
|
|
8
|
-
let
|
|
7
|
+
async function scanGitHubActions(d = process.cwd(), m = GITHUB_DIRECTORY) {
|
|
8
|
+
let h = {
|
|
9
9
|
compositeActions: /* @__PURE__ */ new Map(),
|
|
10
10
|
workflows: /* @__PURE__ */ new Map(),
|
|
11
11
|
actions: []
|
|
12
|
-
},
|
|
13
|
-
function
|
|
12
|
+
}, g = resolve(d);
|
|
13
|
+
function _(e, o) {
|
|
14
14
|
let s = relative(e, o);
|
|
15
15
|
return s !== "" && !s.startsWith("..") && !isAbsolute(s);
|
|
16
16
|
}
|
|
17
|
-
let
|
|
18
|
-
if (!g
|
|
19
|
-
function
|
|
17
|
+
let v = join(g, m);
|
|
18
|
+
if (!_(g, v)) throw Error("Invalid path: detected path traversal attempt");
|
|
19
|
+
function y(e) {
|
|
20
20
|
return e.includes("..") || e.includes("/") || e.includes("\\") ? (console.warn(`Skipping invalid name: ${e}`), !1) : !0;
|
|
21
21
|
}
|
|
22
|
-
let
|
|
23
|
-
if (!g(h, y)) return m;
|
|
22
|
+
let b = join(v, WORKFLOWS_DIRECTORY);
|
|
24
23
|
try {
|
|
25
|
-
if ((await stat(
|
|
26
|
-
let e = (await readdir(
|
|
27
|
-
let
|
|
28
|
-
if (!g(y, l)) return console.warn(`Skipping file outside workflows directory: ${e}`), {
|
|
29
|
-
success: !1,
|
|
30
|
-
actions: [],
|
|
31
|
-
path: ""
|
|
32
|
-
};
|
|
24
|
+
if ((await stat(b)).isDirectory()) {
|
|
25
|
+
let e = (await readdir(b)).filter((e) => y(e) ? isYamlFile(e) : !1).map(async (e) => {
|
|
26
|
+
let o = join(b, e);
|
|
33
27
|
try {
|
|
34
|
-
let
|
|
28
|
+
let l = await scanWorkflowFile(o);
|
|
35
29
|
return {
|
|
36
|
-
path: `${
|
|
30
|
+
path: `${m}/${WORKFLOWS_DIRECTORY}/${e}`,
|
|
37
31
|
success: !0,
|
|
38
|
-
actions:
|
|
32
|
+
actions: l
|
|
39
33
|
};
|
|
40
34
|
} catch {
|
|
41
35
|
return {
|
|
42
|
-
path: `${
|
|
36
|
+
path: `${m}/${WORKFLOWS_DIRECTORY}/${e}`,
|
|
43
37
|
success: !1,
|
|
44
38
|
actions: []
|
|
45
39
|
};
|
|
46
40
|
}
|
|
47
|
-
}),
|
|
48
|
-
for (let e of
|
|
41
|
+
}), o = await Promise.all(e);
|
|
42
|
+
for (let e of o) e.success && e.path && (e.actions.length > 0 ? (h.workflows.set(e.path, e.actions), h.actions.push(...e.actions)) : h.workflows.set(e.path, []));
|
|
49
43
|
}
|
|
50
44
|
} catch {}
|
|
51
|
-
let
|
|
52
|
-
if (!g(h, b)) return m;
|
|
45
|
+
let x = join(v, ACTIONS_DIRECTORY);
|
|
53
46
|
try {
|
|
54
|
-
if ((await stat(
|
|
55
|
-
let
|
|
56
|
-
if (!
|
|
57
|
-
let
|
|
58
|
-
if (!g(b, c)) return console.warn(`Skipping subdirectory outside actions path: ${s}`), null;
|
|
47
|
+
if ((await stat(x)).isDirectory()) {
|
|
48
|
+
let o = (await readdir(x)).map(async (o) => {
|
|
49
|
+
if (!y(o)) return null;
|
|
50
|
+
let s = join(x, o);
|
|
59
51
|
try {
|
|
60
|
-
if (!(await stat(
|
|
61
|
-
let
|
|
62
|
-
if (!g(c, u)) return null;
|
|
63
|
-
let d = [];
|
|
52
|
+
if (!(await stat(s)).isDirectory()) return null;
|
|
53
|
+
let c = join(s, "action.yml"), u = [];
|
|
64
54
|
try {
|
|
65
|
-
|
|
55
|
+
u = await scanActionFile(c);
|
|
66
56
|
} catch {
|
|
67
57
|
try {
|
|
68
|
-
|
|
69
|
-
d = await scanActionFile(u);
|
|
58
|
+
c = join(s, "action.yaml"), u = await scanActionFile(c);
|
|
70
59
|
} catch {
|
|
71
60
|
return null;
|
|
72
61
|
}
|
|
73
62
|
}
|
|
74
63
|
return {
|
|
75
|
-
path: `${
|
|
76
|
-
name:
|
|
77
|
-
actions:
|
|
64
|
+
path: `${m}/${ACTIONS_DIRECTORY}/${o}`,
|
|
65
|
+
name: o,
|
|
66
|
+
actions: u
|
|
78
67
|
};
|
|
79
68
|
} catch {
|
|
80
69
|
return null;
|
|
81
70
|
}
|
|
82
|
-
}),
|
|
83
|
-
for (let e of
|
|
71
|
+
}), s = await Promise.all(o);
|
|
72
|
+
for (let e of s) e && (h.compositeActions.set(e.name, e.path), h.actions.push(...e.actions));
|
|
84
73
|
}
|
|
85
74
|
} catch {}
|
|
86
75
|
try {
|
|
87
|
-
let e = await getCurrentRepoSlug(
|
|
76
|
+
let e = await getCurrentRepoSlug(g);
|
|
88
77
|
if (e) {
|
|
89
78
|
if (process.env.ACTIONS_UP_TEST_THROW === "1") throw Error("test");
|
|
90
79
|
let o = /* @__PURE__ */ new Set(), s = [];
|
|
91
|
-
for (let c of
|
|
80
|
+
for (let c of h.actions) {
|
|
92
81
|
if (c.type !== "external") continue;
|
|
93
82
|
let l = c.name.split("/");
|
|
94
83
|
if (l.length < 3 || `${l[0]}/${l[1]}` !== e) continue;
|
|
95
|
-
let u = join(
|
|
96
|
-
g
|
|
84
|
+
let u = join(g, ...l.slice(2));
|
|
85
|
+
_(g, u) && (o.has(u) || (o.add(u), s.push(u)));
|
|
97
86
|
}
|
|
98
87
|
async function c() {
|
|
99
88
|
if (s.length === 0) return;
|
|
@@ -107,14 +96,14 @@ async function scanGitHubActions(d = process.cwd()) {
|
|
|
107
96
|
d = u;
|
|
108
97
|
}
|
|
109
98
|
let f = await scanActionFile(d);
|
|
110
|
-
f.length > 0 &&
|
|
99
|
+
f.length > 0 && h.actions.push(...f);
|
|
111
100
|
let p = [];
|
|
112
101
|
for (let s of f) {
|
|
113
102
|
if (s.type !== "external") continue;
|
|
114
103
|
let c = s.name.split("/");
|
|
115
104
|
if (c.length < 3 || `${c[0]}/${c[1]}` !== e) continue;
|
|
116
|
-
let l = join(
|
|
117
|
-
g
|
|
105
|
+
let l = join(g, ...c.slice(2));
|
|
106
|
+
_(g, l) && (o.has(l) || (o.add(l), p.push(l)));
|
|
118
107
|
}
|
|
119
108
|
return p;
|
|
120
109
|
} catch {
|
|
@@ -127,7 +116,7 @@ async function scanGitHubActions(d = process.cwd()) {
|
|
|
127
116
|
await c();
|
|
128
117
|
}
|
|
129
118
|
} catch {}
|
|
130
|
-
return
|
|
119
|
+
return h;
|
|
131
120
|
}
|
|
132
121
|
async function getCurrentRepoSlug(e) {
|
|
133
122
|
let o = process.env.GITHUB_REPOSITORY;
|
package/dist/package.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const version = "1.
|
|
1
|
+
const version = "1.5.0";
|
|
2
2
|
export { version };
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -122,6 +122,14 @@ Check for updates without making any changes:
|
|
|
122
122
|
npx actions-up --dry-run
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
+
### Custom Directory
|
|
126
|
+
|
|
127
|
+
By default, Actions Up scans the `.github` directory. You can specify a different directory (e.g., for Gitea):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx actions-up --dir .gitea
|
|
131
|
+
```
|
|
132
|
+
|
|
125
133
|
## GitHub Actions Integration
|
|
126
134
|
|
|
127
135
|
### Automated PR Checks
|