mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
chore: release v1.22.0
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = {
|
||||
extraAsset: [],
|
||||
};
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const item = argv[index];
|
||||
if (!item.startsWith("--")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = item.slice(2);
|
||||
const next = argv[index + 1];
|
||||
const value = !next || next.startsWith("--") ? "true" : next;
|
||||
if (value !== "true") {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if (key === "extra-asset") {
|
||||
args.extraAsset.push(value);
|
||||
} else {
|
||||
args[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function normalizeVersion(value) {
|
||||
return String(value || "")
|
||||
.trim()
|
||||
.replace(/^v/, "");
|
||||
}
|
||||
|
||||
function listFilesRecursive(root) {
|
||||
if (!fs.existsSync(root)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fs
|
||||
.readdirSync(root, { withFileTypes: true })
|
||||
.flatMap((entry) => {
|
||||
const filePath = path.join(root, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
return listFilesRecursive(filePath);
|
||||
}
|
||||
return entry.isFile() ? [filePath] : [];
|
||||
})
|
||||
.sort();
|
||||
}
|
||||
|
||||
function isPathInside(parent, child) {
|
||||
const relative = path.relative(parent, child);
|
||||
return (
|
||||
Boolean(relative) &&
|
||||
!relative.startsWith("..") &&
|
||||
!path.isAbsolute(relative)
|
||||
);
|
||||
}
|
||||
|
||||
function targetFromAssetPath(assetsDir, filePath) {
|
||||
if (!isPathInside(assetsDir, filePath)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return path.relative(assetsDir, filePath).split(path.sep)[0] || "";
|
||||
}
|
||||
|
||||
function duplicateTargetLabel(target) {
|
||||
if (target === "aarch64-apple-darwin") {
|
||||
return "macos-arm64";
|
||||
}
|
||||
if (target === "x86_64-apple-darwin") {
|
||||
return "macos-x64";
|
||||
}
|
||||
if (target === "x86_64-pc-windows-msvc") {
|
||||
return "windows-x64";
|
||||
}
|
||||
if (target === "x86_64-unknown-linux-gnu") {
|
||||
return "linux-x64";
|
||||
}
|
||||
return target.replace(/[^A-Za-z0-9._-]+/g, "-") || "asset";
|
||||
}
|
||||
|
||||
function macUpdaterAssetName(basename, target, version) {
|
||||
let arch = "";
|
||||
if (target === "aarch64-apple-darwin") {
|
||||
arch = "aarch64";
|
||||
} else if (target === "x86_64-apple-darwin") {
|
||||
arch = "x64";
|
||||
}
|
||||
|
||||
if (!arch) {
|
||||
return "";
|
||||
}
|
||||
if (basename === "Lime.app.tar.gz") {
|
||||
return `Lime_${version}_${arch}.app.tar.gz`;
|
||||
}
|
||||
if (basename === "Lime.app.tar.gz.sig") {
|
||||
return `Lime_${version}_${arch}.app.tar.gz.sig`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function githubAssetName(filePath, context) {
|
||||
const basename = path.basename(filePath);
|
||||
const target = targetFromAssetPath(context.assetsDir, filePath);
|
||||
const duplicateCount = context.basenameCounts.get(basename) || 0;
|
||||
|
||||
if (duplicateCount <= 1) {
|
||||
return basename;
|
||||
}
|
||||
|
||||
const macName = macUpdaterAssetName(basename, target, context.version);
|
||||
if (macName) {
|
||||
return macName;
|
||||
}
|
||||
|
||||
return `${duplicateTargetLabel(target)}-${basename}`;
|
||||
}
|
||||
|
||||
function prepareGitHubReleaseAssets(options) {
|
||||
const assetsDir = path.resolve(options.assetsDir || "release-assets");
|
||||
const outDir = path.resolve(options.outDir || "release-github-assets");
|
||||
const version = normalizeVersion(options.version);
|
||||
const extraAssets = (options.extraAssets || []).map((item) =>
|
||||
path.resolve(item),
|
||||
);
|
||||
|
||||
if (!version) {
|
||||
throw new Error("version is required");
|
||||
}
|
||||
if (!fs.existsSync(assetsDir)) {
|
||||
throw new Error(`assets directory is missing: ${assetsDir}`);
|
||||
}
|
||||
|
||||
const releaseAssetFiles = listFilesRecursive(assetsDir).filter(
|
||||
(filePath) => !/^latest.*\.json$/i.test(path.basename(filePath)),
|
||||
);
|
||||
const inputFiles = [...releaseAssetFiles, ...extraAssets].sort();
|
||||
|
||||
for (const filePath of inputFiles) {
|
||||
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
||||
throw new Error(`release asset is missing: ${filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
const basenameCounts = new Map();
|
||||
for (const filePath of inputFiles) {
|
||||
const basename = path.basename(filePath);
|
||||
basenameCounts.set(basename, (basenameCounts.get(basename) || 0) + 1);
|
||||
}
|
||||
|
||||
fs.rmSync(outDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
|
||||
const usedNames = new Set();
|
||||
const copied = [];
|
||||
for (const filePath of inputFiles) {
|
||||
const name = githubAssetName(filePath, {
|
||||
assetsDir,
|
||||
basenameCounts,
|
||||
version,
|
||||
});
|
||||
if (usedNames.has(name)) {
|
||||
throw new Error(`duplicate GitHub release asset name: ${name}`);
|
||||
}
|
||||
usedNames.add(name);
|
||||
|
||||
const destination = path.join(outDir, name);
|
||||
fs.copyFileSync(filePath, destination);
|
||||
copied.push({
|
||||
name,
|
||||
source: filePath,
|
||||
destination,
|
||||
});
|
||||
}
|
||||
|
||||
return copied.sort((left, right) => left.name.localeCompare(right.name));
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
const copied = prepareGitHubReleaseAssets({
|
||||
assetsDir: args["assets-dir"],
|
||||
extraAssets: args.extraAsset,
|
||||
outDir: args["out-dir"],
|
||||
version:
|
||||
args.version || process.env.RELEASE_TAG || process.env.GITHUB_REF_NAME,
|
||||
});
|
||||
|
||||
console.log("Prepared GitHub release upload assets:");
|
||||
for (const item of copied) {
|
||||
console.log(` - ${path.relative(process.cwd(), item.destination)}`);
|
||||
}
|
||||
}
|
||||
|
||||
const isCli =
|
||||
process.argv[1] &&
|
||||
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
||||
|
||||
if (isCli) {
|
||||
main();
|
||||
}
|
||||
|
||||
export { prepareGitHubReleaseAssets };
|
||||
@@ -4,6 +4,7 @@ import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { planR2ReleaseCleanup } from "./plan-r2-release-cleanup.mjs";
|
||||
import { prepareGitHubReleaseAssets } from "./prepare-github-release-assets.mjs";
|
||||
import {
|
||||
collectUpdaterManifest,
|
||||
writeOutputs,
|
||||
@@ -215,9 +216,7 @@ describe("release updater manifest", () => {
|
||||
path.join(assetsDir, "aarch64-apple-darwin", "Lime.app.tar.gz.sig"),
|
||||
encodedSignature,
|
||||
);
|
||||
writeFile(
|
||||
path.join(assetsDir, "x86_64-apple-darwin", "Lime.app.tar.gz"),
|
||||
);
|
||||
writeFile(path.join(assetsDir, "x86_64-apple-darwin", "Lime.app.tar.gz"));
|
||||
writeFile(
|
||||
path.join(assetsDir, "x86_64-apple-darwin", "Lime.app.tar.gz.sig"),
|
||||
rawSignature,
|
||||
@@ -243,11 +242,7 @@ describe("release updater manifest", () => {
|
||||
baseUrl: "https://updates.limecloud.com",
|
||||
channel: "stable",
|
||||
notes: "notes",
|
||||
requiredPlatforms: [
|
||||
"darwin-aarch64",
|
||||
"darwin-x86_64",
|
||||
"windows-x86_64",
|
||||
],
|
||||
requiredPlatforms: ["darwin-aarch64", "darwin-x86_64", "windows-x86_64"],
|
||||
version: "v1.20.0",
|
||||
});
|
||||
|
||||
@@ -292,3 +287,63 @@ describe("R2 release cleanup", () => {
|
||||
expect(plan.protectedVersions).toContain("1.16.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("GitHub release asset staging", () => {
|
||||
it("同名 macOS updater 资产上传 GitHub Release 前应重命名", () => {
|
||||
const root = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "lime-github-release-assets-"),
|
||||
);
|
||||
const assetsDir = path.join(root, "release-assets");
|
||||
const outDir = path.join(root, "release-github-assets");
|
||||
const latestPath = path.join(root, "release-updater", "latest.json");
|
||||
|
||||
writeFile(path.join(assetsDir, "aarch64-apple-darwin", "Lime.app.tar.gz"));
|
||||
writeFile(
|
||||
path.join(assetsDir, "aarch64-apple-darwin", "Lime.app.tar.gz.sig"),
|
||||
"arm-sig",
|
||||
);
|
||||
writeFile(
|
||||
path.join(assetsDir, "aarch64-apple-darwin", "Lime_1.22.0_aarch64.dmg"),
|
||||
);
|
||||
writeFile(path.join(assetsDir, "x86_64-apple-darwin", "Lime.app.tar.gz"));
|
||||
writeFile(
|
||||
path.join(assetsDir, "x86_64-apple-darwin", "Lime.app.tar.gz.sig"),
|
||||
"x64-sig",
|
||||
);
|
||||
writeFile(
|
||||
path.join(assetsDir, "x86_64-apple-darwin", "Lime_1.22.0_x64.dmg"),
|
||||
);
|
||||
writeFile(latestPath, "{}");
|
||||
|
||||
const copied = prepareGitHubReleaseAssets({
|
||||
assetsDir,
|
||||
extraAssets: [latestPath],
|
||||
outDir,
|
||||
version: "v1.22.0",
|
||||
});
|
||||
|
||||
expect(copied.map((item) => item.name).sort()).toEqual(
|
||||
[
|
||||
"Lime_1.22.0_aarch64.app.tar.gz",
|
||||
"Lime_1.22.0_aarch64.app.tar.gz.sig",
|
||||
"Lime_1.22.0_aarch64.dmg",
|
||||
"Lime_1.22.0_x64.app.tar.gz",
|
||||
"Lime_1.22.0_x64.app.tar.gz.sig",
|
||||
"Lime_1.22.0_x64.dmg",
|
||||
"latest.json",
|
||||
].sort(),
|
||||
);
|
||||
expect(
|
||||
fs.readFileSync(
|
||||
path.join(outDir, "Lime_1.22.0_aarch64.app.tar.gz.sig"),
|
||||
"utf8",
|
||||
),
|
||||
).toBe("arm-sig");
|
||||
expect(
|
||||
fs.readFileSync(
|
||||
path.join(outDir, "Lime_1.22.0_x64.app.tar.gz.sig"),
|
||||
"utf8",
|
||||
),
|
||||
).toBe("x64-sig");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user