danger-spm_version_updates 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Guardfile ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A guardfile for making Danger Plugins
4
+ # For more info see https://github.com/guard/guard#readme
5
+
6
+ # To run, use `bundle exec guard`.
7
+
8
+ guard :rspec, cmd: "bundle exec rspec" do
9
+ require "guard/rspec/dsl"
10
+ dsl = Guard::RSpec::Dsl.new(self)
11
+
12
+ # RSpec files
13
+ rspec = dsl.rspec
14
+ watch(rspec.spec_helper) { rspec.spec_dir }
15
+ watch(rspec.spec_support) { rspec.spec_dir }
16
+ watch(rspec.spec_files)
17
+
18
+ # Ruby files
19
+ ruby = dsl.ruby
20
+ dsl.watch_spec_files_for(ruby.lib_files)
21
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2023 Harold Martin <harold.martin@gmail.com>
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # danger-spm_version_updates
2
+
3
+ [![CI](https://github.com/hbmartin/danger-spm_version_updates/actions/workflows/lint_and_test.yml/badge.svg)](https://github.com/hbmartin/danger-spm_version_updates/actions/workflows/lint_and_test.yml)
4
+ [![CodeFactor](https://www.codefactor.io/repository/github/hbmartin/danger-spm_version_updates/badge/main)](https://www.codefactor.io/repository/github/hbmartin/danger-spm_version_updates/overview/main)
5
+ [![Gem Version](https://badge.fury.io/rb/danger-spm_version_updates.svg)](https://badge.fury.io/rb/danger-spm_version_updates)
6
+
7
+
8
+ A [Danger](https://danger.systems/ruby/) plugin to detect if there are any updates to your Swift Package Manager dependencies.
9
+
10
+ It is lightweight and does not require swift to be installed on the CI where it is run.
11
+
12
+
13
+ ## Installation
14
+
15
+ $ gem install danger-spm_version_updates
16
+
17
+ or add the following to your Gemfile:
18
+
19
+
20
+ ```ruby
21
+ gem "danger-spm_version_updates"
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Just add this to your Dangerfile! Note that it is required to configure the path to your Xcode project.
27
+
28
+ ```ruby
29
+ spm_version_updates.xcodeproj_path = ".../Example.xcodeproj"
30
+ spm_version_updates.check_for_updates
31
+ ```
32
+
33
+ You can also configure custom behaviors:
34
+
35
+ ```ruby
36
+ # Whether to check when dependencies are exact versions or commits, default false
37
+ spm_version_updates.check_when_exact = true
38
+
39
+ # Whether to ignore version above the maximum version range, default true
40
+ spm_version_updates.quiet_above_maximum = false
41
+
42
+ # A list of repositories to ignore entirely, must exactly match the URL as configured in the Xcode project
43
+ spm_version_updates.ignore_repositories = ["https://github.com/pointfreeco/swift-snapshot-testing"]
44
+ ```
45
+
46
+ ## Development
47
+
48
+ 1. Clone this repo
49
+ 2. Run `bundle install` to setup dependencies.
50
+ 3. Run `bundle exec rake spec` to run the tests.
51
+ 4. Use `bundle exec guard` to automatically have tests run as you make changes.
52
+ 5. Make your changes.
53
+
54
+
55
+ ## Authors
56
+
57
+ * [Harold Martin](https://www.linkedin.com/in/harold-martin-98526971/) - harold.martin at gmail
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "reek/rake/task"
5
+ require "rspec/core/rake_task"
6
+ require "rubocop/rake_task"
7
+
8
+ RSpec::Core::RakeTask.new(:specs)
9
+
10
+ task default: :specs
11
+
12
+ task :spec do
13
+ Rake::Task["specs"].invoke
14
+ Rake::Task["rubocop"].invoke
15
+ Rake::Task["spec_docs"].invoke
16
+ end
17
+
18
+ desc "Run RuboCop on the lib/specs directory"
19
+ RuboCop::RakeTask.new(:rubocop) { |task|
20
+ task.requires << "rubocop-rspec"
21
+ task.requires << "rubocop-rake"
22
+ task.patterns = ["lib/**/*.rb", "spec/**/*.rb"]
23
+ }
24
+
25
+ desc "Run Reek on the lib/specs directory"
26
+ Reek::Rake::Task.new(:reek) { |task|
27
+ task.source_files = FileList["lib/**/*.rb", "spec/**/*.rb"]
28
+ }
29
+
30
+ desc "Ensure that the plugin passes `danger plugins lint`"
31
+ task :spec_docs do
32
+ sh "bundle exec danger plugins lint"
33
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "spm_version_updates/gem_version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "danger-spm_version_updates"
9
+ spec.version = SpmVersionUpdates::VERSION
10
+ spec.authors = ["Harold Martin"]
11
+ spec.email = ["harold.martin@gmail.com"]
12
+ spec.description = "A Danger plugin to detect if there are any updates to your Swift Package Manager dependencies."
13
+ spec.summary = "A Danger plugin to detect if there are any updates to your Swift Package Manager dependencies."
14
+ spec.homepage = "https://github.com/hbmartin/danger-spm_version_updates"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_runtime_dependency("danger-plugin-api", "~> 1.0")
23
+
24
+ # General ruby development
25
+ spec.add_development_dependency("bundler", "~> 2.0")
26
+ spec.add_development_dependency("rake", "~> 13.0")
27
+
28
+ # Testing support
29
+ spec.add_development_dependency("rspec", "~> 3.4")
30
+
31
+ # Linting code and docs
32
+ spec.add_development_dependency("reek")
33
+ spec.add_development_dependency("rubocop")
34
+ spec.add_development_dependency("rubocop-rake")
35
+ spec.add_development_dependency("rubocop-rspec")
36
+ spec.add_development_dependency("yard")
37
+
38
+ # Makes testing easy via `bundle exec guard`
39
+ spec.add_development_dependency("guard", "~> 2.14")
40
+ spec.add_development_dependency("guard-rspec", "~> 4.7")
41
+
42
+ # If you want to work on older builds of ruby
43
+ spec.add_development_dependency("listen", "3.0.7")
44
+
45
+ # This gives you the chance to run a REPL inside your tests
46
+ # via:
47
+ #
48
+ # require 'pry'
49
+ # binding.pry
50
+ #
51
+ # This will stop test execution and let you inspect the results
52
+ spec.add_development_dependency("pry")
53
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spm_version_updates/plugin"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spm_version_updates/gem_version"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpmVersionUpdates
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "semantic"
4
+ require "xcodeproj"
5
+
6
+ module Danger
7
+ # @example Ensure people are well warned about merging on Mondays
8
+ #
9
+ # my_plugin.warn_on_mondays
10
+ #
11
+ # @see Harold Martin/danger-spm_version_updates
12
+ # @tags swift, spm, swift package manager, xcode, xcodeproj, version, updates
13
+ #
14
+ class DangerSpmVersionUpdates < Plugin
15
+ # The path to the xcodeproj file
16
+ # @return [String]
17
+ attr_accessor :xcodeproj_path
18
+
19
+ # Whether to check when dependencies are exact versions or commits, default false
20
+ # @return [Boolean]
21
+ attr_accessor :check_when_exact
22
+
23
+ # Whether to ignore version above the maximum version range, default true
24
+ # @return [Boolean]
25
+ attr_accessor :quiet_above_maximum
26
+
27
+ # A list of repositories to ignore entirely, must exactly match the URL as configured in the Xcode project
28
+ # @return [Array<String>]
29
+ attr_accessor :ignore_repos
30
+
31
+ def initialize(dangerfile)
32
+ super(dangerfile)
33
+ @check_when_exact = false
34
+ @quiet_above_maximum = false
35
+ @ignore_repos = []
36
+ end
37
+
38
+ # A method that you can call from your Dangerfile
39
+ # @return [Array<String>]
40
+ def check_for_updates
41
+ raise(XcodeprojPathMustBeSet) if xcodeproj_path.nil?
42
+
43
+ project = Xcodeproj::Project.open(xcodeproj_path)
44
+ remote_packages = filter_remote_packages(project)
45
+
46
+ resolved_path = find_packages_resolved
47
+ raise(CouldNotFindResolvedFile) unless File.exist?(resolved_path)
48
+
49
+ resolved_versions = JSON.load_file!(resolved_path)["pins"]
50
+ .to_h { |pin| [pin["location"], pin["state"]["version"] || pin["state"]["revision"]] }
51
+
52
+ remote_packages.each { |repository_url, requirement|
53
+ next if ignore_repos.include?(repository_url)
54
+
55
+ name = repo_name(repository_url)
56
+ resolved_version = resolved_versions[repository_url]
57
+ kind = requirement["kind"]
58
+
59
+ # kind can be major, minor, range, exact, branch, or commit
60
+
61
+ if kind == "branch" && check_when_exact
62
+ last_commit = git_branch_last_commit(repository_url, requirement["branch"])
63
+ warn("Newer commit available for #{name}: #{last_commit}") unless last_commit == resolved_version
64
+ next
65
+ end
66
+
67
+ available_versions = git_versions(repository_url)
68
+ next if available_versions.first.to_s == resolved_version
69
+
70
+ if kind == "exactVersion" && @check_when_exact
71
+ warn(
72
+ <<-TEXT
73
+ Newer version of #{name}: #{available_versions.first} (but this package is set to exact version #{resolved_version})
74
+ TEXT
75
+ )
76
+ elsif kind == "upToNextMajorVersion"
77
+ warn_for_new_versions(:major, available_versions, name, resolved_version)
78
+ elsif kind == "upToNextMinorVersion"
79
+ warn_for_new_versions(:minor, available_versions, name, resolved_version)
80
+ elsif kind == "range"
81
+ warn_for_new_versions_range(available_versions, name, requirement, resolved_version)
82
+ end
83
+ }
84
+ end
85
+
86
+ def repo_name(repo_url)
87
+ match = repo_url.match(%r{([\w-]+/[\w-]+)(.git)?$})
88
+
89
+ if match
90
+ match[1] || match[0]
91
+ else
92
+ repo_url
93
+ end
94
+ end
95
+
96
+ def filter_remote_packages(project)
97
+ project.objects.select { |obj|
98
+ obj.kind_of?(Xcodeproj::Project::Object::XCRemoteSwiftPackageReference) &&
99
+ obj.requirement["kind"] != "commit"
100
+ }
101
+ .to_h { |package| [package.repositoryURL, package.requirement] }
102
+ end
103
+
104
+ def find_packages_resolved
105
+ if Dir.exist?(xcodeproj_path.sub("xcodeproj", "xcworkspace"))
106
+ File.join(xcodeproj_path.sub("xcodeproj", "xcworkspace"), "xcshareddata", "swiftpm", "Package.resolved")
107
+ else
108
+ File.join(xcodeproj_path, "project.xcworkspace", "xcshareddata", "swiftpm", "Package.resolved")
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def warn_for_new_versions_range(available_versions, name, requirement, resolved_version)
115
+ max_version = Semantic::Version.new(requirement["maximumVersion"])
116
+ if available_versions.first < max_version
117
+ warn("Newer version of #{name}: #{available_versions.first}")
118
+ else
119
+ newest_meeting_reqs = available_versions.find { |version|
120
+ version < max_version
121
+ }
122
+ warn("Newer version of #{name}: #{newest_meeting_reqs} ") unless newest_meeting_reqs.to_s == resolved_version
123
+ warn(
124
+ <<-TEXT
125
+ Newest version of #{name}: #{available_versions.first} (but this package is configured up to the next #{max_version} version)
126
+ TEXT
127
+ ) unless quiet_above_maximum
128
+ end
129
+ end
130
+
131
+ def warn_for_new_versions(major_or_minor, available_versions, name, resolved_version_string)
132
+ resolved_version = Semantic::Version.new(resolved_version_string)
133
+ if available_versions.first.send(major_or_minor) == resolved_version.send(major_or_minor)
134
+ warn("Newer version of #{name}: #{available_versions.first}")
135
+ else
136
+ newest_meeting_reqs = available_versions.find { |version|
137
+ version.send(major_or_minor) == resolved_version.send(major_or_minor)
138
+ }
139
+ warn("Newer version of #{name}: #{newest_meeting_reqs}") unless newest_meeting_reqs == resolved_version
140
+ warn(
141
+ <<-TEXT
142
+ Newest version of #{name}: #{available_versions.first} (but this package is configured up to the next #{major_or_minor} version)
143
+ TEXT
144
+ ) unless quiet_above_maximum
145
+ end
146
+ end
147
+
148
+ def git_versions(repo_url)
149
+ `git ls-remote -t #{repo_url}`
150
+ .split("\n")
151
+ .map { |line| line.split("/tags/").last }
152
+ .filter_map { |line|
153
+ begin
154
+ Semantic::Version.new(line)
155
+ rescue ArgumentError
156
+ nil
157
+ end
158
+ }
159
+ .sort
160
+ .reverse
161
+ end
162
+
163
+ def git_branch_last_commit(repo_url, branch_name)
164
+ `git ls-remote -h #{repo_url}`
165
+ .split("\n")
166
+ .find { |line| line.split("\trefs/heads/")[1] == branch_name }
167
+ .split("\trefs/heads/")[0]
168
+ end
169
+ end
170
+
171
+ class XcodeprojPathMustBeSet < StandardError
172
+ end
173
+
174
+ class CouldNotFindResolvedFile < StandardError
175
+ end
176
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ ROOT = Pathname.new(File.expand_path("..", __dir__))
5
+ $:.unshift("#{ROOT}lib".to_s)
6
+ $:.unshift("#{ROOT}spec".to_s)
7
+
8
+ require "bundler/setup"
9
+ require "pry"
10
+
11
+ require "danger"
12
+ require "rspec"
13
+
14
+ if `git remote -v` == ""
15
+ puts "You cannot run tests without setting a local git remote on this repo"
16
+ puts "It's a weird side-effect of Danger's internals."
17
+ exit(0)
18
+ end
19
+
20
+ # Use coloured output, it's the best.
21
+ RSpec.configure do |config|
22
+ config.filter_gems_from_backtrace("bundler")
23
+ config.color = true
24
+ config.tty = true
25
+ end
26
+
27
+ require "danger_plugin"
28
+
29
+ # These functions are a subset of https://github.com/danger/danger/blob/master/spec/spec_helper.rb
30
+ # If you are expanding these files, see if it's already been done ^.
31
+
32
+ # A silent version of the user interface,
33
+ # it comes with an extra function `.string` which will
34
+ # strip all ANSI colours from the string.
35
+
36
+ # rubocop:disable Lint/NestedMethodDefinition
37
+ def testing_ui
38
+ @output = StringIO.new
39
+ def @output.winsize
40
+ [20, 9999]
41
+ end
42
+
43
+ cork = Cork::Board.new(out: @output)
44
+ def cork.string
45
+ out.string.gsub(/\e\[([;\d]+)?m/, "")
46
+ end
47
+ cork
48
+ end
49
+ # rubocop:enable Lint/NestedMethodDefinition
50
+
51
+ # Example environment (ENV) that would come from
52
+ # running a PR on TravisCI
53
+ def testing_env
54
+ {
55
+ "HAS_JOSH_K_SEAL_OF_APPROVAL" => "true",
56
+ "TRAVIS_PULL_REQUEST" => "800",
57
+ "TRAVIS_REPO_SLUG" => "artsy/eigen",
58
+ "TRAVIS_COMMIT_RANGE" => "759adcbd0d8f...13c4dc8bb61d",
59
+ "DANGER_GITHUB_API_TOKEN" => "123sbdq54erfsd3422gdfio"
60
+ }
61
+ end
62
+
63
+ # A stubbed out Dangerfile for use in tests
64
+ def testing_dangerfile
65
+ env = Danger::EnvironmentManager.new(testing_env)
66
+ Danger::Dangerfile.new(env, testing_ui)
67
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path("spec_helper", __dir__)
4
+
5
+ module Danger
6
+ describe Danger::DangerSpmVersionUpdates do
7
+ it "is a plugin" do
8
+ expect(described_class.new(nil)).to be_a Danger::Plugin
9
+ end
10
+
11
+ #
12
+ # You should test your custom attributes and methods here
13
+ #
14
+ describe "with Dangerfile" do
15
+ before do
16
+ @dangerfile = testing_dangerfile
17
+ @my_plugin = @dangerfile.spm_version_updates
18
+
19
+ # mock the PR data
20
+ # you can then use this, eg. github.pr_author, later in the spec
21
+ json = File.read("#{File.dirname(__FILE__)}/support/fixtures/github_pr.json") # example json: `curl https://api.github.com/repos/danger/danger-plugin-template/pulls/18 > github_pr.json`
22
+ allow(@my_plugin.github).to receive(:pr_json).and_return(json)
23
+ end
24
+
25
+ it "raises error if xcodeproj_path is not set" do
26
+ expect { @my_plugin.check_for_updates }
27
+ .to raise_error(Danger::XcodeprojPathMustBeSet)
28
+ end
29
+
30
+ it "Reports none without exact version matching" do
31
+ monday_date = Date.parse("2016-07-11")
32
+ allow(Date).to receive(:today).and_return monday_date
33
+
34
+ @my_plugin.xcodeproj_path = "#{File.dirname(__FILE__)}/support/fixtures/Example.xcodeproj"
35
+ @my_plugin.check_for_updates
36
+
37
+ expect(@dangerfile.status_report[:warnings]).to eq([])
38
+ end
39
+
40
+ it "Reports some with exact version matching" do
41
+ # TODO: mock git calls
42
+ # allow(@my_plugin).to receive(:git_versions).and_return false
43
+
44
+ @my_plugin.xcodeproj_path = "#{File.dirname(__FILE__)}/support/fixtures/Example.xcodeproj"
45
+ @my_plugin.check_when_exact = true
46
+ @my_plugin.check_for_updates
47
+
48
+ expect(@dangerfile.status_report[:warnings]).to eq(
49
+ [
50
+ "Newer version of pointfreeco/swift-snapshot-testing: 1.14.2 (but this package is set to exact version 1.13.0)\n",
51
+ "Newer version of kean/Nuke: 12.2.0-beta.2 (but this package is set to exact version 12.1.6)\n",
52
+ "Newer version of pointfreeco/swiftui-navigation: 1.0.3 (but this package is set to exact version 1.0.2)\n",
53
+ "Newer version of getsentry/sentry-cocoa: 8.14.2 (but this package is set to exact version 8.12.0)\n",
54
+ "Newer version of firebase/firebase-ios-sdk: 10.17.0 (but this package is set to exact version 10.15.0)\n",
55
+ ]
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,152 @@
1
+ // !$*UTF8*$!
2
+ {
3
+ archiveVersion = 1;
4
+ classes = {
5
+ };
6
+ objectVersion = 54;
7
+ objects = {
8
+ /* Begin PBXGroup section */
9
+ F1465EF423AA94BF0055F7C3 = {
10
+ isa = PBXGroup;
11
+ children = (
12
+ F1465EFF23AA94BF0055F7C3 /* Demo */,
13
+ );
14
+ sourceTree = "<group>";
15
+ };
16
+ F1465EFF23AA94BF0055F7C3 /* Demo */ = {
17
+ isa = PBXGroup;
18
+ children = ();
19
+ path = Demo;
20
+ sourceTree = "<group>";
21
+ };
22
+ /* End PBXGroup section */
23
+
24
+ /* Begin PBXProject section */
25
+ F1465EF523AA94BF0055F7C3 /* Project object */ = {
26
+ isa = PBXProject;
27
+ attributes = {
28
+ BuildIndependentTargetsInParallel = YES;
29
+ LastSwiftUpdateCheck = 1420;
30
+ LastUpgradeCheck = 1420;
31
+ TargetAttributes = {
32
+ F1465EFC23AA94BF0055F7C3 = {
33
+ CreatedOnToolsVersion = 11.2.1;
34
+ };
35
+ };
36
+ };
37
+ buildConfigurationList = F1465EF823AA94BF0055F7C3 /* Build configuration list for PBXProject "Demo" */;
38
+ compatibilityVersion = "Xcode 9.3";
39
+ developmentRegion = en;
40
+ hasScannedForEncodings = 0;
41
+ knownRegions = (
42
+ en,
43
+ Base,
44
+ es,
45
+ "zh-Hans",
46
+ fr,
47
+ );
48
+ mainGroup = F1465EF423AA94BF0055F7C3;
49
+ packageReferences = (
50
+ 115E40CB29B6D0EC00887A45 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */,
51
+ 11BBD37629C1571400F7A968 /* XCRemoteSwiftPackageReference "Nuke" */,
52
+ 11B8EE072A01A5AA00201115 /* XCRemoteSwiftPackageReference "maplibre-gl-native-distribution" */,
53
+ 116B05982A66192400EB92A4 /* XCRemoteSwiftPackageReference "swiftui-navigation" */,
54
+ F21242692A88338C005407A6 /* XCRemoteSwiftPackageReference "sentry-cocoa" */,
55
+ 1179CA742AAF97DB006EF58C /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
56
+ 112F9EF52ABD298C003D2CE6 /* XCRemoteSwiftPackageReference "LookingGlassUI" */,
57
+ CA7732012ADDBF49009F6B1B /* XCRemoteSwiftPackageReference "PhoneNumberKit" */,
58
+ F2C780582AEDB44700588969 /* XCRemoteSwiftPackageReference "analytics-swift" */,
59
+ );
60
+ productRefGroup = F1465EFE23AA94BF0055F7C3 /* Products */;
61
+ projectDirPath = "";
62
+ projectRoot = "";
63
+ targets = ();
64
+ };
65
+ /* End PBXProject section */
66
+
67
+ /* Begin XCConfigurationList section */
68
+ F1465EF823AA94BF0055F7C3 /* Build configuration list for PBXNativeTarget "Demo" */ = {
69
+ isa = XCConfigurationList;
70
+ buildConfigurations = ();
71
+ defaultConfigurationIsVisible = 0;
72
+ defaultConfigurationName = Release;
73
+ };
74
+ /* End XCConfigurationList section */
75
+
76
+ /* Begin XCRemoteSwiftPackageReference section */
77
+ 112F9EF52ABD298C003D2CE6 /* XCRemoteSwiftPackageReference "LookingGlassUI" */ = {
78
+ isa = XCRemoteSwiftPackageReference;
79
+ repositoryURL = "https://github.com/ryanlintott/LookingGlassUI";
80
+ requirement = {
81
+ kind = upToNextMajorVersion;
82
+ minimumVersion = 0.3.1;
83
+ };
84
+ };
85
+ 115E40CB29B6D0EC00887A45 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = {
86
+ isa = XCRemoteSwiftPackageReference;
87
+ repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing";
88
+ requirement = {
89
+ kind = exactVersion;
90
+ version = 1.13.0;
91
+ };
92
+ };
93
+ 116B05982A66192400EB92A4 /* XCRemoteSwiftPackageReference "swiftui-navigation" */ = {
94
+ isa = XCRemoteSwiftPackageReference;
95
+ repositoryURL = "https://github.com/pointfreeco/swiftui-navigation";
96
+ requirement = {
97
+ kind = exactVersion;
98
+ version = 1.0.2;
99
+ };
100
+ };
101
+ 1179CA742AAF97DB006EF58C /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
102
+ isa = XCRemoteSwiftPackageReference;
103
+ repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
104
+ requirement = {
105
+ kind = exactVersion;
106
+ version = 10.15.0;
107
+ };
108
+ };
109
+ 11B8EE072A01A5AA00201115 /* XCRemoteSwiftPackageReference "maplibre-gl-native-distribution" */ = {
110
+ isa = XCRemoteSwiftPackageReference;
111
+ repositoryURL = "https://github.com/maplibre/maplibre-gl-native-distribution";
112
+ requirement = {
113
+ kind = exactVersion;
114
+ version = 5.13.0;
115
+ };
116
+ };
117
+ 11BBD37629C1571400F7A968 /* XCRemoteSwiftPackageReference "Nuke" */ = {
118
+ isa = XCRemoteSwiftPackageReference;
119
+ repositoryURL = "https://github.com/kean/Nuke";
120
+ requirement = {
121
+ kind = exactVersion;
122
+ version = 12.1.6;
123
+ };
124
+ };
125
+ CA7732012ADDBF49009F6B1B /* XCRemoteSwiftPackageReference "PhoneNumberKit" */ = {
126
+ isa = XCRemoteSwiftPackageReference;
127
+ repositoryURL = "https://github.com/marmelroy/PhoneNumberKit.git";
128
+ requirement = {
129
+ kind = upToNextMajorVersion;
130
+ minimumVersion = 3.7.4;
131
+ };
132
+ };
133
+ F21242692A88338C005407A6 /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = {
134
+ isa = XCRemoteSwiftPackageReference;
135
+ repositoryURL = "https://github.com/getsentry/sentry-cocoa.git";
136
+ requirement = {
137
+ kind = exactVersion;
138
+ version = 8.12.0;
139
+ };
140
+ };
141
+ F2C780582AEDB44700588969 /* XCRemoteSwiftPackageReference "analytics-swift" */ = {
142
+ isa = XCRemoteSwiftPackageReference;
143
+ repositoryURL = "git@github.com:hbmartin/analytics-swift.git";
144
+ requirement = {
145
+ branch = main;
146
+ kind = branch;
147
+ };
148
+ };
149
+ /* End XCRemoteSwiftPackageReference section */
150
+ };
151
+ rootObject = F1465EF523AA94BF0055F7C3 /* Project object */;
152
+ }