devinstall 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
-