Abd El-Latif

Script Tracker: Migration-Style Management for Rails One-Off Scripts

Ahmed Abd El-Latif
Ahmed Abd El-LatifNovember 17, 2024 • 8 min read
RailsRuby GemsDevOps
Script Tracker - Migration-style tracking for Rails one-off scripts
TwitterLinkedIn

The Problem Every Rails Developer Knows

You need to run a script once to fix data, update user preferences, or migrate something. You write a rake task, run it in production, and then the questions start: Did it actually run? Did it complete successfully? If something broke halfway through, where do you restart?

These one-off scripts are essential but dangerous. Unlike migrations, there's no built-in tracking, no transaction safety, and no clear audit trail.

The Solution: Treat Scripts Like Migrations

Script Tracker is a Ruby gem that brings the reliability and tracking of database migrations to your one-off scripts. Every script is tracked, logged, and managed with the same confidence you have in your schema migrations.

Key Features

Execution Tracking

Never wonder if a script ran again. Every execution is logged with timestamps, status, and results.

Transaction Support

Scripts run inside database transactions by default. If something fails, everything rolls back cleanly.

Built-in Logging

Comprehensive logging with timestamps and progress tracking built right in.

Batch Processing

Helper methods for processing large datasets without overwhelming your database or memory.

Timeout Protection

Configure custom timeouts to prevent runaway scripts from causing problems.

Simple API, Powerful Results

Creating Scripts

1rake scripts:create["update user preferences"]

This generates a timestamped script file with a clean template:

db/scripts/20241117120000_update_user_preferences.rb
1module Scripts
2 class UpdateUserPreferences < ScriptTracker::Base
3 def self.execute
4 log "Starting user preference updates"
5
6 User.find_each do |user|
7 user.update!(preferences: { theme: 'dark' })
8 end
9
10 log "Successfully updated preferences for all users"
11 end
12 end
13end

Running and Managing Scripts

1rake scripts:run # Execute all pending scripts
2rake scripts:status # View execution history
3rake scripts:rollback[filename] # Rollback if needed

Advanced Features

Smart Skipping:

1def self.execute
2 skip! "No users need updating" if User.where(needs_update: true).count.zero?
3 # Your logic here
4end

Progress Tracking:

1def self.execute
2 total = User.count
3 processed = 0
4
5 User.find_each do |user|
6 # Process user
7 processed += 1
8 log_progress(processed, total) if (processed % 100).zero?
9 end
10end

Batch Processing:

1def self.execute
2 users = User.where(processed: false)
3 process_in_batches(users, batch_size: 1000) do |user|
4 user.update!(processed: true)
5 end
6end

Why Script Tracker Matters

❌ Before Script Tracker

  • Manual tracking of script execution
  • No transaction safety
  • Silent failures go unnoticed
  • Risk of running scripts multiple times
  • No audit trail for compliance

✓ After Script Tracker

  • Automatic execution tracking
  • Built-in transaction safety
  • Clear failure reporting
  • Duplicate execution prevention
  • Complete audit trail

Real-World Impact

This gem started as internal tooling to solve a recurring problem: too many "wait, did that script run?" conversations in production deployments. Now data migrations are as reliable and trackable as schema migrations.

Getting Started

Add to your Gemfile:

1gem 'script_tracker'

Install and set up:

1bundle install
2rails generate script_tracker:install
3rails db:migrate

Create your first tracked script:

1rake scripts:create["your script description"]

Open Source and Ready

Script Tracker is open source, actively maintained, and ready for production use. The gem provides the reliability and peace of mind that one-off scripts have been missing.

Repository: github.com/a-abdellatif98/script_tracker

License: MIT

💡 Have you been burned by one-off scripts in production? Script Tracker would have prevented it.

Related Posts

Comments

💬 Comments coming soon! Stay tuned for discussions.

← Back to Blog