Skip to content

Commit 78526e1

Browse files
feat: support selecting distribution channel (#192)
--------- Co-authored-by: Jason Luong <[email protected]>
1 parent fbf37b3 commit 78526e1

File tree

10 files changed

+106
-13
lines changed

10 files changed

+106
-13
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ To run the code, a GitHub PR against `develop` should be raised with the committ
1111

1212
### Local debugging
1313

14-
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`
14+
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`
1515

16-
```
16+
```json
1717
{
1818
"version": "0.2.0",
1919
"configurations": [

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ This extension requires that Node.js and npm be installed on the build agent. Th
3636
| 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 |
3737
| 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 |
3838
| additionalArguments | Additional Snyk CLI arguments to be passed in. Refer to the Snyk CLI help page for information on additional arguments. | no | none | string |
39+
| distributionChannel | Select distribution channel for Snyk binaries. 'Stable' is for stable releases, whilst 'Preview' is for access to the latest features. | no | Stable | string |
3940

4041
## Usage Examples
4142

snykTask/src/__tests__/install/index.test.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('getSnykDownloadInfo', () => {
2323
expect(dlInfo).toEqual({
2424
snyk: {
2525
filename: 'snyk-linux',
26-
downloadUrl: 'https://static.snyk.io/cli/latest/snyk-linux',
26+
downloadUrl: 'https://static.snyk.io/cli/stable/snyk-linux',
2727
},
2828
snykToHtml: {
2929
filename: 'snyk-to-html-linux',
@@ -38,7 +38,7 @@ describe('getSnykDownloadInfo', () => {
3838
expect(dlInfo).toEqual({
3939
snyk: {
4040
filename: 'snyk-win.exe',
41-
downloadUrl: 'https://static.snyk.io/cli/latest/snyk-win.exe',
41+
downloadUrl: 'https://static.snyk.io/cli/stable/snyk-win.exe',
4242
},
4343
snykToHtml: {
4444
filename: 'snyk-to-html-win.exe',
@@ -53,7 +53,40 @@ describe('getSnykDownloadInfo', () => {
5353
expect(dlInfo).toEqual({
5454
snyk: {
5555
filename: 'snyk-macos',
56-
downloadUrl: 'https://static.snyk.io/cli/latest/snyk-macos',
56+
downloadUrl: 'https://static.snyk.io/cli/stable/snyk-macos',
57+
},
58+
snykToHtml: {
59+
filename: 'snyk-to-html-macos',
60+
downloadUrl:
61+
'https://static.snyk.io/snyk-to-html/latest/snyk-to-html-macos',
62+
},
63+
});
64+
});
65+
66+
it('retrieves the correct download info a preview release', () => {
67+
const dlInfo = getSnykDownloadInfo(Platform.MacOS, 'preview');
68+
expect(dlInfo).toEqual({
69+
snyk: {
70+
filename: 'snyk-macos',
71+
downloadUrl: 'https://static.snyk.io/cli/preview/snyk-macos',
72+
},
73+
snykToHtml: {
74+
filename: 'snyk-to-html-macos',
75+
downloadUrl:
76+
'https://static.snyk.io/snyk-to-html/latest/snyk-to-html-macos',
77+
},
78+
});
79+
});
80+
81+
it('ignores invalid distribution channels', () => {
82+
const dlInfo = getSnykDownloadInfo(
83+
Platform.MacOS,
84+
'invalid-channel' as any,
85+
);
86+
expect(dlInfo).toEqual({
87+
snyk: {
88+
filename: 'snyk-macos',
89+
downloadUrl: 'https://static.snyk.io/cli/stable/snyk-macos',
5790
},
5891
snykToHtml: {
5992
filename: 'snyk-to-html-macos',

snykTask/src/__tests__/test-auth-token-retrieved.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ test('test auth token pulled from service connection', () => {
3131
jest.doMock('azure-pipelines-task-lib/task', () => {
3232
return {
3333
// getInput: jest.fn((name: string, required?: boolean) => "mockValue")
34-
getInput: jest.fn((name: string, required?: boolean) => {
34+
getInput: jest.fn((name: string) => {
3535
if (name === 'serviceConnectionEndpoint') {
3636
return 'some-serviceConnectionEndpoint';
3737
} else if (name === 'authToken') {
@@ -65,7 +65,7 @@ test('test auth token pulled from serviceConnectionEndpoint if both authToken an
6565
jest.doMock('azure-pipelines-task-lib/task', () => {
6666
return {
6767
// getInput: jest.fn((name: string, required?: boolean) => "mockValue")
68-
getInput: jest.fn((name: string, required?: boolean) => {
68+
getInput: jest.fn((name: string) => {
6969
if (name === 'serviceConnectionEndpoint') {
7070
return 'some-serviceConnectionEndpoint';
7171
} else if (name === 'authToken') {
@@ -99,7 +99,7 @@ test('test auth token pulled from authToken if both authToken set and serviceCon
9999
jest.doMock('azure-pipelines-task-lib/task', () => {
100100
return {
101101
// getInput: jest.fn((name: string, required?: boolean) => "mockValue")
102-
getInput: jest.fn((name: string, required?: boolean) => {
102+
getInput: jest.fn((name: string) => {
103103
if (name === 'serviceConnectionEndpoint') {
104104
return null;
105105
} else if (name === 'authToken') {
@@ -133,7 +133,7 @@ test('test auth token returns empty string if both authToken set and serviceConn
133133
jest.doMock('azure-pipelines-task-lib/task', () => {
134134
return {
135135
// getInput: jest.fn((name: string, required?: boolean) => "mockValue")
136-
getInput: jest.fn((name: string, required?: boolean) => {
136+
getInput: jest.fn((name: string) => {
137137
if (name === 'serviceConnectionEndpoint') {
138138
return null;
139139
} else if (name === 'authToken') {

snykTask/src/__tests__/test-task-args.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ test('ensure that ignoreUnknownCA is false by default', () => {
122122
expect(args.ignoreUnknownCA).toBe(false);
123123
});
124124

125+
test('ensure that distributionChannel is stable by default', () => {
126+
const args = defaultTaskArgs();
127+
expect(args.getDistributionChannel()).toBe('stable');
128+
});
129+
125130
describe('TaskArgs.setMonitorWhen', () => {
126131
const args = defaultTaskArgs();
127132

snykTask/src/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
import * as fs from 'fs';
3636
import * as path from 'path';
3737
import { getSnykDownloadInfo, downloadExecutable } from './install';
38+
import { CliDistributionChannel } from './types';
3839

3940
class SnykError extends Error {
4041
constructor(message?: string) {
@@ -96,6 +97,8 @@ function parseInputArgs(): TaskArgs {
9697
taskArgs.failOnThreshold =
9798
tl.getInput('failOnThreshold', false) || Severity.LOW;
9899
taskArgs.ignoreUnknownCA = tl.getBoolInput('ignoreUnknownCA', false);
100+
taskArgs.distributionChannel = (tl.getInput('distributionChannel', false) ||
101+
'stable') as CliDistributionChannel;
99102

100103
if (isDebugMode()) {
101104
logAllTaskArgs(taskArgs);
@@ -403,6 +406,7 @@ async function run() {
403406
}
404407

405408
const taskArgs: TaskArgs = parseInputArgs();
409+
const distributionChannel = taskArgs.getDistributionChannel();
406410
const snykToken = getAuthToken();
407411
if (!snykToken) {
408412
const errorMsg =
@@ -411,9 +415,15 @@ async function run() {
411415
}
412416

413417
const platform: tl.Platform = tl.getPlatform();
414-
if (isDebugMode()) console.log(`platform: ${platform}`);
418+
if (isDebugMode()) {
419+
console.log(`platform: ${platform}`);
420+
console.log(`distributionChannel: ${distributionChannel}`);
421+
}
415422

416-
const snykToolDownloads = getSnykDownloadInfo(platform);
423+
const snykToolDownloads = getSnykDownloadInfo(
424+
platform,
425+
distributionChannel,
426+
);
417427
await downloadExecutable(agentTempDirectory, snykToolDownloads.snyk);
418428
await downloadExecutable(agentTempDirectory, snykToolDownloads.snykToHtml);
419429
const snykPath = path.resolve(

snykTask/src/install/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Platform } from 'azure-pipelines-task-lib/task';
1818
import * as fs from 'fs';
1919
import * as path from 'path';
2020
import * as https from 'https';
21+
import { CliDistributionChannel } from '../types';
2122

2223
export type Executable = {
2324
filename: string;
@@ -29,7 +30,10 @@ export type SnykDownloads = {
2930
snykToHtml: Executable;
3031
};
3132

32-
export function getSnykDownloadInfo(platform: Platform): SnykDownloads {
33+
export function getSnykDownloadInfo(
34+
platform: Platform,
35+
distributionChannel: CliDistributionChannel = 'stable',
36+
): SnykDownloads {
3337
const baseUrl = 'https://static.snyk.io';
3438

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

45+
const validDistributionChannels = ['stable', 'preview'];
46+
47+
if (!validDistributionChannels.includes(distributionChannel)) {
48+
distributionChannel = 'stable';
49+
}
50+
4151
return {
4252
snyk: {
4353
filename: `snyk-${filenameSuffixes[platform]}`,
44-
downloadUrl: `${baseUrl}/cli/latest/snyk-${filenameSuffixes[platform]}`,
54+
downloadUrl: `${baseUrl}/cli/${distributionChannel}/snyk-${filenameSuffixes[platform]}`,
4555
},
4656
snykToHtml: {
4757
filename: `snyk-to-html-${filenameSuffixes[platform]}`,

snykTask/src/task-args.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

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

4142
delayAfterReportGenerationSeconds: number = 0;
4243

44+
/**
45+
* The distribution channel to use for the Snyk CLI.
46+
* Defaults to 'stable', but can be set to 'preview' for early access to new features.
47+
*/
48+
distributionChannel: CliDistributionChannel | undefined = 'stable';
49+
4350
// the params here are the ones which are mandatory
4451
constructor(params: { failOnIssues: boolean }) {
4552
this.failOnIssues = params.failOnIssues;
@@ -111,6 +118,19 @@ class TaskArgs {
111118
return this.projectName;
112119
}
113120

121+
public getDistributionChannel(): CliDistributionChannel {
122+
if (!this.distributionChannel) {
123+
this.distributionChannel = 'stable';
124+
}
125+
126+
const validDistributionChannels = ['stable', 'preview'];
127+
if (!validDistributionChannels.includes(this.distributionChannel)) {
128+
this.distributionChannel = 'stable';
129+
}
130+
131+
return this.distributionChannel;
132+
}
133+
114134
// validate based on testTypeSeverityThreshold applicable thresholds
115135
public validate() {
116136
const taskTestType = this.testType || TestType.APPLICATION;

snykTask/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type CliDistributionChannel = 'stable' | 'preview';

snykTask/task.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@
162162
"defaultValue": "",
163163
"helpMarkDown": "Additional command-line args for Snyk CLI (advanced)",
164164
"groupName": "advanced"
165+
},
166+
{
167+
"name": "distributionChannel",
168+
"label": "CLI Distribution channel",
169+
"type": "pickList",
170+
"options": {
171+
"stable": "Stable",
172+
"preview": "Preview"
173+
},
174+
"required": false,
175+
"defaultValue": "stable",
176+
"helpMarkDown": "Defaults to 'stable', but can be set to 'preview' for early access to new features",
177+
"groupName": "advanced"
165178
}
166179
],
167180
"execution": {

0 commit comments

Comments
 (0)