devinstall 1.0.1 → 1.1.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.
data/bin/pkg-tool CHANGED
@@ -1,36 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'rubygems'
3
3
  require 'bundler/setup'
4
- require 'devinstall/cli'
4
+ require 'devinstall'
5
5
 
6
- unless ARGV[0]
7
- puts 'You must specify an action'
8
- exit! ''
9
- end
10
-
11
- package = [] # creata a new Array
12
- command = ARGV.shift
13
-
14
- while ARGV[0] and ARGV[0][0..1] != '--'
15
- package << ARGV.shift
16
- end
17
-
18
- if %w"version --version -v".include? command
19
- command = 'version'
20
- end
21
-
22
- if %w"help --help -h".include? command
23
- command = 'help'
24
- end
25
-
26
- unless %w"build install upload test help version".include? command
27
- puts "Unknown command #{command}"
28
- exit! 1
29
- end
30
-
31
- ## the next should be the package
32
- # so if ARGV[0] don't start with --
33
- # we name it package
34
- cli=Devinstall::Cli.new(*package) ## Cli can be called for multiple packages
35
- cli.send(command.to_sym)
6
+ include Cli
36
7
 
data/devinstall.gemspec CHANGED
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency 'rake'
23
23
  spec.add_development_dependency 'getopt'
24
24
  spec.add_development_dependency 'rspec'
25
+ spec.add_development_dependency 'commander'
25
26
  end
data/doc/example.yml CHANGED
@@ -12,7 +12,7 @@ base:
12
12
  arch: debian64
13
13
  defaults:
14
14
  package: devinstall
15
- env: dev
15
+ # env: dev
16
16
  type: deb
17
17
  tests:
18
18
  # dev is the environment
@@ -1,95 +1,94 @@
1
- require 'devinstall'
1
+ require 'devinstall/pkg'
2
2
  require 'getopt/long'
3
3
  require 'devinstall/settings'
4
+ require 'commander/import'
4
5
 
5
- module Devinstall
6
- class Cli
6
+ module Cli
7
7
 
8
- include Utils
8
+ program :name, 'DevInstall'
9
+ program :version, Devinstall::VERSION
10
+ program :description, 'Poor man builder/installer'
9
11
 
10
- def get_config(*fnames)
11
- config=nil
12
- fnames.each do |f|
13
- (config ||= (File.expand_path(f) if File.exist? f)) and break
14
- end
15
- config
12
+ global_option('--config FILE' 'Configuration file to be used') do |file|
13
+ unless Devinstall::Settings.instance.load! file
14
+ puts "Couldn't find #{file}"
15
+ exit!
16
16
  end
17
+ end
17
18
 
18
- def initialize(*package)
19
- begin
20
- opt = Getopt::Long.getopts(
21
- %w(--config -c),
22
- %w(--type -t),
23
- %w(--env -e),
24
- %w(--verbose -v),
25
- %w(--dry-run -d),
26
- )
27
- rescue
28
- puts 'Invalid option at command line'
29
- help
30
- end
31
- #verbose and dry-run
32
- $verbose ||= opt['verbose']
33
- $dry ||= opt['dry-run']
34
- # get config file
35
- config = Devinstall::Settings.instance # is a singleton so we don't use new here
36
- cfgfile = get_config('./devinstall.yml', '~/.devinstall.yml', opt['config'])
37
- exit! 'You must specify the config file' if cfgfile.empty?
38
- config.load! cfgfile # load cfgfile
39
- config.env = opt['env'] || config.env
40
- config.type = opt['type'] || config.type
41
- @packages = package || []
42
- @packages = config.defaults(:package) if @packages.empty?
43
- exit! 'You must ask for a package' if @packages.empty?
44
- config.validate
45
- rescue KeyNotDefinedError => e
46
- exit! e.message
19
+ global_option('--verbose', 'Verbose output') { $verbose=true }
20
+ global_option('--dry-run', 'Dry-run; don\'t run commands, just pretend to') { $dry=true }
21
+ global_option('--type STRING', 'Package type (deb, rpm, tgz). Currently only deb')
22
+ global_option('--env STRING', 'Package environment to be built for')
23
+
24
+ def load_defaults
25
+ %w(./devinstall.yml ./.devinstall.yml ~/.devinstall).each do |f|
26
+ Devinstall::Settings.instance.load! f and return true
47
27
  end
