end_of_life 0.4.1 → 0.5.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: b8e0689d1cdf6da1fa737f8d699a060a8544184dfb345d6af4093e6e5e000c13
4
- data.tar.gz: 608169452246f72b9351b07ae4f243d4ce74c3416c451a5fdf600b97dbc3f529
3
+ metadata.gz: 19209199d1886b9a553b2ce566a075a162fc0f877073aad2c63739f99d32f0ee
4
+ data.tar.gz: 9ef0fbd8ef557d73c0a6739b29fa4f40edb3dbba4e6b306f4e132ae160ed815e
5
5
  SHA512:
6
- metadata.gz: 57c0401637ca7d60d59d27ab35f4bee948f09bf188df6189a41be14ceb906c50ee77059a550076122f14a3b118c9e6fdd2f10dd520d19b562670c8fe229cf5df
7
- data.tar.gz: 8cf1db7f6007559d3066696e1f3820fdaae1b055654456d47d48d67b6828530315992aae95ac9ea2ecf3927b3a39b80f84df7a762d24cc6c4f073bc2956d648e
6
+ metadata.gz: 66fe59b2626bf7656eed917b4fa8e196b15e81dd327f19636af914359956b318648e334f8655d249a979f8d50c94ece93fde880ac241fc497985ba06f18ab55f
7
+ data.tar.gz: 924eed863de214be0a8ac4a91fccfa964f1349efa13631acec507a6b59a481811885f9dbdc6ff3cd4473e6d460f42a1cffb4400bf6f1297707d83b23b422ffda
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.4.2
1
+ ruby 3.4.5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.1] - 2025-09-10
4
+
5
+ - [BUGFIX] Handle when Gemfile pulls version from a file and the file is missing
6
+ - [BUGFIX] Handle running end_of_life outside of a bundler project
7
+
8
+ ## [0.5.0] - 2025-09-07
9
+
10
+ - 🎉 Add support for scanning EOL Rails versions!
11
+ - Use the `--product=rails` option to scan for Rails instead of Ruby.
12
+ - End of Life looks for an exact Rails version on the `Gemfile` file or `Gemfile.lock`.
13
+
3
14
  ## [0.4.1] - 2025-05-23
4
15
 
5
16
  - [BUGFIX] Handle empty/invalid version files
@@ -161,7 +172,8 @@ $ end_of_life --user=matz # searches on matz's repositories
161
172
 
162
173
  - Initial release
163
174
 
164
- [unreleased]: https://github.com/MatheusRich/end_of_life/compare/v0.4.1...HEAD
175
+ [unreleased]: https://github.com/MatheusRich/end_of_life/compare/v0.5.0...HEAD
176
+ [0.5.0]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.5.0
165
177
  [0.4.1]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.4.1
166
178
  [0.4.0]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.4.0
167
179
  [0.3.0]: https://github.com/MatheusRich/end_of_life/releases/tag/v0.3.0
data/Gemfile CHANGED
@@ -10,6 +10,6 @@ gem "rake", "~> 13.0"
10
10
  gem "rspec", "~> 3.10"
11
11
  gem "rspec-mocks", "~> 3.10"
12
12
  gem "simplecov", "~> 0.22.0"
13
- gem "standard", "~> 1.26"
13
+ gem "standard", github: "testdouble/standard"
14
14
  gem "vcr", "~> 6.0"
15
15
  gem "webmock", "~> 3.13"
data/Gemfile.lock CHANGED
@@ -1,31 +1,44 @@
1
+ GIT
2
+ remote: https://github.com/testdouble/standard.git
3
+ revision: 2a0584ed57122065d0972fe5a55eb83aec150b54
4
+ specs:
5
+ standard (1.50.0)
6
+ language_server-protocol (~> 3.17.0.2)
7
+ lint_roller (~> 1.0)
8
+ rubocop (~> 1.75.5)
9
+ standard-custom (~> 1.0.0)
10
+ standard-performance (~> 1.8)
11
+
1
12
  PATH
2
13
  remote: .
3
14
  specs:
4
- end_of_life (0.4.1)
15
+ end_of_life (0.5.1)
5
16
  async
