Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
# The branches below must be a subset of the branches above
branches: [ 'develop' ]
paths-ignore: [ '**/*.md', '.gitignore', '**/.gitignore', '.editorconfig',
'.gitattributes', 'docs/**', 'CHANGELOG', '.github/ISSUE_TEMPLATE/**',
'.github/PULL_REQUEST_TEMPLATE/**', '.github/CODEOWNERS' ]
schedule:
- cron: '6 10 * * 0'

Expand All @@ -29,16 +32,23 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: manual

- name: Autobuild
uses: github/codeql-action/autobuild@v4
- name: Set up JDK 8
uses: actions/setup-java@v5
with:
java-version: '8'
distribution: 'temurin'

- name: Build
run: ./gradlew build -x test --no-daemon

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/coverage-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Coverage Build
Copy link
Copy Markdown
Contributor

@bladehan1 bladehan1 Mar 17, 2026

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codecov Upload & Compare runs in tron-protocol environment triggered by another workflow, but Coverage Build can only runs in user' environment.


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
92 changes: 92 additions & 0 deletions .github/workflows/coverage-update-baseline.yml
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
Copy link
Copy Markdown
Contributor

@bladehan1 bladehan1 Mar 17, 2026

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown

@liuyifei001 liuyifei001 Mar 17, 2026

Choose a reason for hiding this comment

The 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:

  • secrets.CODECOV_TOKEN: used to upload coverage artifacts
  • secrets.CODECOV_API_TOKEN: used to query coverage reports

Due to GitHub’s permission restrictions, secrets.CODECOV_API_TOKEN is not available in forked pull requests, which prevents querying the base repository’s coverage first.

In addition, the maintainers of java-tron have not configured secrets.CODECOV_API_TOKEN; only the upload token (secrets.CODECOV_TOKEN) is set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @bladehan1 — running a full build + test on every push to develop is expensive. Consider querying Codecov API first to check if coverage for this commit already exists, and skip the build if it does. This could save significant CI minutes, especially on active branches with frequent merges.

Copy link
Copy Markdown

@liuyifei001 liuyifei001 Mar 22, 2026

Choose a reason for hiding this comment

The 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 secrets.CODECOV_API_TOKEN. Therefore, the procedure has to be changed and seems to be quite complex. Actually, push operations (mainly include merge operation) to develop are not very frequent, so it may be acceptable.

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
73 changes: 73 additions & 0 deletions .github/workflows/coverage-upload.yml
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
119 changes: 119 additions & 0 deletions .github/workflows/coverage-waiting.yml
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After uploading the coverage event, check whether it was successful.

Copy link
Copy Markdown

@liuyifei001 liuyifei001 Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Workflow Codecov Upload & Compare replies on Coverage Build. If we merge Coverage Build and Waiting Coverage project, Codecov Upload & Compare can never be triggered.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 workflow_run event to react to the Codecov upload completion instead of active polling, which would free up the runner slot. If polling is necessary (e.g., Codecov doesn't trigger workflow events), consider reducing intervalMs to 60s and maxAttempts to 30 (30 min total) — Codecov usually reports within 10-15 minutes.

Copy link
Copy Markdown

@liuyifei001 liuyifei001 Mar 22, 2026

Choose a reason for hiding this comment

The 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:

  1. The Codecov report depends on the completion of the Coverage Build job.
  2. The Coverage Build job is configured with a maximum runtime of 60 minutes.
  3. Therefore, the waiting job must exceed this duration.

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}.`
);
Loading
Loading