Skip to content

Commit

Permalink
feat: use cli ls extension [IDE-76] (#551)
Browse files Browse the repository at this point in the history
* feat: use cli ls extension

---------

Co-authored-by: DariusZdroba <[email protected]>
  • Loading branch information
ShawkyZ and DariusZdroba authored Nov 11, 2024
1 parent 0d56250 commit 3a41a25
Show file tree
Hide file tree
Showing 31 changed files with 586 additions and 679 deletions.
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 / 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 / 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 / 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 / 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 / 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 / Build and Test (windows-latest)

Identifier 'linux_alpine' is not in camel case
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 / 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 / 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 / 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 / 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 / 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 / Build and Test (windows-latest)

Identifier 'macos_arm64' is not in camel case
windows: 'snyk-win.exe',
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 / 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 / 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 / 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';
} 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

0 comments on commit 3a41a25

Please sign in to comment.