mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
refactor(scripts): use dependency metadata parsers
This commit is contained in:
+8
-23
@@ -1,11 +1,17 @@
|
||||
// Gh Read script supports OpenClaw repository automation.
|
||||
import { execFileSync, spawnSync } from "node:child_process";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { createPrivateKey, createSign } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { expectDefined } from "../packages/normalization-core/src/expect.js";
|
||||
import { readBoundedResponseText } from "./lib/bounded-response.ts";
|
||||
import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts";
|
||||
import {
|
||||
normalizeGitHubRepo as normalizeRepo,
|
||||
resolveGitHubRepoFromOrigin,
|
||||
} from "./lib/github-repo.ts";
|
||||
|
||||
export { normalizeRepo };
|
||||
|
||||
const APP_ID_ENV = "OPENCLAW_GH_READ_APP_ID";
|
||||
const KEY_FILE_ENV = "OPENCLAW_GH_READ_PRIVATE_KEY_FILE";
|
||||
@@ -65,23 +71,6 @@ export function parseRepoArg(args: string[]): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function normalizeRepo(value: string | null | undefined): string | null {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const withoutProtocol = trimmed.replace(/^[a-z]+:\/\//i, "");
|
||||
const withoutHost = withoutProtocol.replace(/^(?:[^@/]+@)?github\.com[:/]/i, "");
|
||||
const normalized = withoutHost.replace(/\.git$/i, "").replace(/^\/+|\/+$/g, "");
|
||||
const parts = normalized.split("/").filter(Boolean);
|
||||
if (parts.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
|
||||
}
|
||||
|
||||
export function parsePermissionKeys(raw: string | null | undefined): string[] {
|
||||
const trimmed = raw?.trim();
|
||||
if (!trimmed) {
|
||||
@@ -147,11 +136,7 @@ function resolveRepo(args: string[]): string | null {
|
||||
}
|
||||
|
||||
try {
|
||||
const remote = execFileSync("git", ["config", "--get", "remote.origin.url"], {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
}).trim();
|
||||
return normalizeRepo(remote);
|
||||
return resolveGitHubRepoFromOrigin();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { pathToFileURL } from "node:url";
|
||||
import { isRecord } from "../src/utils.js";
|
||||
import { readBoundedResponseText as readBoundedBodyText } from "./lib/bounded-response.ts";
|
||||
import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts";
|
||||
import { resolveGitHubRepoFromOrigin } from "./lib/github-repo.ts";
|
||||
|
||||
function writeStdoutLine(message = ""): void {
|
||||
process.stdout.write(`${message}\n`);
|
||||
@@ -466,33 +467,8 @@ function runGh(args: string[]): string {
|
||||
}
|
||||
|
||||
function resolveRepo(): RepoInfo {
|
||||
const remote = execFileSync("git", ["config", "--get", "remote.origin.url"], {
|
||||
encoding: "utf8",
|
||||
}).trim();
|
||||
|
||||
if (!remote) {
|
||||
throw new Error("Unable to determine repository from git remote.");
|
||||
}
|
||||
|
||||
const normalized = remote.replace(/\.git$/, "");
|
||||
|
||||
if (normalized.startsWith("git@github.com:")) {
|
||||
const slug = normalized.replace("git@github.com:", "");
|
||||
const [owner, name] = slug.split("/");
|
||||
if (owner && name) {
|
||||
return { owner, name };
|
||||
}
|
||||
}
|
||||
|
||||
if (normalized.startsWith("https://github.com/")) {
|
||||
const slug = normalized.replace("https://github.com/", "");
|
||||
const [owner, name] = slug.split("/");
|
||||
if (owner && name) {
|
||||
return { owner, name };
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported GitHub remote: ${remote}`);
|
||||
const [owner, name] = resolveGitHubRepoFromOrigin().split("/") as [string, string];
|
||||
return { owner, name };
|
||||
}
|
||||
|
||||
function fetchIssuePage(repo: RepoInfo, after: string | null): IssuePage {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import hostedGitInfo from "hosted-git-info";
|
||||
|
||||
export function normalizeGitHubRepo(value: string | null | undefined): string | null {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const candidate = /^github\.com\//i.test(trimmed) ? `https://${trimmed}` : trimmed;
|
||||
const info = hostedGitInfo.fromUrl(candidate);
|
||||
return info?.type === "github" && info.user && info.project
|
||||
? `${info.user}/${info.project}`
|
||||
: null;
|
||||
}
|
||||
|
||||
export function resolveGitHubRepoFromOrigin(): string {
|
||||
const remote = execFileSync("git", ["config", "--get", "remote.origin.url"], {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
}).trim();
|
||||
if (!remote) {
|
||||
throw new Error("Unable to determine repository from git remote.");
|
||||
}
|
||||
|
||||
const repo = normalizeGitHubRepo(remote);
|
||||
if (!repo) {
|
||||
throw new Error(`Unsupported GitHub remote: ${remote}`);
|
||||
}
|
||||
return repo;
|
||||
}
|
||||
+5
-48
@@ -2,6 +2,8 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { parse } from "yaml";
|
||||
import { resolveGitHubRepoFromOrigin } from "./lib/github-repo.ts";
|
||||
|
||||
type RepoLabel = {
|
||||
name: string;
|
||||
@@ -48,15 +50,10 @@ const EXTRA_LABELS = [
|
||||
"size: XL",
|
||||
"beta-blocker",
|
||||
] as const;
|
||||
const labelNames = [
|
||||
...new Set([...extractLabelNames(readFileSync(configPath, "utf8")), ...EXTRA_LABELS]),
|
||||
];
|
||||
const labelerConfig = parse(readFileSync(configPath, "utf8")) as Record<string, unknown>;
|
||||
const labelNames = [...new Set([...Object.keys(labelerConfig), ...EXTRA_LABELS])];
|
||||
|
||||
if (!labelNames.length) {
|
||||
throw new Error("labeler.yml must declare at least one label.");
|
||||
}
|
||||
|
||||
const repo = resolveRepo();
|
||||
const repo = resolveGitHubRepoFromOrigin();
|
||||
const existing = fetchExistingLabels(repo);
|
||||
|
||||
const missing = labelNames.filter((label) => !existing.has(label));
|
||||
@@ -84,26 +81,6 @@ for (const label of missing) {
|
||||
console.log(`Created label: ${label}`);
|
||||
}
|
||||
|
||||
function extractLabelNames(contents: string): string[] {
|
||||
const labels: string[] = [];
|
||||
for (const line of contents.split("\n")) {
|
||||
if (!line.trim() || line.trimStart().startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
if (/^\s/.test(line)) {
|
||||
continue;
|
||||
}
|
||||
const match = line.match(/^(["'])(.+)\1\s*:/) ?? line.match(/^([^:]+):/);
|
||||
if (match) {
|
||||
const name = (match[2] ?? match[1] ?? "").trim();
|
||||
if (name) {
|
||||
labels.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
function resolveLabelMetadata(label: string): { color: string; description?: string } {
|
||||
const extraMetadata = EXTRA_LABEL_METADATA.get(label);
|
||||
if (extraMetadata) {
|
||||
@@ -113,26 +90,6 @@ function resolveLabelMetadata(label: string): { color: string; description?: str
|
||||
return { color: COLOR_BY_PREFIX.get(prefix) ?? "ededed" };
|
||||
}
|
||||
|
||||
function resolveRepo(): string {
|
||||
const remote = execFileSync("git", ["config", "--get", "remote.origin.url"], {
|
||||
encoding: "utf8",
|
||||
}).trim();
|
||||
|
||||
if (!remote) {
|
||||
throw new Error("Unable to determine repository from git remote.");
|
||||
}
|
||||
|
||||
if (remote.startsWith("git@github.com:")) {
|
||||
return remote.replace("git@github.com:", "").replace(/\.git$/, "");
|
||||
}
|
||||
|
||||
if (remote.startsWith("https://github.com/")) {
|
||||
return remote.replace("https://github.com/", "").replace(/\.git$/, "");
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported GitHub remote: ${remote}`);
|
||||
}
|
||||
|
||||
function fetchExistingLabels(repoLocal: string): Map<string, RepoLabel> {
|
||||
const raw = execFileSync("gh", ["api", `repos/${repoLocal}/labels?per_page=100`, "--paginate"], {
|
||||
encoding: "utf8",
|
||||
|
||||
@@ -42,8 +42,10 @@ describe("gh-read helpers", () => {
|
||||
it("normalizes repo strings from common git formats", () => {
|
||||
expect(normalizeRepo("openclaw/openclaw")).toBe("openclaw/openclaw");
|
||||
expect(normalizeRepo("github.com/openclaw/openclaw")).toBe("openclaw/openclaw");
|
||||
expect(normalizeRepo("github:openclaw/openclaw")).toBe("openclaw/openclaw");
|
||||
expect(normalizeRepo("https://github.com/openclaw/openclaw.git")).toBe("openclaw/openclaw");
|
||||
expect(normalizeRepo("git@github.com:openclaw/openclaw.git")).toBe("openclaw/openclaw");
|
||||
expect(normalizeRepo("https://gitlab.com/openclaw/openclaw.git")).toBeNull();
|
||||
expect(normalizeRepo("invalid")).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user