Skip to content

Commit 416bd38

Browse files
committed
use delegating shard tasks for connectedCheck
These shard tasks collectively depend upon all Android `connectedCheck` tasks in the entire project. Each shard depends upon the `connectedCheck` tasks of some subset of Android projects. Projects are assigned to a shard by counting the number of `@Test` annotations within their `androidTest` directory, then associating those projects to a shard in a round-robin fashion. These shards are invoked in CI using a GitHub Actions matrix. If the number of shards changes, the `connectedCheckShardMatrixYamlUpdate` task can automatically update the workflow file so that they're all invoked. The shard tasks are invoked as: ```shell # roughly 1/3 of the tests ./gradlew connectedCheckShard1 # the second third ./gradlew connectedCheckShard2 # the last third ./gradlew connectedCheckShard3 ``` The task filtering we currently use in CI (`./gradlew fooShard1 -x :my-project:foo`) will still work here, however the "cost" of the excluded task's tests is still accounted for when the sharding is performed.
1 parent 155cbfe commit 416bd38

File tree

8 files changed

+555
-142
lines changed

8 files changed

+555
-142
lines changed

.github/workflows/kotlin.yml

Lines changed: 51 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,22 @@ concurrency :
1414

1515
jobs :
1616

17-
build-all:
18-
name: Build all
19-
runs-on: macos-latest
20-
steps:
21-
- uses: actions/checkout@v3
22-
23-
- name: main build
24-
uses: ./.github/actions/gradle-task
25-
with:
26-
task: compileKotlin compileDebugKotlin
27-
write-cache-key: main-build-artifacts
17+
build-all :
18+
name : Build all
19+
runs-on : macos-latest
20+
steps :
21+
- uses : actions/checkout@v3
22+
23+
- name : main build
24+
uses : ./.github/actions/gradle-task
25+
with :
26+
task : compileKotlin assembleDebug
27+
write-cache-key : main-build-artifacts
2828

2929
dokka :
3030
name : Assemble & Dokka
3131
runs-on : ubuntu-latest
32-
needs: build-all
32+
needs : build-all
3333
steps :
3434
- uses : actions/checkout@v3
3535

@@ -39,6 +39,19 @@ jobs :
3939
task : siteDokka
4040
write-cache-key : main-build-artifacts
4141

