Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: verify fails if node not installed #167

Merged
merged 20 commits into from
Sep 24, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/commands/plugins/trust/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ export class Verify extends SfdxCommand {
const vConfig = new VerificationConfig();

const configContext: ConfigContext = {
cacheDir: get(this.config, 'configDir') as string,
configDir: get(this.config, 'cacheDir') as string,
cacheDir: get(this.config, 'cacheDir') as string,
configDir: get(this.config, 'configDir') as string,
dataDir: get(this.config, 'dataDir') as string,
cliRoot: get(this.config, 'root') as string,
};

this.logger.debug(`cacheDir: ${configContext.cacheDir}`);
Expand Down
1 change: 1 addition & 0 deletions src/hooks/verifyInstallSignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const hook: Hook.PluginsPreinstall = async function (options) {
cacheDir: options.config.cacheDir,
configDir: options.config.configDir,
dataDir: options.config.dataDir,
cliRoot: options.config.root,
};

const vConfig = VerificationConfigBuilder.build(npmName, configContext);
Expand Down
7 changes: 5 additions & 2 deletions src/lib/installationVerification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface ConfigContext {
configDir?: string;
cacheDir?: string;
dataDir?: string;
cliRoot?: string;
}
export interface Verifier {
verify(): Promise<NpmMeta>;
Expand Down Expand Up @@ -306,7 +307,9 @@ export class InstallationVerification implements Verifier {
// Make sure the cache path exists.
try {
await fs.mkdirp(this.getCachePath());
new NpmModule(npmMeta.moduleName, npmMeta.version).pack(getNpmRegistry().href, { cwd: this.getCachePath() });
new NpmModule(npmMeta.moduleName, npmMeta.version, this.config.cliRoot).pack(getNpmRegistry().href, {
cwd: this.getCachePath(),
});
const tarBallFile = fs
.readdirSync(this.getCachePath(), { withFileTypes: true })
.find((entry) => entry.isFile() && entry.name.includes(npmMeta.version));
Expand Down Expand Up @@ -345,7 +348,7 @@ export class InstallationVerification implements Verifier {
? `@${this.pluginNpmName.scope}/${this.pluginNpmName.name}`
: this.pluginNpmName.name;

const npmModule = new NpmModule(npmShowModule, this.pluginNpmName.tag);
const npmModule = new NpmModule(npmShowModule, this.pluginNpmName.tag, this.config.cliRoot);
const npmMetadata = npmModule.show(npmRegistry.href);
logger.debug('retrieveNpmMeta | Found npm meta information.');
if (!npmMetadata.versions) {
Expand Down
57 changes: 52 additions & 5 deletions src/lib/npmCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type NpmShowResults = {
type NpmCommandOptions = shelljs.ExecOptions & {
json?: boolean;
registry?: string;
cliRoot?: string;
};

type NpmCommandResult = NpmShowResults & {
Expand All @@ -55,9 +56,10 @@ export class NpmCommand {
private static npmPkgPath = require.resolve('npm/package.json');

public static runNpmCmd(cmd: string, options = {} as NpmCommandOptions): NpmCommandResult {
const nodeExecutable = NpmCommand.findNode(options.cliRoot);
const npmCli = NpmCommand.npmCli();
const exec = `${npmCli} ${cmd} --registry=${options.registry} --json`;
const npmShowResult = shelljs.exec(exec, {
const command = `"${nodeExecutable}" "${npmCli}" ${cmd} --registry=${options.registry} --json`;
const npmShowResult = shelljs.exec(command, {
...options,
silent: true,
fatal: true,
Expand All @@ -78,28 +80,73 @@ export class NpmCommand {
return this.npmPkgPath;
}

/**
* Returns the path to the npm-cli.js file in this package's node_modules
*
* @private
*/
private static npmCli(): string {
const pkgPath = NpmCommand.npmPackagePath();
const pkgJson = fs.readJsonSync(pkgPath) as NpmPackage;
const prjPath = pkgPath.substring(0, pkgPath.lastIndexOf(path.sep));
return path.join(prjPath, pkgJson.bin['npm']);
}

/**
* Locate node executable and return its absolute path
* First it tries to locate the node executable on the root path passed in
* If not found then tries to use whatver 'node' resolves to on the user's PATH
* If found return absolute path to the executable
* If the node executable cannot be found, an error is thrown
*
* @private
*/
private static findNode(root: string = undefined): string {
if (root) {
const sfdxBinDirs = NpmCommand.findSfdxBinDirs(root);
if (sfdxBinDirs.length > 0) {
// Find the node executable
const node = shelljs.find(sfdxBinDirs).find((file) => file.includes('node'));
if (node) {
return fs.realpathSync(node);
}
}
}

// Check to see if node is installed
const nodeShellString: shelljs.ShellString = shelljs.which('node');
if (nodeShellString?.code === 0 && nodeShellString?.stdout) return nodeShellString.stdout;

throw new SfdxError('Cannot locate node executable.', 'CannotFindNodeExecutable');
}

/**
* Finds the bin directory in the sfdx installation root path
*
* @param sfdxPath
* @private
*/
private static findSfdxBinDirs(sfdxPath: string): string[] {
return sfdxPath
? [path.join(sfdxPath, 'bin'), path.join(sfdxPath, 'client', 'bin')].filter((p) => fs.existsSync(p))
: [];
}
}

export class NpmModule {
public npmMeta: NpmMeta;
public constructor(private module: string, private version: string = 'latest') {
public constructor(private module: string, private version: string = 'latest', private cliRoot: string = undefined) {
this.npmMeta = {
moduleName: module,
};
}

public show(registry: string): NpmShowResults {
return NpmCommand.runNpmCmd(`show ${this.module}@${this.version}`, { registry });
return NpmCommand.runNpmCmd(`show ${this.module}@${this.version}`, { registry, cliRoot: this.cliRoot });
}

public pack(registry: string, options?: shelljs.ExecOptions): void {
NpmCommand.runNpmCmd(`pack ${this.module}@${this.version}`, { ...options, registry });
NpmCommand.runNpmCmd(`pack ${this.module}@${this.version}`, { ...options, registry, cliRoot: this.cliRoot });
return;
}
}
23 changes: 22 additions & 1 deletion test/lib/installationVerification.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ const getShelljsExecStub = (
stderr,
stdout: JSON.stringify(PACK_RESULT),
};
} else if (cmd.includes('node')) {
return {
code: 0,
stderr,
stdout: 'node',
};
} else if (cmd.includes('sfdx')) {
return {
code: 0,
stderr,
stdout: 'sfdx',
};
} else {
throw new Error(`Unexpected test cmd - ${cmd}`);
}
Expand Down Expand Up @@ -105,12 +117,17 @@ describe('InstallationVerification Tests', () => {
get configDir() {
return 'configDir';
},
get cliRoot() {
return __dirname;
},
};
const currentRegistry = process.env.SFDX_NPM_REGISTRY;
let fsReaddirSyncStub: Sinon.SinonStub;
let plugin: NpmName;
let realpathSyncStub: Sinon.SinonStub;
let sandbox: sinon.SinonSandbox;
let shelljsExecStub: Sinon.SinonStub;
let fsReaddirSyncStub: Sinon.SinonStub;
let shelljsFindStub: Sinon.SinonStub;

beforeEach(() => {
sandbox = Sinon.createSandbox();
Expand All @@ -122,11 +139,15 @@ describe('InstallationVerification Tests', () => {
},
},
]);
realpathSyncStub = stubMethod(sandbox, fs, 'realpathSync').returns('node.exe');
shelljsFindStub = stubMethod(sandbox, shelljs, 'find').returns(['node.exe']);
plugin = NpmName.parse('foo');
});

afterEach(() => {
fsReaddirSyncStub.restore();
realpathSyncStub.restore();
shelljsFindStub.restore();
if (shelljsExecStub) {
shelljsExecStub.restore();
}
Expand Down
Loading