pg_online_schema_change 0.1.0 → 0.4.0

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.
Binary file
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "thor"
2
4
 
3
5
  module PgOnlineSchemaChange
@@ -25,7 +27,7 @@ module PgOnlineSchemaChange
25
27
  def perform
26
28
  client_options = Struct.new(*options.keys.map(&:to_sym)).new(*options.values)
27
29
 
28
- PgOnlineSchemaChange.logger = client_options.verbose
30
+ PgOnlineSchemaChange.logger(verbose: client_options.verbose)
29
31
  PgOnlineSchemaChange::Orchestrate.run!(client_options)
30
32
  end
31
33
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pg"
2
4
 
3
5
  module PgOnlineSchemaChange
@@ -16,7 +18,9 @@ module PgOnlineSchemaChange
16
18
  @drop = options.drop
17
19
  @kill_backends = options.kill_backends
18
20
  @wait_time_for_lock = options.wait_time_for_lock
21
+
19
22
  handle_copy_statement(options.copy_statement)
23
+ handle_validations
20
24
 
21
25
  @connection = PG.connect(
22
26
  dbname: @dbname,
@@ -26,17 +30,19 @@ module PgOnlineSchemaChange
26
30
  port: @port,
27
31
  )
28
32
 
29
- raise Error, "Not a valid ALTER statement: #{@alter_statement}" unless Query.alter_statement?(@alter_statement)
30
-
31
- unless Query.same_table?(@alter_statement)
32
- raise Error "All statements should belong to the same table: #{@alter_statement}"
33
- end
34
-
35
33
  @table = Query.table(@alter_statement)
36
34
 
37
35
  PgOnlineSchemaChange.logger.debug("Connection established")
38
36
  end
39
37
 
38
+ def handle_validations
39
+ raise Error, "Not a valid ALTER statement: #{@alter_statement}" unless Query.alter_statement?(@alter_statement)
40
+
41
+ return if Query.same_table?(@alter_statement)
42
+
43
+ raise Error "All statements should belong to the same table: #{@alter_statement}"
44
+ end
45
+
40
46
  def handle_copy_statement(statement)
41
47
  return if statement.nil? || statement == ""
42
48
 
@@ -1,4 +1,6 @@
1
- FUNC_FIX_SERIAL_SEQUENCE = <<~SQL.freeze
1
+ # frozen_string_literal: true
2
+
3
+ FUNC_FIX_SERIAL_SEQUENCE = <<~SQL
2
4
  CREATE OR REPLACE FUNCTION fix_serial_sequence(_table regclass, _newtable text)
3
5
  RETURNS void AS
4
6
  $func$
@@ -35,7 +37,7 @@ FUNC_FIX_SERIAL_SEQUENCE = <<~SQL.freeze
35
37
  $func$ LANGUAGE plpgsql VOLATILE;
36
38
  SQL
37
39
 
38
- FUNC_CREATE_TABLE_ALL = <<~SQL.freeze
40
+ FUNC_CREATE_TABLE_ALL = <<~SQL
39
41
  CREATE OR REPLACE FUNCTION create_table_all(source_table text, newsource_table text)
40
42
  RETURNS void language plpgsql
41
43
  as $$
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PgOnlineSchemaChange
2
4
  module Helper
3
5
  def primary_key
@@ -15,7 +17,14 @@ module PgOnlineSchemaChange
15
17
  result = Store.send(:get, method)
16
18
  return result if result
17
19
 
18
- raise ArgumentError, "Method `#{method}` doesn't exist."
20
+ super
21
+ end
22
+
23
+ def respond_to_missing?(method_name, *args)
24
+ result = Store.send(:get, method)
25
+ return true if result
26
+
27
+ super
19
28
  end
20
29
  end
21
30
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "securerandom"
2
4
 
3
5
  module PgOnlineSchemaChange
4
6
  class Orchestrate
5
- SWAP_STATEMENT_TIMEOUT = "5s".freeze
7
+ SWAP_STATEMENT_TIMEOUT = "5s"
6
8
 
7
9
  extend Helper
8
10
 
@@ -21,12 +23,20 @@ module PgOnlineSchemaChange
21
23
  Query.run(client.connection, FUNC_FIX_SERIAL_SEQUENCE)