17
+ bundler (>= 2.3.0, < 3)
6
18
  dry-monads (~> 1.3)
7
19
  octokit (~> 9.0)
8
20
  pastel (~> 0.8.0)
9
21
  tty-spinner (~> 0.9.0)
10
22
  tty-table (~> 0.12.0)
23
+ zeitwerk (~> 2.7)
11
24
 
12
25
  GEM
13
26
  remote: https://rubygems.org/
14
27
  specs:
15
28
  addressable (2.8.7)
16
29
  public_suffix (>= 2.0.2, < 7.0)
17
- ast (2.4.2)
18
- async (2.24.0)
30
+ ast (2.4.3)
31
+ async (2.31.0)
19
32
  console (~> 1.29)
20
33
  fiber-annotation
21
- io-event (~> 1.9)
34
+ io-event (~> 1.11)
22
35
  metrics (~> 0.12)
23
- traces (~> 0.15)
36
+ traces (~> 0.18)
24
37
  base64 (0.2.0)
25
38
  bigdecimal (3.1.9)
26
39
  climate_control (1.2.0)
27
40
  concurrent-ruby (1.3.5)
28
- console (1.30.2)
41
+ console (1.34.0)
29
42
  fiber-annotation
30
43
  fiber-local (~> 1.1)
31
44
  json
@@ -38,43 +51,44 @@ GEM
38
51
  concurrent-ruby (~> 1.0)
39
52
  logger
40
53
  zeitwerk (~> 2.6)
41
- dry-monads (1.8.3)
54
+ dry-monads (1.9.0)
42
55
  concurrent-ruby (~> 1.0)
43
56
  dry-core (~> 1.1)
44
57
  zeitwerk (~> 2.6)
45
- faraday (2.13.1)
58
+ faraday (2.13.4)
46
59
  faraday-net_http (>= 2.0, < 3.5)
47
60
  json
48
61
  logger
49
- faraday-net_http (3.4.0)
62
+ faraday-net_http (3.4.1)
50
63
  net-http (>= 0.5.0)
51
64
  fiber-annotation (0.2.0)
52
65
  fiber-local (1.1.0)
53
66
  fiber-storage
54
67
  fiber-storage (1.0.1)
55
68
  hashdiff (1.1.2)
56
- io-event (1.10.1)
69
+ io-event (1.14.0)
57
70
  json (2.9.1)
58
- language_server-protocol (3.17.0.3)
71
+ language_server-protocol (3.17.0.5)
59
72
  lint_roller (1.1.0)
60
73
  logger (1.7.0)
61
- metrics (0.12.2)
74
+ metrics (0.15.0)
62
75
  net-http (0.6.0)
63
76
  uri
64
77
  octokit (9.2.0)
65
78
  faraday (>= 1, < 3)
66
79
  sawyer (~> 0.9)
67
- parallel (1.26.3)
68
- parser (3.3.6.0)
80
+ parallel (1.27.0)
81
+ parser (3.3.9.0)
69
82
  ast (~> 2.4.1)
70
83
  racc
71
84
  pastel (0.8.0)
72
85
  tty-color (~> 0.5)
86
+ prism (1.4.0)
73
87
  public_suffix (6.0.1)
74
88
  racc (1.8.1)
75
89
  rainbow (3.1.1)
76
90
  rake (13.2.1)
77
- regexp_parser (2.10.0)
91
+ regexp_parser (2.11.2)
78
92
  rexml (3.4.0)
79
93
  rspec (3.13.0)
80
94
  rspec-core (~> 3.13.0)
@@ -89,21 +103,24 @@ GEM
89
103
  diff-lcs (>= 1.2.0, < 2.0)
90
104
  rspec-support (~> 3.13.0)
91
105
  rspec-support (3.13.2)
92
- rubocop (1.69.2)
106
+ rubocop (1.75.8)
93
107
  json (~> 2.3)
94
- language_server-protocol (>= 3.17.0)
108
+ language_server-protocol (~> 3.17.0.2)
109
+ lint_roller (~> 1.1.0)
95
110
  parallel (~> 1.10)
96
111
  parser (>= 3.3.0.2)
97
112
  rainbow (>= 2.2.2, < 4.0)
