rails_pulse 0.1.3 → 0.2.2

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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +134 -16
  3. data/Rakefile +315 -83
  4. data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
  5. data/app/assets/stylesheets/rails_pulse/components/datepicker.css +191 -0
  6. data/app/assets/stylesheets/rails_pulse/components/switch.css +36 -0
  7. data/app/assets/stylesheets/rails_pulse/components/tags.css +98 -0
  8. data/app/assets/stylesheets/rails_pulse/components/utilities.css +26 -0
  9. data/app/controllers/concerns/response_range_concern.rb +15 -2
  10. data/app/controllers/concerns/tag_filter_concern.rb +26 -0
  11. data/app/controllers/concerns/time_range_concern.rb +27 -8
  12. data/app/controllers/rails_pulse/application_controller.rb +73 -0
  13. data/app/controllers/rails_pulse/queries_controller.rb +18 -21
  14. data/app/controllers/rails_pulse/requests_controller.rb +80 -35
  15. data/app/controllers/rails_pulse/routes_controller.rb +4 -2
  16. data/app/controllers/rails_pulse/tags_controller.rb +51 -0
  17. data/app/helpers/rails_pulse/application_helper.rb +2 -0
  18. data/app/helpers/rails_pulse/breadcrumbs_helper.rb +1 -1
  19. data/app/helpers/rails_pulse/chart_helper.rb +1 -1
  20. data/app/helpers/rails_pulse/form_helper.rb +75 -0
  21. data/app/helpers/rails_pulse/formatting_helper.rb +21 -2
  22. data/app/helpers/rails_pulse/tags_helper.rb +29 -0
  23. data/app/javascript/rails_pulse/application.js +6 -0
  24. data/app/javascript/rails_pulse/controllers/custom_range_controller.js +115 -0
  25. data/app/javascript/rails_pulse/controllers/datepicker_controller.js +48 -0
  26. data/app/javascript/rails_pulse/controllers/global_filters_controller.js +110 -0
  27. data/app/javascript/rails_pulse/controllers/index_controller.js +11 -3
  28. data/app/models/concerns/taggable.rb +61 -0
  29. data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +1 -1
  30. data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +1 -1
  31. data/app/models/rails_pulse/queries/cards/average_query_times.rb +1 -1
  32. data/app/models/rails_pulse/queries/cards/execution_rate.rb +56 -17
  33. data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +1 -1
  34. data/app/models/rails_pulse/queries/charts/average_query_times.rb +3 -7
  35. data/app/models/rails_pulse/queries/tables/index.rb +10 -2
  36. data/app/models/rails_pulse/query.rb +2 -0
  37. data/app/models/rails_pulse/request.rb +10 -2
  38. data/app/models/rails_pulse/requests/charts/average_response_times.rb +2 -2
  39. data/app/models/rails_pulse/requests/tables/index.rb +77 -0
  40. data/app/models/rails_pulse/route.rb +2 -0
  41. data/app/models/rails_pulse/routes/cards/average_response_times.rb +1 -1
  42. data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +1 -1
  43. data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +1 -1
  44. data/app/models/rails_pulse/routes/cards/request_count_totals.rb +16 -5
  45. data/app/models/rails_pulse/routes/tables/index.rb +14 -4
  46. data/app/models/rails_pulse/summary.rb +7 -7
  47. data/app/services/rails_pulse/analysis/query_characteristics_analyzer.rb +11 -3
  48. data/app/services/rails_pulse/summary_service.rb +2 -0
  49. data/app/views/layouts/rails_pulse/_global_filters.html.erb +84 -0
  50. data/app/views/layouts/rails_pulse/_menu_items.html.erb +5 -5
  51. data/app/views/layouts/rails_pulse/application.html.erb +8 -5
  52. data/app/views/rails_pulse/components/_metric_card.html.erb +2 -2
  53. data/app/views/rails_pulse/components/_operation_details_popover.html.erb +1 -1
  54. data/app/views/rails_pulse/components/_page_header.html.erb +20 -0
  55. data/app/views/rails_pulse/components/_sparkline_stats.html.erb +1 -1
  56. data/app/views/rails_pulse/dashboard/index.html.erb +1 -1
  57. data/app/views/rails_pulse/operations/show.html.erb +1 -1
  58. data/app/views/rails_pulse/queries/_analysis_results.html.erb +53 -23
  59. data/app/views/rails_pulse/queries/_show_table.html.erb +33 -5
  60. data/app/views/rails_pulse/queries/_table.html.erb +4 -6
  61. data/app/views/rails_pulse/queries/index.html.erb +3 -7
  62. data/app/views/rails_pulse/queries/show.html.erb +3 -7
  63. data/app/views/rails_pulse/requests/_table.html.erb +32 -19
  64. data/app/views/rails_pulse/requests/index.html.erb +45 -55
  65. data/app/views/rails_pulse/requests/show.html.erb +1 -3
  66. data/app/views/rails_pulse/routes/_requests_table.html.erb +41 -0
  67. data/app/views/rails_pulse/routes/_table.html.erb +4 -8
  68. data/app/views/rails_pulse/routes/index.html.erb +4 -8
  69. data/app/views/rails_pulse/routes/show.html.erb +6 -12
  70. data/app/views/rails_pulse/tags/_tag_manager.html.erb +73 -0
  71. data/config/initializers/rails_charts_csp_patch.rb +32 -40
  72. data/config/routes.rb +5 -0
  73. data/db/migrate/20250930105043_install_rails_pulse_tables.rb +23 -0
  74. data/db/rails_pulse_schema.rb +4 -1
  75. data/lib/generators/rails_pulse/convert_to_migrations_generator.rb +25 -9
  76. data/lib/generators/rails_pulse/install_generator.rb +30 -7
  77. data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +75 -2
  78. data/lib/generators/rails_pulse/templates/migrations/install_rails_pulse_tables.rb +3 -2
  79. data/lib/generators/rails_pulse/templates/rails_pulse.rb +21 -0
  80. data/lib/generators/rails_pulse/upgrade_generator.rb +147 -30
  81. data/lib/rails_pulse/configuration.rb +16 -1
  82. data/lib/rails_pulse/engine.rb +21 -0
  83. data/lib/rails_pulse/version.rb +1 -1
  84. data/public/rails-pulse-assets/rails-pulse-icons.js +16 -15
  85. data/public/rails-pulse-assets/rails-pulse-icons.js.map +1 -1
  86. data/public/rails-pulse-assets/rails-pulse.css +1 -1
  87. data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
  88. data/public/rails-pulse-assets/rails-pulse.js +73 -69
  89. data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
  90. metadata +20 -5
  91. data/app/views/rails_pulse/components/_breadcrumbs.html.erb +0 -12
  92. data/db/migrate/20241222000001_create_rails_pulse_summaries.rb +0 -54
  93. data/db/migrate/20250916031656_add_analysis_to_rails_pulse_queries.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11728d425611e7ab3a9edb171f05638a783d1c8d3ded8dff112bb615e8a18d11