22
24
  Query.run(client.connection, FUNC_CREATE_TABLE_ALL)
23
25
 
26
+ setup_store
27
+ end
28
+
29
+ def setup_store
24
30
  # Set this early on to ensure their creation and cleanup (unexpected)
25
31
  # happens at all times. IOW, the calls from Store.get always return
26
32
  # the same value.
27
33
  Store.set(:old_primary_table, "pgosc_op_table_#{client.table}")
28
- Store.set(:audit_table, "pgosc_at_#{client.table}_#{random_string}")
29
- Store.set(:shadow_table, "pgosc_st_#{client.table}_#{random_string}")
34
+ Store.set(:audit_table, "pgosc_at_#{client.table}_#{pgosc_identifier}")
35
+ Store.set(:operation_type_column, "operation_type_#{pgosc_identifier}")
36
+ Store.set(:trigger_time_column, "trigger_time_#{pgosc_identifier}")
37
+ Store.set(:audit_table_pk, "at_#{pgosc_identifier}_id")
38
+ Store.set(:audit_table_pk_sequence, "#{audit_table}_#{audit_table_pk}_seq")
39
+ Store.set(:shadow_table, "pgosc_st_#{client.table}_#{pgosc_identifier}")
30
40
  end
31
41
 
32
42
  def run!(options)
@@ -37,10 +47,10 @@ module PgOnlineSchemaChange
37
47
 
38
48
  setup_audit_table!
39
49
  setup_trigger!
40
- setup_shadow_table!
41
- disable_vacuum!
42
- run_alter_statement!
43
- copy_data!
50
+ setup_shadow_table! # re-uses transaction with serializable
51
+ disable_vacuum! # re-uses transaction with serializable
52
+ run_alter_statement! # re-uses transaction with serializable
53
+ copy_data! # re-uses transaction with serializable
44
54
  run_analyze!
45
55
  replay_and_swap!
46
56
  run_analyze!
@@ -70,7 +80,7 @@ module PgOnlineSchemaChange
70
80
  reader = setup_signals!
71
81
  signal = reader.gets.chomp
72
82
 
73
- while !reader.closed? && IO.select([reader])
83
+ while !reader.closed? && IO.select([reader]) # rubocop:disable Lint/UnreachableLoop
74
84
  logger.info "Signal #{signal} received, cleaning up"
75
85
 
76
86
  client.connection.cancel
@@ -85,7 +95,7 @@ module PgOnlineSchemaChange
85
95
  logger.info("Setting up audit table", { audit_table: audit_table })
86
96
 
87
97
  sql = <<~SQL
88
- CREATE TABLE #{audit_table} (operation_type text, trigger_time timestamp, LIKE #{client.table});
98
+ CREATE TABLE #{audit_table} (#{audit_table_pk} SERIAL PRIMARY KEY, #{operation_type_column} text, #{trigger_time_column} timestamp, LIKE #{client.table});
89
99
  SQL
90
100
 
91
101
  Query.run(client.connection, sql)
@@ -109,13 +119,13 @@ module PgOnlineSchemaChange
109
119
  $$
110
120
  BEGIN
111
121
  IF ( TG_OP = 'INSERT') THEN
112
- INSERT INTO \"#{audit_table}\" select 'INSERT', now(), NEW.* ;
122
+ INSERT INTO \"#{audit_table}\" select nextval(\'#{audit_table_pk_sequence}\'), 'INSERT', clock_timestamp(), NEW.* ;
113
123
  RETURN NEW;
114
124
  ELSIF ( TG_OP = 'UPDATE') THEN
115
- INSERT INTO \"#{audit_table}\" select 'UPDATE', now(), NEW.* ;
125
+ INSERT INTO \"#{audit_table}\" select nextval(\'#{audit_table_pk_sequence}\'), 'UPDATE', clock_timestamp(), NEW.* ;
116
126
  RETURN NEW;
117
127
  ELSIF ( TG_OP = 'DELETE') THEN
118
- INSERT INTO \"#{audit_table}\" select 'DELETE', now(), OLD.* ;
128
+ INSERT INTO \"#{audit_table}\" select nextval(\'#{audit_table_pk_sequence}\'), 'DELETE', clock_timestamp(), OLD.* ;
119
129
  RETURN NEW;
