Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link to Pull Request Analysis Result when pull-request Input is Specified #34

Merged
merged 12 commits into from
Oct 10, 2024
5 changes: 3 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:

env:
SONAR_HOST_URL: https://sonarcloud.io
SONAR_ORGANIZATION: phwt
SONAR_PROJECT_KEY: sonarqube-quality-gate-action

jobs:
Expand Down Expand Up @@ -35,8 +36,8 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.organization=phwt
-Dsonar.projectKey=sonarqube-quality-gate-action
-Dsonar.organization=${{ env.SONAR_ORGANIZATION }}
-Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
-Dsonar.javascript.lcov.reportPaths=./coverage/lcov.info
-Dsonar.cpd.exclusions=**/__tests__/*.ts

Expand Down
15 changes: 8 additions & 7 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:

env:
SONAR_HOST_URL: https://sonarcloud.io
SONAR_ORGANIZATION: phwt
SONAR_PROJECT_KEY: sonarqube-quality-gate-action

jobs:
Expand Down Expand Up @@ -33,8 +34,8 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.organization=phwt
-Dsonar.projectKey=sonarqube-quality-gate-action
-Dsonar.organization=${{ env.SONAR_ORGANIZATION }}
-Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
-Dsonar.javascript.lcov.reportPaths=./coverage/lcov.info
-Dsonar.branch.name=${{ github.event.pull_request.head.ref }}
-Dsonar.cpd.exclusions=**/__tests__/*.ts
Expand All @@ -49,9 +50,8 @@ jobs:
sonar-host-url: ${{ env.SONAR_HOST_URL }}
sonar-project-key: ${{ env.SONAR_PROJECT_KEY }}
sonar-token: ${{ secrets.SONAR_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.event.pull_request.head.ref }}
fail-on-quality-gate-error: true
disable-pr-comment: true

- run: |
echo "${{ steps.quality-gate-check.outputs.project-status }}"
Expand Down Expand Up @@ -82,8 +82,8 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.organization=phwt
-Dsonar.projectKey=sonarqube-quality-gate-action
-Dsonar.organization=${{ env.SONAR_ORGANIZATION }}
-Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
-Dsonar.javascript.lcov.reportPaths=./coverage/lcov.info
-Dsonar.pullrequest.key=${{ github.event.number }}
-Dsonar.pullrequest.branch=${{ github.event.pull_request.head.ref }}
Expand All @@ -100,7 +100,8 @@ jobs:
sonar-project-key: ${{ env.SONAR_PROJECT_KEY }}
sonar-token: ${{ secrets.SONAR_TOKEN }}
pull-request: ${{ github.event.number }}
disable-pr-comment: true
github-token: ${{ secrets.GITHUB_TOKEN }}
fail-on-quality-gate-error: true

- run: |
echo "${{ steps.quality-gate-check.outputs.project-status }}"
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sonarqube-quality-gate-action",
"version": "1.5.0",
"version": "1.5.1",
"description": "",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -29,4 +29,4 @@
"ts-node": "^10.9.2",
"typescript": "^4.9.5"
}
}
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ import { findComment } from "./modules/find-comment/main";
inputs.hostURL,
inputs.projectKey,
context,
inputs.branch
inputs.branch,
inputs.pullRequest
);

console.log("Finding comment associated with the report...");
Expand Down
49 changes: 27 additions & 22 deletions src/modules/__tests__/report.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,11 @@ const context: Context = {
};

describe("buildReport", () => {
test("should build report", () => {
beforeEach(() => {
timezone_mock.register("UTC");
});

test("should build report", () => {
const hostURL = "https://host-url.com/";
const projectKey = "project-key";

Expand Down Expand Up @@ -142,8 +145,7 @@ describe("buildReport", () => {
expect(report).toBe(expectedReport);
});

test("should build report with report link including branch parameter when branch is defined", () => {
timezone_mock.register("UTC");
test("should build report with report link including a `branch` parameter when `branch` is defined", () => {
const hostURL = "https://host-url.com/";
const projectKey = "project-key";
const branch = "branch-name";
Expand All @@ -156,26 +158,29 @@ describe("buildReport", () => {
branch
);

const expectedReport = `### SonarQube Quality Gate Result
- **Result**: :exclamation: Error
- **Branch**: \`branch-name\`
- Triggered by @me on \`pull_request\`
expect(report).toContain("- **Branch**: `branch-name`");
expect(report).toContain(
"[View on SonarQube](https://host-url.com/dashboard?id=project-key&branch=branch-name)"
);
});

| Metric | Status | Value | Error Threshold |
|:------:|:------:|:-----:|:---------------:|
|Reliability rating|:exclamation: Error|4|> 1|
|Security rating|:exclamation: Error|2|> 1|
|Sqale rating|:white_check_mark: OK|1|> 1|
|Blocker violations|:exclamation: Error|53|> 0|
|Critical violations|:exclamation: Error|45|> 0|
|Line coverage|:exclamation: Error|10.10|< 80|
|Major violations|:exclamation: Error|1168|> 0|
|Minor violations|:exclamation: Error|81|> 30|
|New duplicated blocks|:white_check_mark: OK|0|> 0|
|New minor violations|:white_check_mark: OK|0|> 0|
test("should build report with report link including a `pullRequest` parameter when `pullRequest` is defined", () => {
const hostURL = "https://host-url.com/";
const projectKey = "project-key";
const pullRequest = "12";

[View on SonarQube](https://host-url.com/dashboard?id=project-key&branch=branch-name)
###### _updated: 1/1/1970, 08:31:00 (UTC+0)_`;
expect(report).toBe(expectedReport);
const report = buildReport(
qualityGate,
hostURL,
projectKey,
context,
undefined,
pullRequest
);

expect(report).toContain("- **Pull Request**: #12");
expect(report).toContain(
"[View on SonarQube](https://host-url.com/dashboard?id=project-key&pullRequest=12)"
);
});
});
4 changes: 2 additions & 2 deletions src/modules/__tests__/sonarqube-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe("fetchQualityGate", () => {
);
});

it("should make a GET request to the correct URL with `projectKey` and `pullRequest` parameter when `pull-request` is defined", async () => {
it("should make a GET request to the correct URL with `projectKey` and `pullRequest` parameter when `pullRequest` is defined", async () => {
(axios.get as jest.Mock).mockResolvedValue({});

await fetchQualityGate(
Expand All @@ -52,7 +52,7 @@ describe("fetchQualityGate", () => {
);
});

it("should thrown an error when both `branch` and `pull-request` are defined", async () => {
it("should thrown an error when both `branch` and `pullRequest` are defined", async () => {
(axios.get as jest.Mock).mockResolvedValue({});

const fetchQualityGateFunction = async () => {
Expand Down
47 changes: 38 additions & 9 deletions src/modules/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,61 @@ const buildRow = (condition: Condition) => {
return "|" + rowValues.join("|") + "|";
};

/**
* Constructs a SonarQube report URL based on the provided parameters.
*
* @param hostUrl - The base URL of the SonarQube server.
* @param projectKey - The unique key of the project in SonarQube.
* @param branch - (Optional) The branch name to include in the report URL.
* @param pullRequest - (Optional) The pull request identifier to include in the report URL.
* @returns The constructed SonarQube report URL.
*/
const buildReportUrl = (
hostUrl: string,
projectKey: string,
branch?: string,
pullRequest?: string
) => {
const baseUrl = `${trimTrailingSlash(hostUrl)}/dashboard`;

const urlParams = new URLSearchParams({
id: projectKey,
...(branch && { branch }),
...(pullRequest && { pullRequest }),
});

return `${baseUrl}?${urlParams}`;
};

