ci: test all testcases #33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Check | |
| on: | |
| pull_request: | |
| branches: [ 'develop_ci', 'release_**' ] | |
| types: [ opened, edited, synchronize, reopened ] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| pr-lint: | |
| name: PR Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Validate PR title and description | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const title = context.payload.pull_request.title; | |
| const body = context.payload.pull_request.body; | |
| const errors = []; | |
| const warnings = []; | |
| const allowedTypes = ['feat','fix','refactor','docs','style','test','chore','ci','perf','build','revert']; | |
| const knownScopes = [ | |
| 'framework','chainbase','actuator','consensus','common','crypto','plugins','protocol', | |
| 'net','db','vm','tvm','api','jsonrpc','rpc','http','event','config', | |
| 'block','proposal','trie','log','metrics','test','docker','version', | |
| 'freezeV2','DynamicEnergy','stable-coin','reward','lite','toolkit' | |
| ]; | |
| // 1. Title length check | |
| if (!title || title.trim().length < 10) { | |
| errors.push('PR title is too short (minimum 10 characters).'); | |
| } | |
| if (title && title.length > 72) { | |
| errors.push(`PR title is too long (${title.length}/72 characters).`); | |
| } | |
| // 2. Conventional format check | |
| const conventionalRegex = /^(feat|fix|refactor|docs|style|test|chore|ci|perf|build|revert)(\([^)]+\))?:\s\S.*/; | |
| if (title && !conventionalRegex.test(title)) { | |
| errors.push( | |
| 'PR title must follow conventional format: `type(scope): description`\n' + | |
| ' Allowed types: ' + allowedTypes.map(t => `\`${t}\``).join(', ') + '\n' + | |
| ' Example: `feat(tvm): add blob opcodes`' | |
| ); | |
| } | |
| // 3. No trailing period | |
| if (title && title.endsWith('.')) { | |
| errors.push('PR title should not end with a period (.).'); | |
| } | |
| // 4. Description part should not start with a capital letter | |
| if (title) { | |
| const descMatch = title.match(/^\w+(?:\([^)]+\))?:\s*(.+)/); | |
| if (descMatch) { | |
| const desc = descMatch[1]; | |
| if (/^[A-Z]/.test(desc)) { | |
| errors.push('Description should not start with a capital letter.'); | |
| } | |
| } | |
| } | |
| // 5. Scope validation (warning only) | |
| if (title) { | |
| const scopeMatch = title.match(/^\w+\(([^)]+)\):/); | |
| if (scopeMatch && !knownScopes.includes(scopeMatch[1])) { | |
| warnings.push(`Unknown scope \`${scopeMatch[1]}\`. See CONTRIBUTING.md for known scopes.`); | |
| } | |
| } | |
| // 6. PR description check | |
| if (!body || body.trim().length < 20) { | |
| errors.push('PR description is too short or empty (minimum 20 characters). Please describe what this PR does and why.'); | |
| } | |
| // Output warnings | |
| for (const w of warnings) { | |
| core.warning(w); | |
| } | |
| // Output result | |
| if (errors.length > 0) { | |
| const docLink = 'See [CONTRIBUTING.md](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/blob/develop/CONTRIBUTING.md#pull-request-guidelines) for details.'; | |
| const message = '### PR Lint Failed\n\n' + errors.map(e => `- ${e}`).join('\n') + '\n\n' + docLink; | |
| core.setFailed(message); | |
| } else { | |
| core.info('PR lint passed.'); | |
| } | |
| # build: | |
| # name: Build ${{ matrix.os-name }}(JDK ${{ matrix.java }} / ${{ matrix.arch }}) | |
| # needs: pr-lint | |
| # runs-on: ${{ matrix.runner }} | |
| # 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-latest | |
| # 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) | |
| needs: pr-lint | |
| runs-on: ubuntu-latest | |
| container: | |
| image: rockylinux:8 | |
| env: | |
| GRADLE_USER_HOME: /github/home/.gradle | |
| 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 | |
| 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: Prepare checkstyle config copy | |
| run: | | |
| set -euxo pipefail | |
| cp -f config/checkstyle/checkStyle.xml config/checkstyle/checkStyleAll.xml || true | |
| - 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 | |
| #run: | | |
| # ./gradlew clean build -x test --no-daemon --no-build-cache | |
| # ./gradlew framework:test --tests org.tron.core.zksnark.ShieldedReceiveTest | |
| - 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) | |
| # needs: pr-lint | |
| # runs-on: ubuntu-latest | |
| # | |
| # container: | |
| # image: eclipse-temurin:8-jdk | |
| # | |
| # 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 | |
| coverage-gate: | |
| name: Coverage Gate (from rockylinux build) | |
| needs: docker-build-rockylinux | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: Checkout code (needed by codecov-action for git context) | |
| uses: actions/checkout@v4 | |
| - name: Download JaCoCo artifacts (rockylinux) | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: jacoco-rockylinux | |
| path: artifacts/jacoco-rockylinux | |
| - name: List downloaded reports | |
| run: | | |
| set -eux | |
| echo "JaCoCo XML reports found:" | |
| find artifacts/jacoco-rockylinux -name jacocoTestReport.xml | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| directory: artifacts/jacoco-rockylinux | |
| override_commit: ${{ github.event.pull_request.head.sha }} | |
| override_branch: ${{ github.event.pull_request.head.ref }} | |
| override_pr: ${{ github.event.pull_request.number }} | |
| verbose: true | |
| fail_ci_if_error: true | |
| - name: Install tools | |
| run: sudo apt-get update && sudo apt-get install -y jq bc curl | |
| - name: Wait for Codecov processing | |
| env: | |
| CODECOV_API_TOKEN: ${{ secrets.CODECOV_API_TOKEN }} | |
| CODECOV_OWNER: ${{ github.repository_owner }} | |
| CODECOV_REPO: ${{ github.event.repository.name }} | |
| COMMIT_ID: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| set -euxo pipefail | |
| API_URL="https://api.codecov.io/api/v2/github/${CODECOV_OWNER}/repos/${CODECOV_REPO}/commits/${COMMIT_ID}" | |
| MAX_ATTEMPTS=20 | |
| INTERVAL=30 | |
| for i in $(seq 1 $MAX_ATTEMPTS); do | |
| echo "=== Polling attempt $i / $MAX_ATTEMPTS ===" | |
| http_code=$(curl -sS -o /tmp/poll.json -w '%{http_code}' \ | |
| -H "Authorization: Bearer ${CODECOV_API_TOKEN}" \ | |
| "$API_URL") | |
| if [ "$http_code" = "200" ]; then | |
| state=$(jq -r '.state // "unknown"' /tmp/poll.json) | |
| echo "Commit processing state: $state" | |
| if [ "$state" = "complete" ]; then | |
| echo "Codecov has finished processing." | |
| exit 0 | |
| fi | |
| else | |
| echo "HTTP $http_code — commit not yet available." | |
| cat /tmp/poll.json 2>/dev/null || true | |
| fi | |
| if [ "$i" -lt "$MAX_ATTEMPTS" ]; then | |
| sleep "$INTERVAL" | |
| fi | |
| done | |
| echo "Timed out waiting for Codecov (${MAX_ATTEMPTS} x ${INTERVAL}s)." | |
| exit 1 | |
| - name: Coverage gate via Codecov REST API | |
| env: | |
| CODECOV_API_TOKEN: ${{ secrets.CODECOV_API_TOKEN }} | |
| CODECOV_OWNER: ${{ github.repository_owner }} | |
| CODECOV_REPO: ${{ github.event.repository.name }} | |
| COMMIT_ID: ${{ github.event.pull_request.head.sha }} | |
| BASE_BRANCH: ${{ github.event.pull_request.base.ref }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: | | |
| set -euxo pipefail | |
| API_BASE="https://api.codecov.io/api/v2/github/${CODECOV_OWNER}/repos/${CODECOV_REPO}" | |
| AUTH="Authorization: Bearer ${CODECOV_API_TOKEN}" | |
| # Helper: GET with error handling | |
| api_get() { | |
| local url="$1" | |
| local http_code | |
| http_code=$(curl -sS -o /tmp/api_out.json -w '%{http_code}' \ | |
| -H "$AUTH" "$url") | |
| if [ "$http_code" != "200" ]; then | |
| echo "ERROR: GET $url => HTTP $http_code" >&2 | |
| cat /tmp/api_out.json >&2 | |
| return 1 | |
| fi | |
| cat /tmp/api_out.json | |
| } | |
| # 1) Current commit coverage | |
| echo "=== 1. Current commit coverage (sha: ${COMMIT_ID}) ===" | |
| commit_resp=$(api_get "${API_BASE}/totals/?sha=${COMMIT_ID}") | |
| self_cov=$(echo "$commit_resp" | jq -r '.totals.coverage // 0') | |
| echo "self_cov = ${self_cov}%" | |
| # 2) Base branch head coverage | |
| echo "=== 2. Base branch coverage (branch: ${BASE_BRANCH}) ===" | |
| base_resp=$(api_get "${API_BASE}/totals/?branch=${BASE_BRANCH}") | |
| base_branch_cov=$(echo "$base_resp" | jq -r '.totals.coverage // 0') | |
| echo "base_branch_cov = ${base_branch_cov}%" | |
| # 3) PR comparison — patch coverage | |
| echo "=== 3. PR #${PR_NUMBER} comparison ===" | |
| compare_resp=$(api_get "${API_BASE}/compare/?pullid=${PR_NUMBER}") | |
| patch_cov=$(echo "$compare_resp" | jq -r '.totals.patch.coverage // 0') | |
| impacted_files=$(echo "$compare_resp" | jq -r '(.files // []) | length') | |
| echo "patch_cov = ${patch_cov}%" | |
| echo "impacted_files = ${impacted_files}" | |
| # ===== Gate Rules ===== | |
| # Rule 1: current commit must have valid coverage | |
| if [ "$(echo "$self_cov <= 0" | bc)" -eq 1 ]; then | |
| echo "FAIL: Could not retrieve valid coverage for commit ${COMMIT_ID}." | |
| exit 1 | |
| fi | |
| # Rule 2: overall coverage must not decrease vs base branch | |
| if [ "$(echo "$self_cov < $base_branch_cov" | bc)" -eq 1 ]; then | |
| echo "FAIL: Overall coverage decreased!" | |
| echo " Current commit : ${self_cov}%" | |
| echo " Base branch : ${base_branch_cov}%" | |
| echo "Please add unit tests to maintain coverage." | |
| exit 1 | |
| fi | |
| # Rule 3: patch coverage on changed files >= 80% | |
| if [ "$impacted_files" -gt 0 ] && [ "$(echo "$patch_cov > 0" | bc)" -eq 1 ]; then | |
| if [ "$(echo "$patch_cov < 80" | bc)" -eq 1 ]; then | |
| echo "FAIL: Patch coverage is ${patch_cov}% (minimum 80%)." | |
| echo "Please add tests for new/changed code." | |
| exit 1 | |
| fi | |
| else | |
| echo "No impacted files or no patch data; skipping patch coverage check." | |
| fi | |
| echo "" | |
| echo "All coverage gates passed!" | |
| echo " Current commit : ${self_cov}%" | |
| echo " Base branch : ${base_branch_cov}%" | |
| echo " Patch coverage : ${patch_cov}%" | |
| checkstyle: | |
| name: Checkstyle | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up JDK 8 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '8' | |
| distribution: 'temurin' | |
| - name: Cache Gradle packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/gradle-wrapper.properties') }} | |
| restore-keys: ${{ runner.os }}-gradle- | |
| - name: Run Checkstyle | |
| run: ./gradlew :framework:checkstyleMain :framework:checkstyleTest :plugins:checkstyleMain | |
| - name: Upload Checkstyle reports | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: checkstyle-reports | |
| path: | | |
| framework/build/reports/checkstyle/ | |
| plugins/build/reports/checkstyle/ | |