From cae1c4e236071c65647206947d1cda781c3e310b Mon Sep 17 00:00:00 2001 From: charlocharlie Date: Sat, 15 Jun 2024 21:01:30 -0500 Subject: [PATCH] Cleanup dangling chromium processes on exit. Resolves #394 --- package-lock.json | 28 ++++++++++++++-------------- package.json | 2 +- src/common/puppeteer.ts | 17 ++++++++++++++++- src/index.ts | 6 ++++-- src/version.ts | 4 ++++ 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 21e5a10..25a714b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1808,12 +1808,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3326,9 +3326,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -8533,12 +8533,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "buffer": { @@ -9644,9 +9644,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" diff --git a/package.json b/package.json index d9066ff..2c75c74 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "entrypoint-config": "node dist/src/entrypoint-config.js", "build": "rimraf dist && tsc", "lint": "tsc --noEmit && eslint .", - "docker:build": "docker build . -t charlocharlie/epicgames-freegames-node:latest --target deploy", + "docker:build": "docker build . -t charlocharlie/epicgames-freegames-node:latest --target deploy --build-arg=\"COMMIT_SHA=$(git rev-parse HEAD)\"", "docker:build-debian": "docker build . -t charlocharlie/epicgames-freegames-node:debian --target deploy --file Dockerfile.debian", "docker:build-dev": "docker build . -t charlocharlie/epicgames-freegames:dev --target deploy", "docker:run": "docker run --rm -ti -v $(pwd)/config:/usr/app/config -p 3000:3000 -p 3001:3001 -e DEBUG=puppeteer-extra-plugin:portal* charlocharlie/epicgames-freegames-node:latest", diff --git a/src/common/puppeteer.ts b/src/common/puppeteer.ts index 3952f82..80d51d5 100644 --- a/src/common/puppeteer.ts +++ b/src/common/puppeteer.ts @@ -8,6 +8,7 @@ import pidtree from 'pidtree'; import findProcess from 'find-process'; import { ETCCookie, ToughCookieFileStore } from './cookie.js'; import { config } from './config/index.js'; +import { getCommitSha } from '../version.js'; const puppeteer = _puppeteer as unknown as PuppeteerExtra; const stealth = StealthPlugin(); @@ -154,7 +155,7 @@ const retryFunction = async ( chromiumProcesses.forEach((p) => process.kill(p.pid)); if (attempts >= MAX_ATTEMPTS) { L.error( - `If not already, consider using the Debian (:bullseye-slim) version of the image. More: https://github.com/claabs/epicgames-freegames-node#docker-configuration`, + `If not already, consider using the Debian (:debian) version of the image. More: https://github.com/claabs/epicgames-freegames-node#docker-configuration`, ); throw new Error(`Could not do ${outputName} after ${MAX_ATTEMPTS + 1} failed attempts.`); } @@ -166,6 +167,20 @@ const retryFunction = async ( } }; +export const killBrowserProcesses = async (L: Logger) => { + if (!getCommitSha()) return; // Don't kill processes if not in docker + const afterProcesses = await pidtree(process.pid); + const newProcesses = await Promise.all( + afterProcesses.map(async (p) => (await findProcess('pid', p))[0]), + ); + const browserProcesses = newProcesses.filter( + (p): p is NonNullable => + p !== undefined && ['chromium', 'chrome', 'headless_shell'].some((n) => p.name.includes(n)), + ); + L.debug({ browserProcesses }, 'Killing dangling browser processes'); + browserProcesses.forEach((p) => process.kill(p.pid)); +}; + /** * Create a new page within a wrapper that will retry if it hangs for 30 seconds */ diff --git a/src/index.ts b/src/index.ts index 9f1ee61..8a44ecd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import logger from './common/logger.js'; import { sendNotification, testNotifiers } from './notify.js'; import { checkForUpdate, logVersionOnError } from './version.js'; import PuppetLogin from './puppet/login.js'; -import { safeLaunchBrowser } from './common/puppeteer.js'; +import { killBrowserProcesses, safeLaunchBrowser } from './common/puppeteer.js'; import PuppetFreeGames from './puppet/free-games.js'; import { createServer } from './common/server.js'; import { convertImportCookies } from './common/cookie.js'; @@ -84,13 +84,15 @@ export async function main(): Promise { ); await Promise.all(accountPromises); server.close(); + await killBrowserProcesses(logger); logger.info('Exiting successfully'); exit(0); // For some reason, puppeteer will keep a zombie promise alive and stop Node from exiting } } -main().catch((err) => { +main().catch(async (err) => { logger.error(err); + await killBrowserProcesses(logger); logVersionOnError(); exit(1); }); diff --git a/src/version.ts b/src/version.ts index 49cd15f..c56ca4f 100644 --- a/src/version.ts +++ b/src/version.ts @@ -43,6 +43,10 @@ export async function checkForUpdate(): Promise { } } +export const getCommitSha = () => { + return COMMIT_SHA; +}; + export function logVersionOnError(): void { if (COMMIT_SHA || BRANCH || DISTRO) { L.warn({ COMMIT_SHA, BRANCH, DISTRO }, 'Current version');