120
130
  END IF;
121
131
  END;
@@ -132,18 +142,28 @@ module PgOnlineSchemaChange
132
142
  end
133
143
 
134
144
  def setup_shadow_table!
145
+ # re-uses transaction with serializable
146
+ # This ensures that all queries from here till copy_data run with serializable.
147
+ # This is to to ensure that once the trigger is added to the primay table
148
+ # and contents being copied into the shadow, after a delete all on audit table,
149
+ # any replaying of rows that happen next from audit table do not contain
150
+ # any duplicates. We are ensuring there are no race conditions between
151
+ # adding the trigger, till the copy ends, since they all happen in the
152
+ # same serializable transaction.
153
+ Query.run(client.connection, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", true)
135
154
  logger.info("Setting up shadow table", { shadow_table: shadow_table })
136
155
 
137
- Query.run(client.connection, "SELECT create_table_all('#{client.table}', '#{shadow_table}');")
156
+ Query.run(client.connection, "SELECT create_table_all('#{client.table}', '#{shadow_table}');", true)
138
157
 
139
158
  # update serials
140
- Query.run(client.connection, "SELECT fix_serial_sequence('#{client.table}', '#{shadow_table}');")
159
+ Query.run(client.connection, "SELECT fix_serial_sequence('#{client.table}', '#{shadow_table}');", true)
141
160
  end
142
161
 
143
- # Disabling vacuum to avoid any issues during the process
144
162
  def disable_vacuum!
145
- result = Query.storage_parameters_for(client, client.table) || ""
146
- primary_table_storage_parameters = Store.set(:primary_table_storage_parameters, result)
163
+ # re-uses transaction with serializable
164
+ # Disabling vacuum to avoid any issues during the process
165
+ result = Query.storage_parameters_for(client, client.table, true) || ""
166
+ Store.set(:primary_table_storage_parameters, result)
147
167
 
148
168
  logger.debug("Disabling vacuum on shadow and audit table",
149
169
  { shadow_table: shadow_table, audit_table: audit_table })
@@ -156,32 +176,38 @@ module PgOnlineSchemaChange
156
176
  autovacuum_enabled = false, toast.autovacuum_enabled = false
157
177
  );
158
178
  SQL
159
- Query.run(client.connection, sql)
179
+ Query.run(client.connection, sql, true)
160
180
  end
161
181
 
162
182
  def run_alter_statement!
183
+ # re-uses transaction with serializable
163
184
  statement = Query.alter_statement_for(client, shadow_table)
164
185
  logger.info("Running alter statement on shadow table",
165
186
  { shadow_table: shadow_table, parent_table: client.table })
166
- Query.run(client.connection, statement)
187
+ Query.run(client.connection, statement, true)
167
188
 
168
189
  Store.set(:dropped_columns_list, Query.dropped_columns(client))
169
190
  Store.set(:renamed_columns_list, Query.renamed_columns(client))
170
191
  end
171
192
 
172
- # Begin the process to copy data into copy table
173
- # depending on the size of the table, this can be a time
174
- # taking operation.
175
193
  def copy_data!
176
- logger.info("Copying contents..", { shadow_table: shadow_table, parent_table: client.table })
194
+ # re-uses transaction with serializable
195
+ # Begin the process to copy data into copy table
196
+ # depending on the size of the table, this can be a time
197
+ # taking operation.
198
+ logger.info("Clearing contents of audit table before copy..", { shadow_table: shadow_table, parent_table: client.table })
199
+ Query.run(client.connection, "DELETE FROM #{audit_table}", true)
177
200
 
201
+ logger.info("Copying contents..", { shadow_table: shadow_table, parent_table: client.table })
178
202
  if client.copy_statement
179
203
  query = format(client.copy_statement, shadow_table: shadow_table)
180
- return Query.run(client.connection, query)
204
+ return Query.run(client.connection, query, true)
181
205
  end
182
206
 
183
207
  sql = Query.copy_data_statement(client, shadow_table)
184
- Query.run(client.connection, sql)
208
+ Query.run(client.connection, sql, true)
209
+ ensure
210
+ Query.run(client.connection, "COMMIT;") # commit the serializable transaction
185
211
  end