28
+ puts "Couldn't find default config file and no --config option given at command line"
29
+ exit!
30
+ end
31
+
32
+ command :build do |c|
33
+ c.action do |args, options|
34
+ config=Devinstall::Settings.instance
35
+ load_defaults unless options.config
36
+ type = options.type ? options.type.to_sym : config.defaults(:type)
37
+ env = options.env ? options.env.to_sym : config.defaults(:env)
48
38
 
49
- def build
50
- @packages.each do |package|
51
- pk=Devinstall::Pkg.new(package)
39
+ args.each do |p|
40
+ pk=Devinstall::Pkg.new(p, type, env)
52
41
  pk.build
53
42
  end
54
43
  end
44
+ end
45
+
46
+ command :install do |c|
47
+ c.action do |args, options|
48
+ config=Devinstall::Settings.instance
49
+ load_defaults unless options.config
50
+ type = options.type ? options.type.to_sym : config.defaults(:type)
51
+ env = options.env ? options.env.to_sym : config.defaults(:env)
55
52
 
56
- def install
57
- @packages.each do |package|
58
- pk=Devinstall::Pkg.new(package)
53
+ args.each do |p|
54
+ pk=Devinstall::Pkg.new(p, type, env)
59
55
  pk.build
60
56
  pk.install
61
57
  end
62
58
  end
59
+ end
63
60
 
64
- def upload
65
- @packages.each do |package|
66
- pk=Devinstall::Pkg.new(package)
67
- pk.build
68
- pk.run_tests
69
- pk.upload
70
- end
71
- end
61
+ command :test do |c|
62
+ c.action do |args, options|
63
+ config=Devinstall::Settings.instance
64
+ load_defaults unless options.config
65
+ type = options.type ? options.type.to_sym : config.defaults(:type)
66
+ env = options.env ? options.env.to_sym : config.defaults(:env)
72
67
 
73
- def test
74
- @packages.each do |package|
75
- pk=Devinstall::Pkg.new(package)
68
+ args.each do |p|
69
+ pk=Devinstall::Pkg.new(p, type, env)
76
70
  pk.run_tests
77
71
  end
78
72
  end
73
+ end
79
74
 
80
- def help
81
- puts 'Usage:'
82
- puts 'pkg-tool command [package_name ... ] --config|-c <file> --type|-t <package_type> --env|-e <environment>'
83
- puts 'where command is one of the: build, install, upload, help, version'
84
- exit! ''
85
- end
75
+ command :upload do |c|
76
+ c.action do |args, options|
77
+ config=Devinstall::Settings.instance
78
+ load_defaults unless options.config
79
+ type = options.type ? options.type.to_sym : config.defaults(:type)
80
+ env = options.env ? options.env.to_sym : config.defaults(:env)
86
81
 
87
- def version
88
- puts "devinstall version #{Devinstall::VERSION}"
89
- puts "pkg-tool version #{Devinstall::VERSION}"
90
- exit! ''
82
+ args.each do |p|
83
+ pk=Devinstall::Pkg.new(p, type, env)
84
+ pk.build
85
+ pk.run_tests
86
+ pk.upload
87
+ end
91
88
  end
92
-
93
89
  end
90
+
94
91
  end
95
92
 
93
+ __END__
94
+
@@ -0,0 +1,178 @@
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
+
@@ -7,142 +7,127 @@ class Hash
7
7
  include DeepSymbolizable
8
8
  end
9
9
 
10
- module Devinstall
11
10
 
12
- class KeyNotDefinedError < RuntimeError;
13
- end
14
11
 
15
- class UnknownKeyError < RuntimeError;
16
- end
12
+ module Devinstall
13
+
14
+ class KeyNotDefinedError < RuntimeError;end
15
+ class UnknownKeyError < RuntimeError;end
17
16
 
18
17
  class Settings
19
18
  include Singleton
20
19
 
21
- attr_accessor :env, :type
22
20
  FILES = []
23
21
  SETTINGS = {}
