Skip to content

Commit f060199

Browse files
hybloidtiulpin
authored andcommitted
⬆️ Azure DevOps should support pr-mode flag
1 parent 6bdac20 commit f060199

File tree

5 files changed

+176
-8
lines changed

5 files changed

+176
-8
lines changed

vsts/QodanaScan/index.js

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14690,6 +14690,7 @@ var require_utils3 = __commonJS({
1469014690
var tool = __importStar2(require_tool());
1469114691
var qodana_12 = (init_qodana(), __toCommonJS(qodana_exports));
1469214692
var path2 = require("path");
14693+
var node_stream_1 = require("node:stream");
1469314694
function setFailed(message) {
1469414695
tl2.setResult(tl2.TaskResult.Failed, message);
1469514696
}
@@ -14703,28 +14704,39 @@ var require_utils3 = __commonJS({
1470314704
uploadSarif: tl2.getBoolInput("uploadSarif", false) || true,
1470414705
artifactName: tl2.getInput("artifactName", false) || "qodana-report",
1470514706
useNightly: tl2.getBoolInput("useNightly", false) || false,
14707+
prMode: tl2.getBoolInput("prMode", false) || false,
1470614708
// Not used by the Azure task
1470714709
postComment: false,
1470814710
additionalCacheKey: "",
1470914711
primaryCacheKey: "",
1471014712
useAnnotations: false,
1471114713
useCaches: false,
1471214714
cacheDefaultBranchOnly: false,
14713-
prMode: false,
1471414715
githubToken: "",
1471514716
pushFixes: "none",
1471614717
commitMessage: ""
1471714718
};
1471814719
}
1471914720
function qodana() {
1472014721
return __awaiter2(this, arguments, void 0, function* (args = []) {
14722+
const env = Object.assign(Object.assign({}, process.env), { NONINTERACTIVE: "1" });
1472114723
if (args.length === 0) {
1472214724
const inputs = getInputs();
1472314725
args = (0, qodana_12.getQodanaScanArgs)(inputs.args, inputs.resultsDir, inputs.cacheDir);
14726+
if (inputs.prMode && tl2.getVariable("Build.Reason") === "PullRequest") {
14727+
const sha = yield getPrSha();
14728+
if (sha !== "") {
14729+
args.push("--commit", sha);
14730+
const sourceBranch = process.env.QODANA_BRANCH || getSourceAndTargetBranches().sourceBranch;
14731+
if (sourceBranch) {
14732+
env.QODANA_BRANCH = sourceBranch;
14733+
}
14734+
}
14735+
}
1472414736
}
1472514737
return yield tl2.execAsync(qodana_12.EXECUTABLE, args, {
1472614738
ignoreReturnCode: true,
14727-
env: Object.assign(Object.assign({}, process.env), { NONINTERACTIVE: "1" })
14739+
env
1472814740
});
1472914741
});
1473014742
}
@@ -14783,6 +14795,63 @@ var require_utils3 = __commonJS({
1478314795
tl2.warning(`Failed to upload SARIF \u2013 ${error.message}`);
1478414796
}
1478514797
}
14798+
function getSourceAndTargetBranches() {
14799+
var _a, _b;
14800+
const sourceBranch = (_a = tl2.getVariable("System.PullRequest.SourceBranch")) === null || _a === void 0 ? void 0 : _a.replace("refs/heads/", "");
14801+
const targetBranch = (_b = tl2.getVariable("System.PullRequest.TargetBranch")) === null || _b === void 0 ? void 0 : _b.replace("refs/heads/", "");
14802+
return { sourceBranch, targetBranch };
14803+
}
14804+
function getPrSha() {
14805+
return __awaiter2(this, void 0, void 0, function* () {
14806+
if (process.env.QODANA_PR_SHA) {
14807+
return process.env.QODANA_PR_SHA;
14808+
}
14809+
const { sourceBranch, targetBranch } = getSourceAndTargetBranches();
14810+
if (sourceBranch && targetBranch) {
14811+
yield git(["fetch", "origin"]);
14812+
const output = yield gitOutput(["merge-base", "origin/" + sourceBranch, "origin/" + targetBranch], {
14813+
ignoreReturnCode: true
14814+
});
14815+
if (output.exitCode === 0) {
14816+
const lines = output.stdout.trim().split("\n");
14817+
if (lines.length > 1) {
14818+
return lines[1].trim();
14819+
}
14820+
}
14821+
}
14822+
return "";
14823+
});
14824+
}
14825+
function git(args_1) {
14826+
return __awaiter2(this, arguments, void 0, function* (args, options = {}) {
14827+
return (yield gitOutput(args, options)).exitCode;
14828+
});
14829+
}
14830+
function gitOutput(args_1) {
14831+
return __awaiter2(this, arguments, void 0, function* (args, options = {}) {
14832+
const result = {
14833+
exitCode: 0,
14834+
stdout: "",
14835+
stderr: ""
14836+
};
14837+
const outStream = new node_stream_1.Writable({
14838+
write(chunk, _, callback) {
14839+
result.stdout += chunk.toString("utf8");
14840+
callback();
14841+
}
14842+
});
14843+
const errStream = new node_stream_1.Writable({
14844+
write(chunk, _, callback) {
14845+
result.stderr += chunk.toString("utf8");
14846+
callback();
14847+
}
14848+
});
14849+
options.outStream = outStream;
14850+
options.errStream = errStream;
14851+
result.exitCode = yield tl2.execAsync("git", args, options);
14852+
return result;
14853+
});
14854+
}
1478614855
}
1478714856
});
1478814857

