const { ScreepsAPI } = require("screeps-api"); const core = require("@actions/core"); const fs = require("fs"); const { glob } = require("glob"); const path = require("path"); /** * Replaces specific placeholder strings within the provided content with corresponding dynamic values. * * This function targets: * - {{gitHash}} -> GITHUB_SHA * - {{gitRef}} -> GITHUB_REF * - {{deployTime}} -> ISO timestamp * - {{hostname}} -> hostname * * @param {string} content * @param {string} hostname * @returns {string} */ function replacePlaceholders(content, hostname) { const deployTime = new Date().toISOString(); const gitHash = process.env.GITHUB_SHA; const gitRef = process.env.GITHUB_REF; core.info("Replacing placeholders:"); core.info(` GITHUB_SHA: ${gitHash}`); core.info(` GITHUB_REF: ${gitRef}`); core.info(` deployTime: ${deployTime}`); core.info(` hostname: ${hostname}`); return content .replace(/{{gitHash}}/g, gitHash) .replace(/{{gitRef}}/g, gitRef) .replace(/{{deployTime}}/g, deployTime) .replace(/{{hostname}}/g, hostname); } /** * Replaces placeholders in files matching a glob pattern. * * @param {string} pattern * @param {string} [prefix] * @param {string} hostname * @returns {Promise} */ function readReplaceAndWriteFiles(pattern, prefix, hostname) { return new Promise((resolve, reject) => { const globPattern = prefix ? path.join(prefix, pattern) : pattern; glob(globPattern, async (err, files) => { if (err) { return reject(err); } let processPromises = []; files.forEach((file) => { core.info(`Replacing placeholders in file: ${file}`); let processPromise = fs.promises .readFile(file, "utf8") .then((content) => { content = replacePlaceholders(content, hostname); return fs.promises.writeFile(file, content); }); processPromises.push(processPromise); }); try { await Promise.all(processPromises); resolve(files); } catch (processError) { reject(processError); } }); }); } /** * Reads files matching a glob pattern into a dictionary. * @param {string} pattern * @param {string} prefix * @returns {Promise} */ function readFilesIntoDict(pattern, prefix) { const globPattern = prefix ? path.join(prefix, pattern) : pattern; return new Promise((resolve, reject) => { glob(globPattern, (err, files) => { if (err) { return reject(err); } let fileDict = {}; let readPromises = []; files.forEach((file) => { let readPromise = fs.promises.readFile(file, "utf8").then((content) => { let key = file; if (prefix && file.startsWith(prefix)) { key = key.slice(prefix.length); } key = path.basename(key, path.extname(key)); fileDict[key] = content; }); readPromises.push(readPromise); }); Promise.all(readPromises) .then(() => resolve(fileDict)) .catch(reject); }); }); } /** * Validates the provided authentication credentials. * @param {string} token * @param {string} username * @param {string} password * @returns {string|null} */ function validateAuthentication(token, username, password) { if (token) { if (username || password) { return "Token is defined along with username and/or password."; } } else { if (!username && !password) { return "Neither token nor password and username are defined."; } if (username && !password) { return "Username is defined but no password is provided."; } if (!username && password) { return "Password is defined but no username is provided."; } } return null; } /** * Posts code to Screeps server. */ function postCode() { const protocol = core.getInput("protocol") || "https"; const hostname = core.getInput("hostname") || "screeps.com"; const port = core.getInput("port") || "443"; const path = core.getInput("path") || "/"; const token = core.getInput("token") || undefined; const username = core.getInput("username") || undefined; const password = core.getInput("password") || undefined; const prefix = core.getInput("source-prefix"); const pattern = core.getInput("pattern") || "*.js"; const branch = core.getInput("branch") || "default"; const gitReplace = core.getInput("git-replace") || null; const login_arguments = { token, username, password, protocol, hostname, port, path, }; const errorMessage = validateAuthentication(token, username, password); if (errorMessage) { core.setFailed(errorMessage); return Promise.reject(new Error(errorMessage)); } const replacePromise = gitReplace ? readReplaceAndWriteFiles(gitReplace, prefix, hostname) : Promise.resolve(); return replacePromise .then(() => readFilesIntoDict(pattern, prefix)) .then((files_to_push) => { core.info( `Uploading ${ Object.keys(files_to_push).length } file(s) to branch '${branch}':`, ); Object.keys(files_to_push).forEach((key) => { core.info(` File: ${key}`); }); core.info("Authentication arguments:"); core.info(JSON.stringify(login_arguments, null, 2)); const api = new ScreepsAPI(login_arguments); if (token) { return api.code.set(branch, files_to_push).then((response) => { core.info(JSON.stringify(response, null, 2)); console.log(`Code set successfully to ${branch}`); }); } else { core.info(`Logging in as user ${username}`); return api .auth(username, password, login_arguments) .then(() => api.code.set(branch, files_to_push)) .then(() => { console.log(`Code set successfully to ${branch}`); }); } }); } postCode().catch((err) => { core.setFailed(err.message || err); });