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

Granularize skippy configs for future work #678

Merged
merged 28 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8edf144
Granularize skippy configs for future work
ZacSweers Dec 8, 2023
db1acf6
Spotless
ZacSweers Dec 8, 2023
d0f78bc
Make computer single-config again
ZacSweers Dec 11, 2023
97c3cca
Parallelize multi-config + support merging and global overlays
ZacSweers Dec 11, 2023
c0ece98
Spotless
ZacSweers Dec 11, 2023
52be905
Extract SkippyOutput
ZacSweers Dec 11, 2023
cd6bbfd
Extract SkippyRunner
ZacSweers Dec 11, 2023
6b83331
Move context control up
ZacSweers Dec 11, 2023
509f2d8
Switch to Okio APIs
ZacSweers Dec 11, 2023
e9bd19a
Extract TestProject
ZacSweers Dec 11, 2023
6b5c146
Start basic SkippyRunnerTest
ZacSweers Dec 11, 2023
8b6d68e
Expose global accessor
ZacSweers Dec 11, 2023
84cceab
Spotless
ZacSweers Dec 11, 2023
c151c5f
Add more tests
ZacSweers Dec 12, 2023
2678800
Fix up naming use
ZacSweers Dec 12, 2023
227965c
Fix included builds
ZacSweers Dec 12, 2023
93db0d9
Write config to diagnostics too
ZacSweers Dec 12, 2023
c71471e
Wire in debug and mergeOutputs
ZacSweers Dec 12, 2023
a7c3496
Various improvements and fixes
ZacSweers Dec 12, 2023
7450d29
Pre-compute project dependency metadata
ZacSweers Dec 12, 2023
0fcc114
Spotless
ZacSweers Dec 12, 2023
930fc51
TODO for continuing tomorrow
ZacSweers Dec 12, 2023
0cedbcd
Don't start undispatched
ZacSweers Dec 12, 2023
bf8e3c9
Final fixes
ZacSweers Dec 12, 2023
8bccc45
Make computeInParallel configurable
ZacSweers Dec 12, 2023
d617058
Update log
ZacSweers Dec 12, 2023
2efd12b
Merge branch 'main' into z/granularSkippyConfigs
ZacSweers Dec 12, 2023
5c81d9a
Fix sort deps
ZacSweers Dec 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .fleet/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"backend.maxHeapSizeMb": 1487
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import com.jraska.module.graph.DependencyGraph
import kotlin.time.measureTimedValue
import okio.FileSystem
import okio.Path
import slack.gradle.avoidance.AffectedProjectsDefaults.DEFAULT_INCLUDE_PATTERNS
import slack.gradle.avoidance.AffectedProjectsDefaults.DEFAULT_NEVER_SKIP_PATTERNS
import slack.gradle.util.SgpLogger

