Philipp Horstenkamp adedc4e7a0
All checks were successful
Lint / pre-commit Linting (push) Successful in 28s
Added the index.js
2025-04-22 01:10:40 +02:00

248 lines
6.9 KiB
JavaScript

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.
*
* @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<string[]>}
*/
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);
}
if (!files.length) {
core.warning(
`No files matched for placeholder replacement with pattern: ${globPattern}`,
);
return resolve([]);
}
core.info(`📝 Matched files for replacement:`);
files.forEach((file) => core.info(` - ${file}`));
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);
core.info(`Replaced placeholders in ${files.length} file(s).`);
resolve(files);
} catch (processError) {
reject(processError);
}
});
});
}
/**
* Reads files matching a glob pattern into a dictionary.
*
* @param {string} pattern
* @param {string} prefix
* @returns {Promise<Object>}
*/
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);
}
if (!files.length) {
core.warning(
`No files matched for upload with pattern: ${globPattern}`,
);
} else {
core.info(`📁 Matched files for upload:`);
files.forEach((file) => core.info(` - ${file}`));
}
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;
}
/**
* Main execution logic for the Screeps upload action.
*/
function postCode() {
core.info("🟢 Starting Screeps upload action...");
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,
};
core.info("🔧 Inputs:");
core.info(` prefix: ${prefix}`);
core.info(` pattern: ${pattern}`);
core.info(` branch: ${branch}`);
core.info(` gitReplace: ${gitReplace}`);
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(() => {
core.info("✅ Placeholder replacement complete.");
return readFilesIntoDict(pattern, prefix);
})
.then((files_to_push) => {
const fileCount = Object.keys(files_to_push).length;
core.info(`📦 Files prepared for upload: ${fileCount}`);
if (fileCount === 0) {
core.warning("No files were found to upload. Exiting.");
return;
}
core.info(`⬆️ Uploading to branch '${branch}':`);
Object.keys(files_to_push).forEach((key) => {
core.info(` - ${key}`);
});
core.info("🔐 Authentication:");
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));
core.info(`✅ Code uploaded to branch '${branch}' using token.`);
});
} else {
core.info(`Logging in as user '${username}'...`);
return api
.auth(username, password, login_arguments)
.then(() => api.code.set(branch, files_to_push))
.then(() => {
core.info(`✅ Code uploaded to branch '${branch}' via basic auth.`);
});
}
});
}
// Run the action and catch any errors
postCode().catch((err) => {
core.setFailed(err.message || err);
});