From 21fd6442489a5eb582ef3c9e94bd8a9f88a4a6ae Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Sun, 19 Mar 2023 19:57:46 -0700 Subject: [PATCH 1/5] updated setup for cocoapods fixtures 1. use local gemfile with cocoapods and cocoapods-dependencies-list plugin 2. update to cocoapods 1.12.x --- script/source-setup/cocoapods | 17 +- test/fixtures/cocoapods/.gitignore | 2 + test/fixtures/cocoapods/Gemfile | 9 ++ test/fixtures/cocoapods/Gemfile.lock | 102 ++++++++++++ test/fixtures/cocoapods/Podfile.lock | 2 +- .../cocoapods/ios.xcodeproj/project.pbxproj | 145 ++++++++++++++++++ 6 files changed, 269 insertions(+), 8 deletions(-) create mode 100644 test/fixtures/cocoapods/Gemfile create mode 100644 test/fixtures/cocoapods/Gemfile.lock diff --git a/script/source-setup/cocoapods b/script/source-setup/cocoapods index 5faa0dac..e9ab9b85 100755 --- a/script/source-setup/cocoapods +++ b/script/source-setup/cocoapods @@ -1,18 +1,21 @@ #!/bin/bash set -e -if [ -z "$(which pod)" ]; then - echo "A local pod installation is required for cocoapods development." >&2 - exit 127 -fi - # setup test fixtures BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -cd $BASE_PATH/test/fixtures/cocoapods +cd "$BASE_PATH/test/fixtures/cocoapods" + +if [ "$1" == "-f" ]; then + git clean -ffX . +fi + +# install cocoapods and cocoapods-dependencies-list plugin +bundle config set path 'vendor/gems' +bundle install OPTIONS=() if [ "$1" == "-f" ]; then OPTIONS+="--clean-install" fi -pod install ${OPTIONS[@]} +pod install "${OPTIONS[@]}" diff --git a/test/fixtures/cocoapods/.gitignore b/test/fixtures/cocoapods/.gitignore index 773a22d8..396fca0d 100644 --- a/test/fixtures/cocoapods/.gitignore +++ b/test/fixtures/cocoapods/.gitignore @@ -1 +1,3 @@ /Pods/ +/vendor/ +/.bundle/ diff --git a/test/fixtures/cocoapods/Gemfile b/test/fixtures/cocoapods/Gemfile new file mode 100644 index 00000000..b9653ede --- /dev/null +++ b/test/fixtures/cocoapods/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true +source "https://rubygems.org" + +# Specify your gem's dependencies in licensed.gemspec +gem "cocoapods" + +group "plugins" do + gem "cocoapods-dependencies-list", git: "https://github.com/jonabc/cocoapods-dependencies-list" +end diff --git a/test/fixtures/cocoapods/Gemfile.lock b/test/fixtures/cocoapods/Gemfile.lock new file mode 100644 index 00000000..95f4b2a0 --- /dev/null +++ b/test/fixtures/cocoapods/Gemfile.lock @@ -0,0 +1,102 @@ +GIT + remote: https://github.com/jonabc/cocoapods-dependencies-list + revision: 437d6e1060c2c5510241d04ae480f184146593b0 + specs: + cocoapods-dependencies-list (0.0.1) + +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.6) + rexml + activesupport (7.0.4.3) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + claide (1.1.0) + cocoapods (1.12.0) + addressable (~> 2.8) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.12.0) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.6.0, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.6.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.8.0) + nap (~> 1.0) + ruby-macho (>= 2.3.0, < 3.0) + xcodeproj (>= 1.21.0, < 2.0) + cocoapods-core (1.12.0) + activesupport (>= 5.0, < 8) + addressable (~> 2.8) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + public_suffix (~> 4.0) + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.6.3) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) + colored2 (3.1.2) + concurrent-ruby (1.2.2) + escape (0.0.4) + ethon (0.16.0) + ffi (>= 1.15.0) + ffi (1.15.5) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.8.3) + i18n (1.12.0) + concurrent-ruby (~> 1.0) + json (2.6.3) + minitest (5.18.0) + molinillo (0.8.0) + nanaimo (0.3.0) + nap (1.1.0) + netrc (0.11.0) + public_suffix (4.0.7) + rexml (3.2.5) + ruby-macho (2.5.1) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + xcodeproj (1.22.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods + cocoapods-dependencies-list! + +BUNDLED WITH + 2.3.26 diff --git a/test/fixtures/cocoapods/Podfile.lock b/test/fixtures/cocoapods/Podfile.lock index 9ff51d8e..61d27b48 100644 --- a/test/fixtures/cocoapods/Podfile.lock +++ b/test/fixtures/cocoapods/Podfile.lock @@ -99,4 +99,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: bdfd89e228c2312fe7d97a12be23107d74b78421 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.0 diff --git a/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj b/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj index f61ab755..8a201df1 100644 --- a/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj +++ b/test/fixtures/cocoapods/ios.xcodeproj/project.pbxproj @@ -6,6 +6,11 @@ objectVersion = 51; objects = { +/* Begin PBXBuildFile section */ + 725F0B593C7ED093B3780084 /* Pods_iosTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 578D04770F54DE477CC389D6 /* Pods_iosTests.framework */; }; + 909D4D4AC672E3A516A2D64F /* Pods_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2FCDB6FF29A9F30EF2279A7 /* Pods_ios.framework */; }; +/* End PBXBuildFile section */ + /* Begin PBXContainerItemProxy section */ E3999824D112043DE8E88846 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -18,9 +23,34 @@ /* Begin PBXFileReference section */ 07598229C97B25EC09E1E750 /* ios.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = ios.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 376F843147F5EEF103790AD2 /* Pods-iosTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosTests.debug.xcconfig"; path = "Target Support Files/Pods-iosTests/Pods-iosTests.debug.xcconfig"; sourceTree = ""; }; 56F698D1A2EE3DA1EA36472F /* iosTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = iosTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 578D04770F54DE477CC389D6 /* Pods_iosTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 782118AAD40B5A5DA8641741 /* Pods-iosTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosTests.release.xcconfig"; path = "Target Support Files/Pods-iosTests/Pods-iosTests.release.xcconfig"; sourceTree = ""; }; + A1D3BEB94FDAB97EE219BF0C /* Pods-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.debug.xcconfig"; path = "Target Support Files/Pods-ios/Pods-ios.debug.xcconfig"; sourceTree = ""; }; + A2FCDB6FF29A9F30EF2279A7 /* Pods_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C0780757057593B8B74D7075 /* Pods-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.release.xcconfig"; path = "Target Support Files/Pods-ios/Pods-ios.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFrameworksBuildPhase section */ + 02D85C912F4B87472613976E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 725F0B593C7ED093B3780084 /* Pods_iosTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EE91A0103D87756169485CDC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 909D4D4AC672E3A516A2D64F /* Pods_ios.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + /* Begin PBXGroup section */ 56DF4E8E10A8991E1530FAE8 /* iosTests */ = { isa = PBXGroup; @@ -45,12 +75,35 @@ path = ios; sourceTree = ""; }; + C91A823B3C7712079552DF6A /* Pods */ = { + isa = PBXGroup; + children = ( + A1D3BEB94FDAB97EE219BF0C /* Pods-ios.debug.xcconfig */, + C0780757057593B8B74D7075 /* Pods-ios.release.xcconfig */, + 376F843147F5EEF103790AD2 /* Pods-iosTests.debug.xcconfig */, + 782118AAD40B5A5DA8641741 /* Pods-iosTests.release.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + CF390191D1E201D300E1177F /* Frameworks */ = { + isa = PBXGroup; + children = ( + A2FCDB6FF29A9F30EF2279A7 /* Pods_ios.framework */, + 578D04770F54DE477CC389D6 /* Pods_iosTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; DCC682D4A34A47286DC3668D = { isa = PBXGroup; children = ( AD5CE3D685274DEA767D3FBA /* ios */, 56DF4E8E10A8991E1530FAE8 /* iosTests */, 98DCE871D516BA368C096FB6 /* Products */, + C91A823B3C7712079552DF6A /* Pods */, + CF390191D1E201D300E1177F /* Frameworks */, ); sourceTree = ""; }; @@ -61,7 +114,10 @@ isa = PBXNativeTarget; buildConfigurationList = B09050E69F79472FFA774918 /* Build configuration list for PBXNativeTarget "iosTests" */; buildPhases = ( + FBD34C141E4AE05EF793D223 /* [CP] Check Pods Manifest.lock */, 4ADB27C84D37D42F473C6F1A /* Sources */, + 02D85C912F4B87472613976E /* Frameworks */, + 9EC36E7999744F04590964A0 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -77,7 +133,10 @@ isa = PBXNativeTarget; buildConfigurationList = 254407FBAE608B768D0F34E8 /* Build configuration list for PBXNativeTarget "ios" */; buildPhases = ( + BBBBC32AA3D5BEBF642472ED /* [CP] Check Pods Manifest.lock */, 1A12E8A67ABB0C6B7845294E /* Sources */, + EE91A0103D87756169485CDC /* Frameworks */, + 7C86221739FEEF2DDCB60E48 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -107,6 +166,7 @@ en, ); mainGroup = DCC682D4A34A47286DC3668D; + productRefGroup = 98DCE871D516BA368C096FB6 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( @@ -116,6 +176,87 @@ }; /* End PBXProject section */ +/* Begin PBXShellScriptBuildPhase section */ + 7C86221739FEEF2DDCB60E48 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios/Pods-ios-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9EC36E7999744F04590964A0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosTests/Pods-iosTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + BBBBC32AA3D5BEBF642472ED /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ios-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + FBD34C141E4AE05EF793D223 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-iosTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 1A12E8A67ABB0C6B7845294E /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -144,6 +285,7 @@ /* Begin XCBuildConfiguration section */ 1588C5088418414A4D125A34 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 782118AAD40B5A5DA8641741 /* Pods-iosTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; LD_RUNPATH_SEARCH_PATHS = ( @@ -159,6 +301,7 @@ }; 3F06443FC191E03534A2DE7A /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 376F843147F5EEF103790AD2 /* Pods-iosTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; LD_RUNPATH_SEARCH_PATHS = ( @@ -174,6 +317,7 @@ }; 94AAA1C5A9C82E2F2012CF9C /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = C0780757057593B8B74D7075 /* Pods-ios.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -306,6 +450,7 @@ }; F406B4E41AD59D4051593F2C /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A1D3BEB94FDAB97EE219BF0C /* Pods-ios.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; From 9734aff210cc33a7eb7c3d207a4a05f2424c7810 Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Sun, 19 Mar 2023 20:12:35 -0700 Subject: [PATCH 2/5] rewrite cocoapods source using cocoapods plugin --- Gemfile.lock | 3 + docs/sources/cocoapods.md | 15 ++- lib/licensed/sources/cocoapods.rb | 67 ++++++------ lib/licensed/sources/source.rb | 5 + licensed.gemspec | 2 +- test/sources/cocoapods_test.rb | 167 +++++++++++++++++++----------- test/test_helper.rb | 1 + 7 files changed, 161 insertions(+), 99 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b5ac602d..6e1f75a0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,6 +40,8 @@ GEM thor (>= 0.19, < 2.0) mini_portile2 (2.8.1) minitest (5.18.0) + minitest-hooks (1.5.0) + minitest (> 5.3) mocha (2.0.2) ruby2_keywords (>= 0.0.5) nokogiri (1.14.2) @@ -104,6 +106,7 @@ DEPENDENCIES byebug (~> 11.1) licensed! minitest (~> 5.17) + minitest-hooks (~> 1.5) mocha (~> 2.0) rake (~> 13.0) rubocop-github (~> 0.20) diff --git a/docs/sources/cocoapods.md b/docs/sources/cocoapods.md index 5abcc39b..ac795dfb 100644 --- a/docs/sources/cocoapods.md +++ b/docs/sources/cocoapods.md @@ -1,10 +1,8 @@ # CocoaPods -**NOTE!**: Enumerating Cocoapods dependencies is disabled until the cocoapods-core gem is compatible with Rails 7+. See https://github.com/CocoaPods/Core/pull/733 +The cocoapods source will detect dependencies when `Podfile` and `Podfile.lock` are found at an app's `source_path`. The cocoapods source uses the [cocoapods-dependencies-list](https://github.com/jonabc/cocoapods-dependencies-list) plugin to enumerate dependencies and gather metadata on each package. -The cocoapods source will detect dependencies when `Podfile` and `Podfile.lock` are found at an app's `source_path`. - -It uses the `pod` CLI commands to enumerate dependencies and gather metadata on each package. +**NOTE: Licensed does not install the [cocoapods-dependencies-list](https://github.com/jonanc/cocoapods-dependencies-list) plugin. Users must install the gem alongside the cocoapods gem to enumerate cocoapods dependencies.** ## Evaluating dependencies from a specific target @@ -15,3 +13,12 @@ cocoapods: targets: - ios ``` + +## Specifying which pod executable to run + +The cocoapods source will call the `pod` executable to evaluate dependencies by default. If needed, you can override the executable used with the `cocoapods.command` configuration option. This might be useful if the full path to the `pod` executable is needed (e.g. `pod` is not findable from the system `PATH`), or if you need to execute `pod` with `bundle exec`. + +```yml +cocoapods: + command: 'bundle exec pod' +``` diff --git a/lib/licensed/sources/cocoapods.rb b/lib/licensed/sources/cocoapods.rb index bbce4fdb..28c31993 100644 --- a/lib/licensed/sources/cocoapods.rb +++ b/lib/licensed/sources/cocoapods.rb @@ -3,32 +3,29 @@ require "pathname" require "uri" -# **NOTE** Cocoapods is disabled until cocoapods-core supports recent rails versions -# https://github.com/CocoaPods/Core/pull/733 -# require "cocoapods-core" - module Licensed module Sources class Cocoapods < Source - def enabled? - false + DEFAULT_POD_COMMAND = "pod".freeze + MISSING_PLUGIN_MESSAGE = "Error running `pods dependencies`. Please ensure the cocoapods-dependencies-list gem is installed, it is required for licensed to enumerate dependencies.".freeze - # return unless Licensed::Shell.tool_available?("pod") + def enabled? + return unless Licensed::Shell.tool_available?("pod") - # config.pwd.join("Podfile").exist? && config.pwd.join("Podfile.lock").exist? + config.pwd.join("Podfile").exist? && config.pwd.join("Podfile.lock").exist? end def enumerate_dependencies pods.map do |pod| - name = pod.name - path = dependency_path(pod.root_name) - version = lockfile.version(name).version - Dependency.new( - path: path, - name: name, - version: version, - metadata: { "type" => Cocoapods.type } + name: pod["name"], + version: pod["version"], + path: pod["path"], + metadata: { + "type" => Cocoapods.type, + "summary" => pod["summary"], + "homepage" => pod["homepage"] + } ) end end @@ -36,32 +33,32 @@ def enumerate_dependencies private def pods - return lockfile.dependencies if targets.nil? - - targets_to_validate = podfile.target_definition_list.filter { |t| targets.include?(t.label) } - if targets_to_validate.any? - targets_to_validate.map(&:dependencies).flatten - else - raise Licensed::Sources::Source::Error, "Unable to find any target in the Podfile matching the ones provided in the config." - end + cocoapods_dependencies_json.values.flatten end - def targets - @targets ||= config.dig("cocoapods", "targets")&.map { |t| "Pods-#{t}" } - end + def cocoapods_dependencies_json + args = ["dependencies", "--include-path=true"] + args << "--targets=#{targets.join(",")}" if targets.any? - def lockfile - @lockfile = nil - # @lockfile ||= Pod::Lockfile.from_file(config.pwd.join("Podfile.lock")) + output = Licensed::Shell.execute(*pod_command, *args, allow_failure: true) + if output.include? "Unknown command" + raise Licensed::Sources::Source::Error, MISSING_PLUGIN_MESSAGE + end + + JSON.parse(output) + rescue JSON::ParserError => e + message = "Licensed was unable to parse the output from 'pod dependencies'. JSON Error: #{e.message}" + raise Licensed::Sources::Source::Error, message end - def podfile - @podfile = nil - # @podfile ||= Pod::Podfile.from_file(config.pwd.join("Podfile")) + def targets + return [] unless [String, Array].any? { |type| source_config["targets"].is_a?(type) } + Array(source_config["targets"]).map { |t| "Pods-#{t}" } end - def dependency_path(name) - config.pwd.join("Pods/#{name}") + def pod_command + return DEFAULT_POD_COMMAND unless source_config["command"].is_a?(String) + source_config["command"].split end end end diff --git a/lib/licensed/sources/source.rb b/lib/licensed/sources/source.rb index 2ac52ae5..fafde4da 100644 --- a/lib/licensed/sources/source.rb +++ b/lib/licensed/sources/source.rb @@ -90,6 +90,11 @@ def ignored?(dependency) config.ignored?(dependency.metadata, require_version: self.class.require_matched_dependency_version) end + # Returns configuration options set for the current source + def source_config + @source_config ||= config[self.class.type].is_a?(Hash) ? config[self.class.type] : {} + end + private # Returns a cached list of dependencies diff --git a/licensed.gemspec b/licensed.gemspec index 376af9e9..021e3ca5 100644 --- a/licensed.gemspec +++ b/licensed.gemspec @@ -31,10 +31,10 @@ Gem::Specification.new do |spec| spec.add_dependency "parallel", "~> 1.22" spec.add_dependency "reverse_markdown", "~> 2.1" spec.add_dependency "json", "~> 2.6" - # spec.add_dependency "cocoapods-core", "~> 1.11" spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "minitest", "~> 5.17" + spec.add_development_dependency "minitest-hooks", "~> 1.5" spec.add_development_dependency "mocha", "~> 2.0" spec.add_development_dependency "rubocop-github", "~> 0.20" spec.add_development_dependency "byebug", "~> 11.1" diff --git a/test/sources/cocoapods_test.rb b/test/sources/cocoapods_test.rb index 5005a129..3e8b0022 100644 --- a/test/sources/cocoapods_test.rb +++ b/test/sources/cocoapods_test.rb @@ -2,62 +2,111 @@ require "test_helper" require "tmpdir" -# Cocoapods is disabled until cocoapods-core supports recent rails versions -# https://github.com/CocoaPods/Core/pull/733 - -# if Licensed::Shell.tool_available?("pod") -# describe Licensed::Sources::Cocoapods do -# let(:fixtures) { File.expand_path("../../fixtures/cocoapods", __FILE__) } -# let(:config) { Licensed::AppConfiguration.new({ "source_path" => Dir.pwd }) } -# let(:source) { Licensed::Sources::Cocoapods.new(config) } - -# describe "enabled?" do -# it "is true if Podfiles exist" do -# Dir.chdir(fixtures) do -# assert source.enabled? -# end -# end - -# it "is false if Podfiles do not exist" do -# Dir.mktmpdir do |dir| -# Dir.chdir(dir) do -# refute source.enabled? -# end -# end -# end -# end - -# describe "dependencies" do -# it "finds Cocoapods dependencies" do -# Dir.chdir(fixtures) do -# dep = source.dependencies.find { |d| d.name == "Alamofire" } -# assert dep -# assert_equal "5.4.3", dep.version -# end -# end - -# it "handle multiple subspecs from the same root dependencies" do -# Dir.chdir fixtures do -# assert source.dependencies.detect { |dep| dep.name == "MaterialComponents/Cards" } -# assert source.dependencies.detect { |dep| dep.name == "MaterialComponents/Buttons" } -# end -# end - -# it "supports pods from git" do -# Dir.chdir(fixtures) do -# dep = source.dependencies.detect { |d| d.name == "Chatto" } -# end -# end -# end - -# describe "targets" do -# it "includes only dependencies from target if configured" do -# Dir.chdir fixtures do -# config["cocoapods"] = { "targets" => ["iosTests"] } -# assert source.dependencies.detect { |dep| dep.name == "lottie-ios" } -# assert_nil source.dependencies.detect { |dep| dep.name == "Alamofire" } -# end -# end -# end -# end -# end +if Licensed::Shell.tool_available?("pod") + describe Licensed::Sources::Cocoapods do + let(:fixtures) { File.expand_path("../../fixtures/cocoapods", __FILE__) } + let(:config) { Licensed::AppConfiguration.new({ "source_path" => fixtures, "cocoapods" => { "command" => "bundle exec pod" } }) } + let(:source) { Licensed::Sources::Cocoapods.new(config) } + + def with_local_bundler_environment + backup_env = nil + + ::Bundler.ui.silence do + if ::Bundler.root != config.source_path + backup_env = ENV.to_hash + ENV.replace(::Bundler.original_env) + + # reset bundler to load from the current app's source path + ::Bundler.reset! + end + + # ensure the bundler environment is loaded before enumeration + ::Bundler.load + + yield + end + ensure + if backup_env + # restore bundler configuration + ENV.replace(backup_env) + ::Bundler.reset! + end + + # reload the bundler environment after enumeration + ::Bundler.load + end + + around do |&block| + with_local_bundler_environment { block.call } + end + + describe "enabled?" do + it "is true if Podfiles exist" do + Dir.chdir(fixtures) do + assert source.enabled? + end + end + + it "is false if Podfiles do not exist" do + Dir.mktmpdir do |dir| + Dir.chdir(dir) do + refute source.enabled? + end + end + end + end + + describe "dependencies" do + it "finds Cocoapods dependencies" do + Dir.chdir(fixtures) do + dep = source.dependencies.find { |d| d.name == "Alamofire" } + assert dep + assert_equal "5.4.3", dep.version + refute_nil dep.record["summary"] + refute_nil dep.record["homepage"] + refute_nil dep.record["license"] + end + end + + it "handle multiple subspecs from the same root dependencies" do + Dir.chdir fixtures do + assert source.dependencies.detect { |dep| dep.name == "MaterialComponents/Cards" } + assert source.dependencies.detect { |dep| dep.name == "MaterialComponents/Buttons" } + end + end + + it "supports pods from git" do + Dir.chdir(fixtures) do + dep = source.dependencies.detect { |d| d.name == "Chatto" } + end + end + + it "raises an error if cocoapods-dependencies-list isn't available" do + Dir.mktmpdir do |dir| + FileUtils.cp_r(fixtures, dir) + Dir.chdir(File.join(dir, "cocoapods")) do + with_local_bundler_environment do + Licensed::Shell.execute(*%w{bundle config without plugins}) + Licensed::Shell.execute(*%w{bundle install}) + error = assert_raises Licensed::Sources::Source::Error do + source.dependencies.find { |d| d.name == "Alamofire" } + end + + assert_equal Licensed::Sources::Cocoapods::MISSING_PLUGIN_MESSAGE, error.message + end + end + end + end + end + + describe "targets" do + it "includes only dependencies from target if configured" do + Dir.chdir fixtures do + config["cocoapods"]["targets"] = ["iosTests"] + assert source.dependencies.detect { |dep| dep.name == "lottie-ios" } + assert_nil source.dependencies.detect { |dep| dep.name == "Alamofire" } + end + end + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index ac48f89f..6f791c6e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "bundler/setup" require "minitest/autorun" +require "minitest/hooks/default" require "mocha/minitest" require "byebug" require "licensed" From 56698643bbbdce8076a94caa70e25b30cb6751ba Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Sun, 19 Mar 2023 21:37:56 -0700 Subject: [PATCH 3/5] update for released 1.0.0 plugin gem --- lib/licensed/sources/cocoapods.rb | 2 +- test/fixtures/cocoapods/Gemfile | 2 +- test/fixtures/cocoapods/Gemfile.lock | 9 ++------- test/sources/cocoapods_test.rb | 2 +- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/licensed/sources/cocoapods.rb b/lib/licensed/sources/cocoapods.rb index 28c31993..3a95345c 100644 --- a/lib/licensed/sources/cocoapods.rb +++ b/lib/licensed/sources/cocoapods.rb @@ -37,7 +37,7 @@ def pods end def cocoapods_dependencies_json - args = ["dependencies", "--include-path=true"] + args = ["dependencies", "--include-path"] args << "--targets=#{targets.join(",")}" if targets.any? output = Licensed::Shell.execute(*pod_command, *args, allow_failure: true) diff --git a/test/fixtures/cocoapods/Gemfile b/test/fixtures/cocoapods/Gemfile index b9653ede..2c7239ec 100644 --- a/test/fixtures/cocoapods/Gemfile +++ b/test/fixtures/cocoapods/Gemfile @@ -5,5 +5,5 @@ source "https://rubygems.org" gem "cocoapods" group "plugins" do - gem "cocoapods-dependencies-list", git: "https://github.com/jonabc/cocoapods-dependencies-list" + gem "cocoapods-dependencies-list", "~> 1.0" end diff --git a/test/fixtures/cocoapods/Gemfile.lock b/test/fixtures/cocoapods/Gemfile.lock index 95f4b2a0..08f91fa5 100644 --- a/test/fixtures/cocoapods/Gemfile.lock +++ b/test/fixtures/cocoapods/Gemfile.lock @@ -1,9 +1,3 @@ -GIT - remote: https://github.com/jonabc/cocoapods-dependencies-list - revision: 437d6e1060c2c5510241d04ae480f184146593b0 - specs: - cocoapods-dependencies-list (0.0.1) - GEM remote: https://rubygems.org/ specs: @@ -50,6 +44,7 @@ GEM public_suffix (~> 4.0) typhoeus (~> 1.0) cocoapods-deintegrate (1.0.5) + cocoapods-dependencies-list (1.0.0) cocoapods-downloader (1.6.3) cocoapods-plugins (1.0.0) nap @@ -96,7 +91,7 @@ PLATFORMS DEPENDENCIES cocoapods - cocoapods-dependencies-list! + cocoapods-dependencies-list (~> 1.0) BUNDLED WITH 2.3.26 diff --git a/test/sources/cocoapods_test.rb b/test/sources/cocoapods_test.rb index 3e8b0022..27801be0 100644 --- a/test/sources/cocoapods_test.rb +++ b/test/sources/cocoapods_test.rb @@ -77,7 +77,7 @@ def with_local_bundler_environment it "supports pods from git" do Dir.chdir(fixtures) do - dep = source.dependencies.detect { |d| d.name == "Chatto" } + assert source.dependencies.detect { |d| d.name == "Chatto" } end end From b5feb3e1a0003c3272c35d409fa45a83a3eee916 Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Sun, 19 Mar 2023 21:53:37 -0700 Subject: [PATCH 4/5] do not install global cocoapods in CI --- .github/workflows/test.yml | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2b6f68d4..98615a18 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -98,6 +98,21 @@ jobs: - name: Run tests run: script/test cargo + cocoapods: + runs-on: ubuntu-latest + needs: core + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: Set up fixtures + run: script/source-setup/cocoapods + - name: Run tests + run: script/test cocoapods + + composer: runs-on: ubuntu-latest needs: core @@ -458,22 +473,3 @@ jobs: run: script/source-setup/yarn/berry - name: Run tests run: script/test yarn/berry - - cocoapods: - runs-on: ubuntu-latest - needs: core - strategy: - matrix: - cocoapods: [ '1.11.3' ] - steps: - - uses: actions/checkout@v3 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - - name: Set up Cocoapods - run: gem install cocoapods -v ${{ matrix.cocoapods }} - - name: Set up fixtures - run: script/source-setup/cocoapods - - name: Run tests - run: script/test cocoapods From 37046b4e3fe68029dbd67e1af13d2e9835a2895e Mon Sep 17 00:00:00 2001 From: Jon Ruskin Date: Sun, 19 Mar 2023 21:58:25 -0700 Subject: [PATCH 5/5] use cocoapods from bundle install --- script/source-setup/cocoapods | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/source-setup/cocoapods b/script/source-setup/cocoapods index e9ab9b85..f6d3f584 100755 --- a/script/source-setup/cocoapods +++ b/script/source-setup/cocoapods @@ -18,4 +18,4 @@ if [ "$1" == "-f" ]; then OPTIONS+="--clean-install" fi -pod install "${OPTIONS[@]}" +bundle exec pod install "${OPTIONS[@]}"