devinstall 1.1.0 → 1.2.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fbc1a0725e5afda859bd39e8b8cfe23f97e22ff0
4
+ data.tar.gz: 55490a5aba501da261edf43b83c66d3d72dc70a8
5
+ SHA512:
6
+ metadata.gz: b1fbbc6c8ab443b9cce341905d2507f884adf05bff1f47b030f5a47ec115a9622fd4ee385da61c4fcbb4cb31333b6386a030e5fe049c5e92978eaca82c7f99a5
7
+ data.tar.gz: 6a3a3bfb5fdcc517e2b976a6e80fcca4665d236f96e216260d5e7dfe511c7e4187ff5cf40d310639806c2763efbb405187845692e325e61149597875c75e9d40
data/.gitignore CHANGED
@@ -9,7 +9,6 @@ InstalledFiles
9
9
  _yardoc
10
10
  coverage
11
11
  lib/bundler/man
12
- pkg
13
12
  rdoc
14
13
  spec/reports
15
14
  test/tmp
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # Devinstall
1
+ # Devinstall [![Gem Version][GV img]][Gem Version]
2
2
 
3
- This is a poor man automatic builder / installer / deployer for packages.
3
+ [Gem Version]: https://rubygems.org/gems/devinstall
4
+
5
+ [GV img]: https://badge.fury.io/rb/devinstall.png
6
+
7
+ This is a poor man automatic package builder / installer / deployer.
4
8
 
5
9
  The build happens on a remote machine (in the future on several remote machines by package type)
6
10
  via external tools rsync and ssh.
@@ -75,6 +79,103 @@ or
75
79
 
76
80
  This will build and upload package "devinstall" to repository for dev-rh environment as defined in config.yml
77
81
 
