stackharbinger 0.4.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 +4 -4
- data/README.md +110 -34
- data/docs/index.html +64 -21
- data/lib/harbinger/analyzers/go_detector.rb +86 -0
- data/lib/harbinger/analyzers/node_detector.rb +109 -0
- data/lib/harbinger/analyzers/python_detector.rb +110 -0
- data/lib/harbinger/analyzers/rails_analyzer.rb +5 -1
- data/lib/harbinger/analyzers/rust_detector.rb +116 -0
- data/lib/harbinger/cli.rb +365 -252
- data/lib/harbinger/exporters/base_exporter.rb +5 -2
- data/lib/harbinger/version.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d46315e9aab151406a3993d75a034eaf28f16d1ce92f44b6344438ba8b9c482
|
|
4
|
+
data.tar.gz: b4d3c35c6578600ed85d1a387b1cfe13ce5873c256dce6d20267c0366e329052
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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`,
|
|
10
|
-
- 🐘 **Database detection** for PostgreSQL
|
|
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
|
|
@@ -116,18 +118,42 @@ harbinger show myproject --format json
|
|
|
116
118
|
**Example output:**
|
|
117
119
|
|
|
118
120
|
```
|
|
119
|
-
Tracked Projects (
|
|
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)
|
|
120
146
|
================================================================================
|
|
121
|
-
|
|
122
|
-
│ Project
|
|
123
|
-
|
|
124
|
-
│
|
|
125
|
-
│
|
|
126
|
-
|
|
127
|
-
│ job_tracker │ 3.3.0 │ 8.0.4 │ 16.11 │ - │ ✓ Current │
|
|
128
|
-
└───────────────────┴───────┴──────────┴────────────┴───────┴─────────────┘
|
|
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
|
+
└───────────────┴─────────┴────────────┴──────────────────────┘
|
|
129
153
|
```
|
|
130
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
|
+
|
|
131
157
|
### Re-scan all tracked projects
|
|
132
158
|
|
|
133
159
|
```bash
|
|
@@ -163,8 +189,14 @@ harbinger version
|
|
|
163
189
|
1. **Detection**: Harbinger looks for version info in your project:
|
|
164
190
|
- Ruby: `.ruby-version`, `Gemfile` (`ruby "x.x.x"`), `Gemfile.lock` (RUBY VERSION)
|
|
165
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`
|
|
166
196
|
- PostgreSQL: `config/database.yml` (adapter check) + `psql --version` or `pg` gem
|
|
167
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
|
|
168
200
|
|
|
169
201
|
2. **EOL Data**: Fetches official EOL dates from [endoflife.date](https://endoflife.date) API
|
|
170
202
|
|
|
@@ -205,6 +237,53 @@ Parses `Gemfile.lock` for the rails gem version.
|
|
|
205
237
|
|
|
206
238
|
**Supported adapters**: `mysql2` (traditional) and `trilogy` (Rails 7.1+)
|
|
207
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
|
+
|
|
208
287
|
## Requirements
|
|
209
288
|
|
|
210
289
|
- Ruby >= 3.1.0
|
|
@@ -229,30 +308,27 @@ bundle exec exe/harbinger scan .
|
|
|
229
308
|
|
|
230
309
|
## Roadmap
|
|
231
310
|
|
|
232
|
-
###
|
|
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
|
|
233
315
|
- ✅ Export reports to JSON/CSV
|
|
234
|
-
- ✅
|
|
235
|
-
- ✅
|
|
236
|
-
- ✅
|
|
237
|
-
|
|
238
|
-
###
|
|
239
|
-
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
242
|
-
-
|
|
243
|
-
- ✅ EOL tracking for PostgreSQL and MySQL
|
|
244
|
-
|
|
245
|
-
### V1.0 - Future
|
|
246
|
-
- 🐍 Python support (pyproject.toml, requirements.txt)
|
|
247
|
-
- 📦 Node.js support (package.json, .nvmrc)
|
|
248
|
-
- 🦀 Rust support (Cargo.toml)
|
|
249
|
-
- 🐘 Go support (go.mod)
|
|
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
|
|
250
325
|
|
|
251
326
|
### V2.0 - Vision
|
|
252
|
-
- 🤖 AI-powered upgrade summaries
|
|
253
|
-
- 📧 Email/Slack notifications
|
|
254
|
-
- ☁️ Cloud platform detection (AWS, Heroku,
|
|
255
|
-
- 👥 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
|
|
256
332
|
|
|
257
333
|
## Contributing
|
|
258
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
|
|
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, Rails, PostgreSQL, and
|
|
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">Detects Ruby, Rails, PostgreSQL, and
|
|
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">
|
|
66
|
-
<p class="text-gray-600">
|
|
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">
|
|
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,7 +109,49 @@
|
|
|
109
109
|
</div>
|
|
110
110
|
|
|
111
111
|
<div class="prose max-w-none">
|
|
112
|
-
<h3 class="text-2xl font-semibold mb-4">
|
|
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>
|
|
@@ -131,30 +173,31 @@
|
|
|
131
173
|
<h2 class="text-3xl font-bold mb-8 text-center">Roadmap</h2>
|
|
132
174
|
<div class="grid md:grid-cols-3 gap-8">
|
|
133
175
|
<div>
|
|
134
|
-
<h3 class="text-xl font-semibold mb-4 text-green-600">✅
|
|
176
|
+
<h3 class="text-xl font-semibold mb-4 text-green-600">✅ V1.0 (Current)</h3>
|
|
135
177
|
<ul class="space-y-2 text-gray-600">
|
|
178
|
+
<li>• Ruby, Rails, Python, Node.js, Rust, Go</li>
|
|
179
|
+
<li>• PostgreSQL, MySQL, Redis, MongoDB</li>
|
|
180
|
+
<li>• Ecosystem-grouped dashboard</li>
|
|
136
181
|
<li>• Export to JSON/CSV</li>
|
|
137
|
-
<li>•
|
|
138
|
-
<li>• Redis detection</li>
|
|
139
|
-
<li>• MongoDB detection</li>
|
|
182
|
+
<li>• Bulk scanning & rescan</li>
|
|
140
183
|
</ul>
|
|
141
184
|
</div>
|
|
142
185
|
<div>
|
|
143
|
-
<h3 class="text-xl font-semibold mb-4 text-blue-600">📋
|
|
186
|
+
<h3 class="text-xl font-semibold mb-4 text-blue-600">📋 V1.1 (Next)</h3>
|
|
144
187
|
<ul class="space-y-2 text-gray-600">
|
|
145
|
-
<li>•
|
|
146
|
-
<li>•
|
|
147
|
-
<li>•
|
|
148
|
-
<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>
|
|
149
192
|
</ul>
|
|
150
193
|
</div>
|
|
151
194
|
<div>
|
|
152
|
-
<h3 class="text-xl font-semibold mb-4 text-purple-600">🚀
|
|
195
|
+
<h3 class="text-xl font-semibold mb-4 text-purple-600">🚀 V2.0 (Vision)</h3>
|
|
153
196
|
<ul class="space-y-2 text-gray-600">
|
|
154
|
-
<li>•
|
|
155
|
-
<li>•
|
|
156
|
-
<li>•
|
|
157
|
-
<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>
|
|
158
201
|
</ul>
|
|
159
202
|
</div>
|
|
160
203
|
</div>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Harbinger
|
|
4
|
+
module Analyzers
|
|
5
|
+
class GoDetector
|
|
6
|
+
def initialize(project_path)
|
|
7
|
+
@project_path = project_path
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def detect
|
|
11
|
+
version = detect_from_go_mod ||
|
|
12
|
+
detect_from_go_work ||
|
|
13
|
+
detect_from_go_version_file ||
|
|
14
|
+
detect_from_docker_compose ||
|
|
15
|
+
detect_from_shell
|
|
16
|
+
|
|
17
|
+
normalize_version(version) if version
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def go_detected?
|
|
21
|
+
File.exist?(File.join(@project_path, "go.mod")) ||
|
|
22
|
+
File.exist?(File.join(@project_path, "go.work")) ||
|
|
23
|
+
File.exist?(File.join(@project_path, ".go-version"))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def detect_from_go_mod
|
|
29
|
+
go_mod_path = File.join(@project_path, "go.mod")
|
|
30
|
+
return nil unless File.exist?(go_mod_path)
|
|
31
|
+
|
|
32
|
+
content = File.read(go_mod_path)
|
|
33
|
+
# Match "go 1.21" or "go 1.21.0" format
|
|
34
|
+
match = content.match(/^go\s+([\d.]+)/m)
|
|
35
|
+
match[1] if match
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def detect_from_go_work
|
|
39
|
+
go_work_path = File.join(@project_path, "go.work")
|
|
40
|
+
return nil unless File.exist?(go_work_path)
|
|
41
|
+
|
|
42
|
+
content = File.read(go_work_path)
|
|
43
|
+
# Match "go 1.21" or "go 1.21.0" format
|
|
44
|
+
match = content.match(/^go\s+([\d.]+)/m)
|
|
45
|
+
match[1] if match
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def detect_from_go_version_file
|
|
49
|
+
go_version_path = File.join(@project_path, ".go-version")
|
|
50
|
+
return nil unless File.exist?(go_version_path)
|
|
51
|
+
|
|
52
|
+
version = File.read(go_version_path).strip
|
|
53
|
+
version unless version.empty?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def detect_from_docker_compose
|
|
57
|
+
docker_compose_path = File.join(@project_path, "docker-compose.yml")
|
|
58
|
+
return nil unless File.exist?(docker_compose_path)
|
|
59
|
+
|
|
60
|
+
content = File.read(docker_compose_path)
|
|
61
|
+
# Match golang:1.21, golang:1.21.0, golang:1.21-alpine, etc.
|
|
62
|
+
match = content.match(/golang:([\d.]+)/)
|
|
63
|
+
match[1] if match
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def detect_from_shell
|
|
67
|
+
# Only try shell detection if Go project files exist
|
|
68
|
+
return nil unless go_detected?
|
|
69
|
+
|
|
70
|
+
version_output = `go version 2>/dev/null`.strip
|
|
71
|
+
return nil if version_output.empty?
|
|
72
|
+
|
|
73
|
+
# Parse "go version go1.21.0 darwin/amd64" format
|
|
74
|
+
match = version_output.match(/go version go([\d.]+)/)
|
|
75
|
+
match[1] if match
|
|
76
|
+
rescue StandardError
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def normalize_version(version)
|
|
81
|
+
# Remove any trailing qualifiers like -alpine, -rc1, etc.
|
|
82
|
+
version.gsub(/-(alpine|rc\d+|beta\d*).*/, "")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "docker_compose_detector"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module Harbinger
|
|
7
|
+
module Analyzers
|
|
8
|
+
class NodeDetector
|
|
9
|
+
def initialize(project_path)
|
|
10
|
+
@project_path = project_path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def detect
|
|
14
|
+
detect_from_version_files ||
|
|
15
|
+
detect_from_package_json ||
|
|
16
|
+
detect_from_docker_compose ||
|
|
17
|
+
(nodejs_detected? ? detect_from_shell : nil)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def nodejs_detected?
|
|
21
|
+
File.exist?(File.join(project_path, "package.json")) ||
|
|
22
|
+
File.exist?(File.join(project_path, "package-lock.json")) ||
|
|
23
|
+
File.exist?(File.join(project_path, ".nvmrc")) ||
|
|
24
|
+
File.exist?(File.join(project_path, ".node-version")) ||
|
|
25
|
+
File.exist?(File.join(project_path, "node_modules"))
|
|
26
|
+
rescue StandardError
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
attr_reader :project_path
|
|
33
|
+
|
|
34
|
+
def detect_from_version_files
|
|
35
|
+
[".nvmrc", ".node-version"].each do |filename|
|
|
36
|
+
file_path = File.join(project_path, filename)
|
|
37
|
+
next unless File.exist?(file_path)
|
|
38
|
+
|
|
39
|
+
content = File.read(file_path).strip
|
|
40
|
+
next if content.empty?
|
|
41
|
+
|
|
42
|
+
return extract_version(content)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
nil
|
|
46
|
+
rescue StandardError
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def detect_from_package_json
|
|
51
|
+
file_path = File.join(project_path, "package.json")
|
|
52
|
+
return nil unless File.exist?(file_path)
|
|
53
|
+
|
|
54
|
+
content = File.read(file_path)
|
|
55
|
+
package = JSON.parse(content)
|
|
56
|
+
|
|
57
|
+
engines = package.dig("engines", "node")
|
|
58
|
+
return nil unless engines
|
|
59
|
+
|
|
60
|
+
extract_version(engines)
|
|
61
|
+
rescue StandardError
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def detect_from_docker_compose
|
|
66
|
+
docker = DockerComposeDetector.new(project_path)
|
|
67
|
+
docker.image_version("node")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def detect_from_shell
|
|
71
|
+
output = `node --version 2>&1`.strip
|
|
72
|
+
return nil unless $CHILD_STATUS.success?
|
|
73
|
+
|
|
74
|
+
# Parse: "v18.16.0" (note the 'v' prefix)
|
|
75
|
+
match = output.match(/^v?(\d+\.\d+(?:\.\d+)?)/)
|
|
76
|
+
match[1] if match
|
|
77
|
+
rescue StandardError
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def extract_version(version_string)
|
|
82
|
+
# Remove 'v' prefix (Node.js convention)
|
|
83
|
+
version = version_string.sub(/^v/, "")
|
|
84
|
+
|
|
85
|
+
# Strip constraint operators: >=18.0.0, ^18.0.0, ~18.0.0
|
|
86
|
+
version = version.gsub(/^[><=~^!\s]+/, "")
|
|
87
|
+
|
|
88
|
+
# Handle ranges like ">=14.0.0 <20.0.0" - extract first version
|
|
89
|
+
version = version.split(/\s+/).first if version.include?(" ")
|
|
90
|
+
|
|
91
|
+
# Handle .x suffix (e.g., "18.x" => "18")
|
|
92
|
+
version = version.sub(/\.x$/, "")
|
|
93
|
+
|
|
94
|
+
# Handle LTS names like "lts/hydrogen" (hydrogen=18, gallium=16, fermium=14)
|
|
95
|
+
if version =~ /lts\/(hydrogen|gallium|fermium)/i
|
|
96
|
+
lts_name = $1.downcase
|
|
97
|
+
return case lts_name
|
|
98
|
+
when "hydrogen" then "18"
|
|
99
|
+
when "gallium" then "16"
|
|
100
|
+
when "fermium" then "14"
|
|
101
|
+
else nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
version
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Harbinger
|
|
4
|
+
module Analyzers
|
|
5
|
+
class PythonDetector
|
|
6
|
+
def initialize(project_path)
|
|
7
|
+
@project_path = project_path
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def detect
|
|
11
|
+
detect_from_pyproject_toml ||
|
|
12
|
+
detect_from_python_version ||
|
|
13
|
+
detect_from_pyvenv_cfg ||
|
|
14
|
+
(python_detected? ? detect_from_shell : nil)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def python_detected?
|
|
18
|
+
File.exist?(File.join(project_path, "pyproject.toml")) ||
|
|
19
|
+
File.exist?(File.join(project_path, "requirements.txt")) ||
|
|
20
|
+
File.exist?(File.join(project_path, ".python-version")) ||
|
|
21
|
+
File.exist?(File.join(project_path, "setup.py")) ||
|
|
22
|
+
File.exist?(File.join(project_path, "setup.cfg")) ||
|
|
23
|
+
venv_exists?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
attr_reader :project_path
|
|
29
|
+
|
|
30
|
+
def detect_from_pyproject_toml
|
|
31
|
+
file_path = File.join(project_path, "pyproject.toml")
|
|
32
|
+
return nil unless File.exist?(file_path)
|
|
33
|
+
|
|
34
|
+
content = File.read(file_path)
|
|
35
|
+
|
|
36
|
+
# Try [project] requires-python = ">=3.11"
|
|
37
|
+
match = content.match(/requires-python\s*=\s*["']([^"']+)["']/)
|
|
38
|
+
return extract_version(match[1]) if match
|
|
39
|
+
|
|
40
|
+
# Try [tool.poetry.dependencies] python = "^3.11"
|
|
41
|
+
match = content.match(/\[tool\.poetry\.dependencies\].*?python\s*=\s*["']([^"']+)["']/m)
|
|
42
|
+
return extract_version(match[1]) if match
|
|
43
|
+
|
|
44
|
+
nil
|
|
45
|
+
rescue StandardError
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def detect_from_python_version
|
|
50
|
+
file_path = File.join(project_path, ".python-version")
|
|
51
|
+
return nil unless File.exist?(file_path)
|
|
52
|
+
|
|
53
|
+
content = File.read(file_path).strip
|
|
54
|
+
extract_version(content)
|
|
55
|
+
rescue StandardError
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def detect_from_pyvenv_cfg
|
|
60
|
+
["venv/pyvenv.cfg", ".venv/pyvenv.cfg"].each do |cfg_path|
|
|
61
|
+
file_path = File.join(project_path, cfg_path)
|
|
62
|
+
next unless File.exist?(file_path)
|
|
63
|
+
|
|
64
|
+
content = File.read(file_path)
|
|
65
|
+
# Parse: "version = 3.11.5"
|
|
66
|
+
match = content.match(/version\s*=\s*(\d+\.\d+(?:\.\d+)?)/)
|
|
67
|
+
return match[1] if match
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
nil
|
|
71
|
+
rescue StandardError
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def detect_from_shell
|
|
76
|
+
# Try python3 first (more reliable on multi-Python systems)
|
|
77
|
+
output = `python3 --version 2>&1`.strip
|
|
78
|
+
if $CHILD_STATUS.success?
|
|
79
|
+
match = output.match(/Python\s+(\d+\.\d+(?:\.\d+)?)/)
|
|
80
|
+
return match[1] if match
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Fall back to python
|
|
84
|
+
output = `python --version 2>&1`.strip
|
|
85
|
+
return nil unless $CHILD_STATUS.success?
|
|
86
|
+
|
|
87
|
+
match = output.match(/Python\s+(\d+\.\d+(?:\.\d+)?)/)
|
|
88
|
+
match[1] if match
|
|
89
|
+
rescue StandardError
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def extract_version(version_string)
|
|
94
|
+
# Strip constraint operators: >=3.11, ^3.11, ~3.11, >3.11, <4.0
|
|
95
|
+
version = version_string.gsub(/^[><=~^!\s]+/, "")
|
|
96
|
+
|
|
97
|
+
# Handle ranges like ">=3.9,<4.0" - extract first version
|
|
98
|
+
version = version.split(",").first.strip if version.include?(",")
|
|
99
|
+
|
|
100
|
+
# Remove "python-" prefix if present (from .python-version)
|
|
101
|
+
version.sub(/^python-/, "")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def venv_exists?
|
|
105
|
+
File.exist?(File.join(project_path, "venv/pyvenv.cfg")) ||
|
|
106
|
+
File.exist?(File.join(project_path, ".venv/pyvenv.cfg"))
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|