spm_version_updates 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Derives per-update upgrade guidance from a package's requirement kind and the
4
+ # available version: the SwiftPM identity, a ready-to-run `swift package update`
5
+ # command (manifest mode only), and the manifest requirement change needed when
6
+ # the new version is outside the declared constraint. Shared between the GitHub
7
+ # Action reporters and the Danger plugin.
8
+ module UpgradeSuggestion
9
+ # SwiftPM's default package identity: the last path component of the
10
+ # repository URL, lowercased (the normalized URL already has no `.git`).
11
+ def self.identity(normalized_url)
12
+ normalized_url.to_s.split("/").last.to_s.downcase
13
+ end
14
+
15
+ # @param package [SpmPackageContext]
16
+ # @param available_version [#to_s] the version (or commit) being suggested
17
+ # @param type [Symbol] :version, :above_maximum, :branch, or :revision
18
+ # @return [Hash] package_identity / requirement_kind / suggested_command /
19
+ # suggested_requirement, with inapplicable entries nil
20
+ def self.fields(package, available_version, type)
21
+ {
22
+ package_identity: identity(package.normalized_url),
23
+ requirement_kind: package.kind,
24
+ suggested_command: command(package),
25
+ suggested_requirement: requirement_change(package, available_version.to_s, type)
26
+ }
27
+ end
28
+
29
+ # `swift package update` only applies to Package.swift-managed dependencies
30
+ # (never Xcode projects, where source is nil) and cannot move a revision pin.
31
+ def self.command(package)
32
+ return nil unless package.source
33
+ return nil if package.kind == "revision"
34
+
35
+ "swift package update #{identity(package.normalized_url)}"
36
+ end
37
+
38
+ # The Package.swift requirement text needed before `swift package update` can
39
+ # reach the suggested version. In-range updates, branch pins, and revision
40
+ # pins need no manifest change.
41
+ def self.requirement_change(package, available, type)
42
+ return above_maximum_change(package, available) if type == :above_maximum
43
+
44
+ %(exact: "#{available}") if package.kind == "exactVersion"
45
+ end
46
+
47
+ def self.above_maximum_change(package, available)
48
+ case package.kind
49
+ when "exactVersion" then %(exact: "#{available}")
50
+ when "upToNextMajorVersion" then %(from: "#{available}")
51
+ when "upToNextMinorVersion" then %(.upToNextMinor(from: "#{available}"))
52
+ when "versionRange" then range_change(package.requirement, available)
53
+ end
54
+ end
55
+ private_class_method :above_maximum_change
56
+
57
+ def self.range_change(requirement, available)
58
+ major = available[/\A(\d+)/, 1]
59
+ %("#{requirement['minimumVersion']}"..<"#{major.to_i + 1}.0.0") if major
60
+ end
61
+ private_class_method :range_change
62
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpmVersionUpdates
4
+ VERSION = "1.1.1"
5
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "git_operations"
4
+
5
+ # Fetches git tag versions concurrently for cache-key/repository URL lookup pairs.
6
+ class VersionTagFetcher
7
+ # Thread-safe result/error accumulator shared by fetcher workers.
8
+ FetchState = Struct.new(:mutex, :results, :errors, keyword_init: true) {
9
+ def self.build
10
+ new(mutex: Mutex.new, results: {}, errors: {})
11
+ end
12
+
13
+ def record_result(cache_key, versions)
14
+ mutex.synchronize { results[cache_key] = versions }
15
+ end
16
+
17
+ def record_error(cache_key, error)
18
+ mutex.synchronize { errors[cache_key] = error }
19
+ end
20
+ }
21
+ private_constant :FetchState
22
+
23
+ # Re-raises worker lookup failures as one combined git lookup error.
24
+ LookupErrors = Struct.new(:errors) {
25
+ def raise_error
26
+ first_error = errors.first
27
+
28
+ raise(combined_error(first_error), cause: first_error) if first_error.kind_of?(GitOperations::LsRemoteError)
29
+
30
+ raise(first_error)
31
+ end
32
+
33
+ private
34
+
35
+ def combined_error(first_error)
36
+ GitOperations::LsRemoteError.new(message).tap { |error| error.set_backtrace(first_error.backtrace) }
37
+ end
38
+
39
+ def message
40
+ errors.map(&:message).uniq.join("\n")
41
+ end
42
+ }
43
+ private_constant :LookupErrors
44
+
45
+ # Fetch all lookups, returning `[results, errors]`, both keyed by cache key.
46
+ #
47
+ # With `raise_on_error: true` (the default) any lookup failure is re-raised as
48
+ # one combined error after all workers finish; `errors` is then always empty.
49
+ # With `raise_on_error: false` failed lookups are returned in `errors` so the
50
+ # caller can degrade gracefully per package.
51
+ def self.call(lookups, worker_limit:, persistent_cache: nil, raise_on_error: true)
52
+ new(lookups, worker_limit, persistent_cache:, raise_on_error:).call
53
+ end
54
+
55
+ def initialize(lookups, worker_limit, persistent_cache: nil, raise_on_error: true)
56
+ @lookups = lookups
57
+ @worker_limit = worker_limit
58
+ @persistent_cache = persistent_cache
59
+ @raise_on_error = raise_on_error
60
+ @state = FetchState.build
61
+ end
62
+
63
+ def call
64
+ queue = build_queue
65
+ workers(queue).each(&:value)
66
+ errors = @state.errors
67
+ raise_lookup_error if @raise_on_error && !errors.empty?
68
+
69
+ [@state.results, errors]
70
+ end
71
+
72
+ private
73
+
74
+ def build_queue
75
+ Queue.new.tap { |queue| @lookups.each { |lookup| queue << lookup } }
76
+ end
77
+
78
+ def workers(queue)
79
+ Array.new(worker_count) { Thread.new { drain_queue(queue) } }
80
+ end
81
+
82
+ def worker_count
83
+ [@worker_limit, @lookups.size].min
84
+ end
85
+
86
+ def drain_queue(queue)
87
+ loop { fetch_lookup(queue) }
88
+ rescue ThreadError
89
+ nil
90
+ end
91
+
92
+ def fetch_lookup(queue)
93
+ cache_key, repository_url, persistent_cache_key = queue.pop(true)
94
+ @state.record_result(cache_key, versions_for(repository_url, persistent_cache_key))
95
+ rescue GitOperations::LsRemoteError => error
96
+ @state.record_error(cache_key, error)
97
+ end
98
+
99
+ def versions_for(repository_url, persistent_cache_key)
100
+ cached_versions = @persistent_cache&.read(persistent_cache_key)
101
+ return cached_versions if cached_versions
102
+
103
+ GitOperations.version_tags(repository_url)
104
+ .tap { |versions| @persistent_cache&.write(persistent_cache_key, versions) }
105
+ end
106
+
107
+ def raise_lookup_error
108
+ LookupErrors.new(@state.errors.values).raise_error
109
+ end
110
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "fileutils"
5
+ require "json"
6
+ require "time"
7
+ require_relative "credential_redactor"
8
+ require_relative "semver"
9
+
10
+ # Persistent on-disk cache for successful git tag lookups restored by actions/cache.
11
+ class VersionTagsPersistentCache
12
+ DEFAULT_TTL_SECONDS = 21_600
13
+ SCHEMA_VERSION = 1
14
+
15
+ def self.cache_key(normalized_url, repository_url)
16
+ Digest::SHA256.hexdigest("#{normalized_url}\n#{CredentialRedactor.redact(repository_url)}")
17
+ end
18
+
19
+ def initialize(directory:, ttl_seconds:, clock: -> { Time.now.utc })
20
+ @directory = directory.to_s
21
+ @ttl_seconds = ttl_seconds.to_i
22
+ @clock = clock
23
+ end
24
+
25
+ def enabled?
26
+ !@directory.empty? && @ttl_seconds.positive?
27
+ end
28
+
29
+ def read(cache_key)
30
+ return nil unless enabled?
31
+
32
+ record = read_record(cache_key)
33
+ return nil unless fresh_record?(record)
34
+
35
+ versions_from(record)
36
+ end
37
+
38
+ def write(cache_key, versions)
39
+ return unless enabled?
40
+
41
+ write_record(cache_key, versions)
42
+ rescue StandardError => error
43
+ warn("Failed to write to persistent cache: #{error.message}")
44
+ end
45
+
46
+ private
47
+
48
+ def write_record(cache_key, versions)
49
+ temp = temp_path(cache_key)
50
+ FileUtils.mkdir_p(@directory)
51
+ File.write(temp, JSON.pretty_generate(record_for(versions)))
52
+ File.rename(temp, path_for(cache_key))
53
+ ensure
54
+ FileUtils.rm_f(temp) if temp
55
+ end
56
+
57
+ def read_record(cache_key)
58
+ JSON.parse(File.read(path_for(cache_key)))
59
+ rescue Errno::ENOENT, JSON::ParserError
60
+ nil
61
+ end
62
+
63
+ def fresh_record?(record)
64
+ return false unless record && record["schema_version"] == SCHEMA_VERSION
65
+
66
+ fetched_at = Time.iso8601(record.fetch("fetched_at"))
67
+ (@clock.call - fetched_at) <= @ttl_seconds
68
+ rescue StandardError
69
+ false
70
+ end
71
+
72
+ def versions_from(record)
73
+ Array(record["tags"]).filter_map { |tag|
74
+ begin
75
+ SpmVersionUpdates::Semver.new(tag)
76
+ rescue ArgumentError
77
+ nil
78
+ end
79
+ }
80
+ .sort
81
+ .reverse
82
+ end
83
+
84
+ def record_for(versions)
85
+ {
86
+ "schema_version" => SCHEMA_VERSION,
87
+ "fetched_at" => @clock.call.iso8601,
88
+ "tags" => versions.map(&:to_s)
89
+ }
90
+ end
91
+
92
+ def path_for(cache_key)
93
+ File.join(@directory, "#{cache_key}.json")
94
+ end
95
+
96
+ def temp_path(cache_key)
97
+ "#{path_for(cache_key)}.#{Process.pid}-#{Thread.current.object_id.abs}.tmp"
98
+ end
99
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "git_operations"
4
+ require_relative "package_resolved"
5
+ require_relative "xcode_project_package_reader"
6
+
7
+ # Xcode project and Package.resolved parsing (migrated from xcode.rb)
8
+ module XcodeParser
9
+ # Find the configured SPM dependencies in the xcodeproj.
10
+ #
11
+ # Keyed by the normalized repository URL (used to match against
12
+ # `Package.resolved` pins and `ignore-repos`), while the original,
13
+ # scheme-bearing `repository_url` is retained for git operations.
14
+ #
15
+ # @param [String] xcodeproj_path The path of the Xcode project
16
+ # @return [Hash<String, Hash>] normalized URL => { "repository_url", "requirement" }
17
+ def self.get_packages(xcodeproj_path)
18
+ raise(XcodeprojPathMustBeSet) if xcodeproj_path.nil? || xcodeproj_path.empty?
19
+
20
+ XcodeProjectPackageReader.package_references(xcodeproj_path).to_h { |package|
21
+ repository_url = package.repository_url
22
+ [
23
+ GitOperations.trim_repo_url(repository_url),
24
+ { "repository_url" => repository_url, "requirement" => package.requirement },
25
+ ]
26
+ }
27
+ end
28
+
29
+ # Extracts resolved versions from Package.resolved relative to an Xcode project.
30
+ # When a block is given, malformed resolved files are reported to it as
31
+ # `(resolved_path, error)` and skipped; without a block the error is raised.
32
+ # @param [String] xcodeproj_path The path to your Xcode project
33
+ # @raise [CouldNotFindResolvedFile] if no Package.resolved files were found
34
+ # @raise [PackageResolved::MalformedFileError] if a resolved file is invalid JSON and no block is given
35
+ # @return [Hash<String, String>]
36
+ def self.get_resolved_versions(xcodeproj_path)
37
+ resolved_paths = find_packages_resolved_file(xcodeproj_path)
38
+ raise(CouldNotFindResolvedFile) if resolved_paths.empty?
39
+
40
+ resolved_paths.each_with_object({}) { |resolved_path, pins|
41
+ begin
42
+ pins.merge!(PackageResolved.versions_from(resolved_path))
43
+ rescue PackageResolved::MalformedFileError => error
44
+ raise unless block_given?
45
+
46
+ yield(resolved_path, error)
47
+ end
48
+ }
49
+ end
50
+
51
+ # Find the Packages.resolved file
52
+ # @return [Array<String>]
53
+ def self.find_packages_resolved_file(xcodeproj_path)
54
+ checked = XcodeProjectPackageReader.package_resolved_candidate_paths(xcodeproj_path)
55
+ locations = checked.select { |path| File.exist?(path) }
56
+
57
+ puts("Checked Package.resolved paths: #{checked}")
58
+ puts("Found Package.resolved paths: #{locations}")
59
+ locations
60
+ end
61
+
62
+ private_class_method :find_packages_resolved_file
63
+
64
+ # Raised when Xcode project mode is invoked without a project path.
65
+ class XcodeprojPathMustBeSet < StandardError
66
+ end
67
+
68
+ # Raised when an Xcode project does not have a Package.resolved file.
69
+ class CouldNotFindResolvedFile < StandardError
70
+ end
71
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ autoload :Xcodeproj, "xcodeproj"
4
+
5
+ # Reads Swift package references and adjacent Package.resolved locations for an
6
+ # Xcode project without requiring Xcode to be installed.
7
+ module XcodeProjectPackageReader
8
+ # Lightweight package reference read from either project objects or pbxproj data.
9
+ PackageReference = Struct.new(:repository_url, :requirement, keyword_init: true)
10
+
11
+ # Builds the lightweight pbxproj parser fallback error list without triggering autoloads.
12
+ module PbxprojFallbackErrors
13
+ def self.to_a
14
+ [
15
+ SystemCallError,
16
+ IOError,
17
+ loaded_nested_constant(:Xcodeproj, :Informative),
18
+ loaded_nested_constant(:Nanaimo, :Error),
19
+ loaded_nested_constant(:CFPropertyList, :CFPlistError),
20
+ ].compact
21
+ end
22
+
23
+ def self.loaded_nested_constant(parent_name, child_name)
24
+ parent = loaded_constant(Object, parent_name)
25
+ loaded_constant(parent, child_name) if parent
26
+ end
27
+
28
+ def self.loaded_constant(namespace, name)
29
+ return unless namespace.kind_of?(Module)
30
+ return if namespace.autoload?(name)
31
+ return unless namespace.const_defined?(name, false)
32
+
33
+ namespace.const_get(name, false)
34
+ end
35
+ end
36
+
37
+ private_constant :PackageReference, :PbxprojFallbackErrors
38
+
39
+ def self.package_references(xcodeproj_path)
40
+ package_references_from_pbxproj(xcodeproj_path) || package_references_from_project(xcodeproj_path)
41
+ end
42
+
43
+ def self.package_resolved_candidate_paths(xcodeproj_path)
44
+ [
45
+ workspace_resolved_path(xcodeproj_path),
46
+ File.join(xcodeproj_path, "project.xcworkspace", "xcshareddata", "swiftpm", "Package.resolved"),
47
+ ].compact
48
+ end
49
+
50
+ def self.package_references_from_pbxproj(xcodeproj_path)
51
+ read_existing_pbxproj(xcodeproj_path)
52
+ rescue *PbxprojFallbackErrors.to_a => error
53
+ warn(pbxproj_fallback_message(pbxproj_path_for(xcodeproj_path), error))
54
+ nil
55
+ end
56
+
57
+ def self.read_existing_pbxproj(xcodeproj_path)
58
+ pbxproj_path = existing_pbxproj_path(xcodeproj_path)
59
+ return unless pbxproj_path
60
+
61
+ package_references_from_pbxproj_objects(pbxproj_objects(pbxproj_path))
62
+ end
63
+
64
+ def self.existing_pbxproj_path(xcodeproj_path)
65
+ pbxproj_path = pbxproj_path_for(xcodeproj_path)
66
+ pbxproj_path if File.file?(pbxproj_path)
67
+ end
68
+
69
+ def self.pbxproj_fallback_message(pbxproj_path, error)
70
+ "WARNING: Could not read #{pbxproj_path} with the lightweight pbxproj parser " \
71
+ "(#{error.class}: #{error.message}); falling back to full Xcode project parsing."
72
+ end
73
+
74
+ def self.pbxproj_path_for(xcodeproj_path)
75
+ File.join(xcodeproj_path, "project.pbxproj")
76
+ end
77
+
78
+ def self.pbxproj_objects(pbxproj_path)
79
+ Xcodeproj::Plist.read_from_path(pbxproj_path).fetch("objects", {}).values
80
+ end
81
+
82
+ def self.package_references_from_pbxproj_objects(objects)
83
+ objects.filter_map { |object| package_reference_from_plist_object(object) }
84
+ end
85
+
86
+ def self.package_reference_from_plist_object(object)
87
+ return unless object["isa"] == "XCRemoteSwiftPackageReference"
88
+
89
+ repository_url = object["repositoryURL"]
90
+ return if repository_url.to_s.strip.empty?
91
+
92
+ PackageReference.new(repository_url:, requirement: object["requirement"])
93
+ end
94
+
95
+ def self.package_references_from_project(xcodeproj_path)
96
+ package_references_from_project_objects(Xcodeproj::Project.open(xcodeproj_path).objects)
97
+ end
98
+
99
+ def self.package_references_from_project_objects(objects)
100
+ objects.filter_map { |object| package_reference_from_project_object(object) }
101
+ end
102
+
103
+ def self.package_reference_from_project_object(object)
104
+ return unless object.kind_of?(Xcodeproj::Project::Object::XCRemoteSwiftPackageReference)
105
+
106
+ repository_url = object.repositoryURL
107
+ return if repository_url.to_s.strip.empty?
108
+
109
+ PackageReference.new(repository_url:, requirement: object.requirement)
110
+ end
111
+
112
+ def self.workspace_resolved_path(xcodeproj_path)
113
+ path = xcodeproj_path.to_s.sub(%r{/+\z}, "")
114
+ return unless path.end_with?(".xcodeproj")
115
+
116
+ workspace = path.sub(/\.xcodeproj\z/, ".xcworkspace")
117
+ File.join(workspace, "xcshareddata", "swiftpm", "Package.resolved")
118
+ end
119
+
120
+ private_class_method :package_references_from_pbxproj,
121
+ :read_existing_pbxproj,
122
+ :existing_pbxproj_path,
123
+ :pbxproj_fallback_message,
124
+ :pbxproj_path_for,
125
+ :pbxproj_objects,
126
+ :package_references_from_pbxproj_objects,
127
+ :package_reference_from_plist_object,
128
+ :package_references_from_project,
129
+ :package_references_from_project_objects,
130
+ :package_reference_from_project_object,
131
+ :workspace_resolved_path
132
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "spm_version_updates/allow_host_normalizer"
4
+ require_relative "spm_version_updates/credential_redactor"
5
+ require_relative "spm_version_updates/fail_on_threshold"
6
+ require_relative "spm_version_updates/git_host_normalizer"
7
+ require_relative "spm_version_updates/git_operations"
8
+ require_relative "spm_version_updates/manifest_parser"
9
+ require_relative "spm_version_updates/package_resolved"
10
+ require_relative "spm_version_updates/repository_link"
11
+ require_relative "spm_version_updates/repository_update_rules"
12
+ require_relative "spm_version_updates/semver"
13
+ require_relative "spm_version_updates/spm_checker"
14
+ require_relative "spm_version_updates/spm_package_context"
15
+ require_relative "spm_version_updates/update_severity"
16
+ require_relative "spm_version_updates/upgrade_suggestion"
17
+ require_relative "spm_version_updates/version"
18
+ require_relative "spm_version_updates/version_tag_fetcher"
19
+ require_relative "spm_version_updates/version_tags_persistent_cache"
20
+ require_relative "spm_version_updates/xcode_parser"
21
+ require_relative "spm_version_updates/xcode_project_package_reader"
@@ -0,0 +1,46 @@
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/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "spm_version_updates"
9
+ spec.version = SpmVersionUpdates::VERSION
10
+ spec.authors = ["Harold Martin"]
11
+ spec.email = ["harold.martin@gmail.com"]
12
+ spec.description = "Detect available updates to Swift Package Manager dependencies " \
13
+ "from Package.swift manifests or Xcode projects."
14
+ spec.summary = "Core library for checking Swift Package Manager dependency updates."
15
+ spec.homepage = "https://github.com/hbmartin/github-action-spm_version_updates"
16
+ spec.license = "MIT"
17
+ spec.required_ruby_version = ">= 3.2"
18
+
19
+ release_paths = [
20
+ "LICENSE.txt",
21
+ "README.md",
22
+ "spm_version_updates.gemspec",
23
+ ]
24
+ spec.files = begin
25
+ git_files = begin
26
+ IO.popen(
27
+ ["git", "-C", __dir__, "ls-files", "-z", "lib", *release_paths],
28
+ err: File::NULL,
29
+ &:read
30
+ )
31
+ .split("\x0")
32
+ .reject(&:empty?)
33
+ rescue Errno::ENOENT
34
+ []
35
+ end
36
+ fallback_files = Dir.glob(["lib/**/*", *release_paths], base: __dir__)
37
+ .select { |path| File.file?(File.join(__dir__, path)) }
38
+ (git_files.empty? ? fallback_files : git_files).sort
39
+ end
40
+ spec.require_paths = ["lib"]
41
+ spec.metadata["rubygems_mfa_required"] = "true"
42
+
43
+ spec.add_runtime_dependency("semverify", "~> 0.3")
44
+ # Xcode-project mode additionally requires the "xcodeproj" gem (loaded
45
+ # lazily); manifest mode works without it.
46
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spm_version_updates
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Harold Martin
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: semverify
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.3'
26
+ description: Detect available updates to Swift Package Manager dependencies from Package.swift
27
+ manifests or Xcode projects.
28
+ email:
29
+ - harold.martin@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE.txt
35
+ - README.md
36
+ - lib/spm_version_updates.rb
37
+ - lib/spm_version_updates/allow_host_normalizer.rb
38
+ - lib/spm_version_updates/credential_redactor.rb
39
+ - lib/spm_version_updates/fail_on_threshold.rb
40
+ - lib/spm_version_updates/git_host_normalizer.rb
41
+ - lib/spm_version_updates/git_operations.rb
42
+ - lib/spm_version_updates/manifest_parser.rb
43
+ - lib/spm_version_updates/package_resolved.rb
44
+ - lib/spm_version_updates/repository_link.rb
45
+ - lib/spm_version_updates/repository_update_rules.rb
46
+ - lib/spm_version_updates/semver.rb
47
+ - lib/spm_version_updates/spm_checker.rb
48
+ - lib/spm_version_updates/spm_package_context.rb
49
+ - lib/spm_version_updates/update_severity.rb
50
+ - lib/spm_version_updates/upgrade_suggestion.rb
51
+ - lib/spm_version_updates/version.rb
52
+ - lib/spm_version_updates/version_tag_fetcher.rb
53
+ - lib/spm_version_updates/version_tags_persistent_cache.rb
54
+ - lib/spm_version_updates/xcode_parser.rb
55
+ - lib/spm_version_updates/xcode_project_package_reader.rb
56
+ - spm_version_updates.gemspec
57
+ homepage: https://github.com/hbmartin/github-action-spm_version_updates
58
+ licenses:
59
+ - MIT
60
+ metadata:
61
+ rubygems_mfa_required: 'true'
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '3.2'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubygems_version: 4.0.10
77
+ specification_version: 4
78
+ summary: Core library for checking Swift Package Manager dependency updates.
79
+ test_files: []