import fs from "fs"; import upath from "upath"; import { configFolder, configOverridesFolder, rootDirectory, serverDestDirectory, sharedDestDirectory, templatesFolder, } from "#globals"; import mustache from "mustache"; import gulp from "gulp"; import dedent from "dedent-js"; import { isEnvVariableSet } from "#utils/util.ts"; import { BuildData } from "#types/transformFiles.ts"; import { logWarn } from "#utils/log.ts"; import sortKeysRecursive from "sort-keys-recursive"; // This updates all the files, for a release. // If it is not a release or build, and thus no changes to versions need to be made. // This occurs when the files are to be updated from the templates outside of a release or a build. let updateFiles: boolean; let updateFileVersion: string; let updateFileTransformedVersion: string; let buildData: BuildData; async function updateFilesSetup(): Promise { updateFiles = false; // See if current run is to update files if (isEnvVariableSet("UPDATE_FILES")) { try { updateFiles = JSON.parse( (process.env.UPDATE_FILES ?? "false").toLowerCase(), ); } catch (err) { throw new Error("Update Files Env Variable set to Invalid Value."); } } buildData = new BuildData(); const versionsFilePath: string = upath.join(templatesFolder, "versions.txt"); updateFileVersion = ""; if (!buildData.isVersionBuild() && !updateFiles) return; // Versions.txt handling if (!fs.existsSync(versionsFilePath)) { if (updateFiles) { if (!buildData.isVersionBuild()) throw new Error( "In order to update files, needs versions.txt to exist and have values, or the version to be set via the GITHUB_TAG environmental variable.", ); updateFileVersion = buildData.rawVersion; updateFileTransformedVersion = buildData.transformedVersion; return; } logWarn( "Version.txt does not exist. Creating empty file. This may be an error.", ); // Create Versions.txt fs.closeSync(fs.openSync(versionsFilePath, "w")); } else { const versionList = await fs.promises.readFile(versionsFilePath, "utf8"); if (updateFiles) { if (!versionList) { if (!buildData.isVersionBuild()) throw new Error( "In order to update files, needs versions.txt to exist and have values, or the version to be set via the GITHUB_TAG environmental variable.", ); updateFileVersion = buildData.rawVersion; updateFileTransformedVersion = buildData.transformedVersion; return; } updateFileVersion = versionList.split("\n")[0].replace("-", "").trim(); updateFileTransformedVersion = `v${updateFileVersion}`; return; } // Duplicate Key if (versionList.includes(`${buildData.rawVersion}\n`)) { throw new Error("Version already exists in version.txt. Exiting..."); } } } async function updateFilesBuildSetup() { updateFiles = true; buildData = new BuildData(); updateFileVersion = buildData.rawVersion; updateFileTransformedVersion = buildData.transformedVersion; } export async function updateBuildServerProperties(): Promise { await updateFilesBuildSetup(); // Note we can't use the `configOverridesFolder` variable as that includes `overrides/` // File name of the output files const fileName = "server.properties"; // File name of the Normal Template File const fileNameNormal = "server_normal.properties"; // File name of the Expert Template File const fileNameExpert = "server_expert.properties"; // Replacement Object const replacementObject: Record = { versionTitle: updateFiles ? updateFileTransformedVersion : buildData.transformedVersion, }; // Read and Write paths for normal const readPathNormal: string = upath.join(templatesFolder, fileNameNormal); const writePathsNormal: string[] = [ upath.join(serverDestDirectory, fileName), upath.join(serverDestDirectory, "config-overrides", "normal", fileName), ]; // Modify Normal File await modifyFile(readPathNormal, writePathsNormal, replacementObject, false); // Read and Write paths for expert const readPathExpert: string = upath.join(templatesFolder, fileNameExpert); const writePathExpert: string = upath.join( serverDestDirectory, "config-overrides", "expert", fileName, ); // Modify Expert File await modifyFile(readPathExpert, [writePathExpert], replacementObject, false); } export async function updateBuildRandomPatches(): Promise { await updateFilesBuildSetup(); // Filename & paths const fileName = "randompatches.cfg"; const readPath: string = upath.join(templatesFolder, fileName); const writePathsNormal: string[] = [ upath.join(sharedDestDirectory, configFolder, fileName), upath.join(sharedDestDirectory, configOverridesFolder, "normal", fileName), ]; // Replacement object const replacementObject: Record = { versionTitle: updateFiles ? updateFileTransformedVersion : buildData.transformedVersion, mode: "Normal", }; // Modify Normal File await modifyFile(readPath, writePathsNormal, replacementObject, false); // Change values for Expert Config replacementObject["mode"] = "Expert"; const writePathExpert = upath.join( sharedDestDirectory, configOverridesFolder, "expert", fileName, ); // Modify Expert File await modifyFile(readPath, [writePathExpert], replacementObject, false); } /** * @param readPath The filepath to read from. (Template) * @param writePaths The filepaths to write to. * @param replacementObject A record, of type string to type unknown, containing the keys, and replacement for those keys * @param addWarning whether to add warning not to edit file *

*