186
212
 
187
213
  def replay_and_swap!
@@ -255,8 +281,10 @@ module PgOnlineSchemaChange
255
281
  Query.run(client.connection, sql)
256
282
  end
257
283
 
258
- private def random_string
259
- @random_string ||= SecureRandom.hex(3)
284
+ private
285
+
286
+ def pgosc_identifier
287
+ @pgosc_identifier ||= SecureRandom.hex(3)
260
288
  end
261
289
  end
262
290
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pg_query"
2
4
  require "pg"
3
5
 
@@ -5,7 +7,7 @@ module PgOnlineSchemaChange
5
7
  class Query
6
8
  extend Helper
7
9
 
8
- INDEX_SUFFIX = "_pgosc".freeze
10
+ INDEX_SUFFIX = "_pgosc"
9
11
  DROPPED_COLUMN_TYPE = :AT_DropColumn
10
12
  RENAMED_COLUMN_TYPE = :AT_RenameColumn
11
13
  LOCK_ATTEMPT = 4
@@ -15,28 +17,28 @@ module PgOnlineSchemaChange
15
17
  PgQuery.parse(query).tree.stmts.all? do |statement|
16
18
  statement.stmt.alter_table_stmt.instance_of?(PgQuery::AlterTableStmt) || statement.stmt.rename_stmt.instance_of?(PgQuery::RenameStmt)
17
19
  end
18
- rescue PgQuery::ParseError => e
20
+ rescue PgQuery::ParseError
19
21
  false
20
22
  end
21
23
 
22
24
  def same_table?(query)
23
- tables = PgQuery.parse(query).tree.stmts.map do |statement|
25
+ tables = PgQuery.parse(query).tree.stmts.filter_map do |statement|
24
26
  if statement.stmt.alter_table_stmt.instance_of?(PgQuery::AlterTableStmt)
25
27
  statement.stmt.alter_table_stmt.relation.relname
26
28
  elsif statement.stmt.rename_stmt.instance_of?(PgQuery::RenameStmt)
27
29
  statement.stmt.rename_stmt.relation.relname
28
30
  end
29
- end.compact
31
+ end
30
32
 
31
33
  tables.uniq.count == 1
32
- rescue PgQuery::ParseError => e
34
+ rescue PgQuery::ParseError
33
35
  false
34
36
  end
35
37
 
36
38
  def table(query)
37
- from_rename_statement = PgQuery.parse(query).tree.stmts.map do |statement|
39
+ from_rename_statement = PgQuery.parse(query).tree.stmts.filter_map do |statement|
38
40
  statement.stmt.rename_stmt&.relation&.relname
39
- end.compact[0]
41
+ end[0]
40
42
  PgQuery.parse(query).tables[0] || from_rename_statement
41
43
  end
42
44
 
@@ -48,7 +50,7 @@ module PgOnlineSchemaChange
48
50
  connection.async_exec("BEGIN;")
49
51
 
50
52
  result = connection.async_exec(query, &block)
51
- rescue Exception
53
+ rescue Exception # rubocop:disable Lint/RescueException
52
54
  connection.cancel if connection.transaction_status != PG::PQTRANS_IDLE
53
55
  connection.block
54
56
  logger.info("Exception raised, rolling back query", { rollback: true, query: query })
@@ -60,7 +62,7 @@ module PgOnlineSchemaChange
60
62
  result
61
63
  end
62
64
 
63
- def table_columns(client, table = nil)
65
+ def table_columns(client, table = nil, reuse_trasaction = false)
64
66
  sql = <<~SQL
65
67
  SELECT attname as column_name, format_type(atttypid, atttypmod) as type, attnum as column_position FROM pg_attribute
66
68
  WHERE attrelid = \'#{table || client.table}\'::regclass AND attnum > 0 AND NOT attisdropped
@@ -68,7 +70,7 @@ module PgOnlineSchemaChange
68
70
  SQL
69
71
  mapped_columns = []
70
72
 
71
- run(client.connection, sql) do |result|
73
+ run(client.connection, sql, reuse_trasaction) do |result|
72
74
  mapped_columns = result.map do |row|
73
75
  row["column_name_regular"] = row["column_name"]
