22
33on :
44 push :
5- branches : [ main, master ]
5+ branches : [main, master]
66 pull_request :
7- branches : [ '**' ]
7+ branches : ["**" ]
88
99concurrency :
1010 group : ci-${{ github.workflow }}-${{ github.ref }}
@@ -15,16 +15,34 @@ jobs:
1515 name : Lint, Test, Coverage
1616 runs-on : ubuntu-latest
1717 timeout-minutes : 30
18+ env :
19+ ANDROID_HOME : /usr/local/lib/android/sdk
20+ ANDROID_SDK_ROOT : /usr/local/lib/android/sdk
21+ permissions :
22+ contents : read
23+ checks : write
24+ pull-requests : write
1825
1926 steps :
2027 - name : Checkout
2128 uses : actions/checkout@v4
2229
23- - name : Setup Java 21
30+ - name : Cache Android SDK
31+ uses : actions/cache@v4
32+ with :
33+ path : |
34+ ${{ env.ANDROID_SDK_ROOT }}
35+ ~/.android
36+ key : ${{ runner.os }}-android-sdk-36-buildtools-35-0-0
37+ restore-keys : |
38+ ${{ runner.os }}-android-sdk-
39+
40+ - name : Setup Java 17
2441 uses : actions/setup-java@v4
2542 with :
2643 distribution : temurin
27- java-version : ' 21'
44+ java-version : " 17"
45+ cache : gradle
2846
2947 - name : Setup Android SDK
3048 uses : android-actions/setup-android@v3
@@ -45,14 +63,60 @@ jobs:
4563 run : ./gradlew --version
4664
4765 - name : Lint
48- run : ./gradlew --stacktrace lint
66+ run : ./gradlew --stacktrace --build-cache lint
67+
68+ - name : Publish lint results to PR (masked-edittext)
69+ if : >-
70+ always() &&
71+ github.event_name == 'pull_request' &&
72+ github.event.pull_request.head.repo.full_name == github.repository &&
73+ hashFiles('masked-edittext/build/reports/lint-results-debug.xml') != ''
74+ uses : dvdandroid/action-android-lint@master
75+ with :
76+ github_token : ${{ secrets.GITHUB_TOKEN }}
77+ lint_xml_file : masked-edittext/build/reports/lint-results-debug.xml
78+ reporter : github-pr-check
79+
80+ - name : Publish lint results to PR (demo_app)
81+ if : >-
82+ always() &&
83+ github.event_name == 'pull_request' &&
84+ github.event.pull_request.head.repo.full_name == github.repository &&
85+ hashFiles('demo_app/build/reports/lint-results-debug.xml') != ''
86+ uses : dvdandroid/action-android-lint@master
87+ with :
88+ github_token : ${{ secrets.GITHUB_TOKEN }}
89+ lint_xml_file : demo_app/build/reports/lint-results-debug.xml
90+ reporter : github-pr-check
91+
92+ - name : Publish lint results to PR (demo_app_compose)
93+ if : >-
94+ always() &&
95+ github.event_name == 'pull_request' &&
96+ github.event.pull_request.head.repo.full_name == github.repository &&
97+ hashFiles('demo_app_compose/build/reports/lint-results-debug.xml') != ''
98+ uses : dvdandroid/action-android-lint@master
99+ with :
100+ github_token : ${{ secrets.GITHUB_TOKEN }}
101+ lint_xml_file : demo_app_compose/build/reports/lint-results-debug.xml
102+ reporter : github-pr-check
103+
104+ - name : Publish lint reports
105+ if : always()
106+ uses : actions/upload-artifact@v4
107+ with :
108+ name : lint-reports
109+ path : |
110+ **/build/reports/lint-results-*.html
111+ **/build/reports/lint-results-*.xml
112+ **/build/reports/lint-results-*.txt
49113
50114 - name : Unit tests (all modules)
51- run : ./gradlew --stacktrace test
115+ run : ./gradlew --stacktrace --build-cache test
52116
53117 - name : Coverage (Kover) for library module
54118 run : |
55- ./gradlew --stacktrace :masked-edittext:koverXmlReport :masked-edittext:koverHtmlReport
119+ ./gradlew --stacktrace --build-cache :masked-edittext:koverXmlReport :masked-edittext:koverHtmlReport
56120
57121 - name : Publish test reports
58122 if : always()
@@ -76,32 +140,50 @@ jobs:
76140 shell : bash
77141 run : |
78142 set -euo pipefail
79- REPORT_XML="masked-edittext/build/reports/kover/xml/report.xml"
80- if [[ -f "$REPORT_XML" ]]; then
81- python3 - << 'PY'
82- import xml.etree.ElementTree as ET
83- import os
84- path = os.environ.get('REPORT_XML', 'masked-edittext/build/reports/kover/xml/report.xml')
85- tree = ET.parse(path)
86- root = tree.getroot()
87- line = next((c for c in root.findall('counter') if c.get('type')=='LINE'), None)
88- inst = next((c for c in root.findall('counter') if c.get('type')=='INSTRUCTION'), None)
89- def pct(counter) :
90- if counter is None : return None
91- missed = int(counter.get('missed', '0'))
92- covered = int(counter.get('covered', '0'))
93- total = missed + covered
94- return (covered * 100.0 / total) if total else 0.0
95- lines = pct(line)
96- instructions = pct(inst)
97- summary = []
98- if lines is not None :
99- summary.append(f"Line coverage : {lines:.2f}%")
100- if instructions is not None :
101- summary.append(f"Instruction coverage : {instructions:.2f}%")
102- print("\n".join(summary) or "No coverage counters found.")
103- PY
104- else
105- echo "Coverage report not found at $REPORT_XML"
106- fi >> "$GITHUB_STEP_SUMMARY"
107-
143+ SUMMARY_FILE="${GITHUB_STEP_SUMMARY:-/dev/stdout}"
144+ report_candidates=(
145+ "masked-edittext/build/reports/kover/xml/report.xml"
146+ "masked-edittext/build/reports/kover/report.xml"
147+ "masked-edittext/build/reports/kover/xmlDebug/report.xml"
148+ "masked-edittext/build/reports/kover/xmlRelease/report.xml"
149+ )
150+ REPORT_XML=""
151+ for candidate in "${report_candidates[@]}"; do
152+ if [[ -f "$candidate" ]]; then
153+ REPORT_XML="$candidate"
154+ break
155+ fi
156+ done
157+ if [[ -n "$REPORT_XML" ]]; then
158+ REPORT_XML="$REPORT_XML" python3 - << 'PY'
159+ import xml.etree.ElementTree as ET
160+ import os
161+ import sys
162+ path = os.environ["REPORT_XML"]
163+ try:
164+ tree = ET.parse(path)
165+ except Exception as exc:
166+ print(f"Failed to parse coverage report at {path}: {exc}")
167+ sys.exit(0)
168+ root = tree.getroot()
169+ line = next((c for c in root.findall('counter') if c.get('type')=='LINE'), None)
170+ inst = next((c for c in root.findall('counter') if c.get('type')=='INSTRUCTION'), None)
171+ def pct(counter):
172+ if counter is None: return None
173+ missed = int(counter.get('missed', '0'))
174+ covered = int(counter.get('covered', '0'))
175+ total = missed + covered
176+ return (covered * 100.0 / total) if total else 0.0
177+ lines = pct(line)
178+ instructions = pct(inst)
179+ summary = []
180+ if lines is not None:
181+ summary.append(f"Line coverage: {lines:.2f}%")
182+ if instructions is not None:
183+ summary.append(f"Instruction coverage: {instructions:.2f}%")
184+ print("\n".join(summary) or f"No coverage counters found in {path}.")
185+ PY
186+ else
187+ echo "Coverage report not found. Checked:"
188+ printf '%s\n' "${report_candidates[@]}"
189+ fi >> "$SUMMARY_FILE"
0 commit comments