equilibrium 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -3
- data/README.md +41 -37
- data/lib/equilibrium/analyzer.rb +16 -14
- data/lib/equilibrium/canonical_version_mapper.rb +19 -0
- data/lib/equilibrium/catalog_builder.rb +31 -15
- data/lib/equilibrium/cli.rb +17 -172
- data/lib/equilibrium/commands/actual_command.rb +36 -0
- data/lib/equilibrium/commands/analyze_command.rb +49 -0
- data/lib/equilibrium/commands/catalog_command.rb +38 -0
- data/lib/equilibrium/commands/expected_command.rb +36 -0
- data/lib/equilibrium/commands/uncatalog_command.rb +42 -0
- data/lib/equilibrium/commands/version_command.rb +16 -0
- data/lib/equilibrium/mixins/error_handling.rb +34 -0
- data/lib/equilibrium/mixins/input_output.rb +40 -0
- data/lib/equilibrium/registry_client.rb +57 -45
- data/lib/equilibrium/repository_tags_service.rb +32 -0
- data/lib/equilibrium/repository_url_validator.rb +14 -0
- data/lib/equilibrium/schema_validator.rb +10 -2
- data/lib/equilibrium/schemas/analyzer_output.rb +8 -2
- data/lib/equilibrium/schemas/catalog.rb +16 -9
- data/lib/equilibrium/schemas/expected_actual.rb +1 -1
- data/lib/equilibrium/schemas/registry_api.rb +1 -1
- data/lib/equilibrium/summary_formatter.rb +1 -1
- data/lib/equilibrium/tag_data_builder.rb +16 -0
- data/lib/equilibrium/tag_processor.rb +24 -14
- data/lib/equilibrium/tag_sorter.rb +39 -0
- data/lib/equilibrium/tags_operation_service.rb +32 -0
- data/lib/equilibrium/version.rb +1 -1
- data/lib/equilibrium.rb +1 -1
- metadata +18 -4
- data/lib/equilibrium/semantic_version.rb +0 -24
@@ -8,6 +8,14 @@ module Equilibrium
|
|
8
8
|
new.compute_virtual_tags(semantic_tags)
|
9
9
|
end
|
10
10
|
|
11
|
+
def self.filter_semantic_tags(tagged_digests)
|
12
|
+
new.filter_semantic_tags(tagged_digests)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.filter_mutable_tags(tagged_digests)
|
16
|
+
new.filter_mutable_tags(tagged_digests)
|
17
|
+
end
|
18
|
+
|
11
19
|
def compute_virtual_tags(semantic_tags)
|
12
20
|
return {"digests" => {}, "canonical_versions" => {}} if semantic_tags.empty?
|
13
21
|
|
@@ -55,30 +63,32 @@ module Equilibrium
|
|
55
63
|
{"digests" => digests, "canonical_versions" => canonical_versions}
|
56
64
|
end
|
57
65
|
|
58
|
-
def filter_semantic_tags(
|
59
|
-
|
60
|
-
filtered = all_tags.select { |tag, _| semantic_version?(tag) }
|
61
|
-
# Sort by key in reverse order (matching original jq: sort_by(.key) | reverse)
|
62
|
-
filtered.sort_by { |tag, _| tag }.reverse.to_h
|
66
|
+
def filter_semantic_tags(tagged_digests)
|
67
|
+
tagged_digests.select { |tag, _| semantic_version?(tag) }
|
63
68
|
end
|
64
69
|
|
65
|
-
def filter_mutable_tags(
|
66
|
-
|
67
|
-
filtered = all_tags.select { |tag, _| mutable_tag?(tag) }
|
68
|
-
# Sort by key in reverse order (matching original jq: sort_by(.key) | reverse)
|
69
|
-
filtered.sort_by { |tag, _| tag }.reverse.to_h
|
70
|
+
def filter_mutable_tags(tagged_digests)
|
71
|
+
tagged_digests.select { |tag, _| mutable_tag?(tag) }
|
70
72
|
end
|
71
73
|
|
72
74
|
private
|
73
75
|
|
74
76
|
def semantic_version?(tag)
|
75
|
-
|
77
|
+
# Strictly validate MAJOR.MINOR.PATCH format where:
|
78
|
+
# - MAJOR, MINOR, PATCH are non-negative integers
|
79
|
+
# - No leading zeros (except for '0' itself)
|
80
|
+
# - No prefixes (like 'v1.2.3', 'release-1.2.3')
|
81
|
+
# - No suffixes (like '1.2.3-alpha', '1.2.3+build')
|
82
|
+
# - No prereleases (like '1.2.3-rc.1', '1.2.3-beta.2')
|
83
|
+
tag.match?(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/)
|
76
84
|
end
|
77
85
|
|
78
86
|
def mutable_tag?(tag)
|
79
|
-
|
80
|
-
|
81
|
-
|
87
|
+
# Validate mutable tags: latest, major versions (digits only), or major.minor format
|
88
|
+
# - 'latest' is the special case for the highest overall version
|
89
|
+
# - Major versions: non-negative integers without leading zeros (e.g., '1', '0', '42')
|
90
|
+
# - Minor versions: MAJOR.MINOR format with same zero-leading rules (e.g., '1.2', '0.9')
|
91
|
+
tag.match?(/^(latest|(0|[1-9]\d*)(\.(0|[1-9]\d*))?)$/)
|
82
92
|
end
|
83
93
|
end
|
84
94
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Equilibrium
|
4
|
+
class TagSorter
|
5
|
+
# Sort tags in descending version order: latest first, then major versions (descending), then minor versions (descending)
|
6
|
+
def self.sort_descending(tags_hash)
|
7
|
+
return {} if tags_hash.nil? || tags_hash.empty?
|
8
|
+
|
9
|
+
sorted = {}
|
10
|
+
|
11
|
+
# Add latest first if present
|
12
|
+
if tags_hash.key?("latest")
|
13
|
+
sorted["latest"] = tags_hash["latest"]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Sort other tags by version (descending)
|
17
|
+
other_tags = tags_hash.keys.reject { |k| k == "latest" }
|
18
|
+
sorted_tags = other_tags.sort_by do |tag|
|
19
|
+
if tag.match?(/^[0-9]+$/)
|
20
|
+
# Major version: sort by numeric value (descending)
|
21
|
+
[-tag.to_i]
|
22
|
+
elsif tag.match?(/^[0-9]+\.[0-9]+$/)
|
23
|
+
# Minor version: sort by version (descending)
|
24
|
+
parts = tag.split(".").map(&:to_i)
|
25
|
+
[-parts[0], -parts[1]]
|
26
|
+
else
|
27
|
+
# Fallback for any unexpected formats - sort alphabetically
|
28
|
+
[1, tag]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
sorted_tags.each do |tag|
|
33
|
+
sorted[tag] = tags_hash[tag]
|
34
|
+
end
|
35
|
+
|
36
|
+
sorted
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "repository_tags_service"
|
4
|
+
require_relative "tag_data_builder"
|
5
|
+
|
6
|
+
module Equilibrium
|
7
|
+
class TagsOperationService
|
8
|
+
def self.generate_expected_output(repository_url)
|
9
|
+
tag_data = RepositoryTagsService.generate_expected_tags(repository_url)
|
10
|
+
repository_name = repository_url.split("/").last
|
11
|
+
|
12
|
+
TagDataBuilder.build_output(
|
13
|
+
repository_url,
|
14
|
+
repository_name,
|
15
|
+
tag_data["digests"],
|
16
|
+
tag_data["canonical_versions"]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.generate_actual_output(repository_url)
|
21
|
+
tag_data = RepositoryTagsService.generate_actual_tags(repository_url)
|
22
|
+
repository_name = repository_url.split("/").last
|
23
|
+
|
24
|
+
TagDataBuilder.build_output(
|
25
|
+
repository_url,
|
26
|
+
repository_name,
|
27
|
+
tag_data["digests"],
|
28
|
+
tag_data["canonical_versions"]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/equilibrium/version.rb
CHANGED
data/lib/equilibrium.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "equilibrium/version"
|
4
|
-
require_relative "equilibrium/semantic_version"
|
5
4
|
require_relative "equilibrium/registry_client"
|
6
5
|
require_relative "equilibrium/tag_processor"
|
6
|
+
require_relative "equilibrium/tag_sorter"
|
7
7
|
require_relative "equilibrium/catalog_builder"
|
8
8
|
require_relative "equilibrium/analyzer"
|
9
9
|
require_relative "equilibrium/schemas/expected_actual"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: equilibrium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Hsu
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -71,17 +71,30 @@ files:
|
|
71
71
|
- exe/equilibrium
|
72
72
|
- lib/equilibrium.rb
|
73
73
|
- lib/equilibrium/analyzer.rb
|
74
|
+
- lib/equilibrium/canonical_version_mapper.rb
|
74
75
|
- lib/equilibrium/catalog_builder.rb
|
75
76
|
- lib/equilibrium/cli.rb
|
77
|
+
- lib/equilibrium/commands/actual_command.rb
|
78
|
+
- lib/equilibrium/commands/analyze_command.rb
|
79
|
+
- lib/equilibrium/commands/catalog_command.rb
|
80
|
+
- lib/equilibrium/commands/expected_command.rb
|
81
|
+
- lib/equilibrium/commands/uncatalog_command.rb
|
82
|
+
- lib/equilibrium/commands/version_command.rb
|
83
|
+
- lib/equilibrium/mixins/error_handling.rb
|
84
|
+
- lib/equilibrium/mixins/input_output.rb
|
76
85
|
- lib/equilibrium/registry_client.rb
|
86
|
+
- lib/equilibrium/repository_tags_service.rb
|
87
|
+
- lib/equilibrium/repository_url_validator.rb
|
77
88
|
- lib/equilibrium/schema_validator.rb
|
78
89
|
- lib/equilibrium/schemas/analyzer_output.rb
|
79
90
|
- lib/equilibrium/schemas/catalog.rb
|
80
91
|
- lib/equilibrium/schemas/expected_actual.rb
|
81
92
|
- lib/equilibrium/schemas/registry_api.rb
|
82
|
-
- lib/equilibrium/semantic_version.rb
|
83
93
|
- lib/equilibrium/summary_formatter.rb
|
94
|
+
- lib/equilibrium/tag_data_builder.rb
|
84
95
|
- lib/equilibrium/tag_processor.rb
|
96
|
+
- lib/equilibrium/tag_sorter.rb
|
97
|
+
- lib/equilibrium/tags_operation_service.rb
|
85
98
|
- lib/equilibrium/version.rb
|
86
99
|
- tmp/.gitkeep
|
87
100
|
homepage: https://github.com/TonyCTHsu/equilibrium
|
@@ -90,7 +103,8 @@ licenses:
|
|
90
103
|
metadata:
|
91
104
|
homepage_uri: https://github.com/TonyCTHsu/equilibrium
|
92
105
|
source_code_uri: https://github.com/TonyCTHsu/equilibrium
|
93
|
-
changelog_uri: https://github.com/TonyCTHsu/equilibrium/blob/
|
106
|
+
changelog_uri: https://github.com/TonyCTHsu/equilibrium/blob/v0.2.0/CHANGELOG.md
|
107
|
+
github_repo: ssh://github.com/TonyCTHsu/equilibrium
|
94
108
|
post_install_message:
|
95
109
|
rdoc_options: []
|
96
110
|
require_paths:
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Equilibrium
|
4
|
-
module SemanticVersion
|
5
|
-
# Strictly validate MAJOR.MINOR.PATCH format where:
|
6
|
-
# - MAJOR, MINOR, PATCH are non-negative integers
|
7
|
-
# - No leading zeros (except for '0' itself)
|
8
|
-
# - No prefixes (like 'v1.2.3', 'release-1.2.3')
|
9
|
-
# - No suffixes (like '1.2.3-alpha', '1.2.3+build')
|
10
|
-
# - No prereleases (like '1.2.3-rc.1', '1.2.3-beta.2')
|
11
|
-
def self.valid?(tag)
|
12
|
-
# Strict regex: each component must be either '0' or a number without leading zeros
|
13
|
-
return false unless tag.match?(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/)
|
14
|
-
|
15
|
-
# Additional validation: ensure it's a valid Gem::Version
|
16
|
-
begin
|
17
|
-
Gem::Version.new(tag)
|
18
|
-
true
|
19
|
-
rescue ArgumentError
|
20
|
-
false
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|