24
-
25
- def pkg=(pkg)
26
- if Array === pkg and pkg.length == 1
27
- @pkg = pkg[0]
28
- elsif pkg.class.method_defined? :to_sym
29
- @pkg = pkg.to_sym
30
- else
31
- @pkg = pkg
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]
28
+ }
29
+
30
+ class Action
31
+ include Enumerable
32
+
33
+ def initialize(set, sect, pkg, type, env)
34
+ @set=set
35
+ @sect=sect
36
+ @pkg=pkg
37
+ @type=type
38
+ @env=env
32
39
  end
33
- end
34
40
 
35
- def pkg
36
- @pkg
37
- end
38
-
39
- def validate
40
- raise KeyNotDefinedError, 'Missing package' unless self.pkg
41
- raise KeyNotDefinedError, 'Missing environment' unless self.env
42
- raise KeyNotDefinedError, 'Missing package type' unless self.type
43
- if Array === self.pkg
44
- self.pkg.each do |p|
45
- raise KeyNotDefinedError, "Package '#{p}' not defined" unless SETTINGS[:packages].has_key? p.to_sym
46
- raise KeyNotDefinedError, "Package #{p} type '#{type}' not defined " unless SETTINGS[:packages][p.to_sym].has_key? self.type
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)
47
46
  end
48
- else
49
- raise KeyNotDefinedError, "Package '#{pkg}' not defined" unless SETTINGS[:packages].has_key? self.pkg.to_sym
50
- raise KeyNotDefinedError, "Package #{pkg} type '#{type}' not defined " unless SETTINGS[:packages][self.pkg.to_sym].has_key? self.type
47
+ rescue KeyNotDefinedError => e
48
+ puts e.message
49
+ puts e.backtrace if $verbose
50
+ return false
51
51
  end
52
- rescue KeyNotDefinedError => e
53
- raise e
54
- rescue UnknownKeyError => e
55
- puts "Program error: #{e.message} at:"
56
- puts e.backtrace
57
- exit!
58
- end
59
52
 
60
- def load! (filename) # Multiple load -> merge settings
61
- unless File.exist?(File.expand_path(filename))
62
- puts "Unable to find config file \"#{File.expand_path(filename)}\""
63
- exit!
53
+ def [](key)
54
+ Settings.instance.send(@sect, key, pkg: @pkg, type: @type, env: @env)
64
55
  end
65
- unless FILES.include? filename
66
- FILES << filename
67
- newsets = YAML::load_file(filename).deep_symbolize
68
- deep_merge!(SETTINGS, newsets)
69
- end
70
- ### initialize type, env from defaults unless already defined
71
- self.env ||= defaults(:env).to_sym
72
- self.type ||= defaults(:type).to_sym
73
- end
74
56
 
75
- def deep_merge!(target, data)
76
- merger = proc do |_, v1, v2|
77
- Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2
57
+ def each
58
+ 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?
61
+ end
62
+ end
63
+ end ## Class Action
64
+
65
+ def load! (filename)
66
+ if File.exist?(File.expand_path(filename))
67
+ unless FILES.include? filename
68
+ FILES << filename
69
+ data = YAML::load_file(filename).deep_symbolize
70
+ merger = proc do |_, v1, v2|
71
+ Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2
72
+ end
73
+ SETTINGS.merge! data, &merger
74
+ end
78
75
  end
79
- target.merge! data, &merger
80
- end
81
-
82
- def self.defaults(key=nil)
83
- return SETTINGS.has_key? :defaults if key.nil?
84
- raise UnknownKeyError, "Don't know what are you asking about: '#{key}'" unless [:package, :type, :env, :providers].include? key
85
- SETTINGS[:defaults][key] or raise KeyNotDefinedError, "Undefined key :default:#{key.to_s}"
86
76
  end
87
77
 
88
78
  def defaults(key=nil)
89
- self.class.defaults key
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}"
90
83
  end
91
84
 
92
- def self.base(key=nil)
85
+ def base(key=nil)
93
86
  return SETTINGS.has_key? :base if key.nil?
94
- raise UnknownKeyError, "Don't know what are you asking about: '#{key}'" unless [:rsync, :ssh, :sudo, :scp].include? key
87
+ raise UnknownKeyError, "Unknown key: '#{key}'" unless [:rsync, :ssh, :sudo, :scp].include? key
88
+ return nil unless key_chain(:base, key)
95
89
  SETTINGS[:base][key] or raise KeyNotDefinedError, "Undefined key :base:#{key.to_s}"