74
76
  row["column_name"] = client.connection.quote_ident(row["column_name"])
@@ -144,11 +146,11 @@ module PgOnlineSchemaChange
144
146
  end
145
147
 
146
148
  references.map do |row|
147
- if row["definition"].end_with?("NOT VALID")
148
- add_statement = "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]};"
149
- else
150
- add_statement = "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]} NOT VALID;"
151
- end
149
+ add_statement = if row["definition"].end_with?("NOT VALID")
150
+ "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]};"
151
+ else
152
+ "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]} NOT VALID;"
153
+ end
152
154
 
153
155
  drop_statement = "ALTER TABLE #{row["table_on"]} DROP CONSTRAINT #{row["constraint_name"]};"
154
156
 
@@ -210,13 +212,13 @@ module PgOnlineSchemaChange
210
212
  columns.first
211
213
  end
212
214
 
213
- def storage_parameters_for(client, table)
215
+ def storage_parameters_for(client, table, reuse_trasaction = false)
214
216
  query = <<~SQL
215
217
  SELECT array_to_string(reloptions, ',') as params FROM pg_class WHERE relname=\'#{table}\';
216
218
  SQL
217
219
 
218
220
  columns = []
219
- run(client.connection, query) do |result|
221
+ run(client.connection, query, reuse_trasaction) do |result|
220
222
  columns = result.map { |row| row["params"] }
221
223
  end
222
224
 
@@ -266,8 +268,8 @@ module PgOnlineSchemaChange
266
268
  run(client.connection, query, true)
267
269
  end
268
270
 
269
- def copy_data_statement(client, shadow_table)
270
- select_columns = table_columns(client).map do |entry|
271
+ def copy_data_statement(client, shadow_table, reuse_trasaction = false)
272
+ select_columns = table_columns(client, client.table, reuse_trasaction).map do |entry|
271
273
  entry["column_name_regular"]
272
274
  end
273
275
 
@@ -291,7 +293,7 @@ module PgOnlineSchemaChange
291
293
  client.connection.quote_ident(select_column)
292
294
  end
293
295
 
294
- sql = <<~SQL
296
+ <<~SQL
295
297
  INSERT INTO #{shadow_table}(#{insert_into_columns.join(", ")})
296
298
  SELECT #{select_columns.join(", ")}
297
299
  FROM ONLY #{client.table}
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
4
+
1
5
  module PgOnlineSchemaChange
2
6
  class Replay
3
7
  extend Helper
@@ -5,7 +9,6 @@ module PgOnlineSchemaChange
5
9
  class << self
6
10
  PULL_BATCH_COUNT = 1000
7
11
  DELTA_COUNT = 20
8
- RESERVED_COLUMNS = %w[operation_type trigger_time].freeze
9
12
 
10
13
  # This, picks PULL_BATCH_COUNT rows by primary key from audit_table,
11
14
  # replays it on the shadow_table. Once the batch is done,
@@ -25,7 +28,7 @@ module PgOnlineSchemaChange
25
28
 
26
29
  def rows_to_play(reuse_trasaction = false)
27
30
  select_query = <<~SQL
28
- SELECT * FROM #{audit_table} ORDER BY #{primary_key} LIMIT #{PULL_BATCH_COUNT};
31
+ SELECT * FROM #{audit_table} ORDER BY #{audit_table_pk} LIMIT #{PULL_BATCH_COUNT};
29
32
  SQL
30
33
 
31
34
  rows = []
@@ -34,6 +37,10 @@ module PgOnlineSchemaChange
34
37
  rows
35
38
  end
36
39
 
40
+ def reserved_columns
41
+ @reserved_columns ||= [trigger_time_column, operation_type_column, audit_table_pk]
42
+ end
43
+
37
44
  def play!(rows, reuse_trasaction = false)
38
45
  logger.info("Replaying rows, count: #{rows.size}")
39
46
 
@@ -44,7 +51,7 @@ module PgOnlineSchemaChange
44
51
 
45
52
  # Remove audit table cols, since we will be
46
53
  # re-mapping them for inserts and updates
47
- RESERVED_COLUMNS.each do |col|
54
+ reserved_columns.each do |col|
48
55
  new_row.delete(col)
49
56
  end
50
57
 