98
113
  regexp_parser (>= 2.9.3, < 3.0)
99
- rubocop-ast (>= 1.36.2, < 2.0)
114
+ rubocop-ast (>= 1.44.0, < 2.0)
100
115
  ruby-progressbar (~> 1.7)
101
116
  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)
117
+ rubocop-ast (1.46.0)
118
+ parser (>= 3.3.7.2)
119
+ prism (~> 1.4)
120
+ rubocop-performance (1.25.0)
121
+ lint_roller (~> 1.1)
122
+ rubocop (>= 1.75.0, < 2.0)
123
+ rubocop-ast (>= 1.38.0, < 2.0)
107
124
  ruby-progressbar (1.13.0)
108
125
  sawyer (0.9.2)
109
126
  addressable (>= 2.3.5)
@@ -114,24 +131,18 @@ GEM
114
131
  simplecov_json_formatter (~> 0.1)
115
132
  simplecov-html (0.13.1)
116
133
  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
134
  standard-custom (1.0.2)
124
135
  lint_roller (~> 1.0)
125
136
  rubocop (~> 1.50)
126
- standard-performance (1.6.0)
137
+ standard-performance (1.8.0)
127
138
  lint_roller (~> 1.1)
128
- rubocop-performance (~> 1.23.0)
139
+ rubocop-performance (~> 1.25.0)
129
140
  strings (0.2.1)
130
141
  strings-ansi (~> 0.2)
131
142
  unicode-display_width (>= 1.5, < 3.0)
132
143
  unicode_utils (~> 1.4)
133
144
  strings-ansi (0.2.0)
134
- traces (0.15.2)
145
+ traces (0.18.2)
135
146
  tty-color (0.6.0)
136
147
  tty-cursor (0.7.1)
137
148
  tty-screen (0.8.2)
@@ -162,7 +173,7 @@ DEPENDENCIES
162
173
  rspec (~> 3.10)
163
174
  rspec-mocks (~> 3.10)
164
175
  simplecov (~> 0.22.0)
165
- standard (~> 1.26)
176
+ standard!
166
177
  vcr (~> 6.0)
167
178
  webmock (~> 3.13)
168
179
 
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # End of Life
2
2
 
3
- This gem lists GitHub repositories using end-of-life Ruby versions.
3
+ This gem lists GitHub repositories using end-of-life versions of various
4
+ products.
4
5
 
5
6
  ![End of Life Demo](demo.gif)
6
7
 
@@ -14,18 +15,20 @@ gem install end_of_life
14
15
 
15
16
  1. Set up a [GitHub access token][] (we recommend using a read-only token);
16
17
 
17
- [github access token]: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-token
18
+ [github access token]:
19
+ https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-token
18
20
 
19
- 2. Export the `GITHUB_TOKEN` environment variable or set it when calling `end_of_life`;
21
+ 2. Export the `GITHUB_TOKEN` environment variable or set it when calling
22
+ `end_of_life`;
20
23
 
21
24
  3. Use the `end_of_life` command to list the repositories:
22
25
 
23
26
  ```sh
24
27
  $ GITHUB_TOKEN=something end_of_life # if your platform supports symlinks, you can use the `eol` command instead
25
- [✔] Fetching repositories...
28
+ [✔] Searching repositories with Ruby...
26
29
  [✔] Searching for EOL Ruby in repositories...
27
30
 
28
- Found 2 repositories using EOL Ruby (<= 2.7.8):
31
+ Found 2 repositories using EOL Ruby (<= 3.1.7):
29
32
  ┌───┬──────────────────────────────────────────────┬──────────────┐
30
33
  │ │ Repository │ Ruby version │
31
34
  ├───┼──────────────────────────────────────────────┼──────────────┤
@@ -40,6 +43,7 @@ There are some options to help you filter down the results:
40
43
 
41
44
  ```
42
45
  Usage: end_of_life [options]
46
+ -p, --product NAME Sets the product to scan for (default: ruby). Supported products are: ruby, rails.
43
47
  --exclude=NAME,NAME2 Exclude repositories containing a certain word in its name. You can specify up to five words.
44
48
  --public-only Searches only public repositories
45
49
  --private-only Searches only private repositories
