stackharbinger 0.3.0 → 1.0.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: 0625ffb650537306abc4297407d0373c204c69412c7610254488c1006461bbcc
4
- data.tar.gz: 65106dadaa487141d44e7772c57d9b0c59e18ab478517f0e94cad1440b8ab8b4
3
+ metadata.gz: 1d46315e9aab151406a3993d75a034eaf28f16d1ce92f44b6344438ba8b9c482
4
+ data.tar.gz: b4d3c35c6578600ed85d1a387b1cfe13ce5873c256dce6d20267c0366e329052
5
5
  SHA512:
6
- metadata.gz: 3922e4f871e0782ebf821ef3e01f91aebba249c908f81a3b8f94e550d0d7d68a8525118d61f78b326b801aeecb801fbd89f975b48c2c95bbafd12c7703ebcf7a
7
- data.tar.gz: c50866bca2f2b69d995d01735d62da1cb44f436aa546e5a069d7f8b50b8d25e63d95969f4df85ed008cd7d893a7e8d92959efe12e29e7834199f7322455a9cb9
6
+ metadata.gz: a6d586f4ccc611050bff15516e5ce10601b21b530c51a899742dd8fb8a167cb2d297d0d182fb3a4536b78d85db09b084c25c646fbf3249fd1120125a614aa50b
7
+ data.tar.gz: a1a0aa1900b07a3be28862e6ad4e3196e05735dc338c22ed592f6ad6d3af6d925764b0c2d957a96fa3d48b9f995e500edcfbb932897237a1a4c44e8bd1d16228
data/README.md CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  **Track End-of-Life dates for your tech stack and stay ahead of deprecations.**
4
4
 
5
- Harbinger is a CLI tool that scans your Ruby, Rails, PostgreSQL, and MySQL versions, and warns you about upcoming EOL (End-of-Life) dates. Never get caught off-guard by unsupported dependencies again.
5
+ Harbinger is a CLI tool that scans your Ruby, Rails, Python, Node.js, Rust, Go, PostgreSQL, MySQL, Redis, and MongoDB versions, and warns you about upcoming EOL (End-of-Life) dates. Never get caught off-guard by unsupported dependencies again.
6
6
 
7
7
  ## Features
8
8
 
