end_of_life 0.3.0 → 0.4.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: cb7c4804e17e676497b2cf43afed14a21bec731ff6ea5e16bfd708ffbb265488
4
- data.tar.gz: efaec85105f37185a05e10cd489e4c47403ac6a530615c1900ae978033573142
3
+ metadata.gz: b8e0689d1cdf6da1fa737f8d699a060a8544184dfb345d6af4093e6e5e000c13
4
+ data.tar.gz: 608169452246f72b9351b07ae4f243d4ce74c3416c451a5fdf600b97dbc3f529
5
5
  SHA512:
6
- metadata.gz: 73bb63d650d73851b37e0286fb7a6e771286bb49c35f329d91e805d087ca65cf0b160f43fdcb95f1613275f5872594745fe584494ae1314f6ea3ea8cb9048e4e
7
- data.tar.gz: 30fed354f3c6a6a5d42c515157d531c2123a225aca3e996bd4193a8d718fd7a1860f0a2b0053cd8354cb4f2b5184751a978439509098977bab5b5d6f61111882
6
+ metadata.gz: 57c0401637ca7d60d59d27ab35f4bee948f09bf188df6189a41be14ceb906c50ee77059a550076122f14a3b118c9e6fdd2f10dd520d19b562670c8fe229cf5df
7
+ data.tar.gz: 8cf1db7f6007559d3066696e1f3820fdaae1b055654456d47d48d67b6828530315992aae95ac9ea2ecf3927b3a39b80f84df7a762d24cc6c4f073bc2956d648e
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.0.3
1
+ ruby 3.4.2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.1] - 2025-05-23
4
+
5
+ - [BUGFIX] Handle empty/invalid version files
6
+ - if a file like `.ruby-version` was empty, we would parse it it's version as
7
+ `0` which would take precedence over any other file when comparing versions.
8
+ - We now ignore those files and any file which version parses to `0`.
9
+
10
+ ## [0.4.0] - 2025-05-23
11
+
12
+ - Skip archived repos (by default)
13
+
14
+ It's possible to include them with the `--include-archived` flag.
15
+
16
+ - Sort repositories by most recently updated
17
+
18
+ These are likely the ones one cares more about.
19
+
20
+ - Require Ruby >= 3.2.0
21
+
3
22
  ## [0.3.0]
4
23
 
5
24
  ### Added
@@ -142,7 +161,9 @@ $ end_of_life --user=matz # searches on matz's repositories
142
161
 
143
162
  - Initial release
144
163
 
145
- [unreleased]: https://github.com/MatheusRich/end_of_life/compare/v0.3.0...HEAD
164
+ [unreleased]: https://github.com/MatheusRich/end_of_life/compare/v0.4.1...HEAD
165
+ [0.4.1]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.4.1
166
+ [0.4.0]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.4.0
146
167
  [0.3.0]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.3.0
147
168
  [0.2.0]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.2.0
148
169
  [0.1.0]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.1.0
data/Gemfile CHANGED
@@ -5,11 +5,11 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in end_of_life.gemspec
6
6
  gemspec
7
7
 
8
- gem "climate_control", "~> 1.0.1"
8
+ gem "climate_control", "~> 1.0"
9
9
  gem "rake", "~> 13.0"
10
10
  gem "rspec", "~> 3.10"
11
11
  gem "rspec-mocks", "~> 3.10"
12
- gem "simplecov", "~> 0.21.2"
13
- gem "standard", "~> 1.6"
14
- gem "vcr", "~> 6.0.0"
12
+ gem "simplecov", "~> 0.22.0"
13
+ gem "standard", "~> 1.26"
14
+ gem "vcr", "~> 6.0"
15
15
  gem "webmock", "~> 3.13"
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- end_of_life (0.3.0)
4
+ end_of_life (0.4.1)
5
+ async
5
6
  dry-monads (~> 1.3)
6
- octokit (~> 4.22)
7
+ octokit (~> 9.0)
7
8
  pastel (~> 0.8.0)
8
9
  tty-spinner (~> 0.9.0)
9
10
  tty-table (~> 0.12.0)
@@ -11,135 +12,159 @@ PATH
11
12
  GEM
12
13
  remote: https://rubygems.org/
13
14
  specs:
14
- addressable (2.8.0)
15
- public_suffix (>= 2.0.2, < 5.0)
15
+ addressable (2.8.7)
16
+ public_suffix (>= 2.0.2, < 7.0)
16
17
  ast (2.4.2)