vsts/QodanaScan/task.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@
6969
"defaultValue": false,
7070
"required": false,
7171
"helpMarkDown": "This option is for development purposes only. Do not use it in production."
72+
},
73+
{
74+
"name": "prMode",
75+
"type": "boolean",
76+
"label": "PR Mode",
77+
"defaultValue": false,
78+
"required": false,
79+
"helpMarkDown": "Whether the PR analysis gets executed in the pull request mode."
7280
}
7381
],
7482
"execution": {

vsts/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ You probably won't need other options than `args`: all other options can be help
9292
| `uploadSarif` | Upload qodana.sarif.json as an qodana.sarif artifact to the job. Optional. | `true` |
9393
| `artifactName` | Specify Qodana results artifact name, used for results uploading. Optional. | `qodana-report` |
9494
| `cacheDir` | Directory to store Qodana caches. Optional. | `$(Agent.TempDirectory)/qodana/cache` |
95+
| `prMode` | Analyze ONLY changed files in a pull request. Optional. | `false` |
9596

9697
[gh:qodana]: https://github.com/JetBrains/qodana-action/actions/workflows/code_scanning.yml
9798
[youtrack]: https://youtrack.jetbrains.com/issues/QD

vsts/src/utils.ts

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import {
3434

3535
// eslint-disable-next-line @typescript-eslint/no-require-imports
3636
import path = require('path')
37+
import {IExecOptions} from 'azure-pipelines-task-lib/toolrunner'
38+
import {Writable} from 'node:stream'
3739

3840
export function setFailed(message: string): void {
3941
tl.setResult(tl.TaskResult.Failed, message)
@@ -54,14 +56,14 @@ export function getInputs(): Inputs {
5456
uploadSarif: tl.getBoolInput('uploadSarif', false) || true,
5557
artifactName: tl.getInput('artifactName', false) || 'qodana-report',
5658
useNightly: tl.getBoolInput('useNightly', false) || false,
59+
prMode: tl.getBoolInput('prMode', false) || false,
5760
// Not used by the Azure task
5861
postComment: false,
5962
additionalCacheKey: '',
6063
primaryCacheKey: '',
6164
useAnnotations: false,
6265
useCaches: false,
6366
cacheDefaultBranchOnly: false,
64-
prMode: false,
6567
githubToken: '',
6668
pushFixes: 'none',
6769
commitMessage: ''
@@ -74,16 +76,28 @@ export function getInputs(): Inputs {
7476
* @returns The qodana command execution output.
7577
*/
7678
export async function qodana(args: string[] = []): Promise<number> {
79+
const env: Record<string, string> = {
80+
...process.env,
81+
NONINTERACTIVE: '1'
82+
}
7783
if (args.length === 0) {
7884
const inputs = getInputs()
7985
args = getQodanaScanArgs(inputs.args, inputs.resultsDir, inputs.cacheDir)
86+
if (inputs.prMode && tl.getVariable('Build.Reason') === 'PullRequest') {
87+
const sha = await getPrSha()
88+
if (sha !== '') {
89+
args.push('--commit', sha)
90+
const sourceBranch =
91+
process.env.QODANA_BRANCH || getSourceAndTargetBranches().sourceBranch
92+
if (sourceBranch) {
93+
env.QODANA_BRANCH = sourceBranch
94+
}
95+
}
96+
}
8097
}
8198
return await tl.execAsync(EXECUTABLE, args, {
8299
ignoreReturnCode: true,
83-
env: {
84-
...process.env,
85-
NONINTERACTIVE: '1'
86-
}
100+
env
87101
})
88102
}
89103

@@ -171,3 +185,79 @@ export function uploadSarif(resultsDir: string, execute: boolean): void {
171185
tl.warning(`Failed to upload SARIF – ${(error as Error).message}`)
172186
}
173187
}
188+
189+
function getSourceAndTargetBranches(): {
190+
sourceBranch?: string
191+
targetBranch?: string
192+
} {
193+
const sourceBranch = tl
194+
.getVariable('System.PullRequest.SourceBranch')
195+
?.replace('refs/heads/', '')
196+
const targetBranch = tl
197+
.getVariable('System.PullRequest.TargetBranch')
198+
?.replace('refs/heads/', '')
199+
return {sourceBranch, targetBranch}
200+
}
201+
202+
async function getPrSha(): Promise<string> {
203+
if (process.env.QODANA_PR_SHA) {
204+
return process.env.QODANA_PR_SHA
205+
}
206+
const {sourceBranch, targetBranch} = getSourceAndTargetBranches()
207+
208+
if (sourceBranch && targetBranch) {
209+
await git(['fetch', 'origin'])
210+
const output = await gitOutput(
211+
['merge-base', 'origin/' + sourceBranch, 'origin/' + targetBranch],
212+
{
213+
ignoreReturnCode: true
214+
}
215+
)
216+
if (output.exitCode === 0) {
217+
const lines = output.stdout.trim().split('\n')
218+
if (lines.length > 1) {
219+
return lines[1].trim()
220+
}
221+
}
222+
}
223+
return ''
224+
}
225+
226+
async function git(
227+
args: string[],
228+
options: IExecOptions = {}
229+
): Promise<number> {
230+
return (await gitOutput(args, options)).exitCode
231+
}
232+
233+
async function gitOutput(
234+
args: string[],
235+
options: IExecOptions = {}
236+
): Promise<{exitCode: number; stderr: string; stdout: string}> {
237+
const result = {
238+
exitCode: 0,
239+
stdout: '',
240+
stderr: ''
241+
}
242+
243+
const outStream = new Writable({
244+
write(chunk, _, callback) {
245+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
246+
result.stdout += chunk.toString('utf8')
247+
callback()
248+
}
249+
})
250+
251+
const errStream = new Writable({
252+
write(chunk, _, callback) {
253+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
254+
result.stderr += chunk.toString('utf8')
255+
callback()
256+
}
257+
})
258+
options.outStream = outStream
259+
options.errStream = errStream
260+
261+
result.exitCode = await tl.execAsync('git', args, options)
262+
return result
263+
}

vsts/vss-extension.dev.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifestVersion": 1,
33
"id": "qodana-dev",
44
"name": "Qodana (Dev)",
5-
"version": "2024.3.135",
5+
"version": "2024.3.146",
66
"publisher": "JetBrains",
77
"targets": [
88
{

0 commit comments

Comments
 (0)