actions-up 1.12.1 → 1.14.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.
@@ -4,60 +4,60 @@ import { GITHUB_DIRECTORY } from "../constants.js";
4
4
  import { isSha } from "../versions/is-sha.js";
5
5
  import { stripAnsi } from "./strip-ansi.js";
6
6
  import { padString } from "./pad-string.js";
7
+ import path from "node:path";
7
8
  import "node:worker_threads";
8
9
  import pc from "picocolors";
9
10
  import enquirer from "enquirer";
10
- import path from "node:path";
11
11
  var MIN_ACTION_WIDTH = 40, MIN_JOB_WIDTH = 4, MIN_CURRENT_WIDTH = 16, MAX_VERSION_WIDTH = 7;
12
- async function promptUpdateSelection(g, v = {}) {
13
- let { showAge: y = !1 } = v;
14
- if (g.length === 0) return null;
15
- let b = g.filter((t) => t.hasUpdate);
16
- if (b.length === 0) return console.info(pc.green("✓ All actions are up to date!")), null;
17
- let x = /* @__PURE__ */ new Map();
18
- for (let [t, i] of b.entries()) {
19
- let o = i.action.file ?? "unknown file", s = path.relative(path.join(process.cwd(), GITHUB_DIRECTORY), o);
20
- s === "" && (s = o);
21
- let c = x.get(s) ?? [];
22
- c.push({
23
- update: i,
24
- index: t
25
- }), x.set(s, c);
12
+ async function promptUpdateSelection(b, S = {}) {
13
+ let { showAge: C = !1 } = S;
14
+ if (b.length === 0) return null;
15
+ let w = b.filter((e) => e.hasUpdate);
16
+ if (w.length === 0) return console.info(pc.green("✓ All actions are up to date!")), null;
17
+ let T = /* @__PURE__ */ new Map();
18
+ for (let [e, c] of w.entries()) {
19
+ let u = c.action.file ?? "unknown file", d = path.relative(path.join(process.cwd(), GITHUB_DIRECTORY), u);
20
+ d === "" && (d = u);
21
+ let f = T.get(d) ?? [];
22
+ f.push({
23
+ update: c,
24
+ index: e
25
+ }), T.set(d, f);
26
26
  }
27
- let S = await Promise.all(b.map(async (i) => {
28
- let a = formatVersionOrSha(i.currentVersion), s = i.currentVersion ?? void 0, c = null, l = null;
29
- if (!i.currentVersion || !isSha(i.currentVersion)) return {
30
- versionForPadding: c,
31
- effectiveForDiff: s,
32
- shortSha: l,
33
- display: a
27
+ let E = await Promise.all(w.map(async (c) => {
28
+ let l = formatVersionOrSha(c.currentVersion), d = c.currentVersion ?? void 0, f = null, p = null;
29
+ if (!c.currentVersion || !isSha(c.currentVersion)) return {
30
+ versionForPadding: f,
31
+ effectiveForDiff: d,
32
+ shortSha: p,
33
+ display: l
34
34
  };
35
- let u = await readInlineVersionComment(i.action.file, i.action.line);
36
- return u && (l = i.currentVersion.slice(0, 7), c = formatVersionOrSha(u), a = c, s = u), {
37
- versionForPadding: c,
38
- effectiveForDiff: s,
39
- shortSha: l,
40
- display: a
35
+ let m = await readInlineVersionComment(c.action.file, c.action.line);
36
+ return m && (p = c.currentVersion.slice(0, 7), f = formatVersionOrSha(m), l = f, d = m), {
37
+ versionForPadding: f,
38
+ effectiveForDiff: d,
39
+ shortSha: p,
40
+ display: l
41
41
  };
42
- })), C = [], w = stripAnsi("Action").length, T = stripAnsi("Current").length, E = stripAnsi("Job").length, D = 0, O = !1;
43
- for (let [t, a] of b.entries()) {
44
- let o = a.action.name, u = S[t], d = u.display, f = a.action.job ?? "–";
45
- if (w = Math.max(w, o.length), T = Math.max(T, stripAnsi(d).length, u.versionForPadding && u.shortSha ? stripAnsi(`${padString(u.versionForPadding, D + 1)}${pc.gray(`(${u.shortSha})`)}`).length : 0), E = Math.max(E, f.length), a.latestVersion) {
46
- let o = formatVersion(a.latestVersion, S[t]?.effectiveForDiff ?? a.currentVersion);
47
- D = Math.max(D, stripAnsi(o).length);
42
+ })), D = [], O = stripAnsi("Action").length, k = stripAnsi("Current").length, A = stripAnsi("Job").length, j = 0, M = !1;
43
+ for (let [e, l] of w.entries()) {
44
+ let u = l.action.name, p = E[e], h = p.display, g = l.action.job ?? "–";
45
+ if (O = Math.max(O, u.length), k = Math.max(k, stripAnsi(h).length, p.versionForPadding && p.shortSha ? stripAnsi(`${padString(p.versionForPadding, j + 1)}${pc.gray(`(${p.shortSha})`)}`).length : 0), A = Math.max(A, g.length), l.latestVersion) {
46
+ let u = formatVersion(l.targetRefStyle === "tag" && l.targetRef ? l.targetRef : l.latestVersion, E[e]?.effectiveForDiff ?? l.currentVersion);
47
+ j = Math.max(j, stripAnsi(u).length);
48
48
  }
49
- let p = S[t]?.versionForPadding;
50
- p && (D = Math.max(D, stripAnsi(p).length)), a.publishedAt && (O = !0);
49
+ let _ = E[e]?.versionForPadding;
50
+ _ && (j = Math.max(j, stripAnsi(_).length)), l.publishedAt && (M = !0);
51
51
  }
52
- let k = Math.max(w, MIN_ACTION_WIDTH), A = Math.max(T, MIN_CURRENT_WIDTH), j = Math.max(E, MIN_JOB_WIDTH), M = Math.min(D, MAX_VERSION_WIDTH), N = M + 1 + 9, P = y && O ? 6 : 0, F = [...x.keys()].toSorted();
53
- for (let [t, a] of F.entries()) {
54
- let o = x.get(a);
55
- if (!o) {
56
- console.warn(`Unexpected missing group for file: ${a}`);
52
+ let N = Math.max(O, MIN_ACTION_WIDTH), P = Math.max(k, MIN_CURRENT_WIDTH), F = Math.max(A, MIN_JOB_WIDTH), I = Math.min(j, MAX_VERSION_WIDTH), L = I + 1 + 9, R = C && M ? 6 : 0, z = [...T.keys()].toSorted();
53
+ for (let [e, l] of z.entries()) {
54
+ let u = T.get(l);
55
+ if (!u) {
56
+ console.warn(`Unexpected missing group for file: ${l}`);
57
57
  continue;
58
58
  }
59
- let s = [], u = o;
60
- s.push({
59
+ let d = [], p = u;
60
+ d.push({
61
61
  current: "Current",
62
62
  action: "Action",
63
63
  target: "Target",
@@ -65,74 +65,74 @@ async function promptUpdateSelection(g, v = {}) {
65
65
  job: "Job",
66
66
  age: "Age"
67
67
  });
68
- for (let { update: t, index: a } of u) {
69
- let o = !!t.latestSha, u = S[a], d = u.display;
70
- u.versionForPadding && u.shortSha && (d = `${padString(u.versionForPadding, M + 1)}${pc.gray(`(${u.shortSha})`)}`);
71
- let f = u.effectiveForDiff ?? t.currentVersion, p = formatVersion(t.latestVersion, f), m = t.action.name;
72
- if (t.latestSha) {
73
- let i = t.latestSha.slice(0, 7);
74
- p = `${padString(p, M + 1)}${pc.gray(`(${i})`)}`;
68
+ for (let { update: e, index: l } of p) {
69
+ let u = hasResolvedTarget(e), p = E[l], h = p.display;
70
+ p.versionForPadding && p.shortSha && (h = `${padString(p.versionForPadding, I + 1)}${pc.gray(`(${p.shortSha})`)}`);
71
+ let g = p.effectiveForDiff ?? e.currentVersion, _ = formatVersion(getTargetVersion(e), g), v = e.action.name;
72
+ if (getResolvedTargetStyle(e) === "sha" && getResolvedTarget(e)) {
73
+ let c = getResolvedTarget(e).slice(0, 7);
74
+ _ = `${padString(_, I + 1)}${pc.gray(`(${c})`)}`;
75
75
  }
76
- o || (p = pc.gray(p), d = pc.gray(d), m = pc.gray(m));
77
- let h = t.action.job ?? "–", g = formatAge(t.publishedAt);
78
- s.push({
79
- job: o ? h : pc.gray(h),
80
- age: o ? g : pc.gray(g),
81
- action: m,
82
- target: p,
76
+ u || (_ = pc.gray(_), h = pc.gray(h), v = pc.gray(v));
77
+ let y = e.action.job ?? "–", b = formatAge(e.publishedAt);
78
+ d.push({
79
+ job: u ? y : pc.gray(y),
80
+ age: u ? b : pc.gray(b),
81
+ action: v,
82
+ target: _,
83
83
  arrow: "❯",
84
- current: d
84
+ current: h
85
85
  });
86
86
  }
87
- let d = Math.max(k, MIN_ACTION_WIDTH), h = Math.max(A, MIN_CURRENT_WIDTH), g = Math.max(j, MIN_JOB_WIDTH), _ = [];
88
- for (let [t, i] of s.entries()) {
89
- let a = t === 0, o = formatTableRow({
90
- targetWidth: N,
91
- currentWidth: h,
92
- actionWidth: d,
93
- ageWidth: P,
94
- jobWidth: g,
95
- row: i
87
+ let h = Math.max(N, MIN_ACTION_WIDTH), y = Math.max(P, MIN_CURRENT_WIDTH), b = Math.max(F, MIN_JOB_WIDTH), x = [];
88
+ for (let [e, c] of d.entries()) {
89
+ let l = e === 0, u = formatTableRow({
90
+ targetWidth: L,
91
+ currentWidth: y,
92
+ actionWidth: h,
93
+ ageWidth: R,
94
+ jobWidth: b,
95
+ row: c
96
96
  });
97
- if (a) _.push({
98
- message: pc.gray(` ○ ${o}`),
97
+ if (l) x.push({
98
+ message: pc.gray(` ○ ${u}`),
99
99
  role: "separator",
100
100
  indent: "",
101
101
  name: ""
102
102
  });
103
103
  else {
104
- let { update: i, index: a } = u[t - 1], s = !!i.latestSha, c = s && !i.isBreaking;
105
- _.push({
106
- message: o,
107
- value: String(a),
108
- name: String(a),
109
- disabled: !s,
104
+ let { update: c, index: l } = p[e - 1], d = hasResolvedTarget(c), f = d && !c.isBreaking;
105
+ x.push({
106
+ message: u,
107
+ value: String(l),
108
+ disabled: !d,
109
+ name: String(l),
110
110
  indent: "",
111
- enabled: c
111
+ enabled: f
112
112
  });
113
113
  }
114
114
  }
115
- C.push({
116
- message: pc.gray(a),
117
- value: `label|${a}`,
118
- choices: _,
119
- name: `label|${a}`,
115
+ D.push({
116
+ message: pc.gray(l),
117
+ value: `label|${l}`,
118
+ choices: x,
119
+ name: `label|${l}`,
120
120
  isGroupLabel: !0,
121
121
  enabled: !1
122
- }), t < F.length - 1 && C.push({
122
+ }), e < z.length - 1 && D.push({
123
123
  role: "separator",
124
124
  message: " ",
125
125
  name: ""
126
126
  });
127
127
  }
128
128
  try {
129
- let t = {
130
- indicator(t, i) {
131
- if (i.isGroupLabel) {
132
- let t = (i.choices ?? []).filter((t) => !("role" in t)), a = t.length, o = t.filter((t) => !!t.enabled).length === a ? "●" : "○";
133
- return ` ${pc.gray(o)}`;
129
+ let e = {
130
+ indicator(e, c) {
131
+ if (c.isGroupLabel) {
132
+ let e = (c.choices ?? []).filter((e) => !("role" in e)), l = e.length, u = e.filter((e) => !!e.enabled).length === l ? "●" : "○";
133
+ return ` ${pc.gray(u)}`;
134
134
  }
135
- return ` ${i.enabled ? "●" : "○"}`;
135
+ return ` ${c.enabled ? "●" : "○"}`;
136
136
  },
137
137
  message: `Choose which actions to update (Press ${pc.cyan("<space>")} to select, ${pc.cyan("<a>")} to toggle all, ${pc.cyan("<i>")} to invert selection)`,
138
138
  styles: {
@@ -153,44 +153,56 @@ async function promptUpdateSelection(g, v = {}) {
153
153
  type: "multiselect",
154
154
  name: "selected",
155
155
  pointer: "❯",
156
- choices: C
157
- }, { selected: i } = await enquirer.prompt(t), a = /* @__PURE__ */ new Set();
158
- for (let t of i) {
159
- if (t.startsWith("label|")) {
160
- let i = t.slice(6), o = x.get(i) ?? [];
161
- for (let { update: t, index: i } of o) t.latestSha && a.add(i);
156
+ choices: D
157
+ }, { selected: c } = await enquirer.prompt(e), l = /* @__PURE__ */ new Set();
158
+ for (let e of c) {
159
+ if (e.startsWith("label|")) {
160
+ let c = e.slice(6), u = T.get(c) ?? [];
161
+ for (let { update: e, index: c } of u) hasResolvedTarget(e) && l.add(c);
162
162
  continue;
163
163
  }
164
- let i = Number.parseInt(t, 10);
165
- Number.isFinite(i) && a.add(i);
164
+ let c = Number.parseInt(e, 10);
165
+ Number.isFinite(c) && l.add(c);
166
166
  }
167
- let o = [];
168
- for (let [t, i] of b.entries()) a.has(t) && i.latestSha && o.push(i);
169
- return o.length === 0 ? (console.info(pc.yellow("\nNo actions selected")), null) : o;
170
- } catch (t) {
171
- if (t instanceof Error && (t.message.includes("cancelled") || t.message.includes("ESC") || t.name === "ExitPromptError")) return logSelectionCancelled(), null;
172
- throw console.error(pc.red("Unexpected error during selection:"), t), t;
167
+ let u = [];
168
+ for (let [e, c] of w.entries()) l.has(e) && hasResolvedTarget(c) && u.push(c);
169
+ return u.length === 0 ? (console.info(pc.yellow("\nNo actions selected")), null) : u;
170
+ } catch (e) {
171
+ if (e instanceof Error && (e.message.includes("cancelled") || e.message.includes("ESC") || e.name === "ExitPromptError")) return logSelectionCancelled(), null;
172
+ throw console.error(pc.red("Unexpected error during selection:"), e), e;
173
173
  }
174
174
  }
175
- function formatAge(t) {
176
- if (!t) return "";
177
- let i = Date.now() - t.getTime(), a = Math.floor(i / (1e3 * 60 * 60)), o = Math.floor(a / 24), s = Math.floor(o / 7), c = o % 7;
178
- return s >= 1 ? c > 0 ? `${s}w ${c}d` : `${s}w` : o >= 1 ? `${o}d` : `${a}h`;
175
+ function formatAge(e) {
176
+ if (!e) return "";
177
+ let c = Date.now() - e.getTime(), l = Math.floor(c / (1e3 * 60 * 60)), u = Math.floor(l / 24), d = Math.floor(u / 7), f = u % 7;
178
+ return d >= 1 ? f > 0 ? `${d}w ${f}d` : `${d}w` : u >= 1 ? `${u}d` : `${l}h`;
179
179
  }
180
- function formatTableRow(t) {
181
- let { currentWidth: i, actionWidth: a, targetWidth: o, jobWidth: s, ageWidth: l, row: u } = t, d = [
182
- padString(u.action, a),
183
- padString(u.job, s),
184
- padString(u.current, i),
185
- u.arrow,
186
- padString(u.target, o)
180
+ function formatTableRow(e) {
181
+ let { currentWidth: c, actionWidth: l, targetWidth: u, jobWidth: d, ageWidth: p, row: m } = e, h = [
182
+ padString(m.action, l),
183
+ padString(m.job, d),
184
+ padString(m.current, c),
185
+ m.arrow,
186
+ padString(m.target, u)
187
187
  ];
188
- return l > 0 && d.push(u.age), d.join(" ").replace(/\s+$/u, "");
188
+ return p > 0 && h.push(m.age), h.join(" ").replace(/\s+$/u, "");
189
+ }
190
+ function formatVersionOrSha(e) {
191
+ return e ? isSha(e) ? e.slice(0, 7) : e.replace(/^v/u, "") : pc.gray("unknown");
189
192
  }
190
- function formatVersionOrSha(t) {
191
- return t ? isSha(t) ? t.slice(0, 7) : t.replace(/^v/u, "") : pc.gray("unknown");
193
+ function getTargetVersion(e) {
194
+ return getResolvedTargetStyle(e) === "tag" && getResolvedTarget(e) ? getResolvedTarget(e) : e.latestVersion;
195
+ }
196
+ function getResolvedTargetStyle(e) {
197
+ return e.targetRefStyle ? e.targetRefStyle : e.latestSha ? "sha" : null;
198
+ }
199
+ function getResolvedTarget(e) {
200
+ return e.targetRef ? e.targetRef : e.latestSha;
192
201
  }
193
202
  function logSelectionCancelled() {
194
203
  console.info(`\r\u001B[K${pc.yellow("Selection cancelled")}`);
195
204
  }
205
+ function hasResolvedTarget(e) {
206
+ return !!getResolvedTarget(e);
207
+ }
196
208
  export { promptUpdateSelection };
@@ -2,17 +2,17 @@ import { ACTIONS_DIRECTORY, GITHUB_DIRECTORY, WORKFLOWS_DIRECTORY } from "./cons
2
2
  import { isYamlFile } from "./fs/is-yaml-file.js";
3
3
  import { scanWorkflowFile } from "./scan-workflow-file.js";
4
4
  import { scanActionFile } from "./scan-action-file.js";
5
- import { readFile, readdir, stat } from "node:fs/promises";
6
5
  import { isAbsolute, join, relative, resolve } from "node:path";
7
- async function scanGitHubActions(d = process.cwd(), m = GITHUB_DIRECTORY) {
6
+ import { readFile, readdir, stat } from "node:fs/promises";
7
+ async function scanGitHubActions(p = process.cwd(), m = GITHUB_DIRECTORY) {
8
8
  let h = {
9
9
  compositeActions: /* @__PURE__ */ new Map(),
10
10
  workflows: /* @__PURE__ */ new Map(),
11
11
  actions: []
12
- }, g = resolve(d);
13
- function _(e, o) {
14
- let s = relative(e, o);
15
- return s !== "" && !s.startsWith("..") && !isAbsolute(s);
12
+ }, g = resolve(p);
13
+ function _(e, a) {
14
+ let o = relative(e, a);
15
+ return o !== "" && !o.startsWith("..") && !isAbsolute(o);
16
16
  }
17
17
  let v = join(g, m);
18
18
  function y(e) {
@@ -20,8 +20,8 @@ async function scanGitHubActions(d = process.cwd(), m = GITHUB_DIRECTORY) {
20
20
  }
21
21
  async function b(e) {
22
22
  try {
23
- let o = await stat(e);
24
- return typeof o.isFile == "function" ? o.isFile() : !1;
23
+ let a = await stat(e);
24
+ return typeof a.isFile == "function" ? a.isFile() : !1;
25
25
  } catch {
26
26
  return !1;
27
27
  }
@@ -30,13 +30,13 @@ async function scanGitHubActions(d = process.cwd(), m = GITHUB_DIRECTORY) {
30
30
  try {
31
31
  if ((await stat(x)).isDirectory()) {
32
32
  let e = (await readdir(x)).filter((e) => y(e) ? isYamlFile(e) : !1).map(async (e) => {
33
- let o = join(x, e);
33
+ let a = join(x, e);
34
34
  try {
35
- let c = await scanWorkflowFile(o);
35
+ let s = await scanWorkflowFile(a);
36
36
  return {
37
37
  path: `${m}/${WORKFLOWS_DIRECTORY}/${e}`,
38
38
  success: !0,
39
- actions: c
39
+ actions: s
40
40
  };
41
41
  } catch {
42
42
  return {
@@ -45,111 +45,111 @@ async function scanGitHubActions(d = process.cwd(), m = GITHUB_DIRECTORY) {
45
45
  actions: []
46
46
  };
47
47
  }
48
- }), o = await Promise.all(e);
49
- 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, []));
48
+ }), a = await Promise.all(e);
49
+ for (let e of a) 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, []));
50
50
  }
51
51
  } catch {}
52
52
  try {
53
- let e = join(g, "action.yml"), o = join(g, "action.yaml"), s = null, c = [];
53
+ let e = join(g, "action.yml"), a = join(g, "action.yaml"), o = null, s = [];
54
54
  if (await b(e)) try {
55
- c = await scanActionFile(e), s = e;
55
+ s = await scanActionFile(e), o = e;
56
56
  } catch {
57
- s = null;
57
+ o = null;
58
58
  }
59
- if (!s && await b(o)) try {
60
- c = await scanActionFile(o), s = o;
59
+ if (!o && await b(a)) try {
60
+ s = await scanActionFile(a), o = a;
61
61
  } catch {
62
- s = null;
62
+ o = null;
63
63
  }
64
- if (s) {
65
- let e = relative(g, s);
66
- h.compositeActions.set(e, e), c.length > 0 && h.actions.push(...c);
64
+ if (o) {
65
+ let e = relative(g, o);
66
+ h.compositeActions.set(e, e), s.length > 0 && h.actions.push(...s);
67
67
  }
68
68
  } catch {}
69
69
  let S = join(v, ACTIONS_DIRECTORY);
70
70
  try {
71
71
  if ((await stat(S)).isDirectory()) {
72
- let o = (await readdir(S)).map(async (o) => {
73
- if (!y(o)) return null;
74
- let s = join(S, o);
72
+ let a = (await readdir(S)).map(async (a) => {
73
+ if (!y(a)) return null;
74
+ let o = join(S, a);
75
75
  try {
76
- if (!(await stat(s)).isDirectory()) return null;
77
- let c = join(s, "action.yml"), l = [];
76
+ if (!(await stat(o)).isDirectory()) return null;
77
+ let s = join(o, "action.yml"), c = [];
78
78
  try {
79
- l = await scanActionFile(c);
79
+ c = await scanActionFile(s);
80
80
  } catch {
81
81
  try {
82
- c = join(s, "action.yaml"), l = await scanActionFile(c);
82
+ s = join(o, "action.yaml"), c = await scanActionFile(s);
83
83
  } catch {
84
84
  return null;
85
85
  }
86
86
  }
87
87
  return {
88
- path: `${m}/${ACTIONS_DIRECTORY}/${o}`,
89
- name: o,
90
- actions: l
88
+ path: `${m}/${ACTIONS_DIRECTORY}/${a}`,
89
+ name: a,
90
+ actions: c
91
91
  };
92
92
  } catch {
93
93
  return null;
94
94
  }
95
- }), s = await Promise.all(o);
96
- for (let e of s) e && (h.compositeActions.set(e.name, e.path), h.actions.push(...e.actions));
95
+ }), o = await Promise.all(a);
96
+ for (let e of o) e && (h.compositeActions.set(e.name, e.path), h.actions.push(...e.actions));
97
97
  }
98
98
  } catch {}
99
99
  try {
100
100
  let e = await getCurrentRepoSlug(g);
101
101
  if (e) {
102
102
  if (process.env.ACTIONS_UP_TEST_THROW === "1") throw Error("test");
103
- let o = /* @__PURE__ */ new Set(), s = [];
104
- for (let c of h.actions) {
105
- if (c.type !== "external") continue;
106
- let l = c.name.split("/");
107
- if (l.length < 3 || `${l[0]}/${l[1]}` !== e) continue;
108
- let u = join(g, ...l.slice(2));
109
- _(g, u) && (o.has(u) || (o.add(u), s.push(u)));
103
+ let a = /* @__PURE__ */ new Set(), o = [];
104
+ for (let s of h.actions) {
105
+ if (s.type !== "external") continue;
106
+ let c = s.name.split("/");
107
+ if (c.length < 3 || `${c[0]}/${c[1]}` !== e) continue;
108
+ let l = join(g, ...c.slice(2));
109
+ _(g, l) && (a.has(l) || (a.add(l), o.push(l)));
110
110
  }
111
- async function c() {
112
- if (s.length === 0) return;
113
- let l = s.splice(0), d = await Promise.all(l.map(async (s) => {
111
+ async function s() {
112
+ if (o.length === 0) return;
113
+ let c = o.splice(0), u = await Promise.all(c.map(async (o) => {
114
114
  try {
115
- let c = join(s, "action.yml"), l = join(s, "action.yaml"), d = c;
115
+ let s = join(o, "action.yml"), c = join(o, "action.yaml"), u = s;
116
116
  try {
117
- if (!(await stat(c)).isFile()) throw Error("not a file");
117
+ if (!(await stat(s)).isFile()) throw Error("not a file");
118
118
  } catch {
119
- if (!(await stat(l)).isFile()) throw Error("not a file");
120
- d = l;
119
+ if (!(await stat(c)).isFile()) throw Error("not a file");
120
+ u = c;
121
121
  }
122
- let f = await scanActionFile(d);
123
- f.length > 0 && h.actions.push(...f);
124
- let p = [];
125
- for (let s of f) {
126
- if (s.type !== "external") continue;
127
- let c = s.name.split("/");
128
- if (c.length < 3 || `${c[0]}/${c[1]}` !== e) continue;
129
- let l = join(g, ...c.slice(2));
130
- _(g, l) && (o.has(l) || (o.add(l), p.push(l)));
122
+ let d = await scanActionFile(u);
123
+ d.length > 0 && h.actions.push(...d);
124
+ let f = [];
125
+ for (let o of d) {
126
+ if (o.type !== "external") continue;
127
+ let s = o.name.split("/");
128
+ if (s.length < 3 || `${s[0]}/${s[1]}` !== e) continue;
129
+ let c = join(g, ...s.slice(2));
130
+ _(g, c) && (a.has(c) || (a.add(c), f.push(c)));
131
131
  }
132
- return p;
132
+ return f;
133
133
  } catch {
134
134
  return [];
135
135
  }
136
136
  }));
137
- for (let e of d) for (let o of e) s.push(o);
138
- await c();
137
+ for (let e of u) for (let a of e) o.push(a);
138
+ await s();
139
139
  }
140
- await c();
140
+ await s();
141
141
  }
142
142
  } catch {}
143
143
  return h;
144
144
  }
145
145
  async function getCurrentRepoSlug(e) {
146
- let o = process.env.GITHUB_REPOSITORY;
147
- if (o && /^[^\s/]+\/[^\s/]+$/u.test(o)) return o;
146
+ let a = process.env.GITHUB_REPOSITORY;
147
+ if (a && /^[^\s/]+\/[^\s/]+$/u.test(a)) return a;
148
148
  try {
149
- let o = await readFile(join(e, ".git", "config"), "utf8"), s = o.match(/\[remote "origin"\][\s\S]*?url\s*=\s*(?<url>.+)/u)?.groups?.url?.trim();
150
- if (s ||= o.match(/url\s*=\s*(?<url>.+)/u)?.groups?.url?.trim(), !s) return null;
151
- let c = s.match(/github\.com[/:](?<owner>[^/]+)\/(?<repo>[^./]+)(?:\.git)?$/u);
152
- if (c?.groups) return `${c.groups.owner}/${c.groups.repo}`;
149
+ let a = await readFile(join(e, ".git", "config"), "utf8"), o = a.match(/\[remote "origin"\][\s\S]*?url\s*=\s*(?<url>.+)/u)?.groups?.url?.trim();
150
+ if (o ||= a.match(/url\s*=\s*(?<url>.+)/u)?.groups?.url?.trim(), !o) return null;
151
+ let s = o.match(/github\.com[/:](?<owner>[^/]+)\/(?<repo>[^./]+)(?:\.git)?$/u);
152
+ if (s?.groups) return `${s.groups.owner}/${s.groups.repo}`;
153
153
  } catch {}
154
154
  return null;
155
155
  }
@@ -0,0 +1,10 @@
1
+ import { ActionUpdate } from '../../types/action-update';
2
+ import { UpdateStyle } from '../../types/update-style';
3
+ /**
4
+ * Resolve the final reference that should be written back to the workflow.
5
+ *
6
+ * @param update - Update entry enriched with lookup data.
7
+ * @param style - Effective update style.
8
+ * @returns Update entry with resolved target reference fields.
9
+ */
10
+ export declare function resolveTargetReference(update: ActionUpdate, style: UpdateStyle): ActionUpdate;
@@ -0,0 +1,24 @@
1
+ function resolveTargetReference(e, t) {
2
+ return e.hasUpdate ? t === "sha" || e.currentRefType === "sha" ? e.latestSha ? {
3
+ ...e,
4
+ targetRef: e.latestSha,
5
+ targetRefStyle: "sha"
6
+ } : {
7
+ ...e,
8
+ targetRefStyle: null,
9
+ targetRef: null
10
+ } : e.currentRefType === "tag" && e.latestVersion ? {
11
+ ...e,
12
+ targetRef: e.latestVersion,
13
+ targetRefStyle: "tag"
14
+ } : {
15
+ ...e,
16
+ targetRefStyle: null,
17
+ targetRef: null
18
+ } : {
19
+ ...e,
20
+ targetRefStyle: null,
21
+ targetRef: null
22
+ };
23
+ }
24
+ export { resolveTargetReference };
package/dist/package.js CHANGED
@@ -1,2 +1,2 @@
1
- const version = "1.12.1";
1
+ const version = "1.14.0";
2
2
  export { version };
@@ -6,7 +6,17 @@ export interface ActionUpdate {
6
6
  /**
7
7
  * Reason for skipping the update check.
8
8
  */
9
- skipReason?: 'unknown' | 'branch'
9
+ skipReason?: 'unsupported-style' | 'unknown' | 'branch'
10
+
11
+ /**
12
+ * Detected style of the current reference in the source file.
13
+ */
14
+ currentRefType?: 'unknown' | 'branch' | 'sha' | 'tag'
15
+
16
+ /**
17
+ * Style of the final reference that should be written back to the file.
18
+ */
19
+ targetRefStyle?: 'sha' | 'tag' | null
10
20
 
11
21
  /**
12
22
  * Current version string.
@@ -23,6 +33,11 @@ export interface ActionUpdate {
23
33
  */
24
34
  status?: 'skipped' | 'ok'
25
35
 
36
+ /**
37
+ * Final reference that should be written back to the file.
38
+ */
39
+ targetRef?: string | null
40
+
26
41
  /**
27
42
  * SHA hash of the latest version.
28
43
  */
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Strategy used when writing updated action references back to workflow files.
3
+ */
4
+ export type UpdateStyle = 'preserve' | 'sha'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "actions-up",
3
- "version": "1.12.1",
4
- "description": "Interactive CLI tool to update GitHub Actions to latest versions with SHA pinning",
3
+ "version": "1.14.0",
4
+ "description": "Interactive CLI tool to update GitHub Actions with SHA pinning or preserved refs",
5
5
  "keywords": [
6
6
  "github-actions",
7
7
  "actions",