Skip to content

Commit

Permalink
Add support for wildcard entries in the ownership file
Browse files Browse the repository at this point in the history
  • Loading branch information
simonschiller committed Feb 16, 2022
1 parent 27ffd2c commit 948f3af
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ In larger organizations, Gradle modules and dependencies are often owned by spec
# Identifier for dependencies -> dependency shorthand without the version
- identifier: androidx.core:core
owner: core-team

# Wildcard identifier -> matches multiple components (can be both modules or dependencies)
- identifier: :sample:wildcard:*
owner: wildcard-team
```
This ownership file can be maintained manually, but in most cases it will be more practical to generate it during the build. You can point Ruler to your YAML file in the `build.gradle`, you can also configure default owners for components missing from the ownership file:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,38 @@ import com.spotify.ruler.models.ComponentType
* @param defaultOwner Owner which should be used if no explicit owner is defined.
*/
class OwnershipInfo(entries: List<OwnershipEntry>, private val defaultOwner: String) {
private val ownershipEntries = entries.associate { entry -> entry.identifier to entry.owner }
private val explicitOwnershipEntries = mutableMapOf<String, String>()
private val wildcardOwnershipEntries = mutableMapOf<String, String>()

// Differentiate between explicit (full match) entries and wildcard entries
init {
entries.forEach { (identifier, owner) ->
if (identifier.endsWith('*')) {
wildcardOwnershipEntries[identifier.substringBeforeLast('*')] = owner
} else {
explicitOwnershipEntries[identifier] = owner
}
}
}

/**
* Returns the owner of a given [component]. If the owner has no explicit owner, the [defaultOwner] will be returned
* instead.
*/
fun getOwner(component: String, componentType: ComponentType): String = when (componentType) {
ComponentType.INTERNAL -> ownershipEntries[component] ?: defaultOwner
ComponentType.EXTERNAL -> ownershipEntries[component.substringBeforeLast(':')] ?: defaultOwner
fun getOwner(component: String, componentType: ComponentType): String {
val owner = when (componentType) {
ComponentType.INTERNAL -> explicitOwnershipEntries[component]
ComponentType.EXTERNAL -> explicitOwnershipEntries[component.substringBeforeLast(':')]
}
return owner ?: getWildcardOwner(component) ?: defaultOwner
}

/** Tries to find the owner for a component with the given [component] identifier based on all wildcard entries. */
private fun getWildcardOwner(component: String): String? {
val identifier = wildcardOwnershipEntries.keys
.filter(component::startsWith) // Find all identifiers that match the wildcard
.maxByOrNull(String::length) // Take the longest one because that one is the most specific

return wildcardOwnershipEntries[identifier]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ import org.junit.jupiter.api.Test
class OwnershipInfoTest {
private val entries = listOf(
OwnershipEntry(":foo:bar", "internal-component-owner"),
OwnershipEntry(":wildcard:foo:*", "internal-wildcard-foo-owner"),
OwnershipEntry(":wildcard:*", "internal-wildcard-owner"),
OwnershipEntry(":wildcard:foo:bar", "internal-wildcard-foo-bar-owner"),
OwnershipEntry("com.spotify:main", "external-component-owner"),
OwnershipEntry("com.wildcard.spotify:*", "external-wildcard-spotify-owner"),
OwnershipEntry("com.wildcard.*", "external-wildcard-owner"),
OwnershipEntry("com.wildcard.spotify:foo", "external-wildcard-spotify-foo-owner"),
)
private val ownershipInfo = OwnershipInfo(entries, "default-owner")

Expand All @@ -45,6 +51,24 @@ class OwnershipInfoTest {
assertThat(owner).isEqualTo("default-owner")
}

@Test
fun `Internal component owner is found for wildcard entries`() {
val owner = ownershipInfo.getOwner(":wildcard:test", ComponentType.INTERNAL)
assertThat(owner).isEqualTo("internal-wildcard-owner")
}

@Test
fun `Internal component owner is found for more specific wildcard entries`() {
val owner = ownershipInfo.getOwner(":wildcard:foo:test", ComponentType.INTERNAL)
assertThat(owner).isEqualTo("internal-wildcard-foo-owner")
}

@Test
fun `Internal component owner is found for explicit entry when wildcard is present`() {
val owner = ownershipInfo.getOwner(":wildcard:foo:bar", ComponentType.INTERNAL)
assertThat(owner).isEqualTo("internal-wildcard-foo-bar-owner")
}

@Test
fun `External component owner is found`() {
val owner = ownershipInfo.getOwner("com.spotify:main:1.0.0", ComponentType.EXTERNAL)
Expand All @@ -62,4 +86,22 @@ class OwnershipInfoTest {
val owner = ownershipInfo.getOwner("com.spotify:main:1.0.0", ComponentType.INTERNAL)
assertThat(owner).isEqualTo("default-owner")
}

@Test
fun `External component owner is found for wildcard entries`() {
val owner = ownershipInfo.getOwner("com.wildcard.test:test:1.0.0", ComponentType.EXTERNAL)
assertThat(owner).isEqualTo("external-wildcard-owner")
}

@Test
fun `External component owner is found for more specific wildcard entries`() {
val owner = ownershipInfo.getOwner("com.wildcard.spotify:test:1.0.0", ComponentType.EXTERNAL)
assertThat(owner).isEqualTo("external-wildcard-spotify-owner")
}

@Test
fun `External component owner is found for explicit entry when wildcard is present`() {
val owner = ownershipInfo.getOwner("com.wildcard.spotify:foo:1.0.0", ComponentType.EXTERNAL)
assertThat(owner).isEqualTo("external-wildcard-spotify-foo-owner")
}
}

0 comments on commit 948f3af

Please sign in to comment.