diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2fc16f3155..e37d120311 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/coverage-base-upload.yml b/.github/workflows/coverage-base-upload.yml new file mode 100644 index 0000000000..1c63fac81f --- /dev/null +++ b/.github/workflows/coverage-base-upload.yml @@ -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@v5 + 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 + 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@v5 + 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 diff --git a/.github/workflows/coverage-pr-gen-artifact.yml b/.github/workflows/coverage-pr-gen-artifact.yml new file mode 100644 index 0000000000..f18758395d --- /dev/null +++ b/.github/workflows/coverage-pr-gen-artifact.yml @@ -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@v5 + 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 diff --git a/.github/workflows/coverage-pr-upload.yml b/.github/workflows/coverage-pr-upload.yml new file mode 100644 index 0000000000..f8c411374f --- /dev/null +++ b/.github/workflows/coverage-pr-upload.yml @@ -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@v5 + 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: 'open', + head: `${headOwner}:${headBranch}` + }); + if (pulls.length > 0) { + const pr = pulls[0]; + 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 open pull request found for ${headOwner}:${headBranch}`); + } + + - 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 diff --git a/.github/workflows/coverage-project-waiting.yml b/.github/workflows/coverage-project-waiting.yml new file mode 100644 index 0000000000..53c25ec6e6 --- /dev/null +++ b/.github/workflows/coverage-project-waiting.yml @@ -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 + 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}.` + ); \ No newline at end of file diff --git a/.github/workflows/math-check.yml b/.github/workflows/math-check.yml index 0f0255815d..dfc26f7ce2 100644 --- a/.github/workflows/math-check.yml +++ b/.github/workflows/math-check.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Check for java.lang.Math usage id: check-math @@ -55,14 +55,14 @@ jobs: - name: Upload findings if: steps.check-math.outputs.math_found == 'true' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: math-usage-report path: math_usage.txt - name: Create comment if: github.event_name == 'pull_request' && steps.check-math.outputs.math_found == 'true' - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: | const fs = require('fs'); diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml new file mode 100644 index 0000000000..e1597f7dff --- /dev/null +++ b/.github/workflows/pr-build.yml @@ -0,0 +1,164 @@ +name: PR Build + +on: + pull_request: + branches: [ 'master','develop', 'release_**' ] + types: [ opened, synchronize, reopened ] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + + build-macos: + name: Build macos26 (JDK ${{ matrix.java }} / ${{ matrix.arch }}) + #if: github.event.action != 'edited' && !failure() && !cancelled() + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - java: '8' + runner: macos-26-intel + arch: x86_64 + - java: '17' + runner: macos-26 + arch: aarch64 + + steps: + - uses: actions/checkout@v5 + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v5 + with: + java-version: ${{ matrix.java }} + distribution: 'temurin' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: macos26-${{ matrix.arch }}-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }} + restore-keys: macos26-${{ matrix.arch }}-gradle- + + - name: Build + run: ./gradlew clean build --no-daemon + + build-ubuntu: + name: Build ubuntu24 (JDK 17 / aarch64) + #if: github.event.action != 'edited' && !failure() && !cancelled() + runs-on: ubuntu-24.04-arm + timeout-minutes: 60 + + steps: + - uses: actions/checkout@v5 + + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + + - name: Check Java version + run: java -version + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ubuntu24-aarch64-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }} + restore-keys: ubuntu24-aarch64-gradle- + + - name: Build + run: ./gradlew clean build --no-daemon + + docker-build-rockylinux: + name: Build rockylinux (JDK 8 / x86_64) + #if: github.event.action != 'edited' && !failure() && !cancelled() + runs-on: ubuntu-latest + timeout-minutes: 60 + + container: + image: rockylinux:8 + + env: + GRADLE_USER_HOME: /github/home/.gradle + LANG: en_US.UTF-8 + LC_ALL: en_US.UTF-8 + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Install dependencies (Rocky 8 + JDK8) + run: | + set -euxo pipefail + dnf -y install java-1.8.0-openjdk-devel git wget unzip which jq bc curl glibc-langpack-en + dnf -y groupinstall "Development Tools" + + - name: Check Java version + run: java -version + + - name: Cache Gradle + uses: actions/cache@v4 + with: + path: | + /github/home/.gradle/caches + /github/home/.gradle/wrapper + key: rockylinux-x86_64-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }} + restore-keys: | + rockylinux-x86_64-gradle- + + - name: Stop Gradle daemon + run: ./gradlew --stop || true + + - name: Build + run: ./gradlew clean build --no-daemon + + docker-build-debian11: + name: Build debian11 (JDK 8 / x86_64) + #if: github.event.action != 'edited' && !failure() && !cancelled() + runs-on: ubuntu-latest + timeout-minutes: 60 + + container: + image: eclipse-temurin:8-jdk # base image is Debian 11 (Bullseye) + + defaults: + run: + shell: bash + + env: + GRADLE_USER_HOME: /github/home/.gradle + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Install dependencies (Debian + build tools) + run: | + set -euxo pipefail + apt-get update + apt-get install -y git wget unzip build-essential curl jq + + - name: Check Java version + run: java -version + + - name: Cache Gradle + uses: actions/cache@v4 + with: + path: | + /github/home/.gradle/caches + /github/home/.gradle/wrapper + key: debian11-x86_64-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }} + restore-keys: | + debian11-x86_64-gradle- + + - name: Build + run: ./gradlew clean build --no-daemon diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 82fa4bcd35..918bfab800 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Validate PR title and description - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const title = context.payload.pull_request.title; @@ -101,10 +101,10 @@ jobs: runs-on: ubuntu-24.04-arm steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -123,163 +123,9 @@ jobs: - name: Upload Checkstyle reports if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: checkstyle-reports path: | framework/build/reports/checkstyle/ plugins/build/reports/checkstyle/ - - build: - name: Build ${{ matrix.os-name }}(JDK ${{ matrix.java }} / ${{ matrix.arch }}) - if: github.event.action != 'edited' && !failure() - needs: [pr-lint, checkstyle] - runs-on: ${{ matrix.runner }} - timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - include: - - java: '8' - runner: ubuntu-latest - os-name: ubuntu - arch: x86_64 - - java: '17' - runner: ubuntu-24.04-arm - os-name: ubuntu - arch: aarch64 - - java: '8' - runner: macos-26-intel - os-name: macos - arch: x86_64 - - java: '17' - runner: macos-26 - os-name: macos - arch: aarch64 - - steps: - - uses: actions/checkout@v4 - - - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v4 - with: - java-version: ${{ matrix.java }} - distribution: 'temurin' - - - name: Cache Gradle packages - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-${{ matrix.arch }}-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }} - restore-keys: ${{ runner.os }}-${{ matrix.arch }}-gradle- - - - name: Build - run: ./gradlew clean build --no-daemon - - docker-build-rockylinux: - name: Build rockylinux (JDK 8 / x86_64) - if: github.event.action != 'edited' && !failure() - needs: [pr-lint, checkstyle] - runs-on: ubuntu-latest - timeout-minutes: 60 - - container: - image: rockylinux:8 - - env: - GRADLE_USER_HOME: /github/home/.gradle - LANG: en_US.UTF-8 - LC_ALL: en_US.UTF-8 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install dependencies (Rocky 8 + JDK8) - run: | - set -euxo pipefail - dnf -y install java-1.8.0-openjdk-devel git wget unzip which jq bc curl glibc-langpack-en - dnf -y groupinstall "Development Tools" - - - name: Check Java version - run: java -version - - - name: Cache Gradle - uses: actions/cache@v4 - with: - path: | - /github/home/.gradle/caches - /github/home/.gradle/wrapper - key: ${{ runner.os }}-rockylinux-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-rockylinux-gradle- - - - 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 - - - name: Generate JaCoCo report - run: ./gradlew jacocoTestReport --no-daemon --no-build-cache - - - name: Upload JaCoCo artifacts - uses: actions/upload-artifact@v4 - with: - name: jacoco-rockylinux - path: | - **/build/reports/jacoco/test/jacocoTestReport.xml - **/build/reports/** - **/build/test-results/** - if-no-files-found: error - - docker-build-debian11: - name: Build debian11 (JDK 8 / x86_64) - if: github.event.action != 'edited' && !failure() - needs: [pr-lint, checkstyle] - runs-on: ubuntu-latest - timeout-minutes: 60 - - container: - image: eclipse-temurin:8-jdk # base image is Debian 11 (Bullseye) - - defaults: - run: - shell: bash - - env: - GRADLE_USER_HOME: /github/home/.gradle - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install dependencies (Debian + build tools) - run: | - set -euxo pipefail - apt-get update - apt-get install -y git wget unzip build-essential curl jq - - - name: Check Java version - run: java -version - - - name: Cache Gradle - uses: actions/cache@v4 - with: - path: | - /github/home/.gradle/caches - /github/home/.gradle/wrapper - key: ${{ runner.os }}-debian11-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-debian11-gradle- - - - name: Grant execute permission - run: chmod +x gradlew - - - name: Build - run: ./gradlew clean build --no-daemon --no-build-cache diff --git a/.github/workflows/system-test.yml b/.github/workflows/system-test.yml index 4c234d4bf3..2ce05d82f2 100644 --- a/.github/workflows/system-test.yml +++ b/.github/workflows/system-test.yml @@ -19,20 +19,20 @@ jobs: steps: - name: Set up JDK 8 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '8' distribution: 'temurin' - name: Clone system-test - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: tronprotocol/system-test ref: release_workflow path: system-test - name: Checkout java-tron - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: path: java-tron @@ -85,7 +85,7 @@ jobs: - name: Upload FullNode log if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: fullnode-log path: java-tron/fullnode.log diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..e8e752a9b6 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,36 @@ +# Post a Codecov comment on pull requests. If don't need comment, use comment: false, else use following +comment: false +#comment: +# # Show coverage diff, flags table, and changed files in the PR comment +# layout: "diff, flags, files" +# # Update existing comment if present, otherwise create a new one +# behavior: default +# # Post a comment even when coverage numbers do not change +# require_changes: false +# # Do not require a base report before posting the comment +# require_base: false +# # Require the PR head commit to have a coverage report +# require_head: true +# # Show both project coverage and patch coverage in the PR comment +# hide_project_coverage: false + +codecov: + # Do not wait for all CI checks to pass before sending notifications + require_ci_to_pass: false + notify: + wait_for_ci: false + +coverage: + status: + project: # PR coverage/project UI + default: + # Compare against the base branch automatically + target: auto + + # Allow a small coverage drop tolerance + threshold: 0.02% + patch: off +# patch: # PR coverage/patch UI +# default: +# # No minimum requirement for changed-line coverage +# target: 0% \ No newline at end of file