data-migration 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +14 -0
- data/.github/dependabot.yml +27 -0
- data/.github/workflows/_trunk_check.yml +15 -0
- data/.github/workflows/test.yml +42 -0
- data/.gitignore +24 -0
- data/.ruby-version +1 -0
- data/.trunk/.gitignore +9 -0
- data/.trunk/configs/.markdownlint.yaml +2 -0
- data/.trunk/configs/.shellcheckrc +7 -0
- data/.trunk/configs/.yamllint.yaml +7 -0
- data/.trunk/trunk.yaml +39 -0
- data/.vscode/extensions.json +18 -0
- data/.vscode/settings.json +7 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +246 -0
- data/LICENSE.md +21 -0
- data/README.md +141 -0
- data/data-migration.gemspec +44 -0
- data/lib/data_migration/config.rb +84 -0
- data/lib/data_migration/job.rb +63 -0
- data/lib/data_migration/task.rb +127 -0
- data/lib/data_migration.rb +37 -0
- data/lib/generators/data_migration_generator.rb +13 -0
- data/lib/generators/install_generator.rb +17 -0
- data/lib/generators/templates/data_migration.rb.tt +26 -0
- data/lib/generators/templates/install_data_migration_tasks.rb.tt +27 -0
- data/spec/data_migration/config_spec.rb +116 -0
- data/spec/data_migration/job_spec.rb +96 -0
- data/spec/data_migration/task_spec.rb +152 -0
- data/spec/data_migration_spec.rb +65 -0
- data/spec/fixtures/data_migrations/20241206200111_create_users.rb +17 -0
- data/spec/fixtures/data_migrations/20241206200112_create_bad_users.rb +5 -0
- data/spec/fixtures/data_migrations/20241206200113_change_users.rb +5 -0
- data/spec/fixtures/data_migrations/20241206200114_create_batch_users.rb +9 -0
- data/spec/fixtures/schema.rb +26 -0
- data/spec/generators/install_generator_spec.rb +48 -0
- data/spec/generators/migration_generator_spec.rb +50 -0
- data/spec/rails_helper.rb +21 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/junit_formatter.rb +6 -0
- data/spec/support/rails_helpers.rb +53 -0
- data/usr/bin/release.sh +35 -0
- metadata +246 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe DataMigration::Task do
|
4
|
+
subject(:task) { DataMigration::Task.create!(name: migration_name) }
|
5
|
+
|
6
|
+
let(:migration_name) { "20241206200111_create_users" }
|
7
|
+
let(:data_migrations_full_path) { File.expand_path("../fixtures/data_migrations", __dir__) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
# allow(Rails).to receive(:root).and_return(rails_root)
|
11
|
+
# allow(Rails).to receive(:logger).and_return(rails_logger)
|
12
|
+
allow(DataMigration.config).to receive(:data_migrations_full_path).and_return(data_migrations_full_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#file_path" do
|
16
|
+
it "returns the full path to the migration file" do
|
17
|
+
expect(task.file_path).to eq("#{data_migrations_full_path}/#{migration_name}.rb")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#file_exists?" do
|
22
|
+
it "returns true if the file exists" do
|
23
|
+
expect(task.file_exists?).to be(true)
|
24
|
+
expect(task.valid?).to be(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when the file does not exist" do
|
28
|
+
let(:migration_name) { "20241206200111_create_something_else" }
|
29
|
+
let(:task) { DataMigration::Task.new(name: migration_name) }
|
30
|
+
|
31
|
+
it "returns false" do
|
32
|
+
expect(task.file_exists?).to be(false)
|
33
|
+
expect(task.valid?).to be(false)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#requires_pause?" do
|
39
|
+
before do
|
40
|
+
task.status = :started
|
41
|
+
task.pause_minutes = 10
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns true if pause_minutes is positive and status is not paused" do
|
45
|
+
expect(task.requires_pause?).to be(true)
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when pause_minutes is 0" do
|
49
|
+
before do
|
50
|
+
task.pause_minutes = 0
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns false" do
|
54
|
+
expect(task.requires_pause?).to be(false)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when status is paused" do
|
59
|
+
before do
|
60
|
+
task.status = :paused
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns false" do
|
64
|
+
expect(task.requires_pause?).to be(false)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#perform_now" do
|
70
|
+
it "calls the job class with the task and arguments" do
|
71
|
+
kwargs = { foo: "bar" }
|
72
|
+
expect(DataMigration::Job).to receive(:perform_now).with(task.id, **kwargs)
|
73
|
+
task.perform_now(**kwargs)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#perform_later" do
|
78
|
+
it "calls the job class with the task and arguments" do
|
79
|
+
kwargs = { foo: "bar" }
|
80
|
+
expect(DataMigration::Job).to receive(:perform_later).with(task.id, **kwargs)
|
81
|
+
task.perform_later(**kwargs)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#prepare" do
|
86
|
+
subject(:task) { DataMigration::Task.prepare(migration_name, pause_minutes: 10, jobs_limit: 5) }
|
87
|
+
|
88
|
+
it "creates a new task with the given name, pause, and jobs_limit" do
|
89
|
+
expect { task }.to change(DataMigration::Task, :count).by(1)
|
90
|
+
expect(task.name).to eq(migration_name)
|
91
|
+
expect(task.status).to be_nil
|
92
|
+
expect(task.pause_minutes).to eq(10)
|
93
|
+
expect(task.jobs_limit).to eq(5)
|
94
|
+
end
|
95
|
+
|
96
|
+
context "when chained with perform_now" do
|
97
|
+
it "calls the job class with the task and arguments" do
|
98
|
+
perform_args = { "foo" => "bar" }
|
99
|
+
expect(DataMigration::Job).to receive(:perform_now).with(task.id, **perform_args)
|
100
|
+
task.perform_now(**perform_args)
|
101
|
+
expect(task.kwargs).to eq(perform_args)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#job_check_in!" do
|
107
|
+
subject(:job_check_in!) { task.job_check_in!(job_id, job_args: ["foo"], job_kwargs: { bar: "baz" }) }
|
108
|
+
|
109
|
+
let(:job_id) { "123" }
|
110
|
+
|
111
|
+
it "adds the job to the current_jobs hash" do
|
112
|
+
expect { job_check_in! }.to change { task.current_jobs.size }.by(1)
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when there is a job with the same id" do
|
116
|
+
before do
|
117
|
+
task.job_check_in!(job_id, job_args: ["foo"], job_kwargs: { bar: "baz" })
|
118
|
+
end
|
119
|
+
|
120
|
+
it "raises a JobConflictError" do
|
121
|
+
expect { job_check_in! }.to raise_error(DataMigration::JobConflictError)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context "when default_jobs_limit is 1 and there are jobs" do
|
126
|
+
before do
|
127
|
+
DataMigration.config.default_jobs_limit = 1
|
128
|
+
task.job_check_in!("321", job_args: ["foo"], job_kwargs: { bar: "baz" })
|
129
|
+
end
|
130
|
+
|
131
|
+
it "raises a JobConcurrencyLimitError" do
|
132
|
+
expect { job_check_in! }.to raise_error(DataMigration::JobConcurrencyLimitError)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "#job_check_out!" do
|
138
|
+
subject(:job_check_out!) { task.job_check_out!(job_id) }
|
139
|
+
|
140
|
+
let(:job_id) { "123" }
|
141
|
+
|
142
|
+
before do
|
143
|
+
task.save!
|
144
|
+
task.job_check_in!(job_id, job_args: ["foo"], job_kwargs: { bar: "baz" })
|
145
|
+
end
|
146
|
+
|
147
|
+
it "removes the job from the current_jobs hash" do
|
148
|
+
expect { job_check_out! }.to change { task.current_jobs.size }.by(-1)
|
149
|
+
expect(task.current_jobs).not_to have_key(job_id)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe DataMigration do
|
4
|
+
let(:gem_specification) { Gem::Specification.load(File.expand_path("../../data-migration.gemspec", __FILE__)) }
|
5
|
+
|
6
|
+
it "has a version number" do
|
7
|
+
expect(described_class::VERSION).to eq gem_specification.version.to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:changelog_file) { File.expand_path("../../CHANGELOG.md", __FILE__) }
|
11
|
+
it "has changelog for the version" do
|
12
|
+
expect(File.exist?(changelog_file)).to be true
|
13
|
+
expect(File.read(changelog_file)).to include("# #{gem_specification.version}")
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:license_file) { File.expand_path("../../LICENSE.md", __FILE__) }
|
17
|
+
it "has license" do
|
18
|
+
expect(File.exist?(license_file)).to be true
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:readme_file) { File.expand_path("../../README.md", __FILE__) }
|
22
|
+
it "has readme" do
|
23
|
+
expect(File.exist?(readme_file)).to be true
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".notify" do
|
27
|
+
subject(:notify) { DataMigration.notify("test") }
|
28
|
+
let(:action_reporter) { Class.new }
|
29
|
+
before do
|
30
|
+
stub_const("ActionReporter", action_reporter)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "sends message to ActionReporter" do
|
34
|
+
expect(action_reporter).to receive(:notify).with("test", context: {})
|
35
|
+
expect { notify }.not_to raise_error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe ".perform_now" do
|
40
|
+
subject(:perform_now) { DataMigration.perform_now("test_perform_now") }
|
41
|
+
|
42
|
+
it "delegates to DataMigration::Task.perform_now" do
|
43
|
+
expect(DataMigration::Task).to receive(:perform_now).with("test_perform_now")
|
44
|
+
expect { perform_now }.not_to raise_error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe ".perform_later" do
|
49
|
+
subject(:perform_later) { DataMigration.perform_later("test_perform_later") }
|
50
|
+
|
51
|
+
it "delegates to DataMigration::Task.perform_later" do
|
52
|
+
expect(DataMigration::Task).to receive(:perform_later).with("test_perform_later")
|
53
|
+
expect { perform_later }.not_to raise_error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".prepare" do
|
58
|
+
subject(:prepare) { DataMigration.prepare("test_prepare") }
|
59
|
+
|
60
|
+
it "delegates to DataMigration::Task.prepare" do
|
61
|
+
expect(DataMigration::Task).to receive(:prepare).with("test_prepare")
|
62
|
+
expect { prepare }.not_to raise_error
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateUsers
|
2
|
+
def perform(**kwargs)
|
3
|
+
User.find_or_create_by(email: "test@example.com")
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
if ENV["RUN_MIGRATION_TESTS"] && Object.const_defined?(:RSpec)
|
8
|
+
require "rails_helper"
|
9
|
+
|
10
|
+
RSpec.describe CreateUsers, type: :data_migration do
|
11
|
+
subject(:perform) { described_class.new.perform }
|
12
|
+
|
13
|
+
it do
|
14
|
+
expect { perform }.not_to raise_error
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
ActiveRecord::Schema.define(version: 2024_12_06_200411) do
|
2
|
+
create_table :data_migration_tasks, force: true do |t|
|
3
|
+
t.string "name", null: false
|
4
|
+
|
5
|
+
t.json "kwargs", default: {}, null: false
|
6
|
+
t.json "current_jobs", default: {}, null: false
|
7
|
+
|
8
|
+
t.string "status"
|
9
|
+
t.datetime "started_at"
|
10
|
+
t.datetime "completed_at"
|
11
|
+
|
12
|
+
t.bigint "operator_id"
|
13
|
+
t.string "operator_type"
|
14
|
+
|
15
|
+
t.integer "pause_minutes", default: 0, null: false
|
16
|
+
t.integer "jobs_limit"
|
17
|
+
|
18
|
+
t.datetime "created_at", null: false
|
19
|
+
t.datetime "updated_at", null: false
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :users, force: true do |t|
|
23
|
+
t.string :email
|
24
|
+
t.string :version
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "generators/install_generator"
|
4
|
+
|
5
|
+
describe InstallGenerator, type: :generator do
|
6
|
+
include FileUtils
|
7
|
+
|
8
|
+
subject(:generator) { described_class.start params }
|
9
|
+
|
10
|
+
let(:root_path) { rails_root(File.expand_path("../../../tmp/rspec", __FILE__)) }
|
11
|
+
|
12
|
+
let(:migration_name) { "data_migration_tasks" }
|
13
|
+
let(:params) { [migration_name] }
|
14
|
+
let(:created_files) { Dir["#{root_path}/db/migrate/*_#{migration_name}.rb"] }
|
15
|
+
let(:migration_content) { File.readlines(created_files.first).reject(&:blank?).map(&:strip) }
|
16
|
+
|
17
|
+
before do
|
18
|
+
mkdir_p root_path.to_s
|
19
|
+
allow(DataMigration.config).to receive(:schema_migrations_path).and_return("#{root_path}/db/migrate")
|
20
|
+
allow(Rails).to receive(:root).and_return(root_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
rm_rf root_path.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
it "creates a migration file" do
|
28
|
+
generator
|
29
|
+
|
30
|
+
expect(created_files).not_to be_empty
|
31
|
+
|
32
|
+
expect(migration_content).to include("create_table :data_migration_tasks, force: true do |t|")
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when tasks_table_name is not default" do
|
36
|
+
before do
|
37
|
+
allow(DataMigration).to receive(:tasks_table_name).and_return("other_data_migration_tasks")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "creates a migration file with the correct table name" do
|
41
|
+
generator
|
42
|
+
|
43
|
+
expect(created_files).not_to be_empty
|
44
|
+
|
45
|
+
expect(migration_content).to include("create_table :other_data_migration_tasks, force: true do |t|")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "generators/data_migration_generator"
|
4
|
+
|
5
|
+
describe DataMigrationGenerator, type: :generator do
|
6
|
+
include FileUtils
|
7
|
+
|
8
|
+
subject(:generator) { described_class.start params }
|
9
|
+
|
10
|
+
let(:root_path) { rails_root(File.expand_path("../../../tmp/rspec", __FILE__)) }
|
11
|
+
|
12
|
+
let(:migration_name) { "create_users" }
|
13
|
+
let(:params) { [migration_name] }
|
14
|
+
let(:created_files) { Dir["#{root_path}/db/data_migrations/*_#{migration_name}.rb"] }
|
15
|
+
let(:migration_content) { File.readlines(created_files.first).reject(&:blank?).map(&:strip) }
|
16
|
+
|
17
|
+
before do
|
18
|
+
mkdir_p root_path.to_s
|
19
|
+
allow(DataMigration.config).to receive(:data_migrations_path).and_return("#{root_path}/db/data_migrations")
|
20
|
+
allow(Rails).to receive(:root).and_return(root_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
rm_rf root_path.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
it "creates a migration file" do
|
28
|
+
generator
|
29
|
+
|
30
|
+
expect(created_files).not_to be_empty
|
31
|
+
|
32
|
+
expect(migration_content).to include("class CreateUsers")
|
33
|
+
expect(migration_content).to include("def perform(**kwargs)")
|
34
|
+
expect(migration_content).to include("RSpec.describe CreateUsers, type: :data_migration do")
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when generate_spec is false" do
|
38
|
+
before do
|
39
|
+
DataMigration.config.generate_spec = false
|
40
|
+
end
|
41
|
+
|
42
|
+
it "does not add RSpec describe block" do
|
43
|
+
generator
|
44
|
+
|
45
|
+
expect(created_files).not_to be_empty
|
46
|
+
|
47
|
+
expect(migration_content).not_to include("RSpec.describe CreateUsers, type: :data_migration do")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "active_record"
|
2
|
+
|
3
|
+
require "data_migration"
|
4
|
+
|
5
|
+
require "support/rails_helpers"
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
include RailsHelpers
|
9
|
+
|
10
|
+
config.before(:suite) do
|
11
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
12
|
+
load File.expand_path("../fixtures/schema.rb", __FILE__)
|
13
|
+
end
|
14
|
+
|
15
|
+
config.before(:each) do
|
16
|
+
tables = ActiveRecord::Base.connection.tables
|
17
|
+
tables.each do |table|
|
18
|
+
ActiveRecord::Base.connection.execute("DELETE FROM #{table}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "simplecov"
|
2
|
+
SimpleCov.start do
|
3
|
+
add_filter "/spec/"
|
4
|
+
add_filter { |source_file| source_file.lines.count < 5 }
|
5
|
+
end
|
6
|
+
|
7
|
+
require "simplecov-cobertura"
|
8
|
+
SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
|
9
|
+
|
10
|
+
require "active_record"
|
11
|
+
|
12
|
+
require "data_migration"
|
13
|
+
|
14
|
+
Dir[File.expand_path("support/**/*.rb", __dir__)].each { |f| require_relative f }
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
include RailsHelpers
|
18
|
+
|
19
|
+
config.before(:suite) do
|
20
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
21
|
+
load File.expand_path("../fixtures/schema.rb", __FILE__)
|
22
|
+
end
|
23
|
+
|
24
|
+
config.before(:each) do
|
25
|
+
tables = ActiveRecord::Base.connection.tables
|
26
|
+
tables.each do |table|
|
27
|
+
ActiveRecord::Base.connection.execute("DELETE FROM #{table}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module RailsHelpers
|
2
|
+
class User < ActiveRecord::Base
|
3
|
+
self.table_name = "users"
|
4
|
+
end
|
5
|
+
|
6
|
+
def rails_root(destination_root = File.expand_path("../../tmp/rspec", __dir__))
|
7
|
+
Class.new do
|
8
|
+
def initialize(destination_root)
|
9
|
+
@destination_root = destination_root
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
@destination_root
|
14
|
+
end
|
15
|
+
|
16
|
+
def join(*args)
|
17
|
+
File.join(@destination_root, *args)
|
18
|
+
end
|
19
|
+
end.new(destination_root)
|
20
|
+
end
|
21
|
+
|
22
|
+
def rails_env(env = "development")
|
23
|
+
Class.new do
|
24
|
+
def initialize(env)
|
25
|
+
@env = env.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def development?
|
29
|
+
@env == "development"
|
30
|
+
end
|
31
|
+
|
32
|
+
def test?
|
33
|
+
@env == "test"
|
34
|
+
end
|
35
|
+
|
36
|
+
def production?
|
37
|
+
@env == "production"
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
@env
|
42
|
+
end
|
43
|
+
end.new(env)
|
44
|
+
end
|
45
|
+
|
46
|
+
def rails_logger
|
47
|
+
Class.new do
|
48
|
+
def info(message)
|
49
|
+
puts message
|
50
|
+
end
|
51
|
+
end.new
|
52
|
+
end
|
53
|
+
end
|
data/usr/bin/release.sh
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
e() {
|
6
|
+
GREEN='\033[0;32m'
|
7
|
+
NC='\033[0m'
|
8
|
+
echo -e "${GREEN}$1${NC}"
|
9
|
+
eval "$1"
|
10
|
+
}
|
11
|
+
|
12
|
+
e "bundle"
|
13
|
+
e "bundle exec rspec"
|
14
|
+
|
15
|
+
if [[ $(git diff --shortstat 2>/dev/null | tail -n1) != "" ]]; then
|
16
|
+
echo -e "\033[1;31mgit working directory not clean, please commit your changes first \033[0m"
|
17
|
+
exit 1
|
18
|
+
fi
|
19
|
+
|
20
|
+
GEM_NAME="data-migration"
|
21
|
+
VERSION=$(grep -Eo "VERSION\s*=\s*\".+\"" lib/data_migration.rb | grep -Eo "[0-9.]{5,}")
|
22
|
+
GEM_FILE="$GEM_NAME-$VERSION.gem"
|
23
|
+
|
24
|
+
e "gem build $GEM_NAME.gemspec"
|
25
|
+
|
26
|
+
echo "Ready to release $GEM_FILE $VERSION"
|
27
|
+
read -p "Continue? [Y/n] " answer
|
28
|
+
if [[ "$answer" != "Y" ]]; then
|
29
|
+
echo "Exiting"
|
30
|
+
exit 1
|
31
|
+
fi
|
32
|
+
|
33
|
+
e "gem push $GEM_FILE"
|
34
|
+
e "git tag $VERSION && git push --tags"
|
35
|
+
e "gh release create $VERSION --generate-notes"
|