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

feat: use cli ls extension [IDE-76] #551

Merged
merged 26 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
## [2.20.0]
- reduce hover verbosity to only title and description
- If $/snyk.hasAuthenticated transmits an API URL, this is saved in the settings.
- Added CLI release channel.
- Added option to change base URL to download CLI.
- Run Snyk language Server from the CLI extension.
- Change default CLI download path to be in extension directory.
- Delete sentry reporting.
- send analytics event "plugin installed" the first time the extension is started

Expand Down
20 changes: 16 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,17 +303,29 @@
"scope": "machine",
"markdownDescription": "Snyk will download, install and update dependencies for you. If this option is disabled, make sure valid paths to the dependencies are provided."
},
"snyk.advanced.cliPath": {
"snyk.advanced.cliBaseDownloadUrl": {
"order": 2,
"type": "string",
"scope": "machine",
"markdownDescription": "Sets path to Snyk CLI extension dependency."
"default": "https://downloads.snyk.io",
"markdownDescription": "Base URL to download the CLI."
},
"snyk.advanced.languageServerPath": {
"snyk.advanced.cliReleaseChannel": {
"order": 3,
"type": "string",
"default": "stable",
"enum": [
"stable",
"rc",
"preview"
],
"markdownDescription": "CLI release channel."
},
"snyk.advanced.cliPath": {
"order": 4,
"type": "string",
"scope": "machine",
"markdownDescription": "Sets path to Snyk Language Server (requires restart)."
"markdownDescription": "Sets path to Snyk CLI extension dependency."
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/snyk/base/modules/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IWorkspaceTrust } from '../../common/configuration/trustedFolders';
import { IContextService } from '../../common/services/contextService';
import { DownloadService } from '../../common/services/downloadService';
import { IOpenerService } from '../../common/services/openerService';
import { IViewManagerService } from '../../common/services/viewManagerService';
import { ExtensionContext } from '../../common/vscode/extensionContext';
Expand Down Expand Up @@ -28,5 +29,7 @@ export interface ISnykLib {
export interface IExtension extends IBaseSnykModule, ISnykLib {
context: ExtensionContext | undefined;
activate(context: VSCodeExtensionContext): void;
stopLanguageServer(): Promise<void>;
restartLanguageServer(): Promise<void>;
initDependencyDownload(): DownloadService;
}
74 changes: 57 additions & 17 deletions src/snyk/cli/cliExecutable.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,77 @@
import * as fs from 'fs/promises';
import path from 'path';
import { Platform } from '../common/platform';
import { Checksum } from './checksum';
import fs from 'fs/promises';
import { CliSupportedPlatform } from './supportedPlatforms';
import { Checksum } from './checksum';
import { Platform } from '../common/platform';

// TODO: This file is to be removed in VS Code + Language Server feature cleanup. We need to ensure all users have migrated to use CLI path that's set by the language server.
export class CliExecutable {
// If values updated, `.vscodeignore` to be changed.
public static filenameSuffixes: Record<CliSupportedPlatform, string> = {
linux: 'snyk-linux',
win32: 'snyk-win.exe',
darwin: 'snyk-macos',
linux_arm64: 'snyk-linux-arm64',

Check warning on line 10 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

Identifier 'linux_arm64' is not in camel case

Check warning on line 10 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (macos-latest)

Identifier 'linux_arm64' is not in camel case

Check warning on line 10 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (windows-latest)

Identifier 'linux_arm64' is not in camel case
linux_alpine: 'snyk-alpine',

Check warning on line 11 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

Identifier 'linux_alpine' is not in camel case

Check warning on line 11 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (macos-latest)

Identifier 'linux_alpine' is not in camel case

Check warning on line 11 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (windows-latest)

Identifier 'linux_alpine' is not in camel case
ShawkyZ marked this conversation as resolved.
Show resolved Hide resolved
linux_alpine_arm64: 'snyk-alpine-arm64',

Check warning on line 12 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

Identifier 'linux_alpine_arm64' is not in camel case

Check warning on line 12 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (macos-latest)

Identifier 'linux_alpine_arm64' is not in camel case

Check warning on line 12 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (windows-latest)

Identifier 'linux_alpine_arm64' is not in camel case
macos: 'snyk-macos',
macos_arm64: 'snyk-macos-arm64',

Check warning on line 14 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

Identifier 'macos_arm64' is not in camel case

Check warning on line 14 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (macos-latest)

Identifier 'macos_arm64' is not in camel case

Check warning on line 14 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (windows-latest)

Identifier 'macos_arm64' is not in camel case
windows: 'snyk-win.exe',
ShawkyZ marked this conversation as resolved.
Show resolved Hide resolved
windows_arm64: 'snyk-win.exe',

Check warning on line 16 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

Identifier 'windows_arm64' is not in camel case

Check warning on line 16 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (macos-latest)

Identifier 'windows_arm64' is not in camel case

Check warning on line 16 in src/snyk/cli/cliExecutable.ts

View workflow job for this annotation

GitHub Actions / Build and Test (windows-latest)

Identifier 'windows_arm64' is not in camel case
};

constructor(public readonly version: string, public readonly checksum: Checksum) {}

static getFilename(platform: CliSupportedPlatform): string {
return this.filenameSuffixes[platform];
}

static getPath(extensionDir: string, customPath?: string): string {
static async getPath(extensionDir: string, customPath?: string): Promise<string> {
if (customPath) {
return customPath;
}

const platform = Platform.getCurrent();
const fileName = CliExecutable.getFilename(platform as CliSupportedPlatform);
const platform = await this.getCurrentWithArch();
const fileName = this.getFileName(platform);
return path.join(extensionDir, fileName);
}

static exists(extensionDir: string, customPath?: string): Promise<boolean> {
static getFileName(platform: CliSupportedPlatform): string {
return this.filenameSuffixes[platform];
}

static async getCurrentWithArch(): Promise<CliSupportedPlatform> {
const osName = Platform.getCurrent().toString().toLowerCase();
const archSuffix = Platform.getArch().toLowerCase();
const platform = await this.getPlatformName(osName);

let cliName = platform;
if (archSuffix === 'arm64') {
cliName = `${platform}_${archSuffix}`;
}
return cliName as CliSupportedPlatform;
}

static async getPlatformName(osName: string): Promise<string> {
let platform = '';
if (osName === 'linux') {
if (await this.isAlpine()) {
platform = 'linux_alpine';
ShawkyZ marked this conversation as resolved.
Show resolved Hide resolved
} else {
platform = 'linux';
}
} else if (osName === 'darwin') {
platform = 'macos';
} else if (osName === 'win32') {
platform = 'windows';
}
if (!platform) {
throw new Error(`${osName} is unsupported.`);
}
return platform;
}

static async exists(extensionDir: string, customPath?: string): Promise<boolean> {
return fs
.access(await CliExecutable.getPath(extensionDir, customPath))
.then(() => true)
.catch(() => false);
}

static isAlpine(): Promise<boolean> {
return fs
.access(CliExecutable.getPath(extensionDir, customPath))
.access('/etc/alpine-release')
.then(() => true)
.catch(() => false);
}
Expand Down
84 changes: 84 additions & 0 deletions src/snyk/cli/staticCliApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import axios, { CancelTokenSource } from 'axios';
import { IConfiguration } from '../common/configuration/configuration';
import { PROTOCOL_VERSION } from '../common/constants/languageServer';
import { DownloadAxiosResponse } from '../common/download/downloader';
import { ILog } from '../common/logger/interfaces';
import { getAxiosConfig } from '../common/proxy';
import { IVSCodeWorkspace } from '../common/vscode/workspace';
import { CliExecutable } from './cliExecutable';
import { CliSupportedPlatform } from './supportedPlatforms';

export interface IStaticCliApi {
getLatestCliVersion(releaseChannel: string): Promise<string>;
downloadBinary(platform: CliSupportedPlatform): Promise<[Promise<DownloadAxiosResponse>, CancelTokenSource]>;
getSha256Checksum(version: string, platform: CliSupportedPlatform): Promise<string>;
}

export class StaticCliApi implements IStaticCliApi {
constructor(
private readonly workspace: IVSCodeWorkspace,
private readonly configuration: IConfiguration,
private readonly logger: ILog,
) {}

getLatestVersionDownloadUrl(releaseChannel: string): string {
const downloadUrl = `${this.configuration.getCliBaseDownloadUrl()}/cli/${releaseChannel}/ls-protocol-version-${PROTOCOL_VERSION}`;
return downloadUrl;
}

getDownloadUrl(version: string, platform: CliSupportedPlatform): string {
if (!version.startsWith('v')) {
version = `v${version}`;
}
const downloadUrl = `${this.configuration.getCliBaseDownloadUrl()}/cli/${version}/${this.getFileName(platform)}`;
return downloadUrl;
}

getSha256DownloadUrl(version: string, platform: CliSupportedPlatform): string {
const downloadUrl = `${this.getDownloadUrl(version, platform)}.sha256`;
return downloadUrl;
}

getFileName(platform: CliSupportedPlatform): string {
return CliExecutable.getFileName(platform);
}

async getLatestCliVersion(releaseChannel: string): Promise<string> {
let { data } = await axios.get<string>(
this.getLatestVersionDownloadUrl(releaseChannel),
await getAxiosConfig(this.workspace, this.configuration, this.logger),
);
data = data.replace('\n', '');
if (data == '') return Promise.reject(new Error('CLI Version not found'));
return data;
}

async downloadBinary(platform: CliSupportedPlatform): Promise<[Promise<DownloadAxiosResponse>, CancelTokenSource]> {
const axiosCancelToken = axios.CancelToken.source();
const latestCliVersion = await this.getLatestCliVersion(this.configuration.getCliReleaseChannel());

const downloadUrl = this.getDownloadUrl(latestCliVersion, platform);

const response = axios.get(downloadUrl, {
responseType: 'stream',
cancelToken: axiosCancelToken.token,
...(await getAxiosConfig(this.workspace, this.configuration, this.logger)),
});

return [response as Promise<DownloadAxiosResponse>, axiosCancelToken];
}

async getSha256Checksum(version: string, platform: CliSupportedPlatform): Promise<string> {
const fileName = this.getFileName(platform);
const { data } = await axios.get<string>(
`${this.getSha256DownloadUrl(version, platform)}`,
await getAxiosConfig(this.workspace, this.configuration, this.logger),
);

const checksum = data.replace(fileName, '').replace('\n', '').trim();

if (!checksum) return Promise.reject(new Error('Checksum not found'));

return checksum;
}
}
11 changes: 10 additions & 1 deletion src/snyk/cli/supportedPlatforms.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
const SupportedCliPlatformsList = ['linux', 'win32', 'darwin'] as const;
const SupportedCliPlatformsList = [
'linux',
'linux_arm64',
'linux_alpine',
'linux_alpine_arm64',
'windows',
'windows_arm64',
'macos',
'macos_arm64',
] as const;
export type CliSupportedPlatform = typeof SupportedCliPlatformsList[number];
Loading
Loading