export const buildReport = (
result: QualityGate,
hostURL: string,
projectKey: string,
context: Context,
branch?: string
branch?: string,
pullRequest?: string
) => {
const projectURL =
trimTrailingSlash(hostURL) +
`/dashboard?id=${projectKey}` +
(branch ? `&branch=${encodeURIComponent(branch)}` : "");

const reportUrl = buildReportUrl(hostURL, projectKey, branch, pullRequest);
const projectStatus = getStatusEmoji(result.projectStatus.status);

const resultTable = result.projectStatus.conditions.map(buildRow).join("\n");

const { value: updatedDate, offset: updatedOffset } = getCurrentDateTime();

const resultContext = [
`- **Result**: ${projectStatus}`,
...(branch ? [`- **Branch**: \`${branch}\``] : []),
...(pullRequest ? [`- **Pull Request**: #${pullRequest}`] : []),
`- Triggered by @${context.actor} on \`${context.eventName}\``,
];

return `### SonarQube Quality Gate Result
- **Result**: ${projectStatus}${branch ? `\n- **Branch**: \`${branch}\`` : ""}
- Triggered by @${context.actor} on \`${context.eventName}\`
${resultContext.join("\n")}

| Metric | Status | Value | Error Threshold |
|:------:|:------:|:-----:|:---------------:|
${resultTable}

[View on SonarQube](${projectURL})
[View on SonarQube](${reportUrl})
###### _updated: ${updatedDate} (${updatedOffset})_`;
};
Loading