equilibrium 0.1.0 → 0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5420df18928550fa411f853968f4b5ac331fbcd3bcc682dce61370c330dd69bc
4
- data.tar.gz: 062322ae383ea3aba97d0d60980f8530808e7877e5bb5a6ce311435ec6a86814
3
+ metadata.gz: d710d1c2fea441e38c5b328c036506115b7b80ade1aae20ffda4badbf9c72b76
4
+ data.tar.gz: c99ba87639818a92306c31c2764a604238a98607d4d655b63e6536c32b64af67
5
5
  SHA512:
6
- metadata.gz: 3900bcb75092a001f250dbe206d4085419435e7d2b370c3a68e57e82619e1a7b014dd32a71510f887e060244c122336f70f946bd836e1c2ef956d5500416d93c
7
- data.tar.gz: 2678994b5a362aa3e282618dd49f401aa63d50905f13b0fc74323d0747dc73d076aa872c915843e8d4002a9f1042992e33b5251a5f10cbeb9405d8bb2d29454d
6
+ metadata.gz: d5ccfd2ac6a95cf83efeed1dc59b27e1c474f9211e6155da8ae35938bc9a4316c002509e9fb60757403c3130359b1f854a0aaf84bfd9c470caacdaa5cb8c9f14
7
+ data.tar.gz: 76445a5a0e8e0f2e0a1f347394b44700a9e65fd2b3d23eaad0d8c1f387d54619aad184d9aa2fa50b6286660e140221cfef8e03def80768cbf5b5421e7aa61e2e
data/CHANGELOG.md CHANGED
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.1] - 2025-08-06
9
+
10
+ ### Added
11
+ - GitHub Packages publishing support in release workflow
12
+ - Draft mode for GitHub releases requiring manual review
13
+ - Enhanced release automation with artifact attachment
14
+
15
+ ### Fixed
16
+ - Preserve descending tag order in summary format output
17
+ - Consistent descending ordering for expected and actual outputs
18
+
19
+ ### Changed
20
+ - Reorganized spec files to mirror lib directory structure
21
+ - Extracted TagSorter utility with comprehensive unit tests
22
+ - Enhanced RegistryClient with pagination analysis capabilities
23
+
8
24
  ## [0.1.0] - 2025-08-05
9
25
 
10
26
  ### Added
@@ -37,4 +53,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
37
53
  - Detailed architecture overview and data flow diagrams
38
54
  - Complete command reference and examples
39
55
 
56
+ [0.1.1]: https://github.com/DataDog/equilibrium/releases/tag/v0.1.1
40
57
  [0.1.0]: https://github.com/DataDog/equilibrium/releases/tag/v0.1.0
@@ -51,11 +51,15 @@ module Equilibrium
51
51
  # Extract repository name from URL
52
52
  repository_name = extract_repository_name(full_repository_url)
53
53
 
54
+ # Sort both digests and canonical_versions in descending order right before output
55
+ sorted_digests = TagSorter.sort_descending(virtual_tags_result["digests"])
56
+ sorted_canonical_versions = TagSorter.sort_descending(virtual_tags_result["canonical_versions"])
57
+
54
58
  output = {
55
59
  "repository_url" => full_repository_url,
56
60
  "repository_name" => repository_name,
57
- "digests" => virtual_tags_result["digests"],
58
- "canonical_versions" => virtual_tags_result["canonical_versions"]
61
+ "digests" => sorted_digests,
62
+ "canonical_versions" => sorted_canonical_versions
59
63
  }
60
64
 
61
65
  # Validate output against schema before writing
@@ -100,11 +104,15 @@ module Equilibrium
100
104
  # Extract repository name from URL
101
105
  repository_name = extract_repository_name(full_repository_url)
102
106
 
107
+ # Sort both digests and canonical_versions in descending order right before output
108
+ sorted_digests = TagSorter.sort_descending(mutable_tags)
109
+ sorted_canonical_versions = TagSorter.sort_descending(canonical_versions)
110
+
103
111
  output = {
104
112
  "repository_url" => full_repository_url,
105
113
  "repository_name" => repository_name,
106
- "digests" => mutable_tags,
107
- "canonical_versions" => canonical_versions
114
+ "digests" => sorted_digests,
115
+ "canonical_versions" => sorted_canonical_versions
108
116
  }
109
117
 
110
118
  # Validate output against schema before writing
@@ -26,6 +26,29 @@ module Equilibrium
26
26
  # by some registries (like GCR). Most registries follow the Docker Registry v2 spec
27
27
  # which only returns 'name' and 'tags' fields. To get digest information, separate
28
28
  # calls to /v2/<name>/manifests/<tag> are required per the official specification.
