pgbundle 0.0.15 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 11226f3bd159341b9b82f4ee683782e7f26fa677
4
- data.tar.gz: 6f1eebc8ceaad7f92fe56a8341539d04018ad28d
3
+ metadata.gz: c6ade210a8dde8dcdbdacc39434ba2fa11ff0e92
4
+ data.tar.gz: 7e364dac3b3dfa809a770f39661d4c32bf46f7bd
5
5
  SHA512:
6
- metadata.gz: ff317e2a95f75412ea40c79cc8bda263a696907eb11c53e508cb58f5d28d334779a2307567ab4f185db40609b7652b9acdd3bd86935999fbad9c89a675086b5e
7
- data.tar.gz: bfedab2f6a2ec6f8d1f72fc6fe6580dc73be90bb95fe074b1825853a146964ca9838b2d5afff6dc8d2e7852deeb7fea98b09f8bd74272b1b5fe526396eeb0a5d
6
+ metadata.gz: 294bafbc788b72b7c9cbc621ea447d951c028b99dc2e791d6fef5730d45a6eb2037b278408c75d6f557fb33498651ac34b55e40f0ca8d904ab50d8957c456e63
7
+ data.tar.gz: 186fbf3e1cf15be94c2b72eff4cf00e34b8dec4dceaa067638abe54e94f758961ccb836b1cc8e484e0ce29af14d97bc7ba8a5a1727228b34db3a4450bf270e34
data/.travis.yml CHANGED
@@ -7,5 +7,13 @@ rvm:
7
7
  matrix:
8
8
  allow_failures:
9
9
  - rvm: rbx-2
