-
Notifications
You must be signed in to change notification settings - Fork 1.6k
ci: add coverage check workflow #6582
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
Changes from all commits
ffc82eb
b56a34b
a19812a
5a5b569
33bc5d4
3069ffa
a4933aa
1ba620f
053f4cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| name: Coverage Build | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: [ 'develop', 'release_**' ] | ||
| types: [ opened, synchronize, reopened ] | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| build-coverage: | ||
| name: Build ubuntu24 (JDK 8 / x86_64) | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 60 | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Set up JDK 8 | ||
| uses: actions/setup-java@v5 | ||
| with: | ||
| java-version: '8' | ||
| distribution: 'temurin' | ||
| cache: 'gradle' | ||
|
|
||
| - name: Check Java version | ||
| run: java -version | ||
|
|
||
| - name: Stop Gradle daemon | ||
| run: ./gradlew --stop || true | ||
|
|
||
| - name: Build | ||
| run: ./gradlew clean build --no-daemon --no-build-cache --parallel | ||
|
|
||
| - name: Generate JaCoCo report | ||
| run: ./gradlew jacocoTestReport --no-daemon --no-build-cache | ||
|
|
||
| - name: Upload JaCoCo artifact | ||
| uses: actions/upload-artifact@v6 | ||
| with: | ||
| name: jacoco-coverage | ||
| path: | | ||
| actuator/build/reports/jacoco/test/jacocoTestReport.xml | ||
| chainbase/build/reports/jacoco/test/jacocoTestReport.xml | ||
| common/build/reports/jacoco/test/jacocoTestReport.xml | ||
| consensus/build/reports/jacoco/test/jacocoTestReport.xml | ||
| crypto/build/reports/jacoco/test/jacocoTestReport.xml | ||
| framework/build/reports/jacoco/test/jacocoTestReport.xml | ||
| plugins/build/reports/jacoco/test/jacocoTestReport.xml | ||
| if-no-files-found: error | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| name: Base Coverage Upload | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ 'develop', 'release_**' ] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| build-base-coverage: | ||
| name: Build Base Coverage | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 60 | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Set up JDK 8 | ||
| uses: actions/setup-java@v5 | ||
| with: | ||
| java-version: '8' | ||
| distribution: 'temurin' | ||
| cache: 'gradle' | ||
|
|
||
| - name: Check Java version | ||
| run: java -version | ||
|
|
||
| - name: Grant execute permission | ||
| run: chmod +x gradlew | ||
|
|
||
| - name: Stop Gradle daemon | ||
| run: ./gradlew --stop || true | ||
|
|
||
| - name: Build | ||
| run: ./gradlew clean build --no-daemon --no-build-cache --parallel | ||
|
|
||
| - name: Generate JaCoCo report | ||
| run: ./gradlew jacocoTestReport --no-daemon --no-build-cache | ||
|
|
||
| - name: Upload coverage artifacts | ||
| uses: actions/upload-artifact@v6 | ||
| with: | ||
| name: base-jacoco-xml | ||
| path: | | ||
| actuator/build/reports/jacoco/test/jacocoTestReport.xml | ||
| chainbase/build/reports/jacoco/test/jacocoTestReport.xml | ||
| common/build/reports/jacoco/test/jacocoTestReport.xml | ||
| consensus/build/reports/jacoco/test/jacocoTestReport.xml | ||
| crypto/build/reports/jacoco/test/jacocoTestReport.xml | ||
| framework/build/reports/jacoco/test/jacocoTestReport.xml | ||
| plugins/build/reports/jacoco/test/jacocoTestReport.xml | ||
| if-no-files-found: error | ||
|
|
||
| upload-base-coverage: | ||
| name: Upload Base Coverage to Codecov | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Base coverage should be queried first, and regenerated only if coverage for a corresponding commit is not found. This improves efficiency. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let’s clarify the roles of the two secrets that only access in tronprotocol environment:
Due to GitHub’s permission restrictions, In addition, the maintainers of java-tron have not configured
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree with @bladehan1 — running a full build + test on every push to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Codecov API was evaluated and validated during the initial design phase. But, the maintainers of java-tron refuse to configure |
||
| needs: build-base-coverage | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 20 | ||
|
|
||
| steps: | ||
| - name: Checkout repo | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Download coverage artifacts | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: base-jacoco-xml | ||
| path: coverage-artifacts | ||
|
|
||
| - name: Show coverage files | ||
| run: find coverage-artifacts -type f | sort | ||
|
|
||
| - name: Upload base coverage to Codecov | ||
| uses: codecov/codecov-action@v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| directory: ./coverage-artifacts | ||
| root_dir: ./ | ||
| gcov_executable: '' | ||
| override_branch: ${{ github.ref_name }} | ||
| fail_ci_if_error: true | ||
| verbose: true | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| name: Codecov Upload & Compare | ||
|
|
||
| on: | ||
| workflow_run: | ||
| workflows: | ||
| - Coverage Build | ||
| types: | ||
| - completed | ||
|
|
||
| permissions: | ||
| contents: read | ||
| actions: read | ||
| pull-requests: read | ||
|
|
||
| jobs: | ||
| upload-coverage: | ||
| name: Upload Coverage | ||
| if: > | ||
| github.event.workflow_run.conclusion == 'success' && | ||
| github.event.workflow_run.event == 'pull_request' | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| pr_number: ${{ steps.pr.outputs.pr_number }} | ||
|
|
||
| steps: | ||
| - name: Checkout repo | ||
| uses: actions/checkout@v5 # must download source code | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - name: Download coverage artifact | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: jacoco-coverage | ||
| path: coverage | ||
| run-id: ${{ github.event.workflow_run.id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Get PR details | ||
| id: pr | ||
| uses: actions/github-script@v8 | ||
| with: | ||
| script: | | ||
| const headSha = context.payload.workflow_run.head_sha; | ||
| const headOwner = context.payload.workflow_run.head_repository.owner.login; | ||
| const headBranch = context.payload.workflow_run.head_branch; | ||
| const { data: pulls } = await github.rest.pulls.list({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| state: 'all', | ||
| head: `${headOwner}:${headBranch}`, | ||
| }); | ||
| const pr = pulls.find((p) => p.head.sha === headSha); | ||
| if (pr) { | ||
| core.setOutput('pr_number', pr.number); | ||
| core.setOutput('pr_sha', headSha); | ||
| core.setOutput('pr_branch', headBranch); | ||
| core.setOutput('base_sha', pr.base.sha); | ||
| } else { | ||
| core.setFailed(`No pull request found for commit ${headSha}`); | ||
| } | ||
|
|
||
| - name: Upload to Codecov | ||
| if: ${{ steps.pr.outputs.pr_number != '' }} | ||
| uses: codecov/codecov-action@v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| override_commit: ${{ steps.pr.outputs.pr_sha }} | ||
| override_branch: ${{ steps.pr.outputs.pr_branch }} | ||
| override_pr: ${{ steps.pr.outputs.pr_number }} | ||
| fail_ci_if_error: true | ||
| verbose: true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| name: Waiting Coverage project | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: [ 'develop', 'release_**' ] | ||
| types: [ opened, synchronize, reopened ] | ||
|
|
||
| permissions: | ||
| contents: read | ||
| checks: read | ||
| statuses: read | ||
| pull-requests: read | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| waiting-coverage-project: | ||
| name: waiting-coverage-report | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 70 | ||
|
|
||
| steps: | ||
| - name: Wait for codecov/project status | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After uploading the coverage event, check whether it was successful. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Workflow
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The polling approach occupies a runner for up to 60 minutes just to check a status. Consider using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The purpose of introducing a waiting job is to ensure developers are clearly notified: a PR must not be merged unless the Codecov report is available. Without this waiting mechanism when ci starts, all CI checks may appear green when Codecov report is generating, which can mislead developers into thinking that all validations have passed successfully prematurely. The waiting time is set to 70 minutes for the following reasons:
Why does the Coverage Build job require up to 60 minutes? In practice, its execution time is highly variable, typically ranging from 18 to 40 minutes depending on current load of runner, and can occasionally be longer. If the waiting time is set too short, it may fail prematurely, before the Codecov report is generated. |
||
| uses: actions/github-script@v8 | ||
| with: | ||
| script: | | ||
| const owner = context.repo.owner; | ||
| const repo = context.repo.repo; | ||
| const ref = context.payload.pull_request.head.sha; | ||
|
|
||
| const targetContext = 'codecov/project'; | ||
| const maxAttempts = 120; // 120 * 30s = 60 minutes | ||
| const intervalMs = 30 * 1000; | ||
|
|
||
| function sleep(ms) { | ||
| return new Promise(resolve => setTimeout(resolve, ms)); | ||
| } | ||
|
|
||
| for (let attempt = 1; attempt <= maxAttempts; attempt++) { | ||
| core.info(`Polling attempt ${attempt}/${maxAttempts} for ${targetContext} on ${ref}`); | ||
|
|
||
| try { | ||
| // Check legacy commit statuses | ||
| const combined = await github.rest.repos.getCombinedStatusForRef({ | ||
| owner, | ||
| repo, | ||
| ref, | ||
| per_page: 100 | ||
| }); | ||
|
|
||
| const statuses = combined.data.statuses || []; | ||
| const matchedStatus = statuses.find(s => s.context === targetContext); | ||
|
|
||
| if (matchedStatus) { | ||
| core.info(`Found commit status: ${matchedStatus.context} = ${matchedStatus.state}`); | ||
|
|
||
| if (matchedStatus.state === 'success') { | ||
| core.info(`${targetContext} succeeded.`); | ||
| return; | ||
| } | ||
|
|
||
| if (matchedStatus.state === 'failure' || matchedStatus.state === 'error') { | ||
| core.setFailed(`${targetContext} is ${matchedStatus.state}.`); | ||
| return; | ||
| } | ||
|
|
||
| // pending | ||
| await sleep(intervalMs); | ||
| continue; | ||
| } | ||
|
|
||
| // Check check-runs as a fallback | ||
| const checks = await github.rest.checks.listForRef({ | ||
| owner, | ||
| repo, | ||
| ref, | ||
| per_page: 100 | ||
| }); | ||
|
|
||
| const checkRuns = checks.data.check_runs || []; | ||
| const matchedCheck = checkRuns.find(c => c.name === targetContext); | ||
|
|
||
| if (matchedCheck) { | ||
| core.info( | ||
| `Found check run: ${matchedCheck.name}, status=${matchedCheck.status}, conclusion=${matchedCheck.conclusion}` | ||
| ); | ||
|
|
||
| if (matchedCheck.status === 'completed') { | ||
| if (matchedCheck.conclusion === 'success') { | ||
| core.info(`${targetContext} succeeded.`); | ||
| return; | ||
| } | ||
|
|
||
| core.setFailed( | ||
| `${targetContext} completed with conclusion=${matchedCheck.conclusion}.` | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| // queued / in_progress | ||
| await sleep(intervalMs); | ||
| continue; | ||
| } | ||
|
|
||
| core.info(`${targetContext} not reported yet. Waiting...`); | ||
| } catch (error) { | ||
| core.warning( | ||
| `Attempt ${attempt}/${maxAttempts} failed with transient error: ${error.message}. Retrying in ${intervalMs / 1000}s...` | ||
| ); | ||
| } | ||
|
|
||
| await sleep(intervalMs); | ||
| } | ||
|
|
||
| core.setFailed( | ||
| `Timed out waiting for ${targetContext} to report success on commit ${ref}.` | ||
| ); | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is recommended to merge the PR code coverage workflow files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codecov Upload & Compareruns in tron-protocol environment triggered by another workflow, butCoverage Buildcan only runs in user' environment.