diff --git a/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources-expected-output.yml b/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources-expected-output.yml new file mode 100644 index 0000000000000..6bfaf20bc6571 --- /dev/null +++ b/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources-expected-output.yml @@ -0,0 +1,286 @@ +--- +project: + id: "CocoaPods::src/funTest/assets/projects/synthetic/external-sources/Podfile:" + definition_file_path: "plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources/Podfile" + declared_licenses: [] + declared_licenses_processed: {} + vcs: + type: "" + url: "" + revision: "" + path: "" + vcs_processed: + type: "Git" + url: "" + revision: "" + path: "" + homepage_url: "" + scopes: + - name: "dependencies" + dependencies: + - id: "Pod::AFNetworking:3.2.1" + dependencies: + - id: "Pod::AFNetworking/NSURLSession:3.2.1" + dependencies: + - id: "Pod::AFNetworking/Reachability:3.2.1" + - id: "Pod::AFNetworking/Security:3.2.1" + - id: "Pod::AFNetworking/Serialization:3.2.1" + - id: "Pod::AFNetworking/Reachability:3.2.1" + - id: "Pod::AFNetworking/Security:3.2.1" + - id: "Pod::AFNetworking/Serialization:3.2.1" + - id: "Pod::AFNetworking/UIKit:3.2.1" + - id: "Pod::CocoaSecurity:1.2.4" + - id: "Pod::MLHudAlert:0.0.4-3f23fa41632f8935fed028b04335576633ec53a2" + - id: "Pod::Ono:2.1.1" +packages: +- id: "Pod::AFNetworking:3.2.1" + purl: "pkg:cocoapods/AFNetworking@3.2.1" + declared_licenses: + - "MIT" + declared_licenses_processed: + spdx_expression: "MIT" + description: "A delightful iOS and OS X networking framework." + homepage_url: "https://github.com/AFNetworking/AFNetworking" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" +- id: "Pod::AFNetworking/NSURLSession:3.2.1" + purl: "pkg:cocoapods/AFNetworking%2FNSURLSession@3.2.1" + declared_licenses: + - "MIT" + declared_licenses_processed: + spdx_expression: "MIT" + description: "A delightful iOS and OS X networking framework." + homepage_url: "https://github.com/AFNetworking/AFNetworking" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" +- id: "Pod::AFNetworking/Reachability:3.2.1" + purl: "pkg:cocoapods/AFNetworking%2FReachability@3.2.1" + declared_licenses: + - "MIT" + declared_licenses_processed: + spdx_expression: "MIT" + description: "A delightful iOS and OS X networking framework." + homepage_url: "https://github.com/AFNetworking/AFNetworking" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" +- id: "Pod::AFNetworking/Security:3.2.1" + purl: "pkg:cocoapods/AFNetworking%2FSecurity@3.2.1" + declared_licenses: + - "MIT" + declared_licenses_processed: + spdx_expression: "MIT" + description: "A delightful iOS and OS X networking framework." + homepage_url: "https://github.com/AFNetworking/AFNetworking" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" +- id: "Pod::AFNetworking/Serialization:3.2.1" + purl: "pkg:cocoapods/AFNetworking%2FSerialization@3.2.1" + declared_licenses: + - "MIT" + declared_licenses_processed: + spdx_expression: "MIT" + description: "A delightful iOS and OS X networking framework." + homepage_url: "https://github.com/AFNetworking/AFNetworking" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" +- id: "Pod::AFNetworking/UIKit:3.2.1" + purl: "pkg:cocoapods/AFNetworking%2FUIKit@3.2.1" + declared_licenses: + - "MIT" + declared_licenses_processed: + spdx_expression: "MIT" + description: "A delightful iOS and OS X networking framework." + homepage_url: "https://github.com/AFNetworking/AFNetworking" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/AFNetworking/AFNetworking.git" + revision: "3.2.1" + path: "" +- id: "Pod::CocoaSecurity:1.2.4" + purl: "pkg:cocoapods/CocoaSecurity@1.2.4" + declared_licenses: + - "MIT" + declared_licenses_processed: + spdx_expression: "MIT" + description: "Encrypt/Decrypt: AES. Hash: MD5, SHA(SHA1, SHA224, SHA256, SHA384,\ + \ SHA512). Encode/Decode: Base64, Hex." + homepage_url: "https://github.com/kelp404/CocoaSecurity.git" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/kelp404/CocoaSecurity.git" + revision: "1.2.4" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/kelp404/CocoaSecurity.git" + revision: "1.2.4" + path: "" +- id: "Pod::MLHudAlert:0.0.4-3f23fa41632f8935fed028b04335576633ec53a2" + purl: "pkg:cocoapods/MLHudAlert@0.0.4-3f23fa41632f8935fed028b04335576633ec53a2" + declared_licenses: [] + declared_licenses_processed: {} + description: "" + homepage_url: "https://github.com/MacLabs/MLHudAlert.git" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/MacLabs/MLHudAlert.git" + revision: "3f23fa41632f8935fed028b04335576633ec53a2" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/MacLabs/MLHudAlert.git" + revision: "3f23fa41632f8935fed028b04335576633ec53a2" + path: "" +- id: "Pod::Ono:2.1.1" + purl: "pkg:cocoapods/Ono@2.1.1" + declared_licenses: + - "MIT" + declared_licenses_processed: + spdx_expression: "MIT" + description: "A sensible way to deal with XML & HTML." + homepage_url: "https://github.com/mattt/Ono" + binary_artifact: + url: "" + hash: + value: "" + algorithm: "" + source_artifact: + url: "" + hash: + value: "" + algorithm: "" + vcs: + type: "Git" + url: "https://github.com/mattt/Ono.git" + revision: "2.1.1" + path: "" + vcs_processed: + type: "Git" + url: "https://github.com/mattt/Ono.git" + revision: "2.1.1" + path: "" diff --git a/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources/Podfile b/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources/Podfile new file mode 100644 index 0000000000000..fb34f8aaaca5d --- /dev/null +++ b/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources/Podfile @@ -0,0 +1,14 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +target 'magnetX' do + # Uncomment this line if you're using Swift or would like to use dynamic frameworks + # use_frameworks! + + # Pods for magnetX +pod 'AFNetworking' +pod 'Ono' +pod 'CocoaSecurity' +#pod 'SSZipArchive' +pod 'MLHudAlert', git: 'https://github.com/MacLabs/MLHudAlert.git' +end diff --git a/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources/Podfile.lock b/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources/Podfile.lock new file mode 100644 index 0000000000000..be18ccd61f0e5 --- /dev/null +++ b/plugins/package-managers/cocoapods/src/funTest/assets/projects/synthetic/external-sources/Podfile.lock @@ -0,0 +1,48 @@ +PODS: + - AFNetworking (3.2.1): + - AFNetworking/NSURLSession (= 3.2.1) + - AFNetworking/Reachability (= 3.2.1) + - AFNetworking/Security (= 3.2.1) + - AFNetworking/Serialization (= 3.2.1) + - AFNetworking/UIKit (= 3.2.1) + - AFNetworking/NSURLSession (3.2.1): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (3.2.1) + - AFNetworking/Security (3.2.1) + - AFNetworking/Serialization (3.2.1) + - CocoaSecurity (1.2.4) + - MLHudAlert (0.0.4) + - Ono (2.1.1) + +DEPENDENCIES: + - AFNetworking + - CocoaSecurity + - MLHudAlert (from `https://github.com/MacLabs/MLHudAlert.git`) + - Ono + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - AFNetworking + - CocoaSecurity + - Ono + +EXTERNAL SOURCES: + MLHudAlert: + :git: https://github.com/MacLabs/MLHudAlert.git + +CHECKOUT OPTIONS: + MLHudAlert: + :commit: 3f23fa41632f8935fed028b04335576633ec53a2 + :git: https://github.com/MacLabs/MLHudAlert.git + +SPEC CHECKSUMS: + AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057 + CocoaSecurity: d288a6f87e0f363823d2cb83e753814a6944f71a + MLHudAlert: e1654a23a4c1bffbf0fbcea2ef1c5f1c8c5dc896 + Ono: 13c25831a4a66b30df93bd1fa57cf70f13c62699 + +PODFILE CHECKSUM: 13d1396980f8da373e400ea9318d6521b938a692 + +COCOAPODS: 1.6.0 diff --git a/plugins/package-managers/cocoapods/src/funTest/kotlin/CocoaPodsFunTest.kt b/plugins/package-managers/cocoapods/src/funTest/kotlin/CocoaPodsFunTest.kt index 41a5620608006..ef67e9666b0df 100644 --- a/plugins/package-managers/cocoapods/src/funTest/kotlin/CocoaPodsFunTest.kt +++ b/plugins/package-managers/cocoapods/src/funTest/kotlin/CocoaPodsFunTest.kt @@ -49,6 +49,15 @@ class CocoaPodsFunTest : WordSpec({ result.toYaml() should matchExpectedResult(expectedResultFile, definitionFile) } + "determine dependencies from a Podfile with a external sources" { + val definitionFile = getAssetFile("projects/synthetic/external-sources/Podfile") + val expectedResultFile = getAssetFile("projects/synthetic/external-sources-expected-output.yml") + + val result = create("CocoaPods").resolveSingleProject(definitionFile) + + result.toYaml() should matchExpectedResult(expectedResultFile, definitionFile) + } + "return no dependencies along with an issue if the lockfile is absent" { val definitionFile = getAssetFile("projects/synthetic/no-lockfile/Podfile") val expectedResultFile = getAssetFile("projects/synthetic/no-lockfile-expected-output.yml") diff --git a/plugins/package-managers/cocoapods/src/main/kotlin/CocoaPods.kt b/plugins/package-managers/cocoapods/src/main/kotlin/CocoaPods.kt index 5d7d9789a0926..49df4c82423e8 100644 --- a/plugins/package-managers/cocoapods/src/main/kotlin/CocoaPods.kt +++ b/plugins/package-managers/cocoapods/src/main/kotlin/CocoaPods.kt @@ -127,10 +127,12 @@ class CocoaPods( val issues = mutableListOf() if (lockfile.isFile) { - val dependencies = getPackageReferences(lockfile) + val lockfileData = parseLockfile(lockfile) - scopes += Scope(SCOPE_NAME, dependencies) - packages += scopes.flatMap { it.collectDependencies() }.map { getPackage(it, workingDir) } + scopes += Scope(SCOPE_NAME, lockfileData.dependencies) + packages += scopes.flatMap { it.collectDependencies() }.map { + lockfileData.externalSources[it] ?: getPackage(it, workingDir) + } } else { issues += createAndLogIssue( source = managerName, @@ -240,7 +242,12 @@ private fun parseNameAndVersion(entry: String): Pair { return name to version } -private fun getPackageReferences(podfileLock: File): Set { +private data class LockfileData( + val dependencies: Set, + val externalSources: Map +) + +private fun parseLockfile(podfileLock: File): LockfileData { val versionForName = mutableMapOf() val dependenciesForName = mutableMapOf>() val root = yamlMapper.readTree(podfileLock) @@ -265,16 +272,44 @@ private fun getPackageReferences(podfileLock: File): Set { dependenciesForName.getOrPut(name) { mutableSetOf() } += dependencies } + val externalSources = root.get("CHECKOUT OPTIONS")?.fields()?.asSequence()?.mapNotNull { + val checkout = it.value as ObjectNode + val url = checkout[":git"]?.textValue() ?: return@mapNotNull null + val revision = checkout[":commit"].textValueOrEmpty() + + // The version written to the lockfile matches the version specified in the project's ".podspec" file at the + // given revision, so the same version might be used in different revisions. To still get a unique identifier, + // append the revision to the version. + val versionFromPodspec = checkNotNull(versionForName[it.key]) + val uniqueVersion = "$versionFromPodspec-$revision" + val id = Identifier("Pod", "", it.key, uniqueVersion) + + // Write the unique version back for correctly associating dependencies below. + versionForName[it.key] = uniqueVersion + + id to Package( + id = id, + declaredLicenses = emptySet(), + description = "", + homepageUrl = url, + binaryArtifact = RemoteArtifact.EMPTY, + sourceArtifact = RemoteArtifact.EMPTY, + vcs = VcsInfo(VcsType.GIT, url, revision) + ) + }?.toMap() + fun createPackageReference(name: String): PackageReference = PackageReference( id = Identifier("Pod", "", name, versionForName.getValue(name)), dependencies = dependenciesForName[name].orEmpty().mapTo(mutableSetOf()) { createPackageReference(it) } ) - return root.get("DEPENDENCIES").mapTo(mutableSetOf()) { node -> + val dependencies = root.get("DEPENDENCIES").mapTo(mutableSetOf()) { node -> val name = node.textValue().substringBefore(" ") createPackageReference(name) } + + return LockfileData(dependencies, externalSources.orEmpty()) } @JsonIgnoreProperties(ignoreUnknown = true)