/**
Expand All @@ -38,12 +36,13 @@ import slack.gradle.util.SgpLogger
* The primary input is [changedFilePaths], which is a newline-delimited list of files that have
* changed. Usually the files changed in a pull request.
*
* [includePatterns] is a list of glob patterns that are used to filter the list of changed files.
* These should usually be source files that are deemed to participate in builds (e.g. `.kt` files).
* [SkippyConfig.includePatterns] is a list of glob patterns that are used to filter the list of
* changed files. These should usually be source files that are deemed to participate in builds
* (e.g. `.kt` files).
*
* [neverSkipPatterns] is a list of glob patterns that, if matched with any changed file, indicate
* that nothing should be skipped and the full build should run. This is important for files like
* version catalog toml files or root build.gradle file changes.
* [SkippyConfig.neverSkipPatterns] is a list of glob patterns that, if matched with any changed
* file, indicate that nothing should be skipped and the full build should run. This is important
* for files like version catalog toml files or root build.gradle file changes.
*
* ### Outputs
*
Expand All @@ -56,9 +55,9 @@ import slack.gradle.util.SgpLogger
* dropbox/focus plugin, and will be a minimal list of projects needed to build the affected
* projects.
*
* With both outputs, if any "never-skippable" files [neverSkipPatterns] are changed, then no output
* file is produced and all projects are considered affected. If a file is produced but has no
* content written to it, that simply means that no projects are affected.
* With both outputs, if any "never-skippable" files [SkippyConfig.neverSkipPatterns] are changed,
* then no output file is produced and all projects are considered affected. If a file is produced
* but has no content written to it, that simply means that no projects are affected.
*
* ### Debugging
*
Expand All @@ -73,16 +72,7 @@ import slack.gradle.util.SgpLogger
* @property changedFilePaths A relative (to the repo root) path to a changed_files.txt that
* contains a newline-delimited list of changed files. This is usually computed from a GitHub PR's
* changed files.
* @property includePatterns A set of glob patterns for files to include in computing affected
* projects. This should usually be source files, build files, gradle.properties files, and other
* projects that affect builds.
* @property excludePatterns A set of glob patterns for files to exclude from computing affected
* projects. This is run _after_ [includePatterns] and can be useful for excluding files that
* would otherwise be included by an existing inclusion pattern.
* @property neverSkipPatterns A set of glob patterns that, if matched with a file, indicate that
* nothing should be skipped and [compute] will return null. This is useful for globally-affecting
* things like root build files, `libs.versions.toml`, etc. **NOTE**: This list is always merged
* with [includePatterns] as these are implicitly relevant files.
* @property configs see [SkippyConfig] docs.
* @property androidTestProjects A set of project names that are Android test projects. This is used
* to compute the [AffectedProjectsResult.affectedAndroidTestProjects] value, which can be used to
* statically determine if an instrumentation test pipeline needs to run at all.
Expand All @@ -94,9 +84,7 @@ internal class AffectedProjectsComputer(
private val dependencyGraph: () -> DependencyGraph,
private val changedFilePaths: List<Path>,
private val diagnostics: DiagnosticWriter = DiagnosticWriter.NoOp,
private val includePatterns: Set<String> = DEFAULT_INCLUDE_PATTERNS,
private val excludePatterns: Set<String> = emptySet(),
private val neverSkipPatterns: Set<String> = DEFAULT_NEVER_SKIP_PATTERNS,
private val configs: Map<String, SkippyConfig> = mapOf("default" to SkippyConfig()),
private val androidTestProjects: Set<String> = emptySet(),
private val debug: Boolean = false,
private val fileSystem: FileSystem = FileSystem.SYSTEM,
Expand All @@ -107,6 +95,14 @@ internal class AffectedProjectsComputer(
}

private fun computeImpl(): AffectedProjectsResult? {
// TODO this is not yet implemented granularly
val includePatterns: Set<String> =
configs.values.flatMapTo(mutableSetOf()) { it.includePatterns }
val neverSkipPatterns: Set<String> =
configs.values.flatMapTo(mutableSetOf()) { it.neverSkipPatterns }
val excludePatterns: Set<String> =
configs.values.flatMapTo(mutableSetOf()) { it.excludePatterns }

log("root dir path is: $rootDirPath")
check(rootDirPath.exists()) { "Root dir path $rootDirPath does not exist" }
log("changedFilePaths: $changedFilePaths")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import kotlin.time.measureTimedValue
import okio.Path.Companion.toOkioPath
import okio.Path.Companion.toPath
import org.gradle.api.DefaultTask
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
Expand All @@ -35,8 +36,6 @@ import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.UntrackedTask
import org.gradle.api.tasks.options.Option
import slack.gradle.SlackProperties
import slack.gradle.avoidance.AffectedProjectsDefaults.DEFAULT_INCLUDE_PATTERNS
import slack.gradle.avoidance.AffectedProjectsDefaults.DEFAULT_NEVER_SKIP_PATTERNS
import slack.gradle.setProperty
import slack.gradle.util.SgpLogger
import slack.gradle.util.setDisallowChanges
Expand All @@ -59,16 +58,8 @@ public abstract class ComputeAffectedProjectsTask : DefaultTask(), DiagnosticWri
project.objects.property(Boolean::class.java).convention(false)

@get:Input
public val includePatterns: SetProperty<String> =
project.objects.setProperty<String>().convention(DEFAULT_INCLUDE_PATTERNS)

@get:Input
public val excludePatterns: SetProperty<String> =
project.objects.setProperty<String>().convention(emptySet())

@get:Input
public val neverSkipPatterns: SetProperty<String> =
project.objects.setProperty<String>().convention(DEFAULT_NEVER_SKIP_PATTERNS)
public val configs: NamedDomainObjectContainer<SkippyGradleConfig> =
project.objects.domainObjectContainer(SkippyGradleConfig::class.java)

@get:Input
public val androidTestProjects: SetProperty<String> =
Expand Down Expand Up @@ -141,9 +132,7 @@ public abstract class ComputeAffectedProjectsTask : DefaultTask(), DiagnosticWri
DependencyGraph.create(dependencyGraph.get())
}
},
includePatterns = includePatterns.get(),
excludePatterns = excludePatterns.get(),
neverSkipPatterns = neverSkipPatterns.get(),
configs = configs.asMap.mapValues { (_, v) -> v.asSkippyConfig() },
androidTestProjects = androidTestProjects.get(),
debug = debug.get(),
diagnostics = this,
Expand Down Expand Up @@ -229,6 +218,7 @@ public abstract class ComputeAffectedProjectsTask : DefaultTask(), DiagnosticWri
rootProject: Project,
slackProperties: SlackProperties
): TaskProvider<ComputeAffectedProjectsTask> {
val extension = rootProject.extensions.create("skippy", SkippyExtension::class.java)
val configurationsToLook by lazy {
val providedConfigs = slackProperties.affectedProjectConfigurations
providedConfigs?.splitToSequence(',')?.toSet()?.let { providedConfigSet ->
Expand All @@ -246,6 +236,7 @@ public abstract class ComputeAffectedProjectsTask : DefaultTask(), DiagnosticWri

return rootProject.tasks.register(NAME, ComputeAffectedProjectsTask::class.java) {
debug.setDisallowChanges(slackProperties.debug)
configs.addAll(extension.configs)
rootDir.setDisallowChanges(project.layout.projectDirectory)
dependencyGraph.setDisallowChanges(rootProject.provider { moduleGraph })
diagnosticsDir.setDisallowChanges(project.layout.buildDirectory.dir("skippy/diagnostics"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Slack Technologies, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package slack.gradle.avoidance

import javax.inject.Inject
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Input
import slack.gradle.SlackExtensionMarker
import slack.gradle.avoidance.AffectedProjectsDefaults.DEFAULT_INCLUDE_PATTERNS
import slack.gradle.avoidance.AffectedProjectsDefaults.DEFAULT_NEVER_SKIP_PATTERNS
import slack.gradle.setProperty

@SlackExtensionMarker
public abstract class SkippyExtension @Inject constructor(objects: ObjectFactory) {
internal val configs: NamedDomainObjectContainer<SkippyGradleConfig> =
objects.domainObjectContainer(SkippyGradleConfig::class.java)

init {
// One global default
configs.maybeCreate("global")
}

public fun config(name: String, action: Action<SkippyGradleConfig>) {
action.execute(configs.maybeCreate(name))
}
}

/** A gradle representation of [SkippyConfig]. See its doc for more details. */
public abstract class SkippyGradleConfig @Inject constructor(objects: ObjectFactory) {
@get:Input
public val includePatterns: SetProperty<String> =
objects.setProperty<String>().convention(DEFAULT_INCLUDE_PATTERNS)

@get:Input
public val excludePatterns: SetProperty<String> =
objects.setProperty<String>().convention(emptySet())

@get:Input
public val neverSkipPatterns: SetProperty<String> =
objects.setProperty<String>().convention(DEFAULT_NEVER_SKIP_PATTERNS)

internal fun asSkippyConfig(): SkippyConfig {
return SkippyConfig(includePatterns.get(), excludePatterns.get(), neverSkipPatterns.get())
}
}

/**
* Represents a Skippy configuration for a specific tool.
*
* @property includePatterns A set of glob patterns for files to include in computing affected
* projects. This should usually be source files, build files, gradle.properties files, and other
* projects that affect builds.
* @property excludePatterns A set of glob patterns for files to exclude from computing affected
* projects. This is run _after_ [includePatterns] and can be useful for excluding files that
* would otherwise be included by an existing inclusion pattern.
* @property neverSkipPatterns A set of glob patterns that, if matched with a file, indicate that
* nothing should be skipped and [AffectedProjectsComputer.compute] will return null. This is
* useful for globally-affecting things like root build files, `libs.versions.toml`, etc.
* **NOTE**: This list is always merged with [includePatterns] as these are implicitly relevant
* files.
*/
public data class SkippyConfig(
public val includePatterns: Set<String> = DEFAULT_INCLUDE_PATTERNS,
public val excludePatterns: Set<String> = emptySet(),
public val neverSkipPatterns: Set<String> = DEFAULT_NEVER_SKIP_PATTERNS,
)