10
- addons:
11
- postgresql: "9.3"
10
+ before_install:
11
+ - wget https://gist.github.com/petere/5893799/raw/apt.postgresql.org.sh
12
+ - wget https://gist.github.com/petere/6023944/raw/pg-travis-test.sh
13
+ - sudo sh ./apt.postgresql.org.sh
14
+ env:
15
+ - PGVERSION=9.1
16
+ - PGVERSION=9.2
17
+ - PGVERSION=9.3
18
+ - PGVERSION=9.4
19
+ script: bash ./cibuild.sh
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.org/adjust/pgbundle.svg?branch=master)](https://travis-ci.org/adjust/pgbundle)
2
+
1
3
  # pgbundle
2
4
 
3
5
  bundling postgres extension
@@ -11,35 +13,158 @@ bundling postgres extension
11
13
  define your dependent postgres extensions in a Pgfile like this:
12
14
 
13
15
  ```
14
- #Pgfile
16
+ # Pgfile
15
17
 
16
18
  database 'my_database', host: 'my.db.server', use_sudo: true, system_user: 'postgres'
17
19
 
18
20
  pgx 'hstore'
19
21
  pgx 'my_extension', '1.0.2', github: me/my_extension
20
22
  pgx 'my_other_extionsion', :git => 'https://github.com/me/my_other_extionsion.git'
21
- pgx 'my_ltree_dependend_extension', github: me/my_ltree_dependend_extension, require: 'ltree'
23
+ pgx 'my_ltree_dependend_extension', github: me/my_ltree_dependend_extension, requires: 'ltree'
24
+ ```
25
+
26
+ **database**
27
+
28
+ `database` defines on which database(s) the extensions should be installed. The first
29
+ argument is the database name the additional options may specify your setup but
30
+ come with reasonable default values.
31
+
32
+ option | default | desciption
33
+ ------- | ------- | -----------
34
+ user | 'postgres' | the database user (needs privilege to `CREATE EXTENSION`)
35
+ host | 'localhost' | the database host (needs to be accessible from where `pgbundle` runs)
36
+ use_sudo | false | if true use `sudo` to run `make install` if needed
37
+ system_user | 'postgres' | the (os) system user that is allowed to install an extension (through make)
38
+ port | 5432 | the database port
39
+ force_ssh | false | run commands via ssh even if host is `localhost`
40
+ slave | false | defines if the database runs as a read-only slave thus skips any `CREATE` command
41
+
42
+ **pgx**
43
+
44
+ The `pgx` command defines you actual Extension. The first argument specifies the Extension name,
45
+ the second optional parameter defines the required Version. If the Extension is not yet
46
+ installed on the server you may wish to define how `pgbundle` can find it's source to build
47
+ and install it. And which Extensions may be required
48
+
49
+ option | description
50
+ ------ | -----------
51
+ git | any git repository pgbundle can clone from
52
+ github | any github repository in the form `user/repository`
53
+ branch | an optional branch name for git or github sources defaults to `master`
54
+ requires | an optional extension that the extension depends on
55
+ path | any absolute or relative local path e.g. './foo/bar'
56
+ pgxn | any repository available on http://pgxn.org/
57
+
58
+
59
+ Some Extensions may require other Extensions to allow `pgbundle` to resolve dependencies
60
+ and install it in the right order you can define them with `requires`.
61
+ If the required Extension is not yet available on the target server or the Extension
62
+ requires a specific Version you should define it as well.
63
+ E.g.
64
+
65
+ ```
66
+ # Pgfile
67
+
68
+ database ...
69
+
70
+ pgx 'foo', '0.1.2', github: me/foo
71
+
72
+ # set foo as dependency for bar
73
+ pgx 'bar', '1.2.3', github: me/bar, requires: 'foo'
74
+
75
+ # set bar and boo as dependency for baz
76
+ # will automatically set foo as dependency as well
77
+ pgx 'baz', '0.2.3', github: me/baz, requires: ['bar', 'boo']
78
+ ```
79
+
80
+ ## pgbundle commands
81
+
82
+ `pgbundle` comes with four commands. If the optional `pgfile` is not given it assumes
83
+ to find a file named `Pgfile` in the current directory.
84
+
85
+ **check**
86
+
87
+ checks availability of required extensions.
88
+
89
+ ```
90
+ pgbundle check [pgfile]
22
91
  ```
23
92
 
24
- ### install your extension
93
+ `check` does not change anything on your system, it only checks which
94
+ of your specified extensions are available and which are missing.
95
+ It returns with exitcode `1` if any Extension is missing and `0` otherwise.
25
96
 
26
- pgbundle install
27
97
 
28
- installs the extensions and dependencies on your database server
98
+ **install**
29
99
 
30
- ### check your dependencies
100
+ installs extensions
31
101
 
32
- pgbundle check
102
+ ```
103
+ pgbundle install [pgfile] [-f]
104
+ ```
105
+
106
+ `install` tries to install missing Extensions. If `--force` is given it installs
107
+ all Extension even if they are already installed.
108
+
109
+ **create**
110
+
111
+ create the extension at the desired version
112
+
113
+ ```
114
+ pgbundle create [pgfile]
115
+ ```
33
116
 
34
- checks whether all dependencies are available for creation on the database server
117
+ `create` runs the `CREATE EXTENSION` command on the specified databases. If a version
118
+ is specified in the `Pgfile` it tries to install with `CREATE EXTENSION VERSION version`.
119
+ If the Extension is already created but with a wrong version, it will run
120
+ `ALTER EXTENSION extension_name UPDATE TO new_version`.
35
121
 
36
- ## getting started
122
+ **init**
123
+
124
+ write an initial pgfile to stdout
125
+
126
+ ```
127
+ pgbundle init db_name -u user -h host -p port
128
+ ```
129
+
130
+ `init` is there to help you get started. If you have already a database with installed
131
+ Extensions you get the content for an initial `Pgfile`. Pgbundle will figure out
132
+ which Extension at which Version are already in use and print a reasonable starting
133
+ point for you Pgfile.
134
+ However this is only to help you get started you may wish to specify sources and
135
+ dependencies correctly.
136
+
137
+ ### How it works
138
+
139
+ You may already have noticed that using Extensions on postgres requires two different
140
+ steps. Building the extension on the database cluster with `make install`
141
+ and creating the extension into the database with `CREATE/ALTER EXTENSION`.
142
+ Pgbundle reflects that with the two different commands `install` and `create`.
143
+
144
+ Usually `pgbundle` runs along with your application on your application server
145
+ which often is different from your database machine. Thus the `install` step
146
+ will (if necessary) try to download the source code of the extension into a
147
+ temporary folder and then copy it to your database servers into `/tmp/pgbundle`.
148
+ From there it will run `make clean && make && make install` for each database.
149
+ You may specify as which user you want these commands to run with the `system_user`
150
+ option. Although for security reasons not recommended you can specify to run the
151
+ install step with sudo `use_sudo: true`, but we suggest to give write permission
152
+ for the postgres system user to the install targets. If you are not sure which these
153
+ are run
154
+
155
+ ```
156
+ pg_config
157
+ ```
37
158
 
38
- if your already have some database on your current project you can get a starting point with
159
+ and find the `LIBDIR`, `SHAREDIR` and `DOCDIR`
39
160
 
40
- pgbundle init
161
+ #### master - slave
41
162
 
42
- lets say your database named 'my_project' runs on localhost with user postges
163
+ Every serious production database cluster usually has a slave often ran as Hot Standby.
164
+ You should make sure that all your Extension are also installed on all slaves.
165
+ Because database slaves run as read-only servers any attempt to `CREATE` or `ALTER`
166
+ Extension will fail, these commands should only run on the master server and will
167
+ be replicated to the slave from there. You can tell `pgbundle` that it should skip
168
+ these steps with `slave: true`.
43
169
 
44
- pgbundle init my_project -u postgres -h localhost
45
170
 
data/bin/pgbundle CHANGED
@@ -2,7 +2,6 @@
2
2
  require 'thor'
3
3
  require 'thor/group'
4
4
  require 'pgbundle'
5
- require 'pry'
6
5
 
7
6
  module PgBundle
8
7
  class Cli < Thor
@@ -10,17 +9,21 @@ module PgBundle
10
9
  method_options %w( force -f ) => :boolean
11
10
  def install(pgfile = 'Pgfile')
12
11
  if options.force?
13
- installed = definition(pgfile).install!
12
+ installed = definitions(pgfile).inject({}){|m, d| m[d.database] = d.install!; m}
14
13
  else
15
- definition(pgfile).available_extensions.each do |dep|
16
- say_status('exists', dep.name)
14
+ definitions(pgfile).each do |d|
15
+ d.available_extensions.each do |dep|
16
+ say_status('exists', "#{d.database}: #{dep.name}")
17
+ end
17
18
  end
18
19
 
19
- installed = definition(pgfile).install
20
+ installed = definitions(pgfile).inject({}){|m, d| m[d.database] = d.install; m}
20
21
  end
21
22
 
22
- installed.each do |d|
23
- say_status('install', d.name, :yellow)
23
+ installed.each do |db, deps|
24
+ deps.each do |d|
25
+ say_status('install', "#{db}: #{d.name}", :yellow)
26
+ end
24
27
  end
25
28
  rescue InstallError, ExtensionCreateError, CircularDependencyError => e
26
29
  say_status('error', e.message, :red)
@@ -30,15 +33,17 @@ module PgBundle
30
33
  desc 'check', 'checks availability of required extensions'
31
34
  def check(pgfile = 'Pgfile')
32
35
  missing = false
33
- definition(pgfile).check.each do |d|
34
- if d[:created]
35
- say_status('created', d[:name])
36
- else
37
- unless d[:installed]
38
- say_status('missing', d[:name], :red)
39
- missing = true
36
+ definitions(pgfile).each do |df|
37
+ df.check.each do |d|
38
+ if d[:created]
39
+ say_status('created', "#{df.database}: #{d[:name]}")
40
+ else
41
+ unless d[:installed]
42
+ say_status('missing', "#{df.database}: #{d[:name]}", :red)
43
+ missing = true
44
+ end
45
+ say_status('installed', "#{df.database}: #{d[:name]}", :yellow) if d[:installed]
40
46
  end
41
- say_status('installed', d[:name], :yellow) if d[:installed]
42
47
  end
43
48
  end
44
49
  exit 1 if missing
@@ -46,14 +51,17 @@ module PgBundle
46
51
 
47
52
  desc 'create', 'create the extension at the desired version'
48
53
  def create(pgfile = 'Pgfile')
49
- definition(pgfile).create.each do | d |
50
- say_status('created', d.name)
54
+ definitions(pgfile).each do |df|
55
+ df.create.each do | d |
56
+ say_status('created', "#{df.database}: #{d.name}")
57
+ end
51
58
  end
52
59
  end
53
60
 
54
61
  desc 'init', 'write an initial pgfile to stdout'
55
62
  method_options %w( user -u ) => :string
56
63
  method_options %w( host -h ) => :string
64
+ method_options %w( port -p ) => :string
57
65
  def init(db_name)
58
66
  definition = PgBundle::Definition.new
59
67
  definition.database = PgBundle::Database.new(db_name, options)
@@ -62,9 +70,9 @@ module PgBundle
62
70
  end
63
71
 
64
72
  no_commands do
65
- def definition(pgfile)
66
- definition = Dsl.new.eval_pgfile(pgfile)
67
- definition.link_dependencies
73
+ def definitions(pgfile)
74
+ definitions = Dsl.new.eval_pgfile(pgfile)
75
+ definitions.map(&:link_dependencies)
68
76
  end
69
77
  end
70
78
  end
data/cibuild.sh ADDED
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+
3
+ set -eux
4
+
5
+ sudo apt-get update
6
+
7
+ packages="postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-common"
8
+
9
+ # bug: http://www.postgresql.org/message-id/20130508192711.GA9243@msgid.df7cb.de
10
+ sudo update-alternatives --remove-all postmaster.1.gz
11
+
12
+ # stop all existing instances (because of https://github.com/travis-ci/travis-cookbooks/pull/221)
13
+ sudo service postgresql stop
14
+ # and make sure they don't come back
15
+ echo 'exit 0' | sudo tee /etc/init.d/postgresql
16
+ sudo chmod a+x /etc/init.d/postgresql
17
+
18
+ sudo apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install $packages
19
+
20
+ status=0
21
+ sudo pg_createcluster --start $PGVERSION test -p 55435 -- -A trust
22
+ # make all PG_CONFIG=/usr/lib/postgresql/$PGVERSION/bin/pg_config
23
+ # sudo make install PG_CONFIG=/usr/lib/postgresql/$PGVERSION/bin/pg_config
24
+ # PGPORT=55435 make installcheck PGUSER=postgres PG_CONFIG=/usr/lib/postgresql/$PGVERSION/bin/pg_config || status=$?
25
+
26
+ # if test -f regression.diffs; then cat regression.diffs; fi
27
+ # exit $status
28
+ PGPORT=55435 rake
@@ -13,6 +13,10 @@ module PgBundle
13
13
  fail NotImplementedError
14
14
  end
15
15
 
16
+ def clean
17
+ fail NotImplementedError
18
+ end
19
+
16
20
  private
17
21
 
18
22
  def copy_local(source, dest)
@@ -1,6 +1,6 @@
1
1
  require 'pg'
2
2
  require 'net/ssh'
3
- require 'pry'
3
+
4
4
  module PgBundle
5
5
  # The Database class defines on which database the extensions should be installed
6
6
  # Note to install an extension the code must be compiled on the database server
@@ -16,6 +16,7 @@ module PgBundle
16
16
  @system_user = opts[:system_user] || 'postgres'
17
17
  @port = opts[:port] || 5432
18
18
  @force_ssh = opts[:force_ssh] || false
19
+ @slave = opts[:slave] || false
19
20
  end
20
21
 
21
22
  def connection
@@ -24,6 +25,13 @@ module PgBundle
24
25
  end
25
26
  end
26
27
 
28
+ def to_s
29
+ "host: #{@host}:#{port} db: #{@name}"
30
+ end
31
+
32
+ def slave?
33
+ @slave
34
+ end
27
35
  # executes the given sql on the database connections
28
36
  # redirects all noise to /dev/null
29
37
  def execute(sql)
@@ -58,6 +66,7 @@ module PgBundle
58
66
  source.load(host, system_user, load_destination(ext_name))
59
67
  run(make_install_cmd(ext_name))
60
68
  remove_source(ext_name)
69
+ source.clean
61
70
  end
62
71
 
63
72
  # loads the source and runs make uninstall
@@ -66,6 +75,7 @@ module PgBundle
66
75
  source.load(host, system_user, load_destination(ext_name))
67
76
  run(make_uninstall_cmd(ext_name))
68
77
  remove_source(ext_name)
78
+ source.clean
69
79
  end
70
80
 
71
81
  def drop_extension(name)
@@ -113,7 +123,8 @@ module PgBundle
113
123
  end
114
124
 
115
125
  def local(cmd)
116
- %x(#{cmd})
126
+ res = %x((#{cmd}) 2>&1 )
127
+ raise res unless $?.success?
117
128
  end
118
129
 
119
130
  def remote(cmd)
@@ -49,7 +49,7 @@ module PgBundle
49
49
  end
50
50
 
51
51
  def init
52
- ["database '#{database.name}', host: '#{database.host}', user: #{database.user}, system_user: #{database.system_user}, use_sudo: #{database.use_sudo}"] +
52
+ ["database '#{database.name}', host: '#{database.host}', user: '#{database.user}', system_user: '#{database.system_user}', use_sudo: #{database.use_sudo}"] +
53
53
  database.current_definition.map do |r|
54
54
  name, version = r['name'], r['version']
55
55
  requires = r['requires'] ? ", requires: " + r['requires'].gsub(/[{},]/,{'{' => '%w(', '}' =>')', ','=> ' '}) : ''
@@ -57,7 +57,7 @@ module PgBundle
57
57
  end
58
58
  end
59
59
 
60
- # returns an array hashes with dependency information
60
+ # returns an array of hashes with dependency information
61
61
  # [{name: 'foo', installed: true, created: false }]
62
62
  def check
63
63
  link_dependencies
data/lib/pgbundle/dsl.rb CHANGED
@@ -5,12 +5,13 @@ module PgBundle
5
5
  def initialize
6
6
  @definition = Definition.new
7
7
  @extensions = []
8
+ @databases = []
8
9
  end
9
10
 
10
11
  def eval_pgfile(pgfile, contents=nil)
11
12
  contents ||= File.read(pgfile.to_s)
12
13
  instance_eval(contents)
13
- @definition
14
+ @databases.map{|d| df = @definition.clone; df.database = d; df}
14
15
  rescue SyntaxError => e
15
16
  syntax_msg = e.message.gsub("#{pgfile}:", 'on line ')
16
17
  raise PgfileError, "Pgfile syntax error #{syntax_msg}"
@@ -24,7 +25,7 @@ module PgBundle
24
25
 
25
26
  def database(*args)
26
27
  opts = extract_options!(args)
27
- @definition.database = Database.new(args.first, opts)
28
+ @databases << Database.new(args.first, opts)
28
29
  end
29
30
 
30
31
  def pgx(*args)
@@ -14,6 +14,7 @@ module PgBundle
14
14
  def initialize(*args)
15
15
  opts = args.last.is_a?(Hash) ? args.pop : {}
16
16
  @name, @version = args
17
+ validate(opts)
17
18
  self.dependencies = opts[:requires]
18
19
  set_source(opts)
19
20
  end
@@ -108,7 +109,6 @@ module PgBundle
108
109
  unless dependencies.empty?
109
110
  install_dependencies(database, force)
110
111
  end
111
-
112
112
  make_install(database, force)
113
113
  raise ExtensionNotFound.new(name, version) unless installed?(database)
114
114
 
@@ -126,6 +126,7 @@ module PgBundle
126
126
  # create the extension along with it's dependencies in a transaction
127
127
  def create_with_dependencies(database)
128
128
  return true if created?(database)
129
+ return false if database.slave?
129
130
 
130
131
  database.transaction do |con|
131
132
  begin
@@ -181,6 +182,20 @@ module PgBundle
181
182
 
182
183
  private
183
184
 
185
+ # validates the options hash
186
+ def validate(opts)
187
+ opts = opts.clone
188
+ opts.delete(:requires)
189
+ opts.delete(:branch)
190
+ if opts.size > 1
191
+ fail PgfileError.new "multiple sources given for #{name} #{opts}"
192
+ end
193
+
194
+ unless opts.empty? || [:path, :git, :github, :pgxn].include?(opts.keys.first)
195
+ fail PgfileError.new "invalid source #{opts.keys.first} for #{name}"
196
+ end
197
+ end
198
+
184
199
  # adds dependencies that are required but not defined yet
185
200
  def add_missing_required_dependencies(database)
186
201
  requires = requires(database)
@@ -223,7 +238,7 @@ module PgBundle
223
238
  end
224
239
 
225
240
  def drop_extension(database)
226
- database.drop_extension(name)
241
+ database.drop_extension(name) unless database.slave?
227
242
  end
228
243
 
229
244
  # loads the source and runs make install
@@ -266,6 +281,7 @@ module PgBundle
266
281
 
267
282
  # hard checks that the dependency can be created running CREATE command in a transaction
268
283
  def creatable!(database)
284
+ return false if database.slave?
269
285
  database.transaction_rollback do |con|
270
286
  begin
271
287
  create_dependencies(con)
@@ -274,6 +290,8 @@ module PgBundle
274
290
  raise ExtensionNotFound.new(name, version)
275
291
  rescue PG::UndefinedObject => err
276
292
  raise MissingDependency.new(name, err.message)
293
+ rescue PG::ReadOnlySqlTransaction
294
+ raise ReadOnlyDb.new(database, name)
277
295
  end
278
296
  end
279
297
 
@@ -286,7 +304,7 @@ module PgBundle
286
304
  database.execute 'BEGIN'
287
305
  begin
288
306
  database.execute update_stmt
289
- rescue PG::UndefinedFile, PG::UndefinedObject => err
307
+ rescue PG::UndefinedFile, PG::UndefinedObject, PG::ReadOnlySqlTransaction => err
290
308
  @error = err.message
291
309
  result = false
292
310
  end
@@ -298,8 +316,12 @@ module PgBundle
298
316
  def set_source(opts)
299
317
  if opts[:path]
300
318
  @source = PathSource.new(opts[:path])
319
+ elsif opts[:git]
320
+ @source = GitSource.new(opts[:git], opts[:branch])
301
321
  elsif opts[:github]
302
322
  @source = GithubSource.new(opts[:github], opts[:branch])
323
+ elsif opts[:pgxn]
324
+ @source = PgxnSource.new(name, version)
303
325
  end
304
326
  end
305
327
  end
@@ -0,0 +1,43 @@
1
+ require 'tmpdir'
2
+ module PgBundle
3
+ # The GithubSource class defines a Github Source
4
+ class GitSource < BaseSource
5
+ attr_reader :branch
6
+
7
+ def initialize(path, branch = 'master')
8
+ @branch = branch || 'master'
9
+ super(path)
10
+ end
11
+
12
+ def load(host, user, dest)
13
+ clone
14
+ if host == 'localhost'
15
+ copy_local("#{clone_dir}/", dest)
16
+ else
17
+ copy_to_remote(host, user, "#{clone_dir}/", dest)
18
+ end
19
+ end
20
+
21
+ def clean
22
+ FileUtils.remove_dir(clone_dir, true)
23
+ end
24
+
25
+ private
26
+
27
+ def clone
28
+ res = %x((rm -rf #{clone_dir} && #{git_command} && rm -rf #{clone_dir}/.git}) 2>&1)
29
+ unless $?.success?
30
+ fail GitCommandError, git_command, res
31
+ end
32
+ end
33
+
34
+ # git clone user@git-server:project_name.git -b branch_name /some/folder
35
+ def git_command
36
+ "git clone #{path} -b #{branch} --quiet --depth=1 #{clone_dir}"
37
+ end
38
+
39
+ def clone_dir
40
+ @clone_dir ||= Dir.mktmpdir
41
+ end
42
+ end
43
+ end
@@ -1,39 +1,13 @@
1
1
  require 'tmpdir'
2
2
  module PgBundle
3
3
  # The GithubSource class defines a Github Source
4
- class GithubSource < BaseSource
4
+ class GithubSource < GitSource
5
5
  attr_reader :branch
6
6
 
7
7
  def initialize(path, branch = 'master')
8
- @branch = branch || 'master'
9
- super(path)
10
- end
11
-
12
- def load(host, user, dest)
13
- clone
14
- if host == 'localhost'
15
- copy_local("#{clone_dir}/", dest)
16
- else
17
- copy_to_remote(host, user, "#{clone_dir}/", dest)
18
- end
19
- end
20
-
21
- private
22
-
23
- def clone
24
- %x((rm -rf #{clone_dir} && #{git_command} && rm -rf #{clone_dir}/.git}) 2>&1)
25
- unless $?.success?
26
- fail GitCommandError, git_command
27
- end
28
- end
29
-
30
- # git clone user@git-server:project_name.git -b branch_name /some/folder
31
- def git_command
32
- "git clone git@github.com:#{path}.git -b #{branch} --quiet --depth=1 #{clone_dir}"
33
- end
34
-
35
- def clone_dir
36
- @clone_dir ||= Dir.mktmpdir
8
+ branch = branch || 'master'
9
+ path = "git@github.com:#{path}.git"
10
+ super(path, branch)
37
11
  end
38
12
  end
39
13
  end
@@ -11,5 +11,8 @@ module PgBundle
11
11
  copy_to_remote(host, user, path, dest)
12
12
  end
13
13
  end
14
+
15
+ def clean
16
+ end
14
17
  end
15
18
  end
@@ -0,0 +1,61 @@
1
+ require 'tmpdir'
2
+ require 'open-uri'
3
+ require 'zip'
4
+
5
+ module PgBundle
6
+ # The GithubSource class defines a Github Source
7
+ class PgxnSource < BaseSource
8
+
9
+ def initialize(dist, version)
10
+ @dist, @version = dist, version
11
+ path = "http://master.pgxn.org/dist/%{dist}/%{version}/%{dist}-%{version}.zip" % {dist: dist, version: version}
12
+ super(path)
13
+ end
14
+
15
+ def load(host, user, dest)
16
+ download
17
+ unzip
18
+ if host == 'localhost'
19
+ copy_local("#{download_dir}/#{@dist}-#{@version}", dest)
20
+ else
21
+ copy_to_remote(host, user, "#{download_dir}/#{@dist}-#{@version}", dest)
22
+ end
23
+ end
24
+
25
+ def clean
26
+ FileUtils.remove_dir(download_dir, true)
27
+ end
28
+
29
+ private
30
+
31
+ def download
32
+ begin
33
+ File.open(zipfile, "wb") do |saved_file|
34
+ open(@path, "rb") do |read_file|
35
+ saved_file.write(read_file.read)
36
+ end
37
+ end
38
+ rescue OpenURI::HTTPError => e
39
+ raise PgxnError.new(path, e.message)
40
+ end
41
+ end
42
+
43
+ def zipfile
44
+ "#{download_dir}/#{@dist}.zip"
45
+ end
46
+
47
+ def unzip
48
+ Zip::ZipFile.open(zipfile) do |zip_file|
49
+ zip_file.each do |f|
50
+ f_path=File.join(download_dir, f.name)
51
+ FileUtils.mkdir_p(File.dirname(f_path))
52
+ zip_file.extract(f, f_path) unless File.exist?(f_path)
53
+ end
54
+ end
55
+ end
56
+
57
+ def download_dir
58
+ @clone_dir ||= Dir.mktmpdir
59
+ end
60
+ end
61
+ end
@@ -1,3 +1,3 @@
1
1
  module Pgbundle
2
- VERSION = '0.0.15'
2
+ VERSION = '0.0.16'
3
3
  end
data/lib/pgbundle.rb CHANGED
@@ -7,7 +7,9 @@ module PgBundle
7
7
  autoload :Extension, 'pgbundle/extension'
8
8
  autoload :BaseSource, 'pgbundle/base_source'
9
9
  autoload :PathSource, 'pgbundle/path_source'
10
+ autoload :GitSource, 'pgbundle/git_source'
10
11
  autoload :GithubSource, 'pgbundle/github_source'
12
+ autoload :PgxnSource, 'pgbundle/pgxn_source'
11
13
 
12
14
  class PgfileError < StandardError
13
15
  end
@@ -44,6 +46,12 @@ module PgBundle
44
46
  end
45
47
  end
46
48
 
49
+ class ReadOnlyDb < ExtensionCreateError
50
+ def initialize(db, base_name)
51
+ super "Can't install Extension #{base_name}, Database #{db} is read only"
52
+ end
53
+ end
54
+
47
55
  class MissingDependency < ExtensionCreateError
48
56
  def initialize(base_name, dependen_msg)
49
57
  required = dependen_msg[/required extension \"(.*?)\" is not installed/, 1]
@@ -52,8 +60,14 @@ module PgBundle
52
60
  end
53
61
 
54
62
  class GitCommandError < InstallError
55
- def initialize(dest)
56
- super "Failed to load git repository cmd: '#{dest}'"
63
+ def initialize(dest, details = nil)
64
+ super "Failed to load git repository cmd: '#{dest}'\n failed: #{details}"
65
+ end
66
+ end
67
+
68
+ class PgxnError < InstallError
69
+ def initialize(dest, message = nil)
70
+ super "Failed to load from pgxn: '#{dest}'\n failed: #{message}"
57
71
  end
58
72
  end
59
73
  end
data/pgbundle.gemspec CHANGED
@@ -21,10 +21,10 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency 'thor'
22
22
  spec.add_dependency 'net-ssh'
23
23
  spec.add_dependency 'net-scp'
24
+ spec.add_dependency 'zip'
24
25
  #https://bitbucket.org/ged/ruby-pg/wiki/Home
25
26
  spec.add_dependency 'pg', '> 0.17'
26
27
  spec.add_development_dependency 'rspec', '~> 2.14.0'
27
- spec.add_development_dependency "bundler", "~> 1.5"
28
+ spec.add_development_dependency "bundler", ">= 1.5.0"
28
29
  spec.add_development_dependency "rake"
29
- spec.add_development_dependency "pry"
30
30
  end
data/spec/Pgfile CHANGED
@@ -1,4 +1,4 @@
1
- database 'pgbundle_test', host: 'localhost', port: 54321
1
+ database 'pgbundle_test', host: 'localhost'
2
2
 
3
3
  pgx 'hstore'
4
4
  pgx 'bar', path: './spec/sample_extensions/bar', requires: 'ltree'
data/spec/dsl_spec.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe PgBundle::Dsl do
4
- subject { PgBundle::Dsl.new.eval_pgfile(File.expand_path('../Pgfile', __FILE__)) }
4
+ subject { PgBundle::Dsl.new.eval_pgfile(File.expand_path('../Pgfile', __FILE__)).first }
5
5
 
6
6
  its(:database) { should be_a PgBundle::Database }
7
- its('database.port') { should be 54321 }
7
+ its('database.host') { should eq 'localhost' }
8
8
  its(:extensions) { should be_a Hash }
9
9
 
10
10
  context 'parsing options' do
@@ -65,6 +65,22 @@ describe PgBundle::Extension do
65
65
  end
66
66
  end
67
67
 
68
+ context 'installing from github' do
69
+ subject { PgBundle::Extension.new('ltree', '1.0', git: 'https://github.com/adjust/ltree.git') }
70
+
71
+ before do
72
+ allow_any_instance_of(PgBundle::GithubSource)
73
+ .to receive(:clone_dir)
74
+ .and_return('/tmp/pgbundle/tree_tmp')
75
+
76
+ subject.install(database, true)
77
+ end
78
+
79
+ it 'cleans the tmp directory after install' do
80
+ expect(Dir.exists?('/tmp/pgbundle/tree_tmp')).to be_false
81
+ end
82
+ end
83
+
68
84
  context 'require not found' do
69
85
  subject { PgBundle::Extension.new('foo', '0.0.2', path: './spec/sample_extensions/foo', requires: PgBundle::Extension.new('noope')) }
70
86
 
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'pgbundle'
3
3
  require 'pg'
4
- require 'pry'
5
4
 
6
5
  Dir.glob('spec/support/**/*.rb').each { |f| require f }
7
6
 
@@ -12,14 +11,19 @@ RSpec.configure do |config|
12
11
  config.filter_run focus: true
13
12
  config.run_all_when_everything_filtered = true
14
13
  config.before(:suite) do
15
- conn = PG.connect(dbname: 'postgres', user: 'postgres')
14
+ system "mkdir -p -m 0777 /tmp/pgbundle/"
15
+ conn = PG.connect(dbname: 'postgres', user: 'postgres', host: 'localhost', port: ENV['PGPORT'] || 5432)
16
16
  conn.exec('CREATE DATABASE pgbundle_test')
17
17
  conn.close
18
18
  end
19
19
 
20
20
  config.after(:suite) do
21
- conn = PG.connect(dbname: 'postgres', user: 'postgres')
22
- conn.exec("SELECT pg_terminate_backend(pid) from pg_stat_activity WHERE datname ='pgbundle_test'")
21
+ conn = PG.connect(dbname: 'postgres', user: 'postgres', host: 'localhost', port: ENV['PGPORT'] || 5432)
22
+ if ENV['PGVERSION']=='9.1'
23
+ conn.exec("SELECT pg_terminate_backend(procpid) from pg_stat_activity WHERE datname ='pgbundle_test'")
24
+ else
25
+ conn.exec("SELECT pg_terminate_backend(pid) from pg_stat_activity WHERE datname ='pgbundle_test'")
26
+ end
23
27
  conn.exec('DROP DATABASE IF EXISTS pgbundle_test')
24
28
  conn.close
25
29
  end
@@ -42,6 +46,6 @@ RSpec.configure do |config|
42
46
  end
43
47
 
44
48
  def database
45
- @db ||= PgBundle::Database.new('pgbundle_test')
49
+ @db ||= PgBundle::Database.new('pgbundle_test', user: 'postgres', host: 'localhost', port: ENV['PGPORT'] || 5432, use_sudo: ENV['TRAVIS'])
46
50
  end
47
51
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgbundle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.15
4
+ version: 0.0.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manuel Kniep
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-08 00:00:00.000000000 Z
11
+ date: 2015-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: zip
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: pg
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -82,34 +96,20 @@ dependencies:
82
96
  version: 2.14.0
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: bundler
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '1.5'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '1.5'
97
- - !ruby/object:Gem::Dependency
98
- name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 1.5.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: 1.5.0
111
111
  - !ruby/object:Gem::Dependency
112
- name: pry
112
+ name: rake
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -137,14 +137,17 @@ files:
137
137
  - README.md
138
138
  - Rakefile
139
139
  - bin/pgbundle
140
+ - cibuild.sh
140
141
  - lib/pgbundle.rb
141
142
  - lib/pgbundle/base_source.rb
142
143
  - lib/pgbundle/database.rb
143
144
  - lib/pgbundle/definition.rb
144
145
  - lib/pgbundle/dsl.rb
145
146
  - lib/pgbundle/extension.rb
147
+ - lib/pgbundle/git_source.rb
146
148
  - lib/pgbundle/github_source.rb
147
149
  - lib/pgbundle/path_source.rb
150
+ - lib/pgbundle/pgxn_source.rb
148
151
  - lib/pgbundle/version.rb
149
152
  - pgbundle.gemspec
150
153
  - spec/Pgfile