96
90
  end
97
91
 
98
- def base(key=nil)
99
- self.class.base key
100
- end
101
-
102
- def global_or_local(section, key)
103
- ret = nil
104
- if SETTINGS[:packages][self.pkg][self.type].has_key? section
105
- ret ||= SETTINGS[:packages][self.pkg][self.type][section][self.env][key] if SETTINGS[:packages][self.pkg][self.type][section].has_key? self.env
106
- ret ||= SETTINGS[:packages][self.pkg][self.type][section][key] # or nil
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)
107
100
  end
108
- ret ||= SETTINGS[section][self.env][key] if SETTINGS[section].has_key? self.env
109
- ret ||= SETTINGS[section][key] # or nil
110
- ret
111
- end
112
-
113
- def local(key=nil)
114
- return SETTINGS.has_key? :local if key.nil?
115
- raise UnknownKeyError, "Don't know what are you asking about: '#{key}'" unless [:folder, :temp].include? key
116
- global_or_local(:local, key) or raise KeyNotDefinedError, "Undefined key :local:#{key} or :#{self.pkg}:local:#{key}"
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}']"
117
108
  end
118
109
 
119
- def build(key=nil)
120
- return (SETTINGS.has_key? :build or SETTINGS[:packages][self.pkg][self.type].has_key? :build) if key.nil?
121
- raise UnknownKeyError, "Don't know what are you asking about: '#{key}'" unless [:user, :host, :folder, :target, :arch, :command, :provider].include? key
122
- global_or_local(:build, key) or raise KeyNotDefinedError, "Undefined key :build:#{key} or :#{self.pkg}:#{self.type}:build:#{key}"
110
+ def respond_to_missing?(method, _)
111
+ MDEFS.has_key? method and SETTINGS.has_key? method
123
112
  end
124
113
 
125
- def install(key=nil)
126
- return SETTINGS.has_key? :install if key.nil?
127
- raise UnknownKeyError, "Don't know what are you asking about: '#{key}'" unless [:user, :host, :folder, :type, :arch, :provider].include? key
128
- global_or_local(:install, key) or raise KeyNotDefinedError, "Undefined key :install:#{self.env.to_s}:#{key} or :#{self.pkg}:install:#{self.env.to_s}:#{key}"
129
- end
114
+ private
130
115
 
131
- def tests(key=nil) # tests don't have 'env'
132
- return SETTINGS.has_key?(:tests) if key.nil?
133
- raise UnknownKeyError, "Don't know what are you asking about: '#{key}'" unless [:machine, :folder, :user, :command, :provider].include? key
134
- global_or_local(:tests, key) or raise KeyNotDefinedError, "Undefined key :tests:#{self.env.to_s}:#{key} or :#{self.pkg}:tests:#{self.env.to_s}:#{key}"
135
- end
136
-
137
- def repos(key=nil)
138
- return SETTINGS.has_key?(:repos) if key.nil?
139
- raise UnknownKeyError, "Don't know what are you asking about: '#{key}'" unless [:user, :host, :folder, :type, :arch].include? key
140
- global_or_local(:repos, key) or raise KeyNotDefinedError, "Undefined key :repos:environments:#{self.env.to_s}:#{key} or :#{self.pkg}:repos:#{self.env.to_s}:#{key}"
116
+ def key_chain(*keys)
117
+ res=SETTINGS
118
+ keys.each do |key|
119
+ next if key.nil?
120
+ return nil unless res.has_key? key
121
+ res=res[key]
122
+ end
123
+ res
141
124
  end
142
125
 
143
- def packages(key=nil)
144
- return SETTINGS.has_key?(:packages) if key.nil?
145
- SETTINGS[:packages][key] ## no checks here!
126
+ def global_or_local(section, key, pkg, type, env)
127
+ key_chain(:packages, pkg, type, section, env, key) ||
128
+ key_chain(:packages, pkg, type, section, key) ||
129
+ key_chain(section, env, key) ||
130
+ key_chain(section, key)
146
131
  end
147
132
 
148
133
  end
@@ -28,8 +28,8 @@ module Utils
28
28
  ret
29
29
  end
30
30
 
31
- def exit!(msg=nil)
32
- puts msg || 'Aborting!'
31
+ def exit!(msg='Aborting')
32
+ puts msg
33
33
  Kernel.exit 1
