feat: add rollback_on_failure feature (#88)
This PR adds the ability to download existing code before deployment and automatically roll back if the post-deployment monitor detects a failure. Reviewed-on: #88 Reviewed-by: LLMReview <27+autoreview@noreplay.horstenkamp.eu>
This commit was merged in pull request #88.
This commit is contained in:
@@ -118,17 +118,19 @@ export function validateAuthentication(token, username, password) {
|
||||
* @param {'ignore'|'warn'|'fail'} action
|
||||
* @param {boolean} flag - Only acts when true
|
||||
* @param {string} message - Passed to core.warning / core.setFailed
|
||||
* @returns {boolean} - Returns true if the action was 'fail' and the flag was true.
|
||||
*/
|
||||
export function applyOnAction(action, flag, message) {
|
||||
if (!flag) return;
|
||||
if (!flag) return false;
|
||||
if (action === "warn") {
|
||||
core.warning(message);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (action === "fail") {
|
||||
core.setFailed(message);
|
||||
return true;
|
||||
}
|
||||
// 'ignore' → no-op
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,33 +181,72 @@ export async function postCode() {
|
||||
return;
|
||||
}
|
||||
let api = new ScreepsAPI(login_arguments);
|
||||
if (token) {
|
||||
|
||||
if (!token) {
|
||||
core.info(`Logging in as user ${username}`);
|
||||
try {
|
||||
await api.auth(username, password, login_arguments);
|
||||
} catch (err) {
|
||||
core.error(`Authentication error: ${err}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
let oldCode = null;
|
||||
let rollbackOnFailure = false;
|
||||
try {
|
||||
rollbackOnFailure = core.getBooleanInput("rollback_on_failure");
|
||||
} catch (e) {
|
||||
// getBooleanInput throws if not 'true' or 'false', ignore
|
||||
}
|
||||
|
||||
if (rollbackOnFailure) {
|
||||
core.info(
|
||||
`Downloading existing code from branch ${branch} for potential rollback...`,
|
||||
);
|
||||
try {
|
||||
const getResponse = await api.code.get(branch);
|
||||
if (getResponse && getResponse.ok && getResponse.modules) {
|
||||
oldCode = getResponse.modules;
|
||||
core.info(
|
||||
`Successfully downloaded existing code (modules: ${Object.keys(oldCode).join(", ")})`,
|
||||
);
|
||||
} else {
|
||||
core.setFailed(
|
||||
`Failed to download existing code, but rollback_on_failure is enabled. Aborting deployment.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
core.setFailed(
|
||||
`Error downloading existing code: ${err.message}. Aborting deployment.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.code.set(branch, files_to_push);
|
||||
core.info(JSON.stringify(response, null, 2));
|
||||
core.info(`Code set successfully to ${branch}`);
|
||||
} else {
|
||||
core.info(`Logging in as user ${username}`);
|
||||
await Promise.resolve()
|
||||
.then(() => api.auth(username, password, login_arguments))
|
||||
.then(() => api.code.set(branch, files_to_push))
|
||||
.then(() => {
|
||||
core.info(`Code set successfully to ${branch}`);
|
||||
})
|
||||
.catch((err) => {
|
||||
core.error(`Upload error: ${err}`);
|
||||
throw err;
|
||||
});
|
||||
} catch (err) {
|
||||
core.error(`Upload error: ${err}`);
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Console monitoring (optional)
|
||||
const monitorTicks = parseInt(core.getInput("monitor") || "0", 10);
|
||||
if (monitorTicks > 0) {
|
||||
const onTraceback = core.getInput("on_traceback") || "fail";
|
||||
const onErrorLog = core.getInput("on_error_log") || "warn";
|
||||
const onWarningLog = core.getInput("on_warning_log") || "ignore";
|
||||
|
||||
const result = await monitorConsole(api, {
|
||||
monitor: monitorTicks,
|
||||
logToFile: core.getBooleanInput("log_to_file"),
|
||||
onTraceback: core.getInput("on_traceback") || "fail",
|
||||
onErrorLog: core.getInput("on_error_log") || "warn",
|
||||
onWarningLog: core.getInput("on_warning_log") || "ignore",
|
||||
onTraceback,
|
||||
onErrorLog,
|
||||
onWarningLog,
|
||||
monitorInterval: parseInt(core.getInput("monitor_interval") || "10", 10),
|
||||
hostname,
|
||||
shard: core.getInput("shard") || undefined,
|
||||
@@ -215,21 +256,37 @@ export async function postCode() {
|
||||
core.setOutput("saw_error_log", String(result.sawErrorLog));
|
||||
core.setOutput("saw_warning_log", String(result.sawWarningLog));
|
||||
|
||||
applyOnAction(
|
||||
core.getInput("on_traceback"),
|
||||
const fail1 = applyOnAction(
|
||||
onTraceback,
|
||||
result.sawTraceback,
|
||||
"Screeps console: traceback detected",
|
||||
);
|
||||
applyOnAction(
|
||||
core.getInput("on_error_log"),
|
||||
const fail2 = applyOnAction(
|
||||
onErrorLog,
|
||||
result.sawErrorLog,
|
||||
"Screeps console: error log output detected",
|
||||
);
|
||||
applyOnAction(
|
||||
core.getInput("on_warning_log"),
|
||||
const fail3 = applyOnAction(
|
||||
onWarningLog,
|
||||
result.sawWarningLog,
|
||||
"Screeps console: warning log output detected",
|
||||
);
|
||||
|
||||
const shouldFail = fail1 || fail2 || fail3;
|
||||
|
||||
if (shouldFail && rollbackOnFailure && oldCode) {
|
||||
core.info(
|
||||
"Action failed based on monitor configuration. Rolling back to previous code...",
|
||||
);
|
||||
try {
|
||||
await api.code.set(branch, oldCode);
|
||||
core.info(
|
||||
`Successfully rolled back to previous code on branch ${branch}.`,
|
||||
);
|
||||
} catch (err) {
|
||||
core.error(`Rollback failed: ${err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user