修改 meta.json 规则, 涉及version 、group 字段.
This commit is contained in:
+43
-48
@@ -12,7 +12,15 @@ function normalizeRemotePath(value) {
|
||||
if (!value || value === "/") {
|
||||
return "/";
|
||||
}
|
||||
return path.posix.normalize("/" + String(value).replace(/\\/g, "/")).replace(/\/+$|^$/g, "") || "/";
|
||||
return path.posix.normalize("/" + String(value).replace(/\\/g, "/")).replace(/\/+$/g, "") || "/";
|
||||
}
|
||||
|
||||
function normalizeGroup(group) {
|
||||
return normalizeRemotePath(group == null ? "/modules" : group);
|
||||
}
|
||||
|
||||
function joinRemote(...items) {
|
||||
return normalizeRemotePath(items.filter((item) => item !== undefined && item !== null && item !== "").join("/"));
|
||||
}
|
||||
|
||||
function normalizeOs(value) {
|
||||
@@ -136,7 +144,8 @@ module.exports = class Remote {
|
||||
}
|
||||
|
||||
async list(projectId) {
|
||||
const state = this.getInstallState(projectId);
|
||||
const parsed = this.parseProjectVersion(projectId);
|
||||
const state = this.getInstallState(parsed.projectId);
|
||||
for (const version of state.installed_versions) {
|
||||
const current = version === state.current_version ? "*" : " ";
|
||||
$logger.info(`${current} ${version}${current === "*" ? " (current)" : ""}`);
|
||||
@@ -150,14 +159,15 @@ module.exports = class Remote {
|
||||
}
|
||||
|
||||
async rollback(projectId) {
|
||||
const state = this.getInstallState(projectId);
|
||||
const parsed = this.parseProjectVersion(projectId);
|
||||
const state = this.getInstallState(parsed.projectId);
|
||||
const history = [...state.history].reverse();
|
||||
const current = state.current_version;
|
||||
const previous = history.find((item) => item.version && item.version !== current && state.installed_versions.includes(item.version));
|
||||
if (!previous) {
|
||||
throw new Error(`没有可回滚版本 ${projectId}`);
|
||||
throw new Error(`没有可回滚版本 ${parsed.projectId}`);
|
||||
}
|
||||
return this.activate(projectId, previous.version, "rollback");
|
||||
return this.activate(parsed.projectId, previous.version, "rollback");
|
||||
}
|
||||
|
||||
async remove(projectVersion) {
|
||||
@@ -183,10 +193,15 @@ module.exports = class Remote {
|
||||
parseProjectVersion(value) {
|
||||
const text = String(value || "kit");
|
||||
const index = text.lastIndexOf("@");
|
||||
if (index <= 0) {
|
||||
return { projectId: text, version: undefined };
|
||||
const target = index <= 0 ? text : text.substring(0, index);
|
||||
const version = index <= 0 ? undefined : text.substring(index + 1);
|
||||
if (target.startsWith("/")) {
|
||||
const normalized = normalizeRemotePath(target);
|
||||
const projectId = path.posix.basename(normalized);
|
||||
const group = normalizeGroup(path.posix.dirname(normalized));
|
||||
return { projectId, version, group };
|
||||
}
|
||||
return { projectId: text.substring(0, index), version: text.substring(index + 1) };
|
||||
return { projectId: target, version, group: "/modules" };
|
||||
}
|
||||
|
||||
getLocalModuleRoot(projectId) {
|
||||
@@ -255,6 +270,10 @@ module.exports = class Remote {
|
||||
];
|
||||
}
|
||||
|
||||
getRemoteProjectRoot(projectId, group = "/modules") {
|
||||
return joinRemote(normalizeGroup(group), projectId);
|
||||
}
|
||||
|
||||
async ensureDir(remotePath) {
|
||||
const target = normalizeRemotePath(remotePath);
|
||||
if (target === "/") {
|
||||
@@ -299,51 +318,29 @@ module.exports = class Remote {
|
||||
return this.ensureDir(folder);
|
||||
}
|
||||
|
||||
async wGetModuleInfo(projectId) {
|
||||
return this.getProjectMetadata(projectId);
|
||||
async wGetModuleInfo(projectId, group = "/modules") {
|
||||
return this.getProjectMetadata(projectId, group);
|
||||
}
|
||||
|
||||
async getProjectMetadata(projectId) {
|
||||
const metadata = await this.getJson(`/${projectId}/metadata.json`, undefined);
|
||||
async getProjectMetadata(projectId, group = "/modules") {
|
||||
const metadata = await this.getJson(joinRemote(this.getRemoteProjectRoot(projectId, group), "metadata.json"), undefined);
|
||||
if (metadata) {
|
||||
metadata.group = normalizeGroup(metadata.group == null ? group : metadata.group);
|
||||
return metadata;
|
||||
}
|
||||
throw new Error(`远程模块不存在 ${normalizeGroup(group)}/${projectId}`);
|
||||
}
|
||||
|
||||
async getVersionMetadata(projectId, version, group = "/modules") {
|
||||
const metadata = await this.getJson(joinRemote(this.getRemoteProjectRoot(projectId, group), version, "version.json"), undefined);
|
||||
if (metadata) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
const legacy = await this.getJson(`/modules/${projectId}/meta.json`, undefined);
|
||||
if (legacy) {
|
||||
return {
|
||||
project_id: legacy.id || projectId,
|
||||
description: legacy.desc || legacy.name || "",
|
||||
latest_version: legacy.latest && legacy.latest.version,
|
||||
versions: (legacy.versions || []).map((item) => item.version || item),
|
||||
extra: legacy
|
||||
};
|
||||
}
|
||||
throw new Error(`远程模块不存在 ${projectId}`);
|
||||
throw new Error(`远程版本不存在 ${normalizeGroup(group)}/${projectId}@${version}`);
|
||||
}
|
||||
|
||||
async getVersionMetadata(projectId, version) {
|
||||
const metadata = await this.getJson(`/${projectId}/${version}/version.json`, undefined);
|
||||
if (metadata) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
const project = await this.getProjectMetadata(projectId);
|
||||
const legacyVersion = project.extra && project.extra.versions && project.extra.versions.find((item) => (item.version || item) === version);
|
||||
if (legacyVersion && legacyVersion.dist) {
|
||||
return {
|
||||
version,
|
||||
artifacts: legacyVersion.dist.map((dist) => ({
|
||||
path: `${normalizeOs(dist.os)}/${normalizeArch(dist.platform)}/${dist.filename}`,
|
||||
type: "executable"
|
||||
}))
|
||||
};
|
||||
}
|
||||
throw new Error(`远程版本不存在 ${projectId}@${version}`);
|
||||
}
|
||||
|
||||
async downloadArtifact(projectId, version, artifact, versionDir) {
|
||||
const remotePath = `/${projectId}/${version}/${artifact.path}`;
|
||||
async downloadArtifact(projectId, version, artifact, versionDir, group = "/modules") {
|
||||
const remotePath = joinRemote(this.getRemoteProjectRoot(projectId, group), version, artifact.path);
|
||||
const localPath = path.resolve(versionDir, artifact.path);
|
||||
await this.wDownload(remotePath, localPath);
|
||||
return localPath;
|
||||
@@ -362,5 +359,3 @@ module.exports = class Remote {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -50,13 +50,14 @@ function unpackArtifact(artifact, localPath, versionDir) {
|
||||
module.exports = async function install(remote, id) {
|
||||
const parsed = remote.parseProjectVersion(id || "kit");
|
||||
const projectId = parsed.projectId;
|
||||
const project = await remote.getProjectMetadata(projectId);
|
||||
const version = parsed.version || project.latest_version;
|
||||
const group = parsed.group || "/modules";
|
||||
const project = await remote.getProjectMetadata(projectId, group);
|
||||
const version = parsed.version || project.version;
|
||||
if (!version) {
|
||||
throw new Error(`远程模块没有可安装版本 ${projectId}`);
|
||||
}
|
||||
|
||||
const versionMeta = await remote.getVersionMetadata(projectId, version);
|
||||
const versionMeta = await remote.getVersionMetadata(projectId, version, project.group || group);
|
||||
const artifacts = remote.getMatchingArtifacts(versionMeta);
|
||||
if (artifacts.length < 1) {
|
||||
throw new Error(`没有匹配当前平台的文件 ${projectId}@${version}`);
|
||||
@@ -67,7 +68,7 @@ module.exports = async function install(remote, id) {
|
||||
fs.mkdirSync(versionDir, { recursive: true });
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
const localPath = await remote.downloadArtifact(projectId, version, artifact, versionDir);
|
||||
const localPath = await remote.downloadArtifact(projectId, version, artifact, versionDir, project.group || group);
|
||||
verifyChecksum(localPath, artifact.checksum || (versionMeta.checksums && versionMeta.checksums[artifact.path]));
|
||||
unpackArtifact(artifact, localPath, versionDir);
|
||||
}
|
||||
|
||||
+60
-22
@@ -7,6 +7,21 @@ function now() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function normalizeRemotePath(value) {
|
||||
if (!value || value === "/") {
|
||||
return "/";
|
||||
}
|
||||
return path.posix.normalize("/" + String(value).replace(/\\/g, "/")).replace(/\/+$/g, "") || "/";
|
||||
}
|
||||
|
||||
function normalizeGroup(group) {
|
||||
return normalizeRemotePath(group == null ? "/modules" : group);
|
||||
}
|
||||
|
||||
function joinRemote(...items) {
|
||||
return normalizeRemotePath(items.filter((item) => item !== undefined && item !== null && item !== "").join("/"));
|
||||
}
|
||||
|
||||
function normalizeOs(value) {
|
||||
const name = String(value || "any").toLowerCase();
|
||||
if (["windows_nt", "win32", "windows"].includes(name)) {
|
||||
@@ -70,16 +85,6 @@ function archiveFormat(filePath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function buildVersion(localMeta) {
|
||||
if (localMeta.release_version) {
|
||||
return localMeta.release_version;
|
||||
}
|
||||
if (localMeta.version) {
|
||||
return localMeta.version;
|
||||
}
|
||||
return new Date().toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
||||
}
|
||||
|
||||
function readLocalMeta(localFile) {
|
||||
const base = localFile ? path.resolve(localFile) : process.cwd();
|
||||
const metaFile = fs.statSync(base).isDirectory() ? path.resolve(base, "meta.json") : path.resolve(process.cwd(), "meta.json");
|
||||
@@ -93,7 +98,8 @@ function readLocalMeta(localFile) {
|
||||
|
||||
return {
|
||||
id: path.basename(localFile, path.extname(localFile)).toLowerCase(),
|
||||
version: buildVersion({}),
|
||||
group: "/modules",
|
||||
version: "",
|
||||
desc: "",
|
||||
dist: [{ path: localFile, os: "any", platform: "any", filename: path.basename(localFile) }]
|
||||
};
|
||||
@@ -123,6 +129,34 @@ function buildArtifacts(localMeta) {
|
||||
});
|
||||
}
|
||||
|
||||
function bumpTailVersion(version) {
|
||||
const text = String(version || "").trim();
|
||||
if (!text) {
|
||||
return "1.0";
|
||||
}
|
||||
const match = text.match(/^(.*?)(\d+)$/);
|
||||
if (!match) {
|
||||
return `${text}.1`;
|
||||
}
|
||||
return `${match[1]}${Number(match[2]) + 1}`;
|
||||
}
|
||||
|
||||
async function resolveVersion(remote, projectRoot, localMeta) {
|
||||
const explicitVersion = String(localMeta.version || "").trim();
|
||||
if (explicitVersion) {
|
||||
if (await remote.client.exists(joinRemote(projectRoot, explicitVersion))) {
|
||||
throw new Error(`远程版本已存在 ${projectRoot}@${explicitVersion}`);
|
||||
}
|
||||
return explicitVersion;
|
||||
}
|
||||
|
||||
const metadata = await remote.getJson(joinRemote(projectRoot, "metadata.json"), undefined);
|
||||
if (metadata && metadata.version) {
|
||||
return bumpTailVersion(metadata.version);
|
||||
}
|
||||
return "1.0";
|
||||
}
|
||||
|
||||
module.exports = async function publish(remote, localFile, remotePath) {
|
||||
if (remotePath) {
|
||||
const target = path.posix.resolve("/", remotePath.replace(/\\/g, "/"));
|
||||
@@ -137,9 +171,11 @@ module.exports = async function publish(remote, localFile, remotePath) {
|
||||
throw new Error("meta.json 缺少 project_id/id/name");
|
||||
}
|
||||
|
||||
const version = buildVersion(localMeta);
|
||||
const group = normalizeGroup(localMeta.group);
|
||||
const projectRoot = remote.getRemoteProjectRoot ? remote.getRemoteProjectRoot(projectId, group) : joinRemote(group, projectId);
|
||||
const version = await resolveVersion(remote, projectRoot, localMeta);
|
||||
const artifacts = buildArtifacts(localMeta);
|
||||
await remote.ensureDir(`/${projectId}/${version}`);
|
||||
await remote.ensureDir(joinRemote(projectRoot, version));
|
||||
|
||||
const versionMeta = {
|
||||
version,
|
||||
@@ -150,27 +186,29 @@ module.exports = async function publish(remote, localFile, remotePath) {
|
||||
};
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
await remote.wUpload(artifact.localPath, `/${projectId}/${version}/${artifact.path}`);
|
||||
await remote.wUpload(artifact.localPath, joinRemote(projectRoot, version, artifact.path));
|
||||
}
|
||||
await remote.putJson(`/${projectId}/${version}/version.json`, versionMeta);
|
||||
await remote.putJson(joinRemote(projectRoot, version, "version.json"), versionMeta);
|
||||
|
||||
const metadata = await remote.getJson(`/${projectId}/metadata.json`, {
|
||||
const metadata = await remote.getJson(joinRemote(projectRoot, "metadata.json"), {
|
||||
project_id: projectId,
|
||||
group,
|
||||
official_url: localMeta.official_url || localMeta.vcs || "",
|
||||
description: localMeta.description || localMeta.desc || "",
|
||||
latest_version: version,
|
||||
versions: [],
|
||||
version,
|
||||
fetch_rules: localMeta.fetch_rules || [],
|
||||
extra: {}
|
||||
});
|
||||
|
||||
metadata.project_id = projectId;
|
||||
metadata.group = group;
|
||||
metadata.official_url = metadata.official_url || localMeta.official_url || localMeta.vcs || "";
|
||||
metadata.description = localMeta.description || localMeta.desc || metadata.description || "";
|
||||
metadata.latest_version = version;
|
||||
metadata.versions = Array.from(new Set([...(metadata.versions || []), version]));
|
||||
metadata.version = version;
|
||||
delete metadata.versions;
|
||||
delete metadata.latest_version;
|
||||
metadata.extra = Object.assign({}, metadata.extra || {}, localMeta.extra || {});
|
||||
|
||||
await remote.putJson(`/${projectId}/metadata.json`, metadata);
|
||||
$logger.info(`文件上传成功 ${projectId}@${version}`);
|
||||
await remote.putJson(joinRemote(projectRoot, "metadata.json"), metadata);
|
||||
$logger.info(`文件上传成功 ${group}/${projectId}@${version}`);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user