Skip to content

Commit 26244ef

Browse files
shati-patelcharisk
andauthored
Create remote file links to GitHub URL (#1209)
Co-authored-by: Charis Kyriakou <charisk@github.com>
1 parent 6339eef commit 26244ef

13 files changed

Lines changed: 149 additions & 55 deletions

extensions/ql-vscode/src/pure/bqrs-utils.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
LineColumnLocation,
55
WholeFileLocation
66
} from './bqrs-cli-types';
7+
import { createRemoteFileRef } from './location-link-utils';
78

89
/**
910
* The CodeQL filesystem libraries use this pattern in `getURL()` predicates
@@ -93,3 +94,30 @@ export function isWholeFileLoc(loc: UrlValue): loc is WholeFileLocation {
9394
export function isStringLoc(loc: UrlValue): loc is string {
9495
return typeof loc === 'string';
9596
}
97+
98+
export function tryGetRemoteLocation(
99+
loc: UrlValue | undefined,
100+
fileLinkPrefix: string
101+
): string | undefined {
102+
const resolvableLocation = tryGetResolvableLocation(loc);
103+
if (!resolvableLocation) {
104+
return undefined;
105+
}
106+
107+
// Remote locations have the following format:
108+
// file:/home/runner/work/<repo>/<repo/relative/path/to/file
109+
// So we need to drop the first 6 parts of the path.
110+
111+
// TODO: We can make this more robust to other path formats.
112+
const locationParts = resolvableLocation.uri.split('/');
113+
const trimmedLocation = locationParts.slice(6, locationParts.length).join('/');
114+
115+
const fileLink = {
116+
fileLinkPrefix,
117+
filePath: trimmedLocation,
118+
};
119+
return createRemoteFileRef(
120+
fileLink,
121+
resolvableLocation.startLine,
122+
resolvableLocation.endLine);
123+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { FileLink } from '../remote-queries/shared/analysis-result';
2+
3+
export function createRemoteFileRef(
4+
fileLink: FileLink,
5+
startLine?: number,
6+
endLine?: number
7+
): string {
8+
if (startLine && endLine) {
9+
return `${fileLink.fileLinkPrefix}/${fileLink.filePath}#L${startLine}-L${endLine}`;
10+
} else if (startLine) {
11+
return `${fileLink.fileLinkPrefix}/${fileLink.filePath}#L${startLine}`;
12+
} else {
13+
return `${fileLink.fileLinkPrefix}/${fileLink.filePath}`;
14+
}
15+
}

extensions/ql-vscode/src/remote-queries/analyses-results-manager.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,17 +121,19 @@ export class AnalysesResultsManager {
121121
throw new Error(`Could not download the analysis results for ${analysis.nwo}: ${e.message}`);
122122
}
123123

124+
const fileLinkPrefix = this.createGitHubDotcomFileLinkPrefix(analysis.nwo, analysis.databaseSha);
125+
124126
let newAnaysisResults: AnalysisResults;
125127
const fileExtension = path.extname(artifactPath);
126128
if (fileExtension === '.sarif') {
127-
const queryResults = await this.readSarifResults(artifactPath);
129+
const queryResults = await this.readSarifResults(artifactPath, fileLinkPrefix);
128130
newAnaysisResults = {
129131
...analysisResults,
130132
interpretedResults: queryResults,
131133
status: 'Completed'
132134
};
133135
} else if (fileExtension === '.bqrs') {
134-
const queryResults = await this.readBqrsResults(artifactPath);
136+
const queryResults = await this.readBqrsResults(artifactPath, fileLinkPrefix);
135137
newAnaysisResults = {
136138
...analysisResults,
137139
rawResults: queryResults,
@@ -148,15 +150,15 @@ export class AnalysesResultsManager {
148150
void publishResults([...resultsForQuery]);
149151
}
150152

151-
private async readBqrsResults(filePath: string): Promise<AnalysisRawResults> {
152-
return await extractRawResults(this.cliServer, this.logger, filePath);
153+
private async readBqrsResults(filePath: string, fileLinkPrefix: string): Promise<AnalysisRawResults> {
154+
return await extractRawResults(this.cliServer, this.logger, filePath, fileLinkPrefix);
153155
}
154156

155-
private async readSarifResults(filePath: string): Promise<AnalysisAlert[]> {
157+
private async readSarifResults(filePath: string, fileLinkPrefix: string): Promise<AnalysisAlert[]> {
156158
const sarifLog = await sarifParser(filePath);
157159

158-
const processedSarif = extractAnalysisAlerts(sarifLog);
159-
if (processedSarif.errors) {
160+
const processedSarif = extractAnalysisAlerts(sarifLog, fileLinkPrefix);
161+
if (processedSarif.errors.length) {
160162
void this.logger.log(`Error processing SARIF file: ${os.EOL}${processedSarif.errors.join(os.EOL)}`);
161163
}
162164

@@ -166,4 +168,8 @@ export class AnalysesResultsManager {
166168
private isAnalysisInMemory(analysis: AnalysisSummary): boolean {
167169
return this.internalGetAnalysesResults(analysis.downloadLink.queryId).some(x => x.nwo === analysis.nwo);
168170
}
171+
172+
private createGitHubDotcomFileLinkPrefix(nwo: string, sha: string): string {
173+
return `https://github.com/${nwo}/blob/${sha}`;
174+
}
169175
}

extensions/ql-vscode/src/remote-queries/bqrs-processing.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { MAX_RAW_RESULTS } from './shared/result-limits';
77
export async function extractRawResults(
88
cliServer: CodeQLCliServer,
99
logger: Logger,
10-
filePath: string
10+
filePath: string,
11+
fileLinkPrefix: string,
1112
): Promise<AnalysisRawResults> {
1213
const bqrsInfo = await cliServer.bqrsInfo(filePath);
1314
const resultSets = bqrsInfo['result-sets'];
@@ -30,5 +31,5 @@ export async function extractRawResults(
3031

3132
const capped = !!chunk.next;
3233

33-
return { schema, resultSet, capped };
34+
return { schema, resultSet, fileLinkPrefix, capped };
3435
}

extensions/ql-vscode/src/remote-queries/remote-queries-interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ export class RemoteQueriesInterfaceManager {
319319

320320
return sortedAnalysisSummaries.map((analysisResult) => ({
321321
nwo: analysisResult.nwo,
322-
databaseSha: analysisResult.databaseSha,
322+
databaseSha: analysisResult.databaseSha || 'HEAD',
323323
resultCount: analysisResult.resultCount,
324324
downloadLink: analysisResult.downloadLink,
325325
fileSize: this.formatFileSize(analysisResult.fileSizeInBytes)

extensions/ql-vscode/src/remote-queries/sarif-processing.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
const defaultSeverity = 'Warning';
1616

1717
export function extractAnalysisAlerts(
18-
sarifLog: sarif.Log
18+
sarifLog: sarif.Log,
19+
fileLinkPrefix: string
1920
): {
2021
alerts: AnalysisAlert[],
2122
errors: string[]
@@ -26,7 +27,7 @@ export function extractAnalysisAlerts(
2627
for (const run of sarifLog.runs ?? []) {
2728
for (const result of run.results ?? []) {
2829
try {
29-
alerts.push(...extractResultAlerts(run, result));
30+
alerts.push(...extractResultAlerts(run, result, fileLinkPrefix));
3031
} catch (e) {
3132
errors.push(`Error when processing SARIF result: ${e}`);
3233
continue;
@@ -39,14 +40,15 @@ export function extractAnalysisAlerts(
3940

4041
function extractResultAlerts(
4142
run: sarif.Run,
42-
result: sarif.Result
43+
result: sarif.Result,
44+
fileLinkPrefix: string
4345
): AnalysisAlert[] {
4446
const alerts: AnalysisAlert[] = [];
4547

46-
const message = getMessage(result);
48+
const message = getMessage(result, fileLinkPrefix);
4749
const rule = tryGetRule(run, result);
4850
const severity = tryGetSeverity(run, result, rule) || defaultSeverity;
49-
const codeFlows = getCodeFlows(result);
51+
const codeFlows = getCodeFlows(result, fileLinkPrefix);
5052
const shortDescription = getShortDescription(rule, message!);
5153

5254
for (const location of result.locations ?? []) {
@@ -60,7 +62,10 @@ function extractResultAlerts(
6062
const analysisAlert: AnalysisAlert = {
6163
message,
6264
shortDescription,
63-
filePath,
65+
fileLink: {
66+
fileLinkPrefix,
67+
filePath,
68+
},
6469
severity,
6570
codeSnippet,
6671
highlightedRegion,
@@ -177,7 +182,8 @@ function getHighlightedRegion(region: sarif.Region): HighlightedRegion {
177182
}
178183

179184
function getCodeFlows(
180-
result: sarif.Result
185+
result: sarif.Result,
186+
fileLinkPrefix: string
181187
): CodeFlow[] {
182188
const codeFlows = [];
183189

@@ -195,7 +201,10 @@ function getCodeFlows(
195201
: undefined;
196202

197203
threadFlows.push({
198-
filePath,
204+
fileLink: {
205+
fileLinkPrefix,
206+
filePath,
207+
},
199208
codeSnippet,
200209
highlightedRegion
201210
} as ThreadFlow);
@@ -209,7 +218,7 @@ function getCodeFlows(
209218
return codeFlows;
210219
}
211220

212-
function getMessage(result: sarif.Result): AnalysisMessage {
221+
function getMessage(result: sarif.Result, fileLinkPrefix: string): AnalysisMessage {
213222
const tokens: AnalysisMessageToken[] = [];
214223

215224
const messageText = result.message!.text!;
@@ -224,7 +233,10 @@ function getMessage(result: sarif.Result): AnalysisMessage {
224233
t: 'location',
225234
text: messagePart.text,
226235
location: {
227-
filePath: relatedLocation!.physicalLocation!.artifactLocation!.uri!,
236+
fileLink: {
237+
fileLinkPrefix: fileLinkPrefix,
238+
filePath: relatedLocation!.physicalLocation!.artifactLocation!.uri!,
239+
},
228240
highlightedRegion: getHighlightedRegion(relatedLocation!.physicalLocation!.region!),
229241
}
230242
});

extensions/ql-vscode/src/remote-queries/shared/analysis-result.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,27 @@ export interface AnalysisResults {
1010
}
1111

1212
export interface AnalysisRawResults {
13-
schema: ResultSetSchema,
14-
resultSet: RawResultSet,
13+
schema: ResultSetSchema;
14+
resultSet: RawResultSet;
15+
fileLinkPrefix: string;
1516
capped: boolean;
1617
}
1718

1819
export interface AnalysisAlert {
1920
message: AnalysisMessage;
2021
shortDescription: string;
2122
severity: ResultSeverity;
22-
filePath: string;
23+
fileLink: FileLink;
2324
codeSnippet: CodeSnippet;
2425
highlightedRegion?: HighlightedRegion;
2526
codeFlows: CodeFlow[];
2627
}
2728

29+
export interface FileLink {
30+
fileLinkPrefix: string;
31+
filePath: string;
32+
}
33+
2834
export interface CodeSnippet {
2935
startLine: number;
3036
endLine: number;
@@ -43,7 +49,7 @@ export interface CodeFlow {
4349
}
4450

4551
export interface ThreadFlow {
46-
filePath: string;
52+
fileLink: FileLink;
4753
codeSnippet: CodeSnippet;
4854
highlightedRegion?: HighlightedRegion;
4955
message?: AnalysisMessage;
@@ -66,7 +72,7 @@ export interface AnalysisMessageLocationToken {
6672
t: 'location';
6773
text: string;
6874
location: {
69-
filePath: string;
75+
fileLink: FileLink;
7076
highlightedRegion?: HighlightedRegion;
7177
};
7278
}

extensions/ql-vscode/src/remote-queries/view/AnalysisAlertResult.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const AnalysisAlertResult = ({ alert }: { alert: AnalysisAlert }) => {
77
const showPathsLink = alert.codeFlows.length > 0;
88

99
return <FileCodeSnippet
10-
filePath={alert.filePath}
10+
fileLink={alert.fileLink}
1111
codeSnippet={alert.codeSnippet}
1212
highlightedRegion={alert.highlightedRegion}
1313
severity={alert.severity}

extensions/ql-vscode/src/remote-queries/view/CodePaths.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const CodePath = ({
7171

7272
<VerticalSpace size={2} />
7373
<FileCodeSnippet
74-
filePath={threadFlow.filePath}
74+
fileLink={threadFlow.fileLink}
7575
codeSnippet={threadFlow.codeSnippet}
7676
highlightedRegion={threadFlow.highlightedRegion}
7777
severity={severity}
@@ -82,7 +82,7 @@ const CodePath = ({
8282
};
8383

8484
const getCodeFlowName = (codeFlow: CodeFlow) => {
85-
const filePath = codeFlow.threadFlows[codeFlow.threadFlows.length - 1].filePath;
85+
const filePath = codeFlow.threadFlows[codeFlow.threadFlows.length - 1].fileLink.filePath;
8686
return filePath.substring(filePath.lastIndexOf('/') + 1);
8787
};
8888

extensions/ql-vscode/src/remote-queries/view/FileCodeSnippet.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import * as React from 'react';
22
import styled from 'styled-components';
3-
import { CodeSnippet, HighlightedRegion, AnalysisMessage, ResultSeverity } from '../shared/analysis-result';
3+
import { CodeSnippet, FileLink, HighlightedRegion, AnalysisMessage, ResultSeverity } from '../shared/analysis-result';
44
import { Box, Link } from '@primer/react';
55
import VerticalSpace from './VerticalSpace';
6+
import { createRemoteFileRef } from '../../pure/location-link-utils';
67

78
const borderColor = 'var(--vscode-editor-snippetFinalTabstopHighlightBorder)';
89
const warningColor = '#966C23';
@@ -106,7 +107,14 @@ const Message = ({
106107
case 'text':
107108
return <span key={`token-${index}`}>{token.text}</span>;
108109
case 'location':
109-
return <Link key={`token-${index}`} href='TODO'>{token.text}</Link>;
110+
return <Link
111+
key={`token-${index}`}
112+
href={createRemoteFileRef(
113+
token.location.fileLink,
114+
token.location.highlightedRegion?.startLine,
115+
token.location.highlightedRegion?.endLine)}>
116+
{token.text}
117+
</Link>;
110118
default:
111119
return <></>;
112120
}
@@ -165,14 +173,14 @@ const CodeLine = ({
165173
};
166174

167175
const FileCodeSnippet = ({
168-
filePath,
176+
fileLink,
169177
codeSnippet,
170178
highlightedRegion,
171179
severity,
172180
message,
173181
messageChildren,
174182
}: {
175-
filePath: string,
183+
fileLink: FileLink,
176184
codeSnippet: CodeSnippet,
177185
highlightedRegion?: HighlightedRegion,
178186
severity?: ResultSeverity,
@@ -183,11 +191,17 @@ const FileCodeSnippet = ({
183191
const code = codeSnippet.text.split('\n');
184192

185193
const startingLine = codeSnippet.startLine;
194+
const endingLine = codeSnippet.endLine;
195+
196+
const titleFileUri = createRemoteFileRef(
197+
fileLink,
198+
startingLine,
199+
endingLine);
186200

187201
return (
188202
<Container>
189203
<TitleContainer>
190-
<Link>{filePath}</Link>
204+
<Link href={titleFileUri}>{fileLink.filePath}</Link>
191205
</TitleContainer>
192206
<CodeContainer>
193207
{code.map((line, index) => (

0 commit comments

Comments
 (0)