34
34
  end
35
35
 
@@ -1,3 +1,3 @@
1
1
  module Devinstall
2
- VERSION = '1.0.1' # Working!
2
+ VERSION = '1.1.0' # Working!
3
3
  end
data/lib/devinstall.rb CHANGED
@@ -2,168 +2,6 @@ require 'devinstall/version'
2
2
  require 'devinstall/deep_symbolize'
3
3
  require 'devinstall/utils'
4
4
  require 'devinstall/settings'
5
- require 'pp'
6
-
7
- module Devinstall
8
-
9
- class Pkg
10
-
11
- include Utils
12
-
13
- def get_version
14
- case @config.type
15
- when :deb
16
- begin
17
- deb_changelog = File.expand_path "#{@config.local(:folder)}/#{@package}/debian/changelog" # This is the folder that should be checked
18
- unless File.exists? deb_changelog
19
- exit! <<-eos
20
- No 'debian/changelog' found in specified :local:folder (#{@config.local(:folder)})
21
- Please check your config file
22
- eos
23
- end
24
- @_package_version[:deb] = File.open(deb_changelog, 'r') { |f| f.gets.chomp.sub(/^.*\((.*)\).*$/, '\1') }
25
- rescue IOError => e
26
- exit! <<-eos
27
- IO Error while opening #{deb_changelog}
28
- Aborting \n #{e}
29
- eos
30
- end
31
- end
32
- end
33
-
34
- # @param [String] package
35
- def initialize(package)
36
- @config=Settings.instance #class variable,first thing!
37
- @config.pkg=package # very important!
38
- @package = package # currently implemented only for .deb packages (for .rpm later :D)
39
- @_package_version = {} # versions for types:
40
- @package_files = {}
41
- arch = @config.build(:arch)
42
- p_name = "#{@package}_#{get_version}"
43
- @package_files[:deb] = {deb: "#{p_name}_#{arch}.deb",
44
- tgz: "#{p_name}.tar.gz",
45
- dsc: "#{p_name}.dsc",
46
- chg: "#{p_name}_amd64.changes"}
47
- end
48
-
49
- def upload
50
- scp = @config.base(:scp)
51
- repo = {}
52
- [:user, :host, :folder, :type].each do |k|
53
- repo[k] = @config.repos(k) # looks stupid
54
- end
55
- @package_files[type].each do |p, f|
56
- puts "Uploading #{f}\t\t[#{p}] to $#{repo[:host]}"
57
- command("#{scp} #{@config.local(:temp)}/#{f} #{repo[:user]}@#{repo[:host]}:#{repo[:folder]}")
58
- end
59
- rescue CommandError => e
60
- puts e.verbose_message
61
- exit! ''
62
- rescue KeyNotdefinederror => e
63
- puts e.message
64
- exit! ''
65
- end
66
-
67
- def build
68
- type = @config.type
69
- puts "Building package #{@package} type #{type}"
70
- build = {}
71
- [:user, :host, :folder, :target].each do |k|
72
- build[k] = @config.build(k)
73
- end
74
-
75
- ssh = @config.base(:ssh)
76
- build_command = @config.build(:command)
77
- rsync = @config.base(:rsync)
78
- local_folder = File.expand_path @config.local(:folder)
79
- local_temp = File.expand_path @config.local(:temp)
80
-
81
- build_command = build_command.gsub('%f', build[:folder]).
82
- gsub('%t', @config.build(:target)).
83
- gsub('%p', @package.to_s).
84
- gsub('%T', type.to_s)
85
-
86
- upload_sources("#{local_folder}/", "#{build[:user]}@#{build[:host]}:#{build[:folder]}")
87
- command("#{ssh} #{build[:user]}@#{build[:host]} \"#{build_command}\"")
88
- @package_files[type].each do |p, t|
89
- puts "Receiving target #{p.to_s} for #{t.to_s}"
90
- command("#{rsync} -az #{build[:user]}@#{build[:host]}:#{build[:target]}/#{t} #{local_temp}")
91
- end
92
- rescue CommandError => e
93
- puts e.verbose_message
94
- exit! ''
95
- rescue KeyNotdefinederror => e
96
- puts e.message
97
- exit! ''
98
- end
99
-
100
- def install
101
- env = @config.env
102
- puts "Installing #{@package} in #{env} environment."
103
- local_temp = @config.local(:temp)
104
- sudo = @config.base(:sudo)
105
- scp = @config.base(:scp)
106
- type = @config.type
107
- install = {}
108
- [:user, :host, :folder].each do |k|
109
- install[k] = @config.install(k)
110
- end
111
- install[:host] = [install[:host]] unless Array === install[:host]
112
- case type
113
- when :deb
114
- install[:host].each do |host|
115
- command("#{scp} #{local_temp}/#{@package_files[type][:deb]} #{install[:user]}@#{host}:#{install[:folder]}")
116
- command("#{sudo} #{install[:user]}@#{host} /usr/bin/dpkg -i #{install[:folder]}/#{@package_files[type][:deb]}")
117
- end
118
- else
119
- exit! "unknown package type '#{type.to_s}'"
120
- end
121
- rescue CommandError => e
122
- puts e.verbose_message
123
- exit! ''
124
- rescue KeyNotdefinederror => e
125
- puts e.message
126
- exit! ''
127
- end
128
-
129
- def run_tests
130
- # check if we have the test section in the configuration file
131
- unless @config.tests
132
- puts 'No test section in the config file.'
133
- puts 'Skipping tests'
134
- return
135
- end
136
- # for tests we will use almost the same setup as for build
137
- test = {}
138
- [:user, :machine, :command, :folder].each do |k|
139
- test[k] = @config.tests(k)
140
- end
141
- ssh = @config.base(:ssh)
142
- # replace "variables" in commands
143
- test[:command] = test[:command].
144
- gsub('%f', test[:folder]).# %f is the folder where the sources are rsync-ed
145
- gsub('%t', @config.build(:target)).# %t is the folder where the build places the result
146
- gsub('%p', @package.to_s) # %p is the package name
147
- # take the sources from the local folder
148
- local_folder = File.expand_path @config.local(:folder)
149
- # upload them to the test machine
150
- upload_sources("#{local_folder}/", "#{test[:user]}@#{test[:machine]}:#{test[:folder]}")
151
- puts 'Running all tests'
152
- puts 'This will take some time and you have no output'
153
- command("#{ssh} #{test[:user]}@#{test[:machine]} \"#{test[:command]}\"")
154
- end
155
-
156
- def upload_sources (source, dest)
157
- rsync = @config.base(:rsync)
158
- command("#{rsync} -az #{source} #{dest}")
159
- end
160
- rescue CommandError => e
161
- puts e.verbose_message
162
- exit! ''
163
- rescue KeyNotdefinederror => e
164
- puts e.message
165
- exit! ''
166
- end
167
-
168
- end
5
+ require 'devinstall/pkg'
6
+ require 'devinstall/cli'
169
7
 
@@ -4,12 +4,24 @@ require 'devinstall/settings'
4
4
 
5
5
  describe 'Settings' do
6
6
  config=Devinstall::Settings.instance
7
- config.load! "./doc/example.yml"
7
+ config.load! './doc/example.yml' ## use defaults for type and env
8
+
9
+ package=:devinstall
10
+ type=:deb
11
+ env=nil
12
+
13
+ it 'should load an existig file' do
14
+ expect(config.load!('doc/example.yml')).to be_true
15
+ end
16
+
17
+ it 'should not load an unexisting file' do
18
+ expect(config.load!('doc/unexisting.yml')).to be_false
19
+ end
8
20
 
9
21
  it 'should load at init' do
10
22
  expect(config).to be_an_instance_of(Devinstall::Settings)
11
- [:defaults, :base, :local, :build, :install, :tests, :packages, :repos].each do |p|
12
- expect(config.send(p)).to be_true
23
+ [:defaults, :base, :local, :build, :install, :tests, :repos].each do |p|
24
+ expect(config.respond_to? p).to be_true
13
25
  end # all sections loaded!
14
26
  end
15
27
 
@@ -17,8 +29,22 @@ describe 'Settings' do
17
29
  expect(config.defaults).to be_true
18
30
  end
19
31
 
20
- it 'should have a static default' do
21
- expect(Devinstall::Settings.defaults(:package)).to be(config.defaults(:package))
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)
34
+ end
35
+
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)
38
+ end
39
+
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
43
+ end
44
+
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
22
48
  end
