Skip to content

Commit

Permalink
chore: update dependencies, improve progress reporting appearence
Browse files Browse the repository at this point in the history
  • Loading branch information
connor4312 committed May 24, 2024
1 parent 50afe68 commit 813ab3a
Show file tree
Hide file tree
Showing 12 changed files with 1,211 additions and 899 deletions.
26 changes: 0 additions & 26 deletions .eslintrc.js

This file was deleted.

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Allow installing unreleased builds using an `-unreleased` suffix, such as `insiders-unreleased`.
- Allow passing different data directories in `runVSCodeCommand`, using it for extension development.
- Improve the appearance progress reporting.

### 2.3.10 | 2024-05-13

Expand Down
4 changes: 4 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommended);
31 changes: 18 additions & 13 deletions lib/download.test.ts → lib/download.test.mts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { spawnSync } from 'child_process';
import { existsSync, promises as fs } from 'fs';
import { tmpdir } from 'os';
Expand All @@ -8,9 +13,9 @@ import {
fetchInsiderVersions,
fetchStableVersions,
fetchTargetInferredVersion,
} from './download';
import { SilentReporter } from './progress';
import { resolveCliPathFromVSCodeExecutablePath, systemDefaultPlatform } from './util';
} from './download.js';
import { SilentReporter } from './progress.js';
import { resolveCliPathFromVSCodeExecutablePath, systemDefaultPlatform } from './util.js';