@@ -54,17 +58,19 @@ Usage: end_of_life [options]
54
58
 
55
59
  ## How it works
56
60
 
57
- This gem fetches all your GitHub repositories that contain Ruby code, then
58
- searches for files that may have a Ruby version. Currently, those files are:
59
- `.ruby-version`, `Gemfile`, `Gemfile.lock`, and `.tool-version`. We parse these
60
- files and extract the minimum Ruby version used in the repository.
61
+ This gem fetches all your GitHub repositories that contain code for the
62
+ specified product, then searches for files that may contain version information.
63
+ For Ruby, those files are: `.ruby-version`, `Gemfile`, `Gemfile.lock`, and
64
+ `.tool-version`. End of Life parses these files and extracts the minimum version
65
+ used in the repository.
61
66
 
62
- The EOL Ruby version is provided by https://endoflife.date/, with a file
67
+ The EOL version information is provided by https://endoflife.date/, with a file
63
68
  [fallback].
64
69
 
65
- > **IMPORTANT:** To parse Gemfiles, we need to execute the code inside it. **Be
66
- > careful** because this may be a security risk. We plan to add a secure parser
67
- > for Gemfiles in the future.
70
+ > [!ATTENTION]
71
+ > To parse Gemfiles, we need to execute the code inside them.
72
+ > **Be careful** because this may be a security risk. We plan to add secure
73
+ > parsers for these files in the future.
68
74
 
69
75
  Some other limitations are listed on the [issues page].
70
76
 
@@ -86,7 +92,10 @@ git commits and the created tag, and push the `.gem` file to
86
92
  ## Contributing
87
93
 
88
94
  Bug reports and pull requests are welcome on GitHub at
89
- https://github.com/MatheusRich/end_of_life.
95
+ https://github.com/MatheusRich/end_of_life. If you want to add a new product,
96
+ [check out this commit for reference].
97
+
98
+ [check out this commit for reference]: ba9a92a690e0d61ea09e508c1cd76b8309fb89df
90
99
 
91
100
  ## License
92
101
 
data/end_of_life.gemspec CHANGED
@@ -8,9 +8,11 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Matheus Richard"]
9
9
  spec.email = ["matheusrichardt@gmail.com"]
10
10
 
11
- spec.summary = "Lists repositories using end-of-life Ruby versions."
12
- spec.description = "Searches your GitHub repositores and lists the ones using end-of-life, i.e. " \
13
- "unmaintained, Ruby versions."
11
+ spec.summary = "Lists repositories using end-of-life software"
12
+ spec.description = <<~TEXT
13
+ Searches your GitHub repositores and lists the ones using end-of-life, i.e.
14
+ unmaintained, software.
15
+ TEXT
14
16
  spec.homepage = "https://github.com/MatheusRich/end_of_life"
15
17
  spec.license = "MIT"
16
18
  spec.required_ruby_version = ">= 3.2.0"
@@ -33,11 +35,13 @@ Gem::Specification.new do |spec|
33
35
  spec.require_paths = ["lib"]
34
36
 
35
37
  spec.add_dependency "async"
38
+ spec.add_dependency "bundler", ">= 2.3.0", "< 3"
36
39
  spec.add_dependency "dry-monads", "~> 1.3"
37
40
  spec.add_dependency "octokit", "~> 9.0"
38
41
  spec.add_dependency "pastel", "~> 0.8.0"
39
42
  spec.add_dependency "tty-spinner", "~> 0.9.0"
40
43
  spec.add_dependency "tty-table", "~> 0.12.0"
44
+ spec.add_dependency "zeitwerk", "~> 2.7"
41
45
 
42
46
  # For more information and examples about making a new gem, check out our
43
47
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -0,0 +1,17 @@
1
+ module EndOfLife
2
+ module API
3
+ extend self
4
+
5
+ BASE_URL = "https://endoflife.date/api/v1/"
6
+
7
+ def fetch_product(product) = get("products/#{product}")
8
+
9
+ private
10
+
11
+ def get(path)
12
+ response = Net::HTTP.get(URI("#{BASE_URL}#{path}"))
13
+
14
+ JSON.parse(response, symbolize_names: true)
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,6 @@
1
1
  module EndOfLife