@@ -73,7 +80,7 @@ module PgOnlineSchemaChange
73
80
  client.connection.escape_string(value)
74
81
  end
75
82
 
76
- case row["operation_type"]
83
+ case row[operation_type_column]
77
84
  when "INSERT"
78
85
  values = new_row.map { |_, val| "'#{val}'" }.join(",")
79
86
 
@@ -110,13 +117,15 @@ module PgOnlineSchemaChange
110
117
  Query.run(client.connection, to_be_replayed.join, reuse_trasaction)
111
118
 
112
119
  # Delete items from the audit now that are replayed
113
- if to_be_deleted_rows.count >= 1
114
- delete_query = <<~SQL
115
- DELETE FROM #{audit_table} WHERE #{primary_key} IN (#{to_be_deleted_rows.join(",")})
116
- SQL
117
- Query.run(client.connection, delete_query, reuse_trasaction)
118
- end
120
+ return unless to_be_deleted_rows.count >= 1
121
+
122
+ delete_query = <<~SQL
123
+ DELETE FROM #{audit_table} WHERE #{primary_key} IN (#{to_be_deleted_rows.join(",")})
124
+ SQL
125
+ Query.run(client.connection, delete_query, reuse_trasaction)
119
126
  end
120
127
  end
121
128
  end
122
129
  end
130
+
131
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
@@ -1,17 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pg_query"
2
4
  require "pg"
3
5
 
4
6
  module PgOnlineSchemaChange
5
7
  class Store
6
8
  class << self
7
- @@object = {}
9
+ @object = {}
8
10
 
9
11
  def get(key)
10
- @@object[key.to_s] || @@object[key.to_sym]
12
+ @object ||= {}
13
+ @object[key.to_s] || @object[key.to_sym]
11
14
  end
12
15
 
13
16
  def set(key, value)
14
- @@object[key.to_sym] = value
17
+ @object ||= {}
18
+ @object[key.to_sym] = value
15
19
  end
16
20
  end
17
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgOnlineSchemaChange
4
- VERSION = "0.1.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -18,16 +18,12 @@ module PgOnlineSchemaChange
18
18
  class CountBelowDelta < StandardError; end
19
19
  class AccessExclusiveLockNotAcquired < StandardError; end
20
20
 
21
- def self.logger=(verbose)
22
- @@logger ||= begin
23
- logger = Ougai::Logger.new($stdout)
24
- logger.level = verbose ? Ougai::Logger::TRACE : Ougai::Logger::INFO
25
- logger.with_fields = { version: PgOnlineSchemaChange::VERSION }
26
- logger
27
- end
28
- end
29
-
30
- def self.logger
31
- @@logger
21
+ def self.logger(verbose: false)
22
+ @logger ||= begin
23
+ logger = Ougai::Logger.new($stdout)
24
+ logger.level = verbose ? Ougai::Logger::TRACE : Ougai::Logger::INFO
25
+ logger.with_fields = { version: PgOnlineSchemaChange::VERSION }
26
+ logger
27
+ end
32
28
  end
33
29
  end
@@ -0,0 +1,28 @@
1
+ export VERSION=$1
2
+ echo "VERSION: ${VERSION}"
3
+
4
+ echo "=== Pushing tags to github ===="
5
+ git tag v$VERSION
6
+ git push origin --tags
7
+
8
+ echo "=== Building Gem ===="
9
+ gem build pg_online_schema_change.gemspec
10
+
11
+ echo "=== Pushing gem ===="
12
+ gem push pg_online_schema_change-$VERSION.gem
13
+
14
+ echo "=== Sleeping for 5s ===="
15
+ sleep 5
16
+
17
+ echo "=== Building Image ===="
18
+ docker build . --build-arg VERSION=$VERSION -t shayonj/pg-osc:$VERSION
19
+
20
+ echo "=== Tagging Image ===="
21
+ docker image tag shayonj/pg-osc:$VERSION shayonj/pg-osc:latest
22
+
23
+ echo "=== Pushing Image ===="
24
+ docker push shayonj/pg-osc:$VERSION
25
+ docker push shayonj/pg-osc:latest
26
+
27
+ echo "=== Cleaning up ===="
28
+ rm pg_online_schema_change-$VERSION.gem