17
- climate_control (1.0.1)
18
- concurrent-ruby (1.1.9)
19
- crack (0.4.5)
18
+ async (2.24.0)
19
+ console (~> 1.29)
20
+ fiber-annotation
21
+ io-event (~> 1.9)
22
+ metrics (~> 0.12)
23
+ traces (~> 0.15)
24
+ base64 (0.2.0)
25
+ bigdecimal (3.1.9)
26
+ climate_control (1.2.0)
27
+ concurrent-ruby (1.3.5)
28
+ console (1.30.2)
29
+ fiber-annotation
30
+ fiber-local (~> 1.1)
31
+ json
32
+ crack (1.0.0)
33
+ bigdecimal
20
34
  rexml
21
- diff-lcs (1.4.4)
22
- docile (1.4.0)
23
- dry-core (0.7.1)
35
+ diff-lcs (1.5.1)
36
+ docile (1.4.1)
37
+ dry-core (1.1.0)
24
38
  concurrent-ruby (~> 1.0)
25
- dry-monads (1.4.0)
39
+ logger
40
+ zeitwerk (~> 2.6)
41
+ dry-monads (1.8.3)
26
42
  concurrent-ruby (~> 1.0)
27
- dry-core (~> 0.7)
28
- faraday (1.9.3)
29
- faraday-em_http (~> 1.0)
30
- faraday-em_synchrony (~> 1.0)
31
- faraday-excon (~> 1.1)
32
- faraday-httpclient (~> 1.0)
33
- faraday-multipart (~> 1.0)
34
- faraday-net_http (~> 1.0)
35
- faraday-net_http_persistent (~> 1.0)
36
- faraday-patron (~> 1.0)
37
- faraday-rack (~> 1.0)
38
- faraday-retry (~> 1.0)
39
- ruby2_keywords (>= 0.0.4)
40
- faraday-em_http (1.0.0)
41
- faraday-em_synchrony (1.0.0)
42
- faraday-excon (1.1.0)
43
- faraday-httpclient (1.0.1)
44
- faraday-multipart (1.0.3)
45
- multipart-post (>= 1.2, < 3)
46
- faraday-net_http (1.0.1)
47
- faraday-net_http_persistent (1.2.0)
48
- faraday-patron (1.0.0)
49
- faraday-rack (1.0.0)
50
- faraday-retry (1.0.3)
51
- hashdiff (1.0.1)
52
- multipart-post (2.1.1)
53
- octokit (4.22.0)
54
- faraday (>= 0.9)
55
- sawyer (~> 0.8.0, >= 0.5.3)
56
- parallel (1.21.0)
57
- parser (3.1.0.0)
43
+ dry-core (~> 1.1)
44
+ zeitwerk (~> 2.6)
45
+ faraday (2.13.1)
46
+ faraday-net_http (>= 2.0, < 3.5)
47
+ json
48
+ logger
49
+ faraday-net_http (3.4.0)
50
+ net-http (>= 0.5.0)
51
+ fiber-annotation (0.2.0)
52
+ fiber-local (1.1.0)
53
+ fiber-storage
54
+ fiber-storage (1.0.1)
55
+ hashdiff (1.1.2)
56
+ io-event (1.10.1)
57
+ json (2.9.1)
58
+ language_server-protocol (3.17.0.3)
59
+ lint_roller (1.1.0)
60
+ logger (1.7.0)
61
+ metrics (0.12.2)
62
+ net-http (0.6.0)
63
+ uri
64
+ octokit (9.2.0)
65
+ faraday (>= 1, < 3)
66
+ sawyer (~> 0.9)
67
+ parallel (1.26.3)
68
+ parser (3.3.6.0)
58
69
  ast (~> 2.4.1)
70
+ racc
59
71
  pastel (0.8.0)
60
72
  tty-color (~> 0.5)
61
- public_suffix (4.0.6)
62
- rainbow (3.0.0)
63
- rake (13.0.6)
64
- regexp_parser (2.2.0)
65
- rexml (3.2.5)
66
- rspec (3.10.0)
67
- rspec-core (~> 3.10.0)
68
- rspec-expectations (~> 3.10.0)
69
- rspec-mocks (~> 3.10.0)
70
- rspec-core (3.10.1)
71
- rspec-support (~> 3.10.0)
72
- rspec-expectations (3.10.1)
73
+ public_suffix (6.0.1)
74
+ racc (1.8.1)
75
+ rainbow (3.1.1)
76
+ rake (13.2.1)
77
+ regexp_parser (2.10.0)
78
+ rexml (3.4.0)
79
+ rspec (3.13.0)
80
+ rspec-core (~> 3.13.0)
81
+ rspec-expectations (~> 3.13.0)
82
+ rspec-mocks (~> 3.13.0)
83
+ rspec-core (3.13.2)
84
+ rspec-support (~> 3.13.0)
85
+ rspec-expectations (3.13.3)
73
86
  diff-lcs (>= 1.2.0, < 2.0)
