Skip to content

Commit

Permalink
Merge pull request #2149 from snyk/refactor/hotload
Browse files Browse the repository at this point in the history
refactor: dynamically load cli modules
  • Loading branch information
JackuB authored Aug 13, 2021
2 parents 34c9347 + 41f9ddf commit bb7b621
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 159 deletions.
35 changes: 19 additions & 16 deletions src/cli/commands/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
const abbrev = require('abbrev');
const hotload = require('../../lib/hotload')(__dirname);
require('../../lib/spinner').isRequired = false;

// the aim of this module is to load as little as possible to keep cli boot
// time as low as possible
// Wrapper for Commonjs compatibility
async function callModule(mod, args) {
const resolvedModule = await mod;
return (resolvedModule.default || resolvedModule)(...args);
}

const commands = {
auth: hotload('./auth'),
config: hotload('./config'),
help: hotload('./help'),
ignore: hotload('./ignore'),
modules: hotload('./modules'),
monitor: hotload('./monitor'),
fix: hotload('./fix'),
policy: hotload('./policy'),
protect: hotload('./protect'),
test: hotload('./test'),
version: hotload('./version'),
wizard: hotload('./protect/wizard'),
woof: hotload('./woof'),
auth: async (...args) => callModule(import('./auth'), args),
config: async (...args) => callModule(import('./config'), args),
help: async (...args) => callModule(import('./help'), args),
ignore: async (...args) => callModule(import('./ignore'), args),
monitor: async (...args) => callModule(import('./monitor'), args),
fix: async (...args) => callModule(import('./fix'), args),
policy: async (...args) => callModule(import('./policy'), args),
protect: async (...args) => callModule(import('./protect'), args),
test: async (...args) => callModule(import('./test'), args),
version: async (...args) => callModule(import('./version'), args),
wizard: async (...args) => callModule(import('./protect/wizard'), args),
woof: async (...args) => callModule(import('./woof'), args),
};

commands.aliases = abbrev(Object.keys(commands));
commands.aliases.t = 'test';

module.exports = commands;
7 changes: 4 additions & 3 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import * as alerts from '../lib/alerts';
import * as sln from '../lib/sln';
import { TestCommandResult } from './commands/types';
import { copy } from './copy';
import spinner = require('../lib/spinner');
import errors = require('../lib/errors/legacy-errors');
import ansiEscapes = require('ansi-escapes');
import * as spinner from '../lib/spinner';
import * as errors from '../lib/errors/legacy-errors';
import * as ansiEscapes from 'ansi-escapes';

