Skip to content

Commit f1a60cd

Browse files
Feat: Add support for different Node versions (NVM support) (#1357)
* add nvm tool support functions Signed-off-by: Aryan Rajoria <[email protected]> * added all required functions Signed-off-by: Aryan Rajoria <[email protected]> * bug fixes Signed-off-by: Aryan Rajoria <[email protected]> * add nodejs<version> project alias Signed-off-by: Aryan Rajoria <[email protected]> * add nodejs version supported to project type alias Signed-off-by: Aryan Rajoria <[email protected]> * run gen-types Signed-off-by: Aryan Rajoria <[email protected]> * run lint Signed-off-by: Aryan Rajoria <[email protected]> * applied reviewed changes Signed-off-by: Aryan Rajoria <[email protected]> * advanced security warning fixed Signed-off-by: Aryan Rajoria <[email protected]> * rerun gen types Signed-off-by: Aryan Rajoria <[email protected]> * run lint Signed-off-by: Aryan Rajoria <[email protected]> * remove comment Signed-off-by: Aryan Rajoria <[email protected]> * add repotests Signed-off-by: Aryan Rajoria <[email protected]> * fix indentation error Signed-off-by: Aryan Rajoria <[email protected]> * added ref for all new repotests Signed-off-by: Aryan Rajoria <[email protected]> * fix repotests Signed-off-by: Aryan Rajoria <[email protected]> * remove refs Signed-off-by: Aryan Rajoria <[email protected]> * fix directoryPath Signed-off-by: Aryan Rajoria <[email protected]> * lint Signed-off-by: Aryan Rajoria <[email protected]> * debug nvm ci Signed-off-by: Aryan Rajoria <[email protected]> * debug remove unused import Signed-off-by: Aryan Rajoria <[email protected]> * debug bug fix Signed-off-by: Aryan Rajoria <[email protected]> * debug add nvm and rearrange repotests Signed-off-by: Aryan Rajoria <[email protected]> * add early return Signed-off-by: Aryan Rajoria <[email protected]> * remove directory list Signed-off-by: Aryan Rajoria <[email protected]> * run gen-types Signed-off-by: Aryan Rajoria <[email protected]> * add suggested changes Signed-off-by: Aryan Rajoria <[email protected]> --------- Signed-off-by: Aryan Rajoria <[email protected]>
1 parent e593e91 commit f1a60cd

File tree

10 files changed

+337
-6
lines changed

10 files changed

+337
-6
lines changed

.github/workflows/repotests.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,22 @@ jobs:
128128
repository: 'zoom/meetingsdk-vuejs-sample'
129129
ref: 'v2.18.0'
130130
path: 'repotests/meetingsdk-vuejs-sample'
131+
- uses: actions/checkout@v4
132+
with:
133+
repository: 'kriasoft/react-app'
134+
path: 'repotests/react-app'
135+
- uses: actions/checkout@v4
136+
with:
137+
repository: 'patrickjuchli/basic-ftp'
138+
path: 'repotests/basic-ftp'
139+
- uses: actions/checkout@v4
140+
with:
141+
repository: 'Atome-FE/llama-node'
142+
path: 'repotests/llama-node'
143+
- uses: actions/checkout@v4
144+
with:
145+
repository: 'DIYgod/RSSHub'
146+
path: 'repotests/RSSHub'
131147
- uses: actions/checkout@v4
132148
with:
133149
repository: 'sveltejs/examples'
@@ -232,6 +248,26 @@ jobs:
232248
run: |
233249
curl -s "https://get.sdkman.io" | bash
234250
if: runner.os != 'Windows'
251+
- name: repotests react-app
252+
run: |
253+
FETCH_LICENSE=false bin/cdxgen.js -p -t nodejs8 repotests/react-app -o bomresults/react-app.json
254+
node bin/evinse.js -i bomresults/react-app.json -o bomresults/react-app.evinse.json -l javascript --with-data-flow -p repotests/react-app
255+
shell: bash
256+
- name: repotests basic-ftp
257+
run: |
258+
FETCH_LICENSE=false bin/cdxgen.js -p -t nodejs10 repotests/basic-ftp -o bomresults/basic-ftp.json
259+
node bin/evinse.js -i bomresults/basic-ftp.json -o bomresults/basic-ftp.evinse.json -l javascript --with-data-flow -p repotests/basic-ftp
260+
shell: bash
261+
- name: repotests llama-node
262+
run: |
263+
FETCH_LICENSE=false bin/cdxgen.js -p -t nodejs16 repotests/llama-node -o bomresults/llama-node.json
264+
node bin/evinse.js -i bomresults/llama-node.json -o bomresults/llama-node.evinse.json -l javascript --with-data-flow -p repotests/llama-node
265+
shell: bash
266+
- name: repotests RSSHub
267+
run: |
268+
FETCH_LICENSE=false bin/cdxgen.js -p -t nodejs22 repotests/RSSHub -o bomresults/RSSHub.json
269+
node bin/evinse.js -i bomresults/RSSHub.json -o bomresults/RSSHub.evinse.json -l javascript --with-data-flow -p repotests/RSSHub
270+
shell: bash
235271
- name: repotests java-sec-code
236272
run: |
237273
bin/cdxgen.js -p -t java repotests/java-sec-code -o bomresults/bom-java-sec-code-1.json --include-formulation --include-crypto

envcontext.js

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Buffer } from "node:buffer";
22
import { spawnSync } from "node:child_process";
33
import { existsSync } from "node:fs";
44
import { homedir } from "node:os";
5-
import { delimiter, join } from "node:path";
5+
import { delimiter, dirname, join } from "node:path";
66
import process from "node:process";
77
import {
88
CARGO_CMD,
@@ -362,6 +362,21 @@ export function isSdkmanAvailable() {
362362
return isSdkmanSetup;
363363
}
364364

365+
/**
366+
* Method to check if nvm is available.
367+
*/
368+
export function isNvmAvailable() {
369+
let isNvmSetup = false;
370+
const result = spawnSync(process.env.SHELL || "bash", ["-i", "-c", "nvm"], {
371+
encoding: "utf-8",
372+
shell: process.env.SHELL || true,
373+
});
374+
if (result.status === 0) {
375+
isNvmSetup = true;
376+
}
377+
return isNvmSetup;
378+
}
379+
365380
/**
366381
* Method to check if a given sdkman tool is installed and available.
367382
*
@@ -491,6 +506,80 @@ export function installSdkmanTool(toolType, toolName) {
491506
return true;
492507
}
493508

509+
/**
510+
* Method to check if a given nvm tool is installed and available.
511+
*
512+
* @param {String} toolName Tool name with version. Eg: 22.0.2-tem
513+
*
514+
* @returns {String} path of nvm if present, otherwise false
515+
*/
516+
export function getNvmToolDirectory(toolName) {
517+
const resultWhichNode = spawnSync(
518+
process.env.SHELL || "bash",
519+
["-i", "-c", `"nvm which ${toolName}"`],
520+
{
521+
encoding: "utf-8",
522+
shell: process.env.SHELL || true,
523+
},
524+
);
525+
if (DEBUG_MODE) {
526+
if (console.stdout) {
527+
console.log(resultWhichNode.stdout);
528+
}
529+
if (console.stderr) {
530+
console.log(resultWhichNode.stderr);
531+
}
532+
}
533+
if (resultWhichNode.status !== 0 || resultWhichNode.stderr) {
534+
return;
535+
}
536+
537+
return dirname(resultWhichNode.stdout.trim());
538+
}
539+
540+
/**
541+
* Method to return nvm tool path
542+
*
543+
* @param {String} toolVersion Tool name with version. Eg: 22.0.2-tem
544+
*
545+
* @returns {String} path of the tool if not found installs and then returns paths. false if encounters an error.
546+
*/
547+
export function getOrInstallNvmTool(toolVersion) {
548+
const nvmNodePath = getNvmToolDirectory(toolVersion);
549+
if (!nvmNodePath) {
550+
// nvm couldn't directly use toolName so maybe needs to be installed
551+
const resultInstall = spawnSync(
552+
process.env.SHELL || "bash",
553+
["-i", "-c", `"nvm install ${toolVersion}"`],
554+
{
555+
encoding: "utf-8",
556+
shell: process.env.SHELL || true,
557+
},
558+
);
559+
560+
if (DEBUG_MODE) {
561+
if (console.stdout) {
562+
console.log(resultInstall.stdout);
563+
}
564+
if (console.stderr) {
565+
console.log(resultInstall.stderr);
566+
}
567+
}
568+
569+
if (resultInstall.status !== 0) {
570+
// There was some problem install the tool
571+
// output has already been printed out
572+
return false;
573+
}
574+
const nvmNodePath = getNvmToolDirectory(toolVersion);
575+
if (nvmNodePath) {
576+
return nvmNodePath;
577+
}
578+
return false;
579+
}
580+
return nvmNodePath;
581+
}
582+
494583
/**
495584
* Retrieve sdkman tool full name
496585
*/

envcontext.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
collectRustInfo,
1212
getBranch,
1313
getOriginUrl,
14+
isNvmAvailable,
15+
isNvmToolAvailable,
1416
isSdkmanAvailable,
1517
isSdkmanToolAvailable,
1618
listFiles,
@@ -39,3 +41,10 @@ test("sdkman tests", () => {
3941
expect(isSdkmanToolAvailable("java", "22.0.1-tem")).toBeTruthy();
4042
}
4143
});
44+
45+
test("nvm tests", () => {
46+
if (process.env?.SDKMAN_VERSION) {
47+
expect(isNvmAvailable()).toBeTruthy();
48+
expect(isNvmToolAvailable("22")).toBeTruthy();
49+
}
50+
});

