Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UI #70

Merged
merged 36 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
12218c5
Add UI
VladislavSokov Jun 24, 2024
930ffa1
fixup! Add UI
VladislavSokov Jun 25, 2024
4ba8147
fixup! fixup! Add UI
VladislavSokov Jun 26, 2024
4a73bd1
Add tests draft
VladislavSokov Jun 27, 2024
ff630e8
Add tests
VladislavSokov Jun 28, 2024
0ceffa9
fixup! Add tests
VladislavSokov Jul 1, 2024
a970630
fixup! fixup! Add tests
VladislavSokov Jul 1, 2024
05576d8
Refactoring
VladislavSokov Jul 2, 2024
bc614fa
fixup! Refactoring
VladislavSokov Jul 2, 2024
994aea7
fixup! fixup! Refactoring
VladislavSokov Jul 2, 2024
6335366
fixup! fixup! fixup! Refactoring
VladislavSokov Jul 3, 2024
9008e5d
fixup! fixup! fixup! fixup! Refactoring
VladislavSokov Jul 4, 2024
8aa0418
fixup! fixup! fixup! fixup! fixup! Refactoring
VladislavSokov Jul 4, 2024
f749e4f
fixup! fixup! fixup! fixup! fixup! fixup! Refactoring
VladislavSokov Jul 4, 2024
7d8e928
Add Migration class
VladislavSokov Jul 4, 2024
80e81cf
fixup! Add Migration class
VladislavSokov Jul 4, 2024
a45030d
fixup! fixup! Add Migration class
VladislavSokov Jul 4, 2024
7c22e05
Add DatabaseConnection class
VladislavSokov Jul 4, 2024
fd65605
Add unecode icons to buttons
VladislavSokov Jul 4, 2024
ed534ea
fixup! Add DatabaseConnection class
VladislavSokov Jul 4, 2024
0a465fd
fixup! fixup! Add DatabaseConnection class
VladislavSokov Jul 5, 2024
b715574
fixup! fixup! fixup! fixup! fixup! fixup! fixup! Refactoring
VladislavSokov Jul 5, 2024
8404a76
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Refactoring
VladislavSokov Jul 5, 2024
b7c52f8
fixup! fixup! fixup! Add tests
VladislavSokov Jul 5, 2024
f0b4432
Add new env var for "disabled"
VladislavSokov Jul 5, 2024
ae996f9
Remove railtie
VladislavSokov Jul 5, 2024
e796256
fixup! fixup! fixup! Add DatabaseConnection class
VladislavSokov Jul 5, 2024
c26ace6
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Refact…
VladislavSokov Jul 5, 2024
95a53b7
Memoize helper methods
VladislavSokov Jul 8, 2024
64559bb
Rename migrations to phantom migrations
VladislavSokov Jul 8, 2024
af67231
Fix render not_found and add test
VladislavSokov Jul 9, 2024
bf05208
Revert changes for metadata method
VladislavSokov Jul 9, 2024
336a088
add migration not found text
VladislavSokov Jul 9, 2024
11aece9
hide "down" migrations
VladislavSokov Jul 9, 2024
da3bad1
fix tests
VladislavSokov Jul 9, 2024
d7f3ee2
fixup! fix tests
VladislavSokov Jul 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ The gem offers the following rake tasks that can be manually run according to yo
- `rails db:rollback_branches:manual` - run it to manually rolls back phantom migrations one by one.
- `rails db:phantom_migrations` - displays a list of phantom migrations.

## Accessing the UI

The UI for managing migrations is enabled automatically. To access the UI, simply navigate to the following URL in your web browser:
```
http://localhost:3000/rails/migrations
```
This page will display a list of phantom migrations for each database connection and provide options to view details and rollback migrations.
VladislavSokov marked this conversation as resolved.
Show resolved Hide resolved

## Disabling Automatic Rollback

By default, the automatic rollback of migrations is enabled. If you prefer to perform manual rollbacks, you can disable the automatic rollback in two ways:
Expand Down
68 changes: 68 additions & 0 deletions app/assets/stylesheets/actual_db_schema/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
body {
margin: 8px;
background-color: #fff;
color: #333;
}

body, p, td {
font-family: helvetica, verdana, arial, sans-serif;
font-size: 13px;
line-height: 18px;
}