23
49
 
24
50
  it 'should raise errors for unknown keys' do
@@ -26,6 +52,24 @@ describe 'Settings' do
26
52
  end
27
53
 
28
54
  it 'should raise errors for undefined keys' do
29
- expect{config.tests(:provider)}.to raise_error(Devinstall::KeyNotDefinedError)
55
+ expect{config.tests(:provider, pkg:package)}.to raise_error(Devinstall::KeyNotDefinedError)
30
56
  end
57
+
58
+ it 'should produce a value if aok' do
59
+ expect(config.build(:user, pkg:package)).to eq('dboca')
60
+ end
61
+
62
+ it 'validator should have a [] method' do
63
+ rr=config.build(pkg:package, type:type, env:env)
64
+ expect(rr[:user]).to eq('dboca')
65
+ end
66
+
67
+ it 'should enumerate all defined values' do
68
+ ar=[]
69
+ config.build(pkg:package, type:type, env:env).each do |k,v|
70
+ ar << k
71
+ end
72
+ expect(ar).to eql([:user, :host, :folder, :target, :arch, :command, :provider])
73
+ end
74
+
31
75
  end
metadata CHANGED
@@ -1,69 +1,94 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devinstall
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Dragos Boca
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-05-02 00:00:00.000000000 Z
12
+ date: 2013-05-03 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: '0'
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0'
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: rake
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - '>='
35
+ - - ! '>='
32
36
  - !ruby/object:Gem::Version
