Skip to content

Commit b3a51d7

Browse files
authored
Merge pull request github#978 from mgsium/case-insensitive-slugs
Case insensitive fallback check for GitHub repositories
2 parents 20cdca7 + 3d24328 commit b3a51d7

File tree

2 files changed

+46
-12
lines changed

2 files changed

+46
-12
lines changed

extensions/ql-vscode/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Fix a bug that shows 'Set current database' when hovering over the currently selected database in the databases view. [#976](https://github.com/github/vscode-codeql/pull/976)
66
- Fix a bug with importing large databases. Databases over 4GB can now be imported directly from LGTM or from a zip file. This functionality is only available when using CodeQL CLI version 2.6.0 or later. [#971](https://github.com/github/vscode-codeql/pull/971)
77
- Replace certain control codes (`U+0000` - `U+001F`) with their corresponding control labels (`U+2400` - `U+241F`) in the results view. [#963](https://github.com/github/vscode-codeql/pull/963)
8+
- Allow case-insensitive project slugs for GitHub repositories when adding a CodeQL database from LGTM. [#978](https://github.com/github/vscode-codeql/pull/961)
89

910
## 1.5.6 - 07 October 2021
1011

extensions/ql-vscode/src/databaseFetcher.ts

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ export async function promptImportLgtmDatabase(
112112
return;
113113
}
114114

115+
export async function retrieveCanonicalRepoName(lgtmUrl: string) {
116+
const givenRepoName = extractProjectSlug(lgtmUrl);
117+
const response = await checkForFailingResponse(await fetch(`https://api.github.com/repos/${givenRepoName}`), 'Failed to locate the repository on github');
118+
const repo = await response.json();
119+
if (!repo || !repo.full_name) {
120+
return;
121+
}
122+
return repo.full_name;
123+
}
124+
115125
/**
116126
* Imports a database from a local archive.
117127
*
@@ -300,7 +310,7 @@ async function fetchAndUnzip(
300310
step: 1,
301311
});
302312

303-
const response = await checkForFailingResponse(await fetch(databaseUrl));
313+
const response = await checkForFailingResponse(await fetch(databaseUrl), 'Error downloading database');
304314
const archiveFileStream = fs.createWriteStream(archivePath);
305315

306316
const contentLength = response.headers.get('content-length');
@@ -320,7 +330,7 @@ async function fetchAndUnzip(
320330
await fs.remove(archivePath);
321331
}
322332

323-
async function checkForFailingResponse(response: Response): Promise<Response | never> {
333+
async function checkForFailingResponse(response: Response, errorMessage: string): Promise<Response | never> {
324334
if (response.ok) {
325335
return response;
326336
}
@@ -334,7 +344,7 @@ async function checkForFailingResponse(response: Response): Promise<Response | n
334344
} catch (e) {
335345
msg = text;
336346
}
337-
throw new Error(`Error downloading database.\n\nReason: ${msg}`);
347+
throw new Error(`${errorMessage}.\n\nReason: ${msg}`);
338348
}
339349

340350
function isFile(databaseUrl: string) {
@@ -424,24 +434,37 @@ function convertRawLgtmSlug(maybeSlug: string): string | undefined {
424434
}
425435
return;
426436
}
437+
438+
function extractProjectSlug(lgtmUrl: string): string | undefined {
439+
// Only matches the '/g/' provider (github)
440+
const re = new RegExp('https://lgtm.com/projects/g/(.*[^/])');
441+
const match = lgtmUrl.match(re);
442+
if (!match) {
443+
return;
444+
}
445+
return match[1];
446+
}
427447

428448
// exported for testing
429449
export async function convertToDatabaseUrl(
430450
lgtmUrl: string,
431451
progress: ProgressCallback) {
432452
try {
433453
lgtmUrl = convertRawLgtmSlug(lgtmUrl) || lgtmUrl;
434-
435-
const uri = Uri.parse(lgtmUrl, true);
436-
const paths = ['api', 'v1.0'].concat(
437-
uri.path.split('/').filter((segment) => segment)
438-
).slice(0, 6);
439-
const projectUrl = `https://lgtm.com/${paths.join('/')}`;
440-
const projectResponse = await fetch(projectUrl);
441-
const projectJson = await projectResponse.json();
454+
let projectJson = await downloadLgtmProjectMetadata(lgtmUrl);
442455

443456
if (projectJson.code === 404) {
444-
throw new Error();
457+
// fallback check for github repositories with same name but different case
458+
// will fail for other providers
459+
let canonicalName = await retrieveCanonicalRepoName(lgtmUrl);
460+
if (!canonicalName) {
461+
throw new Error(`Project was not found at ${lgtmUrl}.`);
462+
}
463+
canonicalName = convertRawLgtmSlug(`g/${canonicalName}`);
464+
projectJson = await downloadLgtmProjectMetadata(canonicalName);
465+
if (projectJson.code === 404) {
466+
throw new Error('Failed to download project from LGTM.');
467+
}
445468
}
446469

447470
const language = await promptForLanguage(projectJson, progress);
@@ -461,6 +484,16 @@ export async function convertToDatabaseUrl(
461484
}
462485
}
463486

487+
async function downloadLgtmProjectMetadata(lgtmUrl: string): Promise<any> {
488+
const uri = Uri.parse(lgtmUrl, true);
489+
const paths = ['api', 'v1.0'].concat(
490+
uri.path.split('/').filter((segment) => segment)
491+
).slice(0, 6);
492+
const projectUrl = `https://lgtm.com/${paths.join('/')}`;
493+
const projectResponse = await fetch(projectUrl);
494+
return projectResponse.json();
495+
}
496+
464497
async function promptForLanguage(
465498
projectJson: any,
466499
progress: ProgressCallback

0 commit comments

Comments
 (0)