From 7d1c5630d841674300d2a841e1e657fb0f37ca05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl?= <raphael.dhote@gmail.com>
Date: Mon, 27 Nov 2023 16:06:28 +0100
Subject: [PATCH] Explicitly exit the process to not wait for hanging promises

See https://github.com/actions/setup-node/issues/878
---
 __tests__/canary-installer.test.ts   |  4 ++++
 __tests__/main.test.ts               | 12 ++++++++++++
 __tests__/nightly-installer.test.ts  |  4 ++++
 __tests__/official-installer.test.ts |  4 ++++
 __tests__/rc-installer.test.ts       |  4 ++++
 dist/setup/index.js                  |  3 +++
 src/main.ts                          |  4 ++++
 7 files changed, 35 insertions(+)

diff --git a/__tests__/canary-installer.test.ts b/__tests__/canary-installer.test.ts
index 6d141fc3c..5d8112367 100644
--- a/__tests__/canary-installer.test.ts
+++ b/__tests__/canary-installer.test.ts
@@ -46,6 +46,7 @@ describe('setup-node', () => {
   let isCacheActionAvailable: jest.SpyInstance;
   let getExecOutputSpy: jest.SpyInstance;
   let getJsonSpy: jest.SpyInstance;
+  let processExitSpy: jest.SpyInstance;
 
   beforeEach(() => {
     // @actions/core
@@ -63,6 +64,9 @@ describe('setup-node', () => {
     archSpy = jest.spyOn(osm, 'arch');
     archSpy.mockImplementation(() => os['arch']);
     execSpy = jest.spyOn(cp, 'execSync');
+    processExitSpy = jest
+      .spyOn(process, 'exit')
+      .mockImplementation((() => {}) as () => never);
 
     // @actions/tool-cache
     findSpy = jest.spyOn(tc, 'find');
diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
index b57248757..6af32ec05 100644
--- a/__tests__/main.test.ts
+++ b/__tests__/main.test.ts
@@ -35,6 +35,8 @@ describe('main tests', () => {
 
   let setupNodeJsSpy: jest.SpyInstance;
 
+  let processExitSpy: jest.SpyInstance;
+
   beforeEach(() => {
     inputs = {};
 
@@ -72,6 +74,10 @@ describe('main tests', () => {
 
     setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs');
     setupNodeJsSpy.mockImplementation(() => {});
+
+    processExitSpy = jest
+      .spyOn(process, 'exit')
+      .mockImplementation((() => {}) as () => never);
   });
 
   afterEach(() => {
@@ -257,6 +263,12 @@ describe('main tests', () => {
         `::error::The specified node version file at: ${versionFilePath} does not exist${osm.EOL}`
       );
     });
+
+    it('should call process.exit() explicitly after running', async () => {
+      await main.run();
+
+      expect(processExitSpy).toHaveBeenCalled();
+    });
   });
 
   describe('cache on GHES', () => {
diff --git a/__tests__/nightly-installer.test.ts b/__tests__/nightly-installer.test.ts
index 87c437957..debd7e86f 100644
--- a/__tests__/nightly-installer.test.ts
+++ b/__tests__/nightly-installer.test.ts
@@ -46,6 +46,7 @@ describe('setup-node', () => {
   let isCacheActionAvailable: jest.SpyInstance;
   let getExecOutputSpy: jest.SpyInstance;
   let getJsonSpy: jest.SpyInstance;
+  let processExitSpy: jest.SpyInstance;
 
   beforeEach(() => {
     // @actions/core
@@ -64,6 +65,9 @@ describe('setup-node', () => {
     archSpy = jest.spyOn(osm, 'arch');
     archSpy.mockImplementation(() => os['arch']);
     execSpy = jest.spyOn(cp, 'execSync');
+    processExitSpy = jest
+      .spyOn(process, 'exit')
+      .mockImplementation((() => {}) as () => never);
 
     // @actions/tool-cache
     findSpy = jest.spyOn(tc, 'find');
diff --git a/__tests__/official-installer.test.ts b/__tests__/official-installer.test.ts
index 2d36c19c7..4f6bf4145 100644
--- a/__tests__/official-installer.test.ts
+++ b/__tests__/official-installer.test.ts
@@ -46,6 +46,7 @@ describe('setup-node', () => {
   let isCacheActionAvailable: jest.SpyInstance;
   let getExecOutputSpy: jest.SpyInstance;
   let getJsonSpy: jest.SpyInstance;
+  let processExitSpy: jest.SpyInstance;
 
   beforeEach(() => {
     // @actions/core
@@ -63,6 +64,9 @@ describe('setup-node', () => {
     archSpy = jest.spyOn(osm, 'arch');
     archSpy.mockImplementation(() => os['arch']);
     execSpy = jest.spyOn(cp, 'execSync');
+    processExitSpy = jest
+      .spyOn(process, 'exit')
+      .mockImplementation((() => {}) as () => never);
 
     // @actions/tool-cache
     findSpy = jest.spyOn(tc, 'find');
diff --git a/__tests__/rc-installer.test.ts b/__tests__/rc-installer.test.ts
index 736260a4d..58b0a5eae 100644
--- a/__tests__/rc-installer.test.ts
+++ b/__tests__/rc-installer.test.ts
@@ -41,6 +41,7 @@ describe('setup-node', () => {
   let isCacheActionAvailable: jest.SpyInstance;
   let getExecOutputSpy: jest.SpyInstance;
   let getJsonSpy: jest.SpyInstance;
+  let processExitSpy: jest.SpyInstance;
 
   beforeEach(() => {
     // @actions/core
@@ -58,6 +59,9 @@ describe('setup-node', () => {
     archSpy = jest.spyOn(osm, 'arch');
     archSpy.mockImplementation(() => os['arch']);
     execSpy = jest.spyOn(cp, 'execSync');
+    processExitSpy = jest
+      .spyOn(process, 'exit')
+      .mockImplementation((() => {}) as () => never);
 
     // @actions/tool-cache
     findSpy = jest.spyOn(tc, 'find');
diff --git a/dist/setup/index.js b/dist/setup/index.js
index 0512f3a79..6a837faef 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -93711,6 +93711,9 @@ function run() {
         catch (err) {
             core.setFailed(err.message);
         }
+        // Explicit process.exit() to not wait for hanging promises,
+        // see https://github.com/actions/setup-node/issues/878
+        process.exit();
     });
 }
 exports.run = run;
diff --git a/src/main.ts b/src/main.ts
index ac0517665..e4594092d 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -77,6 +77,10 @@ export async function run() {
   } catch (err) {
     core.setFailed((err as Error).message);
   }
+
+  // Explicit process.exit() to not wait for hanging promises,
+  // see https://github.com/actions/setup-node/issues/878
+  process.exit();
 }
 
 function resolveVersionInput(): string {