29
+ #
30
+ # PAGINATION ANALYSIS (August 2025):
31
+ # Current implementation fetches ALL tags in single API call. Analysis of production registries:
32
+ # - apm-inject: 29 tags
33
+ # - dd-lib-dotnet-init: 31 tags
34
+ # - dd-lib-java-init: 18 tags
35
+ # - dd-lib-js-init: 56 tags (largest)
36
+ # - dd-lib-php-init: 10 tags
37
+ # - dd-lib-python-init: 27 tags
38
+ # - dd-lib-ruby-init: 19 tags
39
+ # Total: 190 tags across all registries
40
+ #
41
+ # GCR PAGINATION RESEARCH FINDINGS:
42
+ # - DOCUMENTED LIMIT: 10,000 items for format-specific API requests
43
+ # Source: https://cloud.google.com/artifact-registry/quotas
44
+ # - NO PAGINATION SUPPORT: GCR ignores Docker Registry v2 pagination parameters ('n', 'last')
45
+ # Source: https://stackoverflow.com/questions/38307259 (unresolved since 2016)
46
+ # - ALL-OR-NOTHING: Returns complete tag lists up to 10k limit, then truncates unpredictably
47
+ # - NO PER-PAGE LIMIT: Not documented, not applicable due to lack of pagination support
48
+ #
49
+ # RECOMMENDATION: Current single-request approach is sufficient.
50
+ # Implementation complexity for pagination: 3-4 hours with minimal benefit.
51
+ # Consider only if individual repositories approach thousands of tags.
29
52
  class RegistryClient
30
53
  class Error < StandardError; end
31
54
 
@@ -104,7 +104,7 @@ module Equilibrium
104
104
 
105
105
  # Create table data: [["Tag", "Version", "Digest"]]
106
106
  table_data = [["Tag", "Version", "Digest"]]
107
- mutable_tags.keys.sort.each do |tag|
107
+ mutable_tags.keys.each do |tag|
108
108
  canonical_version = canonical_versions[tag]
109
109
  digest = mutable_tags[tag]
110
110
  table_data << [tag, canonical_version, digest]
@@ -57,16 +57,12 @@ module Equilibrium
57
57
 
58
58
  def filter_semantic_tags(all_tags)
59
59
  # Filter semantic tags (canonical_tags.json): exact major.minor.patch format
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
60
+ all_tags.select { |tag, _| semantic_version?(tag) }
63
61
  end
64
62
 
65
63
  def filter_mutable_tags(all_tags)
66
64
  # Filter mutable tags (actual_tags.json): latest, digits, or major.minor format
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
65
+ all_tags.select { |tag, _| mutable_tag?(tag) }
70
66
  end
71
67
 
72
68
  private
@@ -0,0 +1,53 @@
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
+ new.sort_descending(tags_hash)
8
+ end
9
+
10
+ def sort_descending(tags_hash)
11
+ return {} if tags_hash.nil? || tags_hash.empty?
12
+
13
+ sorted = {}
14
+
15
+ # Add latest first if present
16
+ if tags_hash.key?("latest")
17
+ sorted["latest"] = tags_hash["latest"]
18
+ end
19
+
20
+ # Sort other tags by version (descending)
21
+ other_tags = tags_hash.keys.reject { |k| k == "latest" }
22
+ sorted_tags = other_tags.sort_by do |tag|
23
+ if major_version?(tag)
24
+ # Major version: sort by numeric value (descending)
25
+ [-tag.to_i]
26
+ elsif minor_version?(tag)
27
+ # Minor version: sort by version (descending)
28
+ parts = tag.split(".").map(&:to_i)
29
+ [-parts[0], -parts[1]]
30
+ else
31
+ # Fallback for any unexpected formats - sort alphabetically
32
+ [1, tag]
33
+ end
34
+ end
35
+
36
+ sorted_tags.each do |tag|
37
+ sorted[tag] = tags_hash[tag]
38
+ end
39
+
40
+ sorted
41
+ end
42
+
43
+ private
44
+
45
+ def major_version?(tag)
46
+ tag.match?(/^[0-9]+$/)
47
+ end
48
+
49
+ def minor_version?(tag)
50
+ tag.match?(/^[0-9]+\.[0-9]+$/)
51
+ end
52
+ end
53
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Equilibrium
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
data/lib/equilibrium.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "equilibrium/version"
4
4
  require_relative "equilibrium/semantic_version"
5
5
  require_relative "equilibrium/registry_client"
6
6
  require_relative "equilibrium/tag_processor"
7
+ require_relative "equilibrium/tag_sorter"
7
8
  require_relative "equilibrium/catalog_builder"
8
9
  require_relative "equilibrium/analyzer"
9
10
  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.1.0
4
+ version: 0.1.1
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-05 00:00:00.000000000 Z
11
+ date: 2025-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -82,6 +82,7 @@ files:
82
82
  - lib/equilibrium/semantic_version.rb
83
83
  - lib/equilibrium/summary_formatter.rb
84
84
  - lib/equilibrium/tag_processor.rb
85
+ - lib/equilibrium/tag_sorter.rb
85
86
  - lib/equilibrium/version.rb
86
87
  - tmp/.gitkeep
87
88
  homepage: https://github.com/TonyCTHsu/equilibrium
@@ -91,6 +92,7 @@ metadata:
91
92
  homepage_uri: https://github.com/TonyCTHsu/equilibrium
92
93
  source_code_uri: https://github.com/TonyCTHsu/equilibrium
93
94
  changelog_uri: https://github.com/TonyCTHsu/equilibrium/blob/master/CHANGELOG.md
95
+ github_repo: ssh://github.com/TonyCTHsu/tobee
94
96
  post_install_message:
95
97
  rdoc_options: []
96
98
  require_paths: