2017-08-20 02:58:36 +00:00
|
|
|
const xml = require('xml');
|
|
|
|
const fs = require('fs-extra');
|
|
|
|
const exec = require('execa');
|
|
|
|
const sha1 = require('sha1-file');
|
|
|
|
const req = require('request-promise');
|
|
|
|
const zip_bin = require('7zip-bin').path7za;
|
|
|
|
|
2017-08-20 03:42:53 +00:00
|
|
|
const logger = require('winston');
|
|
|
|
logger.exitOnError = false;
|
|
|
|
logger.add(logger.transports.File, { filename: './qt-installer-repository.log' });
|
|
|
|
|
|
|
|
const distDir = "./dist";
|
2017-08-20 02:58:36 +00:00
|
|
|
|
|
|
|
function mkdirIfNotExists(path) {
|
2017-08-20 03:42:53 +00:00
|
|
|
if (!fs.existsSync(path)) { fs.mkdirSync(path); }
|
2017-08-20 02:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function getReleases(repo) {
|
|
|
|
const result = await req({
|
|
|
|
uri: `https://api.github.com/repos/${repo}/releases`,
|
|
|
|
headers: {
|
|
|
|
"Accept": "application/vnd.github.v3+json",
|
|
|
|
"User-Agent": "Citra Installer - Repo (j-selby)"
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return JSON.parse(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTopResultFor(jsonData, platform) {
|
|
|
|
for (let releaseKey in jsonData) {
|
|
|
|
const release = jsonData[releaseKey];
|
|
|
|
for (let assetKey in release.assets) {
|
|
|
|
const asset = release.assets[assetKey];
|
|
|
|
if (asset.name.indexOf(platform) !== -1 && asset.name.endsWith(".7z")) {
|
|
|
|
return {
|
|
|
|
"release_id": release.tag_name.split("-")[1],
|
|
|
|
"published_at": release.published_at.substr(0, 10),
|
|
|
|
"name": asset.name,
|
|
|
|
"size": asset.size,
|
2017-08-20 03:42:53 +00:00
|
|
|
"hash": release.tag_name
|
2017-08-20 02:58:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {"notFound": true};
|
|
|
|
}
|
|
|
|
|
2017-08-20 03:42:53 +00:00
|
|
|
fs.removeSync(distDir);
|
|
|
|
mkdirIfNotExists(distDir);
|
2017-08-20 02:58:36 +00:00
|
|
|
|
|
|
|
// The Qt Installer Framework is a pain to build or download.
|
|
|
|
// Because all we need are a few 7-zipped + xml files, we might as well generate them for ourselves.
|
|
|
|
let targets = [
|
|
|
|
{
|
|
|
|
"Name": "org.citra.nightly.%platform%",
|
|
|
|
"DisplayName": "Citra Nightly",
|
|
|
|
"Description": "The nightly builds of Citra are official, tested versions of Citra that are known to work.\n" +
|
|
|
|
"(%platform%, commit: %commithash%, release date: %releasedate%)",
|
|
|
|
"Repo": "citra-emu/citra-nightly",
|
|
|
|
"ScriptName": "nightly",
|
|
|
|
"Default": "script",
|
|
|
|
"Licenses": [
|
|
|
|
{"License": [{ _attr: { file: 'license.txt', name: "GNU General Public License v2.0" }}]}
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
2017-08-20 03:42:53 +00:00
|
|
|
"Name": "org.citra.canary.%platform%",
|
|
|
|
"DisplayName": "Citra Canary",
|
2017-08-20 02:58:36 +00:00
|
|
|
"Description": "An in-development version of Citra that uses changes that are relatively untested.\n" +
|
|
|
|
"(%platform%, commit: %commithash%, release date: %releasedate%)",
|
2017-08-20 03:42:53 +00:00
|
|
|
"Repo": "citra-emu/citra-canary",
|
|
|
|
"ScriptName": "canary",
|
2017-08-20 02:58:36 +00:00
|
|
|
"Default": "script",
|
|
|
|
"Licenses": [
|
|
|
|
{"License": [{ _attr: { file: 'license.txt', name: "GNU General Public License v2.0" }}]}
|
|
|
|
],
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
async function execute() {
|
|
|
|
// Get Git information
|
2017-08-20 03:42:53 +00:00
|
|
|
logger.debug("Getting release info...");
|
2017-08-20 02:58:36 +00:00
|
|
|
for (result_key in targets) {
|
|
|
|
const target = targets[result_key];
|
|
|
|
target.Repo = await getReleases(target.Repo);
|
|
|
|
}
|
|
|
|
|
2017-08-20 03:42:53 +00:00
|
|
|
logger.debug("Building metadata...");
|
2017-08-20 02:58:36 +00:00
|
|
|
// Updates.xml
|
|
|
|
let updates = [
|
|
|
|
{"ApplicationName": "{AnyApplication}"},
|
|
|
|
{"ApplicationVersion": "1.0.0"}, // Separate from nightly/be versions
|
|
|
|
{"Checksum": false} // As they are pulled straight from Github
|
|
|
|
];
|
|
|
|
|
|
|
|
["msvc", "osx", "linux"].forEach((platform) => {
|
|
|
|
targets.forEach((target_source) => {
|
|
|
|
// Get Git metadata
|
|
|
|
const release_data = getTopResultFor(target_source.Repo, platform);
|
|
|
|
const name = target_source.Name.replace("%platform%", platform);
|
|
|
|
|
|
|
|
if (release_data.notFound === true) {
|
2017-08-20 03:42:53 +00:00
|
|
|
logger.error(`Release information not found for ${name}!`);
|
2017-08-20 02:58:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-20 03:42:53 +00:00
|
|
|
logger.info(`Building release information for ${name}.`);
|
2017-08-20 02:58:36 +00:00
|
|
|
const scriptName = platform + "-" + target_source.ScriptName;
|
|
|
|
|
|
|
|
// Build 7zip file
|
|
|
|
const version = release_data.release_id;
|
|
|
|
|
|
|
|
// Build directory structure
|
2017-08-20 03:42:53 +00:00
|
|
|
mkdirIfNotExists(`${distDir}/${name}`);
|
|
|
|
|
|
|
|
logger.debug(`Copying files for ${name}`);
|
2017-08-20 02:58:36 +00:00
|
|
|
|
|
|
|
// Copy license
|
2017-08-20 03:42:53 +00:00
|
|
|
fs.copySync("license.txt", `${distDir}/${name}/license.txt`);
|
|
|
|
fs.copySync("scripts/" + scriptName + ".qs", `${distDir}/${name}/installscript.qs`);
|
2017-08-20 02:58:36 +00:00
|
|
|
|
|
|
|
// Create 7zip archive
|
|
|
|
fs.removeSync("meta.7z");
|
2017-08-20 03:42:53 +00:00
|
|
|
exec.sync(zip_bin, ["a", "../meta.7z", name], {"cwd": distDir});
|
|
|
|
fs.removeSync(`${distDir}/${name}`);
|
2017-08-20 02:58:36 +00:00
|
|
|
const sha = sha1("meta.7z");
|
|
|
|
|
|
|
|
// Setup final structure
|
2017-08-20 03:42:53 +00:00
|
|
|
mkdirIfNotExists(`${distDir}/${name}`);
|
|
|
|
fs.moveSync("meta.7z", `${distDir}/${name}/${version}/meta.7z`);
|
|
|
|
|
|
|
|
logger.debug(`Creating target metadata for ${name}`);
|
2017-08-20 02:58:36 +00:00
|
|
|
|
|
|
|
// Create metadata
|
|
|
|
let target = [];
|
|
|
|
target.push({"Name": name});
|
|
|
|
target.push({"DisplayName": target_source.DisplayName.replace("%platform%", platform)});
|
|
|
|
target.push({"Version": version});
|
|
|
|
target.push({"DownloadableArchives": release_data.name});
|
|
|
|
|
|
|
|
// Because we cannot compute the uncompressed size ourselves, just give a generous estimate (to make
|
|
|
|
// sure they have enough disk space).
|
|
|
|
// OS flag is useless - i.e the installer stubs it :P
|
|
|
|
target.push({"UpdateFile": [{_attr: {UncompressedSize: release_data.size * 2,
|
|
|
|
CompressedSize: release_data.size, OS: "Any"}}]});
|
|
|
|
|
|
|
|
target.push({"ReleaseDate": release_data.published_at});
|
|
|
|
target.push({"Description": target_source.Description.replace("%platform%", platform)
|
2017-08-20 03:42:53 +00:00
|
|
|
.replace("%commithash%", release_data.hash)
|
2017-08-20 02:58:36 +00:00
|
|
|
.replace("%releasedate%", release_data.published_at)});
|
|
|
|
target.push({"Default": target_source.Default});
|
|
|
|
target.push({"Licenses": target_source.Licenses});
|
|
|
|
target.push({"Script": "installscript.qs"});
|
|
|
|
target.push({"SHA": sha});
|
|
|
|
|
|
|
|
updates.push({"PackageUpdate": target});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const updatesXml = xml({"Updates": updates}, {indent: " "});
|
|
|
|
|
|
|
|
// Save Updates.xml
|
2017-08-20 03:42:53 +00:00
|
|
|
fs.writeFile(`${distDir}/Updates.xml`, updatesXml, function (err) {
|
2017-08-20 02:58:36 +00:00
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-20 03:42:53 +00:00
|
|
|
execute().then(function() {
|
|
|
|
logger.info(`Completed repository creation at ${distDir}.`);
|
|
|
|
}).catch((err) => {
|
|
|
|
logger.error(err);
|
|
|
|
});
|