9
- - 🔍 **Auto-detects versions** from `.ruby-version`, `Gemfile`, `Gemfile.lock`, and `config/database.yml`
10
- - 🐘 **Database detection** for PostgreSQL and MySQL (mysql2/trilogy adapters)
9
+ - 🔍 **Auto-detects versions** from `.ruby-version`, `Gemfile`, `Gemfile.lock`, `.nvmrc`, `.python-version`, `pyproject.toml`, `package.json`, `rust-toolchain`, `Cargo.toml`, `go.mod`, `config/database.yml`, and `docker-compose.yml`
10
+ - 🐘 **Database detection** for PostgreSQL, MySQL, Redis, and MongoDB
11
+ - 🌐 **Multi-language support** - Ruby, Python, Node.js, Rust, Go
12
+ - 📊 **Ecosystem-grouped dashboard** - Projects organized by language ecosystem with relevant components only
11
13
  - 📅 **Fetches EOL data** from [endoflife.date](https://endoflife.date)
12
14
  - 🎨 **Color-coded warnings** (red: already EOL, yellow: <6 months, green: safe)
13
15
  - ⚡ **Smart caching** (24-hour cache, works offline after first fetch)
14
- - 📊 **Track multiple projects** with `--save` and view dashboard with `harbinger show`
15
16
  - 🔄 **Bulk operations** with `--recursive` scan and `rescan` command
17
+ - 📤 **Export to JSON/CSV** for reporting and automation
16
18
  - 🚀 **Zero configuration** - just run `harbinger scan`
17
19
 
18
20
  ## Installation
@@ -86,23 +88,72 @@ PostgreSQL 16.11:
86
88
  ```bash
87
89
  # Show dashboard of all tracked projects
88
90
  harbinger show
91
+
92
+ # Filter to specific project(s) by name or path
93
+ harbinger show budget
94
+ harbinger show job
95
+
96
+ # Show project paths with verbose mode
97
+ harbinger show -v
98
+ harbinger show job --verbose
99
+ ```
100
+
101
+ ### Export data
102
+
103
+ ```bash
104
+ # Export to JSON (stdout)
105
+ harbinger show --format json
106
+
107
+ # Export to CSV (stdout)
108
+ harbinger show --format csv
109
+
110
+ # Save to file
111
+ harbinger show --format json -o report.json
112
+ harbinger show --format csv --output eol-report.csv
113
+
114
+ # Export filtered projects
115
+ harbinger show myproject --format json
89
116
  ```
90
117
 
91
118
  **Example output:**
92
119
 
93
120
  ```
94
- Tracked Projects (10)
121
+ Tracked Projects (12)
122
+
123
+ Ruby Ecosystem (7)
124
+ ================================================================================
125
+ ┌─────────────────┬───────┬──────────┬────────────┬───────┬─────────────────┐
126
+ │ Project │ Ruby │ Rails │ PostgreSQL │ Redis │ Status │
127
+ ├─────────────────┼───────┼──────────┼────────────┼───────┼─────────────────┤
128
+ │ shop-api │ 3.2.0 │ 6.1.7 │ 15.0 │ - │ ✗ Rails EOL │
129
+ │ blog-engine │ 3.3.0 │ 7.0.8 │ 16.0 │ 7.0 │ ✗ Rails EOL │
130
+ │ analytics-app │ 3.3.0 │ 8.0.1 │ 16.0 │ - │ ✓ Current │
131
+ │ admin-portal │ 3.3.0 │ 8.0.4 │ 16.11 │ 7.2 │ ✓ Current │
132
+ │ billing-service │ 3.4.1 │ 8.1.0 │ 17.0 │ - │ ✓ Current │
133
+ └─────────────────┴───────┴──────────┴────────────┴───────┴─────────────────┘
134
+
135
+ Python Ecosystem (3)
136
+ ================================================================================
137
+ ┌──────────────┬────────┬────────────┬─────────┐
138
+ │ Project │ Python │ PostgreSQL │ Status │
139
+ ├──────────────┼────────┼────────────┼─────────┤
140
+ │ ml-pipeline │ 3.11 │ 16.0 │ ✓ Current │
141
+ │ data-scraper │ 3.12 │ - │ ✓ Current │
142
+ │ ai-worker │ 3.13 │ 15.0 │ ✓ Current │
143
+ └──────────────┴────────┴────────────┴─────────┘
144
+
145
+ Node.js Ecosystem (2)
95
146
  ================================================================================
96
- ┌───────────────────┬───────┬──────────┬────────────┬───────┬─────────────┐
97
- │ Project Ruby │ Rails │ PostgreSQL │ MySQL │ Status
98
- ├───────────────────┼───────┼──────────┼────────────┼───────┼─────────────┤
99
- ledger 3.3.0 6.1.7.10 │ - │ - │ Rails EOL
100
- option_tracker 3.3.0 7.0.8.7 - │ - ✗ Rails EOL │
101
- │ CarCal │ - │ 8.0.2 │ - │ - │ ✓ Current │
102
- │ job_tracker │ 3.3.0 │ 8.0.4 │ 16.11 │ - │ ✓ Current │
103
- └───────────────────┴───────┴──────────┴────────────┴───────┴─────────────┘
147
+ ┌───────────────┬─────────┬────────────┬──────────────────────┐
148
+ │ Project Node.js │ PostgreSQL │ Status
149
+ ├───────────────┼─────────┼────────────┼──────────────────────┤
150
+ frontend-app 18.0 │ - │ Node.js ending soon
151
+ realtime-api 22.0 16.0 Current
152
+ └───────────────┴─────────┴────────────┴──────────────────────┘
104
153
  ```
105
154
 
155
+ Projects are grouped by their primary programming language ecosystem. Each ecosystem only displays relevant components (e.g., Python projects don't show Ruby/Rails columns).
156
+
106
157
  ### Re-scan all tracked projects
107
158
 
108
159
  ```bash
@@ -113,6 +164,13 @@ harbinger rescan
113
164
  harbinger rescan --verbose
114
165
  ```
115
166
 
167
+ ### Remove a project
168
+
169
+ ```bash
170
+ # Remove a project from tracking
171
+ harbinger remove my-project
172
+ ```
173
+
116
174
  ### Update EOL data
117
175
 
118
176
  ```bash
@@ -131,8 +189,14 @@ harbinger version
131
189
  1. **Detection**: Harbinger looks for version info in your project:
132
190
  - Ruby: `.ruby-version`, `Gemfile` (`ruby "x.x.x"`), `Gemfile.lock` (RUBY VERSION)
133
191
  - Rails: `Gemfile.lock` (rails gem)
192
+ - Python: `.python-version`, `pyproject.toml`, `docker-compose.yml`, or `python --version`
193
+ - Node.js: `.nvmrc`, `.node-version`, `package.json` engines, `docker-compose.yml`, or `node --version`
194
+ - Rust: `rust-toolchain`, `rust-toolchain.toml`, `Cargo.toml` (rust-version), `docker-compose.yml`, or `rustc --version`
195
+ - Go: `go.mod`, `go.work`, `.go-version`, `docker-compose.yml`, or `go version`
134
196
  - PostgreSQL: `config/database.yml` (adapter check) + `psql --version` or `pg` gem
135
197
  - MySQL: `config/database.yml` (mysql2/trilogy adapter) + `mysql --version` or gem version
198
+ - Redis: `docker-compose.yml` + `redis-server --version` or `redis` gem
199
+ - MongoDB: `docker-compose.yml` + `mongod --version` or `mongoid`/`mongo` gem
136
200
 
137
201
  2. **EOL Data**: Fetches official EOL dates from [endoflife.date](https://endoflife.date) API
138
202
 
@@ -173,6 +237,53 @@ Parses `Gemfile.lock` for the rails gem version.
173
237
 
174
238
  **Supported adapters**: `mysql2` (traditional) and `trilogy` (Rails 7.1+)
175
239
 
240
+ ### Redis Detection
241
+
242
+ 1. Checks `docker-compose.yml` for redis image with version tag
243
+ 2. Tries `redis-server --version` for local installations
244
+ 3. Falls back to `redis` gem version from `Gemfile.lock`
245
+
246
+ ### MongoDB Detection
247
+
248
+ 1. Checks `docker-compose.yml` for mongo image with version tag
249
+ 2. Tries `mongod --version` for local installations
250
+ 3. Falls back to `mongoid` or `mongo` gem version from `Gemfile.lock`
251
+
252
+ ### Python Detection
253
+
254
+ 1. `.python-version` file (highest priority)
255
+ 2. `pyproject.toml` (`requires-python` field)
256
+ 3. Docker Compose `python:*` images
257
+ 4. `python --version` for system installation
258
+
259
+ ### Node.js Detection
260
+
261
+ 1. `.nvmrc` or `.node-version` files (highest priority - explicit version specification)
262
+ 2. `package.json` `engines.node` field (e.g., ">=18.0.0")
263
+ 3. Docker Compose `node:*` images
264
+ 4. `node --version` for system installation
265
+
266
+ **Version Normalization**: Handles constraint operators (`>=`, `^`, `~`), LTS names (`lts/hydrogen`), and version ranges
267
+
268
+ ### Rust Detection
269
+
270
+ 1. `rust-toolchain` or `rust-toolchain.toml` files (highest priority - explicit toolchain specification)
271
+ 2. `Cargo.toml` `rust-version` field (MSRV - Minimum Supported Rust Version)
272
+ 3. Docker Compose `rust:*` images
273
+ 4. `rustc --version` for system installation (only when Rust project files exist)
274
+
275
+ **Note:** Symbolic channels like "stable", "beta", "nightly" are skipped as they don't provide specific versions.
276
+
277
+ ### Go Detection
278
+
279
+ 1. `go.mod` file (highest priority - standard Go module version specification)
280
+ 2. `go.work` file (Go workspace format)
281
+ 3. `.go-version` file
282
+ 4. Docker Compose `golang:*` images
283
+ 5. `go version` for system installation (only when Go project files exist)
284
+
285
+ **Note:** Shell fallback only executes when Go project files are detected, preventing unnecessary command execution.
286
+
176
287
  ## Requirements
177
288
 
178
289
  - Ruby >= 3.1.0
@@ -197,30 +308,27 @@ bundle exec exe/harbinger scan .
197
308
 
198
309
  ## Roadmap
199
310
 
200
- ### V0.3.0 - Current
201
- - ✅ PostgreSQL version detection with local/remote database handling
202
- - ✅ MySQL version detection (mysql2 and trilogy adapters)
203
- - ✅ Rescan command to update all tracked projects
204
- - ✅ Enhanced dashboard with database columns
205
- - ✅ EOL tracking for PostgreSQL and MySQL
206
-
207
- ### V0.4.0 - Planned
208
- - 📋 Export reports to JSON/CSV
209
- - 🐳 Docker Compose database version detection
210
- - 🔴 Redis version detection
211
- - 🍃 MongoDB version detection
212
-
213
- ### V1.0 - Future
214
- - 🐍 Python support (pyproject.toml, requirements.txt)
215
- - 📦 Node.js support (package.json, .nvmrc)
216
- - 🦀 Rust support (Cargo.toml)
217
- - 🐘 Go support (go.mod)
311
+ ### V1.0 - Current (Ready for Release!)
312
+ - ✅ Ruby, Rails, Python, Node.js, Rust, Go version detection
313
+ - ✅ PostgreSQL, MySQL, Redis, MongoDB version detection
314
+ - ✅ Ecosystem-grouped dashboard with smart component display
315
+ - ✅ Export reports to JSON/CSV
316
+ - ✅ Bulk scanning with `--recursive` and `rescan` commands
317
+ - ✅ 24-hour smart caching for offline support
318
+ - Color-coded EOL warnings
319
+
320
+ ### V1.1 - Next
321
+ - 🔷 TypeScript version detection
322
+ - 🎯 Framework detection (Django, Flask, Express, Gin)
323
+ - 📦 Package manager detection (npm, yarn, pip, bundler, go modules versions)
324
+ - 🔍 Dependency vulnerability scanning integration
218
325
 
219
326
  ### V2.0 - Vision
220
- - 🤖 AI-powered upgrade summaries
221
- - 📧 Email/Slack notifications
222
- - ☁️ Cloud platform detection (AWS, Heroku, etc.)
223
- - 👥 Team collaboration features
327
+ - 🤖 AI-powered upgrade summaries and breaking change analysis
328
+ - 📧 Email/Slack notifications for approaching EOL dates
329
+ - ☁️ Cloud platform detection (AWS, Heroku, Render)
330
+ - 👥 Team collaboration features (shared dashboards)
331
+ - 📈 Historical tracking and trends
224
332
 
225
333
  ## Contributing
226
334
 
data/docs/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Harbinger - Track EOL Dates for Your Tech Stack</title>
7
- <meta name="description" content="Never get caught off-guard by unsupported dependencies. Harbinger tracks End-of-Life dates for Ruby, Rails, and more.">
7
+ <meta name="description" content="Never get caught off-guard by unsupported dependencies. Harbinger tracks End-of-Life dates for Ruby, Rails, PostgreSQL, MySQL, Redis, MongoDB, Python, Node.js, and Rust.">
8
8
  <script src="https://cdn.tailwindcss.com"></script>
9
9
  <style>
10
10
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;900&display=swap');
@@ -17,7 +17,7 @@
17
17
  <div class="max-w-6xl mx-auto px-4 py-20">
18
18
  <h1 class="text-5xl md:text-7xl font-black mb-6">Harbinger</h1>
19
19
  <p class="text-2xl md:text-3xl mb-8 text-blue-100">Track End-of-Life dates for your tech stack</p>
20
- <p class="text-xl mb-12 text-blue-100 max-w-2xl">Never get caught off-guard by unsupported dependencies. Harbinger scans your Ruby and Rails projects and warns you before support ends.</p>
20
+ <p class="text-xl mb-12 text-blue-100 max-w-2xl">Never get caught off-guard by unsupported dependencies. Harbinger scans your Ruby, Rails, Python, Node.js, Rust, Go, PostgreSQL, MySQL, Redis, and MongoDB versions and warns you before support ends.</p>
21
21
 
22
22
  <div class="flex flex-col sm:flex-row gap-4">
23
23
  <div class="bg-gray-900 rounded-lg p-4 font-mono text-sm">
@@ -38,7 +38,7 @@
38
38
  <div class="bg-white p-6 rounded-lg shadow-sm">
39
39
  <div class="text-4xl mb-4">🔍</div>
40
40
  <h3 class="text-xl font-semibold mb-2">Auto-Detection</h3>
41
- <p class="text-gray-600">Automatically detects Ruby and Rails versions from .ruby-version, Gemfile, and Gemfile.lock</p>
41
+ <p class="text-gray-600">Detects Ruby, Rails, Python, Node.js, Rust, Go, PostgreSQL, MySQL, Redis, and MongoDB versions from project files and Docker configs</p>
42
42
  </div>
43
43
  <div class="bg-white p-6 rounded-lg shadow-sm">
44
44
  <div class="text-4xl mb-4">📅</div>
@@ -62,8 +62,8 @@
62
62
  </div>
63
63
  <div class="bg-white p-6 rounded-lg shadow-sm">
64
64
  <div class="text-4xl mb-4">📊</div>
65
- <h3 class="text-xl font-semibold mb-2">Dashboard View</h3>
66
- <p class="text-gray-600">Track multiple projects and see EOL status at a glance with harbinger show</p>
65
+ <h3 class="text-xl font-semibold mb-2">Ecosystem Grouping</h3>
66
+ <p class="text-gray-600">Projects organized by language ecosystem (Ruby, Python, Node.js, Rust) with only relevant components displayed</p>
67
67
  </div>
68
68
  <div class="bg-white p-6 rounded-lg shadow-sm">
69
69
  <div class="text-4xl mb-4">🔄</div>
@@ -73,7 +73,7 @@
73
73
  <div class="bg-white p-6 rounded-lg shadow-sm">
74
74
  <div class="text-4xl mb-4">🧪</div>
75
75
  <h3 class="text-xl font-semibold mb-2">Well Tested</h3>
76
- <p class="text-gray-600">52 RSpec tests with 100% pass rate, built with TDD</p>
76
+ <p class="text-gray-600">146 RSpec tests with 100% pass rate, built with TDD</p>
77
77
  </div>
78
78
  </div>
79
79
  </div>
@@ -109,12 +109,58 @@
109
109
  </div>
110
110
 
111
111
  <div class="prose max-w-none">
112
- <h3 class="text-2xl font-semibold mb-4">Commands</h3>
112
+ <h3 class="text-2xl font-semibold mb-4">Dashboard with Ecosystem Grouping</h3>
113
+ <p class="text-gray-600 mb-4">Projects are organized by their primary programming language ecosystem, showing only relevant components:</p>
114
+
115
+ <div class="bg-gray-900 rounded-lg p-6 font-mono text-xs overflow-x-auto mb-8">
116
+ <div class="text-gray-400 mb-1">$ harbinger show</div>
117
+ <div class="text-white mb-2">Tracked Projects (12)</div>
118
+
119
+ <div class="text-cyan-400 mt-3 mb-1">Ruby Ecosystem (7)</div>
120
+ <div class="text-gray-400">================================================================================</div>
121
+ <div class="text-white">
122
+ <div>┌─────────────────┬───────┬──────────┬────────────┬───────┬─────────────────┐</div>
123
+ <div>│ Project │ Ruby │ Rails │ PostgreSQL │ Redis │ Status │</div>
124
+ <div>├─────────────────┼───────┼──────────┼────────────┼───────┼─────────────────┤</div>
125
+ <div>│ shop-api │ 3.2.0 │ 6.1.7 │ 15.0 │ - │ <span class="text-red-400">✗ Rails EOL</span> │</div>
126
+ <div>│ blog-engine │ 3.3.0 │ 7.0.8 │ 16.0 │ 7.0 │ <span class="text-red-400">✗ Rails EOL</span> │</div>
127
+ <div>│ admin-portal │ 3.3.0 │ 8.0.4 │ 16.11 │ 7.2 │ <span class="text-green-400">✓ Current</span> │</div>
128
+ <div>└─────────────────┴───────┴──────────┴────────────┴───────┴─────────────────┘</div>
129
+ </div>
130
+
131
+ <div class="text-cyan-400 mt-3 mb-1">Python Ecosystem (3)</div>
132
+ <div class="text-gray-400">================================================================================</div>
133
+ <div class="text-white">
134
+ <div>┌──────────────┬────────┬────────────┬─────────┐</div>
135
+ <div>│ Project │ Python │ PostgreSQL │ Status │</div>
136
+ <div>├──────────────┼────────┼────────────┼─────────┤</div>
137
+ <div>│ ml-pipeline │ 3.11 │ 16.0 │ <span class="text-green-400">✓ Current</span> │</div>
138
+ <div>│ data-scraper │ 3.12 │ - │ <span class="text-green-400">✓ Current</span> │</div>
139
+ <div>└──────────────┴────────┴────────────┴─────────┘</div>
140
+ </div>
141
+
142
+ <div class="text-cyan-400 mt-3 mb-1">Node.js Ecosystem (2)</div>
143
+ <div class="text-gray-400">================================================================================</div>
144
+ <div class="text-white">
145
+ <div>┌──────────────┬─────────┬────────────┬─────────┐</div>
146
+ <div>│ Project │ Node.js │ PostgreSQL │ Status │</div>
147
+ <div>├──────────────┼─────────┼────────────┼─────────┤</div>
148
+ <div>│ frontend-app │ 18.0 │ - │ <span class="text-yellow-400">⚠ Node.js ending soon</span> │</div>
149
+ <div>│ realtime-api │ 22.0 │ 16.0 │ <span class="text-green-400">✓ Current</span> │</div>
150
+ <div>└──────────────┴─────────┴────────────┴─────────┘</div>
151
+ </div>
152
+ </div>
153
+
154
+ <h3 class="text-2xl font-semibold mb-4 mt-8">Commands</h3>
113
155
  <ul class="space-y-3 text-gray-700">
114
156
  <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger scan --path [PATH]</code> - Scan a project and show EOL status</li>
115
157
  <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger scan --save</code> - Save project for tracking</li>
116
158
  <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger scan --recursive</code> - Scan all projects in a directory</li>
117
- <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger show</code> - View dashboard of all tracked projects</li>
159
+ <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger show [PROJECT] [-v]</code> - View dashboard (filter by name/path, -v shows paths)</li>
160
+ <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger show --format json|csv</code> - Export data to JSON or CSV</li>
161
+ <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger show --format json -o report.json</code> - Save export to file</li>
162
+ <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger rescan</code> - Re-scan all tracked projects and update versions</li>
163
+ <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger remove PROJECT</code> - Remove a project from tracking</li>
118
164
  <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger update</code> - Force refresh EOL data from API</li>
119
165
  <li><code class="bg-gray-100 px-2 py-1 rounded">harbinger version</code> - Show harbinger version</li>
120
166
  </ul>
@@ -127,30 +173,31 @@
127
173
  <h2 class="text-3xl font-bold mb-8 text-center">Roadmap</h2>
128
174
  <div class="grid md:grid-cols-3 gap-8">
129
175
  <div>
130
- <h3 class="text-xl font-semibold mb-4 text-green-600">✅ V0.2.0 (Current)</h3>
176
+ <h3 class="text-xl font-semibold mb-4 text-green-600">✅ V1.0 (Current)</h3>
131
177
  <ul class="space-y-2 text-gray-600">
132
- <li>• Dashboard view</li>
133
- <li>• Project tracking</li>
134
- <li>• Recursive scanning</li>
135
- <li>• Homebrew distribution</li>
178
+ <li>• Ruby, Rails, Python, Node.js, Rust, Go</li>
179
+ <li>• PostgreSQL, MySQL, Redis, MongoDB</li>
180
+ <li>• Ecosystem-grouped dashboard</li>
181
+ <li>• Export to JSON/CSV</li>
182
+ <li>• Bulk scanning & rescan</li>
136
183
  </ul>
137
184
  </div>
138
185
  <div>
139
- <h3 class="text-xl font-semibold mb-4 text-blue-600">📋 V0.3.0 (Planned)</h3>
186
+ <h3 class="text-xl font-semibold mb-4 text-blue-600">📋 V1.1 (Next)</h3>
140
187
  <ul class="space-y-2 text-gray-600">
141
- <li>• PostgreSQL detection</li>
142
- <li>• MySQL detection</li>
143
- <li>• Rescan command</li>
144
- <li>• Export to JSON/CSV</li>
188
+ <li>• TypeScript detection</li>
189
+ <li>• Framework detection (Django, Flask, Express, Gin)</li>
190
+ <li>• Package manager versions</li>
191
+ <li>• Vulnerability scanning integration</li>
145
192
  </ul>
146
193
  </div>
147
194
  <div>
148
- <h3 class="text-xl font-semibold mb-4 text-purple-600">🚀 V1.0 (Future)</h3>
195
+ <h3 class="text-xl font-semibold mb-4 text-purple-600">🚀 V2.0 (Vision)</h3>
149
196
  <ul class="space-y-2 text-gray-600">
150
- <li>• Python support</li>
151
- <li>• Node.js support</li>
152
- <li>• Go support</li>
153
- <li>• Rust support</li>
197
+ <li>• AI-powered upgrade summaries</li>
198
+ <li>• Email/Slack notifications</li>
199
+ <li>• Cloud platform detection</li>
200
+ <li>• Team collaboration</li>
154
201
  </ul>
155
202
  </div>
156
203
  </div>
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "English"
3
4
  require "yaml"
4
5
 
5
6
  module Harbinger
@@ -17,7 +18,11 @@ module Harbinger
17
18
  def detect
18
19
  return nil unless database_detected?
19
20
 
20
- # Try shell command first (actual database version)
21
+ # Try docker-compose.yml first (most accurate for Docker-based projects)
22
+ version = detect_from_docker_compose
23
+ return version if version
24
+
25
+ # Try shell command (actual database version for non-Docker setups)
21
26
  version = detect_from_shell
22
27
  return version if version
23
28
 
@@ -68,6 +73,11 @@ module Harbinger
68
73
  raise NotImplementedError, "Subclasses must implement detect_from_shell"
69
74
  end
70
75
 
76
+ # Optional method - subclasses can override to detect from docker-compose.yml
77
+ def detect_from_docker_compose
78
+ nil
79
+ end
80
+
71
81
  # Abstract method - must be implemented by subclasses
72
82
  # Detects version from Gemfile.lock gem version
73
83
  def detect_from_gemfile_lock
@@ -81,7 +91,7 @@ module Harbinger
81
91
 
82
92
  content = File.read(database_yml_path)
83
93
  YAML.safe_load(content, aliases: true)
84
- rescue Psych::SyntaxError, StandardError
94
+ rescue StandardError
85
95
  nil
86
96
  end
87
97
 
@@ -112,7 +122,7 @@ module Harbinger
112
122
  # Execute shell command safely
113
123
  def execute_command(command)
114
124
  output = `#{command} 2>&1`.strip
115
- return nil unless $?.success?
125
+ return nil unless $CHILD_STATUS.success?
116
126
 
117
127
  output
118
128
  rescue StandardError
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Harbinger
6
+ module Analyzers
7
+ # Detects versions from docker-compose.yml and Dockerfile
8
+ class DockerComposeDetector
9
+ attr_reader :project_path
10
+
11
+ def initialize(project_path)
12
+ @project_path = project_path
13
+ end
14
+
15
+ # Extract version from a Docker image in docker-compose.yml
16
+ # e.g., "postgres:16-alpine" => "16", "mysql:8.0" => "8.0"
17
+ def image_version(image_pattern)
18
+ compose = parse_docker_compose
19
+ return nil unless compose
20
+
21
+ services = compose["services"]
22
+ return nil unless services
23
+
24
+ services.each_value do |service|
25
+ image = service["image"]
26
+ next unless image
27
+
28
+ if image.match?(/^#{image_pattern}[:\d]/)
29
+ version = extract_version_from_image(image)
30
+ return version if version
31
+ end
32
+ end
33
+
34
+ nil
35
+ end
36
+
37
+ # Extract Ruby version from Dockerfile
38
+ # e.g., "FROM ruby:3.4.7-slim" => "3.4.7"
39
+ def ruby_version_from_dockerfile
40
+ dockerfile = read_dockerfile
41
+ return nil unless dockerfile
42
+
43
+ # Match patterns like:
44
+ # FROM ruby:3.4.7
45
+ # FROM ruby:3.4.7-slim
46
+ # FROM ruby:3.4.7-alpine
47
+ match = dockerfile.match(/^FROM\s+ruby:(\d+\.\d+(?:\.\d+)?)/i)
48
+ match[1] if match
49
+ end
50
+
51
+ # Extract Rails version from Dockerfile (rare, but possible)
52
+ def rails_version_from_dockerfile
53
+ dockerfile = read_dockerfile
54
+ return nil unless dockerfile
55
+
56
+ # Match patterns like:
57
+ # FROM rails:7.0
58
+ # ARG RAILS_VERSION=7.0.8
59
+ match = dockerfile.match(/^FROM\s+rails:(\d+\.\d+(?:\.\d+)?)/i)
60
+ return match[1] if match
61
+
62
+ match = dockerfile.match(/RAILS_VERSION[=:](\d+\.\d+(?:\.\d+)?)/i)
63
+ match[1] if match
64
+ end
65
+
66
+ # Check if docker-compose.yml exists
67
+ def docker_compose_exists?
68
+ docker_compose_path != nil
69
+ end
70
+
71
+ # Check if Dockerfile exists
72
+ def dockerfile_exists?
73
+ File.exist?(File.join(project_path, "Dockerfile"))
74
+ end
75
+
76
+ private
77
+
78
+ def docker_compose_path
79
+ paths = [
80
+ File.join(project_path, "docker-compose.yml"),
81
+ File.join(project_path, "docker-compose.yaml"),
82
+ File.join(project_path, "compose.yml"),
83
+ File.join(project_path, "compose.yaml")
84
+ ]
85
+ paths.find { |p| File.exist?(p) }
86
+ end
87
+
88
+ def parse_docker_compose
89
+ path = docker_compose_path
90
+ return nil unless path
91
+
92
+ YAML.safe_load(File.read(path), aliases: true)
93
+ rescue StandardError
94
+ nil
95
+ end
96
+
97
+ def read_dockerfile
98
+ path = File.join(project_path, "Dockerfile")
99
+ return nil unless File.exist?(path)
100
+
101
+ File.read(path)
102
+ rescue StandardError
103
+ nil
104
+ end
105
+
106
+ # Extract version number from Docker image tag
107
+ # "postgres:16-alpine" => "16"
108
+ # "postgres:16.2" => "16.2"
109
+ # "mysql:8.0.33" => "8.0.33"
110
+ # "redis:7-alpine" => "7"
111
+ def extract_version_from_image(image)
112
+ return nil unless image.include?(":")
113
+
114
+ tag = image.split(":").last
115
+ # Extract leading version number (digits and dots)
116
+ match = tag.match(/^(\d+(?:\.\d+)*)/)
117
+ match[1] if match
118
+ end
119
+ end
120
+ end
121
+ end