h2 {
padding-left: 10px;
}

table {
margin: 0;
border-collapse: collapse;

thead tr {
border-bottom: 2px solid #ddd;
}

tbody {
tr {
border-bottom: 1px solid #ddd;
}

tr:nth-child(odd) {
background: #f2f2f2;
}
}

td {
padding: 14px 30px;
}
}

.button {
font-weight: bold;
color: #000;
border: none;
padding: 5px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 0 2px;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
background: none;
}

.button:hover {
color: #fff;
background-color: #000;
}

.button-container {
display: flex;
}

pre {
background-color: #f7f7f7;
padding: 10px;
border: 1px solid #ddd;
}
97 changes: 97 additions & 0 deletions app/controllers/actual_db_schema/migrations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# frozen_string_literal: true

module ActualDbSchema
# Controller to display the list of phantom migrations for each database connection.
class MigrationsController < ActionController::Base
before_action :load_migrations, only: %i[index]

def index; end

def show
@migration = load_migration(params[:id], params[:database])
end

def rollback
version = params[:id]
database = params[:database]

rollback_migration(version, database)

redirect_to migrations_path, notice: "Migration #{version} has been rolled back."
end

private

def load_migrations
VladislavSokov marked this conversation as resolved.
Show resolved Hide resolved
@migrations = []

ActualDbSchema.for_each_db_connection do
context = prepare_context
indexed_phantom_migrations = context.migrations.index_by { |m| m.version.to_s }

context.migrations_status.each do |status, version|
migration = indexed_phantom_migrations[version]
next unless migration

@migrations << build_migration_struct(status, migration)
end
end
end

def load_migration(version, database)
ActualDbSchema.for_each_db_connection do
next unless ActualDbSchema.db_config[:database] == database

context = prepare_context
migration = find_migration_in_context(context, version)
return migration if migration
end
nil
end

def rollback_migration(version, database)
ActualDbSchema.for_each_db_connection do
next unless ActualDbSchema.db_config[:database] == database

context = prepare_context
if context.migrations.detect { |m| m.version.to_s == version }
context.run(:down, version.to_i)
break
end
end
end

def find_migration_in_context(context, version)
migration = context.migrations.detect { |m| m.version.to_s == version }
return unless migration

status = context.migrations_status.detect { |_s, v| v.to_s == version }&.first || "unknown"
build_migration_struct(status, migration)
end

def prepare_context
context = ActualDbSchema.fetch_migration_context
context.extend(ActualDbSchema::Patches::MigrationContext)
context
end

def build_migration_struct(status, migration)
ActualDbSchema::MigrationStruct.new(
status,
migration.version.to_s,
migration.name,
branch_for(migration.version),
ActualDbSchema.db_config[:database],
migration.filename
)
end

def branch_for(version)
metadata.fetch(version.to_s, {})[:branch] || "unknown"
end

def metadata
VladislavSokov marked this conversation as resolved.
Show resolved Hide resolved
ActualDbSchema::Store.instance.read
end
end
end
41 changes: 41 additions & 0 deletions app/views/actual_db_schema/migrations/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>Phantom Migrations</title>
<%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %>
ka8725 marked this conversation as resolved.
Show resolved Hide resolved
</head>
<body>
<div>
<h2>Phantom Migrations</h2>
<table>
<thead>
<tr>
<th>Status</th>
<th>Migration ID</th>
<th>Name</th>
<th>Branch</th>
<th>Database</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% @migrations.each do |migration| %>
<tr>
<td><%= migration[:status] %></td>
<td><%= migration[:version] %></td>
<td><%= migration[:name] %></td>
<td><%= migration[:branch] %></td>
<td><%= migration[:database] %></td>
<td>
<div class='button-container'>
<%= link_to 'Show', migration_path(id: migration[:version], database: migration[:database]), class: 'button' %>
<%= button_to 'Rollback', rollback_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</body>
</html>
45 changes: 45 additions & 0 deletions app/views/actual_db_schema/migrations/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<title>Migration Details</title>
<%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %>
</head>
<body>
<div>
<h2>Migration <%= @migration[:name] %> Details</h2>
<table>
<tbody>
<tr>
<th>Status</th>
<td><%= @migration[:status] %></td>
</tr>
<tr>
<th>Migration ID</th>
<td><%= @migration[:version] %></td>
</tr>
<tr>
<th>Branch</th>
<td><%= @migration[:branch] %></td>
</tr>
<tr>
<th>Database</th>
<td><%= @migration[:database] %></td>
</tr>
<tr>
<th>Path</th>
<td><%= @migration[:filename] %></td>
</tr>
</tbody>
</table>

