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 all 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [0.7.6] - 2024-07-05
- Added UI
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mention env vars/configs added

- Added environment variable `ACTUAL_DB_SCHEMA_UI_ENABLED` to enable/disable the UI in specific environments
- Added configuration option `ActualDbSchema.config[:ui_enabled]` to enable/disable the UI in specific environments

## [0.7.5] - 2024-06-20
- Added db:rollback_migrations:manual task to manually rolls back phantom migrations one by one

Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,35 @@ 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/phantom_migrations
```
This page displays a list of phantom migrations for each database connection and provides options to view details and rollback them.

## UI options

By default, the UI is enabled in the development environment. If you prefer to enable the UI for another environment, you can do so in two ways:

### 1. Using Environment Variable

Set the environment variable `ACTUAL_DB_SCHEMA_UI_ENABLED` to `true`:

```sh
export ACTUAL_DB_SCHEMA_UI_ENABLED=true
```

### 2. Using Initializer
Add the following line to your initializer file (`config/initializers/actual_db_schema.rb`):

```ruby
ActualDbSchema.config[:ui_enabled] = true
```

> With this option, the UI can be disabled for all environments or be enabled in specific ones.

## 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;
}
27 changes: 27 additions & 0 deletions app/controllers/actual_db_schema/phantom_migrations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

module ActualDbSchema
# Controller to display the list of phantom migrations for each database connection.
class PhantomMigrationsController < ActionController::Base
def index; end

def show
render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found unless phantom_migration
end

def rollback
ActualDbSchema::Migration.rollback(params[:id], params[:database])
redirect_to phantom_migrations_path
end

private

helper_method def phantom_migrations
@phantom_migrations ||= ActualDbSchema::Migration.all
end

helper_method def phantom_migration
@phantom_migration ||= ActualDbSchema::Migration.find(params[:id], params[:database])
end
end
end
45 changes: 45 additions & 0 deletions app/views/actual_db_schema/phantom_migrations/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<title>Phantom Migrations</title>
<%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %>
</head>
<body>
<div>
<h2>Phantom Migrations</h2>
<% if phantom_migrations.present? %>
<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>
<% phantom_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', phantom_migration_path(id: migration[:version], database: migration[:database]), class: 'button' %>
<%= button_to '⎌ Rollback', rollback_phantom_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p>No phantom migrations found.</p>
<% end %>
</div>
</body>
</html>
45 changes: 45 additions & 0 deletions app/views/actual_db_schema/phantom_migrations/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<title>Phantom Migration Details</title>
<%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %>
</head>
<body>
<div>
<h2>Phantom Migration <%= phantom_migration[:name] %> Details</h2>
<table>
<tbody>
<tr>
<th>Status</th>
<td><%= phantom_migration[:status] %></td>
</tr>
<tr>
<th>Migration ID</th>
<td><%= phantom_migration[:version] %></td>
</tr>
<tr>
<th>Branch</th>
<td><%= phantom_migration[:branch] %></td>
</tr>
<tr>
<th>Database</th>
<td><%= phantom_migration[:database] %></td>
</tr>
<tr>
<th>Path</th>
<td><%= phantom_migration[:filename] %></td>
</tr>
</tbody>
</table>

<h3>Migration Code</h3>
<div>
<pre><%= File.read(phantom_migration[:filename]) %></pre>
</div>
<div class='button-container'>
<%= link_to '← Back', phantom_migrations_path, class: 'button' %>
<%= button_to '⎌ Rollback', rollback_phantom_migration_path(id: params[:id], database: params[: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 :phantom_migrations, only: %i[index show] do
member do
post :rollback
end
end
end
25 changes: 13 additions & 12 deletions lib/actual_db_schema.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# frozen_string_literal: true

require "actual_db_schema/engine"
require "active_record/migration"
require "csv"
require_relative "actual_db_schema/git"
require_relative "actual_db_schema/store"
require_relative "actual_db_schema/version"
require_relative "actual_db_schema/migration"
require_relative "actual_db_schema/migration_context"
require_relative "actual_db_schema/patches/migration_proxy"
require_relative "actual_db_schema/patches/migrator"
require_relative "actual_db_schema/patches/migration_context"
Expand All @@ -17,16 +20,15 @@
module ActualDbSchema
raise NotImplementedError, "ActualDbSchema is only supported in Rails" unless defined?(Rails)

require "railtie"

class << self
attr_accessor :config, :failed
end

self.failed = []
self.config = {
enabled: Rails.env.development?,
auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present?
auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present?,
ui_enabled: Rails.env.development? || ENV["ACTUAL_DB_SCHEMA_UI_ENABLED"].present?
}

def self.migrated_folder
Expand Down Expand Up @@ -58,17 +60,16 @@ def self.migrations_paths
end
end

def self.migration_filename(fullpath)
fullpath.split("/").last
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.for_each_db_connection
configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
configs.each do |db_config|
config = db_config.respond_to?(:config) ? db_config.config : db_config
ActiveRecord::Base.establish_connection(config)
yield
end
def self.migration_filename(fullpath)
fullpath.split("/").last
end
end

Expand Down
22 changes: 6 additions & 16 deletions lib/actual_db_schema/commands/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ module ActualDbSchema
module Commands
# Base class for all commands
class Base
attr_reader :context

def initialize(context)
@context = context
end

def call
unless ActualDbSchema.config.fetch(:enabled, true)
raise "ActualDbSchema is disabled. Set ActualDbSchema.config[:enabled] = true to enable it."
Expand All @@ -17,22 +23,6 @@ def call
def call_impl
raise NotImplementedError
end

def context
@context ||= 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
19 changes: 5 additions & 14 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 Expand Up @@ -66,14 +57,14 @@ def line_for(status, version)
].join(" ")
end

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

def metadata
@metadata ||= ActualDbSchema::Store.instance.read
end

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

def longest_branch_name
@longest_branch_name ||=
metadata.values.map { |v| v[:branch] }.compact.max_by(&:length) || "unknown"
Expand Down
4 changes: 2 additions & 2 deletions lib/actual_db_schema/commands/rollback.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ module ActualDbSchema
module Commands
# Rolls back all phantom migrations
class Rollback < Base
def initialize(manual_mode: false)
def initialize(context, manual_mode: false)
@manual_mode = manual_mode || manual_mode_default?
super()
super(context)
end

private
Expand Down
Loading
Loading