import { modpackManifest } from "../../globals";
import request from "requestretry";
import fs from "fs";
import log from "fancy-log";
import upath from "upath";
import buildConfig from "../../buildConfig";
import { makeArtifactNameBody } from "../../util/util";
import sanitize from "sanitize-filename";
import mustache from "mustache";
import { DeployReleaseType, inputToDeployReleaseTypes } from "../../types/changelogTypes";
const CURSEFORGE_LEGACY_ENDPOINT = "https://minecraft.curseforge.com/";
const variablesToCheck = ["CURSEFORGE_API_TOKEN", "CURSEFORGE_PROJECT_ID", "RELEASE_TYPE"];
async function upload(files: { name: string; displayName: string }[]) {
files.forEach((file) => {
const path = upath.join(buildConfig.buildDestinationDirectory, file.name);
if (!fs.existsSync(path)) {
throw new Error(`File ${path} doesn't exist!`);
}
});
// Since we've built everything beforehand, the changelog must be available in the shared directory.
let changelog = (
await fs.promises.readFile(upath.join(buildConfig.buildDestinationDirectory, "CHANGELOG_CF.md"))
).toString();
changelog = mustache.render(changelog, {
CENTER_ALIGN: 'style="text-align: center;"',
CF_REDIRECT: `
Looks way better here.
`,
});
const tokenHeaders = {
"X-Api-Token": process.env.CURSEFORGE_API_TOKEN,
};
// Fetch the list of Minecraft versions from CurseForge.
log("Fetching CurseForge version manifest...");
const versionsManifest =
(await request({
uri: CURSEFORGE_LEGACY_ENDPOINT + "api/game/versions",
headers: tokenHeaders,
method: "GET",
json: true,
fullResponse: false,
maxAttempts: 5,
})) || [];
if (!versionsManifest) {
throw new Error("Failed to fetch CurseForge version manifest.");
}
const version = versionsManifest.find((m) => m.name == modpackManifest.minecraft.version);
if (!version) {
throw new Error(`Version ${modpackManifest.minecraft.version} not found on CurseForge.`);
}
let clientFileID: number | null;
const releaseType: DeployReleaseType = inputToDeployReleaseTypes[process.env.RELEASE_TYPE];
// Upload artifacts.
for (const file of files) {
const options = {
uri: CURSEFORGE_LEGACY_ENDPOINT + `api/projects/${process.env.CURSEFORGE_PROJECT_ID}/upload-file`,
method: "POST",
headers: {
...tokenHeaders,
"Content-Type": "multipart/form-data",
},
formData: {
metadata: JSON.stringify({
changelog: changelog,
changelogType: "html",
releaseType: releaseType ? releaseType.cfReleaseType : "release",
parentFileID: clientFileID ? clientFileID : undefined,
gameVersions: clientFileID ? undefined : [version.id],
displayName: file.displayName,
}),
file: fs.createReadStream(upath.join(buildConfig.buildDestinationDirectory, file.name)),
},
json: true,
fullResponse: false,
};
log(`Uploading ${file.name} to CurseForge...` + (clientFileID ? `(child of ${clientFileID})` : ""));
const response = await request(options);
if (response && response.id) {
if (!clientFileID) {
clientFileID = response.id;
}
} else {
throw new Error(`Failed to upload ${file.name}: Invalid Response.`);
}
}
}
/**
* Uploads build artifacts to CurseForge.
*/
export async function deployCurseForge(): Promise {
/**
* Obligatory variable check.
*/
["GITHUB_TAG", ...variablesToCheck].forEach((vari) => {
if (!process.env[vari]) {
throw new Error(`Environmental variable ${vari} is unset.`);
}
});
const displayName = process.env.GITHUB_TAG;
const files = [
{
name: sanitize((makeArtifactNameBody(modpackManifest.name) + "-client.zip").toLowerCase()),
displayName: displayName,
},
{
name: sanitize((makeArtifactNameBody(modpackManifest.name) + "-server.zip").toLowerCase()),
displayName: `${displayName}-server`,
},
];
await upload(files);
}