257 lines
6.5 KiB
JavaScript
257 lines
6.5 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const rootDir = path.resolve(__dirname, '..');
|
|
const defaultManifestPath = path.join(rootDir, 'dist-electron', 'release-manifest.json');
|
|
const descriptorCandidates = ['latest.yml', 'latest-mac.yml', 'latest-linux.yml'];
|
|
|
|
function normalizeVersion(rawValue) {
|
|
if (typeof rawValue !== 'string') {
|
|
return null;
|
|
}
|
|
|
|
const trimmedValue = rawValue.trim();
|
|
|
|
if (!trimmedValue) {
|
|
return null;
|
|
}
|
|
|
|
return trimmedValue.replace(/^v/i, '').split('+')[0] || null;
|
|
}
|
|
|
|
function parseVersion(rawValue) {
|
|
const normalized = normalizeVersion(rawValue);
|
|
|
|
if (!normalized) {
|
|
return null;
|
|
}
|
|
|
|
const match = normalized.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?$/);
|
|
|
|
if (!match) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
major: Number.parseInt(match[1], 10),
|
|
minor: Number.parseInt(match[2] || '0', 10),
|
|
patch: Number.parseInt(match[3] || '0', 10),
|
|
prerelease: match[4] ? match[4].split('.') : []
|
|
};
|
|
}
|
|
|
|
function comparePrereleaseIdentifiers(left, right) {
|
|
const leftNumeric = /^\d+$/.test(left) ? Number.parseInt(left, 10) : null;
|
|
const rightNumeric = /^\d+$/.test(right) ? Number.parseInt(right, 10) : null;
|
|
|
|
if (leftNumeric !== null && rightNumeric !== null) {
|
|
return leftNumeric - rightNumeric;
|
|
}
|
|
|
|
if (leftNumeric !== null) {
|
|
return -1;
|
|
}
|
|
|
|
if (rightNumeric !== null) {
|
|
return 1;
|
|
}
|
|
|
|
return left.localeCompare(right);
|
|
}
|
|
|
|
function compareVersions(leftValue, rightValue) {
|
|
const left = parseVersion(leftValue);
|
|
const right = parseVersion(rightValue);
|
|
|
|
if (!left && !right) {
|
|
return 0;
|
|
}
|
|
|
|
if (!left) {
|
|
return -1;
|
|
}
|
|
|
|
if (!right) {
|
|
return 1;
|
|
}
|
|
|
|
if (left.major !== right.major) {
|
|
return left.major - right.major;
|
|
}
|
|
|
|
if (left.minor !== right.minor) {
|
|
return left.minor - right.minor;
|
|
}
|
|
|
|
if (left.patch !== right.patch) {
|
|
return left.patch - right.patch;
|
|
}
|
|
|
|
if (left.prerelease.length === 0 && right.prerelease.length === 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (left.prerelease.length === 0) {
|
|
return 1;
|
|
}
|
|
|
|
if (right.prerelease.length === 0) {
|
|
return -1;
|
|
}
|
|
|
|
const maxLength = Math.max(left.prerelease.length, right.prerelease.length);
|
|
|
|
for (let index = 0; index < maxLength; index += 1) {
|
|
const leftIdentifier = left.prerelease[index];
|
|
const rightIdentifier = right.prerelease[index];
|
|
|
|
if (!leftIdentifier) {
|
|
return -1;
|
|
}
|
|
|
|
if (!rightIdentifier) {
|
|
return 1;
|
|
}
|
|
|
|
const comparison = comparePrereleaseIdentifiers(leftIdentifier, rightIdentifier);
|
|
|
|
if (comparison !== 0) {
|
|
return comparison;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
function parseArgs(argv) {
|
|
const args = {};
|
|
|
|
for (let index = 0; index < argv.length; index += 1) {
|
|
const token = argv[index];
|
|
|
|
if (!token.startsWith('--')) {
|
|
continue;
|
|
}
|
|
|
|
const key = token.slice(2);
|
|
const nextToken = argv[index + 1];
|
|
|
|
if (!nextToken || nextToken.startsWith('--')) {
|
|
args[key] = true;
|
|
continue;
|
|
}
|
|
|
|
args[key] = nextToken;
|
|
index += 1;
|
|
}
|
|
|
|
return args;
|
|
}
|
|
|
|
function ensureHttpUrl(rawValue) {
|
|
if (typeof rawValue !== 'string' || rawValue.trim().length === 0) {
|
|
throw new Error('A release feed URL is required. Pass it with --feed-url.');
|
|
}
|
|
|
|
const parsedUrl = new URL(rawValue.trim());
|
|
|
|
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
throw new Error('The release feed URL must use http:// or https://.');
|
|
}
|
|
|
|
return parsedUrl.toString().replace(/\/$/, '');
|
|
}
|
|
|
|
function readJsonIfExists(filePath) {
|
|
if (!fs.existsSync(filePath)) {
|
|
return null;
|
|
}
|
|
|
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
}
|
|
|
|
function warnIfNoDescriptors(distPath) {
|
|
const availableDescriptors = descriptorCandidates.filter((fileName) => {
|
|
return fs.existsSync(path.join(distPath, fileName));
|
|
});
|
|
|
|
if (availableDescriptors.length === 0) {
|
|
console.warn('[release-manifest] Warning: no latest*.yml descriptor was found in dist-electron/.');
|
|
console.warn('[release-manifest] Upload a feed directory that contains the Electron Builder update descriptors.');
|
|
}
|
|
}
|
|
|
|
function buildDefaultManifest(version) {
|
|
return {
|
|
schemaVersion: 1,
|
|
generatedAt: new Date().toISOString(),
|
|
minimumServerVersion: version,
|
|
pollIntervalMinutes: 30,
|
|
versions: []
|
|
};
|
|
}
|
|
|
|
function sortManifestVersions(versions) {
|
|
return [...versions].sort((left, right) => compareVersions(right.version, left.version));
|
|
}
|
|
|
|
function main() {
|
|
const args = parseArgs(process.argv.slice(2));
|
|
const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));
|
|
const version = normalizeVersion(args.version || packageJson.version);
|
|
|
|
if (!version) {
|
|
throw new Error('Unable to determine the release version. Pass it with --version.');
|
|
}
|
|
|
|
const feedUrl = ensureHttpUrl(args['feed-url']);
|
|
const manifestPath = path.resolve(rootDir, args.manifest || defaultManifestPath);
|
|
const existingPath = path.resolve(rootDir, args.existing || manifestPath);
|
|
const existingManifest = readJsonIfExists(existingPath) || buildDefaultManifest(version);
|
|
const distPath = path.join(rootDir, 'dist-electron');
|
|
|
|
if (fs.existsSync(distPath)) {
|
|
warnIfNoDescriptors(distPath);
|
|
}
|
|
|
|
const nextEntry = {
|
|
version,
|
|
feedUrl,
|
|
publishedAt: typeof args['published-at'] === 'string'
|
|
? args['published-at']
|
|
: new Date().toISOString(),
|
|
...(typeof args.notes === 'string' && args.notes.trim().length > 0
|
|
? { notes: args.notes.trim() }
|
|
: {})
|
|
};
|
|
const nextManifest = {
|
|
schemaVersion: 1,
|
|
generatedAt: new Date().toISOString(),
|
|
minimumServerVersion: normalizeVersion(args['minimum-server-version'])
|
|
|| normalizeVersion(existingManifest.minimumServerVersion)
|
|
|| version,
|
|
pollIntervalMinutes: Number.isFinite(Number(args['poll-interval-minutes']))
|
|
? Math.max(5, Math.round(Number(args['poll-interval-minutes'])))
|
|
: Math.max(5, Math.round(Number(existingManifest.pollIntervalMinutes || 30))),
|
|
versions: sortManifestVersions(
|
|
[...(Array.isArray(existingManifest.versions) ? existingManifest.versions : [])]
|
|
.filter((entry) => normalizeVersion(entry && entry.version) !== version)
|
|
.concat(nextEntry)
|
|
)
|
|
};
|
|
|
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
fs.writeFileSync(manifestPath, `${JSON.stringify(nextManifest, null, 2)}\n`, 'utf8');
|
|
|
|
console.log(`[release-manifest] Wrote ${manifestPath}`);
|
|
}
|
|
|
|
try {
|
|
main();
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
|
|
console.error(`[release-manifest] ${message}`);
|
|
process.exitCode = 1;
|
|
}
|