4
- data.tar.gz: 3e16c2d59f4f4bd8d2df49625d9f6005edc98dec701212a6052f4abfd1cac6f3
3
+ metadata.gz: a8ed469f307dd71e912b46a4da0a0daa60abbdc262a8dbd12604db00a66335fe
4
+ data.tar.gz: 8af5a6bf946a86e1bc277c1d719100ace739bb429c07969fd1e4ec31887d6f70
5
5
  SHA512:
6
- metadata.gz: 443645ed917ef9beea2d642ff8c4a7d2e57cc87559a09e74dfd75d20d18fb0ff11040eac6e825f3ff0af35637ea23757c88b24e78f35e32f64a7660168aa940a
7
- data.tar.gz: d11227a7a11c24bff167104e712f9d075a394130a196938ed71b360e6cbc82a8f930672187b92d0009ed5c14fee882ff23d3d88a3df25b1ddeff3a8839452b5b
6
+ metadata.gz: eceb3af7e713b6049688e50a8fd79af7b2f09c7720158c8d15257235b0c7488ebe7c0eccfaab3cd5dfb5923f80e9a8bd8693d0a9fb2f4e021aca8846c4ac435c
7
+ data.tar.gz: f982a50256d99968eb68fee89e6380d3f75511e6c3e8e878ddca375919f6a68a201f9ce46c6868e2425c127ae1862ca0d1895b0e01afea89a9af6a4e85d7f0f9
data/README.md CHANGED
@@ -25,6 +25,10 @@
25
25
  - [Authentication](#authentication)
26
26
  - [Authentication Setup](#authentication-setup)
27
27
  - [Authentication Examples](#authentication-examples)
28
+ - [Tagging System](#tagging-system)
29
+ - [Configuring Tags](#configuring-tags)
30
+ - [Using Tags](#using-tags)
31
+ - [Filtering by Tags](#filtering-by-tags)
28
32
  - [Data Management](#data-management)
29
33
  - [Cleanup Strategies](#cleanup-strategies)
30
34
  - [Cleanup Configuration](#cleanup-configuration)
@@ -58,6 +62,11 @@ Rails Pulse is a comprehensive performance monitoring and debugging gem that pro
58
62
  - Smart caching with minimal performance overhead
59
63
  - Multiple database support (SQLite, PostgreSQL, MySQL)
60
64
 
65
+ ### Organization & Filtering
66
+ - Flexible tagging system for routes, requests, and queries
67
+ - Filter performance data by custom tags
68
+ - Organize monitoring data by environment, priority, or custom categories
69
+
61
70
  ## Screenshots
62
71
 
63
72
  <table>
@@ -87,15 +96,22 @@ bundle install
87
96
  Generate the installation files:
88
97
 
89
98
  ```bash
99
+ # Install with single database setup (default - recommended)
90
100
  rails generate rails_pulse:install
91
- ```
92
101
 
93
- Load the database schema:
102
+ # Or install with separate database setup
103
+ rails generate rails_pulse:install --database=separate
104
+ ```
94
105
 
106
+ **For single database setup (default):**
95
107
  ```bash
96
- rails db:prepare
108
+ rails db:migrate # Creates Rails Pulse tables in your main database
97
109
  ```
98
110
 
111
+ **For separate database setup:**
112
+ 1. Configure `config/database.yml` with your Rails Pulse database settings
113
+ 2. Run: `rails db:prepare` to create and load the schema
114
+
99
115
  Add the Rails Pulse route to your application:
100
116
 
101
117
  ```ruby
@@ -122,6 +138,10 @@ Rails Pulse automatically starts collecting performance data once installed. Acc
122
138
  http://localhost:3000/rails_pulse
123
139
  ```
124
140
 
141
+ **Database Setup:**
142
+ - **Single Database (default)**: Rails Pulse tables are created in your main database - no additional configuration needed
143
+ - **Separate Database**: See the [Separate Database Support](#separate-database-support) section for setup instructions
144
+
125
145
  ### Basic Configuration
126
146
 
127
147
  Customize Rails Pulse in `config/initializers/rails_pulse.rb`:
@@ -165,6 +185,9 @@ RailsPulse.configure do |config|
165
185
  config.ignored_requests = [] # Array of request patterns to ignore
166
186
  config.ignored_queries = [] # Array of query patterns to ignore
167
187
 
188
+ # Tagging system - define available tags for categorizing performance data
189
+ config.tags = ["production", "staging", "critical", "needs-optimization"]
190
+
168
191
  # Data cleanup
169
192
  config.archiving_enabled = true # Enable automatic cleanup
170
193
  config.full_retention_period = 2.weeks # Delete records older than this
@@ -232,6 +255,72 @@ config.authentication_method = proc {
232
255
  }
233
256
  ```
234
257
 
258
+ ## Tagging System
259
+
260
+ Rails Pulse includes a flexible tagging system that allows you to categorize and organize your performance data. Tag routes, requests, and queries with custom labels to better organize and filter your monitoring data.
261
+
262
+ ### Configuring Tags
263
+
264
+ Define available tags in your Rails Pulse initializer:
265
+
266
+ ```ruby
267
+ RailsPulse.configure do |config|
268
+ config.tags = [
269
+ "production",
270
+ "staging",
271
+ "critical",
272
+ "needs-optimization",
273
+ "high-traffic",
274
+ "background-job"
275
+ ]
276
+ end
277
+ ```
278
+
279
+ ### Using Tags
280
+
281
+ **Tag from the UI:**
282
+
283
+ 1. Navigate to any route, request, or query detail page
284
+ 2. Click the "+ tag" button next to the record
285
+ 3. Select from your configured tags
286
+ 4. Remove tags by clicking the Ɨ button on any tag badge
287
+
288
+ **Tag Programmatically:**
289
+
290
+ ```ruby
291
+ # Tag a route
292
+ route = RailsPulse::Route.find_by(path: "/api/users")
293
+ route.add_tag("critical")
294
+ route.add_tag("high-traffic")
295
+
296
+ # Tag a query
297
+ query = RailsPulse::Query.find_by(normalized_sql: "SELECT * FROM users WHERE id = ?")
298
+ query.add_tag("needs-optimization")
299
+
300
+ # Remove a tag
301
+ route.remove_tag("critical")
302
+
303
+ # Check if has tag
304
+ route.has_tag?("production") # => true
305
+ ```
306
+
307
+ ### Filtering by Tags
308
+
309
+ Use the global filters modal to filter performance data by tags:
310
+
311
+ 1. Click the filter icon in the top navigation
312
+ 2. Select one or more tags from the tag selector
313
+ 3. Apply filters to see only records with those tags
314
+ 4. Tags appear as badges in all data tables for quick visual identification
315
+
316
+ **Common Tagging Strategies:**
317
+
318
+ - **By Environment**: `production`, `staging`, `development`
319
+ - **By Priority**: `critical`, `high`, `medium`, `low`
320
+ - **By Status**: `needs-optimization`, `investigating`, `resolved`
321
+ - **By Type**: `api`, `background-job`, `user-facing`, `admin`
322
+ - **By Team**: `team-frontend`, `team-backend`, `team-data`
323
+
235
324
  ## Data Management
236
325
 
237
326
  Rails Pulse provides data cleanup to prevent your monitoring database from growing indefinitely while preserving essential performance insights.
@@ -287,20 +376,36 @@ RailsPulse::CleanupJob.perform_later
287
376
 
288
377
  ## Separate Database Support
289
378
 
290
- Rails Pulse supports storing performance monitoring data in a **separate database**. By default, Rails Pulse stores data in your main application database alongside your existing tables.
379
+ Rails Pulse offers two database setup options to fit your application's needs:
380
+
381
+ ### Option 1: Single Database (Default - Recommended)
382
+
383
+ Stores Rails Pulse data in your main application database alongside your existing tables. This is the simplest setup and works great for most applications.
384
+
385
+ **Advantages:**
386
+ - Zero additional configuration required
387
+ - Simpler backup and deployment strategies
388
+ - Works with any database (SQLite, PostgreSQL, MySQL)
389
+
390
+ **Installation:**
391
+ ```bash
392
+ rails generate rails_pulse:install
393
+ rails db:migrate
394
+ ```
395
+
396
+ ### Option 2: Separate Database
291
397
 
292
- Use a separate database when you want:
398
+ Stores Rails Pulse data in a dedicated database, completely isolated from your main application.
293
399
 
400
+ **Use a separate database when you want:**
294
401
  - **Isolating monitoring data** from your main application database
295
402
  - **Using different database engines** optimized for time-series data
296
403
  - **Scaling monitoring independently** from your application
297
404
  - **Simplified backup strategies** with separate retention policies
298
405
 
299
- **For shared database setup (default)**, no database configuration is needed - simply run `rails db:prepare` after installation.
300
-
301
406
  ### Configuration
302
407
 
303
- To use a separate database, configure the `connects_to` option in your Rails Pulse initializer:
408
+ To use a separate database, install with the `--database=separate` flag, then configure the `connects_to` option in your Rails Pulse initializer:
304
409
 
305
410
  ```ruby
306
411
  RailsPulse.configure do |config|
@@ -356,17 +461,30 @@ production:
356
461
  pool: 5
357
462
  ```
358
463
 
359
- ### Schema Loading
464
+ ### Installation Steps
360
465
 
361
- After installation, load the Rails Pulse database schema:
466
+ **For separate database setup:**
362
467
 
363
- ```bash
364
- rails db:prepare
365
- ```
468
+ 1. **Generate installation files:**
469
+ ```bash
470
+ rails generate rails_pulse:install --database=separate
471
+ ```
472
+
473
+ 2. **Configure `config/database.yml`** (see examples above)
474
+
475
+ 3. **Create and load the schema:**
476
+ ```bash
477
+ rails db:prepare
478
+ ```
479
+ This automatically creates the database and loads the Rails Pulse schema.
480
+
481
+ **Schema Management:**
366
482
 
367
- This command works for both:
368
- - Shared database setup (default): Loads tables into your main application database
369
- - Separate database setup: Automatically loads tables into your configured Rails Pulse database
483
+ The schema file `db/rails_pulse_schema.rb` serves as your single source of truth for the database structure. It:
484
+ - Defines all Rails Pulse tables in one place
485
+ - Is loaded by the installation migration
486
+ - Should not be deleted or modified
487
+ - Future updates will provide migrations in `db/rails_pulse_migrate/`
370
488
 
371
489
  ## Testing
372
490
 
data/Rakefile CHANGED
@@ -1,109 +1,341 @@
1
1
  require "bundler/setup"
2
+ require "bundler/gem_tasks"
2
3
 
3
4
  # Load environment variables from .env file
4
5
  require "dotenv/load" if File.exist?(".env")
5
6
 
6
7
  APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
7
8
  load "rails/tasks/engine.rake"
8
- load "rails/tasks/statistics.rake"
9
9
 
10
- require "bundler/gem_tasks"
10
+ desc "Setup database for testing"
11
+ task :test_setup do
12
+ database = ENV['DB'] || 'sqlite3'
11
13
 
12
- # Test tasks
13
- namespace :test do
14
- desc "Run unit tests (models, helpers, services, instrumentation)"
15
- task :unit do
16
- sh "rails test test/models test/helpers test/services test/support test/instrumentation"
17
- end
14
+ puts "\n" + "=" * 50
15
+ puts "šŸ› ļø Rails Pulse Test Setup"
16
+ puts "=" * 50
17
+ puts "Database: #{database.upcase}"
18
+ puts "=" * 50
19
+ puts
20
+
21
+ begin
22
+ # Remove schema.rb to ensure clean migration
23
+ schema_file = "test/dummy/db/schema.rb"
24
+ if File.exist?(schema_file)
25
+ puts "🧹 Removing existing schema.rb file..."
26
+ File.delete(schema_file)
27
+ end
18
28
 
19
- desc "Run functional tests (controllers)"
20
- task :functional do
21
- sh "rails test test/controllers"
29
+ case database.downcase
30
+ when 'sqlite3', 'sqlite'
31
+ puts "šŸ“¦ Setting up SQLite database..."
32
+ sh "RAILS_ENV=test bin/rails db:drop db:create db:migrate"
33
+
34
+ when 'mysql2', 'mysql'
35
+ puts "🐬 Setting up MySQL database..."
36
+ sh "DB=mysql2 RAILS_ENV=test rails db:drop db:create db:migrate"
37
+
38
+ when 'postgresql', 'postgres'
39
+ puts "🐘 Setting up PostgreSQL database..."
40
+ sh "DB=postgresql RAILS_ENV=test rails db:drop db:create db:migrate"
41
+
42
+ else
43
+ puts "āš ļø Unknown database: #{database}"
44
+ puts "Supported databases: sqlite3, mysql2, postgresql"
45
+ exit 1
46
+ end
47
+
48
+ puts "\nāœ… Database setup complete!"
49
+ puts "Ready to run: rake test"
50
+
51
+ rescue => e
52
+ puts "\nāŒ Database setup failed!"
53
+ puts "Error: #{e.message}"
54
+ puts "\nTroubleshooting:"
55
+ puts "• Ensure #{database} is installed and running"
56
+ puts "• Check database credentials in test/dummy/config/database.yml"
57
+ puts "• Verify RAILS_ENV=test environment is configured"
58
+ exit 1
22
59
  end
60
+ end
61
+
62
+ desc "Run test suite"
63
+ task :test do
64
+ database = ENV['DB'] || 'sqlite3'
23
65
 
24
- desc "Run integration tests"
25
- task :integration do
26
- sh "rails test test/integration test/system"
66
+ # Get Rails version from Gemfile.lock or fallback
67
+ rails_version = begin
68
+ require 'rails'
69
+ Rails.version
70
+ rescue LoadError
71
+ # Try to get from Gemfile.lock
72
+ gemfile_lock = File.read('Gemfile.lock') rescue nil
73
+ if gemfile_lock && gemfile_lock.match(/rails \(([^)]+)\)/)
74
+ $1
75
+ else
76
+ 'unknown'
77
+ end
27
78
  end
28
79
 
29
- desc "Run all tests"
30
- task :all do
31
- sh "rails test"
80
+ puts "\n" + "=" * 50
81
+ puts "šŸ’› Rails Pulse Test Suite"
82
+ puts "=" * 50
83
+ puts "Database: #{database.upcase}"
84
+ puts "Rails: #{rails_version}"
85
+ puts "=" * 50
86
+ puts
87
+
88
+ sh "rails test test/controllers test/helpers test/instrumentation test/jobs test/models test/services"
89
+ end
90
+
91
+ desc "Setup database for specific Rails version and database"
92
+ task :test_setup_for_version, [ :database, :rails_version ] do |t, args|
93
+ database = args[:database] || ENV['DB'] || 'sqlite3'
94
+ rails_version = args[:rails_version] || 'rails-8-0'
95
+
96
+ puts "\n" + "=" * 50
97
+ puts "šŸ› ļø Rails Pulse Test Setup"
98
+ puts "=" * 50
99
+ puts "Database: #{database.upcase}"
100
+ puts "Rails: #{rails_version.upcase.gsub('-', ' ')}"
101
+ puts "=" * 50
102
+ puts
103
+
104
+ begin
105
+ # Remove schema.rb to ensure clean migration
106
+ schema_file = "test/dummy/db/schema.rb"
107
+ if File.exist?(schema_file)
108
+ puts "🧹 Removing existing schema.rb file..."
109
+ File.delete(schema_file)
110
+ end
111
+
112
+ if rails_version == "rails-8-0" && database == "sqlite3"
113
+ # Use current default setup
114
+ puts "šŸ“¦ Setting up #{database.upcase} database with Rails 8.0..."
115
+ sh "RAILS_ENV=test bin/rails db:drop db:create db:migrate"
116
+ else
117
+ # Use appraisal with specific database and Rails version
118
+ puts "šŸ“¦ Setting up #{database.upcase} database with #{rails_version.upcase.gsub('-', ' ')}..."
119
+ sh "DB=#{database} bundle exec appraisal #{rails_version} rails db:drop db:create db:migrate RAILS_ENV=test"
120
+ end
121
+
122
+ puts "\nāœ… Database setup complete for #{database.upcase} + #{rails_version.upcase.gsub('-', ' ')}!"
123
+
124
+ rescue => e
125
+ puts "\nāŒ Database setup failed!"
126
+ puts "Error: #{e.message}"
127
+ exit 1
32
128
  end
129
+ end
130
+
131
+ desc "Test all database and Rails version combinations"
132
+ task :test_matrix do
133
+ databases = %w[sqlite3 postgresql mysql2]
134
+ rails_versions = %w[rails-7-2 rails-8-0]
135
+
136
+ failed_combinations = []
137
+ total_combinations = databases.size * rails_versions.size
138
+ current = 0
139
+
140
+ # Check if system tests should be included
141
+ include_system_tests = ENV['BROWSER'] == 'true'
142
+ test_paths = "test/controllers test/helpers test/instrumentation test/jobs test/models test/services"
143
+ test_paths += " test/system" if include_system_tests
144
+
145
+ puts "\n" + "=" * 60
146
+ puts "šŸš€ Rails Pulse Full Test Matrix"
147
+ puts "=" * 60
148
+ puts "Testing #{total_combinations} combinations..."
149
+ puts "System tests: #{include_system_tests ? 'ENABLED (BROWSER=true)' : 'DISABLED (headless mode)'}"
150
+ puts "=" * 60
151
+
152
+ databases.each do |database|
153
+ rails_versions.each do |rails_version|
154
+ current += 1
33
155
 
34
- desc "Run tests across all database and Rails version combinations (local only - CI uses sqlite3 + postgresql)"
35
- task :matrix do
36
- databases = [ "sqlite3", "postgresql", "mysql2" ]
37
- rails_versions = [ "rails-7-2", "rails-8-0" ]
38
-
39
- failed_combinations = []
40
-
41
- databases.each do |database|
42
- rails_versions.each do |rails_version|
43
- puts "\n" + "=" * 80
44
- puts "🧪 Local Testing: #{database.upcase} + #{rails_version.upcase}"
45
- puts "(CI only tests SQLite3 + PostgreSQL for reliability)"
46
- puts "=" * 80
47
-
48
- begin
49
- gemfile = "gemfiles/#{rails_version.gsub('-', '_')}.gemfile"
50
-
51
- # Set environment variables
52
- env_vars = {
53
- "DB" => database,
54
- "BUNDLE_GEMFILE" => gemfile,
55
- "FORCE_DB_CONFIG" => "true"
56
- }
57
-
58
- # Add database-specific environment variables
59
- case database
60
- when "postgresql"
61
- env_vars.merge!({
62
- "POSTGRES_USERNAME" => ENV.fetch("POSTGRES_USERNAME", "postgres"),
63
- "POSTGRES_PASSWORD" => ENV.fetch("POSTGRES_PASSWORD", ""),
64
- "POSTGRES_HOST" => ENV.fetch("POSTGRES_HOST", "localhost"),
65
- "POSTGRES_PORT" => ENV.fetch("POSTGRES_PORT", "5432")
66
- })
67
- when "mysql2"
68
- env_vars.merge!({
69
- "MYSQL_USERNAME" => ENV.fetch("MYSQL_USERNAME", "root"),
70
- "MYSQL_PASSWORD" => ENV.fetch("MYSQL_PASSWORD", "password"),
71
- "MYSQL_HOST" => ENV.fetch("MYSQL_HOST", "localhost"),
72
- "MYSQL_PORT" => ENV.fetch("MYSQL_PORT", "3306")
73
- })
74
- end
75
-
76
- # Build environment string
77
- env_string = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ")
78
-
79
- # Run the test command
80
- sh "#{env_string} bundle exec rails test:all"
81
-
82
- puts "āœ… PASSED: #{database} + #{rails_version}"
83
-
84
- rescue => e
85
- puts "āŒ FAILED: #{database} + #{rails_version}"
86
- puts "Error: #{e.message}"
87
- failed_combinations << "#{database} + #{rails_version}"
156
+ puts "\n[#{current}/#{total_combinations}] Testing: #{database.upcase} + #{rails_version.upcase.gsub('-', ' ')}"
157
+ puts "-" * 50
158
+
159
+ begin
160
+ # First setup the database for this specific combination
161
+ Rake::Task[:test_setup_for_version].reenable
162
+ Rake::Task[:test_setup_for_version].invoke(database, rails_version)
163
+
164
+ # Then run the tests
165
+ if rails_version == "rails-8-0" && database == "sqlite3"
166
+ # Current default setup
167
+ sh "BROWSER=#{ENV['BROWSER']} rails test #{test_paths}"
168
+ else
169
+ # Use appraisal with specific database
170
+ sh "DB=#{database} BROWSER=#{ENV['BROWSER']} bundle exec appraisal #{rails_version} rails test #{test_paths}"
88
171
  end
172
+
173
+ puts "āœ… PASSED: #{database} + #{rails_version}"
174
+
175
+ rescue => e
176
+ puts "āŒ FAILED: #{database} + #{rails_version}"
177
+ puts " Error: #{e.message}"
178
+ failed_combinations << "#{database} + #{rails_version}"
89
179
  end
90
180
  end
181
+ end
182
+
183
+ puts "\n" + "=" * 60
184
+ puts "šŸ Test Matrix Results"
185
+ puts "=" * 60
186
+
187
+ if failed_combinations.empty?
188
+ puts "šŸŽ‰ All #{total_combinations} combinations passed!"
189
+ else
190
+ puts "āœ… Passed: #{total_combinations - failed_combinations.size}/#{total_combinations}"
191
+ puts "āŒ Failed combinations:"
192
+ failed_combinations.each { |combo| puts " • #{combo}" }
193
+ exit 1
194
+ end
195
+ end
91
196
 
92
- puts "\n" + "=" * 80
93
- puts "šŸ Local Test Matrix Results"
94
- puts "(CI automatically tests SQLite3 + PostgreSQL only)"
95
- puts "=" * 80
197
+ desc "Pre-release testing with comprehensive checks"
198
+ task :test_release do
199
+ puts "\n" + "=" * 70
200
+ puts "šŸš€ Rails Pulse Pre-Release Validation"
201
+ puts "=" * 70
202
+ puts
96
203
 
97
- if failed_combinations.empty?
98
- puts "āœ… All combinations passed!"
204
+ failed_tasks = []
205
+ current_step = 0
206
+ total_steps = 7
207
+
208
+ # Step 1: Git status check
209
+ current_step += 1
210
+ begin
211
+ puts "\n[#{current_step}/#{total_steps}] Checking git status..."
212
+ puts "-" * 70
213
+
214
+ git_status = `git status --porcelain`.strip
215
+ if !git_status.empty?
216
+ puts "āŒ Git working directory is not clean!"
217
+ puts "\nUncommitted changes:"
218
+ puts git_status
219
+ puts "\nPlease commit or stash your changes before running pre-release tests."
220
+ failed_tasks << "git_status_check"
99
221
  else
100
- puts "āŒ Failed combinations:"
101
- failed_combinations.each { |combo| puts " - #{combo}" }
102
- exit 1
222
+ puts "āœ… Git working directory is clean"
103
223
  end
224
+ rescue => e
225
+ puts "āš ļø Warning: Could not check git status (#{e.message})"
226
+ end
227
+
228
+ # Step 2: RuboCop linting
229
+ current_step += 1
230
+ begin
231
+ puts "\n[#{current_step}/#{total_steps}] Running RuboCop linting..."
232
+ puts "-" * 70
233
+ sh "bundle exec rubocop"
234
+ puts "āœ… Code style checks passed!"
235
+ rescue => e
236
+ puts "āŒ RuboCop linting failed!"
237
+ puts " Error: #{e.message}"
238
+ failed_tasks << "rubocop"
239
+ end
240
+
241
+ # Step 3: Install Node dependencies
242
+ current_step += 1
243
+ begin
244
+ puts "\n[#{current_step}/#{total_steps}] Installing Node dependencies..."
245
+ puts "-" * 70
246
+ sh "npm install"
247
+ puts "āœ… Node dependencies installed!"
248
+ rescue => e
249
+ puts "āŒ npm install failed!"
250
+ puts " Error: #{e.message}"
251
+ failed_tasks << "npm_install"
252
+ end
253
+
254
+ # Step 4: Build and verify assets
255
+ current_step += 1
256
+ begin
257
+ puts "\n[#{current_step}/#{total_steps}] Building production assets..."
258
+ puts "-" * 70
259
+ sh "npm run build"
260
+
261
+ # Verify assets were built
262
+ assets_dir = "public/rails-pulse-assets"
263
+ if Dir.exist?(assets_dir) && !Dir.empty?(assets_dir)
264
+ puts "āœ… Assets built successfully!"
265
+ puts " Location: #{assets_dir}"
266
+ else
267
+ puts "āŒ Assets directory is missing or empty!"
268
+ failed_tasks << "asset_build_verification"
269
+ end
270
+ rescue => e
271
+ puts "āŒ Asset building failed!"
272
+ puts " Error: #{e.message}"
273
+ failed_tasks << "npm_build"
274
+ end
275
+
276
+ # Step 5: Verify gem builds
277
+ current_step += 1
278
+ begin
279
+ puts "\n[#{current_step}/#{total_steps}] Verifying gem builds correctly..."
280
+ puts "-" * 70
281
+ sh "gem build rails_pulse.gemspec"
282
+
283
+ # Clean up the built gem
284
+ built_gems = Dir.glob("rails_pulse-*.gem")
285
+ built_gems.each { |gem_file| File.delete(gem_file) }
286
+
287
+ puts "āœ… Gem builds successfully!"
288
+ rescue => e
289
+ puts "āŒ Gem build failed!"
290
+ puts " Error: #{e.message}"
291
+ failed_tasks << "gem_build"
292
+ end
293
+
294
+ # Step 6: Run generator tests
295
+ current_step += 1
296
+ begin
297
+ puts "\n[#{current_step}/#{total_steps}] Running generator tests..."
298
+ puts "-" * 70
299
+ sh "./bin/test_generators"
300
+ puts "āœ… Generator tests passed!"
301
+ rescue => e
302
+ puts "āŒ Generator tests failed!"
303
+ puts " Error: #{e.message}"
304
+ failed_tasks << "test_generators"
305
+ end
306
+
307
+ # Step 7: Run full test matrix with system tests
308
+ current_step += 1
309
+ begin
310
+ puts "\n[#{current_step}/#{total_steps}] Running full test matrix with system tests..."
311
+ puts "-" * 70
312
+ sh "BROWSER=true rake test_matrix"
313
+ puts "āœ… Test matrix passed!"
314
+ rescue => e
315
+ puts "āŒ Test matrix failed!"
316
+ puts " Error: #{e.message}"
317
+ failed_tasks << "test_matrix"
318
+ end
319
+
320
+ # Print final results
321
+ puts "\n" + "=" * 70
322
+ puts "šŸ Pre-Release Validation Results"
323
+ puts "=" * 70
324
+
325
+ if failed_tasks.empty?
326
+ puts "šŸŽ‰ All pre-release checks passed!"
327
+ puts "\nāœ… Ready for release!"
328
+ puts "\nNext steps:"
329
+ puts " 1. Update version in lib/rails_pulse/version.rb"
330
+ puts " 2. Update Gemfile.lock files for all Rails versions"
331
+ puts " 3. Follow the release process in docs/releasing.md"
332
+ else
333
+ puts "āŒ Failed checks (#{failed_tasks.size}/#{total_steps}):"
334
+ failed_tasks.each { |task| puts " • #{task}" }
335
+ puts "\nāš ļø Fix these issues before releasing."
336
+ exit 1
104
337
  end
105
338
  end
106
339
 
107
- # Override default test task
108
- desc "Run all tests"
109
- task test: "test:all"
340
+
341
+ task default: :test