82
+ ## The config file
83
+
84
+ In order to set all the variables and to define commands to do when building or installing you need a configuration file.
85
+
86
+ The said config file have simple YAML structure and should define the folowing parameters:
87
+
88
+ local:
89
+ folder:
90
+ temp:
91
+
92
+ The folder where the source/prepackaged files are on the local (developer) machine (`:folder`) and the
93
+ temporary folder where the generated packages will be downloades
94
+
95
+ build:
96
+ folder:
97
+ command:
98
+ provider:
99
+ type:
100
+ arch:
101
+ target:
102
+ env:
103
+
104
+ In order:
105
+
106
+ - the folder where the sources should be copied (might be ignored by some provider_plugins)
107
+
108
+ - The command used to build the package (like `make package` or `dpkg-buildpackage`)
109
+
110
+ - The provider for the build machine (like the `local` machine or another machine
111
+ accessible only by SSH - `ssh`
112
+
113
+ - the package type ( `deb`ian, `rpm`, ...)
114
+
115
+ - the architecture (might be ignored by some package_plugins)
116
+
117
+ - and the folder where the package builder will put the builded packages
118
+
119
+ - env define an environment (lyke `prod` or `QA`) for which the package will be built / installed
120
+
121
+ Unlike the other parameters env is optional
122
+
123
+ install:
124
+ folder:
125
+ command:
126
+ provider:
127
+ type:
128
+ arch:
129
+ env:
130
+ repos:
131
+ folder:
132
+ provider:
133
+ type:
134
+ arch:
135
+ env:
136
+ tests:
137
+ folder:
138
+ command:
139
+ provider
140
+ env:
141
+
142
+ The parameters have the same meaning as for `build:`
143
+ `repos` reffers to the package repository
144
+ `tests` is optional (DON'T do this) and no tests will be performend if it's missing
145
+
146
+ defaults:
147
+ type:
148
+ env:
149
+
150
+ The default `type` and `env` if you don't use command-line switches
151
+
152
+
153
+ The order in which the parameters will be searched is:
154
+
155
+ - local
156
+
157
+ Like:
158
+
159
+ packages:
160
+ <package_name>:
161
+ <type>:
162
+ <section>: # like build: or install:
163
+ <env>:
164
+ <parameter>: <value>
165
+
166
+
167
+ - or global:
168
+
169
+ Like
170
+
171
+ <section>:
172
+ <env>:
173
+ <parameter>: value
174
+
175
+ The parameters speciffied per package have priority over the global ones
176
+
177
+ In any case `env` is optional
178
+
78
179
  ## Contributing
79
180
 
80
181
  1. Fork it
data/bin/pkg-tool CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
2
  require 'bundler/setup'
4
3
  require 'devinstall'
5
4
 
data/devinstall.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Devinstall::VERSION
9
9
  spec.authors = ['Dragos Boca']
10
10
  spec.email = %w(dboca@mail.com)
11
- spec.description = %q{remote builder and installer}
11
+ spec.description = %q{OS package builder and installer}
12
12
  spec.summary = %q{Copy the source files to a build host, build the packages and install builded packages}
13
13
  spec.homepage = 'http://github.com/dboca/devinstall'
14
14
  spec.license = 'MIT'
data/doc/example.yml CHANGED
@@ -19,6 +19,7 @@ tests:
19
19
  dev:
20
20
  machine: dboca.dev.local
21
21
  folder: rs
22
+ target: rs ## this is a new addition
22
23
  user: dboca
23
24
  command: "cd %f && make devtest"
24
25
  local:
@@ -34,18 +35,19 @@ build:
34
35
  # arch is the architecture for the generated package (like amd64 or i686)
35
36
  # provider can be ssh or local (in the future vagrant and openstack)
36
37
  # note: the ssh in provider have nothing to do with ssh command in base
37
- provider: ssh
38
+ provider: local
38
39
  user: dboca
39
40
  host: vm-dboca.dev.local
40
41
  folder: rs
41
42
  target: rs
42
43
  arch: all
44
+ type: deb
43
45
  install:
44
46
  # host - dev/prod deploy and install host
45
47
  # user - for scp / ssh / rsync (defaults to base[user])
46
48
  # environment - live/qa/dev/...
47
49
  dev:
48
- provider: ssh
50
+ provider: ssh ## Not implemented but compulsory for build, install, repos, tests
49
51
  user: dboca
50
52
  host:
51
53
  - server1.lan
@@ -63,14 +65,17 @@ repos:
63
65
  type: deb
64
66
  packages:
65
67
  devinstall:
66
- # might contain all the sections above (local, build, install, repos)
67
- # type and build_command are mandatory
68
+ # might contain all the sections above (local, build, install, repos, tests)
69
+ # type:build:command and type:install:command are mandatory
68
70
  # in build_command the folowing expansions are made:
69
71
  # %f build[folder]
70
72
  # %t build[target]
71
73
  # %p package (current package)
72
74
  # %T type (deb, rpm, tar.gz ,...)
75
+ # %a only make sense for install and is the installed file
73
76
  deb:
74
77
  build:
75
78
  command: "cd %f/%p && dpkg-buildpackage"
79
+ install:
80
+ command: "dpkg -i %a"
76
81
 
@@ -1,4 +1,4 @@
1
- require 'devinstall/pkg'
1
+ require 'devinstall/package'
2
2
  require 'getopt/long'
3
3
  require 'devinstall/settings'
4
4
  require 'commander/import'
@@ -37,7 +37,7 @@ module Cli
37
37
  env = options.env ? options.env.to_sym : config.defaults(:env)
38
38
 
39
39
  args.each do |p|
40
- pk=Devinstall::Pkg.new(p, type, env)
40
+ pk=Devinstall::Package.new(p, type, env)
41
41
  pk.build
42
42
  end
43
43
  end
@@ -51,7 +51,7 @@ module Cli
51
51
  env = options.env ? options.env.to_sym : config.defaults(:env)
52
52
 
53
53
  args.each do |p|
54
- pk=Devinstall::Pkg.new(p, type, env)
54
+ pk=Devinstall::Package.new(p, type, env)
55
55
  pk.build
56
56
  pk.install
57
57
  end
@@ -66,7 +66,7 @@ module Cli
66
66
  env = options.env ? options.env.to_sym : config.defaults(:env)
67
67
 
68
68
  args.each do |p|
69
- pk=Devinstall::Pkg.new(p, type, env)
69
+ pk=Devinstall::Package.new(p, type, env)
70
70
  pk.run_tests
71
71
  end
72
72
  end
@@ -80,7 +80,7 @@ module Cli
80
80
  env = options.env ? options.env.to_sym : config.defaults(:env)
81
81
 
82
82
  args.each do |p|
83
- pk=Devinstall::Pkg.new(p, type, env)
83
+ pk=Devinstall::Package.new(p, type, env)
84
84
  pk.build
85
85
  pk.run_tests
86
86
  pk.upload
@@ -90,5 +90,3 @@ module Cli
90
90
 
91
91
  end
92
92
 
93
- __END__
94
-
@@ -0,0 +1,87 @@
1
+ require 'devinstall/version'
2
+ require 'devinstall/deep_symbolize'
3
+ require 'devinstall/utils'
4
+ require 'devinstall/settings'
5
+ require 'devinstall/provider'
6
+ require 'pp'
7
+
8
+ module Devinstall
9
+ class UndeffError < RuntimeError;
10
+ end
11
+
12
+
13
+ class Package
14
+ include Utils
15
+
16
+ #noinspection RubyResolve
17
+ def load_package_plugin(type)
18
+ require "devinstall/pkg/pkg_#{type.to_s}"
19
+ self.singleton_class.send(:include, Kernel.const_get("Pkg").const_get("#{type.to_s.capitalize}"))
20
+ end
21
+
22
+ def initialize(pkg, type, env)
23
+ @package, @type, @env = pkg, type, env
24
+ load_package_plugin(type)
25
+ end
26
+
27
+ def upload(pkg=@package, type=@type, env=@env)
28
+ uploader = Provider.new(pkg, type, env, :repos)
29
+ info = get_info(pkg, type, env)
30
+ info[:to_upload].each do |target|
31
+ puts "Uploading #{target.to_s}"
32
+ uploader.put_file(info[:files][target].to_s)
33
+ end
34
+ rescue KeyNotDefinedError => e
35
+ puts e.message
36
+ raise "Error uploading #{pkg}"
37
+ end
38
+
39
+ def build(pkg=@package, type=@type, env=@env)
40
+ puts "Building package #{pkg} type #{type}"
41
+ info = get_info(pkg, type, env)
42
+ builder = Provider.new(pkg, type, env, :build)
43
+ builder.copy_sources
44
+ builder.do_action
45
+ info[:files].each do |target, file|
46
+ puts "Receiving target #{target.to_s} for #{file.to_s}"
47
+ builder.get_file(file)
48
+ end
49
+ rescue KeyNotDefinedError => e
50
+ puts e.message
51
+ raise "Error uploading #{pkg}"
52
+ end
53
+
54
+ def install(pkg=@package, type=@type, env=@env)
55
+ puts "Installing #{pkg} in #{env} environment."
56
+ installer = Provider.new(pkg, type, env, :install)
57
+ info = get_info(pkg, type, env)
58
+ info[:to_install].each do |target| # upload each file to all targets
59
+ installer.put_file(info[:files][target])
60
+ installer.do_action(info[:files][target])
61
+ end
62
+ rescue KeyNotDefinedError => e
63
+ puts e.message
64
+ raise "Error uploading #{pkg}"
65
+ end
66
+
67
+ def run_tests(pkg=@package, type=@type, env=@env)
68
+ config=Settings.instance
69
+ # check if we have the test section in the configuration file
70
+ unless config.respond_to? :tests
71
+ puts 'No test section in the config file.'
72
+ puts 'Skipping tests'
73
+ return
74
+ end
75
+ puts 'Running all tests'
76
+ puts 'This will take some time and you have no output'
77
+ tester = Provider.new(pkg, type, env, :tests)
78
+ tester.copy_sources
79
+ tester.do_action
80
+ rescue KeyNotDefinedError => e
81
+ puts e.message
82
+ raise "Error uploading #{pkg}"
83
+ end
84
+
85
+ end
86
+ end
87
+
@@ -0,0 +1,31 @@
1
+ require 'devinstall/utils'
2
+
3
+ module Pkg
4
+ module Deb
5
+ # @type=:deb
6
+ include Utils
7
+
8
+ def get_info(pkg, type, env)
9
+ config=Devinstall::Settings.instance
10
+ folder=config.local(:folder, pkg: pkg, type: type, env: env)
11
+ deb_changelog = File.expand_path "#{folder}/#{pkg}/debian/changelog"
12
+ unless File.exists? deb_changelog
13
+ exit! "No 'debian/changelog' found in specified :local:folder (#{folder})"
14
+ end
15
+ package_version = File.open(deb_changelog, 'r') { |f| f.gets.chomp.sub(/^.*\((.*)\).*$/, '\1') }
16
+ p_name = "#{pkg}_#{package_version}"
17
+ arch = config.build(pkg: pkg, type: type, env: env)[:arch]
18
+ {version: package_version,
19
+ files:
20
+ {deb: "#{p_name}_#{arch}.deb",
21
+ tgz: "#{p_name}.tar.gz",
22
+ dsc: "#{p_name}.dsc",
23
+ chg: "#{p_name}_amd64.changes"},
24
+ to_install: [:deb],
25
+ to_upload: [:deb, :tgz, :dsc, :chg]
26
+ }
27
+ rescue IOError => e
28
+ exit! "IO Error while opening #{deb_changelog}\n Aborting \n #{e.message}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ require 'devinstall/utils'
2
+ require 'devinstall/settings'
3
+
4
+ module Provider
5
+ module Ssh
6
+ include Utils
7
+
8
+ SETTINGS = {
9
+ build: [:user, :host],
10
+ install: [:user, :host],
11
+ tests: [:user, :host],
12
+ repos: [:user, :host],
13
+ ssh: [:ssh, :scp, :rsync, :sudo]
14
+ }
15
+
16
+ def upload_sources(cfg, src, dst)
17
+ config =Devinstall::Settings.instance
18
+ rsync = config.ssh(:rsync)
19
+ source = src[-1]=='/' ? src : "#{src}/" # source folder(we add / because we are using rsync)
20
+ dest = "#{cfg[:user]}@#{cfg[:host]}:#{dst}" # cfg should provide user and host
21
+ command("#{rsync} #{source} #{dest}")
22
+ end
23
+
24
+ def download_file(cfg, file, local)
25
+ config=Devinstall::Settings.instance
26
+ rsync = config.ssh(:rsync) # should be config.provider[:ssh][:rsync]
27
+ command("#{rsync} -az #{cfg[:user]}@#{cfg[:host]}:#{cfg[:target]}/#{file.to_s} #{local}")
28
+ end
29
+
30
+ def upload_file(cfg, file, local)
31
+ config=Devinstall::Settings.instance
32
+ scp = config.ssh(:scp) # should be config.provider[:ssh][:scp]
33
+ hosts = Array === cfg[:host] ? cfg[:host] : [cfg[:host]]
34
+ hosts.each do |host|
35
+ command("#{scp} #{local}/#{file} #{cfg[:user]}@#{host}:#{cfg[:folder]}")
36
+ end
37
+ end
38
+
39
+ def exec_command(cfg, command)
40
+ config=Devinstall::Settings.instance
41
+ ssh = config.ssh(:ssh) # should be config.provider[:ssh][:scp]
42
+ hosts = Array === cfg[:host] ? cfg[:host] : [cfg[:host]]
43
+ hosts.each do |host|
44
+ command("#{ssh} #{cfg[:user]}@#{host} \"#{command}\"")
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,53 @@
1
+ module Devinstall
2
+ class Provider
3
+
4
+ #noinspection RubyResolve
5
+ def initialize(pkg, type, env, action=nil)
6
+ providers={build: :build, install: :install, upload: :repos, run_tests: :tests}
7
+ @pkg, @type, @env = (pkg.to_sym rescue pkg), (type.to_sym rescue type), (env.to_sym rescue env)
8
+ @action = action || providers[caller_locations(1, 1)[0].label.to_sym] # that's realy stupid!
9
+ provider = Settings.instance.send(action, :provider, pkg: pkg, type: type, env: env)
10
+ require "devinstall/provider/provider_#{provider}"
11
+ self.singleton_class.send(:include, Kernel.const_get('Provider').const_get("#{provider.capitalize}"))
12
+ provider_settings=Kernel.const_get('Provider').const_get("#{provider.capitalize}")::SETTINGS
13
+ Settings.instance.register_provider(provider.to_sym, provider_settings)
14
+ ObjectSpace.define_finalizer(self, Proc.new{Settings.instance.unregister_provider(provider)})
15
+ end
16
+
17
+ def copy_sources # that's upload sources
18
+ config = Settings.instance
19
+ remote = config.send(@action, :folder, pkg: @pkg, type: @type, env: @env)
20
+ local = File.expand_path config.local(:folder, pkg: @pkg, type: @type, env: @env)
21
+ cfg = config.send(@action, pkg: @pkg, type: @type, env: @env)
22
+ upload_sources(cfg, local, remote)
23
+ end
24
+
25
+ def get_file(file)
26
+ config = Settings.instance
27
+ local = File.expand_path config.local(:temp, pkg: @pkg, type: @type, env: @env)
28
+ cfg = config.send(@action, pkg: @pkg, type: @type, env: @env)
29
+ download_file(cfg, file, local)
30
+ end
31
+
32
+ def put_file(file)
33
+ config = Settings.instance
34
+ local = File.expand_path config.local(:temp, pkg: @pkg, type: @type, env: @env)
35
+ cfg = config.send(@action, pkg: @pkg, type: @type, env: @env)
36
+ upload_file(cfg, file, local)
37
+ end
38
+
39
+ def do_action(to=nil)
40
+ config = Settings.instance
41
+ cfg =config.send(@action, pkg: @pkg, type: @type, env: @env)
42
+ command = cfg[:command].
43
+ gsub('%f', cfg[:folder]).
44
+ gsub('%p', @pkg.to_s).
45
+ gsub('%T', @type.to_s)
46
+ command = command.gsub('%t', cfg[:target]) if cfg.has_key? :target
47
+ command = command.gsub('%a', to) unless to.nil?
48
+
49
+ exec_command(cfg, command)
50
+ end
51
+
52
+ end
53
+ end
@@ -8,65 +8,55 @@ class Hash
8
8
  end
9
9
 
10
10
 
11
-
12
11
  module Devinstall
13
12
 
14
- class KeyNotDefinedError < RuntimeError;end
15
- class UnknownKeyError < RuntimeError;end
13
+ class KeyNotDefinedError < RuntimeError
14
+ end
15
+ class UnknownKeyError < RuntimeError
16
+ end
16
17
 
17
18
  class Settings
18
19
  include Singleton
19
20
 
20
- FILES = []
21
- SETTINGS = {}
22
- MDEFS={
23
- local: [:folder, :temp],
24
- build: [:user, :host, :folder, :target, :arch, :command, :provider],
25
- install: [:user, :host, :folder, :type, :arch, :provider],
26
- tests: [:machine, :folder, :user, :command, :provider],
27
- repos: [:user, :host, :folder, :type, :arch]
21
+ FILES = []
22
+ SETTINGS = {}
23
+ MDEFS = {
24
+ local: [:folder, :temp],
25
+ build: [:folder, :command, :provider, :type, :arch, :target],
26
+ install: [:folder, :command, :provider, :type, :arch],
27
+ tests: [:folder, :command, :provider],
28
+ repos: [:folder, :provider, :type, :arch],
29
+ defaults: [:type, :env]
28
30
  }
29
-
31
+ PROVIDERS = {}
30
32
  class Action
31
33
  include Enumerable
32
34
 
33
- def initialize(set, sect, pkg, type, env)
34
- @set=set
35
- @sect=sect
36
- @pkg=pkg
37
- @type=type
38
- @env=env
35
+ def initialize(m, pkg, type, env)
36
+ @method, @pkg, @type, @env = (m.to_sym rescue m), (pkg.to_sym rescue pkg), (type.to_sym rescue type), (env.to_sym rescue env)
39
37
  end
40
38
 
41
- def valid?
42
- config = Settings.instance
43
- return false unless Settings::MDEFS.has_key? @sect
44
- Settings::MDEFS[@sect].inject (true) do |res, k|
45
- res and config.respond_to? @sect and config.send(@sect, k, pkg: @pkg, type: @type, env: @env)
46
- end
47
- rescue KeyNotDefinedError => e
48
- puts e.message
49
- puts e.backtrace if $verbose
50
- return false
39
+ def has_key?(key)
40
+ Settings.instance.send(@method, key, pkg: @pkg, type: @type, env: @env) rescue false
51
41
  end
52
42
 
53
43
  def [](key)
54
- Settings.instance.send(@sect, key, pkg: @pkg, type: @type, env: @env)
44
+ Settings.instance.send(@method, key, pkg: @pkg, type: @type, env: @env)
55
45
  end
56
46
 
57
47
  def each
58
48
  config=Settings.instance
59
- Settings::MDEFS[@sect].each do |key|
60
- yield(key, config.send(@sect, key, pkg: @pkg, type: @type, env: @env)) if block_given?
49
+ Settings::MDEFS[@method].each do |key|
50
+ yield(key, config.send(@method, key, pkg: @pkg, type: @type, env: @env)) if block_given?
61
51
  end
62
52
  end
63
- end ## Class Action
53
+ end ## Class Action
64
54
 
65
55
  def load! (filename)
66
56
  if File.exist?(File.expand_path(filename))
67
57
  unless FILES.include? filename
68
58
  FILES << filename
69
- data = YAML::load_file(filename).deep_symbolize
59
+ data = YAML::load_file(filename).deep_symbolize
70
60
  merger = proc do |_, v1, v2|
71
61
  Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2
72
62
  end
@@ -75,40 +65,33 @@ class UnknownKeyError < RuntimeError;end
75
65
  end
76
66
  end
77
67
 
78
- def defaults(key=nil)
79
- return SETTINGS.has_key? :defaults if key.nil?
80
- raise UnknownKeyError, "Unknowwn key: '#{key}'" unless [:package, :type, :env, :providers].include? key
81
- return nil unless key_chain(:defaults, key)
82
- SETTINGS[:defaults][key].to_sym or raise KeyNotDefinedError, "Undefined key :default:#{key.to_s}"
68
+ def method_missing (method, *args)
69
+ raise UnknownKeyError, "Undefined section '#{method}'" unless method_defined? method
70
+ key = (args.shift or {})
71
+ rest = (Hash === key) ? key : (args.shift or {})
72
+ pkg = rest[:pkg]
73
+ if pkg.nil?
74
+ raise UnknownKeyError, "Unknown key #{key}" unless key_defined? method,key
75
+ return SETTINGS[method][key] rescue raise "#{method}: Package must be defined"
76
+ end
77
+ type = rest[:type] || defaults(:type)
78
+ env = rest[:env] || defaults(:env)
79
+ return Action.new(method, pkg, type, env) if Hash === key
80
+ raise UnknownKeyError, "Unknown key #{key}" unless key_defined? method,key
81
+ global_or_local(method, key, pkg, type, env) or
82
+ raise KeyNotDefinedError, "Undefined key '#{method}:#{key}' or alternate for ['#{pkg}' '#{type}' '#{env}']"
83
83
  end
84
84
 
85
- def base(key=nil)
86
- return SETTINGS.has_key? :base if key.nil?
87
- raise UnknownKeyError, "Unknown key: '#{key}'" unless [:rsync, :ssh, :sudo, :scp].include? key
88
- return nil unless key_chain(:base, key)
89
- SETTINGS[:base][key] or raise KeyNotDefinedError, "Undefined key :base:#{key.to_s}"
85
+ def respond_to_missing?(method, _)
86
+ method_defined? method
90
87
  end
91
88
 
92
- def method_missing (m, *args)
93
- raise UnknownKeyError, "Undefined section '#{m}'" unless MDEFS.has_key? m
94
- key=(args.shift or {})
95
- if Hash === key
96
- pkg = key[:pkg] or raise 'package must be defined'
97
- type = (key[:type] or defaults(:type))
98
- env = (key[:env] or defaults(:env))
99
- return Action.new(SETTINGS, m, pkg, type, env)
100
- end
101
- rest=(args.shift or {})
102
- (pkg = rest[:pkg]) or raise 'package must be defined'
103
- type = (rest[:type] or defaults(:type))
104
- env = (rest[:env] or defaults(:env))
105
- pkg=pkg.to_sym
106
- raise UnknownKeyError, "Unknown key #{key}" unless MDEFS[m].include? key
107
- global_or_local(m, key, pkg, type, env) or raise KeyNotDefinedError, "Undefined key '#{m}:#{key}' or alternate for ['#{pkg}' '#{type}' '#{env}']"
89
+ def register_provider(provider, methods)
90
+ PROVIDERS[provider]=methods
108
91
  end
109
92
 
110
- def respond_to_missing?(method, _)
111
- MDEFS.has_key? method and SETTINGS.has_key? method
93
+ def unregister_provider(provider)
94
+ PROVIDERS.delete(provider)
112
95
  end
113
96
 
114
97
  private
@@ -117,8 +100,8 @@ class UnknownKeyError < RuntimeError;end
117
100
  res=SETTINGS
118
101
  keys.each do |key|
119
102
  next if key.nil?
120
- return nil unless res.has_key? key
121
- res=res[key]
103
+ return nil unless res.has_key? key.to_sym
104
+ res=res[key.to_sym]
122
105
  end
123
106
  res
124
107
  end
@@ -130,6 +113,20 @@ class UnknownKeyError < RuntimeError;end
130
113
  key_chain(section, key)
131
114
  end
132
115
 
116
+ def key_defined?(method, key)
117
+ method, key = (method.to_sym rescue method), (key.to_sym rescue key)
118
+ method_defined? method and
119
+ (MDEFS[method].include? key rescue false) or
120
+ PROVIDERS.inject(false){|res,(_,v)| res or (v[method].include? key rescue false)}
121
+ end
122
+
123
+ def method_defined?(method)
124
+ method = (method.to_sym rescue method)
125
+ (MDEFS.has_key?(method) or
126
+ PROVIDERS.inject(false){|res,(k,_)| res or PROVIDERS[k].has_key? method}) and
127
+ SETTINGS.has_key? method
128
+ end
129
+
133
130
  end
134
131
  end
135
132
 
@@ -1,3 +1,3 @@
1
1
  module Devinstall
2
- VERSION = '1.1.0' # Working!
2
+ VERSION = '1.2.0' # Working!
3
3
  end
data/lib/devinstall.rb CHANGED
@@ -2,6 +2,7 @@ require 'devinstall/version'
2
2
  require 'devinstall/deep_symbolize'
3
3
  require 'devinstall/utils'
4
4
  require 'devinstall/settings'
5
- require 'devinstall/pkg'
5
+ require 'devinstall/package'
6
6
  require 'devinstall/cli'
7
+ require 'devinstall/provider'
7
8
 
data/spec/pkg_spec.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'rspec'
2
+ require 'devinstall'
3
+
4
+ describe 'Packages' do
5
+ config=Devinstall::Settings.instance
6
+ config.load! './doc/example.yml' ## use defaults for type and env
7
+
8
+ package=:devinstall
9
+ type=:deb
10
+ env=nil
11
+
12
+ it 'should include the correct file' do
13
+ pk=Devinstall::Package.new(package, type, env)
14
+ expect(pk.singleton_class.include?(Pkg::Deb)).to be_true
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ require 'rspec'
2
+ require 'devinstall'
3
+
4
+ describe 'Provider' do
5
+ before do
6
+ @package, @type, @env, @action = :devinstall, :deb, :dev, :install
7
+ # config=Devinstall::Settings.instance
8
+ end
9
+
10
+ it 'should load the correct (Provider::Ssh) plugin' do
11
+ provider = Devinstall::Provider.new(@package, @type, @env, @action)
12
+ expect(provider.singleton_class.include? Provider::Ssh).to be_true
13
+ end
14
+ end
@@ -10,6 +10,8 @@ describe 'Settings' do
10
10
  type=:deb
11
11
  env=nil
12
12
 
13
+ $verbose=true
14
+
13
15
  it 'should load an existig file' do
14
16
  expect(config.load!('doc/example.yml')).to be_true
15
17
  end
@@ -20,56 +22,59 @@ describe 'Settings' do
20
22
 
21
23
  it 'should load at init' do
22
24
  expect(config).to be_an_instance_of(Devinstall::Settings)
23
- [:defaults, :base, :local, :build, :install, :tests, :repos].each do |p|
25
+ [:defaults, :local, :build, :install, :tests, :repos].each do |p|
24
26
  expect(config.respond_to? p).to be_true
25
27
  end # all sections loaded!
26
28
  end
27
29
 
28
- it 'should have defaults' do
29
- expect(config.defaults).to be_true
30
- end
31
-
32
- it 'should produce validators when hashes are given' do
33
- expect(config.build(type:'deb', env:'dev', pkg:package)).to be_an_instance_of(Devinstall::Settings::Action)
30
+ it 'should have a method defaults' do
31
+ expect(config.respond_to? :defaults).to be_true
34
32
  end
35
33
 
36
- it 'should produce validators when partial hashes are given' do
37
- expect(config.build(pkg:package)).to be_an_instance_of(Devinstall::Settings::Action)
34
+ it 'the method "defaults" should have only one argument' do
35
+ expect(config.defaults(:type)).to eq "deb"
38
36
  end
39
37
 
40
- it 'should validate correct data' do
41
- expect(rr=config.build(pkg:package, type:type, env:env)).to be_an_instance_of(Devinstall::Settings::Action)
42
- expect(rr.valid?).to be_true
38
+ it 'should instantiate a new "Action" when hashes are given' do
39
+ expect(config.build(type:'deb', env:'dev', pkg:package)).to be_an_instance_of(Devinstall::Settings::Action)
43
40
  end
44
41
 
45
- it 'should not validate incorrect data' do
46
- expect(rr=config.build(pkg:package, type: :rpm, env:env)).to be_an_instance_of(Devinstall::Settings::Action)
47
- expect(rr.valid?).to be_false
42
+ it 'should instantiata a new "Action" when partial hashes are given' do
43
+ expect(config.build(pkg:package)).to be_an_instance_of(Devinstall::Settings::Action)
48
44
  end
49
45
 
50
- it 'should raise errors for unknown keys' do
46
+ it 'should raise "UnknownKeys" errors for unknown keys' do
51
47
  expect{config.defaults :none}.to raise_error(Devinstall::UnknownKeyError)
52
48
  end
53
49
 
54
- it 'should raise errors for undefined keys' do
50
+ it 'should raise "KeyNotDefinedError" errors for undefined keys' do
55
51
  expect{config.tests(:provider, pkg:package)}.to raise_error(Devinstall::KeyNotDefinedError)
56
52
  end
57
53
 
58
- it 'should produce a value if aok' do
59
- expect(config.build(:user, pkg:package)).to eq('dboca')
54
+ it 'should produce a value if all arguments are valid' do
55
+ expect(config.build(:command, pkg:package)).to eq('cd %f/%p && dpkg-buildpackage')
60
56
  end
57
+ end
58
+ describe "Action" do
59
+ config=Devinstall::Settings.instance
60
+ config.load! './doc/example.yml' ## use defaults for type and env
61
+
62
+ package=:devinstall
63
+ type=:deb
64
+ env=nil
61
65
 
62
- it 'validator should have a [] method' do
66
+ $verbose=true
67
+
68
+ it 'should have a [] method' do
63
69
  rr=config.build(pkg:package, type:type, env:env)
64
- expect(rr[:user]).to eq('dboca')
70
+ expect(rr[:target]).to eq('rs')
65
71
  end
66
72
 
67
73
  it 'should enumerate all defined values' do
68
74
  ar=[]
69
- config.build(pkg:package, type:type, env:env).each do |k,v|
70
- ar << k
75
+ config.build(pkg:package, type:type, env:env).each do |k,_|
76
+ ar << k
71
77
  end
72
- expect(ar).to eql([:user, :host, :folder, :target, :arch, :command, :provider])
78
+ expect(ar.sort == [:folder, :command, :provider, :type, :arch, :target].sort ).to be_true
73
79
  end
74
-
75
80
  end
metadata CHANGED
@@ -1,97 +1,86 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devinstall
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 1.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Dragos Boca
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-03 00:00:00.000000000 Z
11
+ date: 2013-05-05 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: getopt
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rspec
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: commander
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
- description: remote builder and installer
83
+ description: OS package builder and installer
95
84
  email:
96
85
  - dboca@mail.com
97
86
  executables:
@@ -111,36 +100,42 @@ files:
111
100
  - lib/devinstall.rb
112
101
  - lib/devinstall/cli.rb
113
102
  - lib/devinstall/deep_symbolize.rb
114
- - lib/devinstall/pkg.rb
103
+ - lib/devinstall/package.rb
104
+ - lib/devinstall/pkg/pkg_deb.rb
105
+ - lib/devinstall/provider.rb
106
+ - lib/devinstall/provider/provider_ssh.rb
115
107
  - lib/devinstall/settings.rb
116
108
  - lib/devinstall/utils.rb
117
109
  - lib/devinstall/version.rb
110
+ - spec/pkg_spec.rb
111
+ - spec/provider_spec.rb
118
112
  - spec/settings_spec.rb
119
113
  homepage: http://github.com/dboca/devinstall
120
114
  licenses:
121
115
  - MIT
116
+ metadata: {}
122
117
  post_install_message:
123
118
  rdoc_options: []
124
119
  require_paths:
125
120
  - lib
126
121
  required_ruby_version: !ruby/object:Gem::Requirement
127
- none: false
128
122
  requirements:
129
- - - ! '>='
123
+ - - '>='
130
124
  - !ruby/object:Gem::Version
131
125
  version: '0'
132
126
  required_rubygems_version: !ruby/object:Gem::Requirement
133
- none: false
134
127
  requirements:
135
- - - ! '>='
128
+ - - '>='
136
129
  - !ruby/object:Gem::Version
137
130
  version: '0'
138
131
  requirements: []
139
132
  rubyforge_project:
140
- rubygems_version: 1.8.25
133
+ rubygems_version: 2.0.3
141
134
  signing_key:
142
- specification_version: 3
135
+ specification_version: 4
143
136
  summary: Copy the source files to a build host, build the packages and install builded
144
137
  packages
145
138
  test_files:
139
+ - spec/pkg_spec.rb
140
+ - spec/provider_spec.rb
146
141
  - spec/settings_spec.rb
@@ -1,178 +0,0 @@
1
- require 'devinstall/version'
2
- require 'devinstall/deep_symbolize'
3
- require 'devinstall/utils'
4
- require 'devinstall/settings'
5
- require 'pp'
6
-
7
- module Devinstall
8
- class UndeffError < RuntimeError; end
9
-
10
- class Pkg
11
- include Utils
12
-
13
- def get_version(pkg, type, env)
14
- config=Settings.instance
15
- folder=config.local(:folder, pkg:pkg, type:type, env:env)
16
- case type
17
- when :deb
18
- begin
19
- deb_changelog = File.expand_path "#{folder}/#{pkg}/debian/changelog"
20
- unless File.exists? deb_changelog
21
- exit! <<-eos
22
- No 'debian/changelog' found in specified :local:folder (#{folder})
23
- Please check your config file
24
- eos
25
- end
26
- @_package_version[:deb] = File.open(deb_changelog, 'r') { |f| f.gets.chomp.sub(/^.*\((.*)\).*$/, '\1') }
27
- rescue IOError => e
28
- exit! <<-eos
29
- IO Error while opening #{deb_changelog}
30
- Aborting \n #{e}
31
- eos
32
- end
33
- else
34
- raise UndeffError, "TODO package type #{type}"
35
- end
36
- end
37
-
38
- # @param [String] package
39
- def initialize(package, type, env)
40
- config=Settings.instance #class variable,first thing!
41
- @type=type
42
- @env=env
43
- @package = package
44
- @_package_version = {} # versions for types:
45
- @package_files = {}
46
- arch = config.build(pkg:package, type:type, env:env)[:arch]
47
- p_name = "#{@package}_#{get_version(package, type, env)}"
48
- @package_files[:deb] = {deb: "#{p_name}_#{arch}.deb",
49
- tgz: "#{p_name}.tar.gz",
50
- dsc: "#{p_name}.dsc",
51
- chg: "#{p_name}_amd64.changes"}
52
- end
53
-
54
- def upload(pkg=@package, type=@type, env=@env)
55
- config = Settings.instance
56
- scp = config.base(:scp)
57
- repo = config.repos(pkg:pkg, type:type, env:env)
58
- local = config.local(pkg:pkg, type:type, env:env)
59
-
60
- @package_files[type].each do |p, f|
61
- puts "Uploading #{f}\t\t[#{p}] to #{repo[:host]}"
62
- command("#{scp} #{local[:temp]}/#{f} #{repo[:user]}@#{repo[:host]}:#{repo[:folder]}")
63
- end
64
- rescue CommandError => e
65
- puts e.verbose_message
66
- exit! ''
67
- rescue KeyNotDefinedError => e
68
- puts e.message
69
- exit! ''
70
- end
71
-
72
- def build(pkg=@package, type=@type, env=@env)
73
- config = Settings.instance
74
- puts "Building package #{pkg} type #{type}"
75
- build = config.build(pkg:pkg, type:type, env:env)
76
- local = config.local(pkg:pkg, type:type, env:env)
77
- raise 'Invaild build configuration' unless build.valid?
78
-
79
- ssh = config.base(:ssh)
80
- rsync = config.base(:rsync)
81
- local_folder = File.expand_path local[:folder]
82
- local_temp = File.expand_path local[:temp]
83
-
84
- build_command = build[:command].gsub('%f', build[:folder]).
85
- gsub('%t', build[:target]).
86
- gsub('%p', pkg.to_s).
87
- gsub('%T', type.to_s)
88
-
89
- upload_sources("#{local_folder}/", "#{build[:user]}@#{build[:host]}:#{build[:folder]}")
90
- command("#{ssh} #{build[:user]}@#{build[:host]} \"#{build_command}\"")
91
- @package_files[type].each do |p, t|
92
- puts "Receiving target #{p.to_s} for #{t.to_s}"
93
- command("#{rsync} -az #{build[:user]}@#{build[:host]}:#{build[:target]}/#{t} #{local_temp}")
94
- end
95
- rescue CommandError => e
96
- puts e.verbose_message
97
- exit! ''
98
- rescue KeyNotDefinedError => e
99
- puts e.message
100
- exit! ''
101
- end
102
-
103
- def install(pkg=@package, type=@type, env=@env)
104
- config=Settings.instance
105
- puts "Installing #{pkg} in #{env} environment."
106
- install=config.install(pkg:pkg, type:type, env:env)
107
- temp =config.local(pkg:pkg, type:type, env:env)[:temp]
108
-
109
- sudo = config.base(:sudo)
110
- scp = config.base(:scp)
111
-
112
- install[:host] = [install[:host]] unless Array === install[:host]
113
- case type
114
- when :deb
115
- install[:host].each do |host|
116
- command("#{scp} #{temp}/#{@package_files[type][:deb]} #{install[:user]}@#{host}:#{install[:folder]}")
117
- command("#{sudo} #{install[:user]}@#{host} /usr/bin/dpkg -i #{install[:folder]}/#{@package_files[type][:deb]}")
118
- end
119
- else
120
- exit! "unknown package type '#{type.to_s}'"
121
- end
122
- rescue CommandError => e
123
- puts e.verbose_message
124
- exit! ''
125
- rescue KeyNotDefinedError => e
126
- puts e.message
127
- exit! ''
128
- end
129
-
130
- def run_tests(pkg=@package, type=@type, env=@env)
131
- config=Settings.instance
132
- # check if we have the test section in the configuration file
133
- unless config.respond_to? :tests
134
- puts 'No test section in the config file.'
135
- puts 'Skipping tests'
136
- return
137
- end
138
- # for tests we will use almost the same setup as for build
139
- test = config.tests(pkg:pkg, type:type, env:env)
140
- local = config.local(pkg:pkg, type:type, env:env)
141
- build = config.build(pkg:pkg, type:type, env:env)
142
-
143
- ssh = config.base(:ssh)
144
- # replace "variables" in commands
145
- command = test[:command].
146
- gsub('%f', test[:folder]).# %f is the folder where the sources are rsync-ed
147
- gsub('%t', build[:target]).# %t is the folder where the build places the result
148
- gsub('%p', pkg.to_s) # %p is the package name
149
- # take the sources from the local folder
150
- local_folder = File.expand_path local[:folder]
151
- # upload them to the test machine
152
- upload_sources("#{local_folder}/", "#{test[:user]}@#{test[:machine]}:#{test[:folder]}")
153
- puts 'Running all tests'
154
- puts 'This will take some time and you have no output'
155
- command("#{ssh} #{test[:user]}@#{test[:machine]} \"#{command}\"")
156
- rescue CommandError => e
157
- puts e.verbose_message
158
- exit! ''
159
- rescue KeyNotDefinedError => e
160
- puts e.message
161
- exit! ''
162
- end
163
-
164
- def upload_sources (source, dest)
165
- config=Settings.instance
166
- rsync = config.base(:rsync)
167
- command("#{rsync} -az #{source} #{dest}")
168
- end
169
- rescue CommandError => e
170
- puts e.verbose_message
171
- exit! ''
172
- rescue KeyNotDefinedError => e
173
- puts e.message
174
- exit! ''
175
- end
176
-
177
- end
178
-