42+
shards-and-version :
43+
name : Shard Matrix Yaml
44+
runs-on : ubuntu-latest
45+
steps :
46+
- uses : actions/checkout@v3
47+
48+
- name : check published artifacts
49+
uses : ./.github/actions/gradle-task-with-commit
50+
with :
51+
check-task : connectedCheckShardMatrixYamlCheck checkVersionIsSnapshot
52+
fix-task : connectedCheckShardMatrixYamlUpdate checkVersionIsSnapshot
53+
write-cache-key : build-logic
54+
4255
artifacts-check :
4356
name : ArtifactsCheck
4457
# the `artifactsCheck` task has to run on macOS in order to see the iOS KMP artifacts
@@ -101,7 +114,7 @@ jobs :
101114
android-lint :
102115
name : Android Lint
103116
runs-on : ubuntu-latest
104-
needs: build-all
117+
needs : build-all
105118
timeout-minutes : 20
106119
steps :
107120
- uses : actions/checkout@v3
@@ -114,17 +127,16 @@ jobs :
114127
check :
115128
name : Check
116129
runs-on : ubuntu-latest
117-
needs: build-all
130+
needs : build-all
118131
timeout-minutes : 20
119132
steps :
120133
- uses : actions/checkout@v3
121134
- name : Check with Gradle
122135
uses : ./.github/actions/gradle-task
123136
with :
124137
task : |
125-
checkVersionIsSnapshot
126138
allTests
127-
test
139+
test
128140
--continue
129141
restore-cache-key : build-logic
130142
write-cache-key : main-build-artifacts
@@ -286,43 +298,31 @@ jobs :
286298
name : renderpass-counting-results-${{ matrix.api-level }}
287299
path : ./**/build/reports/androidTests/connected/**
288300

289-
build-instrumentation-tests :
290-
name : Build Instrumentation tests
291-
runs-on : macos-latest
292-
needs: build-all
293-
timeout-minutes : 45
294-
steps :
295-
- uses : actions/checkout@v3
296-
297-
- name : Build instrumented tests
298-
uses : ./.github/actions/gradle-task
299-
with :
300-
task : assembleDebugAndroidTest
301-
restore-cache-key : main-build-artifacts
302-
write-cache-key : androidTest-build-artifacts
303-
304301
instrumentation-tests :
305302
name : Instrumentation tests
306-
needs: build-instrumentation-tests
307303
runs-on : macos-latest
308304
timeout-minutes : 45
309305
strategy :
310306
# Allow tests to continue on other devices if they fail on one device.
311307
fail-fast : false
312308
matrix :
309+
# Unclear that older versions actually honor command to disable animation.
310+
# Newer versions are reputed to be too slow: https://github.com/ReactiveCircus/android-emulator-runner/issues/222
313311
api-level :
314312
- 29
315-
# Unclear that older versions actually honor command to disable animation.
316-
# Newer versions are reputed to be too slow: https://github.com/ReactiveCircus/android-emulator-runner/issues/222
313+
### <start-connected-check-shards>
314+
shardNum : [ 1, 2, 3 ]
315+
### <end-connected-check-shards>
317316
steps :
318317
- uses : actions/checkout@v3
319318

320-
# This really just pulls the cache from the dependency job
319+
## Build before running tests, using cache.
321320
- name : Build instrumented tests
322321
uses : ./.github/actions/gradle-task
323322
with :
324323
task : assembleDebugAndroidTest
325-
restore-cache-key : androidTest-build-artifacts
324+
write-cache-key : androidTest-build-artifacts
325+
restore-cache-key : main-build-artifacts
326326

327327
## Actual task
328328
- name : Instrumentation Tests
@@ -333,16 +333,16 @@ jobs :
333333
api-level : ${{ matrix.api-level }}
334334
arch : x86_64
335335
# Skip the benchmarks as this is running on emulators
336-
script : ./gradlew connectedCheck -x :benchmarks:dungeon-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-poetry:connectedCheck
336+
script : ./gradlew connectedCheckShard${{ matrix.shardNum }} -x :benchmarks:dungeon-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-poetry:connectedCheck
337337

338338
- name : Upload results
339339
if : ${{ always() }}
340340
uses : actions/upload-artifact@v3
341341
with :
342-
name : instrumentation-test-results-${{ matrix.api-level }}
342+
name : instrumentation-test-results-${{ matrix.api-level }}-shard_${{ matrix.shardNum }}
343343
path : ./**/build/reports/androidTests/connected/**
344344

345-
conflate-renderings-instrumentation-tests :
345+
runtime-instrumentation-tests :
346346
name : Conflate Stale Renderings Instrumentation tests
347347
runs-on : macos-latest
348348
timeout-minutes : 45
@@ -352,110 +352,20 @@ jobs :
352352
matrix :
353353
api-level :
354354
- 29
355-
# Unclear that older versions actually honor command to disable animation.
356-
# Newer versions are reputed to be too slow: https://github.com/ReactiveCircus/android-emulator-runner/issues/222
355+
### <start-connected-check-shards>
356+
shardNum : [ 1, 2, 3 ]
357+
### <end-connected-check-shards>
358+
runtime : [ conflate, baseline-stateChange, conflate-stateChange ]
357359
steps :
358360
- uses : actions/checkout@v3
359361

360-
## Build before running tests, using cache.
361-
- name : Build instrumented tests
362-
uses : ./.github/actions/gradle-task
363-
with :
364-
# Unfortunately I don't think we can key this cache based on our project property so
365-
# we clean and rebuild.
366-
task : clean assembleDebugAndroidTest -Pworkflow.runtime=conflate
367-
368-
## Actual task
369-
- name : Instrumentation Tests
370-
uses : reactivecircus/android-emulator-runner@v2
371-
with :
372-
# @ychescale9 suspects Galaxy Nexus is the fastest one
373-
profile : Galaxy Nexus
374-
api-level : ${{ matrix.api-level }}
375-
arch : x86_64
376-
# Skip the benchmarks as this is running on emulators
377-
script : ./gradlew connectedCheck -x :benchmarks:dungeon-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-poetry:connectedCheck -Pworkflow.runtime=conflate
378-
379-
- name : Upload results
380-
if : ${{ always() }}
381-
uses : actions/upload-artifact@v3
382-
with :
383-
name : instrumentation-test-results-${{ matrix.api-level }}
384-
path : ./**/build/reports/androidTests/connected/**
385-
386-
stateChange-runtime-instrumentation-tests :
387-
name : Render on State Change Only Instrumentation tests
388-
runs-on : macos-latest
389-
timeout-minutes : 45
390-
strategy :
391-
# Allow tests to continue on other devices if they fail on one device.
392-
fail-fast : false
393-
matrix :
394-
api-level :
395-
- 29
396-
# Unclear that older versions actually honor command to disable animation.
397-
# Newer versions are reputed to be too slow: https://github.com/ReactiveCircus/android-emulator-runner/issues/222
398-
steps :
399-
- uses : actions/checkout@v3
400-
- name : set up JDK 11
401-
uses : actions/setup-java@v3
402-
with :
403-
distribution : 'zulu'
404-
java-version : 11
405-
406-
## Build before running tests, using cache.
407-
- name : Build instrumented tests
408-
uses : ./.github/actions/gradle-task
409-
with :
410-
# Unfortunately I don't think we can key this cache based on our project property so
411-
# we clean and rebuild.
412-
task : clean assembleDebugAndroidTest -Pworkflow.runtime=baseline-stateChange
413-
414-
## Actual task
415-
- name : Instrumentation Tests
416-
uses : reactivecircus/android-emulator-runner@v2
417-
with :
418-
# @ychescale9 suspects Galaxy Nexus is the fastest one
419-
profile : Galaxy Nexus
420-
api-level : ${{ matrix.api-level }}
421-
arch : x86_64
422-
# Skip the benchmarks as this is running on emulators
423-
script : ./gradlew connectedCheck -x :benchmarks:dungeon-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-poetry:connectedCheck -Pworkflow.runtime=baseline-stateChange
424-
425-
- name : Upload results
426-
if : ${{ always() }}
427-
uses : actions/upload-artifact@v3
428-
with :
429-
name : stateChange-instrumentation-test-results-${{ matrix.api-level }}
430-
path : ./**/build/reports/androidTests/connected/**
431-
432-
conflate-stateChange-runtime-instrumentation-tests :
433-
name : Render on State Change Only and Conflate Stale Renderings Instrumentation tests
434-
runs-on : macos-latest
435-
timeout-minutes : 45
436-
strategy :
437-
# Allow tests to continue on other devices if they fail on one device.
438-
fail-fast : false
439-
matrix :
440-
api-level :
441-
- 29
442-
# Unclear that older versions actually honor command to disable animation.
443-
# Newer versions are reputed to be too slow: https://github.com/ReactiveCircus/android-emulator-runner/issues/222
444-
steps :
445-
- uses : actions/checkout@v3
446-
- name : set up JDK 11
447-
uses : actions/setup-java@v3
448-
with :
449-
distribution : 'zulu'
450-
java-version : 11
451-
452-
## Build before running tests, using cache.
362+
# This really just pulls the cache from the dependency job
453363
- name : Build instrumented tests
454364
uses : ./.github/actions/gradle-task
455365
with :
456-
# Unfortunately I don't think we can key this cache based on our project property so
457-
# we clean and rebuild.
458-
task : clean assembleDebugAndroidTest -Pworkflow.runtime=conflate-stateChange
366+
task : assembleDebugAndroidTest -Pworkflow.runtime=${{matrix.runtime}}
367+
write-cache-key : androidTest-build-artifacts-${{matrix.runtime}}
368+
restore-cache-key : main-build-artifacts
459369

460370
## Actual task
461371
- name : Instrumentation Tests
@@ -466,13 +376,13 @@ jobs :
466376
api-level : ${{ matrix.api-level }}
467377
arch : x86_64
468378
# Skip the benchmarks as this is running on emulators
469-
script : ./gradlew connectedCheck -x :benchmarks:dungeon-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-poetry:connectedCheck -Pworkflow.runtime=conflate-stateChange
379+
script : ./gradlew connectedCheckShard${{ matrix.shardNum }} -Pworkflow.runtime=${{matrix.runtime}} -x :benchmarks:dungeon-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-benchmark:connectedCheck -x :benchmarks:performance-poetry:complex-poetry:connectedCheck
470380

471381
- name : Upload results
472382
if : ${{ always() }}
473383
uses : actions/upload-artifact@v3
474384
with :
475-
name : conflate-stateChange-instrumentation-test-results-${{ matrix.api-level }}
385+
name : conflate-instrumentation-test-results-${{ matrix.api-level }}-shard_${{ matrix.shardNum }}
476386
path : ./**/build/reports/androidTests/connected/**
477387

478388
all-green :
@@ -483,8 +393,6 @@ jobs :
483393
- api-check
484394
- artifacts-check
485395
- check
486-
- conflate-renderings-instrumentation-tests
487-
- conflate-stateChange-runtime-instrumentation-tests
488396
- dependency-guard
489397
- dokka
490398
- instrumentation-tests
@@ -495,7 +403,8 @@ jobs :
495403
- jvm-stateChange-runtime-test
496404
- ktlint
497405
- performance-tests
498-
- stateChange-runtime-instrumentation-tests
406+
- runtime-instrumentation-tests
407+
- shards-and-version
499408
- tutorials
500409

501410
steps :

build-logic/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies {
2323
implementation(libs.squareup.moshi)
2424
implementation(libs.squareup.moshi.adapters)
2525
implementation(libs.vanniktech.publish)
26+
implementation(libs.java.diff.utils)
2627

2728
ksp(libs.squareup.moshi.codegen)
2829
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.squareup.workflow1.buildsrc
2+
3+
import com.github.difflib.text.DiffRow.Tag
4+
import com.github.difflib.text.DiffRowGenerator
5+
import com.squareup.workflow1.buildsrc.Color.Companion.colorized
6+
import com.squareup.workflow1.buildsrc.Color.LIGHT_GREEN
7+
import com.squareup.workflow1.buildsrc.Color.LIGHT_YELLOW
8+
9+
fun diffString(oldStr: String, newStr: String): String {
10+
11+
return buildString {
12+
13+
val rows = DiffRowGenerator.create()
14+
.showInlineDiffs(true)
15+
.inlineDiffByWord(true)
16+
.oldTag { _: Boolean? -> "" }
17+
.newTag { _: Boolean? -> "" }
18+
.build()
19+
.generateDiffRows(oldStr.lines(), newStr.lines())
20+
21+
val linePadding = rows.size.toString().length + 1
22+
23+
rows.forEachIndexed { line, diffRow ->
24+
if (diffRow.tag != Tag.EQUAL) {
25+
append("line ${line.inc().toString().padEnd(linePadding)} ")
26+
}
27+
28+
if (diffRow.tag == Tag.CHANGE || diffRow.tag == Tag.DELETE) {
29+
appendLine("-- ${diffRow.oldLine}".colorized(LIGHT_YELLOW))
30+
}
31+
if (diffRow.tag == Tag.CHANGE) {
32+
append(" " + " ".repeat(linePadding))
33+
}
34+
if (diffRow.tag == Tag.CHANGE || diffRow.tag == Tag.INSERT) {
35+
appendLine("++ ${diffRow.newLine}".colorized(LIGHT_GREEN))
36+
}
37+
}
38+
}
39+
}
40+
41+
@Suppress("MagicNumber")
42+
internal enum class Color(val code: Int) {
43+
LIGHT_GREEN(92),
44+
LIGHT_YELLOW(93);
45+
46+
companion object {
47+
48+
private val supported = "win" !in System.getProperty("os.name").lowercase()
49+
50+
fun String.colorized(color: Color) = if (supported) {
51+
"\u001B[${color.code}m$this\u001B[0m"
52+
} else {
53+
this
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)