2
2
  class CLI
3
- include TerminalHelper
3
+ include Helpers::Terminal
4
4
 
5
5
  def call(argv)
6
6
  parse_options(argv)
@@ -9,6 +9,10 @@ module EndOfLife
9
9
 
10
10
  private
11
11
 
12
+ def parse_options(argv)
13
+ Options.from(argv)
14
+ end
15
+
12
16
  def execute_command(options)
13
17
  case options[:command]
14
18
  when :help
@@ -16,65 +20,10 @@ module EndOfLife
16
20
  when :version
17
21
  puts "end_of_life v#{EndOfLife::VERSION}"
18
22
  when :print_error
19
- puts error_msg(options[:error])
20
- exit(-1)
23
+ abort error_msg(options[:error])
21
24
  else
22
- check_eol_ruby_on_repositories(options)
25
+ Scanner.scan(options)
23
26
  end
24
27
  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
28
  end
80
29
  end
@@ -0,0 +1,18 @@
1
+ require "bundler"
2
+
3
+ module EndOfLife
4
+ module Helpers
5
+ module SilentBundler
6
+ extend self
7
+
8
+ def silence_bundler
9
+ previous_ui = Bundler.ui
10
+ Bundler.ui = Bundler::UI::Silent.new
11
+
12
+ yield
13
+ ensure
14
+ Bundler.ui = previous_ui
15
+ end
16
+ end
17
+ end
18
+ end
@@ -3,7 +3,7 @@ require "tty-spinner"
3
3
  require "tty-table"
4
4
 
5
5
  module EndOfLife
6
- module TerminalHelper
6
+ module Helpers::Terminal
7
7
  def error_msg(message, label: "[ERROR]")
8
8
  label = paint.red("#{label} ")
9
9
 
@@ -0,0 +1,3 @@
1
+ module EndOfLife
2
+ InMemoryFile = Data.define(:path, :read)
3
+ end
@@ -3,50 +3,54 @@ require "optparse"
3
3
  module EndOfLife
4
4
  module Options
5
5
  def self.from(argv)
6
- options = {max_eol_date: Date.today, skip_archived: true}
6
+ options = {product: Product.find("ruby"), max_eol_date: Date.today, skip_archived: true}
7
+ OptionParser.new do |parser|
8
+ options[:parser] = parser
7
9
 
8
- OptionParser.new do |opts|
9
- options[:parser] = opts
10
+ parser.banner = "Usage: end_of_life [options]"
10
11
 