import { isPathToPackageFile } from '../lib/detect';
import {
MissingTargetFileError,
Expand Down
108 changes: 39 additions & 69 deletions test/jest/acceptance/oauth-token.spec.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,58 @@
import {
chdirWorkspaces,
getWorkspaceJSON,
} from '../../acceptance/workspace-helper';
import { fakeServer } from '../../acceptance/fake-server';
import cli = require('../../../src/cli/commands');
import { chdir } from 'process';
import { createProjectFromWorkspace } from '../util/createProject';
import { runSnykCLI } from '../util/runSnykCLI';

describe('test using OAuth token', () => {
let oldkey: string;
let oldendpoint: string;
const apiKey = '123456789';
const port: string = process.env.PORT || process.env.SNYK_PORT || '12345';

const BASE_API = '/api/v1';

const server = fakeServer(BASE_API, apiKey);

const noVulnsResult = getWorkspaceJSON(
'fail-on',
'no-vulns',
'vulns-result.json',
);

let origCwd: string;

beforeAll(async () => {
origCwd = process.cwd();
process.env.SNYK_API = `http://localhost:${port}${BASE_API}`;
process.env.SNYK_HOST = `http://localhost:${port}`;

let key = await cli.config('get', 'api');
oldkey = key;

key = await cli.config('get', 'endpoint');
oldendpoint = key;

await new Promise((resolve) => {
server.listen(port, resolve);
});
let server: ReturnType<typeof fakeServer>;
let env: Record<string, string>;

beforeAll((done) => {
const apiPath = '/api/v1';
const apiPort = process.env.PORT || process.env.SNYK_PORT || '12345';
env = {
...process.env,
SNYK_API: 'http://localhost:' + apiPort + apiPath,
SNYK_TOKEN: '123456789',
SNYK_OAUTH_TOKEN: 'oauth-jwt-token',
};

server = fakeServer(apiPath, env.SNYK_TOKEN);
server.listen(apiPort, () => done());
});

afterAll(async () => {
delete process.env.SNYK_API;
delete process.env.SNYK_HOST;
delete process.env.SNYK_PORT;
delete process.env.SNYK_OAUTH_TOKEN;

await server.close();
let key = 'set';
let value = `api=${oldkey}`;
if (!oldkey) {
key = 'unset';
value = 'api';
}
await cli.config(key, value);
if (oldendpoint) {
await cli.config('endpoint', oldendpoint);
}
chdir(origCwd);
});

it('successfully tests a project with an OAuth env variable set', async () => {
process.env.SNYK_OAUTH_TOKEN = 'oauth-jwt-token';
const project = await createProjectFromWorkspace('fail-on/no-vulns');
const jsonObj = JSON.parse(await project.read('vulns-result.json'));
server.setNextResponse(jsonObj);

server.setNextResponse(noVulnsResult);
chdirWorkspaces('fail-on');
await cli.test('no-vulns', {
json: true,
const { code } = await runSnykCLI(`test --json`, {
cwd: project.path(),
env,
});
const req = server.popRequest();
expect(req.headers.authorization).toBe('Bearer oauth-jwt-token');
expect(req.method).toBe('POST');

expect(code).toEqual(0);
const requests = server.popRequests(2);
expect(requests[0].headers.authorization).toBe('Bearer oauth-jwt-token');
expect(requests[0].method).toBe('POST');
});

it('successfully monitors a project with an OAuth env variable set', async () => {
process.env.SNYK_OAUTH_TOKEN = 'oauth-jwt-token';
const project = await createProjectFromWorkspace('fail-on/no-vulns');
const jsonObj = JSON.parse(await project.read('vulns-result.json'));
server.setNextResponse(jsonObj);

server.setNextResponse(noVulnsResult);
chdirWorkspaces('fail-on');
await cli.monitor('no-vulns', {
json: true,
const { code } = await runSnykCLI(`monitor --json`, {
cwd: project.path(),
env,
});
const req = server.popRequest();
expect(req.headers.authorization).toBe('Bearer oauth-jwt-token');
expect(req.method).toBe('PUT');

expect(code).toEqual(0);
const requests = server.popRequests(2);
expect(requests[0].headers.authorization).toBe('Bearer oauth-jwt-token');
expect(requests[0].method).toBe('PUT');
});
});
74 changes: 18 additions & 56 deletions test/jest/system/lib/commands/fix/fix.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,50 @@ import * as pathLib from 'path';
import stripAnsi from 'strip-ansi';

import { fakeServer } from '../../../../../acceptance/fake-server';
import cli = require('../../../../../../src/cli/commands');

const main = './bin/snyk'.replace(/\//g, pathLib.sep);
const testTimeout = 50000;
describe('snyk fix (system tests)', () => {
let oldkey;
let oldendpoint;
const main = './bin/snyk'.replace(/\//g, pathLib.sep);
const testTimeout = 50000;

const apiKey = '123456789';
const port = process.env.PORT || process.env.SNYK_PORT || '12345';

const BASE_API = '/api/v1';
const SNYK_API = 'http://localhost:' + port + BASE_API;
const SNYK_HOST = 'http://localhost:' + port;

const server = fakeServer(BASE_API, apiKey);

const noVulnsProjectPath = pathLib.join(
__dirname,
'/acceptance',
'workspaces',
'no-vulns',
);

beforeAll(async () => {
let key = await cli.config('get', 'api');
oldkey = key;

key = await cli.config('get', 'endpoint');
oldendpoint = key;
const env = {
PATH: process.env.PATH,
SNYK_TOKEN: apiKey,
SNYK_API,
SNYK_HOST,
};

beforeAll(async () => {
await new Promise((resolve) => {
server.listen(port, resolve);
});
});

afterAll(async () => {
delete process.env.SNYK_API;
delete process.env.SNYK_HOST;
delete process.env.SNYK_PORT;

await server.close();
let key = 'set';
let value = 'api=' + oldkey;
if (!oldkey) {
key = 'unset';
value = 'api';
}
await cli.config(key, value);
if (oldendpoint) {
await cli.config('endpoint', oldendpoint);
}
});

it(
'`errors when FF is not enabled`',
(done) => {
exec(
`node ${main} fix --org=no-flag`,
{
env: {
PATH: process.env.PATH,
SNYK_TOKEN: apiKey,
SNYK_API,
SNYK_HOST,
},
env,
},
(err, stdout, stderr) => {
if (!err) {
Expand All @@ -83,18 +64,14 @@ describe('snyk fix (system tests)', () => {
},
testTimeout,
);

it(
'`shows error when called with --source`',
(done) => {
exec(
`node ${main} fix --source`,
{
env: {
PATH: process.env.PATH,
SNYK_TOKEN: apiKey,
SNYK_API,
SNYK_HOST,
},
env,
},
(err, stdout, stderr) => {
if (!err) {
Expand All @@ -119,12 +96,7 @@ describe('snyk fix (system tests)', () => {
exec(
`node ${main} fix --docker`,
{
env: {
PATH: process.env.PATH,
SNYK_TOKEN: apiKey,
SNYK_API,
SNYK_HOST,
},
env,
},
(err, stdout, stderr) => {
if (!err) {
Expand All @@ -149,12 +121,7 @@ describe('snyk fix (system tests)', () => {
exec(
`node ${main} fix --code`,
{
env: {
PATH: process.env.PATH,
SNYK_TOKEN: apiKey,
SNYK_API,
SNYK_HOST,
},
env,
},
(err, stdout, stderr) => {
if (!err) {
Expand All @@ -179,12 +146,7 @@ describe('snyk fix (system tests)', () => {
exec(
`node ${main} fix ${noVulnsProjectPath}`,
{
env: {
PATH: process.env.PATH,
SNYK_TOKEN: apiKey,
SNYK_API,
SNYK_HOST,
},
env,
},
(err, stdout, stderr) => {
if (!err) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { join } from 'path';
import { mocked } from 'ts-jest/utils';
import { NeedleResponse } from 'needle';
import { test } from '../../../../../src/cli/commands';
const test = require('../../../../../src/cli/commands/test');
import { loadPlugin } from '../../../../../src/lib/plugins/index';
import { CommandResult } from '../../../../../src/cli/commands/types';
import { makeRequest } from '../../../../../src/lib/request/request';
Expand Down
Loading

0 comments on commit bb7b621

Please sign in to comment.