33
37
  version: '0'
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
- - - '>='
43
+ - - ! '>='
39
44
  - !ruby/object:Gem::Version
40
45
  version: '0'
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: getopt
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
- - - '>='
51
+ - - ! '>='
46
52
  - !ruby/object:Gem::Version
47
53
  version: '0'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
- - - '>='
59
+ - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: rspec
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
- - - '>='
67
+ - - ! '>='
60
68
  - !ruby/object:Gem::Version
61
69
  version: '0'
62
70
  type: :development
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
- - - '>='
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: commander
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
67
92
  - !ruby/object:Gem::Version
68
93
  version: '0'
69
94
  description: remote builder and installer
@@ -86,6 +111,7 @@ files:
86
111
  - lib/devinstall.rb
87
112
  - lib/devinstall/cli.rb
88
113
  - lib/devinstall/deep_symbolize.rb
114
+ - lib/devinstall/pkg.rb
89
115
  - lib/devinstall/settings.rb
90
116
  - lib/devinstall/utils.rb
91
117
  - lib/devinstall/version.rb
@@ -93,26 +119,27 @@ files:
93
119
  homepage: http://github.com/dboca/devinstall
94
120
  licenses:
95
121
  - MIT
96
- metadata: {}
97
122
  post_install_message:
98
123
  rdoc_options: []
99
124
  require_paths:
100
125
  - lib
101
126
  required_ruby_version: !ruby/object:Gem::Requirement
127
+ none: false
102
128
  requirements:
103
- - - '>='
129
+ - - ! '>='
104
130
  - !ruby/object:Gem::Version
105
131
  version: '0'
106
132
  required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
107
134
  requirements:
108
- - - '>='
135
+ - - ! '>='
109
136
  - !ruby/object:Gem::Version
110
137
  version: '0'
111
138
  requirements: []
112
139
  rubyforge_project:
113
- rubygems_version: 2.0.3
140
+ rubygems_version: 1.8.25
114
141
  signing_key:
115
- specification_version: 4
142
+ specification_version: 3
116
143
  summary: Copy the source files to a build host, build the packages and install builded
117
144
  packages
118
145
  test_files:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 020bb3bf72c1b2c8c0028b4f3325535ac560bd89
4
- data.tar.gz: dee754076fd8134e9f29ddd04e985495c62ed0ce
5
- SHA512:
6
- metadata.gz: bc609fc9b54577ebc424b47b106af5892c8af416d27634936302162546e084eb4ac273c3518a88a71f60c579d0c70f3763f158bafdf73aa31fc3442e0dd373ed
7
- data.tar.gz: df8ca74a6cddefa371bb4c7cc78a619ee0608396b022cd834a43fd55b78de47bc017688c1fb35dab9e269872de683642e9039363a4b1626439492caca7c1d789