74
- rspec-support (~> 3.10.0)
75
- rspec-mocks (3.10.2)
87
+ rspec-support (~> 3.13.0)
88
+ rspec-mocks (3.13.2)
76
89
  diff-lcs (>= 1.2.0, < 2.0)
77
- rspec-support (~> 3.10.0)
78
- rspec-support (3.10.2)
79
- rubocop (1.24.1)
90
+ rspec-support (~> 3.13.0)
91
+ rspec-support (3.13.2)
92
+ rubocop (1.69.2)
93
+ json (~> 2.3)
94
+ language_server-protocol (>= 3.17.0)
80
95
  parallel (~> 1.10)
81
- parser (>= 3.0.0.0)
96
+ parser (>= 3.3.0.2)
82
97
  rainbow (>= 2.2.2, < 4.0)
83
- regexp_parser (>= 1.8, < 3.0)
84
- rexml
85
- rubocop-ast (>= 1.15.1, < 2.0)
98
+ regexp_parser (>= 2.9.3, < 3.0)
99
+ rubocop-ast (>= 1.36.2, < 2.0)
86
100
  ruby-progressbar (~> 1.7)
87
- unicode-display_width (>= 1.4.0, < 3.0)
88
- rubocop-ast (1.15.1)
89
- parser (>= 3.0.1.1)
90
- rubocop-performance (1.13.1)
91
- rubocop (>= 1.7.0, < 2.0)
92
- rubocop-ast (>= 0.4.0)
93
- ruby-progressbar (1.11.0)
94
- ruby2_keywords (0.0.5)
95
- sawyer (0.8.2)
101
+ unicode-display_width (>= 2.4.0, < 4.0)
102
+ rubocop-ast (1.37.0)
103
+ parser (>= 3.3.1.0)
104
+ rubocop-performance (1.23.0)
105
+ rubocop (>= 1.48.1, < 2.0)
106
+ rubocop-ast (>= 1.31.1, < 2.0)
107
+ ruby-progressbar (1.13.0)
108
+ sawyer (0.9.2)
96
109
  addressable (>= 2.3.5)
97
- faraday (> 0.8, < 2.0)
98
- simplecov (0.21.2)
110
+ faraday (>= 0.17.3, < 3)
111
+ simplecov (0.22.0)
99
112
  docile (~> 1.1)
100
113
  simplecov-html (~> 0.11)
101
114
  simplecov_json_formatter (~> 0.1)
102
- simplecov-html (0.12.3)
103
- simplecov_json_formatter (0.1.3)
104
- standard (1.6.0)
105
- rubocop (= 1.24.1)
106
- rubocop-performance (= 1.13.1)
115
+ simplecov-html (0.13.1)
116
+ simplecov_json_formatter (0.1.4)
117
+ standard (1.43.0)
118
+ language_server-protocol (~> 3.17.0.2)
119
+ lint_roller (~> 1.0)
120
+ rubocop (~> 1.69.1)
121
+ standard-custom (~> 1.0.0)
122
+ standard-performance (~> 1.6)
123
+ standard-custom (1.0.2)
124
+ lint_roller (~> 1.0)
125
+ rubocop (~> 1.50)
126
+ standard-performance (1.6.0)
127
+ lint_roller (~> 1.1)
128
+ rubocop-performance (~> 1.23.0)
107
129
  strings (0.2.1)
108
130
  strings-ansi (~> 0.2)
109
131
  unicode-display_width (>= 1.5, < 3.0)
110
132
  unicode_utils (~> 1.4)
111
133
  strings-ansi (0.2.0)
134
+ traces (0.15.2)
112
135
  tty-color (0.6.0)
113
136
  tty-cursor (0.7.1)
114
- tty-screen (0.8.1)
137
+ tty-screen (0.8.2)
115
138
  tty-spinner (0.9.3)
116
139
  tty-cursor (~> 0.7)
117
140
  tty-table (0.12.0)
118
141
  pastel (~> 0.8)
119
142
  strings (~> 0.2.0)
120
143
  tty-screen (~> 0.8)
121
- unicode-display_width (2.1.0)
144
+ unicode-display_width (2.6.0)
122
145
  unicode_utils (1.4.0)
123
- vcr (6.0.0)
124
- webmock (3.13.0)
125
- addressable (>= 2.3.6)
146
+ uri (1.0.3)
147
+ vcr (6.3.1)
148
+ base64
149
+ webmock (3.24.0)
150
+ addressable (>= 2.8.0)
126
151
  crack (>= 0.3.2)
