schema-evolution-manager 0.9.24 → 0.9.25
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.
- 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
|