pregen.js

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
import { mkdtempSync } from "node:fs";
1+
import { spawn, spawnSync } from "node:child_process";
2+
import { mkdtempSync, readdirSync } from "node:fs";
23
import { arch, platform, tmpdir } from "node:os";
3-
import { join } from "node:path";
4+
import { delimiter, join } from "node:path";
45
import {
56
SDKMAN_TOOL_ALIASES,
7+
getNvmToolDirectory,
8+
getOrInstallNvmTool,
69
installSdkmanTool,
10+
isNvmAvailable,
711
isSdkmanAvailable,
812
} from "./envcontext.js";
913
import { DEBUG_MODE, hasAnyProjectType } from "./utils.js";
@@ -26,6 +30,7 @@ export function prepareEnv(filePath, options) {
2630
}
2731
// Check the pre-requisites for python
2832
preparePythonEnv(filePath, options);
33+
prepareNodeEnv(filePath, options);
2934
}
3035

3136
/**
@@ -91,3 +96,143 @@ export function preparePythonEnv(filePath, options) {
9196
}
9297
}
9398
}
99+
100+
/**
101+
* Method to check and prepare the environment for node
102+
*
103+
* @param {String} filePath Path
104+
* @param {Object} options CLI Options
105+
*/
106+
export function prepareNodeEnv(filePath, options) {
107+
// check tool for windows
108+
for (const pt of options.projectType) {
109+
const nodeVersion = pt.replace(/\D/g, "");
110+
if (
111+
pt.startsWith("nodejs") &&
112+
nodeVersion &&
113+
!process.env.NODE_INSTALL_ARGS
114+
) {
115+
if (!isNvmAvailable()) {
116+
if (process.env.NVM_DIR) {
117+
// for scenarios where nvm is not present, but
118+
// we have $NVM_DIR
119+
// custom logic to find nvmNodePath
120+
let nvmNodePath;
121+
const possibleNodeDir = join(process.env.NVM_DIR, "versions", "node");
122+
123+
if (!tryLoadNvmAndInstallTool(nodeVersion)) {
124+
console.log(
125+
`Could not install Nodejs${nodeVersion}. There is a problem with loading nvm from ${process.env.NVM_DIR}`,
126+
);
127+
return;
128+
}
129+
130+
const nodeVersionArray = readdirSync(possibleNodeDir, {
131+
withFileTypes: true,
132+
});
133+
const nodeRe = new RegExp(`^v${nodeVersion}.`);
134+
for (const nodeVersionsIter of nodeVersionArray) {
135+
const fullPath = join(possibleNodeDir, nodeVersionsIter.name);
136+
if (
137+
nodeVersionsIter.isDirectory() &&
138+
nodeRe.test(nodeVersionsIter.name)
139+
) {
140+
nvmNodePath = join(fullPath, "bin");
141+
}
142+
}
143+
if (nvmNodePath) {
144+
doNpmInstall(filePath, nvmNodePath);
145+
} else {
146+
console.log(
147+
`"node version ${nodeVersion} was not found. Please install it with 'nvm install ${nodeVersion}"`,
148+
);
149+
return;
150+
}
151+
} else {
152+
console.log(
153+
"Install nvm by following the instructions at https://github.com/nvm-sh/nvm",
154+
);
155+
return;
156+
}
157+
}
158+
// set path instead of nvm use
159+
const nvmNodePath = getOrInstallNvmTool(nodeVersion);
160+
doNpmInstall(filePath, nvmNodePath);
161+
}
162+
}
163+
}
164+
165+
/**
166+
* If NVM_DIR is in path, however nvm command is not loaded.
167+
* it is possible that required nodeVersion is not installed.
168+
* This function loads nvm and install the nodeVersion
169+
*
170+
* @param {String} nodeVersion required version number
171+
*
172+
* @returns {Boolean} true if successful, otherwise false
173+
*/
174+
export function tryLoadNvmAndInstallTool(nodeVersion) {
175+
const NVM_DIR = process.env.NVM_DIR;
176+
177+
const command = `
178+
if [ -f ${NVM_DIR}/nvm.sh ]; then
179+
. ${NVM_DIR}/nvm.sh
180+
nvm install ${nodeVersion}
181+
else
182+
echo "NVM script not found at ${NVM_DIR}/nvm.sh"
183+
exit 1
184+
fi
185+
`;
186+
187+
const spawnedShell = spawnSync(process.env.SHELL || "bash", ["-c", command], {
188+
encoding: "utf-8",
189+
shell: process.env.SHELL || true,
190+
});
191+
192+
return result.status === 0;
193+
}
194+
195+
/**
196+
* This method installs and create package-lock.json
197+
*
198+
* @param {String} filePath Path
199+
* @param {String} nvmNodePath Path to node version in nvm
200+
*/
201+
export function doNpmInstall(filePath, nvmNodePath) {
202+
// we do not install if INSTALL_ARGS set false
203+
if (process.env.NODE_INSTALL_ARGS === false) {
204+
return;
205+
}
206+
207+
const newPath = `${nvmNodePath}${delimiter}${process.env.PATH}`;
208+
209+
const resultNpmInstall = spawnSync(
210+
process.env.SHELL || "bash",
211+
[
212+
"-i",
213+
"-c",
214+
`export PATH='${nvmNodePath}${delimiter}$PATH' && npm install --package-lock-only`,
215+
],
216+
{
217+
encoding: "utf-8",
218+
shell: process.env.SHELL || true,
219+
cwd: filePath,
220+
env: {
221+
...process.env,
222+
PATH: newPath,
223+
},
224+
},
225+
);
226+
227+
if (resultNpmInstall.status !== 0 || resultNpmInstall.stderr) {
228+
// There was some problem with NpmInstall
229+
if (DEBUG_MODE) {
230+
if (console.stdout) {
231+
console.log(result.stdout);
232+
}
233+
if (console.stderr) {
234+
console.log(result.stderr);
235+
}
236+
}
237+
}
238+
}