127
152
  hashdiff (>= 0.4.0, < 2.0.0)
153
+ zeitwerk (2.7.3)
128
154
 
129
155
  PLATFORMS
130
- x86_64-darwin-20
131
- x86_64-linux
156
+ ruby
132
157
 
133
158
  DEPENDENCIES
134
- climate_control (~> 1.0.1)
159
+ climate_control (~> 1.0)
135
160
  end_of_life!
136
161
  rake (~> 13.0)
137
162
  rspec (~> 3.10)
138
163
  rspec-mocks (~> 3.10)
139
- simplecov (~> 0.21.2)
140
- standard (~> 1.6)
141
- vcr (~> 6.0.0)
164
+ simplecov (~> 0.22.0)
165
+ standard (~> 1.26)
166
+ vcr (~> 6.0)
142
167
  webmock (~> 3.13)
143
168
 
144
169
  BUNDLED WITH
145
- 2.3.4
170
+ 2.3.9
data/README.md CHANGED
@@ -25,7 +25,7 @@ $ GITHUB_TOKEN=something end_of_life # if your platform supports symlinks, you c
25
25
  [✔] Fetching repositories...
26
26
  [✔] Searching for EOL Ruby in repositories...
27
27
 
28
- Found 2 repositories using EOL Ruby (<= 2.5.9):
28
+ Found 2 repositories using EOL Ruby (<= 2.7.8):
29
29
  ┌───┬──────────────────────────────────────────────┬──────────────┐
30
30
  │ │ Repository │ Ruby version │
31
31
  ├───┼──────────────────────────────────────────────┼──────────────┤
@@ -41,12 +41,13 @@ There are some options to help you filter down the results:
41
41
  ```
42
42
  Usage: end_of_life [options]
43
43
  --exclude=NAME,NAME2 Exclude repositories containing a certain word in its name. You can specify up to five words.
44
- --public-only Searches only public repostories
45
- --private-only Searches only private repostories
46
- --repo, --repository=USER/REPO Searches a specific repostory
44
+ --public-only Searches only public repositories
45
+ --private-only Searches only private repositories
46
+ --repo, --repository=USER/REPO Searches a specific repository
47
47
  --org, --organization=ORG,ORG2 Searches within specific organizations
48
48
  -u, --user=NAME Sets the user used on the repository search
49
49
  --max-eol-days-away NUMBER Sets the maximum number of days away a version can be from EOL. It defaults to 0.
50
+ --include-archived Includes archived repositories on the search
50
51
  -v, --version Displays end_of_life version
51
52
  -h, --help Displays this help
52
53
  ```
data/end_of_life.gemspec CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  "unmaintained, Ruby versions."
14
14
  spec.homepage = "https://github.com/MatheusRich/end_of_life"
15
15
  spec.license = "MIT"
16
- spec.required_ruby_version = ">= 2.7.0"
16
+ spec.required_ruby_version = ">= 3.2.0"
17
17
 
18
18
  # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
19
19
 
@@ -32,8 +32,9 @@ Gem::Specification.new do |spec|
32
32
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
33
  spec.require_paths = ["lib"]
34
34
 
35
+ spec.add_dependency "async"
35
36
  spec.add_dependency "dry-monads", "~> 1.3"
36
- spec.add_dependency "octokit", "~> 4.22"
37
+ spec.add_dependency "octokit", "~> 9.0"
37
38
  spec.add_dependency "pastel", "~> 0.8.0"
38
39
  spec.add_dependency "tty-spinner", "~> 0.9.0"
39
40
  spec.add_dependency "tty-table", "~> 0.12.0"
