gem_changelog_diff 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f774074fe0186c0efa8cd2aef588301c81441f20de1c1b75cd64e0da40bdcb0a
4
- data.tar.gz: cca9f1421c6e505a7b137727e8948e3d5fa7749c226e6e43c51fcd29d1781a2c
3
+ metadata.gz: 157c1e084602c25e10cec4fde2c24b1999a9894ebe1232901cb9536bae50f33b
4
+ data.tar.gz: aa0be940c412f3b1a9b3ee08190d8a6e64ad0bed9cf1b2011979650b87264640
5
5
  SHA512:
6
- metadata.gz: cfe4e6aadaa4310be3a007ed61ae85d0a3aad6938599030b9914f35f41b066e9ed04cef679762faedfc8ebd406156c45688e13f84864645e9168aa73eaf22a54
7
- data.tar.gz: 76f9ea402f5cab49fbe8ecf9466ee73fc633e4a3a35c8dc77aee54fe79763cacd40f775c039c1cde51f8e8f49efbe8cebaa8420da6d7cac549097f4f18a2a85a
6
+ metadata.gz: 0714bc35cee74f49ac0b09cda00e58b931c90a9f0a7688e122f5c8f2c790aedd269244d6e5a0af2dd538a33e8a91e2f8024b313f2c94b6227a4b3fa6901a160c
7
+ data.tar.gz: 8fb0ab418ac9c7e42adfd240360594ef1134536fe9ffa04a2dad8052d3355620d79d224b8f4342336bf853875ac60491ed6968ad8d105860dd770bccb6682fae
data/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] - 2026-06-17
11
+
12
+ ### Added
13
+
14
+ - GitHub personal access token support via `--token` flag or `GITHUB_TOKEN` env var
15
+ - `Configuration` singleton for managing runtime settings
16
+ - Custom error hierarchy: `RepoNotFoundError`, `GitHubAPIError`, `RateLimitError`, `NetworkError`
17
+ - Graceful degradation: failed gems are skipped with a warning instead of aborting
18
+ - Rate limit awareness: warns when GitHub API requests remaining drops below 10
19
+ - `--verbose` flag for detailed status output
20
+ - `--quiet` flag to suppress warnings
21
+
10
22
  ## [0.1.0] - 2026-06-17
11
23
 
12
24
  ### Added
@@ -18,3 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
30
  - GitHub API client to fetch releases between locked and latest versions
19
31
  - Plain text formatter for changelog output
20
32
  - Full end-to-end pipeline: detect → lookup → fetch → format
33
+
34
+ [Unreleased]: https://github.com/eclectic-coding/gem_changelog_diff/compare/v0.2.0...HEAD
35
+ [0.2.0]: https://github.com/eclectic-coding/gem_changelog_diff/releases/tag/v0.2.0
36
+ [0.1.0]: https://github.com/eclectic-coding/gem_changelog_diff/releases/tag/v0.1.0
data/README.md CHANGED
@@ -35,6 +35,24 @@ gem_changelog_diff version # Print version
35
35
  gem_changelog_diff --version # Same as above
