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 7 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
68 changes: 62 additions & 6 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,7 +56,7 @@ export class NpmCommand {
private static npmPkgPath = require.resolve('npm/package.json');

public static runNpmCmd(cmd: string, options = {} as NpmCommandOptions): NpmCommandResult {
const npmCli = NpmCommand.npmCli();
const npmCli = NpmCommand.npmCli(options.cliRoot);
const exec = `${npmCli} ${cmd} --registry=${options.registry} --json`;
const npmShowResult = shelljs.exec(exec, {
...options,
Expand All @@ -78,28 +79,83 @@ export class NpmCommand {
return this.npmPkgPath;
}

private static npmCli(): string {
/**
* Return a executable path to this modules reference to npm as
* <path to node executable> <path to npm-cli.js>
*
* @private
*/
private static npmCli(root: string = undefined): string {
const nodeBinPath = NpmCommand.findNodeBin(root);
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']);
return `"${nodeBinPath}" "${path.join(prjPath, pkgJson.bin['npm'])}"`;
}

/**
* Locate node executable and return its full path.
* First see if node is on the path, if so, use unqualified name.
* If not on the path, try to locate the node version installed with sfdx.
* If found, return full path to the executable
* If sfdx or node executable cannot be found, an exception is thrown
*
* @private
*/
private static findNodeBin(root: string = undefined): string {
if (!root) {
throw new SfdxError('Plugin root dir is not set', 'PluginRootNotSet');
}
// find node within sfdx installation
const sfdxBinDirPaths = NpmCommand.getSfdxBinDirs(sfdxPath);
if (sfdxBinDirPaths?.length > 0) {
const nodeBinPath = shelljs
.find(sfdxBinDirPaths)
.filter((file) => {
const fileName = path.basename(file);
const stat = fs.statSync(file);
const isExecutable = !stat.isDirectory();
return isExecutable && (process.platform === 'win32' ? fileName === 'node.exe' : fileName === 'node');
})
.find((file) => file);
if (nodeBinPath) {
return fs.realpathSync(nodeBinPath);
}
}
// check to see if node is installed
mshanemc marked this conversation as resolved.
Show resolved Hide resolved
if (shelljs.which('node')) {
return 'node';
}
throw new SfdxError('Cannot locate node executable within sfdx installation.', 'CannotFindNodeExecutable');
}

/**
* Test each potential directory exists used for sfdx installation
*
* @param sfdxPath
* @private
*/
private static getSfdxBinDirs(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;
}
}
15 changes: 15 additions & 0 deletions 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,6 +117,9 @@ describe('InstallationVerification Tests', () => {
get configDir() {
return 'configDir';
},
get cliRoot() {
return __dirname;
},
};
const currentRegistry = process.env.SFDX_NPM_REGISTRY;
let plugin: NpmName;
Expand Down
Loading