@@ -0,0 +1,80 @@
1
+ module EndOfLife
2
+ class CLI
3
+ include TerminalHelper
4
+
5
+ def call(argv)
6
+ parse_options(argv)
7
+ .then { |options| execute_command(options) }
8
+ end
9
+
10
+ private
11
+
12
+ def execute_command(options)
13
+ case options[:command]
14
+ when :help
15
+ puts options[:parser]
16
+ when :version
17
+ puts "end_of_life v#{EndOfLife::VERSION}"
18
+ when :print_error
19
+ puts error_msg(options[:error])
20
+ exit(-1)
21
+ else
22
+ check_eol_ruby_on_repositories(options)
23
+ end
24
+ end
25
+
26
+ def check_eol_ruby_on_repositories(options)
27
+ fetch_repositories(options)
28
+ .fmap { |repositories| filter_repositories_with_end_of_life(repositories, max_eol_date: options[:max_eol_date]) }
29
+ .fmap { |repositories| print_diagnose_for(repositories, max_eol_date: options[:max_eol_date]) }
30
+ .or { |error| puts "\n#{error_msg(error)}" }
31
+ end
32
+
33
+ def parse_options(argv)
34
+ Options.from(argv)
35
+ end
36
+
37
+ def fetch_repositories(options)
38
+ with_loading_spinner("Fetching repositories...") do |spinner|
39
+ result = Repository.fetch(options)
40
+
41
+ spinner.error if result.failure?
42
+
43
+ result
44
+ end
45
+ end
46
+
47
+ def filter_repositories_with_end_of_life(repositories, max_eol_date:)
48
+ with_loading_spinner("Searching for EOL Ruby in repositories...") do
49
+ Sync do
50
+ repositories
51
+ .tap { |repos| repos.map { |repo| Async { repo.ruby_version } }.map(&:wait) }
52
+ .filter { |repo| repo.eol_ruby?(at: max_eol_date) }
53
+ end
54
+ end
55
+ end
56
+
57
+ def print_diagnose_for(repositories, max_eol_date:)
58
+ puts
59
+
60
+ if repositories.empty?
61
+ puts "No repositories using EOL Ruby."
62
+ return
63
+ end
64
+
65
+ word = (repositories.size == 1) ? "repository" : "repositories"
66
+ puts "Found #{repositories.size} #{word} using EOL Ruby (<= #{RubyVersion.latest_eol(at: max_eol_date)}):"
67
+ puts end_of_life_table(repositories)
68
+ exit(-1)
69
+ end
70
+
71
+ def end_of_life_table(repositories)
72
+ headers = ["", "Repository", "Ruby version"]
73
+ rows = repositories.map.with_index(1) do |repo, i|
74
+ [i, repo.url, repo.ruby_version]
75
+ end
76
+
77
+ table(headers, rows)
78
+ end
79
+ end
80
+ end
@@ -3,7 +3,7 @@ require "optparse"
3
3
  module EndOfLife
4
4
  module Options
5
5
  def self.from(argv)
6
- options = {max_eol_date: Date.today}
6
+ options = {max_eol_date: Date.today, skip_archived: true}
7
7
 
8
8
  OptionParser.new do |opts|
9
9
  options[:parser] = opts
@@ -14,15 +14,15 @@ module EndOfLife
14
14
  options[:excludes] = excludes.first(5)
15
15
  end
16
16
 
17
- opts.on("--public-only", "Searches only public repostories") do
17
+ opts.on("--public-only", "Searches only public repositories") do
18
18
  options[:visibility] = :public
19
19
  end
20
20
 
21
- opts.on("--private-only", "Searches only private repostories") do
21
+ opts.on("--private-only", "Searches only private repositories") do
22
22
  options[:visibility] = :private
23
23
  end
24
24
 
25
- opts.on("--repo=USER/REPO", "--repository=USER/REPO", "Searches a specific repostory") do |repository|
25
+ opts.on("--repo=USER/REPO", "--repository=USER/REPO", "Searches a specific repository") do |repository|
26
26
  options[:repository] = repository
27
27
  end
28
28
 
@@ -38,6 +38,10 @@ module EndOfLife
38
38
  options[:max_eol_date] = Date.today + days.to_i.abs
39
39
  end
40
40
 
41
+ opts.on("--include-archived", "Includes archived repositories on the search") do
42
+ options[:skip_archived] = false
43
+ end
44
+
41
45
  opts.on("-v", "--version", "Displays end_of_life version") do
42
46
  options[:command] = :version
43
47
  end
@@ -9,10 +9,12 @@ module EndOfLife
9
9
  options[:user] ||= github.user.login
10
10
 
11
11
  query = search_query_for(options)
12
- items = github.search_repositories(query).items
12
+ items = github.search_repositories(query, {sort: :updated}).items
13
13
 