<h3>Migration Code</h3>
<div>
<pre><%= File.read(@migration[:filename]) %></pre>
</div>
<div class='button-container'>
<%= link_to 'Back', migrations_path, class: 'button' %>
<%= button_to 'Rollback', rollback_migration_path(id: @migration[:version], database: @migration[:database]), method: :post, class: 'button' %>
</div>
</div>
</body>
</html>
9 changes: 9 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

ActualDbSchema::Engine.routes.draw do
resources :migrations, only: %i[index show] do
ka8725 marked this conversation as resolved.
Show resolved Hide resolved
member do
post :rollback
end
end
end
20 changes: 20 additions & 0 deletions lib/actual_db_schema.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require "actual_db_schema/engine"
require "active_record/migration"
require "csv"
require_relative "actual_db_schema/git"
Expand Down Expand Up @@ -29,6 +30,8 @@ class << self
auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present?
}

MigrationStruct = Struct.new(:status, :version, :name, :branch, :database, :filename)
VladislavSokov marked this conversation as resolved.
Show resolved Hide resolved

def self.migrated_folder
migrated_folders.first
end
Expand Down Expand Up @@ -58,6 +61,23 @@ def self.migrations_paths
end
end

def self.fetch_migration_context
ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING)
if ar_version >= Gem::Version.new("7.2.0") || (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?)
ActiveRecord::Base.connection_pool.migration_context
else
ActiveRecord::Base.connection.migration_context
end
end

def self.db_config
if ActiveRecord::Base.respond_to?(:connection_db_config)
ActiveRecord::Base.connection_db_config.configuration_hash
else
ActiveRecord::Base.connection_config
end
end

def self.migration_filename(fullpath)
fullpath.split("/").last
end
Expand Down
12 changes: 1 addition & 11 deletions lib/actual_db_schema/commands/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,10 @@ def call_impl
end

def context
@context ||= fetch_migration_context.tap do |c|
@context ||= ActualDbSchema.fetch_migration_context.tap do |c|
c.extend(ActualDbSchema::Patches::MigrationContext)
end
end

def fetch_migration_context
ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING)
if ar_version >= Gem::Version.new("7.2.0") ||
(ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?)
ActiveRecord::Base.connection_pool.migration_context
else
ActiveRecord::Base.connection.migration_context
end
end
end
end
end
11 changes: 1 addition & 10 deletions lib/actual_db_schema/commands/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,11 @@ def preambule
puts "\nPhantom migrations\n\n"
puts "Below is a list of irrelevant migrations executed in unmerged branches."
puts "To bring your database schema up to date, the migrations marked as \"up\" should be rolled back."
database_path = db_config[:database]
puts "\ndatabase: #{database_path}\n\n"
puts "\ndatabase: #{ActualDbSchema.db_config[:database]}\n\n"
puts header.join(" ")
puts "-" * separator_width
end

def db_config
if ActiveRecord::Base.respond_to?(:connection_db_config)
ActiveRecord::Base.connection_db_config.configuration_hash
else
ActiveRecord::Base.connection_config
end
end

def separator_width
header.map(&:length).sum + (header.size - 1) * 2
end
Expand Down
20 changes: 20 additions & 0 deletions lib/actual_db_schema/engine.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module ActualDbSchema
# It isolates the namespace to avoid conflicts with the main application.
class Engine < ::Rails::Engine
isolate_namespace ActualDbSchema

initializer "actual_db_schema.append_routes", after: :add_routing_paths do |app|
if Rails.env.development? || Rails.env.test?
VladislavSokov marked this conversation as resolved.
Show resolved Hide resolved
app.routes.append do
mount ActualDbSchema::Engine => "/rails"
end
end
end

initializer "actual_db_schema.assets.precompile" do |app|
app.config.assets.precompile += %w[actual_db_schema/styles.css] if Rails.env.development?
VladislavSokov marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
Loading
Loading