Skip to content

Commit

Permalink
feat(cocoapods): Add support for Podspecs from external sources
Browse files Browse the repository at this point in the history
Signed-off-by: Sebastian Schuberth <sebastian@doubleopen.org>
  • Loading branch information
sschuberth committed Jan 7, 2025
1 parent de04788 commit 98ee1ee
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 5 deletions.
27 changes: 26 additions & 1 deletion plugins/package-managers/cocoapods/src/main/kotlin/Lockfile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.charleskorn.kaml.yamlScalar

import org.ossreviewtoolkit.plugins.packagemanagers.cocoapods.Lockfile.CheckoutOption
import org.ossreviewtoolkit.plugins.packagemanagers.cocoapods.Lockfile.Dependency
import org.ossreviewtoolkit.plugins.packagemanagers.cocoapods.Lockfile.ExternalSource
import org.ossreviewtoolkit.plugins.packagemanagers.cocoapods.Lockfile.Pod

internal data class Lockfile(
Expand All @@ -39,6 +40,9 @@ internal data class Lockfile(
/** The direct dependencies of the project. */
val dependencies: List<Dependency>,

/** Details about where external pods come from. */
val externalSources: Map<String, ExternalSource>,

/** Details about how to retrieve pods from external sources. */
val checkoutOptions: Map<String, CheckoutOption>
) {
Expand All @@ -54,6 +58,7 @@ internal data class Lockfile(
/** The direct dependencies of this pod. */
val dependencies: List<Dependency> = emptyList()
) {
val externalSource = this@Lockfile.externalSources[name]
val checkoutOption = this@Lockfile.checkoutOptions[name]
}

Expand All @@ -67,6 +72,14 @@ internal data class Lockfile(
val resolvedPod by lazy { this@Lockfile.podsByName[name] }
}

data class ExternalSource(
/** The path to the local directory where the pod is hosted. */
val path: String? = null,

/** The path to the local '.podspec' file of the pod. */
val podspec: String? = null
)

data class CheckoutOption(
/** The Git repository URL to check out from. */
val git: String?,
Expand All @@ -85,6 +98,18 @@ internal data class Lockfile(
internal fun String.parseLockfile(): Lockfile {
val root = Yaml.default.parseToYamlNode(this).yamlMap

val externalSources = root.get<YamlMap>("EXTERNAL SOURCES")?.entries.orEmpty().map {
val name = it.key.content
val node = it.value.yamlMap

val externalSource = ExternalSource(
path = node.get<YamlScalar>(":path")?.content,
podspec = node.get<YamlScalar>(":podspec")?.content
)

name to externalSource
}.toMap()

val checkoutOptions = root.get<YamlMap>("CHECKOUT OPTIONS")?.entries.orEmpty().map {
val name = it.key.content
val node = it.value.yamlMap
Expand All @@ -102,7 +127,7 @@ internal fun String.parseLockfile(): Lockfile {
val pods = mutableListOf<Pod>()
val dependencies = mutableListOf<Dependency>()

val lockfile = Lockfile(pods, dependencies, checkoutOptions)
val lockfile = Lockfile(pods, dependencies, externalSources, checkoutOptions)

pods += root.get<YamlList>("PODS")?.items.orEmpty().map { it.toPod(lockfile) }
dependencies += root.get<YamlList>("DEPENDENCIES")?.items.orEmpty().map { it.toDependency(lockfile) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,15 @@ internal class PodDependencyHandler : DependencyHandler<Lockfile.Pod> {
} else {
val basePodName = dependency.name.substringBefore('/')
val podspec = podspecCache.getOrPut(basePodName) {
getPodspec(basePodName, dependency.version) ?: return Package.EMPTY.copy(id = id, purl = id.toPurl())
// Lazily only call the pod CLI if the podspec is not available from the external source.
val podspecFile = sequence {
yield(dependency.externalSource?.podspec)
yield(getPodspecPath(basePodName, dependency.version))
}.firstNotNullOfOrNull { path ->
path?.let { File(it) }?.takeIf { it.isFile }
}

podspecFile?.readText()?.parsePodspec() ?: return Package.EMPTY.copy(id = id, purl = id.toPurl())
}

val vcs = podspec.source?.git?.let { url ->
Expand All @@ -103,7 +111,7 @@ internal class PodDependencyHandler : DependencyHandler<Lockfile.Pod> {
}
}

private fun getPodspec(name: String, version: String): Podspec? {
private fun getPodspecPath(name: String, version: String): String? {
val podspecProcess = CocoaPodsCommand.run(
"spec", "which", "^$name$",
"--version=$version",
Expand All @@ -127,7 +135,6 @@ internal class PodDependencyHandler : DependencyHandler<Lockfile.Pod> {
return null
}

val podspecFile = File(podspecProcess.stdout.trim())
return podspecFile.readText().parsePodspec()
return podspecProcess.stdout.trim()
}
}

0 comments on commit 98ee1ee

Please sign in to comment.