schema-evolution-manager 0.9.24 → 0.9.25
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +44 -13
- data/bin/sem-apply +13 -9
- data/lib/schema-evolution-manager.rb +2 -0
- data/lib/schema-evolution-manager/args.rb +4 -2
- data/lib/schema-evolution-manager/ask.rb +16 -3
- data/lib/schema-evolution-manager/connection_data.rb +68 -0
- data/lib/schema-evolution-manager/db.rb +17 -5
- data/lib/schema-evolution-manager/sem_version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb6f13a7ee9457b87783a853e81388457ae38297
|
4
|
+
data.tar.gz: 6450ab24e9348ee9e21881256ba26081e97ddd8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41f4a519bcb140564587b6f2faa2749be390523f86cac6520bb0b0f0af08ef213ec8b58ed58fbd74f607e92e4323f5ecb7ba44a1fd1f04e4602d834e5a6ffcb7
|
7
|
+
data.tar.gz: 9945e8b898c84a54ea1db077d862142cfa957b062d5d03f4268fb020f65b2b0f4cef1144cc123adc99a022ae8ed05c19f8ec7bc0b910202e7deb522074719840
|
data/README.md
CHANGED
@@ -103,8 +103,7 @@ manage and release schema changes independent of application changes:
|
|
103
103
|
|
104
104
|
## Talks
|
105
105
|
|
106
|
-
First presented at PGDay NYC 2013
|
107
|
-
https://speakerdeck.com/mbryzek/schema-evolutions-at-gilt-groupe
|
106
|
+
First presented at [PGDay NYC 2013](https://speakerdeck.com/mbryzek/schema-evolutions-at-gilt-groupe)
|
108
107
|
|
109
108
|
## Dependencies
|
110
109
|
|
@@ -116,19 +115,25 @@ https://speakerdeck.com/mbryzek/schema-evolutions-at-gilt-groupe
|
|
116
115
|
|
117
116
|
- plpgsql must be available in the database. If needed you can:
|
118
117
|
|
119
|
-
|
120
|
-
[http://www.postgresql.org/docs/8.4/static/app-createlang.html]
|
118
|
+
[createlang plpgsql template1](http://www.postgresql.org/docs/8.4/static/app-createlang.html)
|
121
119
|
|
122
120
|
- Git: Designed to use git for history (all versions since 1.7).
|
123
121
|
|
124
122
|
## Installation
|
125
123
|
|
126
|
-
|
127
|
-
cd schema-evolution-manager
|
128
|
-
git checkout 0.9.24
|
129
|
-
ruby ./configure.rb
|
130
|
-
sudo ./install.rb
|
124
|
+
There are two ways to install schema evolution manager:
|
131
125
|
|
126
|
+
1. Direct install using ruby (no dependency on ruby gems)
|
127
|
+
|
128
|
+
git clone git://github.com/mbryzek/schema-evolution-manager.git
|
129
|
+
cd schema-evolution-manager
|
130
|
+
git checkout 0.9.25
|
131
|
+
ruby ./configure.rb
|
132
|
+
sudo ./install.rb
|
133
|
+
|
134
|
+
2. If you have ruby gems:
|
135
|
+
|
136
|
+
gem install schema-evolution-manager
|
132
137
|
|
133
138
|
## Upgrading
|
134
139
|
|
@@ -207,6 +212,24 @@ You will likely see a number of create table statements (see data model section
|
|
207
212
|
which tells you that if you apply these changes, that sql script will be applied to the sample db
|
208
213
|
|
209
214
|
|
215
|
+
### Specifying database password
|
216
|
+
|
217
|
+
There are two recommended ways in which to pass user passwords to psql:
|
218
|
+
|
219
|
+
1. Create a [~/.pgpass
|
220
|
+
file](http://www.postgresql.org/docs/9.4/static/libpq-pgpass.html)
|
221
|
+
with the appropriate credentials</li>
|
222
|
+
|
223
|
+
2. Specify a [--password] flag when running sem-apply. You will
|
224
|
+
then be prompted to enter your password once. sem will create a
|
225
|
+
temporary file to store your password, using that file during the
|
226
|
+
duration of the command and ensuring the file is deleted after sem
|
227
|
+
completed.
|
228
|
+
|
229
|
+
Example:
|
230
|
+
|
231
|
+
sem-apply --url postgresql://postgres@localhost/sample --password
|
232
|
+
|
210
233
|
### Apply the changes
|
211
234
|
|
212
235
|
sem-apply --url postgresql://postgres@localhost/sample
|
@@ -262,7 +285,7 @@ For details on these tables, see scripts/*sql where the tables themselves are de
|
|
262
285
|
## PLPGSQL Utilities
|
263
286
|
|
264
287
|
We've included a copy of the schema conventions we practice at
|
265
|
-
|
288
|
+
[Gilt Groupe](CONVENTIONS.md). There are also a number of utility plpgsql
|
266
289
|
functions to help developers apply these conventions in a systematic way.
|
267
290
|
|
268
291
|
The helpers are defined in
|
@@ -283,6 +306,7 @@ and utilities in practice.
|
|
283
306
|
- sem-add: Adds a database upgrade script
|
284
307
|
- sem-dist: Create a distribution tar.gz file containing schema upgrade scripts
|
285
308
|
- sem-apply: Apply any deltas from a distribution tarball to a particular database
|
309
|
+
- sem-baseline: Add any migration scripts to the schema tables without actually applying them. See [Migrating](#migrating)
|
286
310
|
|
287
311
|
|
288
312
|
## Attributes supported in sql migration scripts
|
@@ -311,15 +335,22 @@ Currently supported attributes:
|
|
311
335
|
- -- sem.attribute.transaction = none
|
312
336
|
- -- sem.attribute.transaction = single
|
313
337
|
|
338
|
+
## Migrating
|
339
|
+
|
340
|
+
In some cases you may be migrating from no schema evolutions, or another schema evolution model.
|
314
341
|
|
315
|
-
|
342
|
+
For these cases, sem provides a 'baseline' command.
|
316
343
|
|
317
|
-
|
344
|
+
Current workflow:
|
318
345
|
|
346
|
+
1. sem-add your current schema
|
347
|
+
1. Either via a database dump
|
348
|
+
1. Or by sem-adding existing DB scripts
|
349
|
+
1. Use sem-baseline to bootstrap the sem tables and add existing schema files to sem's migration table without actually applying them
|
319
350
|
|
320
351
|
## License
|
321
352
|
|
322
|
-
Copyright 2013-
|
353
|
+
Copyright 2013-2016 Gilt Groupe, Inc.
|
323
354
|
|
324
355
|
Licensed under the Apache License, Version 2.0 (the "License");
|
325
356
|
you may not use this file except in compliance with the License.
|
data/bin/sem-apply
CHANGED
@@ -8,24 +8,28 @@
|
|
8
8
|
# sem-apply --host <database host> --user <db user> --name <db name>
|
9
9
|
#
|
10
10
|
# == Examples
|
11
|
-
# sem-apply --url postgresql://postgres@localhost/sample
|
11
|
+
# sem-apply --url postgresql://postgres@localhost:port/sample
|
12
12
|
# sem-apply --host localhost --user web --name test
|
13
13
|
#
|
14
|
+
require 'tempfile'
|
14
15
|
|
15
16
|
load File.join(File.dirname(__FILE__), 'sem-config')
|
16
17
|
|
17
|
-
args = SchemaEvolutionManager::Args.from_stdin(:optional => %w(url host port name user dry_run))
|
18
|
+
args = SchemaEvolutionManager::Args.from_stdin(:optional => %w(url host port name user dry_run password))
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
password = if args.password
|
21
|
+
SchemaEvolutionManager::Ask.for_password("Please enter the database user password")
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
util = SchemaEvolutionManager::ApplyUtil.new(db, :dry_run => dry_run)
|
25
|
-
|
26
|
-
puts "Upgrading schema for #{db.url}"
|
26
|
+
db = SchemaEvolutionManager::Db.from_args(args, :password => password)
|
27
|
+
util = SchemaEvolutionManager::ApplyUtil.new(db, :dry_run => args.dry_run || false)
|
27
28
|
|
28
29
|
begin
|
30
|
+
db.bootstrap!
|
31
|
+
|
32
|
+
puts "Upgrading schema for #{db.url}"
|
29
33
|
count = util.apply!("./scripts")
|
30
34
|
if count == 0
|
31
35
|
puts " All scripts have been previously applied"
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'pathname'
|
3
|
+
require 'io/console'
|
3
4
|
|
4
5
|
dir = File.dirname(__FILE__)
|
5
6
|
lib_dir = File.join(dir, "schema-evolution-manager")
|
@@ -16,6 +17,7 @@ load File.join(lib_dir, 'ask.rb')
|
|
16
17
|
load File.join(lib_dir, 'version.rb')
|
17
18
|
load File.join(lib_dir, 'args.rb')
|
18
19
|
load File.join(lib_dir, 'scripts.rb')
|
20
|
+
load File.join(lib_dir, 'connection_data.rb')
|
19
21
|
load File.join(lib_dir, 'db.rb')
|
20
22
|
load File.join(lib_dir, 'apply_util.rb')
|
21
23
|
load File.join(lib_dir, 'baseline_util.rb')
|
@@ -19,13 +19,14 @@ module SchemaEvolutionManager
|
|
19
19
|
}
|
20
20
|
|
21
21
|
FLAGS_NO_ARGUMENTS = {
|
22
|
+
:password => "Prompt user to enter password for the database user. Password is stored for the duration of the process",
|
22
23
|
:dry_run => "Include flag to echo commands that will run without actually executing them",
|
23
24
|
:help => "Display help",
|
24
25
|
:verbose => "Enable verbose logging of all system calls",
|
25
26
|
}
|
26
27
|
end
|
27
28
|
|
28
|
-
attr_reader :artifact_name, :host, :port, :name, :prefix, :url, :user, :dir, :dry_run, :tag
|
29
|
+
attr_reader :artifact_name, :host, :port, :name, :prefix, :url, :user, :dir, :dry_run, :tag, :password
|
29
30
|
|
30
31
|
# args: Actual string arguments
|
31
32
|
# :required => list of parameters that are required
|
@@ -52,7 +53,7 @@ module SchemaEvolutionManager
|
|
52
53
|
|
53
54
|
@artifact_name = found_arguments.delete(:artifact_name)
|
54
55
|
@host = found_arguments.delete(:host)
|
55
|
-
@port = found_arguments.delete(:port)
|
56
|
+
@port = found_arguments.delete(:port)
|
56
57
|
@name = found_arguments.delete(:name)
|
57
58
|
@prefix = found_arguments.delete(:prefix)
|
58
59
|
@url = found_arguments.delete(:url)
|
@@ -61,6 +62,7 @@ module SchemaEvolutionManager
|
|
61
62
|
@tag = found_arguments.delete(:tag)
|
62
63
|
|
63
64
|
@dry_run = found_arguments.delete(:dry_run)
|
65
|
+
@password = found_arguments.delete(:password)
|
64
66
|
@help = found_arguments.delete(:help)
|
65
67
|
@verbose = found_arguments.delete(:verbose)
|
66
68
|
|
@@ -7,8 +7,13 @@ module SchemaEvolutionManager
|
|
7
7
|
TRUE_STRINGS = ['y', 'yes'] unless defined?(TRUE_STRINGS)
|
8
8
|
|
9
9
|
# Asks the user a question. Expects a string back.
|
10
|
+
#
|
11
|
+
# @param default: A default value
|
12
|
+
# @param echo: If true (the default), we echo what the user types
|
13
|
+
# to the screen. If false, we do NOT echo.
|
10
14
|
def Ask.for_string(message, opts={})
|
11
15
|
default = opts.delete(:default)
|
16
|
+
echo = opts[:echo].nil? ? true : opts.delete(:echo)
|
12
17
|
Preconditions.assert_empty_opts(opts)
|
13
18
|
|
14
19
|
final_message = message.dup
|
@@ -19,7 +24,7 @@ module SchemaEvolutionManager
|
|
19
24
|
value = nil
|
20
25
|
while value.to_s == ""
|
21
26
|
print final_message
|
22
|
-
value = get_input.strip
|
27
|
+
value = get_input(echo).strip
|
23
28
|
if value.to_s == "" && default
|
24
29
|
value = default.to_s.strip
|
25
30
|
end
|
@@ -34,9 +39,17 @@ module SchemaEvolutionManager
|
|
34
39
|
TRUE_STRINGS.include?(value.downcase)
|
35
40
|
end
|
36
41
|
|
42
|
+
def Ask.for_password(message)
|
43
|
+
Ask.for_string(message, :echo => false)
|
44
|
+
end
|
45
|
+
|
37
46
|
# here to help with tests
|
38
|
-
def Ask.get_input
|
39
|
-
|
47
|
+
def Ask.get_input(echo)
|
48
|
+
if echo
|
49
|
+
STDIN.gets
|
50
|
+
else
|
51
|
+
STDIN.noecho(&:gets)
|
52
|
+
end
|
40
53
|
end
|
41
54
|
|
42
55
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SchemaEvolutionManager
|
2
|
+
|
3
|
+
class ConnectionData
|
4
|
+
|
5
|
+
DEFAULT_PORT = 5432 unless defined?(DEFAULT_PORT)
|
6
|
+
|
7
|
+
attr_reader :host, :name, :port, :user
|
8
|
+
|
9
|
+
def initialize(host, name, opts={})
|
10
|
+
@host = host
|
11
|
+
@name = name
|
12
|
+
|
13
|
+
port = opts.delete(:port).to_s
|
14
|
+
if port.to_s.empty?
|
15
|
+
@port = DEFAULT_PORT
|
16
|
+
else
|
17
|
+
@port = port.to_i
|
18
|
+
end
|
19
|
+
Preconditions.check_argument(@port > 0, "Port must be > 0")
|
20
|
+
|
21
|
+
@user = opts.delete(:user)
|
22
|
+
Preconditions.assert_empty_opts(opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a valid pgpass line entry representing this connection.
|
26
|
+
#
|
27
|
+
# @param password: Optional password to include in the connection string
|
28
|
+
def pgpass(password=nil)
|
29
|
+
[@host, @port, @name, @user, password.to_s].join(":")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Parses a connection string into a ConnectionData instance. You
|
33
|
+
# will get an error if the URL could not be parsed.
|
34
|
+
#
|
35
|
+
# @param url e.g. postgres://user1@db.com:5553/test_db
|
36
|
+
def ConnectionData.parse_url(url)
|
37
|
+
protocol, rest = url.split("//", 2)
|
38
|
+
if rest.nil?
|
39
|
+
raise "Invalid url[%s]. Expected to start with postgres://" % url
|
40
|
+
end
|
41
|
+
|
42
|
+
lead, name = rest.split("/", 2)
|
43
|
+
if name.nil?
|
44
|
+
raise "Invalid url[%s]. Missing database name" % url
|
45
|
+
end
|
46
|
+
|
47
|
+
parts = lead.split("@", 2)
|
48
|
+
if parts.size == 2
|
49
|
+
user = parts[0]
|
50
|
+
db_host = parts[1]
|
51
|
+
else
|
52
|
+
user = nil
|
53
|
+
db_host = lead
|
54
|
+
end
|
55
|
+
|
56
|
+
host, port = db_host.split(":", 2)
|
57
|
+
if port
|
58
|
+
if port.to_i.to_s != port
|
59
|
+
raise "Invalid url[%s]. Expected database port[%s] to be an integer" % [url, port]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
ConnectionData.new(host, name, :user => user, :port => port)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -4,8 +4,16 @@ module SchemaEvolutionManager
|
|
4
4
|
|
5
5
|
attr_reader :url
|
6
6
|
|
7
|
-
def initialize(url)
|
7
|
+
def initialize(url, opts={})
|
8
8
|
@url = Preconditions.check_not_blank(url, "url cannot be blank")
|
9
|
+
password = opts.delete(:password)
|
10
|
+
Preconditions.assert_empty_opts(opts)
|
11
|
+
connection_data = ConnectionData.parse_url(@url)
|
12
|
+
|
13
|
+
if password
|
14
|
+
ENV['PGPASSFILE'] = Library.write_to_temp_file(connection_data.pgpass(password))
|
15
|
+
puts "Created PGPASSFILE=%s" % ENV['PGPASSFILE']
|
16
|
+
end
|
9
17
|
end
|
10
18
|
|
11
19
|
# Installs schema_evolution_manager. Automatically upgrades schema_evolution_manager.
|
@@ -83,14 +91,18 @@ module SchemaEvolutionManager
|
|
83
91
|
Db.from_args(args)
|
84
92
|
end
|
85
93
|
|
86
|
-
|
94
|
+
# @param password: Optional password to use when connecting to the database.
|
95
|
+
def Db.from_args(args, opts={})
|
87
96
|
Preconditions.assert_class(args, Args)
|
97
|
+
password = opts.delete(:password)
|
98
|
+
Preconditions.assert_empty_opts(opts)
|
99
|
+
|
88
100
|
if args.url
|
89
|
-
Db.new(args.url)
|
101
|
+
Db.new(args.url, :password => password)
|
90
102
|
else
|
91
|
-
base = "%s:%s/%s" % [args.host || "localhost", args.port ||
|
103
|
+
base = "%s:%s/%s" % [args.host || "localhost", args.port || ConnectionData::DEFAULT_PORT, args.name]
|
92
104
|
url = args.user ? "%s@%s" % [args.user, base] : base
|
93
|
-
Db.new("postgres://" + url)
|
105
|
+
Db.new("postgres://" + url, :password => password)
|
94
106
|
end
|
95
107
|
end
|
96
108
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schema-evolution-manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.25
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bryzek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: '["Michael Bryzek"]'
|
14
14
|
email: mbryzek@alum.mit.edu
|
@@ -35,6 +35,7 @@ files:
|
|
35
35
|
- lib/schema-evolution-manager/args.rb
|
36
36
|
- lib/schema-evolution-manager/ask.rb
|
37
37
|
- lib/schema-evolution-manager/baseline_util.rb
|
38
|
+
- lib/schema-evolution-manager/connection_data.rb
|
38
39
|
- lib/schema-evolution-manager/db.rb
|
39
40
|
- lib/schema-evolution-manager/install_template.rb
|
40
41
|
- lib/schema-evolution-manager/library.rb
|