types/envcontext.d.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ export function collectEnvInfo(dir: any): {
145145
* Method to check if sdkman is available.
146146
*/
147147
export function isSdkmanAvailable(): boolean;
148+
/**
149+
* Method to check if nvm is available.
150+
*/
151+
export function isNvmAvailable(): boolean;
148152
/**
149153
* Method to check if a given sdkman tool is installed and available.
150154
*
@@ -163,6 +167,22 @@ export function isSdkmanToolAvailable(toolType: string, toolName: string): boole
163167
* @returns {Boolean} true if the tool is available. false otherwise.
164168
*/
165169
export function installSdkmanTool(toolType: string, toolName: string): boolean;
170+
/**
171+
* Method to check if a given nvm tool is installed and available.
172+
*
173+
* @param {String} toolName Tool name with version. Eg: 22.0.2-tem
174+
*
175+
* @returns {String} path of nvm if present, otherwise false
176+
*/
177+
export function getNvmToolDirectory(toolName: string): string;
178+
/**
179+
* Method to return nvm tool path
180+
*
181+
* @param {String} toolVersion Tool name with version. Eg: 22.0.2-tem
182+
*
183+
* @returns {String} path of the tool if not found installs and then returns paths. false if encounters an error.
184+
*/
185+
export function getOrInstallNvmTool(toolVersion: string): string;
166186
export const GIT_COMMAND: any;
167187
export namespace SDKMAN_TOOL_ALIASES {
168188
let java8: string;

types/envcontext.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)