Skip to content

Commit 207743e

Browse files
author
Dave Bartolomeo
committed
Merge remote-tracking branch 'upstream/master' into dbartol/QLTest
2 parents de2a6cc + 2c9c210 commit 207743e

11 files changed

Lines changed: 330 additions & 73 deletions

File tree

.github/workflows/main.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,25 @@ jobs:
5454
npm run build-ci
5555
shell: bash
5656

57-
- name: Run unit tests
57+
- name: Install CodeQL
58+
run: |
59+
mkdir codeql-home
60+
curl -L --silent https://github.com/github/codeql-cli-binaries/releases/latest/download/codeql.zip -o codeql-home/codeql.zip
61+
unzip -q -o codeql-home/codeql.zip -d codeql-home
62+
rm codeql-home/codeql.zip
63+
shell: bash
64+
65+
- name: Run unit tests (Linux)
66+
if: matrix.os == 'ubuntu-latest'
67+
run: |
68+
cd extensions/ql-vscode
69+
CODEQL_PATH=$GITHUB_WORKSPACE/codeql-home/codeql/codeql npm run test
70+
71+
- name: Run unit tests (Windows)
72+
if: matrix.os == 'windows-latest'
5873
run: |
5974
cd extensions/ql-vscode
75+
$env:CODEQL_PATH=$(Join-Path $env:GITHUB_WORKSPACE -ChildPath 'codeql-home/codeql/codeql.cmd')
6076
npm run test
6177
6278
- name: Run integration tests (Linux)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The extension is released. You can download it from the [Visual Studio Marketpla
66

77
To see what has changed in the last few versions of the extension, see the [Changelog](https://github.com/github/vscode-codeql/blob/master/extensions/ql-vscode/CHANGELOG.md).
88

9-
![CI status badge](https://github.com/github/vscode-codeql/workflows/Build%20Extension/badge.svg)
9+
[![CI status badge](https://github.com/github/vscode-codeql/workflows/Build%20Extension/badge.svg)](https://github.com/github/vscode-codeql/actions?query=workflow%3A%22Build+Extension%22+branch%3Amaster)
1010

1111
## Features
1212

extensions/ql-vscode/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# CodeQL for Visual Studio Code: Changelog
22

3+
## 1.0.3
4+
5+
- Reduce the frequency of CodeQL CLI update checks to help avoid hitting GitHub API limits of 60 requests per
6+
hour for unauthenticated IPs.
7+
- Fix sorting of result sets with names containing special characters.
8+
39
## 1.0.2 - 13 December 2019
410

511
- Fix rendering of negative numbers in results.

extensions/ql-vscode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "CodeQL for Visual Studio Code",
55
"author": "GitHub",
66
"private": true,
7-
"version": "1.0.2",
7+
"version": "1.0.3",
88
"publisher": "GitHub",
99
"license": "MIT",
1010
"icon": "media/VS-marketplace-CodeQL-icon.png",

extensions/ql-vscode/src/distribution.ts

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as unzipper from "unzipper";
66
import * as url from "url";
77
import { ExtensionContext, Event } from "vscode";
88
import { DistributionConfig } from "./config";
9-
import { ProgressUpdate, showAndLogErrorMessage } from "./helpers";
9+
import { InvocationRateLimiter, InvocationRateLimiterResultKind, ProgressUpdate, showAndLogErrorMessage } from "./helpers";
1010
import { logger } from "./logging";
1111
import { getCodeQlCliVersion, tryParseVersionString, Version } from "./cli-version";
1212

@@ -55,6 +55,11 @@ export class DistributionManager implements DistributionProvider {
5555
this._config = config;
5656
this._extensionSpecificDistributionManager = new ExtensionSpecificDistributionManager(extensionContext, config, versionConstraint);
5757
this._onDidChangeDistribution = config.onDidChangeDistributionConfiguration;
58+
this._updateCheckRateLimiter = new InvocationRateLimiter(
59+
extensionContext,
60+
"extensionSpecificDistributionUpdateCheck",
61+
() => this._extensionSpecificDistributionManager.checkForUpdatesToDistribution()
62+
);
5863
this._versionConstraint = versionConstraint;
5964
}
6065

@@ -128,14 +133,21 @@ export class DistributionManager implements DistributionProvider {
128133
*
129134
* Returns a failed promise if an unexpected error occurs during installation.
130135
*/
131-
public async checkForUpdatesToExtensionManagedDistribution(): Promise<DistributionUpdateCheckResult> {
136+
public async checkForUpdatesToExtensionManagedDistribution(
137+
minSecondsSinceLastUpdateCheck: number): Promise<DistributionUpdateCheckResult> {
132138
const codeQlPath = await this.getCodeQlPathWithoutVersionCheck();
133139
const extensionManagedCodeQlPath = await this._extensionSpecificDistributionManager.getCodeQlPathWithoutVersionCheck();
134140
if (codeQlPath !== undefined && codeQlPath !== extensionManagedCodeQlPath) {
135141
// A distribution is present but it isn't managed by the extension.
136-
return createInvalidDistributionLocationResult();
142+
return createInvalidLocationResult();
143+
}
144+
const updateCheckResult = await this._updateCheckRateLimiter.invokeFunctionIfIntervalElapsed(minSecondsSinceLastUpdateCheck);
145+
switch (updateCheckResult.kind) {
146+
case InvocationRateLimiterResultKind.Invoked:
147+
return updateCheckResult.result;
148+
case InvocationRateLimiterResultKind.RateLimited:
149+
return createAlreadyCheckedRecentlyResult();
137150
}
138-
return this._extensionSpecificDistributionManager.checkForUpdatesToDistribution();
139151
}
140152

141153
/**
@@ -154,6 +166,7 @@ export class DistributionManager implements DistributionProvider {
154166

155167
private readonly _config: DistributionConfig;
156168
private readonly _extensionSpecificDistributionManager: ExtensionSpecificDistributionManager;
169+
private readonly _updateCheckRateLimiter: InvocationRateLimiter<DistributionUpdateCheckResult>;
157170
private readonly _onDidChangeDistribution: Event<void> | undefined;
158171
private readonly _versionConstraint: VersionConstraint;
159172
}
@@ -196,7 +209,7 @@ class ExtensionSpecificDistributionManager {
196209
const latestRelease = await this.getLatestRelease();
197210

198211
if (extensionSpecificRelease !== undefined && codeQlPath !== undefined && latestRelease.id === extensionSpecificRelease.id) {
199-
return createDistributionAlreadyUpToDateResult();
212+
return createAlreadyUpToDateResult();
200213
}
201214
return createUpdateAvailableResult(latestRelease);
202215
}
@@ -234,7 +247,7 @@ class ExtensionSpecificDistributionManager {
234247

235248
if (progressCallback && contentLength !== null) {
236249
const totalNumBytes = parseInt(contentLength, 10);
237-
const bytesToDisplayMB = (numBytes: number) => `${(numBytes/(1024*1024)).toFixed(1)} MB`;
250+
const bytesToDisplayMB = (numBytes: number) => `${(numBytes / (1024 * 1024)).toFixed(1)} MB`;
238251
const updateProgress = () => {
239252
progressCallback({
240253
step: numBytesDownloaded,
@@ -258,7 +271,7 @@ class ExtensionSpecificDistributionManager {
258271
.on("error", reject)
259272
);
260273

261-
this.bumpDistributionFolderIndex();
274+
await this.bumpDistributionFolderIndex();
262275

263276
logger.log(`Extracting CodeQL CLI to ${this.getDistributionStoragePath()}`);
264277
await extractZipArchive(archivePath, this.getDistributionStoragePath());
@@ -293,10 +306,10 @@ class ExtensionSpecificDistributionManager {
293306
return new ReleasesApiConsumer(ownerName, repositoryName, this._config.personalAccessToken);
294307
}
295308

296-
private bumpDistributionFolderIndex(): void {
309+
private async bumpDistributionFolderIndex(): Promise<void> {
297310
const index = this._extensionContext.globalState.get(
298311
ExtensionSpecificDistributionManager._currentDistributionFolderIndexStateKey, 0);
299-
this._extensionContext.globalState.update(
312+
await this._extensionContext.globalState.update(
300313
ExtensionSpecificDistributionManager._currentDistributionFolderIndexStateKey, index + 1);
301314
}
302315

@@ -317,8 +330,8 @@ class ExtensionSpecificDistributionManager {
317330
return this._extensionContext.globalState.get(ExtensionSpecificDistributionManager._installedReleaseStateKey);
318331
}
319332

320-
private storeInstalledRelease(release: Release | undefined): void {
321-
this._extensionContext.globalState.update(ExtensionSpecificDistributionManager._installedReleaseStateKey, release);
333+
private async storeInstalledRelease(release: Release | undefined): Promise<void> {
334+
await this._extensionContext.globalState.update(ExtensionSpecificDistributionManager._installedReleaseStateKey, release);
322335
}
323336

324337
private readonly _config: DistributionConfig;
@@ -532,39 +545,50 @@ interface NoDistributionResult {
532545
}
533546

534547
export enum DistributionUpdateCheckResultKind {
548+
AlreadyCheckedRecentlyResult,
535549
AlreadyUpToDate,
536-
InvalidDistributionLocation,
550+
InvalidLocation,
537551
UpdateAvailable
538552
}
539553

540-
type DistributionUpdateCheckResult = DistributionAlreadyUpToDateResult | InvalidDistributionLocationResult |
554+
type DistributionUpdateCheckResult = AlreadyCheckedRecentlyResult | AlreadyUpToDateResult | InvalidLocationResult |
541555
UpdateAvailableResult;
542556

543-
export interface DistributionAlreadyUpToDateResult {
557+
export interface AlreadyCheckedRecentlyResult {
558+
kind: DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult
559+
}
560+
561+
export interface AlreadyUpToDateResult {
544562
kind: DistributionUpdateCheckResultKind.AlreadyUpToDate;
545563
}
546564

547565
/**
548566
* The distribution could not be installed or updated because it is not managed by the extension.
549567
*/
550-
export interface InvalidDistributionLocationResult {
551-
kind: DistributionUpdateCheckResultKind.InvalidDistributionLocation;
568+
export interface InvalidLocationResult {
569+
kind: DistributionUpdateCheckResultKind.InvalidLocation;
552570
}
553571

554572
export interface UpdateAvailableResult {
555573
kind: DistributionUpdateCheckResultKind.UpdateAvailable;
556574
updatedRelease: Release;
557575
}
558576

559-
function createDistributionAlreadyUpToDateResult(): DistributionAlreadyUpToDateResult {
577+
function createAlreadyCheckedRecentlyResult(): AlreadyCheckedRecentlyResult {
578+
return {
579+
kind: DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult
580+
};
581+
}
582+
583+
function createAlreadyUpToDateResult(): AlreadyUpToDateResult {
560584
return {
561585
kind: DistributionUpdateCheckResultKind.AlreadyUpToDate
562586
};
563587
}
564588

565-
function createInvalidDistributionLocationResult(): InvalidDistributionLocationResult {
589+
function createInvalidLocationResult(): InvalidLocationResult {
566590
return {
567-
kind: DistributionUpdateCheckResultKind.InvalidDistributionLocation
591+
kind: DistributionUpdateCheckResultKind.InvalidLocation
568592
};
569593
}
570594

extensions/ql-vscode/src/extension.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import * as archiveFilesystemProvider from './archive-filesystem-provider';
44
import { DistributionConfigListener, QueryServerConfigListener, QueryHistoryConfigListener } from './config';
55
import { DatabaseManager } from './databases';
66
import { DatabaseUI } from './databases-ui';
7-
import { DistributionUpdateCheckResultKind, DistributionManager, FindDistributionResult, FindDistributionResultKind, GithubApiError,
8-
DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT, GithubRateLimitedError } from './distribution';
7+
import {
8+
DistributionUpdateCheckResultKind, DistributionManager, FindDistributionResult, FindDistributionResultKind, GithubApiError,
9+
DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT, GithubRateLimitedError
10+
} from './distribution';
911
import * as helpers from './helpers';
1012
import { spawnIdeServer } from './ide-server';
1113
import { InterfaceManager, WebviewReveal } from './interface';
@@ -86,29 +88,32 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
8688
helpers.showAndLogErrorMessage(`Can't execute ${command}: waiting to finish loading CodeQL CLI.`);
8789
});
8890

89-
interface ReportingConfig {
91+
interface DistributionUpdateConfig {
92+
isUserInitiated: boolean;
9093
shouldDisplayMessageWhenNoUpdates: boolean;
91-
shouldErrorIfUpdateFails: boolean;
9294
}
9395

94-
async function installOrUpdateDistributionWithProgressTitle(progressTitle: string, reportingConfig: ReportingConfig): Promise<void> {
95-
const result = await distributionManager.checkForUpdatesToExtensionManagedDistribution();
96+
async function installOrUpdateDistributionWithProgressTitle(progressTitle: string, config: DistributionUpdateConfig): Promise<void> {
97+
const minSecondsSinceLastUpdateCheck = config.isUserInitiated ? 0 : 86400;
98+
const noUpdatesLoggingFunc = config.shouldDisplayMessageWhenNoUpdates ?
99+
helpers.showAndLogInformationMessage : async (message: string) => logger.log(message);
100+
const result = await distributionManager.checkForUpdatesToExtensionManagedDistribution(minSecondsSinceLastUpdateCheck);
96101
switch (result.kind) {
102+
case DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult:
103+
logger.log("Didn't perform CodeQL CLI update check since a check was already performed within the previous " +
104+
`${minSecondsSinceLastUpdateCheck} seconds.`);
105+
break;
97106
case DistributionUpdateCheckResultKind.AlreadyUpToDate:
98-
if (reportingConfig.shouldDisplayMessageWhenNoUpdates) {
99-
helpers.showAndLogInformationMessage("CodeQL CLI already up to date.");
100-
}
107+
await noUpdatesLoggingFunc("CodeQL CLI already up to date.");
101108
break;
102-
case DistributionUpdateCheckResultKind.InvalidDistributionLocation:
103-
if (reportingConfig.shouldDisplayMessageWhenNoUpdates) {
104-
helpers.showAndLogErrorMessage("CodeQL CLI is installed externally so could not be updated.");
105-
}
109+
case DistributionUpdateCheckResultKind.InvalidLocation:
110+
await noUpdatesLoggingFunc("CodeQL CLI is installed externally so could not be updated.");
106111
break;
107112
case DistributionUpdateCheckResultKind.UpdateAvailable:
108113
if (beganMainExtensionActivation) {
109114
const updateAvailableMessage = `Version "${result.updatedRelease.name}" of the CodeQL CLI is now available. ` +
110115
"The update will be installed after Visual Studio Code restarts. Restart now to upgrade?";
111-
ctx.globalState.update(shouldUpdateOnNextActivationKey, true);
116+
await ctx.globalState.update(shouldUpdateOnNextActivationKey, true);
112117
if (await helpers.showInformationMessageWithAction(updateAvailableMessage, "Restart and Upgrade")) {
113118
await commands.executeCommand("workbench.action.reloadWindow");
114119
}
@@ -121,7 +126,7 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
121126
await helpers.withProgress(progressOptions, progress =>
122127
distributionManager.installExtensionManagedDistributionRelease(result.updatedRelease, progress));
123128

124-
ctx.globalState.update(shouldUpdateOnNextActivationKey, false);
129+
await ctx.globalState.update(shouldUpdateOnNextActivationKey, false);
125130
helpers.showAndLogInformationMessage(`CodeQL CLI updated to version "${result.updatedRelease.name}".`);
126131
}
127132
break;
@@ -130,7 +135,7 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
130135
}
131136
}
132137

133-
async function installOrUpdateDistribution(reportingConfig: ReportingConfig): Promise<void> {
138+
async function installOrUpdateDistribution(config: DistributionUpdateConfig): Promise<void> {
134139
if (isInstallingOrUpdatingDistribution) {
135140
throw new Error("Already installing or updating CodeQL CLI");
136141
}
@@ -140,11 +145,11 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
140145
const messageText = willUpdateCodeQl ? "Updating CodeQL CLI" :
141146
codeQlInstalled ? "Checking for updates to CodeQL CLI" : "Installing CodeQL CLI";
142147
try {
143-
await installOrUpdateDistributionWithProgressTitle(messageText, reportingConfig);
148+
await installOrUpdateDistributionWithProgressTitle(messageText, config);
144149
} catch (e) {
145150
// Don't rethrow the exception, because if the config is changed, we want to be able to retry installing
146151
// or updating the distribution.
147-
const alertFunction = (codeQlInstalled && !reportingConfig.shouldErrorIfUpdateFails) ?
152+
const alertFunction = (codeQlInstalled && !config.isUserInitiated) ?
148153
helpers.showAndLogWarningMessage : helpers.showAndLogErrorMessage;
149154
const taskDescription = (willUpdateCodeQl ? "update" :
150155
codeQlInstalled ? "check for updates to" : "install") + " CodeQL CLI";
@@ -183,8 +188,8 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
183188
return result;
184189
}
185190

186-
async function installOrUpdateThenTryActivate(reportingConfig: ReportingConfig): Promise<void> {
187-
await installOrUpdateDistribution(reportingConfig);
191+
async function installOrUpdateThenTryActivate(config: DistributionUpdateConfig): Promise<void> {
192+
await installOrUpdateDistribution(config);
188193

189194
// Display the warnings even if the extension has already activated.
190195
const distributionResult = await getDistributionDisplayingDistributionWarnings();
@@ -197,26 +202,26 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
197202
const chosenAction = await helpers.showAndLogErrorMessage(`Can't execute ${command}: missing CodeQL CLI.`, installActionName);
198203
if (chosenAction === installActionName) {
199204
installOrUpdateThenTryActivate({
200-
shouldDisplayMessageWhenNoUpdates: false,
201-
shouldErrorIfUpdateFails: true
205+
isUserInitiated: true,
206+
shouldDisplayMessageWhenNoUpdates: false
202207
});
203208
}
204209
});
205210
}
206211
}
207212

208213
ctx.subscriptions.push(distributionConfigListener.onDidChangeDistributionConfiguration(() => installOrUpdateThenTryActivate({
209-
shouldDisplayMessageWhenNoUpdates: false,
210-
shouldErrorIfUpdateFails: true
214+
isUserInitiated: true,
215+
shouldDisplayMessageWhenNoUpdates: false
211216
})));
212217
ctx.subscriptions.push(commands.registerCommand(checkForUpdatesCommand, () => installOrUpdateThenTryActivate({
213-
shouldDisplayMessageWhenNoUpdates: true,
214-
shouldErrorIfUpdateFails: true
218+
isUserInitiated: true,
219+
shouldDisplayMessageWhenNoUpdates: true
215220
})));
216221

217222
await installOrUpdateThenTryActivate({
218-
shouldDisplayMessageWhenNoUpdates: false,
219-
shouldErrorIfUpdateFails: !!ctx.globalState.get(shouldUpdateOnNextActivationKey)
223+
isUserInitiated: !!ctx.globalState.get(shouldUpdateOnNextActivationKey),
224+
shouldDisplayMessageWhenNoUpdates: false
220225
});
221226
}
222227

0 commit comments

Comments
 (0)