11
- opts.banner = "Usage: end_of_life [options]"
12
+ product_names = EndOfLife.products.map(&:name)
13
+ parser.on("-p NAME", "--product NAME", /#{product_names.join("|")}/i, "Sets the product to scan for (default: ruby). Supported products are: #{product_names.join(", ")}.") do |name|
14
+ options[:product] = Product.find(name)
15
+ end
12
16
 
13
- opts.on("--exclude=NAME,NAME2", Array, "Exclude repositories containing a certain word in its name. You can specify up to five words.") do |excludes|
17
+ parser.on("--exclude=NAME,NAME2", Array, "Exclude repositories containing a certain word in its name. You can specify up to five words.") do |excludes|
14
18
  options[:excludes] = excludes.first(5)
15
19
  end
16
20
 
17
- opts.on("--public-only", "Searches only public repositories") do
21
+ parser.on("--public-only", "Searches only public repositories") do
18
22
  options[:visibility] = :public
19
23
  end
20
24
 
21
- opts.on("--private-only", "Searches only private repositories") do
25
+ parser.on("--private-only", "Searches only private repositories") do
22
26
  options[:visibility] = :private
23
27
  end
24
28
 
25
- opts.on("--repo=USER/REPO", "--repository=USER/REPO", "Searches a specific repository") do |repository|
29
+ parser.on("--repo=USER/REPO", "--repository=USER/REPO", "Searches a specific repository") do |repository|
26
30
  options[:repository] = repository
27
31
  end
28
32
 
29
- opts.on("--org=ORG,ORG2...", "--organization=ORG,ORG2", Array, "Searches within specific organizations") do |organizations|
33
+ parser.on("--org=ORG,ORG2...", "--organization=ORG,ORG2", Array, "Searches within specific organizations") do |organizations|
30
34
  options[:organizations] = organizations
31
35
  end
32
36
 
33
- opts.on("-u NAME", "--user=NAME", "Sets the user used on the repository search") do |user|
37
+ parser.on("-u NAME", "--user=NAME", "Sets the user used on the repository search") do |user|
34
38
  options[:user] = user
35
39
  end
36
40
 
37
- opts.on("--max-eol-days-away NUMBER", "Sets the maximum number of days away a version can be from EOL. It defaults to 0.") do |days|
41
+ parser.on("--max-eol-days-away NUMBER", "Sets the maximum number of days away a version can be from EOL. It defaults to 0.") do |days|
38
42
  options[:max_eol_date] = Date.today + days.to_i.abs
39
43
  end
40
44
 
41
- opts.on("--include-archived", "Includes archived repositories on the search") do
45
+ parser.on("--include-archived", "Includes archived repositories on the search") do
42
46
  options[:skip_archived] = false
43
47
  end
44
48
 
45
- opts.on("-v", "--version", "Displays end_of_life version") do
49
+ parser.on("-v", "--version", "Displays end_of_life version") do
46
50
  options[:command] = :version
47
51
  end
48
52
 
49
- opts.on("-h", "--help", "Displays this help") do
53
+ parser.on("-h", "--help", "Displays this help") do
50
54
  options[:command] = :help
51
55
  end
52
56
  end.parse!(argv)
@@ -0,0 +1,31 @@
1
+ module EndOfLife
2
+ module Parsers
3
+ module Gemfile
4
+ extend Helpers::SilentBundler
5
+ extend self
6
+
7
+ def parse(file_content)
8
+ with_temp_gemfile(file_content) do |gemfile|
9
+ silence_bundler do
10
+ # This is security problem, since it runs the code inside the file
11
+ Bundler::Definition.build(gemfile.path, nil, {})
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def with_temp_gemfile(contents)
19
+ # Bundler requires a file to parse, so we need to create a temporary file
20
+ Tempfile.create("tempGemfile") do |tempfile|
21
+ tempfile.write(contents)
22
+ tempfile.rewind
23
+
24
+ yield(tempfile)
25
+ rescue Bundler::BundlerError, Errno::ENOENT # Bundler tries to read the version file, and in some versions it raises this error
26
+ nil # NOTE: maybe a Null object would be cleaner
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module EndOfLife
2
+ module Parsers
3
+ module GemfileLock
4
+ extend Helpers::SilentBundler
5
+ extend self
6
+
7
+ def parse(file_content)
8
+ silence_bundler do
9
+ Bundler::LockfileParser.new(file_content)
10
+ end
11
+ rescue Bundler::BundlerError # outside a bundler project
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module EndOfLife
2
+ module Parsers
3
+ module RubyVersion
4
+ extend self
5
+
6
+ def parse(file_content)
7
+ Gem::Version.new(file_content.strip.delete_prefix("ruby-"))
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ module EndOfLife
2
+ module Parsers
3
+ module ToolVersions
4
+ extend self
5
+
6
+ def parse(file_content)
7
+ file_content
8
+ .lines
9
+ .filter_map { |line|
10
+ line = line.strip
11
+ next if line.start_with?("#") || line.empty?
12
+
13
+ tool, version, * = line.split
14
+
15
+ next if version == "latest"
16
+
17
+ [tool, Gem::Version.new(version)]
18
+ }
19
+ .to_h
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module EndOfLife
2
+ module Product::Registry
3
+ def scans_for(product_name, search_query:)
4
+ product_registry[product_name.to_sym.downcase] = Product.new(
5
+ product_name,
6
+ search_query,
7
+ version_detector_for(product_name)
8
+ )
9
+ end
10
+
11
+ def find_product(name) = product_registry.fetch(name.to_sym.downcase)
12
+
13
+ def products = product_registry.values
14
+
15
+ private
16
+
17
+ def product_registry
18
+ @product_registry ||= {}
19
+ end
20
+
21
+ def version_detector_for(product) = VersionDetectors.for_product(product)
22
+ end
23
+ end