* A warning not to edit the file will also be added to the start of the file. */ async function modifyFile( readPath: string, writePaths: string[], replacementObject: Record, addWarning = true, ) { // Read the file content const data: string = await fs.promises.readFile(readPath, "utf8"); // Moustache Render let modifiedData: string = mustache.render(data, replacementObject); // Add warning to not edit file if (addWarning) modifiedData = dedent`# DO NOT EDIT THIS FILE! EDIT THE TEMPlATES INSTEAD! # See https://github.com/Nomi-CEu/Nomi-CEu/wiki/Part-1:-Contributing-Information#section-5-template-information! ${modifiedData}`; // Write the modified content back to the file for (const filename of writePaths) { await fs.promises.writeFile(filename, modifiedData, "utf8"); } } async function updateIssueTemplates(): Promise { // Filenames const fileNames: string[] = ["001-bug-report.yml", "002-feature-request.yml"]; const versionsFilePath: string = upath.join(templatesFolder, "versions.txt"); let versionList: string = await fs.promises.readFile( versionsFilePath, "utf8", ); if (!updateFiles) { if (!buildData.isVersionBuild()) throw new Error( "In order to update Issue Templates, the version must be set via the GITHUB_TAG environmental variable.", ); // Add new version to list, with indent versionList = ` - ${buildData.rawVersion}\n${versionList}`; } // Replacement Object const replacementObject: Record = { versions: versionList, }; // Write updated Version List await fs.promises.writeFile(versionsFilePath, versionList); const issueTemplatesFolder: string = upath.join( rootDirectory, ".github", "ISSUE_TEMPLATE", ); // Write to issue templates for (const fileName of fileNames) { const readPath = upath.join(templatesFolder, fileName); const writePath = upath.join(issueTemplatesFolder, fileName); await modifyFile(readPath, [writePath], replacementObject); } } async function updateRandomPatchesConfig(): Promise { // Filename & paths const fileName = "randompatches.cfg"; const readPath: string = upath.join(templatesFolder, fileName); const writePathsNormal: string[] = [ upath.join(rootDirectory, configFolder, fileName), upath.join(rootDirectory, configOverridesFolder, "normal", fileName), ]; // Replacement object const replacementObject: Record = { versionTitle: updateFiles ? updateFileTransformedVersion : buildData.transformedVersion, mode: "Normal", }; // Modify Normal File await modifyFile(readPath, writePathsNormal, replacementObject); // Change values for Expert Config replacementObject["mode"] = "Expert"; const writePathExpert = upath.join( rootDirectory, configOverridesFolder, "expert", fileName, ); // Modify Expert File await modifyFile(readPath, [writePathExpert], replacementObject); } async function updateServerProperties(): Promise { // File name of the output files const fileName = "server.properties"; // File name of the Normal Template File const fileNameNormal = "server_normal.properties"; // File name of the Expert Template File const fileNameExpert = "server_expert.properties"; // Replacement Object const replacementObject: Record = { versionTitle: updateFiles ? updateFileTransformedVersion : buildData.transformedVersion, }; // Read and Write paths for normal const readPathNormal: string = upath.join(templatesFolder, fileNameNormal); const writePathsNormal: string[] = [ upath.join(rootDirectory, "serverfiles", fileName), upath.join(rootDirectory, configOverridesFolder, "normal", fileName), ]; // Modify Normal File await modifyFile(readPathNormal, writePathsNormal, replacementObject); // Read and Write paths for expert const readPathExpert: string = upath.join(templatesFolder, fileNameExpert); const writePathExpert: string = upath.join( rootDirectory, configOverridesFolder, "expert", fileName, ); // Modify Expert File await modifyFile(readPathExpert, [writePathExpert], replacementObject); } async function updateMainMenuConfig(): Promise { // Filename & paths const fileName = "mainmenu.json"; const readPath: string = upath.join(templatesFolder, fileName); const writePath: string = upath.join( rootDirectory, configFolder, "CustomMainMenu", fileName, ); if (!updateFiles && !buildData.isVersionBuild()) throw new Error( "The main menu should only be updated if the version is set via the GITHUB_TAG environmental variable.", ); // Replacement object const replacementObject: Record = { version: updateFiles ? updateFileVersion : buildData.rawVersion, }; // Read file const data: string = await fs.promises.readFile(readPath, "utf8"); // Moustache Render let modifiedData = JSON.parse(mustache.render(data, replacementObject)); // Add warning to not edit file modifiedData["_comment"] = "DO NOT EDIT THIS FILE! EDIT THE TEMPlATES INSTEAD! See https://github.com/Nomi-CEu/Nomi-CEu/wiki/Part-1:-Contributing-Information#section-5-template-information!"; // Sort keys so that comment appears first modifiedData = sortKeysRecursive(modifiedData); return await fs.promises.writeFile( writePath, JSON.stringify(modifiedData, null, 2), "utf8", ); } export const updateFilesIssue = gulp.series( updateFilesSetup, updateIssueTemplates, ); export const updateFilesRandomPatches = gulp.series( updateFilesSetup, updateRandomPatchesConfig, ); export const updateFilesServer = gulp.series( updateFilesSetup, updateServerProperties, ); export const updateFilesMainMenu = gulp.series( updateFilesSetup, updateMainMenuConfig, ); export const updateAll = gulp.series( updateFilesSetup, updateIssueTemplates, updateRandomPatchesConfig, updateServerProperties, updateMainMenuConfig, );