You've already forked element-x-android
mirror of
https://github.com/element-hq/element-x-android.git
synced 2026-10-23 18:49:00 +03:00
508 lines
24 KiB
YAML
508 lines
24 KiB
YAML
name: Code Quality Checks
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
pull_request:
|
|
merge_group:
|
|
push:
|
|
branches: [ main, develop ]
|
|
|
|
permissions: {}
|
|
|
|
# Enrich gradle.properties for CI/CD
|
|
env:
|
|
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
|
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
|
|
|
|
jobs:
|
|
checkScript:
|
|
name: Search for forbidden patterns
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Free Disk Space (Ubuntu)
|
|
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
|
with:
|
|
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
|
|
tool-cache: true
|
|
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
|
|
android: false
|
|
dotnet: true
|
|
haskell: true
|
|
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
|
|
large-packages: false
|
|
docker-images: true
|
|
swap-storage: false
|
|
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
- name: Add SSH private keys for submodule repositories
|
|
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
with:
|
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
|
- name: Clone submodules
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
run: git submodule update --init --recursive
|
|
- name: Run code quality check suite
|
|
run: ./tools/check/check_code_quality.sh
|
|
|
|
checkScreenshot:
|
|
name: Search for invalid screenshot files
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: 3.15
|
|
- name: Search for invalid screenshot files
|
|
run: ./tools/test/checkInvalidScreenshots.py
|
|
|
|
checkDependencies:
|
|
name: Search for invalid dependencies
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
- name: Use JDK 21
|
|
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
|
|
with:
|
|
distribution: 'temurin' # See 'Supported distributions' for available options
|
|
java-version: '21'
|
|
- name: Configure gradle
|
|
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
|
|
with:
|
|
cache-read-only: true # Only the debug build and unit tests jobs write the Gradle cache, see build.yml
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: 3.15
|
|
- name: Search for invalid dependencies
|
|
run: ./tools/dependencies/checkDependencies.py
|
|
|
|
# Code checks
|
|
konsist:
|
|
name: Konsist tests
|
|
runs-on: ubuntu-latest
|
|
# Allow all jobs on main and develop. Just one per PR.
|
|
concurrency:
|
|
group: ${{ github.ref == 'refs/heads/main' && format('check-konsist-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-konsist-develop-{0}', github.sha) || format('check-konsist-{0}', github.ref) }}
|
|
cancel-in-progress: true
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
# Ensure we are building the branch and not the branch after being merged on develop
|
|
# https://github.com/actions/checkout/issues/881
|
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
|
persist-credentials: false
|
|
- name: Add SSH private keys for submodule repositories
|
|
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
with:
|
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
|
- name: Clone submodules
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
run: git submodule update --init --recursive
|
|
- name: Use JDK 21
|
|
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
|
|
with:
|
|
distribution: 'temurin' # See 'Supported distributions' for available options
|
|
java-version: '21'
|
|
- name: Configure gradle
|
|
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
|
|
with:
|
|
cache-read-only: true # Only the debug build and unit tests jobs write the Gradle cache, see build.yml
|
|
- name: Run Konsist tests
|
|
run: ./gradlew :tests:konsist:testDebugUnitTest $CI_GRADLE_ARG_PROPERTIES --no-daemon
|
|
- name: Upload reports
|
|
if: always()
|
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
with:
|
|
name: konsist-report
|
|
path: |
|
|
**/build/reports/**/*.*
|
|
|
|
compose:
|
|
name: Compose tests
|
|
runs-on: ubuntu-latest
|
|
# Allow all jobs on main and develop. Just one per PR.
|
|
concurrency:
|
|
group: ${{ github.ref == 'refs/heads/main' && format('check-compose-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-compose-develop-{0}', github.sha) || format('check-compose-{0}', github.ref) }}
|
|
cancel-in-progress: true
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
# Ensure we are building the branch and not the branch after being merged on develop
|
|
# https://github.com/actions/checkout/issues/881
|
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
|
persist-credentials: false
|
|
- name: Add SSH private keys for submodule repositories
|
|
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
with:
|
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
|
- name: Clone submodules
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
run: git submodule update --init --recursive
|
|
- name: Use JDK 21
|
|
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
|
|
with:
|
|
distribution: 'temurin' # See 'Supported distributions' for available options
|
|
java-version: '21'
|
|
- name: Configure gradle
|
|
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
|
|
with:
|
|
cache-read-only: true # Only the debug build and unit tests jobs write the Gradle cache, see build.yml
|
|
- name: Run compose tests
|
|
run: ./tools/compose/check_stability.sh
|
|
|
|
lint:
|
|
name: Android lint check
|
|
runs-on: ubuntu-latest
|
|
# Allow all jobs on main and develop. Just one per PR.
|
|
concurrency:
|
|
group: ${{ github.ref == 'refs/heads/main' && format('check-lint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-lint-develop-{0}', github.sha) || format('check-lint-{0}', github.ref) }}
|
|
cancel-in-progress: true
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
# Ensure we are building the branch and not the branch after being merged on develop
|
|
# https://github.com/actions/checkout/issues/881
|
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
|
persist-credentials: false
|
|
- name: Add SSH private keys for submodule repositories
|
|
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
with:
|
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
|
- name: Clone submodules
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
run: git submodule update --init --recursive
|
|
- name: Use JDK 21
|
|
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
|
|
with:
|
|
distribution: 'temurin' # See 'Supported distributions' for available options
|
|
java-version: '21'
|
|
- name: Configure gradle
|
|
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
|
|
with:
|
|
cache-read-only: true # Only the debug build and unit tests jobs write the Gradle cache, see build.yml
|
|
- name: Build Gplay Debug
|
|
run: ./gradlew :app:compileGplayDebugKotlin $CI_GRADLE_ARG_PROPERTIES
|
|
- name: Build Fdroid Debug
|
|
run: ./gradlew :app:compileFdroidDebugKotlin $CI_GRADLE_ARG_PROPERTIES
|
|
- name: Run lint
|
|
run: ./gradlew :app:lintGplayDebug :app:lintFdroidDebug lintDebug $CI_GRADLE_ARG_PROPERTIES --continue
|
|
- name: Upload reports
|
|
if: always()
|
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
with:
|
|
name: linting-report
|
|
path: |
|
|
**/build/reports/**/*.*
|
|
|
|
detekt:
|
|
name: Detekt checks
|
|
runs-on: ubuntu-latest
|
|
# Allow all jobs on main and develop. Just one per PR.
|
|
concurrency:
|
|
group: ${{ github.ref == 'refs/heads/main' && format('check-detekt-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-detekt-develop-{0}', github.sha) || format('check-detekt-{0}', github.ref) }}
|
|
cancel-in-progress: true
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
# Ensure we are building the branch and not the branch after being merged on develop
|
|
# https://github.com/actions/checkout/issues/881
|
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
|
persist-credentials: false
|
|
- name: Add SSH private keys for submodule repositories
|
|
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
with:
|
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
|
- name: Clone submodules
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
run: git submodule update --init --recursive
|
|
- name: Use JDK 21
|
|
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
|
|
with:
|
|
distribution: 'temurin' # See 'Supported distributions' for available options
|
|
java-version: '21'
|
|
- name: Configure gradle
|
|
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
|
|
with:
|
|
cache-read-only: true # Only the debug build and unit tests jobs write the Gradle cache, see build.yml
|
|
- name: Run Detekt
|
|
run: ./gradlew detekt $CI_GRADLE_ARG_PROPERTIES --no-daemon --continue
|
|
- name: Upload reports
|
|
if: always()
|
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
with:
|
|
name: detekt-report
|
|
path: |
|
|
**/build/reports/**/*.*
|
|
|
|
ktlint:
|
|
name: Ktlint checks
|
|
runs-on: ubuntu-latest
|
|
# Allow all jobs on main and develop. Just one per PR.
|
|
concurrency:
|
|
group: ${{ github.ref == 'refs/heads/main' && format('check-ktlint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-ktlint-develop-{0}', github.sha) || format('check-ktlint-{0}', github.ref) }}
|
|
cancel-in-progress: true
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
# Ensure we are building the branch and not the branch after being merged on develop
|
|
# https://github.com/actions/checkout/issues/881
|
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
|
persist-credentials: false
|
|
- name: Add SSH private keys for submodule repositories
|
|
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
with:
|
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
|
- name: Clone submodules
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
run: git submodule update --init --recursive
|
|
- name: Use JDK 21
|
|
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
|
|
with:
|
|
distribution: 'temurin' # See 'Supported distributions' for available options
|
|
java-version: '21'
|
|
- name: Configure gradle
|
|
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
|
|
with:
|
|
cache-read-only: true # Only the debug build and unit tests jobs write the Gradle cache, see build.yml
|
|
- name: Run Ktlint check
|
|
run: ./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES --continue
|
|
- name: Upload reports
|
|
if: always()
|
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
with:
|
|
name: ktlint-report
|
|
path: |
|
|
**/build/reports/**/*.*
|
|
|
|
docs:
|
|
name: Doc checks
|
|
runs-on: ubuntu-latest
|
|
# Allow all jobs on main and develop. Just one per PR.
|
|
concurrency:
|
|
group: ${{ github.ref == 'refs/heads/main' && format('check-docs-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-docs-develop-{0}', github.sha) || format('check-docs-{0}', github.ref) }}
|
|
cancel-in-progress: true
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
# Ensure we are building the branch and not the branch after being merged on develop
|
|
# https://github.com/actions/checkout/issues/881
|
|
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
|
persist-credentials: false
|
|
- name: Add SSH private keys for submodule repositories
|
|
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
with:
|
|
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
|
- name: Clone submodules
|
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
|
run: git submodule update --init --recursive
|
|
- name: Run docs check
|
|
# This is equivalent to `./gradlew checkDocs`, but we avoid having to install java and gradle
|
|
run: python3 ./tools/docs/generate_toc.py --verify ./*.md docs/**/*.md
|
|
|
|
# Note: to auto fix issues you can use the following command:
|
|
# shellcheck -f diff <files> | git apply
|
|
shellcheck:
|
|
name: Check shell scripts
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
- name: Run shellcheck
|
|
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0
|
|
with:
|
|
severity: warning
|
|
|
|
zizmor:
|
|
name: Run zizmor
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
- uses: zizmorcore/zizmor-action@f9597c869b2e67095f9b527070297a13a6efe928 # v0.7.0
|
|
|
|
upload_reports:
|
|
name: Project Check Suite
|
|
runs-on: ubuntu-latest
|
|
needs: [konsist, lint, ktlint, detekt]
|
|
# Run even if a previous job failed: that is precisely when there are issues to report.
|
|
if: ${{ !cancelled() }}
|
|
permissions:
|
|
# Required to list the files modified by the pull request.
|
|
pull-requests: read
|
|
steps:
|
|
- name: Download reports from previous jobs
|
|
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
- name: Annotate the lint, ktlint and detekt issues
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
with:
|
|
script: |
|
|
// GitHub only surfaces a handful of annotations per level and per step, so cap what we emit
|
|
// and let the job summary carry the full list.
|
|
const MAX_ANNOTATIONS = 20;
|
|
const MAX_SUMMARY_ROWS = 200;
|
|
|
|
function unescapeXml(value) {
|
|
return value
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, "\"")
|
|
.replace(/'/g, "'")
|
|
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code)))
|
|
.replace(/&/g, "&");
|
|
}
|
|
|
|
function attribute(tag, name) {
|
|
const match = new RegExp(`\\b${name}="([^"]*)"`).exec(tag);
|
|
return match ? unescapeXml(match[1]) : undefined;
|
|
}
|
|
|
|
// Paths in the reports are absolute paths on the runner which generated them, i.e.
|
|
// /home/runner/work/<repo>/<repo>/<path>.
|
|
const workspace = `/work/${context.repo.repo}/${context.repo.repo}/`;
|
|
function repoRelative(path) {
|
|
const index = path.indexOf(workspace);
|
|
return index === -1 ? path : path.slice(index + workspace.length);
|
|
}
|
|
|
|
// Checkstyle XML, as produced by ktlint and detekt:
|
|
// <file name="…"><error line=".." column=".." severity=".." message=".." source=".."/></file>
|
|
function parseCheckstyle(content) {
|
|
const issues = [];
|
|
const fileRegex = /<file\s+([^>]*?)(\/>|>([\s\S]*?)<\/file>)/g;
|
|
let file;
|
|
while ((file = fileRegex.exec(content)) !== null) {
|
|
const path = attribute(file[1], "name");
|
|
if (!path) continue;
|
|
const errorRegex = /<error\s+([^>]*?)\/?>/g;
|
|
let error;
|
|
while ((error = errorRegex.exec(file[3] ?? "")) !== null) {
|
|
issues.push({
|
|
file: repoRelative(path),
|
|
line: Number(attribute(error[1], "line")) || 1,
|
|
column: Number(attribute(error[1], "column")) || undefined,
|
|
severity: (attribute(error[1], "severity") ?? "warning").toLowerCase(),
|
|
rule: attribute(error[1], "source"),
|
|
message: attribute(error[1], "message") ?? "",
|
|
});
|
|
}
|
|
}
|
|
return issues;
|
|
}
|
|
|
|
// Android lint XML, which is not Checkstyle:
|
|
// <issue id=".." severity=".." message=".."><location file=".." line=".." column=".."/></issue>
|
|
function parseAndroidLint(content) {
|
|
const issues = [];
|
|
const issueRegex = /<issue\s+([^>]*?)(\/>|>([\s\S]*?)<\/issue>)/g;
|
|
let issue;
|
|
while ((issue = issueRegex.exec(content)) !== null) {
|
|
const location = /<location\s+([^>]*?)\/?>/.exec(issue[3] ?? "");
|
|
if (!location) continue;
|
|
const path = attribute(location[1], "file");
|
|
if (!path) continue;
|
|
issues.push({
|
|
file: repoRelative(path),
|
|
line: Number(attribute(location[1], "line")) || 1,
|
|
column: Number(attribute(location[1], "column")) || undefined,
|
|
severity: (attribute(issue[1], "severity") ?? "warning").toLowerCase(),
|
|
rule: attribute(issue[1], "id"),
|
|
message: attribute(issue[1], "message") ?? "",
|
|
});
|
|
}
|
|
return issues;
|
|
}
|
|
|
|
const reports = await (await glob.create("**/reports/**/*.xml")).glob();
|
|
core.info(`Found ${reports.length} XML report(s).`);
|
|
|
|
const fs = require("fs");
|
|
let issues = [];
|
|
for (const report of reports) {
|
|
const content = fs.readFileSync(report, "utf8");
|
|
if (content.includes("<checkstyle")) {
|
|
issues.push(...parseCheckstyle(content));
|
|
} else if (content.includes("<issues")) {
|
|
issues.push(...parseAndroidLint(content));
|
|
}
|
|
}
|
|
|
|
// Only report on the files touched by the pull request.
|
|
const pr = context.payload.pull_request;
|
|
if (pr) {
|
|
const files = await github.paginate(github.rest.pulls.listFiles, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: pr.number
|
|
});
|
|
const changed = new Set(files.map(file => file.filename));
|
|
issues = issues.filter(issue => changed.has(issue.file));
|
|
}
|
|
|
|
const seen = new Set();
|
|
issues = issues.filter(issue => {
|
|
const key = `${issue.file}:${issue.line}:${issue.rule}:${issue.message}`;
|
|
if (seen.has(key)) return false;
|
|
seen.add(key);
|
|
return true;
|
|
});
|
|
|
|
if (issues.length === 0) {
|
|
core.info("No issue found in the files modified by this pull request.");
|
|
return;
|
|
}
|
|
|
|
const level = severity => {
|
|
if (severity === "error" || severity === "fatal") return "error";
|
|
if (severity === "warning") return "warning";
|
|
return "notice";
|
|
};
|
|
const rank = { error: 0, warning: 1, notice: 2 };
|
|
issues.sort((a, b) =>
|
|
rank[level(a.severity)] - rank[level(b.severity)] ||
|
|
a.file.localeCompare(b.file) ||
|
|
a.line - b.line
|
|
);
|
|
|
|
issues.slice(0, MAX_ANNOTATIONS).forEach(issue => {
|
|
const annotation = {
|
|
title: issue.rule,
|
|
file: issue.file,
|
|
startLine: issue.line,
|
|
startColumn: issue.column,
|
|
};
|
|
core[level(issue.severity)](issue.message, annotation);
|
|
});
|
|
|
|
core.summary.addHeading(`${issues.length} code quality issue(s)`, 3);
|
|
core.summary.addTable([
|
|
[{ data: "Severity", header: true }, { data: "File", header: true }, { data: "Rule", header: true }, { data: "Message", header: true }],
|
|
...issues.slice(0, MAX_SUMMARY_ROWS).map(issue => [
|
|
level(issue.severity),
|
|
`${issue.file}:${issue.line}`,
|
|
issue.rule ?? "",
|
|
issue.message,
|
|
]),
|
|
]);
|
|
if (issues.length > MAX_SUMMARY_ROWS) {
|
|
core.summary.addRaw(`…and ${issues.length - MAX_SUMMARY_ROWS} more, see the uploaded reports.`, true);
|
|
}
|
|
await core.summary.write();
|