Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix part of #5312, part of #59: Introduce better script execution support #5313

Merged
merged 46 commits into from Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
667bf89
Fix a variety of dev platform-specific issues.
BenHenning Aug 22, 2023
fb59232
Tidy some things up, and revert fragment test.
BenHenning Aug 28, 2023
2551d4b
Fix test behavior inconsistency in test.
BenHenning Sep 11, 2023
714f3ea
Merge branch 'develop' into fix-platform-specific-issues
BenHenning Sep 11, 2023
7488b9f
Fix broken ProfileAndDeviceIdFragmentTest test.
BenHenning Sep 13, 2023
e3d4091
Merge branch 'develop' into fix-platform-specific-issues
BenHenning Oct 26, 2023
5671673
Post-merge fix.
BenHenning Oct 26, 2023
bd1466d
Merge branch 'develop' into fix-platform-specific-issues
BenHenning Jan 16, 2024
000bba3
Add ScriptBackgroundCoroutineDispatcher.
BenHenning Jan 18, 2024
79878cd
Some more robustness fixes.
BenHenning Jan 19, 2024
a5f71d6
Merge branch 'fix-platform-specific-issues' into introduce-better-scr…
BenHenning Jan 19, 2024
0b55bd4
Post-merge fixes.
BenHenning Jan 19, 2024
bd97611
Fix BUILD file lint issue.
BenHenning Jan 20, 2024
d54829d
Merge branch 'fix-platform-specific-issues' into introduce-better-scr…
BenHenning Jan 20, 2024
211487c
Merge branch 'develop' into fix-platform-specific-issues
BenHenning Feb 6, 2024
1a666a9
Post-merge fixes.
BenHenning Feb 6, 2024
a2ee5d3
Add smoke tests for instr. binaries & tests.
BenHenning Feb 7, 2024
d5b9012
Some minor refactoring for readability.
BenHenning Feb 7, 2024
e6ec869
Fixed broken instrumentation builds.
BenHenning Feb 7, 2024
9f50469
Merge branch 'develop' into fix-instrumentation-build-failure
BenHenning Feb 7, 2024
8dbf4dc
Add missing CODEOWNERS line.
BenHenning Feb 7, 2024
9c38057
Remove old files from CODEOWNERS.
BenHenning Feb 7, 2024
f2ff4e3
Merge branch 'fix-instrumentation-build-failure' into fix-platform-sp…
BenHenning Feb 7, 2024
fad48ed
Add missing tests for TestBlazeWorkspace changes.
BenHenning Feb 8, 2024
e1802c8
Add missing Firebase auth tests.
BenHenning Feb 8, 2024
00c32f6
Merge branch 'fix-instrumentation-build-failure' into fix-platform-sp…
BenHenning Feb 8, 2024
2f87f4d
Merge branch 'fix-platform-specific-issues' into introduce-better-scr…
BenHenning Feb 8, 2024
d3cbd93
Post-merge fixes for previously missed tests.
BenHenning Feb 8, 2024
13e4358
Merge branch 'fix-platform-specific-issues' into introduce-better-scr…
BenHenning Feb 8, 2024
50f41d2
Merge branch 'develop' into fix-instrumentation-build-failure
BenHenning Feb 8, 2024
c8df70e
Merge branch 'fix-instrumentation-build-failure' into fix-platform-sp…
BenHenning Feb 8, 2024
c85d421
Merge branch 'develop' into fix-instrumentation-build-failure
BenHenning Feb 9, 2024
dcf27b6
Merge branch 'fix-instrumentation-build-failure' into fix-platform-sp…
BenHenning Feb 9, 2024
2369514
Merge branch 'develop' into fix-platform-specific-issues
BenHenning Feb 9, 2024
c7ec5d4
Merge branch 'fix-platform-specific-issues' into introduce-better-scr…
BenHenning Feb 9, 2024
8de4d51
Improve new test robustness.
BenHenning Feb 9, 2024
3f69f11
Fix updated Maven license deps.
BenHenning Feb 9, 2024
2916770
Update scripts/src/javatests/org/oppia/android/scripts/common/GitClie…
BenHenning Feb 14, 2024
f2d810a
Merge branch 'develop' into fix-platform-specific-issues
BenHenning Feb 14, 2024
808f412
Merge branch 'fix-platform-specific-issues' into introduce-better-scr…
BenHenning Feb 14, 2024
6856e3c
Merge branch 'develop' into fix-platform-specific-issues
BenHenning Feb 14, 2024
5a050de
Merge branch 'fix-platform-specific-issues' into introduce-better-scr…
BenHenning Feb 14, 2024
a5eb624
Merge branch 'develop' into introduce-better-script-execution-support
BenHenning Feb 20, 2024
e0e74d5
Merge branch 'develop' into introduce-better-script-execution-support
BenHenning Mar 4, 2024
70df9b3
Merge branch 'develop' into introduce-better-script-execution-support
BenHenning Mar 14, 2024
51a96ce
Merge branch 'develop' into introduce-better-script-execution-support
seanlip Mar 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 13 additions & 2 deletions scripts/assets/maven_dependencies.textproto
Expand Up @@ -1050,8 +1050,19 @@ maven_dependency {
}
}
maven_dependency {
artifact_name: "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"
artifact_version: "1.4.1"
artifact_name: "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.3"
artifact_version: "1.4.3"
license {
license_name: "The Apache Software License, Version 2.0"
original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt"
scrapable_link {
url: "https://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
}
maven_dependency {
artifact_name: "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
artifact_version: "1.4.3"
license {
license_name: "The Apache Software License, Version 2.0"
original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt"
Expand Down
@@ -1,6 +1,8 @@
package org.oppia.android.scripts.build

import org.oppia.android.scripts.common.CommandExecutorImpl
import org.oppia.android.scripts.common.GitClient
import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher
import org.w3c.dom.Document
import org.w3c.dom.NodeList
import java.io.File
Expand Down Expand Up @@ -62,21 +64,24 @@ fun main(args: Array<String>) {
check(args.size >= 9) { USAGE_STRING }

val repoRoot = File(args[0]).also { if (!it.exists()) error("File doesn't exist: ${args[0]}") }
TransformAndroidManifest(
repoRoot = repoRoot,
sourceManifestFile = File(args[1]).also {
if (!it.exists()) {
error("File doesn't exist: ${args[1]}")
}
},
outputManifestFile = File(args[2]),
buildFlavor = args[3],
majorVersion = args[4].toIntOrNull() ?: error(USAGE_STRING),
minorVersion = args[5].toIntOrNull() ?: error(USAGE_STRING),
versionCode = args[6].toIntOrNull() ?: error(USAGE_STRING),
relativelyQualifiedApplicationClass = args[7],
baseDevelopBranchReference = args[8]
).generateAndOutputNewManifest()
ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher ->
TransformAndroidManifest(
repoRoot = repoRoot,
sourceManifestFile = File(args[1]).also {
if (!it.exists()) {
error("File doesn't exist: ${args[1]}")
}
},
outputManifestFile = File(args[2]),
buildFlavor = args[3],
majorVersion = args[4].toIntOrNull() ?: error(USAGE_STRING),
minorVersion = args[5].toIntOrNull() ?: error(USAGE_STRING),
versionCode = args[6].toIntOrNull() ?: error(USAGE_STRING),
relativelyQualifiedApplicationClass = args[7],
baseDevelopBranchReference = args[8],
scriptBgDispatcher
).generateAndOutputNewManifest()
}
}

private class TransformAndroidManifest(
Expand All @@ -88,11 +93,11 @@ private class TransformAndroidManifest(
private val minorVersion: Int,
private val versionCode: Int,
private val relativelyQualifiedApplicationClass: String,
private val baseDevelopBranchReference: String
private val baseDevelopBranchReference: String,
private val scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher
) {
private val gitClient by lazy {
GitClient(repoRoot, baseDevelopBranchReference)
}
private val commandExecutor by lazy { CommandExecutorImpl(scriptBgDispatcher) }
private val gitClient by lazy { GitClient(repoRoot, baseDevelopBranchReference, commandExecutor) }
private val documentBuilderFactory by lazy { DocumentBuilderFactory.newInstance() }
private val transformerFactory by lazy { TransformerFactory.newInstance() }

Expand Down
Expand Up @@ -5,6 +5,7 @@ import org.oppia.android.scripts.common.CommandExecutor
import org.oppia.android.scripts.common.CommandExecutorImpl
import org.oppia.android.scripts.common.GitClient
import org.oppia.android.scripts.common.ProtoStringEncoder.Companion.toCompressedBase64
import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher
import org.oppia.android.scripts.proto.AffectedTestsBucket
import java.io.File
import java.util.Locale
Expand Down Expand Up @@ -59,9 +60,10 @@ fun main(args: Array<String>) {
" '$computeAllTestsValue'"
)
}
ComputeAffectedTests().compute(
pathToRoot, pathToOutputFile, baseDevelopBranchReference, computeAllTestsSetting
)
ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher ->
ComputeAffectedTests(scriptBgDispatcher)
.compute(pathToRoot, pathToOutputFile, baseDevelopBranchReference, computeAllTestsSetting)
}
}

// Needed since the codebase isn't yet using Kotlin 1.5, so this function isn't available.
Expand All @@ -75,10 +77,11 @@ private fun String.toBooleanStrictOrNull(): Boolean? {

/** Utility used to compute affected test targets. */
class ComputeAffectedTests(
private val maxTestCountPerLargeShard: Int = MAX_TEST_COUNT_PER_LARGE_SHARD,
private val maxTestCountPerMediumShard: Int = MAX_TEST_COUNT_PER_MEDIUM_SHARD,
private val maxTestCountPerSmallShard: Int = MAX_TEST_COUNT_PER_SMALL_SHARD,
private val commandExecutor: CommandExecutor = CommandExecutorImpl()
private val scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher,
val maxTestCountPerLargeShard: Int = MAX_TEST_COUNT_PER_LARGE_SHARD,
val maxTestCountPerMediumShard: Int = MAX_TEST_COUNT_PER_MEDIUM_SHARD,
val maxTestCountPerSmallShard: Int = MAX_TEST_COUNT_PER_SMALL_SHARD,
val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher)
) {
private companion object {
private const val GENERIC_TEST_BUCKET_NAME = "generic"
Expand Down Expand Up @@ -108,7 +111,7 @@ class ComputeAffectedTests(

println("Running from directory root: $rootDirectory")

val gitClient = GitClient(rootDirectory, baseDevelopBranchReference)
val gitClient = GitClient(rootDirectory, baseDevelopBranchReference, commandExecutor)
val bazelClient = BazelClient(rootDirectory, commandExecutor)
println("Current branch: ${gitClient.currentBranch}")
println("Most recent common commit: ${gitClient.branchMergeBase}")
Expand Down
13 changes: 13 additions & 0 deletions scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel
Expand Up @@ -36,6 +36,10 @@ kt_jvm_library(
"CommandResult.kt",
],
visibility = ["//scripts:oppia_script_library_visibility"],
deps = [
":script_background_coroutine_dispatcher",
"//third_party:org_jetbrains_kotlinx_kotlinx-coroutines-core",
BenHenning marked this conversation as resolved.
Show resolved Hide resolved
],
)

kt_jvm_library(
Expand All @@ -57,3 +61,12 @@ kt_jvm_library(
"//third_party:org_jetbrains_kotlin_kotlin-stdlib-jdk8_jar",
],
)

kt_jvm_library(
name = "script_background_coroutine_dispatcher",
srcs = ["ScriptBackgroundCoroutineDispatcher.kt"],
visibility = ["//scripts:oppia_script_library_visibility"],
deps = [
"//third_party:org_jetbrains_kotlinx_kotlinx-coroutines-core",
],
)
Expand Up @@ -10,7 +10,7 @@ import java.util.Locale
*/
class BazelClient(
private val rootDirectory: File,
private val commandExecutor: CommandExecutor = CommandExecutorImpl()
private val commandExecutor: CommandExecutor
) {
/** Returns all Bazel test targets in the workspace. */
fun retrieveAllTestTargets(): List<String> {
Expand Down
@@ -1,5 +1,8 @@
package org.oppia.android.scripts.common

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import java.io.File
import java.util.concurrent.TimeUnit

Expand All @@ -11,6 +14,7 @@ const val WAIT_PROCESS_TIMEOUT_MS = 60_000L

/** Default implementation of [CommandExecutor]. */
class CommandExecutorImpl(
private val scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher,
private val processTimeout: Long = WAIT_PROCESS_TIMEOUT_MS,
private val processTimeoutUnit: TimeUnit = TimeUnit.MILLISECONDS
) : CommandExecutor {
Expand All @@ -29,7 +33,11 @@ class CommandExecutorImpl(
.directory(workingDir)
.redirectErrorStream(includeErrorOutput)
.start()
val finished = process.waitFor(processTimeout, processTimeoutUnit)
val finished = runBlocking {
CoroutineScope(scriptBgDispatcher).async {
process.waitFor(processTimeout, processTimeoutUnit)
}.await()
}
BenHenning marked this conversation as resolved.
Show resolved Hide resolved
check(finished) { "Process did not finish within the expected timeout" }
return CommandResult(
process.exitValue(),
Expand Down
Expand Up @@ -9,10 +9,9 @@ import java.io.File
*/
class GitClient(
private val workingDirectory: File,
private val baseDevelopBranchReference: String
private val baseDevelopBranchReference: String,
private val commandExecutor: CommandExecutor
) {
private val commandExecutor by lazy { CommandExecutorImpl() }

/** The commit hash of the HEAD of the local Git repository. */
val currentCommit: String by lazy { retrieveCurrentCommit() }

Expand Down
@@ -0,0 +1,86 @@
package org.oppia.android.scripts.common

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.asCoroutineDispatcher
import java.io.Closeable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext

/**
* A [CoroutineDispatcher] that's [Closeable] and particularly tailored to be easily used in scripts
* that need to perform parallel tasks for expensive IO. It's highly recommended to exclusively use
* this dispatcher over any others, and to ensure that [close] is called at the end of the script to
* avoid any potential threads hanging (causing the script to not actually close).
*
* Note that the dispatcher attempts to finish any ongoing tasks when [close] is called, but it will
* reject new tasks from being scheduled and it will force terminate if any pending tasks at the
* time of closing don't end within the configured [closeTimeout] provided.
*
* A simple example for using this dispatcher:
* ```kotlin
* ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher ->
* val deferred = CoroutineScope(scriptBgDispatcher).async {
* // Expensive task...
* }
* // IMPORTANT: The operation must be observed before use{} ends, otherwise the dispatcher will
* // close and terminate any pending tasks.
* runBlocking { deferred.await() }
* }
* ```
*
* A more complex example for I/O operations:
* ```kotlin
* ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher ->
* val deferred = CoroutineScope(scriptBgDispatcher).async {
* withContext(Dispatchers.IO) {
* // Perform I/O using Kotlin's highly parallelized I/O dispatcher, but wait for the result
* // using the background script dispatcher (since execution could continue if other I/O
* // operations need to be kicked off, or if other work can be done alongside the I/O).
* }
* }
* // IMPORTANT: The operation must be observed before use{} ends, otherwise the dispatcher will
* // close and terminate any pending tasks.
* runBlocking { deferred.await() }
* }
* ```
*
* @property closeTimeout the amount of time, in [closeTimeoutUnit] units, that should be waited
* when [close]ing this dispatcher before force-ending ongoing tasks
* @property closeTimeoutUnit the unit of time used for [closeTimeout]
*/
class ScriptBackgroundCoroutineDispatcher(
private val closeTimeout: Long = 5,
private val closeTimeoutUnit: TimeUnit = TimeUnit.SECONDS
) : CoroutineDispatcher(), Closeable {
private val threadPool by lazy { Executors.newCachedThreadPool() }
private val coroutineDispatcher by lazy { threadPool.asCoroutineDispatcher() }

override fun dispatch(context: CoroutineContext, block: Runnable) {
coroutineDispatcher.dispatch(context, block)
}

override fun close() {
threadPool.tryShutdownFully(timeout = closeTimeout, unit = closeTimeoutUnit)
coroutineDispatcher.close()
}

private companion object {
private fun ExecutorService.tryShutdownFully(timeout: Long, unit: TimeUnit) {
// Try to fully shutdown the executor service per https://stackoverflow.com/a/33690603 and
// https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html.
shutdown()
try {
if (!awaitTermination(timeout, unit)) {
shutdownNow()
check(awaitTermination(timeout, unit)) { "ExecutorService didn't fully shutdown: $this." }
}
} catch (e: InterruptedException) {
shutdownNow()
Thread.currentThread().interrupt()
}
}
}
}
Expand Up @@ -3,6 +3,7 @@ package org.oppia.android.scripts.license
import com.google.protobuf.TextFormat
import org.oppia.android.scripts.common.CommandExecutor
import org.oppia.android.scripts.common.CommandExecutorImpl
import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher
import org.oppia.android.scripts.proto.MavenDependency

/**
Expand All @@ -24,7 +25,9 @@ import org.oppia.android.scripts.proto.MavenDependency
* third_party/maven_install.json scripts/assets/maven_dependencies.pb
*/
fun main(args: Array<String>) {
MavenDependenciesListCheck(LicenseFetcherImpl()).main(args)
ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher ->
MavenDependenciesListCheck(LicenseFetcherImpl(), scriptBgDispatcher).main(args)
}
}

/**
Expand All @@ -33,7 +36,8 @@ fun main(args: Array<String>) {
*/
class MavenDependenciesListCheck(
private val licenseFetcher: LicenseFetcher,
private val commandExecutor: CommandExecutor = CommandExecutorImpl()
scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher,
private val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher)
) {

/**
Expand Down
Expand Up @@ -5,7 +5,6 @@ import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import org.oppia.android.scripts.common.BazelClient
import org.oppia.android.scripts.common.CommandExecutor
import org.oppia.android.scripts.common.CommandExecutorImpl
import org.oppia.android.scripts.maven.model.MavenListDependency
import org.oppia.android.scripts.maven.model.MavenListDependencyTree
import org.oppia.android.scripts.proto.License
Expand All @@ -24,7 +23,7 @@ private const val MAVEN_PREFIX = "@maven//:"
class MavenDependenciesRetriever(
private val rootPath: String,
private val licenseFetcher: LicenseFetcher,
private val commandExecutor: CommandExecutor = CommandExecutorImpl()
private val commandExecutor: CommandExecutor
) {

private val bazelClient by lazy {
Expand Down