15 Commits

Author SHA1 Message Date
f9d8f8601b Log simplified
All checks were successful
Lint / pre-commit Linting (push) Successful in 25s
2025-04-22 01:22:45 +02:00
6ccf7f08f9 Added even more logging
All checks were successful
Lint / pre-commit Linting (push) Successful in 25s
2025-04-22 01:21:31 +02:00
8c12ed263f Added a bit more logging 2025-04-22 01:20:25 +02:00
63d91e18f8 Awaiting some functions
All checks were successful
Lint / pre-commit Linting (push) Successful in 22s
2025-04-22 01:18:34 +02:00
7c788de362 Added an end screeps list
All checks were successful
Lint / pre-commit Linting (push) Successful in 24s
2025-04-22 01:15:39 +02:00
adedc4e7a0 Added the index.js
All checks were successful
Lint / pre-commit Linting (push) Successful in 28s
2025-04-22 01:10:40 +02:00
616584e43a Refactor index.js
All checks were successful
Lint / pre-commit Linting (push) Successful in 45s
2025-04-22 01:07:34 +02:00
ffa09c46c0 Added another debug message
All checks were successful
Lint / pre-commit Linting (push) Successful in 24s
2025-04-22 01:03:29 +02:00
91fdb76f0f Added a few messages
All checks were successful
Lint / pre-commit Linting (push) Successful in 1m0s
2025-04-22 00:57:07 +02:00
751fd07d45 Rework the index.js
All checks were successful
Lint / pre-commit Linting (push) Successful in 24s
2025-04-22 00:53:28 +02:00
d6c9360dcf Glob
All checks were successful
Lint / pre-commit Linting (push) Successful in 25s
2025-04-22 00:48:53 +02:00
bb83d5493a Added an async await
All checks were successful
Lint / pre-commit Linting (push) Successful in 48s
2025-04-22 00:47:33 +02:00
b421097f6b Rework the index.js
All checks were successful
Lint / pre-commit Linting (push) Successful in 28s
2025-04-22 00:20:33 +02:00
d9bd7f6a85 Reverted a lot of changes
Some checks failed
Lint / pre-commit Linting (push) Failing after 10m46s
2025-04-22 00:10:31 +02:00
705d67adec Refactor the index.js
All checks were successful
Lint / pre-commit Linting (push) Successful in 25s
2025-04-21 22:54:49 +02:00
2 changed files with 200 additions and 125 deletions

View File

@ -0,0 +1,57 @@
name: Auto Maintenance Cycle
on:
push:
paths:
- .gitea/workflows/maintenance.yaml
schedule:
- cron: 0 3 2 * *
workflow_dispatch:
jobs:
auto-update:
name: pre-commit Autoupdate
runs-on: pi64
env:
SPECIFIC_BRANCH: chore/update-pre-commit
steps:
- uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.REPO_TOKEN }}
- name: Attempt to checkout specific branch
uses: actions/checkout@v4
with:
ref: ${{ env.SPECIFIC_BRANCH }}
token: ${{ secrets.REPO_TOKEN }}
continue-on-error: true
- uses: actions/setup-python@v4
- run: pip install pre-commit
shell: bash
- run: pre-commit autoupdate
shell: bash
- name: Test pre-commit
run: SKIP=no-commit-to-branch pre-commit run -a
- name: Commit
id: auto-commit-action
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: 'chore: update pre-commit hooks'
branch: ${{ env.SPECIFIC_BRANCH }}
create_branch: true
commit_user_name: runner
commit_user_email: git@horstenkamp.eu
commit_author: runner <git@horstenkamp.eu>
- name: Generate Date
run: echo "CURRENT_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
- name: Create an PR action
if: steps.auto-commit-action.outputs.changes_detected == 'true'
uses: https://git.horstenkamp.eu/Philipp/gitea-act-create-pr@main
with:
token: ${{ secrets.REPO_TOKEN }}
branch: ${{ env.SPECIFIC_BRANCH }}
title: Updates to the pre-commit action created at ${{ env.CURRENT_DATE }}
body: Update to the pre-commit action.
base_branch: ${{gitea.ref_name}}
assignees: ${{ vars.ASSIGNEES }}
reviewers: ${{ vars.REVIEWERS }}

268
index.js
View File