14
14
  Success(
15
- items.map do |repo|
15
+ items.filter_map do |repo|
16
+ next if repo.archived && options[:skip_archived]
17
+
16
18
  Repository.new(
17
19
  full_name: repo.full_name,
18
20
  url: repo.html_url,
@@ -56,7 +58,7 @@ module EndOfLife
56
58
  end
57
59
  end
58
60
 
59
- attr :full_name, :url
61
+ attr_reader :full_name, :url
60
62
 
61
63
  def initialize(full_name:, url:, github_client:)
62
64
  @full_name = full_name
@@ -79,15 +81,22 @@ module EndOfLife
79
81
  def ruby_versions
80
82
  return @ruby_versions if defined?(@ruby_versions)
81
83
 
82
- @ruby_versions = begin
83
- ruby_version_files = [
84
- fetch_file(".ruby-version"),
85
- fetch_file("Gemfile"),
86
- fetch_file("Gemfile.lock"),
87
- fetch_file(".tool-versions")
88
- ].compact
84
+ @ruby_versions = fetch_ruby_version_files.filter_map { |file|
85
+ parse_version_file(file)
86
+ }
87
+ end
89
88
 
90
- ruby_version_files.filter_map { |file| parse_version_file(file) }
89
+ POSSIBLE_RUBY_VERSION_FILES = [
90
+ ".ruby-version",
91
+ "Gemfile.lock",
92
+ "Gemfile",
93
+ ".tool-versions"
94
+ ]
95
+ def fetch_ruby_version_files
96
+ Sync do
97
+ POSSIBLE_RUBY_VERSION_FILES
98
+ .map { |file_path| Async { fetch_file(file_path) } }
99
+ .filter_map(&:wait)
91
100
  end
92
101
  end
93
102
 
@@ -7,7 +7,9 @@ module EndOfLife
7
7
  extend self
8
8
 
9
9
  def parse_file(file_name:, content:)
10
- if file_name == ".ruby-version"
10
+ return if content.strip.empty?
11
+
12
+ version = if file_name == ".ruby-version"
11
13
  parse_ruby_version_file(content)
12
14
  elsif file_name == "Gemfile.lock"
13
15
  parse_gemfile_lock_file(content)
@@ -18,6 +20,14 @@ module EndOfLife
18
20
  else
19
21
  raise ArgumentError, "Unsupported file #{file_name}"
20
22
  end
23
+
24
+ # Gem::Version is pretty forgiving and will accept empty strings
25
+ # as valid versions. This is a catch-all to ensure we don't return
26
+ # a version 0, which always takes precedence over any other version
27
+ # when comparing.
28
+ return if version&.zero?
29
+
30
+ version
21
31
  end
22
32
 
23
33
  private
@@ -38,11 +48,9 @@ module EndOfLife
38
48
  end
39
49
 
40
50
  def parse_gemfile_file(file_content)
41
- return if file_content.empty?
42
-
43
51
  with_temp_gemfile(file_content) do |temp_gemfile|
44
52
  gemfile_version = temp_gemfile.ruby_version&.gem_version
45
- return nil if gemfile_version.nil?
53
+ return if gemfile_version.nil?
46
54
 
47
55
  RubyVersion.new(gemfile_version)
48
56
  end
@@ -6,6 +6,8 @@ module EndOfLife
6
6
  class RubyVersion
7
7
  include Comparable
8
8
 
9
+ ZERO = Gem::Version.new("0")
10
+
9
11
  class << self
10
12
  include Dry::Monads[:try]
11
13
 
@@ -41,7 +43,7 @@ module EndOfLife
41
43
  end
42
44
  end
43
45
 
44
- attr :version, :eol_date
46
+ attr_reader :version, :eol_date
45
47
 
46
48
  def initialize(version_string, eol_date: nil)
47
49
  @version = Gem::Version.new(version_string)
@@ -58,6 +60,8 @@ module EndOfLife
58
60
  @version <=> other.version
59
61
  end
60
62
 
63
+ def zero? = @version == ZERO
64
+
61
65
  def to_s
62
66
  @version.to_s
63
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EndOfLife
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.1"
5
5
  end
data/lib/end_of_life.json CHANGED
@@ -1,13 +1,98 @@
1
1
  [
2
- {"cycle": "3.1", "release": "2021-12-25", "eol": "2025-12-25", "latest": "3.1.0"},
3
- {"cycle": "3.0", "release": "2020-12-25", "eol": "2024-03-31", "latest": "3.0.3"},
4
- {"cycle": "2.7", "release": "2019-12-25", "eol": "2023-03-31", "latest": "2.7.5"},
5
- {"cycle": "2.6", "release": "2018-12-25", "eol": "2022-03-31", "latest": "2.6.9"},
6
- {"cycle": "2.5", "release": "2017-12-25", "eol": "2021-03-31", "latest": "2.5.9"},
7
- {"cycle": "2.4", "release": "2016-12-25", "eol": "2020-03-31", "latest": "2.4.10"},
8
- {"cycle": "2.3", "release": "2015-12-25", "eol": "2019-03-31", "latest": "2.3.8"},
9
- {"cycle": "2.2", "release": "2014-12-25", "eol": "2018-03-31", "latest": "2.2.10"},
10
- {"cycle": "2.1", "release": "2013-12-25", "eol": "2017-03-31", "latest": "2.1.10"},
11
- {"cycle": "2.0.0", "release": "2013-02-24", "eol": "2016-02-24", "latest": "2.0.0-p648"},
12
- {"cycle": "1.9.3", "release": "2011-10-31", "eol": "2015-02-23", "latest": "1.9.3-p551"}
2
+ {
3
+ "cycle": "3.2",
4
+ "eol": "2026-03-31",
5
+ "latest": "3.2.2",
6
+ "latestReleaseDate": "2023-03-30",
7
+ "releaseDate": "2022-12-25",
8
+ "lts": false
9
+ },
10
+ {
11
+ "cycle": "3.1",
12
+ "eol": "2025-03-31",
13
+ "latest": "3.1.4",
14
+ "latestReleaseDate": "2023-03-30",
15
+ "releaseDate": "2021-12-25",
16
+ "lts": false
17
+ },
18
+ {
19
+ "cycle": "3.0",
20
+ "eol": "2024-03-31",
21
+ "latest": "3.0.6",
22
+ "latestReleaseDate": "2023-03-30",
23
+ "releaseDate": "2020-12-25",
24
+ "lts": false
25
+ },
26
+ {
27
+ "cycle": "2.7",
28
+ "eol": "2023-03-31",
29
+ "latest": "2.7.8",
30
+ "latestReleaseDate": "2023-03-30",
31
+ "releaseDate": "2019-12-25",
32
+ "lts": false
33
+ },
34
+ {
35
+ "cycle": "2.6",
36
+ "eol": "2022-03-31",
37
+ "latest": "2.6.10",
38
+ "latestReleaseDate": "2022-04-12",
39
+ "releaseDate": "2018-12-25",
40
+ "lts": false
41
+ },
42
+ {
43
+ "cycle": "2.5",
44
+ "eol": "2021-03-31",
45
+ "latest": "2.5.9",
46
+ "latestReleaseDate": "2021-04-05",
47
+ "releaseDate": "2017-12-25",
48
+ "lts": false
49
+ },
50
+ {
51
+ "cycle": "2.4",
52
+ "eol": "2020-03-31",
53
+ "latest": "2.4.10",
54
+ "latestReleaseDate": "2020-03-31",
55
+ "releaseDate": "2016-12-23",
56
+ "lts": false
57
+ },
58
+ {
59
+ "cycle": "2.3",
60
+ "eol": "2019-03-31",
61
+ "latest": "2.3.8",
62
+ "latestReleaseDate": "2018-10-17",
63
+ "releaseDate": "2015-12-24",
64
+ "lts": false
65
+ },
66
+ {
67
+ "cycle": "2.2",
68
+ "eol": "2018-03-31",
69
+ "latest": "2.2.10",
70
+ "latestReleaseDate": "2018-03-28",
71
+ "releaseDate": "2014-12-25",
72
+ "lts": false
73
+ },
74
+ {
75
+ "cycle": "2.1",
76
+ "eol": "2017-03-31",
77
+ "latest": "2.1.10",
78
+ "latestReleaseDate": "2016-03-31",
79
+ "releaseDate": "2013-12-25",
80
+ "lts": false
81
+ },
82
+ {
83
+ "cycle": "2.0.0",
84
+ "eol": "2016-02-24",
85
+ "latest": "2.0.0p648",
86
+ "latestReleaseDate": "2015-12-16",
87
+ "releaseDate": "2013-02-24",
88
+ "lts": false
89
+ },
90
+ {
91
+ "cycle": "1.9.3",
92
+ "eol": "2015-02-23",
93
+ "latest": "1.9.3p551",
94
+ "latestReleaseDate": "2014-11-13",
95
+ "releaseDate": "2011-10-30",
96
+ "lts": false
97
+ }
13
98
  ]
data/lib/end_of_life.rb CHANGED
@@ -1,88 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "async"
3
4
  require "dry-monads"
4
5
  require "json"
6
+ require "base64"
5
7
  require "octokit"
6
8
  require_relative "end_of_life/options"
7
9
  require_relative "end_of_life/repository"
8
10
  require_relative "end_of_life/ruby_version"
9
11
  require_relative "end_of_life/terminal_helper"
10
12
  require_relative "end_of_life/version"
13
+ require_relative "end_of_life/cli"
11
14
 
12
15
  module EndOfLife
13
16
  extend TerminalHelper
14
-
15
- class CLI
16
- include TerminalHelper
17
-
18
- def call(argv)
19
- parse_options(argv)
20
- .then { |options| execute_command(options) }
21
- end
22
-
23
- private
24
-
25
- def execute_command(options)
26
- case options[:command]
27
- when :help
28
- puts options[:parser]
29
- when :version
30
- puts "end_of_life v#{EndOfLife::VERSION}"
31
- when :print_error
32
- puts error_msg(options[:error])
33
- exit(-1)
34
- else
35
- check_eol_ruby_on_repositories(options)
36
- end
37
- end
38
-
39
- def check_eol_ruby_on_repositories(options)
40
- fetch_repositories(options)
41
- .fmap { |repositories| filter_repositories_with_end_of_life(repositories, max_eol_date: options[:max_eol_date]) }
42
- .fmap { |repositories| print_diagnose_for(repositories, max_eol_date: options[:max_eol_date]) }
43
- .or { |error| puts "\n#{error_msg(error)}" }
44
- end
45
-
46
- def parse_options(argv)
47
- Options.from(argv)
48
- end
49
-
50
- def fetch_repositories(options)
51
- with_loading_spinner("Fetching repositories...") do |spinner|
52
- result = Repository.fetch(options)
53
-
54
- spinner.error if result.failure?
55
-
56
- result
57
- end
58
- end
59
-
60
- def filter_repositories_with_end_of_life(repositories, max_eol_date:)
61
- with_loading_spinner("Searching for EOL Ruby in repositories...") do
62
- repositories.filter { |repo| repo.eol_ruby?(at: max_eol_date) }
63
- end
64
- end
65
-
66
- def print_diagnose_for(repositories, max_eol_date:)
67
- puts
68
-
69
- if repositories.empty?
70
- puts "No repositories using EOL Ruby."
71
- return
72
- end
73
-
74
- puts "Found #{repositories.size} repositories using EOL Ruby (<= #{RubyVersion.latest_eol(at: max_eol_date)}):"
75
- puts end_of_life_table(repositories)
76
- exit(-1)
77
- end
78
-
79
- def end_of_life_table(repositories)
80
- headers = ["", "Repository", "Ruby version"]
81
- rows = repositories.map.with_index(1) do |repo, i|
82
- [i, repo.url, repo.ruby_version]
83
- end
84
-
85
- table(headers, rows)
86
- end
87
- end
88
17
  end
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: end_of_life
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matheus Richard
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2022-02-10 00:00:00.000000000 Z
10
+ date: 2025-05-23 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: async
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: dry-monads
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -30,14 +43,14 @@ dependencies:
30
43
  requirements:
31
44
  - - "~>"
32
45
  - !ruby/object:Gem::Version
33
- version: '4.22'
46
+ version: '9.0'
34
47
  type: :runtime
35
48
  prerelease: false
36
49
  version_requirements: !ruby/object:Gem::Requirement
37
50
  requirements:
38
51
  - - "~>"
39
52
  - !ruby/object:Gem::Version
40
- version: '4.22'
53
+ version: '9.0'
41
54
  - !ruby/object:Gem::Dependency
42
55
  name: pastel
43
56
  requirement: !ruby/object:Gem::Requirement
@@ -91,7 +104,6 @@ extensions: []
91
104
  extra_rdoc_files: []
92
105
  files:
93
106
  - ".rspec"
94
- - ".standard.yml"
95
107
  - ".tool-versions"
96
108
  - CHANGELOG.md
97
109
  - Gemfile
@@ -107,6 +119,7 @@ files:
107
119
  - exe/eol
108
120
  - lib/end_of_life.json
109
121
  - lib/end_of_life.rb
122
+ - lib/end_of_life/cli.rb
110
123
  - lib/end_of_life/options.rb
111
124
  - lib/end_of_life/repository.rb
112
125
  - lib/end_of_life/ruby_version.rb
@@ -120,7 +133,6 @@ metadata:
120
133
  homepage_uri: https://github.com/MatheusRich/end_of_life
121
134
  source_code_uri: https://github.com/MatheusRich/end_of_life
122
135
  changelog_uri: https://github.com/MatheusRich/end_of_life/blob/main/CHANGELOG.md
123
- post_install_message:
124
136
  rdoc_options: []
125
137
  require_paths:
126
138
  - lib
@@ -128,15 +140,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
140
  requirements:
129
141
  - - ">="
130
142
  - !ruby/object:Gem::Version
131
- version: 2.7.0
143
+ version: 3.2.0
132
144
  required_rubygems_version: !ruby/object:Gem::Requirement
133
145
  requirements:
134
146
  - - ">="
135
147
  - !ruby/object:Gem::Version
136
148
  version: '0'
137
149
  requirements: []
138
- rubygems_version: 3.2.32
139
- signing_key:
150
+ rubygems_version: 3.6.2
140
151
  specification_version: 4
141
152
  summary: Lists repositories using end-of-life Ruby versions.
142
153
  test_files: []
data/.standard.yml DELETED
@@ -1 +0,0 @@
1
- ruby_version: 2.7.1