Skip to content

Commit

Permalink
feat: support selecting distribution channel (#192)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Jason Luong <[email protected]>
  • Loading branch information
thisislawatts and j-luong authored Apr 5, 2024
1 parent fbf37b3 commit 78526e1
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 13 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ To run the code, a GitHub PR against `develop` should be raised with the committ

### Local debugging

A number of environment variable are required for debugging, here's an example launch config for `VSCode` that sets mandatory parameters such as `AGENT_TEMPDIRECTORY`, `INPUT_failOnIssues` and `INPUT_authToken`
A number of environment variable are required for debugging, here's an example launch config for `Visual Studio Code` that sets mandatory parameters such as `AGENT_TEMPDIRECTORY`, `INPUT_failOnIssues` and `INPUT_authToken`

```
```json
{
"version": "0.2.0",
"configurations": [
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ This extension requires that Node.js and npm be installed on the build agent. Th
| testDirectory | Alternate working directory. For example, if you want to test a manifest file in a directory other than the root of your repo, you would put in relative path to that directory. | no | none | string |
| ignoreUnknownCA | Use to ignore unknown or self-signed certificates. This might be useful in for self-hosted build agents with unusual network configurations or for Snyk on-prem installs configured with a self-signed certificate. | no | false | boolean |
| additionalArguments | Additional Snyk CLI arguments to be passed in. Refer to the Snyk CLI help page for information on additional arguments. | no | none | string |
| distributionChannel | Select distribution channel for Snyk binaries. 'Stable' is for stable releases, whilst 'Preview' is for access to the latest features. | no | Stable | string |

## Usage Examples

Expand Down
39 changes: 36 additions & 3 deletions snykTask/src/__tests__/install/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('getSnykDownloadInfo', () => {
expect(dlInfo).toEqual({
snyk: {
filename: 'snyk-linux',
downloadUrl: 'https://static.snyk.io/cli/latest/snyk-linux',
downloadUrl: 'https://static.snyk.io/cli/stable/snyk-linux',
},
snykToHtml: {
filename: 'snyk-to-html-linux',
Expand All @@ -38,7 +38,7 @@ describe('getSnykDownloadInfo', () => {
expect(dlInfo).toEqual({
snyk: {
filename: 'snyk-win.exe',
downloadUrl: 'https://static.snyk.io/cli/latest/snyk-win.exe',
downloadUrl: 'https://static.snyk.io/cli/stable/snyk-win.exe',
},
snykToHtml: {
filename: 'snyk-to-html-win.exe',
Expand All @@ -53,7 +53,40 @@ describe('getSnykDownloadInfo', () => {
expect(dlInfo).toEqual({
snyk: {
filename: 'snyk-macos',
downloadUrl: 'https://static.snyk.io/cli/latest/snyk-macos',
downloadUrl: 'https://static.snyk.io/cli/stable/snyk-macos',
},
snykToHtml: {
filename: 'snyk-to-html-macos',
downloadUrl:
'https://static.snyk.io/snyk-to-html/latest/snyk-to-html-macos',
},
});
});

it('retrieves the correct download info a preview release', () => {
const dlInfo = getSnykDownloadInfo(Platform.MacOS, 'preview');
expect(dlInfo).toEqual({
snyk: {
filename: 'snyk-macos',
downloadUrl: 'https://static.snyk.io/cli/preview/snyk-macos',
},
snykToHtml: {
filename: 'snyk-to-html-macos',
downloadUrl:
'https://static.snyk.io/snyk-to-html/latest/snyk-to-html-macos',
},
});
});

it('ignores invalid distribution channels', () => {
const dlInfo = getSnykDownloadInfo(
Platform.MacOS,
'invalid-channel' as any,
);
expect(dlInfo).toEqual({
snyk: {
filename: 'snyk-macos',
downloadUrl: 'https://static.snyk.io/cli/stable/snyk-macos',
},
snykToHtml: {
filename: 'snyk-to-html-macos',
Expand Down
8 changes: 4 additions & 4 deletions snykTask/src/__tests__/test-auth-token-retrieved.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ test('test auth token pulled from service connection', () => {
jest.doMock('azure-pipelines-task-lib/task', () => {
return {
// getInput: jest.fn((name: string, required?: boolean) => "mockValue")
getInput: jest.fn((name: string, required?: boolean) => {
getInput: jest.fn((name: string) => {
if (name === 'serviceConnectionEndpoint') {
return 'some-serviceConnectionEndpoint';
} else if (name === 'authToken') {
Expand Down Expand Up @@ -65,7 +65,7 @@ test('test auth token pulled from serviceConnectionEndpoint if both authToken an
jest.doMock('azure-pipelines-task-lib/task', () => {
return {
// getInput: jest.fn((name: string, required?: boolean) => "mockValue")
getInput: jest.fn((name: string, required?: boolean) => {
getInput: jest.fn((name: string) => {
if (name === 'serviceConnectionEndpoint') {
return 'some-serviceConnectionEndpoint';
} else if (name === 'authToken') {
Expand Down Expand Up @@ -99,7 +99,7 @@ test('test auth token pulled from authToken if both authToken set and serviceCon
jest.doMock('azure-pipelines-task-lib/task', () => {
return {
// getInput: jest.fn((name: string, required?: boolean) => "mockValue")
getInput: jest.fn((name: string, required?: boolean) => {
getInput: jest.fn((name: string) => {
if (name === 'serviceConnectionEndpoint') {
return null;
} else if (name === 'authToken') {
Expand Down Expand Up @@ -133,7 +133,7 @@ test('test auth token returns empty string if both authToken set and serviceConn
jest.doMock('azure-pipelines-task-lib/task', () => {
return {
// getInput: jest.fn((name: string, required?: boolean) => "mockValue")
getInput: jest.fn((name: string, required?: boolean) => {
getInput: jest.fn((name: string) => {
if (name === 'serviceConnectionEndpoint') {
return null;
} else if (name === 'authToken') {
Expand Down
5 changes: 5 additions & 0 deletions snykTask/src/__tests__/test-task-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ test('ensure that ignoreUnknownCA is false by default', () => {
expect(args.ignoreUnknownCA).toBe(false);
});

test('ensure that distributionChannel is stable by default', () => {
const args = defaultTaskArgs();
expect(args.getDistributionChannel()).toBe('stable');
});

describe('TaskArgs.setMonitorWhen', () => {
const args = defaultTaskArgs();

Expand Down
14 changes: 12 additions & 2 deletions snykTask/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
import * as fs from 'fs';
import * as path from 'path';
import { getSnykDownloadInfo, downloadExecutable } from './install';
import { CliDistributionChannel } from './types';

class SnykError extends Error {
constructor(message?: string) {
Expand Down Expand Up @@ -96,6 +97,8 @@ function parseInputArgs(): TaskArgs {
taskArgs.failOnThreshold =
tl.getInput('failOnThreshold', false) || Severity.LOW;
taskArgs.ignoreUnknownCA = tl.getBoolInput('ignoreUnknownCA', false);
taskArgs.distributionChannel = (tl.getInput('distributionChannel', false) ||
'stable') as CliDistributionChannel;

if (isDebugMode()) {
logAllTaskArgs(taskArgs);
Expand Down Expand Up @@ -403,6 +406,7 @@ async function run() {
}

const taskArgs: TaskArgs = parseInputArgs();
const distributionChannel = taskArgs.getDistributionChannel();
const snykToken = getAuthToken();
if (!snykToken) {
const errorMsg =
Expand All @@ -411,9 +415,15 @@ async function run() {
}

const platform: tl.Platform = tl.getPlatform();
if (isDebugMode()) console.log(`platform: ${platform}`);
if (isDebugMode()) {
console.log(`platform: ${platform}`);
console.log(`distributionChannel: ${distributionChannel}`);
}

const snykToolDownloads = getSnykDownloadInfo(platform);
const snykToolDownloads = getSnykDownloadInfo(
platform,
distributionChannel,
);
await downloadExecutable(agentTempDirectory, snykToolDownloads.snyk);
await downloadExecutable(agentTempDirectory, snykToolDownloads.snykToHtml);
const snykPath = path.resolve(
Expand Down
14 changes: 12 additions & 2 deletions snykTask/src/install/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Platform } from 'azure-pipelines-task-lib/task';
import * as fs from 'fs';
import * as path from 'path';
import * as https from 'https';
import { CliDistributionChannel } from '../types';

export type Executable = {
filename: string;
Expand All @@ -29,7 +30,10 @@ export type SnykDownloads = {
snykToHtml: Executable;
};

export function getSnykDownloadInfo(platform: Platform): SnykDownloads {
export function getSnykDownloadInfo(
platform: Platform,
distributionChannel: CliDistributionChannel = 'stable',
): SnykDownloads {
const baseUrl = 'https://static.snyk.io';

const filenameSuffixes: Record<Platform, string> = {
Expand All @@ -38,10 +42,16 @@ export function getSnykDownloadInfo(platform: Platform): SnykDownloads {
[Platform.MacOS]: 'macos',
};

const validDistributionChannels = ['stable', 'preview'];

if (!validDistributionChannels.includes(distributionChannel)) {
distributionChannel = 'stable';
}

return {
snyk: {
filename: `snyk-${filenameSuffixes[platform]}`,
downloadUrl: `${baseUrl}/cli/latest/snyk-${filenameSuffixes[platform]}`,
downloadUrl: `${baseUrl}/cli/${distributionChannel}/snyk-${filenameSuffixes[platform]}`,
},
snykToHtml: {
filename: `snyk-to-html-${filenameSuffixes[platform]}`,
Expand Down
20 changes: 20 additions & 0 deletions snykTask/src/task-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import * as tl from 'azure-pipelines-task-lib';
import { Severity, TestType, testTypeSeverityThreshold } from './task-lib';
import { CliDistributionChannel } from './types';
export type MonitorWhen = 'never' | 'noIssuesFound' | 'always';
class TaskArgs {
testType: string | undefined = 'app';
Expand All @@ -40,6 +41,12 @@ class TaskArgs {

delayAfterReportGenerationSeconds: number = 0;

/**
* The distribution channel to use for the Snyk CLI.
* Defaults to 'stable', but can be set to 'preview' for early access to new features.
*/
distributionChannel: CliDistributionChannel | undefined = 'stable';

// the params here are the ones which are mandatory
constructor(params: { failOnIssues: boolean }) {
this.failOnIssues = params.failOnIssues;
Expand Down Expand Up @@ -111,6 +118,19 @@ class TaskArgs {
return this.projectName;
}

public getDistributionChannel(): CliDistributionChannel {
if (!this.distributionChannel) {
this.distributionChannel = 'stable';
}

const validDistributionChannels = ['stable', 'preview'];
if (!validDistributionChannels.includes(this.distributionChannel)) {
this.distributionChannel = 'stable';
}

return this.distributionChannel;
}

// validate based on testTypeSeverityThreshold applicable thresholds
public validate() {
const taskTestType = this.testType || TestType.APPLICATION;
Expand Down
1 change: 1 addition & 0 deletions snykTask/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type CliDistributionChannel = 'stable' | 'preview';
13 changes: 13 additions & 0 deletions snykTask/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,19 @@
"defaultValue": "",
"helpMarkDown": "Additional command-line args for Snyk CLI (advanced)",
"groupName": "advanced"
},
{
"name": "distributionChannel",
"label": "CLI Distribution channel",
"type": "pickList",
"options": {
"stable": "Stable",
"preview": "Preview"
},
"required": false,
"defaultValue": "stable",
"helpMarkDown": "Defaults to 'stable', but can be set to 'preview' for early access to new features",
"groupName": "advanced"
}
],
"execution": {
Expand Down

0 comments on commit 78526e1

Please sign in to comment.