36
36
  ```
37
37
 
38
+ ### GitHub Authentication
39
+
40
+ To avoid the 60 requests/hour unauthenticated rate limit, provide a GitHub personal access token:
41
+
42
+ ```bash
43
+ gem_changelog_diff --token ghp_your_token
44
+ # or
45
+ export GITHUB_TOKEN=ghp_your_token
46
+ gem_changelog_diff
47
+ ```
48
+
49
+ ### Output Control
50
+
51
+ ```bash
52
+ gem_changelog_diff --verbose # Show detailed status messages
53
+ gem_changelog_diff --quiet # Suppress warnings
54
+ ```
55
+
38
56
  ## Development
39
57
 
40
58
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/ROADMAP.md CHANGED
@@ -2,20 +2,6 @@
2
2
 
3
3
  Feature roadmap for gem_changelog_diff. Each section is auto-pruned by `bin/release` when that version ships.
4
4
 
5
- ## 0.2.0 -- Error Handling & GitHub Authentication
6
-
7
- Handle real-world failures and unblock power users hitting the 60 req/hr unauthenticated rate limit.
8
-
9
- - GitHub personal access token via `--token` flag or `GITHUB_TOKEN` env var
10
- - Custom error hierarchy (`RepoNotFoundError`, `GitHubAPIError`, `RateLimitError`, `NetworkError`)
11
- - Graceful degradation: skip failed gems with a warning, do not abort the run
12
- - Rate limit awareness: read `X-RateLimit-Remaining` headers, warn when approaching the limit
13
- - `--verbose` and `--quiet` flags
14
-
15
- **New files:** `errors.rb`, `configuration.rb`
16
-
17
- ---
18
-
19
5
  ## 0.3.0 -- CHANGELOG.md Fallback & Colored Output
20
6
 
21
7
  Many gems do not use GitHub Releases. Fall back to parsing CHANGELOG.md from the repository.
@@ -10,8 +10,13 @@ module GemChangelogDiff
10
10
 
11
11
  default_task :check
12
12
 
13
+ class_option :token, type: :string, desc: "GitHub personal access token"
14
+ class_option :verbose, type: :boolean, default: false, desc: "Show detailed output"
15
+ class_option :quiet, type: :boolean, default: false, desc: "Suppress warnings"
16
+
13
17
  desc "check", "Show changelog diffs for outdated gems"
14
18
  def check
19
+ configure_token
15
20
  gems = Detector.new.detect
16
21
 
17
22
  if gems.empty?
@@ -32,6 +37,11 @@ module GemChangelogDiff
32
37
 
33
38
  private
34
39
 
40
+ def configure_token
41
+ token = options[:token] || ENV.fetch("GITHUB_TOKEN", nil)
42
+ GemChangelogDiff.configuration.github_token = token if token
43
+ end
44
+
35
45
  def build_reports(gems)
36
46
  rubygems_client = RubygemsClient.new
37
47
  github_client = GithubClient.new
@@ -40,11 +50,24 @@ module GemChangelogDiff
40
50
  end
41
51
 
42
52
  def build_gem_report(gem, rubygems_client, github_client)
53
+ log "Checking #{gem.name}..."
43
54
  repo = rubygems_client.repo_url(gem.name)
44
55
  return { gem: gem, releases: [], error: " Could not find GitHub repository." } if repo.nil?
45
56
 
57
+ log " Found repo: #{repo}"
46
58
  releases = github_client.releases_between(repo, gem.current_version, gem.newest_version)
47
59
  { gem: gem, releases: releases }
60
+ rescue GemChangelogDiff::Error => e
61
+ log_warning " Skipping #{gem.name}: #{e.message}"
62
+ { gem: gem, releases: [], error: " #{e.message}" }
63
+ end
64
+
65
+ def log(message)
66
+ warn message if options[:verbose]
67
+ end
68
+
69
+ def log_warning(message)
70
+ warn message unless options[:quiet]
48
71
  end
49
72
  end
50
73
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GemChangelogDiff
4
+ class Configuration
5
+ attr_accessor :github_token
6
+ end
7
+
8
+ def self.configuration
9
+ @configuration ||= Configuration.new
10
+ end
11
+
12
+ def self.configure
13
+ yield(configuration)
14
+ end
15
+
16
+ def self.reset_configuration!
17
+ @configuration = Configuration.new
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GemChangelogDiff
4
+ class RepoNotFoundError < Error; end
5
+ class GitHubAPIError < Error; end
6
+ class RateLimitError < GitHubAPIError; end
7
+ class NetworkError < Error; end
8
+ end
@@ -7,6 +7,7 @@ module GemChangelogDiff
7
7
  class GithubClient
8
8
  RELEASES_URL = "https://api.github.com/repos/%<repo>s/releases"
9
9
  TAG_VERSION_REGEX = /\Av?(\d+\..+)\z/
10
+ RATE_LIMIT_WARNING_THRESHOLD = 10
10
11
 
11
12
  def releases_between(repo, current_version, newest_version)
12
13
  releases = fetch_releases(repo)
@@ -19,19 +20,47 @@ module GemChangelogDiff
19
20
  uri = URI(format(RELEASES_URL, repo: repo))
20
21
  uri.query = URI.encode_www_form(per_page: 30)
21
22
 
23
+ response = execute_request(uri)
24
+ check_rate_limit(response)
25
+ handle_response(response)
26
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH,
27
+ Net::OpenTimeout, Net::ReadTimeout => e
28
+ raise NetworkError, "GitHub API request failed: #{e.message}"
29
+ end
30
+
31
+ def execute_request(uri)
22
32
  request = Net::HTTP::Get.new(uri)
23
33
  request["Accept"] = "application/vnd.github.v3+json"
24
34
  request["User-Agent"] = "gem_changelog_diff/#{VERSION}"
35
+ apply_auth(request)
36
+
37
+ Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
38
+ end
39
+
40
+ def apply_auth(request)
41
+ token = GemChangelogDiff.configuration.github_token
42
+ request["Authorization"] = "token #{token}" if token
43
+ end
44
+
45
+ def handle_response(response)
46
+ return [] if response.code == "404"
25
47
 
26
- response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
27
- http.request(request)
48
+ if response.code == "403" && response["X-RateLimit-Remaining"] == "0"
49
+ raise RateLimitError, "GitHub API rate limit exceeded. Use --token to authenticate."
28
50
  end
29
51
 
30
- return [] unless response.is_a?(Net::HTTPSuccess)
52
+ raise GitHubAPIError, "GitHub API error (HTTP #{response.code})" unless response.is_a?(Net::HTTPSuccess)
31
53
 
32
54
  JSON.parse(response.body)
33
55
  end
34
56
 
57
+ def check_rate_limit(response)
58
+ remaining = response["X-RateLimit-Remaining"]&.to_i
59
+ return if remaining.nil? || remaining >= RATE_LIMIT_WARNING_THRESHOLD
60
+
61
+ warn "Warning: GitHub API rate limit low (#{remaining} requests remaining). Use --token to authenticate."
62
+ end
63
+
35
64
  def filter_releases(releases, current_version, newest_version)
36
65
  current = Gem::Version.new(current_version)
37
66
  newest = Gem::Version.new(newest_version)
@@ -15,6 +15,9 @@ module GemChangelogDiff
15
15
 
16
16
  data = JSON.parse(response.body)
17
17
  extract_github_repo(data)
18
+ rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH,
19
+ Net::OpenTimeout, Net::ReadTimeout => e
20
+ raise NetworkError, "RubyGems API request failed: #{e.message}"
18
21
  end
19
22
 
20
23
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GemChangelogDiff
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "gem_changelog_diff/version"
4
+ require_relative "gem_changelog_diff/configuration"
5
+
6
+ module GemChangelogDiff
7
+ class Error < StandardError; end
8
+ end
9
+
10
+ require_relative "gem_changelog_diff/errors"
4
11
  require_relative "gem_changelog_diff/outdated_gem"
5
12
  require_relative "gem_changelog_diff/detector"
6
13
  require_relative "gem_changelog_diff/rubygems_client"
7
14
  require_relative "gem_changelog_diff/github_client"
8
15
  require_relative "gem_changelog_diff/formatter"
9
16
  require_relative "gem_changelog_diff/cli"
10
-
11
- module GemChangelogDiff
12
- class Error < StandardError; end
13
- end
@@ -1,9 +1,31 @@
1
1
  module GemChangelogDiff
2
2
  VERSION: String
3
3
 
4
+ self.@configuration: Configuration
5
+
6
+ def self.configuration: () -> Configuration
7
+ def self.configure: () { (Configuration) -> void } -> void
8
+ def self.reset_configuration!: () -> Configuration
9
+
4
10
  class Error < StandardError
5
11
  end
6
12
 
13
+ class RepoNotFoundError < Error
14
+ end
15
+
16
+ class GitHubAPIError < Error
17
+ end
18
+
19
+ class RateLimitError < GitHubAPIError
20
+ end
21
+
22
+ class NetworkError < Error
23
+ end
24
+
25
+ class Configuration
26
+ attr_accessor github_token: String?
27
+ end
28
+
7
29
  class OutdatedGem
8
30
  attr_reader name: String
9
31
  attr_reader current_version: String
@@ -50,6 +72,10 @@ module GemChangelogDiff
50
72
  private
51
73
 
52
74
  def fetch_releases: (String repo) -> Array[Hash[String, untyped]]
75
+ def execute_request: (URI::Generic uri) -> Net::HTTPResponse
76
+ def apply_auth: (Net::HTTPRequest request) -> void
77
+ def handle_response: (Net::HTTPResponse response) -> Array[Hash[String, untyped]]
78
+ def check_rate_limit: (Net::HTTPResponse response) -> void
53
79
  def filter_releases: (Array[Hash[String, untyped]] releases, String current_version, String newest_version) -> Array[release_hash]
54
80
  def build_release: (Hash[String, untyped] release, Gem::Version current, Gem::Version newest) -> release_hash?
55
81
  def sort_releases: (Array[release_hash] releases) -> Array[release_hash]
@@ -79,7 +105,10 @@ module GemChangelogDiff
79
105
 
80
106
  private
81
107
 
108
+ def configure_token: () -> void
82
109
  def build_reports: (Array[OutdatedGem] gems) -> Array[gem_report]
83
110
  def build_gem_report: (OutdatedGem gem, RubygemsClient rubygems_client, GithubClient github_client) -> gem_report
111
+ def log: (String message) -> void
112
+ def log_warning: (String message) -> void
84
113
  end
85
114
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_changelog_diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -44,7 +44,9 @@ files:
44
44
  - exe/gem_changelog_diff
45
45
  - lib/gem_changelog_diff.rb
46
46
  - lib/gem_changelog_diff/cli.rb
47
+ - lib/gem_changelog_diff/configuration.rb
47
48
  - lib/gem_changelog_diff/detector.rb
49
+ - lib/gem_changelog_diff/errors.rb
48
50
  - lib/gem_changelog_diff/formatter.rb
49
51
  - lib/gem_changelog_diff/github_client.rb
50
52
  - lib/gem_changelog_diff/outdated_gem.rb