const platforms = [
'darwin',
Expand Down Expand Up @@ -70,7 +75,7 @@ describe('sane downloads', () => {
describe('fetchTargetInferredVersion', () => {
let stable: string[];
let insiders: string[];
let extensionsDevelopmentPath = join(tmpdir(), 'vscode-test-tmp-workspace');
const extensionsDevelopmentPath = join(tmpdir(), 'vscode-test-tmp-workspace');

beforeAll(async () => {
[stable, insiders] = await Promise.all([fetchStableVersions(true, 5000), fetchInsiderVersions(true, 5000)]);
Expand All @@ -80,7 +85,7 @@ describe('fetchTargetInferredVersion', () => {
await fs.rm(extensionsDevelopmentPath, { recursive: true, force: true });
});

const writeJSON = async (path: string, contents: object) => {
const writeJSON = async (path: string, contents: unknown) => {
const target = join(extensionsDevelopmentPath, path);
await fs.mkdir(dirname(target), { recursive: true });
await fs.writeFile(target, JSON.stringify(contents));
Expand All @@ -96,49 +101,49 @@ describe('fetchTargetInferredVersion', () => {

test('matches stable if no workspace', async () => {
const version = await doFetch();
expect(version).to.equal(stable[0]);
expect(version.id).to.equal(stable[0]);
});

test('matches stable by default', async () => {
await writeJSON('package.json', {});
const version = await doFetch();
expect(version).to.equal(stable[0]);
expect(version.id).to.equal(stable[0]);
});

test('matches if stable is defined', async () => {
await writeJSON('package.json', { engines: { vscode: '^1.50.0' } });
const version = await doFetch();
expect(version).to.equal(stable[0]);
expect(version.id).to.equal(stable[0]);
});

test('matches best', async () => {
await writeJSON('package.json', { engines: { vscode: '<=1.60.5' } });
const version = await doFetch();
expect(version).to.equal('1.60.2');
expect(version.id).to.equal('1.60.2');
});

test('matches multiple workspaces', async () => {
await writeJSON('a/package.json', { engines: { vscode: '<=1.60.5' } });
await writeJSON('b/package.json', { engines: { vscode: '<=1.55.5' } });
const version = await doFetch(['a', 'b']);
expect(version).to.equal('1.55.2');
expect(version.id).to.equal('1.55.2');
});

test('matches insiders to better stable if there is one', async () => {
await writeJSON('package.json', { engines: { vscode: '^1.60.0-insider' } });
const version = await doFetch();
expect(version).to.equal(stable[0]);
expect(version.id).to.equal(stable[0]);
});

test('matches current insiders', async () => {
await writeJSON('package.json', { engines: { vscode: `^${insiders[0]}` } });
const version = await doFetch();
expect(version).to.equal(insiders[0]);
expect(version.id).to.equal(insiders[0]);
});

test('matches insiders to exact', async () => {
await writeJSON('package.json', { engines: { vscode: '1.60.0-insider' } });
const version = await doFetch();
expect(version).to.equal('1.60.0-insider');
expect(version.id).to.equal('1.60.0-insider');
});
});
8 changes: 4 additions & 4 deletions lib/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as path from 'path';
import * as semver from 'semver';
import { pipeline } from 'stream';
import { promisify } from 'util';
import { ConsoleReporter, ProgressReporter, ProgressReportStage } from './progress';
import { makeConsoleReporter, ProgressReporter, ProgressReportStage } from './progress.js';
import * as request from './request';
import {
downloadDirToExecutablePath,
Expand Down Expand Up @@ -359,7 +359,7 @@ async function unzipVSCode(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(process as any).noAsar = true;

const content = await JSZip.loadAsync(buffer);
const content = await JSZip.default.loadAsync(buffer);
// extract file with jszip
for (const filename of Object.keys(content.files)) {
const file = content.files[filename];
Expand Down Expand Up @@ -429,7 +429,7 @@ export async function download(options: Partial<DownloadOptions> = {}): Promise<
const {
platform = systemDefaultPlatform,
cachePath = defaultCachePath,
reporter = new ConsoleReporter(process.stdout.isTTY),
reporter = await makeConsoleReporter(),
timeout = 15_000,
} = options;

Expand Down Expand Up @@ -459,7 +459,7 @@ export async function download(options: Partial<DownloadOptions> = {}): Promise<
throw new Error('Windows 32-bit is no longer supported from v1.85 onwards');
}

reporter.report({ stage: ProgressReportStage.ResolvedVersion, version: version.id });
reporter.report({ stage: ProgressReportStage.ResolvedVersion, version: version.toString() });

const downloadedPath = path.resolve(cachePath, makeDownloadDirName(platform, version));
if (fs.existsSync(path.join(downloadedPath, COMPLETE_FILE_NAME))) {
Expand Down
2 changes: 1 addition & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ export {
VSCodeCommandError,
RunVSCodeCommandOptions,
} from './util';
export * from './progress';
export * from './progress.js';
136 changes: 66 additions & 70 deletions lib/progress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,77 +63,73 @@ export class SilentReporter implements ProgressReporter {
}

/** Default progress reporter that logs VS Code download progress to console */
export class ConsoleReporter implements ProgressReporter {
private version?: string;
export const makeConsoleReporter = async (): Promise<ProgressReporter> => {
// needs to be async targeting Node 16 because ora is an es module that cannot be required
const { default: ora } = await import('ora');
let version: undefined | string;

private downloadReport?: {
timeout: NodeJS.Timeout;
report: { stage: ProgressReportStage.Downloading; totalBytes: number; bytesSoFar: number };
};

constructor(private readonly showDownloadProgress: boolean) {}

public report(report: ProgressReport): void {
switch (report.stage) {
case ProgressReportStage.ResolvedVersion:
this.version = report.version;
break;
case ProgressReportStage.ReplacingOldInsiders:
console.log(`Removing outdated Insiders at ${report.downloadedPath} and re-downloading.`);
console.log(`Old: ${report.oldHash} | ${report.oldDate.toISOString()}`);
console.log(`New: ${report.newHash} | ${report.newDate.toISOString()}`);
break;
case ProgressReportStage.FoundMatchingInstall:
console.log(`Found existing install in ${report.downloadedPath}. Skipping download`);
break;
case ProgressReportStage.ResolvingCDNLocation:
console.log(`Downloading VS Code ${this.version} from ${report.url}`);
break;
case ProgressReportStage.Downloading:
if (!this.showDownloadProgress && report.bytesSoFar === 0) {
console.log(`Downloading VS Code (${report.totalBytes}B)`);
} else if (!this.downloadReport) {
this.downloadReport = { timeout: setTimeout(() => this.reportDownload(), 100), report };
} else {
this.downloadReport.report = report;
}
break;
case ProgressReportStage.Retrying:
this.flushDownloadReport();
console.log(
`Error downloading, retrying (attempt ${report.attempt} of ${report.totalAttempts}): ${report.error.message}`
);
break;
case ProgressReportStage.NewInstallComplete:
this.flushDownloadReport();
console.log(`Downloaded VS Code into ${report.downloadedPath}`);
break;
}
let spinner: undefined | ReturnType<typeof ora> = ora('Resolving version...').start();
function toMB(bytes: number) {
return (bytes / 1024 / 1024).toFixed(2);
}

public error(err: unknown) {
console.error(err);
}

private flushDownloadReport() {
if (this.showDownloadProgress) {
this.reportDownload();
console.log('');
}
}
return {
error(err: unknown): void {
if (spinner) {
spinner?.fail(`Error: ${err}`);
spinner = undefined;
} else {
console.error(err);
}
},

private reportDownload() {
if (!this.downloadReport) {
return;
}

const { totalBytes, bytesSoFar } = this.downloadReport.report;
this.downloadReport = undefined;

const percent = Math.max(0, Math.min(1, bytesSoFar / totalBytes));
const progressBarSize = 30;
const barTicks = Math.floor(percent * progressBarSize);
const progressBar = '='.repeat(barTicks) + '-'.repeat(progressBarSize - barTicks);
process.stdout.write(`\x1b[G\x1b[0KDownloading VS Code [${progressBar}] ${(percent * 100).toFixed()}%`);
}
}
report(report: ProgressReport): void {
switch (report.stage) {
case ProgressReportStage.ResolvedVersion:
version = report.version;
spinner?.succeed(`Validated version: ${version}`);
spinner = undefined;
break;
case ProgressReportStage.ReplacingOldInsiders:
spinner?.succeed();
spinner = ora(
`Updating Insiders ${report.oldHash} (${report.oldDate.toISOString()}) -> ${report.newHash}`
).start();
break;
case ProgressReportStage.FoundMatchingInstall:
spinner?.succeed();
spinner = undefined;
ora(`Found existing install in ${report.downloadedPath}`).succeed();
break;
case ProgressReportStage.ResolvingCDNLocation:
spinner?.succeed();
spinner = ora(`Found at ${report.url}`).start();
break;
case ProgressReportStage.Downloading:
if (report.bytesSoFar === 0) {
spinner?.succeed();
spinner = ora(`Downloading (${toMB(report.totalBytes)} MB)`).start();
} else if (spinner) {
if (report.bytesSoFar === report.totalBytes) {
spinner.text = 'Extracting...';
} else {
const percent = Math.max(0, Math.min(1, report.bytesSoFar / report.totalBytes));
const size = `${toMB(report.bytesSoFar)}/${toMB(report.totalBytes)}MB`;
spinner.text = `Downloading VS Code: ${size} (${(percent * 100).toFixed()}%)`;
}
}
break;
case ProgressReportStage.Retrying:
spinner?.fail(
`Error downloading, retrying (attempt ${report.attempt} of ${report.totalAttempts}): ${report.error.message}`
);
spinner = undefined;
break;
case ProgressReportStage.NewInstallComplete:
spinner?.succeed(`Downloaded VS Code into ${report.downloadedPath}`);
spinner = undefined;
break;
}
},
};
};
14 changes: 7 additions & 7 deletions lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import { ChildProcess, SpawnOptions, spawn } from 'child_process';
import { createHash } from 'crypto';
import { readFileSync } from 'fs';
import * as createHttpProxyAgent from 'http-proxy-agent';
import { HttpProxyAgent } from 'http-proxy-agent';
import * as https from 'https';
import * as createHttpsProxyAgent from 'https-proxy-agent';
import { HttpsProxyAgent } from 'https-proxy-agent';
import * as path from 'path';
import { URL } from 'url';
import { DownloadOptions, DownloadPlatform, defaultCachePath, downloadAndUnzipVSCode } from './download';
Expand Down Expand Up @@ -76,15 +76,15 @@ export function getVSCodeDownloadUrl(version: Version, platform: string) {
}
}

let PROXY_AGENT: createHttpProxyAgent.HttpProxyAgent | undefined = undefined;
let HTTPS_PROXY_AGENT: createHttpsProxyAgent.HttpsProxyAgent | undefined = undefined;
let PROXY_AGENT: HttpProxyAgent<string> | undefined = undefined;
let HTTPS_PROXY_AGENT: HttpsProxyAgent<string> | undefined = undefined;

if (process.env.npm_config_proxy) {
PROXY_AGENT = createHttpProxyAgent(process.env.npm_config_proxy);
HTTPS_PROXY_AGENT = createHttpsProxyAgent(process.env.npm_config_proxy);
PROXY_AGENT = new HttpProxyAgent(process.env.npm_config_proxy);
HTTPS_PROXY_AGENT = new HttpsProxyAgent(process.env.npm_config_proxy);
}
if (process.env.npm_config_https_proxy) {
HTTPS_PROXY_AGENT = createHttpsProxyAgent(process.env.npm_config_https_proxy);
HTTPS_PROXY_AGENT = new HttpsProxyAgent(process.env.npm_config_https_proxy);
}

export function urlToOptions(url: string): https.RequestOptions {
Expand Down
Loading

0 comments on commit 813ab3a

Please sign in to comment.