Merge branch 'pr/57955'
This commit is contained in:
commit
5c5877d686
|
@ -14,6 +14,8 @@ import { parse as parseUrl } from 'url';
|
|||
import { createWriteStream } from 'fs';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { createGunzip } from 'zlib';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
|
||||
export type Agent = any;
|
||||
|
||||
|
@ -46,7 +48,7 @@ export interface IRequestContext {
|
|||
}
|
||||
|
||||
export interface IRequestFunction {
|
||||
(options: IRequestOptions): TPromise<IRequestContext>;
|
||||
(options: IRequestOptions, token: CancellationToken): TPromise<IRequestContext>;
|
||||
}
|
||||
|
||||
async function getNodeRequest(options: IRequestOptions): Promise<IRawRequestFunction> {
|
||||
|
@ -55,7 +57,7 @@ async function getNodeRequest(options: IRequestOptions): Promise<IRawRequestFunc
|
|||
return module.request;
|
||||
}
|
||||
|
||||
export function request(options: IRequestOptions): TPromise<IRequestContext> {
|
||||
export function request(options: IRequestOptions, token: CancellationToken): TPromise<IRequestContext> {
|
||||
let req: http.ClientRequest;
|
||||
|
||||
const rawRequestPromise = options.getRawRequest
|
||||
|
@ -88,7 +90,7 @@ export function request(options: IRequestOptions): TPromise<IRequestContext> {
|
|||
request(assign({}, options, {
|
||||
url: res.headers['location'],
|
||||
followRedirects: followRedirects - 1
|
||||
})).then(c, e);
|
||||
}), token).then(c, e);
|
||||
} else {
|
||||
let stream: Stream = res;
|
||||
|
||||
|
@ -116,7 +118,12 @@ export function request(options: IRequestOptions): TPromise<IRequestContext> {
|
|||
}
|
||||
|
||||
req.end();
|
||||
}, () => req && req.abort());
|
||||
|
||||
token.onCancellationRequested(() => {
|
||||
req.abort();
|
||||
e(canceled());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import product from 'vs/platform/node/product';
|
|||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
import { IRequestContext } from 'vs/base/node/request';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
interface PostResult {
|
||||
readonly blob_id: string;
|
||||
|
@ -86,7 +87,7 @@ async function postLogs(
|
|||
headers: {
|
||||
'Content-Type': 'application/zip'
|
||||
}
|
||||
});
|
||||
}, CancellationToken.None);
|
||||
} catch (e) {
|
||||
clearInterval(dotter);
|
||||
console.log(localize('postError', 'Error posting logs: {0}', e));
|
||||
|
|
|
@ -24,7 +24,6 @@ import { writeFileAndFlushSync } from 'vs/base/node/extfs';
|
|||
import { generateUuid, isUUID } from 'vs/base/common/uuid';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { wireCancellationToken } from 'vs/base/common/async';
|
||||
|
||||
interface IRawGalleryExtensionFile {
|
||||
assetType: string;
|
||||
|
@ -375,7 +374,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
query = query.withFilter(FilterType.ExtensionName, id);
|
||||
}
|
||||
|
||||
return this.queryGallery(query).then(({ galleryExtensions }) => {
|
||||
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => {
|
||||
if (galleryExtensions.length) {
|
||||
const galleryExtension = galleryExtensions[0];
|
||||
const versionAsset = version ? galleryExtension.versions.filter(v => v.version === version)[0] : galleryExtension.versions[0];
|
||||
|
@ -447,7 +446,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
query = query.withSortOrder(options.sortOrder);
|
||||
}
|
||||
|
||||
return this.queryGallery(query).then(({ galleryExtensions, total }) => {
|
||||
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions, total }) => {
|
||||
const extensions = galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, query, options.source));
|
||||
const pageSize = query.pageSize;
|
||||
const getPage = (pageIndex: number, ct: CancellationToken) => {
|
||||
|
@ -456,17 +455,15 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
}
|
||||
|
||||
const nextPageQuery = query.withPage(pageIndex + 1);
|
||||
const promise = this.queryGallery(nextPageQuery)
|
||||
return this.queryGallery(nextPageQuery, ct)
|
||||
.then(({ galleryExtensions }) => galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, nextPageQuery, options.source)));
|
||||
|
||||
return wireCancellationToken(ct, promise);
|
||||
};
|
||||
|
||||
return { firstPage: extensions, total, pageSize, getPage } as IPager<IGalleryExtension>;
|
||||
});
|
||||
}
|
||||
|
||||
private queryGallery(query: Query): TPromise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> {
|
||||
private queryGallery(query: Query, token: CancellationToken): TPromise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> {
|
||||
return this.commonHeadersPromise.then(commonHeaders => {
|
||||
const data = JSON.stringify(query.raw);
|
||||
const headers = assign({}, commonHeaders, {
|
||||
|
@ -481,7 +478,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
url: this.api('/extensionquery'),
|
||||
data,
|
||||
headers
|
||||
}).then(context => {
|
||||
}, token).then(context => {
|
||||
|
||||
if (context.res.statusCode >= 400 && context.res.statusCode < 500) {
|
||||
return { galleryExtensions: [], total: 0 };
|
||||
|
@ -511,7 +508,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
type: 'POST',
|
||||
url: this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`),
|
||||
headers
|
||||
}).then(null, () => null);
|
||||
}, CancellationToken.None).then(null, () => null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -584,7 +581,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
.withAssetTypes(AssetType.Manifest, AssetType.VSIX)
|
||||
.withFilter(FilterType.ExtensionId, extension.identifier.uuid);
|
||||
|
||||
return this.queryGallery(query)
|
||||
return this.queryGallery(query, CancellationToken.None)
|
||||
.then(({ galleryExtensions }) => {
|
||||
const [rawExtension] = galleryExtensions;
|
||||
|
||||
|
@ -619,7 +616,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
.withAssetTypes(AssetType.Icon, AssetType.License, AssetType.Details, AssetType.Manifest, AssetType.VSIX)
|
||||
.withFilter(FilterType.ExtensionName, ...extensionNames);
|
||||
|
||||
return this.queryGallery(query).then(result => {
|
||||
return this.queryGallery(query, CancellationToken.None).then(result => {
|
||||
const dependencies = [];
|
||||
const ids = [];
|
||||
|
||||
|
@ -658,7 +655,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
});
|
||||
}
|
||||
|
||||
private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}): TPromise<IRequestContext> {
|
||||
private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): TPromise<IRequestContext> {
|
||||
return this.commonHeadersPromise.then(commonHeaders => {
|
||||
const baseOptions = { type: 'GET' };
|
||||
const headers = assign({}, commonHeaders, options.headers || {});
|
||||
|
@ -668,7 +665,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
const fallbackUrl = asset.fallbackUri;
|
||||
const firstOptions = assign({}, options, { url });
|
||||
|
||||
return this.requestService.request(firstOptions)
|
||||
return this.requestService.request(firstOptions, token)
|
||||
.then(context => {
|
||||
if (context.res.statusCode === 200) {
|
||||
return TPromise.as(context);
|
||||
|
@ -700,7 +697,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
this.telemetryService.publicLog('galleryService:cdnFallback', { url, message });
|
||||
|
||||
const fallbackOptions = assign({}, options, { url: fallbackUrl });
|
||||
return this.requestService.request(fallbackOptions).then(null, err => {
|
||||
return this.requestService.request(fallbackOptions, token).then(null, err => {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
return TPromise.wrapError<IRequestContext>(err);
|
||||
}
|
||||
|
@ -783,7 +780,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
return this.requestService.request({ type: 'GET', url: this.extensionsControlUrl }).then(context => {
|
||||
return this.requestService.request({ type: 'GET', url: this.extensionsControlUrl }, CancellationToken.None).then(context => {
|
||||
if (context.res.statusCode !== 200) {
|
||||
return TPromise.wrapError(new Error('Could not get extensions report.'));
|
||||
}
|
||||
|
@ -831,4 +828,4 @@ export function resolveMarketplaceHeaders(environmentService: IEnvironmentServic
|
|||
'X-Market-User-Id': uuid
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,18 +8,20 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
|||
import { IRequestOptions, IRequestContext, IRequestFunction } from 'vs/base/node/request';
|
||||
import { Readable } from 'stream';
|
||||
import { RequestService as NodeRequestService } from 'vs/platform/request/node/requestService';
|
||||
import { CancellationToken } from 'vscode';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
|
||||
/**
|
||||
* This service exposes the `request` API, while using the global
|
||||
* or configured proxy settings.
|
||||
*/
|
||||
export class RequestService extends NodeRequestService {
|
||||
request(options: IRequestOptions): TPromise<IRequestContext> {
|
||||
return super.request(options, xhrRequest);
|
||||
request(options: IRequestOptions, token: CancellationToken): TPromise<IRequestContext> {
|
||||
return super.request(options, token, xhrRequest);
|
||||
}
|
||||
}
|
||||
|
||||
export const xhrRequest: IRequestFunction = (options: IRequestOptions): TPromise<IRequestContext> => {
|
||||
export const xhrRequest: IRequestFunction = (options: IRequestOptions, token: CancellationToken): TPromise<IRequestContext> => {
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
return new TPromise<IRequestContext>((resolve, reject) => {
|
||||
|
@ -68,11 +70,12 @@ export const xhrRequest: IRequestFunction = (options: IRequestOptions): TPromise
|
|||
|
||||
// TODO: remove any
|
||||
xhr.send(options.data as any);
|
||||
return null;
|
||||
|
||||
}, () => {
|
||||
// cancel
|
||||
xhr.abort();
|
||||
token.onCancellationRequested(() => {
|
||||
xhr.abort();
|
||||
reject(canceled());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { IRequestOptions, IRequestContext, request, IRawRequestFunction } from '
|
|||
import { RequestService as NodeRequestService } from 'vs/platform/request/node/requestService';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { net } from 'electron';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
function getRawRequest(options: IRequestOptions): IRawRequestFunction {
|
||||
return net.request as any as IRawRequestFunction;
|
||||
|
@ -16,7 +17,7 @@ function getRawRequest(options: IRequestOptions): IRawRequestFunction {
|
|||
|
||||
export class RequestService extends NodeRequestService {
|
||||
|
||||
request(options: IRequestOptions): TPromise<IRequestContext> {
|
||||
return super.request(options, options => request(assign({}, options || {}, { getRawRequest })));
|
||||
request(options: IRequestOptions, token: CancellationToken): TPromise<IRequestContext> {
|
||||
return super.request(options, token, options => request(assign({}, options || {}, { getRawRequest }), token));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,14 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
|||
import { IRequestOptions, IRequestContext } from 'vs/base/node/request';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export const IRequestService = createDecorator<IRequestService>('requestService2');
|
||||
|
||||
export interface IRequestService {
|
||||
_serviceBrand: any;
|
||||
|
||||
request(options: IRequestOptions): TPromise<IRequestContext>;
|
||||
request(options: IRequestOptions, token: CancellationToken): TPromise<IRequestContext>;
|
||||
}
|
||||
|
||||
export interface IHTTPConfiguration {
|
||||
|
@ -50,4 +51,4 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration)
|
|||
description: localize('proxyAuthorization', "The value to send as the 'Proxy-Authorization' header for every network request.")
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ import { getProxyAgent } from 'vs/base/node/proxy';
|
|||
import { IRequestService, IHTTPConfiguration } from 'vs/platform/request/node/request';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
/**
|
||||
* This service exposes the `request` API, while using the global
|
||||
|
@ -40,7 +41,7 @@ export class RequestService implements IRequestService {
|
|||
this.authorization = config.http && config.http.proxyAuthorization;
|
||||
}
|
||||
|
||||
request(options: IRequestOptions, requestFn: IRequestFunction = request): TPromise<IRequestContext> {
|
||||
request(options: IRequestOptions, token: CancellationToken, requestFn: IRequestFunction = request): TPromise<IRequestContext> {
|
||||
this.logService.trace('RequestService#request', options.url);
|
||||
|
||||
const { proxyUrl, strictSSL } = this;
|
||||
|
@ -54,7 +55,7 @@ export class RequestService implements IRequestService {
|
|||
options.headers = assign(options.headers || {}, { 'Proxy-Authorization': this.authorization });
|
||||
}
|
||||
|
||||
return requestFn(options);
|
||||
return requestFn(options, token);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import * as pfs from 'vs/base/node/pfs';
|
|||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
|
||||
interface ColorInfo {
|
||||
|
@ -41,7 +42,7 @@ export const experimental = []; // 'settings.modifiedItemForeground', 'editorUnn
|
|||
suite('Color Registry', function () {
|
||||
|
||||
test('all colors documented', async function () {
|
||||
const reqContext = await request({ url: 'https://raw.githubusercontent.com/Microsoft/vscode-docs/vnext/docs/getstarted/theme-color-reference.md' });
|
||||
const reqContext = await request({ url: 'https://raw.githubusercontent.com/Microsoft/vscode-docs/vnext/docs/getstarted/theme-color-reference.md' }, CancellationToken.None);
|
||||
const content = await asText(reqContext);
|
||||
|
||||
const expression = /\-\s*\`([\w\.]+)\`: (.*)/g;
|
||||
|
|
|
@ -15,6 +15,7 @@ import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } fr
|
|||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export function createUpdateURL(platform: string, quality: string): string {
|
||||
return `${product.updateUrl}/api/update/${platform}/${quality}/${product.commit}`;
|
||||
|
@ -161,7 +162,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
|||
if (!this.url) {
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
return this.requestService.request({ url: this.url }).then(context => {
|
||||
return this.requestService.request({ url: this.url }, CancellationToken.None).then(context => {
|
||||
// The update server replies with 204 (No Content) when no
|
||||
// update is available - that's all we want to know.
|
||||
if (context.res.statusCode === 204) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import { createUpdateURL, AbstractUpdateService } from 'vs/platform/update/elect
|
|||
import { asJson } from 'vs/base/node/request';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { shell } from 'electron';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export class LinuxUpdateService extends AbstractUpdateService {
|
||||
|
||||
|
@ -44,7 +45,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
|
|||
|
||||
this.setState(State.CheckingForUpdates(context));
|
||||
|
||||
this.requestService.request({ url: this.url })
|
||||
this.requestService.request({ url: this.url }, CancellationToken.None)
|
||||
.then<IUpdate>(asJson)
|
||||
.then(update => {
|
||||
if (!update || !update.url || !update.version || !update.productVersion) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import { checksum } from 'vs/base/node/crypto';
|
|||
import { tmpdir } from 'os';
|
||||
import { spawn } from 'child_process';
|
||||
import { shell } from 'electron';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
function pollUntil(fn: () => boolean, timeout = 1000): TPromise<void> {
|
||||
return new TPromise<void>(c => {
|
||||
|
@ -115,7 +116,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
|
||||
this.setState(State.CheckingForUpdates(context));
|
||||
|
||||
this.requestService.request({ url: this.url })
|
||||
this.requestService.request({ url: this.url }, CancellationToken.None)
|
||||
.then<IUpdate>(asJson)
|
||||
.then(update => {
|
||||
const updateType = getUpdateType();
|
||||
|
@ -150,7 +151,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
const hash = update.hash;
|
||||
const downloadPath = `${updatePackagePath}.tmp`;
|
||||
|
||||
return this.requestService.request({ url })
|
||||
return this.requestService.request({ url }, CancellationToken.None)
|
||||
.then(context => download(downloadPath, context))
|
||||
.then(hash ? () => checksum(downloadPath, update.hash) : () => null)
|
||||
.then(() => pfs.rename(downloadPath, updatePackagePath))
|
||||
|
|
|
@ -21,6 +21,7 @@ import { asJson } from 'vs/base/node/request';
|
|||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { WorkspaceStats } from 'vs/workbench/parts/stats/node/workspaceStats';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
interface IExperimentStorageState {
|
||||
enabled: boolean;
|
||||
|
@ -168,7 +169,7 @@ export class ExperimentService extends Disposable implements IExperimentService
|
|||
if (!product.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
return this.requestService.request({ type: 'GET', url: product.experimentsUrl }).then(context => {
|
||||
return this.requestService.request({ type: 'GET', url: product.experimentsUrl }, CancellationToken.None).then(context => {
|
||||
if (context.res.statusCode !== 200) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
@ -431,4 +432,4 @@ function safeParse(text: string, defaultObject: any) {
|
|||
catch (e) {
|
||||
return defaultObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import { assign } from 'vs/base/common/objects';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
const milliSecondsInADay = 1000 * 60 * 60 * 24;
|
||||
const choiceNever = localize('neverShowAgain', "Don't Show Again");
|
||||
|
@ -898,7 +899,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
|||
return null;
|
||||
}
|
||||
|
||||
return this.requestService.request({ type: 'GET', url: this._extensionsRecommendationsUrl }).then(context => {
|
||||
return this.requestService.request({ type: 'GET', url: this._extensionsRecommendationsUrl }, CancellationToken.None).then(context => {
|
||||
if (context.res.statusCode !== 200) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
|
|
@ -271,7 +271,7 @@ class RemoteSearchProvider implements ISearchProvider {
|
|||
'api-key': this.options.endpoint.key
|
||||
},
|
||||
timeout: 5000
|
||||
}).then(context => {
|
||||
}, CancellationToken.None).then(context => {
|
||||
if (context.res.statusCode >= 300) {
|
||||
throw new Error(`${details} returned status code: ${context.res.statusCode}`);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import { IWebviewEditorService } from 'vs/workbench/parts/webview/electron-brows
|
|||
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput';
|
||||
import { KeybindingParser } from 'vs/base/common/keybindingParser';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
function renderBody(
|
||||
body: string,
|
||||
|
@ -156,7 +157,7 @@ export class ReleaseNotesManager {
|
|||
};
|
||||
|
||||
if (!this._releaseNotesCache[version]) {
|
||||
this._releaseNotesCache[version] = this._requestService.request({ url })
|
||||
this._releaseNotesCache[version] = this._requestService.request({ url }, CancellationToken.None)
|
||||
.then(asText)
|
||||
.then(text => {
|
||||
if (!/^#\s/.test(text)) { // release notes always starts with `#` followed by whitespace
|
||||
|
|
Loading…
Reference in a new issue