Flesh out error handling for run-commands.mjs; try simplifying GitHub workflow.

This commit is contained in:
00Fjongl 2024-08-08 19:18:01 -05:00
parent 995183d239
commit f47d6bc28c
4 changed files with 36 additions and 10 deletions

View file

@ -26,7 +26,7 @@ jobs:
run: npm run build run: npm run build
- name: Start server - name: Start server
run: npm run start-test-server run: npm run manual-start
- name: Test server response - name: Test server response
run: npm test run: npm test

View file

@ -12,7 +12,6 @@
"manual-start": "node run-command.mjs start", "manual-start": "node run-command.mjs start",
"kill": "node run-command.mjs stop kill", "kill": "node run-command.mjs stop kill",
"build": "node run-command.mjs build && cd lib/rammerhead && npm install && npm run build", "build": "node run-command.mjs build && cd lib/rammerhead && npm install && npm run build",
"start-test-server": "timeout 5 node backend.js; test $? -eq 124 && ( npm run manual-start & ) || exit 1",
"proxy-validator": "node proxyServiceValidator.js" "proxy-validator": "node proxyServiceValidator.js"
}, },
"keywords": [ "keywords": [

View file

@ -28,7 +28,7 @@ const shutdown = fileURLToPath(new URL("./src/.shutdown", import.meta.url));
// Run each command line argument passed after node run-command.mjs. // Run each command line argument passed after node run-command.mjs.
// Commands are defined in the switch case statement below. // Commands are defined in the switch case statement below.
for (let i = 2; i < process.argv.length; i++) commands: for (let i = 2; i < process.argv.length; i++)
switch (process.argv[i]) { switch (process.argv[i]) {
// Commmand to boot up the server. Use PM2 to run if production is true in the // Commmand to boot up the server. Use PM2 to run if production is true in the
// config file. // config file.
@ -44,7 +44,10 @@ for (let i = 2; i < process.argv.length; i++)
// This should run the server as a background process. // This should run the server as a background process.
else if (process.platform === "win32") else if (process.platform === "win32")
exec('START /MIN "" node backend.js', (error, stdout) => { exec('START /MIN "" node backend.js', (error, stdout) => {
if (error) throw error; if (error) {
console.error(error);
process.exitCode = 1;
}
console.log(stdout); console.log(stdout);
}); });
// The following approach (and similar approaches) will not work on Windows, // The following approach (and similar approaches) will not work on Windows,
@ -52,8 +55,16 @@ for (let i = 2; i < process.argv.length; i++)
else { else {
const server = fork( const server = fork(
fileURLToPath(new URL("./backend.js", import.meta.url)), fileURLToPath(new URL("./backend.js", import.meta.url)),
{detached: true} {
cwd: process.cwd(),
stdio: ["inherit", "inherit", "pipe", "ipc"],
detached: true
}
); );
server.stderr.on("data", stderr => {
console.error(stderr.toString());
process.exitCode = 1;
});
server.unref(); server.unref();
server.disconnect(); server.disconnect();
} }
@ -63,7 +74,7 @@ for (let i = 2; i < process.argv.length; i++)
// to shut down. This is done by sending a GET request to the server. // to shut down. This is done by sending a GET request to the server.
case "stop": { case "stop": {
await writeFile(shutdown, ""); await writeFile(shutdown, "");
let timeoutId = undefined; let timeoutId, hasErrored = false;
try { try {
// Give the server 5 seconds to respond, otherwise cancel this and throw an // Give the server 5 seconds to respond, otherwise cancel this and throw an
// error to the console. The fetch request will also throw an error immediately // error to the console. The fetch request will also throw an error immediately
@ -79,18 +90,33 @@ for (let i = 2; i < process.argv.length; i++)
clearTimeout(timeoutId); clearTimeout(timeoutId);
if (response === "Error") throw new Error("Server is unresponsive."); if (response === "Error") throw new Error("Server is unresponsive.");
} catch (e) { } catch (e) {
// Remove the temporary shutdown file since the server didn't remove it.
await unlink(shutdown);
// Check if this is the error thrown by the fetch request for an unused port. // Check if this is the error thrown by the fetch request for an unused port.
// Don't print the unused port error, since nothing has actually broken. // Don't print the unused port error, since nothing has actually broken.
if (e instanceof TypeError) clearTimeout(timeoutId); if (e instanceof TypeError) clearTimeout(timeoutId);
else console.error(e); else {
await unlink(shutdown); console.error(e);
// Stop here unless Node will be killed later.
if (!process.argv.slice(i + 1).includes("kill"))
hasErrored = true;
}
} }
// Do not run this if Node will be killed later in this script. It will fail. // Do not run this if Node will be killed later in this script. It will fail.
if (config.production && !process.argv.slice(i + 1).includes("kill")) if (config.production && !process.argv.slice(i + 1).includes("kill"))
exec("npx pm2 stop ecosystem.config.js", (error, stdout) => { exec("npx pm2 stop ecosystem.config.js", (error, stdout) => {
if (error) throw error; if (error) {
console.error(error);
hasErrored = true;
}
console.log(stdout); console.log(stdout);
}); });
// Do not continue executing commands since the server was unable to be stopped.
// Mostly implemented to prevent duplicating Node instances with npm restart.
if (hasErrored) {
process.exitCode = 1;
break commands;
}
break; break;
} }
@ -132,4 +158,4 @@ for (let i = 2; i < process.argv.length; i++)
} }
process.exitCode = 0; process.exitCode = process.exitCode || 0;

View file

@ -17,6 +17,7 @@ import loadTemplates from './templates.mjs';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { existsSync, unlinkSync } from 'node:fs'; import { existsSync, unlinkSync } from 'node:fs';
import ecosystem from '../ecosystem.config.js'; import ecosystem from '../ecosystem.config.js';
import { createBareServer } from "@tomphttp/bare-server-node";
const config = Object.freeze( const config = Object.freeze(
JSON.parse(await readFile(new URL("./config.json", import.meta.url))) JSON.parse(await readFile(new URL("./config.json", import.meta.url)))