stackharbinger 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 78f93c3813cf5f8ec4540a11d5901f4832667fc76ac449c2cbc61899d61844af
4
+ data.tar.gz: a713dcab3bf69bd8ba1bf3aa7ff26d841e2beff17a15ba5a4b33918338b4d093
5
+ SHA512:
6
+ metadata.gz: '07888386c6149bbef7a607766cd92c59d34c2f38bdddd6dc06a97fb1734f21b9b5775c47fe5f54c81e71a459931b24dac9f3c3dbd30d49d63bdca8bd19fed3cc'
7
+ data.tar.gz: 223b899768d2c1d6e7ad2bbfd5df74679e119e8d0d79b235b401035587e23dba52c624b86f831686f5d93fbd91177b245b60cb582291d22c5b3520ff11733f3e
data/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-01-18
11
+
12
+ ### Added
13
+ - Ruby version detection from `.ruby-version`, `Gemfile`, and `Gemfile.lock`
14
+ - Rails version detection from `Gemfile.lock`
15
+ - EOL data fetching from endoflife.date API
16
+ - Smart caching with 24-hour expiry
17
+ - CLI commands:
18
+ - `harbinger scan [PATH]` - Scan project and show EOL status
19
+ - `harbinger update` - Force refresh EOL data
20
+ - `harbinger version` - Show harbinger version
21
+ - Color-coded status display:
22
+ - Red: Already EOL or <30 days remaining
23
+ - Yellow: <6 months remaining
24
+ - Green: >6 months remaining
25
+ - Product detection with helpful guidance when version not specified
26
+ - Comprehensive test suite (36 tests)
27
+ - Automatic EOL data fetch on first scan (zero configuration)
28
+
29
+ ### Technical
30
+ - Built with Thor for CLI framework
31
+ - HTTParty for HTTP requests
32
+ - TTY gems for terminal UI
33
+ - RSpec for testing
34
+ - RuboCop for linting
data/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Harbinger
2
+
3
+ **Track End-of-Life dates for your tech stack and stay ahead of deprecations.**
4
+
5
+ Harbinger is a CLI tool that scans your Ruby and Rails projects, detects versions, and warns you about upcoming EOL (End-of-Life) dates. Never get caught off-guard by unsupported dependencies again.
6
+
7
+ ## Features
8
+
9
+ - 🔍 **Auto-detects versions** from `.ruby-version`, `Gemfile`, and `Gemfile.lock`
10
+ - 📅 **Fetches EOL data** from [endoflife.date](https://endoflife.date)
11
+ - 🎨 **Color-coded warnings** (red: already EOL, yellow: <6 months, green: safe)
12
+ - ⚡ **Smart caching** (24-hour cache, works offline after first fetch)
13
+ - 🚀 **Zero configuration** - just run `harbinger scan`
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ gem install stackharbinger
19
+ ```
20
+
21
+ Or add to your Gemfile:
22
+
23
+ ```ruby
24
+ gem 'stackharbinger'
25
+ ```
26
+
27
+ The command is still `harbinger` (shorter to type).
28
+
29
+ ## Usage
30
+
31
+ ### Scan a project
32
+
33
+ ```bash
34
+ # Scan current directory
35
+ harbinger scan
36
+
37
+ # Scan specific project
38
+ harbinger scan ~/Projects/my-rails-app
39
+ ```
40
+
41
+ **Example output:**
42
+
43
+ ```
44
+ Scanning /Users/you/Projects/my-app...
45
+
46
+ Detected versions:
47
+ Ruby: 3.2.0
48
+ Rails: 7.0.8
49
+
50
+ Fetching EOL data...
51
+
52
+ Ruby 3.2.0:
53
+ EOL Date: 2026-03-31
54
+ Status: 437 days remaining
55
+
56
+ Rails 7.0.8:
57
+ EOL Date: 2025-06-01
58
+ Status: ALREADY EOL (474 days ago)
59
+ ```
60
+
61
+ ### Update EOL data
62
+
63
+ ```bash
64
+ # Force refresh EOL data from endoflife.date
65
+ harbinger update
66
+ ```
67
+
68
+ ### Show version
69
+
70
+ ```bash
71
+ harbinger version
72
+ ```
73
+
74
+ ## How It Works
75
+
76
+ 1. **Detection**: Harbinger looks for version info in your project:
77
+ - Ruby: `.ruby-version`, `Gemfile` (`ruby "x.x.x"`), `Gemfile.lock` (RUBY VERSION)
78
+ - Rails: `Gemfile.lock` (rails gem)
79
+
80
+ 2. **EOL Data**: Fetches official EOL dates from [endoflife.date](https://endoflife.date) API
81
+
82
+ 3. **Caching**: Stores data in `~/.harbinger/data/` for 24 hours (works offline)
83
+
84
+ 4. **Analysis**: Compares your versions against EOL dates and color-codes the urgency
85
+
86
+ ## Version Detection
87
+
88
+ ### Ruby Detection Priority
89
+
90
+ 1. `.ruby-version` file (highest priority)
91
+ 2. `ruby "x.x.x"` declaration in Gemfile
92
+ 3. `RUBY VERSION` section in Gemfile.lock
93
+
94
+ If Harbinger detects a Ruby project but no version:
95
+ ```
96
+ Ruby: Present (version not specified - add .ruby-version or ruby declaration in Gemfile)
97
+ ```
98
+
99
+ ### Rails Detection
100
+
101
+ Parses `Gemfile.lock` for the rails gem version.
102
+
103
+ ## Requirements
104
+
105
+ - Ruby >= 3.1.0
106
+ - Internet connection (for initial EOL data fetch)
107
+
108
+ ## Development
109
+
110
+ ```bash
111
+ # Clone the repo
112
+ git clone https://github.com/RichD/harbinger.git
113
+ cd harbinger
114
+
115
+ # Install dependencies
116
+ bundle install
117
+
118
+ # Run tests
119
+ bundle exec rspec
120
+
121
+ # Run locally
122
+ bundle exec exe/harbinger scan .
123
+ ```
124
+
125
+ ## Roadmap
126
+
127
+ ### V0.1.0 (Beta) - Current
128
+ - ✅ Ruby and Rails version detection
129
+ - ✅ EOL data fetching and caching
130
+ - ✅ CLI with scan and update commands
131
+ - ✅ Color-coded status display
132
+
133
+ ### V0.2.0 - Planned
134
+ - 📊 Dashboard: `harbinger show` to see all tracked projects
135
+ - 💾 Config management: Save and track multiple projects
136
+ - 🐘 PostgreSQL version detection
137
+ - 🗄️ MySQL version detection
138
+
139
+ ### V1.0 - Future
140
+ - 🐍 Python support (pyproject.toml, requirements.txt)
141
+ - 📦 Node.js support (package.json, .nvmrc)
142
+ - 🦀 Rust support (Cargo.toml)
143
+ - 🏠 Homebrew distribution: `brew install harbinger`
144
+
145
+ ### V2.0 - Vision
146
+ - 🤖 AI-powered upgrade summaries
147
+ - 📧 Email/Slack notifications
148
+ - ☁️ Cloud platform detection (AWS, Heroku, etc.)
149
+ - 👥 Team collaboration features
150
+
151
+ ## Contributing
152
+
153
+ Contributions welcome! Please:
154
+
155
+ 1. Fork the repo
156
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
157
+ 3. Write tests for your changes
158
+ 4. Ensure all tests pass (`bundle exec rspec`)
159
+ 5. Commit your changes (`git commit -m 'Add amazing feature'`)
160
+ 6. Push to the branch (`git push origin feature/amazing-feature`)
161
+ 7. Open a Pull Request
162
+
163
+ ## License
164
+
165
+ This gem is available as open source under the terms of the MIT License.
166
+
167
+ ## Credits
168
+
169
+ - EOL data provided by [endoflife.date](https://endoflife.date)
170
+ - Built with ❤️ using Ruby and Thor
171
+
172
+ ## Links
173
+
174
+ - Website: [stackharbinger.com](https://stackharbinger.com)
175
+ - GitHub: [github.com/RichD/harbinger](https://github.com/RichD/harbinger)
176
+ - RubyGems: [rubygems.org/gems/stackharbinger](https://rubygems.org/gems/stackharbinger)
177
+
178
+ ---
179
+
180
+ **Like Harbinger?** Give it a ⭐ on GitHub!
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/exe/harbinger ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "harbinger/cli"
5
+
6
+ Harbinger::CLI.start(ARGV)
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Harbinger
4
+ module Analyzers
5
+ class RailsAnalyzer
6
+ def initialize(project_path)
7
+ @project_path = project_path
8
+ end
9
+
10
+ def detect
11
+ detect_from_gemfile_lock
12
+ end
13
+
14
+ def rails_detected?
15
+ gemfile_lock = File.join(project_path, "Gemfile.lock")
16
+ return false unless File.exist?(gemfile_lock)
17
+
18
+ content = File.read(gemfile_lock)
19
+ content.match?(/^\s*rails\s+\(/)
20
+ rescue StandardError
21
+ false
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :project_path
27
+
28
+ def detect_from_gemfile_lock
29
+ file_path = File.join(project_path, "Gemfile.lock")
30
+ return nil unless File.exist?(file_path)
31
+
32
+ content = File.read(file_path)
33
+ match = content.match(/^\s*rails\s+\(([^)]+)\)/)
34
+ match ? match[1] : nil
35
+ rescue StandardError
36
+ nil
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Harbinger
4
+ module Analyzers
5
+ class RubyDetector
6
+ def initialize(project_path)
7
+ @project_path = project_path
8
+ end
9
+
10
+ def detect
11
+ detect_from_ruby_version ||
12
+ detect_from_gemfile ||
13
+ detect_from_gemfile_lock
14
+ end
15
+
16
+ def ruby_detected?
17
+ File.exist?(File.join(project_path, "Gemfile")) ||
18
+ File.exist?(File.join(project_path, "Gemfile.lock")) ||
19
+ File.exist?(File.join(project_path, ".ruby-version"))
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :project_path
25
+
26
+ def detect_from_ruby_version
27
+ file_path = File.join(project_path, ".ruby-version")
28
+ return nil unless File.exist?(file_path)
29
+
30
+ content = File.read(file_path).strip
31
+ extract_version(content)
32
+ rescue StandardError
33
+ nil
34
+ end
35
+
36
+ def detect_from_gemfile
37
+ file_path = File.join(project_path, "Gemfile")
38
+ return nil unless File.exist?(file_path)
39
+
40
+ content = File.read(file_path)
41
+ match = content.match(/ruby\s+["']([^"']+)["']/)
42
+ match ? match[1] : nil
43
+ rescue StandardError
44
+ nil
45
+ end
46
+
47
+ def detect_from_gemfile_lock
48
+ file_path = File.join(project_path, "Gemfile.lock")
49
+ return nil unless File.exist?(file_path)
50
+
51
+ content = File.read(file_path)
52
+ match = content.match(/RUBY VERSION\s+ruby\s+([^\s]+)/)
53
+ match ? extract_version(match[1]) : nil
54
+ rescue StandardError
55
+ nil
56
+ end
57
+
58
+ def extract_version(version_string)
59
+ # Remove "ruby-" prefix if present
60
+ version = version_string.sub(/^ruby-/, "")
61
+ # Remove patch level suffix (e.g., "p223")
62
+ version.sub(/p\d+$/, "")
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "date"
5
+ require_relative "version"
6
+ require "harbinger/analyzers/ruby_detector"
7
+ require "harbinger/analyzers/rails_analyzer"
8
+ require "harbinger/eol_fetcher"
9
+
10
+ module Harbinger
11
+ class CLI < Thor
12
+ def self.exit_on_failure?
13
+ true
14
+ end
15
+
16
+ desc "scan [PATH]", "Scan a project directory and detect versions"
17
+ option :path, type: :string, aliases: "-p", desc: "Path to project directory"
18
+ def scan(path = nil)
19
+ project_path = path || options[:path] || Dir.pwd
20
+
21
+ unless File.directory?(project_path)
22
+ say "Error: #{project_path} is not a valid directory", :red
23
+ exit 1
24
+ end
25
+
26
+ say "Scanning #{project_path}...", :cyan
27
+
28
+ # Detect versions
29
+ ruby_detector = Analyzers::RubyDetector.new(project_path)
30
+ rails_analyzer = Analyzers::RailsAnalyzer.new(project_path)
31
+
32
+ ruby_version = ruby_detector.detect
33
+ rails_version = rails_analyzer.detect
34
+
35
+ ruby_present = ruby_detector.ruby_detected?
36
+ rails_present = rails_analyzer.rails_detected?
37
+
38
+ # Display results
39
+ say "\nDetected versions:", :green
40
+ if ruby_version
41
+ say " Ruby: #{ruby_version}", :white
42
+ elsif ruby_present
43
+ say " Ruby: Present (version not specified - add .ruby-version or ruby declaration in Gemfile)", :yellow
44
+ else
45
+ say " Ruby: Not a Ruby project", :red
46
+ end
47
+
48
+ if rails_version
49
+ say " Rails: #{rails_version}", :white
50
+ elsif rails_present
51
+ say " Rails: Present (version not found in Gemfile.lock)", :yellow
52
+ else
53
+ say " Rails: Not detected", :yellow
54
+ end
55
+
56
+ # Fetch and display EOL dates
57
+ if ruby_version || rails_version
58
+ say "\nFetching EOL data...", :cyan
59
+ fetcher = EolFetcher.new
60
+
61
+ if ruby_version
62
+ display_eol_info(fetcher, "Ruby", ruby_version)
63
+ end
64
+
65
+ if rails_version
66
+ display_eol_info(fetcher, "Rails", rails_version)
67
+ end
68
+ end
69
+ end
70
+
71
+ desc "show", "Show EOL status for tracked projects"
72
+ def show
73
+ say "Show command coming soon!", :yellow
74
+ say "Use 'harbinger scan' to check a project's EOL status", :white
75
+ end
76
+
77
+ desc "update", "Force refresh EOL data from endoflife.date"
78
+ def update
79
+ say "Updating EOL data...", :cyan
80
+
81
+ fetcher = EolFetcher.new
82
+ products = %w[ruby rails]
83
+
84
+ products.each do |product|
85
+ say "Fetching #{product}...", :white
86
+ data = fetcher.fetch(product)
87
+
88
+ if data
89
+ say " ✓ #{product.capitalize}: #{data.length} versions cached", :green
90
+ else
91
+ say " ✗ #{product.capitalize}: Failed to fetch", :red
92
+ end
93
+ end
94
+
95
+ say "\nEOL data updated successfully!", :green
96
+ end
97
+
98
+ desc "version", "Show harbinger version"
99
+ def version
100
+ say "Harbinger version #{Harbinger::VERSION}", :cyan
101
+ end
102
+
103
+ private
104
+
105
+ def display_eol_info(fetcher, product, version)
106
+ product_key = product.downcase
107
+ eol_date = fetcher.eol_date_for(product_key, version)
108
+
109
+ if eol_date
110
+ days_until_eol = days_until(eol_date)
111
+ color = eol_color(days_until_eol)
112
+
113
+ say "\n#{product} #{version}:", :white
114
+ say " EOL Date: #{eol_date}", color
115
+ say " Status: #{eol_status(days_until_eol)}", color
116
+ else
117
+ say "\n#{product} #{version}:", :white
118
+ say " EOL Date: Unknown (version not found in database)", :yellow
119
+ end
120
+ end
121
+
122
+ def days_until(date_string)
123
+ eol_date = Date.parse(date_string)
124
+ (eol_date - Date.today).to_i
125
+ end
126
+
127
+ def eol_color(days)
128
+ if days < 0
129
+ :red
130
+ elsif days < 180 # < 6 months
131
+ :yellow
132
+ else
133
+ :green
134
+ end
135
+ end
136
+
137
+ def eol_status(days)
138
+ if days < 0
139
+ "ALREADY EOL (#{days.abs} days ago)"
140
+ elsif days < 30
141
+ "ENDING SOON (#{days} days remaining)"
142
+ elsif days < 180
143
+ "#{days} days remaining"
144
+ else
145
+ "#{days} days remaining"
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httparty"
4
+ require "json"
5
+ require "fileutils"
6
+
7
+ module Harbinger
8
+ class EolFetcher
9
+ CACHE_EXPIRY_SECONDS = 24 * 60 * 60 # 24 hours
10
+ API_BASE_URL = "https://endoflife.date/api"
11
+
12
+ def initialize(cache_dir: default_cache_dir)
13
+ @cache_dir = cache_dir
14
+ FileUtils.mkdir_p(@cache_dir)
15
+ end
16
+
17
+ def fetch(product)
18
+ cache_file = cache_file_path(product)
19
+
20
+ # Return fresh cache if available
21
+ if cache_fresh?(cache_file)
22
+ return read_cache(cache_file)
23
+ end
24
+
25
+ # Try to fetch from API
26
+ begin
27
+ data = fetch_from_api(product)
28
+ write_cache(cache_file, data)
29
+ data
30
+ rescue StandardError => e
31
+ # Fall back to stale cache if API fails
32
+ read_cache(cache_file) if File.exist?(cache_file)
33
+ end
34
+ end
35
+
36
+ def eol_date_for(product, version)
37
+ data = fetch(product)
38
+ return nil unless data
39
+
40
+ # Extract major.minor from version (e.g., "3.2.1" -> "3.2")
41
+ version_parts = version.split(".")
42
+ major_minor = "#{version_parts[0]}.#{version_parts[1]}"
43
+
44
+ # Find matching cycle
45
+ entry = data.find { |item| item["cycle"] == major_minor }
46
+ entry ? entry["eol"] : nil
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :cache_dir
52
+
53
+ def default_cache_dir
54
+ File.join(Dir.home, ".harbinger", "data")
55
+ end
56
+
57
+ def cache_file_path(product)
58
+ File.join(cache_dir, "#{product}.json")
59
+ end
60
+
61
+ def cache_fresh?(cache_file)
62
+ return false unless File.exist?(cache_file)
63
+
64
+ File.mtime(cache_file) > Time.now - CACHE_EXPIRY_SECONDS
65
+ end
66
+
67
+ def read_cache(cache_file)
68
+ return nil unless File.exist?(cache_file)
69
+
70
+ JSON.parse(File.read(cache_file))
71
+ rescue StandardError
72
+ nil
73
+ end
74
+
75
+ def write_cache(cache_file, data)
76
+ File.write(cache_file, JSON.pretty_generate(data))
77
+ rescue StandardError
78
+ # Silently fail if we can't write cache
79
+ nil
80
+ end
81
+
82
+ def fetch_from_api(product)
83
+ url = "#{API_BASE_URL}/#{product}.json"
84
+ response = HTTParty.get(url)
85
+
86
+ raise "API request failed: #{response.code}" unless response.success?
87
+
88
+ response.parsed_response
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Harbinger
4
+ VERSION = "0.1.0"
5
+ end
data/lib/harbinger.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "harbinger/version"
4
+ require_relative "harbinger/cli"
5
+ require_relative "harbinger/analyzers/ruby_detector"
6
+ require_relative "harbinger/analyzers/rails_analyzer"
7
+ require_relative "harbinger/eol_fetcher"
8
+
9
+ module Harbinger
10
+ class Error < StandardError; end
11
+ end
data/sig/harbinger.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Harbinger
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stackharbinger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rich Dabrowski
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: thor
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: tty-prompt
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.23'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.23'
40
+ - !ruby/object:Gem::Dependency
41
+ name: tty-table
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.12'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.12'
54
+ - !ruby/object:Gem::Dependency
55
+ name: colorize
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.1'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: httparty
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.21'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.21'
82
+ description: Harbinger monitors EOL dates for Ruby, Rails, PostgreSQL and other technologies
83
+ in your stack. Auto-detects versions from your projects and alerts you before support
84
+ ends.
85
+ email:
86
+ - engineering@richd.net
87
+ executables:
88
+ - harbinger
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - CHANGELOG.md
93
+ - README.md
94
+ - Rakefile
95
+ - exe/harbinger
96
+ - lib/harbinger.rb
97
+ - lib/harbinger/analyzers/rails_analyzer.rb
98
+ - lib/harbinger/analyzers/ruby_detector.rb
99
+ - lib/harbinger/cli.rb
100
+ - lib/harbinger/eol_fetcher.rb
101
+ - lib/harbinger/version.rb
102
+ - sig/harbinger.rbs
103
+ homepage: https://stackharbinger.com
104
+ licenses:
105
+ - MIT
106
+ metadata:
107
+ allowed_push_host: https://rubygems.org
108
+ homepage_uri: https://stackharbinger.com
109
+ source_code_uri: https://github.com/RichD/harbinger
110
+ changelog_uri: https://github.com/RichD/harbinger/blob/main/CHANGELOG.md
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 3.1.0
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubygems_version: 3.7.2
126
+ specification_version: 4
127
+ summary: Track End-of-Life dates for your tech stack and stay ahead of deprecations
128
+ test_files: []