actions-up 1.2.1 → 1.3.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 +3 -3
- package/dist/core/api/check-updates.js +11 -7
- package/dist/core/api/create-github-client.d.ts +8 -0
- package/dist/core/api/create-github-client.js +55 -0
- package/dist/core/api/get-all-releases.d.ts +20 -0
- package/dist/core/api/get-all-releases.js +35 -0
- package/dist/core/api/get-all-tags.d.ts +17 -0
- package/dist/core/api/get-all-tags.js +13 -0
- package/dist/core/api/get-latest-release.d.ts +15 -0
- package/dist/core/api/get-latest-release.js +28 -0
- package/dist/core/api/get-reference-type.d.ts +16 -0
- package/dist/core/api/get-reference-type.js +21 -0
- package/dist/core/api/get-tag-info.d.ts +19 -0
- package/dist/core/api/get-tag-info.js +96 -0
- package/dist/core/api/get-tag-sha.d.ts +19 -0
- package/dist/core/api/get-tag-sha.js +30 -0
- package/dist/core/api/internal-rate-limit-error.d.ts +3 -0
- package/dist/core/api/internal-rate-limit-error.js +8 -0
- package/dist/core/api/make-request.d.ts +13 -0
- package/dist/core/api/make-request.js +31 -0
- package/dist/core/api/resolve-github-token-sync.d.ts +6 -0
- package/dist/core/api/resolve-github-token-sync.js +48 -0
- package/dist/core/api/update-rate-limit-info.d.ts +8 -0
- package/dist/core/api/update-rate-limit-info.js +10 -0
- package/dist/core/interactive/format-version.d.ts +3 -2
- package/dist/core/interactive/format-version.js +33 -2
- package/dist/core/interactive/prompt-update-selection.js +42 -8
- package/dist/core/scan-github-actions.js +1 -1
- package/dist/package.js +1 -1
- package/dist/types/github-client-context.d.ts +32 -0
- package/dist/types/github-client.d.ts +42 -0
- package/dist/types/release-info.d.ts +23 -0
- package/dist/types/tag-info.d.ts +14 -0
- package/package.json +1 -1
- package/dist/core/api/client.d.ts +0 -98
- package/dist/core/api/client.js +0 -292
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
import pc from "picocolors";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import semver from "semver";
|
|
3
|
+
function formatVersion(latestVersion, currentVersion) {
|
|
4
|
+
if (!latestVersion) return pc.gray("unknown");
|
|
5
|
+
let latest = semver.parse(latestVersion);
|
|
6
|
+
let current = currentVersion ? semver.parse(normalizeVersion(currentVersion)) : null;
|
|
7
|
+
if (!current || !latest) return latestVersion;
|
|
8
|
+
let change = semver.diff(normalizeVersion(currentVersion), latestVersion);
|
|
9
|
+
let unstable = current.major === 0;
|
|
10
|
+
let changeColor = unstable ? pc.yellowBright : pc.gray;
|
|
11
|
+
let parts = [
|
|
12
|
+
latest.major,
|
|
13
|
+
latest.minor,
|
|
14
|
+
latest.patch
|
|
15
|
+
];
|
|
16
|
+
let colors = parts.map((_, i) => {
|
|
17
|
+
if (change === "major") return pc.redBright;
|
|
18
|
+
if (change === "minor" && i >= 1) return changeColor;
|
|
19
|
+
if (change === "patch" && i === 2) return changeColor;
|
|
20
|
+
return identity;
|
|
21
|
+
});
|
|
22
|
+
let result = colors[0](String(parts[0]));
|
|
23
|
+
if (parts[1] !== void 0) result += colors[0](".") + colors[1](String(parts[1]));
|
|
24
|
+
if (parts[2] !== void 0) result += colors[1](".") + colors[2](String(parts[2]));
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
function normalizeVersion(version) {
|
|
28
|
+
let cleaned = version.replace(/^v/u, "");
|
|
29
|
+
let parts = cleaned.split(".");
|
|
30
|
+
while (parts.length < 3) parts.push("0");
|
|
31
|
+
return parts.slice(0, 3).join(".");
|
|
32
|
+
}
|
|
33
|
+
function identity(string) {
|
|
34
|
+
return string;
|
|
4
35
|
}
|
|
5
36
|
export { formatVersion };
|
|
@@ -4,10 +4,11 @@ import { stripAnsi } from "./strip-ansi.js";
|
|
|
4
4
|
import { padString } from "./pad-string.js";
|
|
5
5
|
import "node:worker_threads";
|
|
6
6
|
import pc from "picocolors";
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
7
8
|
import enquirer from "enquirer";
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
const MIN_ACTION_WIDTH = 56;
|
|
10
|
-
const MIN_CURRENT_WIDTH =
|
|
11
|
+
const MIN_CURRENT_WIDTH = 16;
|
|
11
12
|
async function promptUpdateSelection(updates) {
|
|
12
13
|
if (updates.length === 0) return null;
|
|
13
14
|
let outdated = updates.filter((update) => update.hasUpdate);
|
|
@@ -27,14 +28,33 @@ async function promptUpdateSelection(updates) {
|
|
|
27
28
|
});
|
|
28
29
|
groups.set(file, group);
|
|
29
30
|
}
|
|
31
|
+
let currentComputedByIndex = await Promise.all(outdated.map(async (update) => {
|
|
32
|
+
let display = formatVersionOrSha(update.currentVersion);
|
|
33
|
+
let effectiveForDiff = update.currentVersion ?? void 0;
|
|
34
|
+
if (!update.currentVersion || !isSha(update.currentVersion)) return {
|
|
35
|
+
effectiveForDiff,
|
|
36
|
+
display
|
|
37
|
+
};
|
|
38
|
+
let versionFromComment = await tryReadInlineVersionComment(update.action.file, update.action.line);
|
|
39
|
+
if (versionFromComment) {
|
|
40
|
+
let shortSha = update.currentVersion.slice(0, 7);
|
|
41
|
+
let version = formatVersionOrSha(versionFromComment);
|
|
42
|
+
display = `${version} ${pc.gray(`(${shortSha})`)}`;
|
|
43
|
+
effectiveForDiff = versionFromComment;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
effectiveForDiff,
|
|
47
|
+
display
|
|
48
|
+
};
|
|
49
|
+
}));
|
|
30
50
|
let choices = [];
|
|
31
51
|
let maxActionLength = stripAnsi("Action").length;
|
|
32
52
|
let maxCurrentLength = stripAnsi("Current").length;
|
|
33
|
-
for (let update of outdated) {
|
|
53
|
+
for (let [index, update] of outdated.entries()) {
|
|
34
54
|
let actionNameRaw = update.action.name;
|
|
35
|
-
let currentRaw =
|
|
55
|
+
let currentRaw = currentComputedByIndex[index].display;
|
|
36
56
|
maxActionLength = Math.max(maxActionLength, actionNameRaw.length);
|
|
37
|
-
maxCurrentLength = Math.max(maxCurrentLength, currentRaw.length);
|
|
57
|
+
maxCurrentLength = Math.max(maxCurrentLength, stripAnsi(currentRaw).length);
|
|
38
58
|
}
|
|
39
59
|
let globalActionWidth = Math.max(maxActionLength, MIN_ACTION_WIDTH);
|
|
40
60
|
let globalCurrentWidth = Math.max(maxCurrentLength, MIN_CURRENT_WIDTH);
|
|
@@ -53,10 +73,11 @@ async function promptUpdateSelection(updates) {
|
|
|
53
73
|
target: "Target",
|
|
54
74
|
arrow: "❯"
|
|
55
75
|
});
|
|
56
|
-
for (let { update } of groupOrder) {
|
|
76
|
+
for (let { update, index } of groupOrder) {
|
|
57
77
|
let hasSha = Boolean(update.latestSha);
|
|
58
|
-
let current =
|
|
59
|
-
let
|
|
78
|
+
let current = currentComputedByIndex[index].display;
|
|
79
|
+
let effectiveCurrentForDiff = currentComputedByIndex[index]?.effectiveForDiff ?? update.currentVersion;
|
|
80
|
+
let latest = formatVersion(update.latestVersion, effectiveCurrentForDiff);
|
|
60
81
|
let actionName = update.action.name;
|
|
61
82
|
if (update.latestSha) {
|
|
62
83
|
let shortSha = update.latestSha.slice(0, 7);
|
|
@@ -180,6 +201,19 @@ async function promptUpdateSelection(updates) {
|
|
|
180
201
|
throw error;
|
|
181
202
|
}
|
|
182
203
|
}
|
|
204
|
+
async function tryReadInlineVersionComment(filePath, lineNumber) {
|
|
205
|
+
try {
|
|
206
|
+
if (!filePath || !lineNumber || lineNumber <= 0) return null;
|
|
207
|
+
let content = await readFile(filePath, "utf8");
|
|
208
|
+
let lines = content.split("\n");
|
|
209
|
+
let index = lineNumber - 1;
|
|
210
|
+
if (index < 0 || index >= lines.length) return null;
|
|
211
|
+
let line = lines[index];
|
|
212
|
+
let match = line.match(/#\s*(?<version>[Vv]?\d+(?:\.\d+){0,2}(?:[+-][\w\-.]+)?)/u);
|
|
213
|
+
if (match?.groups?.["version"]) return match.groups["version"];
|
|
214
|
+
} catch {}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
183
217
|
function formatTableRow(row, actionWidth, currentWidth) {
|
|
184
218
|
let parts = [
|
|
185
219
|
padString(row.action, actionWidth),
|
|
@@ -198,6 +232,6 @@ function isSha(value) {
|
|
|
198
232
|
function formatVersionOrSha(version) {
|
|
199
233
|
if (!version) return pc.gray("unknown");
|
|
200
234
|
if (isSha(version)) return version.slice(0, 7);
|
|
201
|
-
return version;
|
|
235
|
+
return version.replace(/^v/u, "");
|
|
202
236
|
}
|
|
203
237
|
export { promptUpdateSelection };
|
|
@@ -2,8 +2,8 @@ import { ACTIONS_DIRECTORY, GITHUB_DIRECTORY, WORKFLOWS_DIRECTORY } from "./cons
|
|
|
2
2
|
import { scanWorkflowFile } from "./scan-workflow-file.js";
|
|
3
3
|
import { scanActionFile } from "./scan-action-file.js";
|
|
4
4
|
import { isYamlFile } from "./fs/is-yaml-file.js";
|
|
5
|
-
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
6
5
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
6
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
7
7
|
async function scanGitHubActions(rootPath = process.cwd()) {
|
|
8
8
|
let result = {
|
|
9
9
|
compositeActions: /* @__PURE__ */ new Map(),
|
package/dist/package.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const version = "1.
|
|
1
|
+
const version = "1.3.0";
|
|
2
2
|
export { version };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { TagInfo } from './tag-info';
|
|
2
|
+
/**
|
|
3
|
+
* Internal client context shared by all API functions.
|
|
4
|
+
*
|
|
5
|
+
* Stores auth, rate-limit state, base URL and in-memory caches to decrease the
|
|
6
|
+
* number of API requests during a single run.
|
|
7
|
+
*/
|
|
8
|
+
export interface GitHubClientContext {
|
|
9
|
+
/** Lightweight caches keyed by owner/repo (+ extra payload). */
|
|
10
|
+
caches: {
|
|
11
|
+
/** Cache of reference type detections (branch/tag/null). */
|
|
12
|
+
refType: Map<string, 'branch' | 'tag' | null>
|
|
13
|
+
|
|
14
|
+
/** Cache of resolved tag metadata (message/date/SHA). */
|
|
15
|
+
tagInfo: Map<string, TagInfo | null>
|
|
16
|
+
|
|
17
|
+
/** Cache of resolved tag commit SHAs. */
|
|
18
|
+
tagSha: Map<string, string | null>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Remaining requests available per current rate-limit window. */
|
|
22
|
+
rateLimitRemaining: number
|
|
23
|
+
|
|
24
|
+
/** GitHub token, if available. */
|
|
25
|
+
token: undefined | string
|
|
26
|
+
|
|
27
|
+
/** Scheduled time when rate limit resets. */
|
|
28
|
+
rateLimitReset: Date
|
|
29
|
+
|
|
30
|
+
/** GitHub REST API base URL. */
|
|
31
|
+
baseUrl: string
|
|
32
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ReleaseInfo } from './release-info';
|
|
2
|
+
import { TagInfo } from './tag-info';
|
|
3
|
+
/**
|
|
4
|
+
* Public API surface for interacting with GitHub from Actions Up.
|
|
5
|
+
*
|
|
6
|
+
* Methods are thin wrappers around lower-level functions bound to a client
|
|
7
|
+
* context (auth + rate-limit + caches). All methods are async and return
|
|
8
|
+
* normalized, serializable data structures.
|
|
9
|
+
*/
|
|
10
|
+
export interface GitHubClient {
|
|
11
|
+
/** Detect whether a reference is a tag or a branch (or unknown). */
|
|
12
|
+
getRefType(
|
|
13
|
+
owner: string,
|
|
14
|
+
repo: string,
|
|
15
|
+
reference: string,
|
|
16
|
+
): Promise<'branch' | 'tag' | null>
|
|
17
|
+
|
|
18
|
+
/** List releases with minimal enrichment. */
|
|
19
|
+
getAllReleases(
|
|
20
|
+
owner: string,
|
|
21
|
+
repo: string,
|
|
22
|
+
limit?: number,
|
|
23
|
+
): Promise<ReleaseInfo[]>
|
|
24
|
+
|
|
25
|
+
/** Fetch tag metadata (message/date) and the resolved commit SHA. */
|
|
26
|
+
getTagInfo(owner: string, repo: string, tag: string): Promise<TagInfo | null>
|
|
27
|
+
|
|
28
|
+
/** Resolve commit SHA for a tag without fetching commit data. */
|
|
29
|
+
getTagSha(owner: string, repo: string, tag: string): Promise<string | null>
|
|
30
|
+
|
|
31
|
+
/** List repository tags (name + commit SHA). */
|
|
32
|
+
getAllTags(owner: string, repo: string, limit?: number): Promise<TagInfo[]>
|
|
33
|
+
|
|
34
|
+
/** Fetch the latest release or null when no latest release exists. */
|
|
35
|
+
getLatestRelease(owner: string, repo: string): Promise<ReleaseInfo | null>
|
|
36
|
+
|
|
37
|
+
/** Current rate limit snapshot. */
|
|
38
|
+
getRateLimitStatus(): { remaining: number; resetAt: Date }
|
|
39
|
+
|
|
40
|
+
/** True when remaining requests are below a threshold. */
|
|
41
|
+
shouldWaitForRateLimit(threshold?: number): boolean
|
|
42
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Normalized release information used across the tool. */
|
|
2
|
+
export interface ReleaseInfo {
|
|
3
|
+
/** Release description (body) or null when absent. */
|
|
4
|
+
description: string | null
|
|
5
|
+
|
|
6
|
+
/** True when the release is marked as prerelease. */
|
|
7
|
+
isPrerelease: boolean
|
|
8
|
+
|
|
9
|
+
/** Commit SHA associated with the release tag (may be null). */
|
|
10
|
+
sha: string | null
|
|
11
|
+
|
|
12
|
+
/** Publication date of the release. */
|
|
13
|
+
publishedAt: Date
|
|
14
|
+
|
|
15
|
+
/** Tag name (e.g. V1.2.3). */
|
|
16
|
+
version: string
|
|
17
|
+
|
|
18
|
+
/** Release name or tag name when name is not provided. */
|
|
19
|
+
name: string
|
|
20
|
+
|
|
21
|
+
/** HTML URL of the release page. */
|
|
22
|
+
url: string
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Normalized tag information (message/date) and the resolved commit SHA. */
|
|
2
|
+
export interface TagInfo {
|
|
3
|
+
/** Tag or commit message, null when absent. */
|
|
4
|
+
message: string | null
|
|
5
|
+
|
|
6
|
+
/** Commit SHA the tag ultimately points to (may be null). */
|
|
7
|
+
sha: string | null
|
|
8
|
+
|
|
9
|
+
/** Date associated with the tag (from release, tagger or commit). */
|
|
10
|
+
date: Date | null
|
|
11
|
+
|
|
12
|
+
/** Tag name (e.g. V1.2.3). */
|
|
13
|
+
tag: string
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
interface ReleaseInfo {
|
|
2
|
-
/** Release description or null if not provided. */
|
|
3
|
-
description: string | null;
|
|
4
|
-
/** Whether this release is marked as a pre-release. */
|
|
5
|
-
isPrerelease: boolean;
|
|
6
|
-
/** Git commit SHA for this release. */
|
|
7
|
-
sha: string | null;
|
|
8
|
-
/** Date when the release was published. */
|
|
9
|
-
publishedAt: Date;
|
|
10
|
-
/** Version tag name (e.g., 'v1.2.3'). */
|
|
11
|
-
version: string;
|
|
12
|
-
/** Release name or tag name if name not provided. */
|
|
13
|
-
name: string;
|
|
14
|
-
/** GitHub URL for this release. */
|
|
15
|
-
url: string;
|
|
16
|
-
}
|
|
17
|
-
/** Processed tag information with normalized types. */
|
|
18
|
-
interface TagInfo {
|
|
19
|
-
/** Tag or commit message, null if not provided. */
|
|
20
|
-
message: string | null;
|
|
21
|
-
/** Git commit SHA that this tag points to. */
|
|
22
|
-
sha: string | null;
|
|
23
|
-
/** Date when the tag was created or committed. */
|
|
24
|
-
date: Date | null;
|
|
25
|
-
/** Tag name (e.g., 'v1.2.3'). */
|
|
26
|
-
tag: string;
|
|
27
|
-
}
|
|
28
|
-
/** GitHub REST API client with optional authentication. */
|
|
29
|
-
export declare class Client {
|
|
30
|
-
private readonly baseUrl;
|
|
31
|
-
private readonly token;
|
|
32
|
-
private rateLimitReset;
|
|
33
|
-
private rateLimitRemaining;
|
|
34
|
-
/**
|
|
35
|
-
* Creates a new GitHub API client.
|
|
36
|
-
*
|
|
37
|
-
* @param token - Optional GitHub token for authentication.
|
|
38
|
-
*/
|
|
39
|
-
constructor(token?: string);
|
|
40
|
-
private static isRateLimitError;
|
|
41
|
-
/**
|
|
42
|
-
* Get specific tag/version information.
|
|
43
|
-
*
|
|
44
|
-
* @param owner - The repository owner.
|
|
45
|
-
* @param repo - The repository name.
|
|
46
|
-
* @param tag - The tag name to fetch.
|
|
47
|
-
* @returns Tag information or null if not found.
|
|
48
|
-
*/
|
|
49
|
-
getTagInfo(owner: string, repo: string, tag: string): Promise<TagInfo | null>;
|
|
50
|
-
/**
|
|
51
|
-
* Get all releases for a repository.
|
|
52
|
-
*
|
|
53
|
-
* @param owner - The repository owner.
|
|
54
|
-
* @param repo - The repository name.
|
|
55
|
-
* @param limit - Maximum number of releases to fetch.
|
|
56
|
-
* @returns Array of release information.
|
|
57
|
-
*/
|
|
58
|
-
getAllReleases(owner: string, repo: string, limit?: number): Promise<ReleaseInfo[]>;
|
|
59
|
-
/**
|
|
60
|
-
* Get the latest release for a GitHub repository.
|
|
61
|
-
*
|
|
62
|
-
* @param owner - The repository owner.
|
|
63
|
-
* @param repo - The repository name.
|
|
64
|
-
* @returns Latest release information or null if not found.
|
|
65
|
-
*/
|
|
66
|
-
getLatestRelease(owner: string, repo: string): Promise<ReleaseInfo | null>;
|
|
67
|
-
getAllTags(owner: string, repo: string, limit?: number): Promise<TagInfo[]>;
|
|
68
|
-
getRefType(owner: string, repo: string, reference: string): Promise<'branch' | 'tag' | null>;
|
|
69
|
-
getRateLimitStatus(): {
|
|
70
|
-
remaining: number;
|
|
71
|
-
resetAt: Date;
|
|
72
|
-
};
|
|
73
|
-
/**
|
|
74
|
-
* Check if we should wait before making more requests.
|
|
75
|
-
*
|
|
76
|
-
* @param threshold - Minimum remaining requests before waiting.
|
|
77
|
-
* @returns True if rate limit is below threshold.
|
|
78
|
-
*/
|
|
79
|
-
shouldWaitForRateLimit(threshold?: number): boolean;
|
|
80
|
-
private makeRequest;
|
|
81
|
-
private updateRateLimitInfo;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Resolve GitHub token from multiple sources with descending priority.
|
|
85
|
-
*
|
|
86
|
-
* Priority:
|
|
87
|
-
*
|
|
88
|
-
* 1. Env GITHUB_TOKEN
|
|
89
|
-
* 2. Env GH_TOKEN
|
|
90
|
-
* 3. Gh auth token
|
|
91
|
-
* 4. .git/config keys (github.token, github.oauth-token, hub.oauthtoken).
|
|
92
|
-
*
|
|
93
|
-
* This is a synchronous best-effort resolver without throwing on failures.
|
|
94
|
-
*
|
|
95
|
-
* @returns Token string or undefined when not found.
|
|
96
|
-
*/
|
|
97
|
-
export declare function resolveGitHubTokenSync(): undefined | string;
|
|
98
|
-
export {};
|
package/dist/core/api/client.js
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import { execFileSync } from "node:child_process";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
var GitHubRateLimitError = class extends Error {
|
|
5
|
-
constructor(resetAt) {
|
|
6
|
-
let resetTime = resetAt.toLocaleTimeString();
|
|
7
|
-
super(`GitHub API rate limit exceeded. Resets at ${resetTime}`);
|
|
8
|
-
this.name = "GitHubRateLimitError";
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
var Client = class Client {
|
|
12
|
-
baseUrl = "https://api.github.com";
|
|
13
|
-
token;
|
|
14
|
-
rateLimitReset = /* @__PURE__ */ new Date();
|
|
15
|
-
rateLimitRemaining = 60;
|
|
16
|
-
constructor(token) {
|
|
17
|
-
this.token = token ?? process.env["GITHUB_TOKEN"] ?? resolveGitHubTokenSync();
|
|
18
|
-
this.rateLimitRemaining = this.token ? 5e3 : 60;
|
|
19
|
-
}
|
|
20
|
-
static isRateLimitError(error) {
|
|
21
|
-
if (error && typeof error === "object") {
|
|
22
|
-
let maybeAny = error;
|
|
23
|
-
let message = typeof maybeAny.message === "string" ? maybeAny.message.toLowerCase() : "";
|
|
24
|
-
let status = typeof maybeAny.status === "number" ? maybeAny.status : void 0;
|
|
25
|
-
return message.includes("rate limit") || message.includes("api rate limit") || status === 403;
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
async getTagInfo(owner, repo, tag) {
|
|
30
|
-
try {
|
|
31
|
-
let displayTag = tag.replace(/^refs\/tags\//u, "");
|
|
32
|
-
try {
|
|
33
|
-
let releaseResp = await this.makeRequest(`/repos/${owner}/${repo}/releases/tags/${displayTag}`);
|
|
34
|
-
let releaseData = releaseResp.data;
|
|
35
|
-
let date = releaseData.published_at ? new Date(releaseData.published_at) : null;
|
|
36
|
-
let message = releaseData.body ?? null;
|
|
37
|
-
let sha = null;
|
|
38
|
-
try {
|
|
39
|
-
let referenceResp = await this.makeRequest(`/repos/${owner}/${repo}/git/refs/tags/${displayTag}`);
|
|
40
|
-
let referenceData = referenceResp.data;
|
|
41
|
-
let objectSha = referenceData.object.sha;
|
|
42
|
-
let objectType = referenceData.object.type;
|
|
43
|
-
if (objectSha && objectType === "tag") try {
|
|
44
|
-
let tagResp = await this.makeRequest(`/repos/${owner}/${repo}/git/tags/${objectSha}`);
|
|
45
|
-
let tagData = tagResp.data;
|
|
46
|
-
let tagObject = tagData.object;
|
|
47
|
-
sha = tagObject?.sha ?? null;
|
|
48
|
-
let taggerDate = tagData.tagger?.date;
|
|
49
|
-
if (!date && taggerDate) date = new Date(taggerDate);
|
|
50
|
-
let tagMessage = tagData.message;
|
|
51
|
-
if (!message && typeof tagMessage === "string") message = tagMessage;
|
|
52
|
-
} catch {
|
|
53
|
-
sha = objectSha;
|
|
54
|
-
}
|
|
55
|
-
else if (objectSha && objectType === "commit") {
|
|
56
|
-
sha = objectSha;
|
|
57
|
-
if (!date || !message) try {
|
|
58
|
-
let commitResp = await this.makeRequest(`/repos/${owner}/${repo}/git/commits/${objectSha}`);
|
|
59
|
-
let commitData = commitResp.data;
|
|
60
|
-
let { message: commitMessage } = commitData;
|
|
61
|
-
if (!message && typeof commitMessage === "string") message = commitMessage;
|
|
62
|
-
let authorDate = commitData.author?.date;
|
|
63
|
-
if (!date && authorDate) date = new Date(authorDate);
|
|
64
|
-
} catch {}
|
|
65
|
-
}
|
|
66
|
-
} catch {
|
|
67
|
-
if (isLikelySha(releaseData.target_commitish)) sha = releaseData.target_commitish;
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
tag: displayTag,
|
|
71
|
-
message,
|
|
72
|
-
date,
|
|
73
|
-
sha
|
|
74
|
-
};
|
|
75
|
-
} catch (releaseError) {
|
|
76
|
-
if (releaseError && typeof releaseError === "object" && "status" in releaseError && releaseError.status === 404) try {
|
|
77
|
-
let referenceResp = await this.makeRequest(`/repos/${owner}/${repo}/git/refs/tags/${displayTag}`);
|
|
78
|
-
let referenceData = referenceResp.data;
|
|
79
|
-
let { sha } = referenceData.object;
|
|
80
|
-
let message = null;
|
|
81
|
-
let date = null;
|
|
82
|
-
if (referenceData.object.type === "tag") try {
|
|
83
|
-
let tagResp = await this.makeRequest(`/repos/${owner}/${repo}/git/tags/${sha}`);
|
|
84
|
-
let tagData = tagResp.data;
|
|
85
|
-
({sha} = tagData.object);
|
|
86
|
-
({message} = tagData);
|
|
87
|
-
date = tagData.tagger.date ? new Date(tagData.tagger.date) : null;
|
|
88
|
-
} catch {}
|
|
89
|
-
else try {
|
|
90
|
-
let commitResp = await this.makeRequest(`/repos/${owner}/${repo}/git/commits/${sha}`);
|
|
91
|
-
let commitData = commitResp.data;
|
|
92
|
-
({message} = commitData);
|
|
93
|
-
date = commitData.author.date ? new Date(commitData.author.date) : null;
|
|
94
|
-
} catch {}
|
|
95
|
-
return {
|
|
96
|
-
tag: displayTag,
|
|
97
|
-
message,
|
|
98
|
-
date,
|
|
99
|
-
sha
|
|
100
|
-
};
|
|
101
|
-
} catch (tagError) {
|
|
102
|
-
if (tagError && typeof tagError === "object" && "status" in tagError && tagError.status === 404) return null;
|
|
103
|
-
throw tagError;
|
|
104
|
-
}
|
|
105
|
-
throw releaseError;
|
|
106
|
-
}
|
|
107
|
-
} catch (error) {
|
|
108
|
-
if (Client.isRateLimitError(error)) throw new GitHubRateLimitError(this.rateLimitReset);
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
async getAllReleases(owner, repo, limit = 10) {
|
|
113
|
-
try {
|
|
114
|
-
let releasesResp = await this.makeRequest(`/repos/${owner}/${repo}/releases?per_page=${limit}`);
|
|
115
|
-
let releases = releasesResp.data;
|
|
116
|
-
let releaseInfos = [];
|
|
117
|
-
await Promise.all(releases.map(async (release) => {
|
|
118
|
-
let sha = null;
|
|
119
|
-
if (release.tag_name) try {
|
|
120
|
-
let tagInfo = await this.getTagInfo(owner, repo, release.tag_name);
|
|
121
|
-
if (tagInfo) ({sha} = tagInfo);
|
|
122
|
-
} catch {
|
|
123
|
-
sha = isLikelySha(release.target_commitish) ? release.target_commitish : null;
|
|
124
|
-
}
|
|
125
|
-
releaseInfos.push({
|
|
126
|
-
publishedAt: new Date(release.published_at),
|
|
127
|
-
name: release.name ?? release.tag_name,
|
|
128
|
-
description: release.body ?? null,
|
|
129
|
-
isPrerelease: release.prerelease,
|
|
130
|
-
version: release.tag_name,
|
|
131
|
-
url: release.html_url,
|
|
132
|
-
sha
|
|
133
|
-
});
|
|
134
|
-
}));
|
|
135
|
-
return releaseInfos;
|
|
136
|
-
} catch (error) {
|
|
137
|
-
if (Client.isRateLimitError(error)) throw new GitHubRateLimitError(this.rateLimitReset);
|
|
138
|
-
throw error;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
async getLatestRelease(owner, repo) {
|
|
142
|
-
try {
|
|
143
|
-
let releaseResp = await this.makeRequest(`/repos/${owner}/${repo}/releases/latest`);
|
|
144
|
-
let release = releaseResp.data;
|
|
145
|
-
let sha = null;
|
|
146
|
-
if (release.tag_name) try {
|
|
147
|
-
let tagInfo = await this.getTagInfo(owner, repo, release.tag_name);
|
|
148
|
-
if (tagInfo) ({sha} = tagInfo);
|
|
149
|
-
} catch {
|
|
150
|
-
sha = isLikelySha(release.target_commitish) ? release.target_commitish : null;
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
publishedAt: new Date(release.published_at),
|
|
154
|
-
name: release.name ?? release.tag_name,
|
|
155
|
-
description: release.body ?? null,
|
|
156
|
-
isPrerelease: release.prerelease,
|
|
157
|
-
version: release.tag_name,
|
|
158
|
-
url: release.html_url,
|
|
159
|
-
sha
|
|
160
|
-
};
|
|
161
|
-
} catch (error) {
|
|
162
|
-
if (error && typeof error === "object" && "status" in error && error.status === 404) return null;
|
|
163
|
-
if (Client.isRateLimitError(error)) throw new GitHubRateLimitError(this.rateLimitReset);
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
async getAllTags(owner, repo, limit = 30) {
|
|
168
|
-
try {
|
|
169
|
-
let tagsResp = await this.makeRequest(`/repos/${owner}/${repo}/tags?per_page=${limit}`);
|
|
170
|
-
let tags = tagsResp.data;
|
|
171
|
-
return tags.map((tag) => ({
|
|
172
|
-
sha: tag.commit.sha,
|
|
173
|
-
tag: tag.name,
|
|
174
|
-
message: null,
|
|
175
|
-
date: null
|
|
176
|
-
}));
|
|
177
|
-
} catch (error) {
|
|
178
|
-
if (Client.isRateLimitError(error)) throw new GitHubRateLimitError(this.rateLimitReset);
|
|
179
|
-
throw error;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
async getRefType(owner, repo, reference) {
|
|
183
|
-
try {
|
|
184
|
-
await this.makeRequest(`/repos/${owner}/${repo}/git/refs/tags/${reference}`);
|
|
185
|
-
return "tag";
|
|
186
|
-
} catch {
|
|
187
|
-
try {
|
|
188
|
-
await this.makeRequest(`/repos/${owner}/${repo}/git/refs/heads/${reference}`);
|
|
189
|
-
return "branch";
|
|
190
|
-
} catch {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
getRateLimitStatus() {
|
|
196
|
-
return {
|
|
197
|
-
remaining: this.rateLimitRemaining,
|
|
198
|
-
resetAt: this.rateLimitReset
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
shouldWaitForRateLimit(threshold = 100) {
|
|
202
|
-
return this.rateLimitRemaining < threshold;
|
|
203
|
-
}
|
|
204
|
-
async makeRequest(path$1, options = {}) {
|
|
205
|
-
let headers = {
|
|
206
|
-
Accept: "application/vnd.github.v3+json",
|
|
207
|
-
"User-Agent": "actions-up",
|
|
208
|
-
...options.headers
|
|
209
|
-
};
|
|
210
|
-
if (this.token) headers["Authorization"] = `Bearer ${this.token}`;
|
|
211
|
-
let response = await fetch(`${this.baseUrl}${path$1}`, {
|
|
212
|
-
...options,
|
|
213
|
-
headers
|
|
214
|
-
});
|
|
215
|
-
let responseHeaders = {};
|
|
216
|
-
for (let [key, value] of response.headers.entries()) responseHeaders[key] = value;
|
|
217
|
-
this.updateRateLimitInfo(responseHeaders);
|
|
218
|
-
if (!response.ok) {
|
|
219
|
-
let error = /* @__PURE__ */ new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
220
|
-
error.status = response.status;
|
|
221
|
-
if (response.status === 403) {
|
|
222
|
-
let text = await response.text();
|
|
223
|
-
if (text.includes("rate limit") || text.includes("API rate limit")) error.message = "API rate limit exceeded";
|
|
224
|
-
}
|
|
225
|
-
throw error;
|
|
226
|
-
}
|
|
227
|
-
let data = await response.json();
|
|
228
|
-
return {
|
|
229
|
-
headers: responseHeaders,
|
|
230
|
-
data
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
updateRateLimitInfo(headers) {
|
|
234
|
-
let remaining = headers["x-ratelimit-remaining"];
|
|
235
|
-
if (remaining !== void 0) this.rateLimitRemaining = typeof remaining === "string" ? Number.parseInt(remaining, 10) : remaining;
|
|
236
|
-
let reset = headers["x-ratelimit-reset"];
|
|
237
|
-
if (reset !== void 0) {
|
|
238
|
-
let resetTime = typeof reset === "string" ? Number.parseInt(reset, 10) : reset;
|
|
239
|
-
this.rateLimitReset = /* @__PURE__ */ new Date(resetTime * 1e3);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
function resolveGitHubTokenSync() {
|
|
244
|
-
let fromGithubToken = process.env["GITHUB_TOKEN"];
|
|
245
|
-
if (fromGithubToken && fromGithubToken.trim() !== "") return fromGithubToken.trim();
|
|
246
|
-
let fromGhToken = process.env["GH_TOKEN"];
|
|
247
|
-
if (fromGhToken && fromGhToken.trim() !== "") return fromGhToken.trim();
|
|
248
|
-
try {
|
|
249
|
-
let output = execFileSync("gh", ["auth", "token"], {
|
|
250
|
-
stdio: [
|
|
251
|
-
"ignore",
|
|
252
|
-
"pipe",
|
|
253
|
-
"ignore"
|
|
254
|
-
],
|
|
255
|
-
encoding: "utf8",
|
|
256
|
-
timeout: 500
|
|
257
|
-
});
|
|
258
|
-
let token = output.trim();
|
|
259
|
-
if (token) return token;
|
|
260
|
-
} catch {}
|
|
261
|
-
try {
|
|
262
|
-
let gitConfigPath = join(process.cwd(), ".git", "config");
|
|
263
|
-
let content = readFileSync(gitConfigPath, "utf8");
|
|
264
|
-
let directMatch = content.match(/^\s*(?:github\.(?:oauth-token|token)|hub\.oauthtoken)\s*=\s*(?<token>\S[^\n\r]*)$/mu);
|
|
265
|
-
let directToken = directMatch?.groups?.["token"]?.trim();
|
|
266
|
-
if (directToken) return directToken;
|
|
267
|
-
let currentSection = null;
|
|
268
|
-
for (let rawLine of content.split(/\r?\n/u)) {
|
|
269
|
-
let line = rawLine.trim();
|
|
270
|
-
let sectionMatch = line.match(/^\[(?<name>[^\]]+)\]$/u);
|
|
271
|
-
if (sectionMatch?.groups) {
|
|
272
|
-
currentSection = sectionMatch.groups["name"].toLowerCase();
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
if (currentSection === "github") {
|
|
276
|
-
let tokenMatch = line.match(/^(?:oauth-token|token)\s*=\s*(?<val>\S[^\n\r]*)$/u);
|
|
277
|
-
if (tokenMatch?.groups?.["val"]) return tokenMatch.groups["val"].trim();
|
|
278
|
-
}
|
|
279
|
-
if (currentSection === "hub") {
|
|
280
|
-
let oauthMatch = line.match(/^oauthtoken\s*=\s*(?<val>\S[^\n\r]*)$/u);
|
|
281
|
-
if (oauthMatch?.groups?.["val"]) return oauthMatch.groups["val"].trim();
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
} catch {}
|
|
285
|
-
return void 0;
|
|
286
|
-
}
|
|
287
|
-
function isLikelySha(value) {
|
|
288
|
-
if (typeof value !== "string" || value.trim() === "") return false;
|
|
289
|
-
let normalized = value.replace(/^v/u, "");
|
|
290
|
-
return /^[0-9a-f]{7,40}$/iu.test(normalized);
|
|
291
|
-
}
|
|
292
|
-
export { Client };
|