@ -5,104 +5,112 @@ const { glob } = require("glob");
const path = require("path"); const path = require("path");
/** /**
* Replaces specific placeholder strings within the provided content with corresponding dynamic values. * Replaces placeholder strings in content with dynamic values.
* *
* This function specifically targets three placeholders: * @param {string} content
* - {{gitHash}} is replaced with the current Git commit hash, obtained from the GITHUB_SHA environment variable. * @param {string} hostname
* - {{gitRef}} is replaced with the Git reference (branch or tag) that triggered the workflow, obtained from the GITHUB_REF environment variable. * @returns {string}
* - {{deployTime}} is replaced with the current ISO timestamp.
*
* Note: This function is designed for use within a GitHub Actions workflow where GITHUB_SHA and GITHUB_REF environment variables are automatically set.
*
* @param {string} content - The string content in which placeholders are to be replaced.
* @returns {string} The content with placeholders replaced by their respective dynamic values.
*/ */
function replacePlaceholders(content, hostname) { function replacePlaceholders(content, hostname) {
const deployTime = new Date().toISOString(); 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 return content
.replace(/{{gitHash}}/g, process.env.GITHUB_SHA) .replace(/{{gitHash}}/g, gitHash)
.replace(/{{gitRef}}/g, process.env.GITHUB_REF) .replace(/{{gitRef}}/g, gitRef)
.replace(/{{deployTime}}/g, deployTime) .replace(/{{deployTime}}/g, deployTime)
.replace(/{{hostname}}/g, hostname); .replace(/{{hostname}}/g, hostname);
} }
/** /**
* Reads all files matching a specified pattern, replaces certain placeholders in their content, and writes the updated content back to the files. * Replaces placeholders in files matched by glob.
* *
* This function searches for files in the filesystem using the provided glob pattern, optionally prefixed. It reads each file, * @param {string} pattern
* uses the `replacePlaceholders` function to replace specific placeholders in the file's content, and then writes the modified content * @param {string} [prefix]
* back to the original file. This is useful for dynamically updating file contents in a batch process, such as during a build or deployment. * @param {string} hostname
* * @returns {Promise<string[]>}
* @param {string} pattern - The glob pattern used to find files. Example: '*.js' for all JavaScript files.
* @param {string} [prefix] - An optional directory prefix to prepend to the glob pattern. This allows searching within a specific directory.
* @returns {Promise<string[]>} A promise that resolves with an array of file paths that were processed, or rejects with an error if the process fails.
*/ */
async function readReplaceAndWriteFiles(pattern, prefix, hostname) { function readReplaceAndWriteFiles(pattern, prefix, hostname) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const globPattern = prefix ? path.join(prefix, pattern) : pattern; const globPattern = prefix ? path.join(prefix, pattern) : pattern;
glob(globPattern, async (err, files) => { glob(globPattern, (err, files) => {
if (err) { if (err) return reject(err);
return reject(err);
}
let processPromises = [];
files.forEach((file) => { if (!files.length) {
let processPromise = fs.promises core.warning(
`No files matched for placeholder replacement: ${globPattern}`,
);
return resolve([]);
}
core.info(`📝 Matched files for replacement:`);
files.forEach((file) => core.info(` - ${file}`));
const promises = files.map((file) =>
fs.promises
.readFile(file, "utf8") .readFile(file, "utf8")
.then((content) => { .then((content) => {
content = replacePlaceholders(content, hostname); content = replacePlaceholders(content, hostname);
return fs.promises.writeFile(file, content); return fs.promises.writeFile(file, content);
}); })
.then(() => {
core.info(`Replaced placeholders in file: ${file}`);
}),
);
processPromises.push(processPromise); Promise.all(promises)
}); .then(() => {
core.info(
try { `Successfully replaced placeholders in ${files.length} file(s).`,
await Promise.all(processPromises); );
resolve(files); resolve(files);
} catch (processError) { })
reject(processError); .catch(reject);
}
}); });
}); });
} }
/** /**
* Reads files matching a glob pattern into a dictionary. * Reads files into a dictionary of key → content.
* @param {string} pattern - Glob pattern to match files. *
* @param {string} prefix - Directory prefix for file paths. * @param {string} pattern
* @returns {Promise<Object>} - Promise resolving to a dictionary of file contents keyed by filenames. * @param {string} prefix
* @returns {Promise<Object>}
*/ */
function readFilesIntoDict(pattern, prefix) { function readFilesIntoDict(pattern, prefix) {
// Prepend the prefix to the glob pattern
const globPattern = prefix ? path.join(prefix, pattern) : pattern;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const globPattern = prefix ? path.join(prefix, pattern) : pattern;
glob(globPattern, (err, files) => { glob(globPattern, (err, files) => {
if (err) { if (err) return reject(err);
return reject(err);
if (!files.length) {
core.warning(`No files matched for upload: ${globPattern}`);
} else {
core.info(`📁 Matched files for upload:`);
files.forEach((file) => core.info(` - ${file}`));
} }
let fileDict = {}; const fileDict = {};
let readPromises = []; const readPromises = files.map((file) =>
fs.promises.readFile(file, "utf8").then((content) => {
files.forEach((file) => {
let readPromise = fs.promises.readFile(file, "utf8").then((content) => {
// Remove the prefix from the filename and drop the file suffix
let key = file; let key = file;
if (prefix && file.startsWith(prefix)) { if (prefix && file.startsWith(prefix)) {
key = key.slice(prefix.length); key = key.slice(prefix.length);
} }
key = path.basename(key, path.extname(key)); // Drop the file suffix key = path.basename(key, path.extname(key));
fileDict[key] = content; fileDict[key] = content;
}); }),
);
readPromises.push(readPromise);
});
// Use Promise.all to ensure all files are read before resolving
Promise.all(readPromises) Promise.all(readPromises)
.then(() => resolve(fileDict)) .then(() => resolve(fileDict))
.catch(reject); .catch(reject);
@ -111,96 +119,106 @@ function readFilesIntoDict(pattern, prefix) {
} }
/** /**
* Validates the provided authentication credentials. * Validates provided credentials.
* @param {string} token - The authentication token. *
* @param {string} username - The username. * @param {string} token
* @param {string} password - The password. * @param {string} username
* @returns {string|null} - Returns an error message if validation fails, otherwise null. * @param {string} password
* @returns {string|null}
*/ */
function validateAuthentication(token, username, password) { function validateAuthentication(token, username, password) {
if (token) { if (token && (username || password)) {
if (username || password) { return "Token is defined along with username and/or 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; // No errors found if (!token && (!username || !password)) {
return "Username and password must both be defined if token is not used.";
}
return null;
} }
/** /**
* Posts code to Screeps server. * Main logic for uploading code to Screeps.
*/ */
async function postCode() { function postCode() {
core.info("🟢 Starting Screeps upload action...");
const protocol = core.getInput("protocol") || "https"; const protocol = core.getInput("protocol") || "https";
const hostname = core.getInput("hostname") || "screeps.com"; const hostname = core.getInput("hostname") || "screeps.com";
const port = core.getInput("port") || "443"; const port = core.getInput("port") || "443";
const path = core.getInput("path") || "/"; const path = core.getInput("path") || "/";
const token = core.getInput("token") || undefined; const token = core.getInput("token") || undefined;
const username = core.getInput("username") || undefined; const username = core.getInput("username") || undefined;
const password = core.getInput("password") || undefined; const password = core.getInput("password") || undefined;
const prefix = core.getInput("source-prefix"); const prefix = core.getInput("source-prefix");
const pattern = core.getInput("pattern") || "*.js"; const pattern = core.getInput("pattern") || "*.js";
const branch = core.getInput("branch") || "default"; const branch = core.getInput("branch") || "default";
const gitReplace = core.getInput("git-replace") || null; const gitReplace = core.getInput("git-replace") || null;
if (gitReplace) {
await readReplaceAndWriteFiles(gitReplace, prefix, hostname);
}
const files_to_push = await readFilesIntoDict(pattern, prefix);
core.info(`Trying to upload the following files to ${branch}:`);
Object.keys(files_to_push).forEach((key) => {
core.info(`Key: ${key}`);
});
const login_arguments = { const login_arguments = {
token: token, token,
username: username, username,
password: password, password,
protocol: protocol, protocol,
hostname: hostname, hostname,
port: port, port,
path: path, path,
}; };
core.info("login_arguments:"); core.info("🔧 Inputs:");
core.info(JSON.stringify(login_arguments, null, 2)); core.info(` prefix: ${prefix}`);
core.info(` pattern: ${pattern}`);
core.info(` branch: ${branch}`);
core.info(` gitReplace: ${gitReplace}`);
const errorMessage = validateAuthentication(token, username, password); const errorMessage = validateAuthentication(token, username, password);
if (errorMessage) { if (errorMessage) {
core.error(errorMessage); core.setFailed(errorMessage);
return; return Promise.reject(new Error(errorMessage));
} }
let api = new ScreepsAPI(login_arguments);
if (token) { const replacePromise = gitReplace
const response = await api.code.set(branch, files_to_push); ? readReplaceAndWriteFiles(gitReplace, prefix, hostname)
core.info(JSON.stringify(response, null, 2)); : Promise.resolve();
console.log(`Code set successfully to ${branch}`);
} else { return replacePromise
core.info(`Logging in as user ${username}`); .then(() => readFilesIntoDict(pattern, prefix))
await Promise.resolve() .then((files_to_push) => {
.then(() => api.auth(username, password, login_arguments)) const fileCount = Object.keys(files_to_push).length;
.then(() => { core.info(`📦 Files prepared for upload: ${fileCount}`);
api.code.set(branch, files_to_push);
}) if (fileCount === 0) {
.then(() => { core.warning("No files were found to upload. Exiting.");
console.log(`Code set successfully to ${branch}`); return;
}) }
.catch((err) => {
console.error("Error:", err); core.info(`⬆️ Uploading to branch '${branch}':`);
Object.keys(files_to_push).forEach((key) => {
core.info(` - ${key}`);
}); });
}
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.`);
});
}
});
} }
postCode();
// Kick off the process and ensure failures are surfaced
postCode().catch((err) => {
core.setFailed(err.message || err);
});
core.info(`✅ Final Code uploaded to branch via basic auth.`);
core.debug(`✅ DEBUG Final Code uploaded to branch via basic auth. DEBUG`);