vanagon 0.5.0 → 0.5.1

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/lib/vanagon/common/pathname.rb +3 -1
  4. data/lib/vanagon/component.rb +5 -2
  5. data/lib/vanagon/component/dsl.rb +10 -3
  6. data/lib/vanagon/component/source/git.rb +3 -3
  7. data/lib/vanagon/component/source/http.rb +11 -19
  8. data/lib/vanagon/driver.rb +1 -1
  9. data/lib/vanagon/engine/base.rb +4 -3
  10. data/lib/vanagon/engine/docker.rb +1 -0
  11. data/lib/vanagon/engine/hardware.rb +1 -0
  12. data/lib/vanagon/engine/local.rb +1 -0
  13. data/lib/vanagon/engine/pooler.rb +1 -0
  14. data/lib/vanagon/platform.rb +6 -2
  15. data/lib/vanagon/platform/deb.rb +1 -1
  16. data/lib/vanagon/platform/dsl.rb +24 -0
  17. data/lib/vanagon/platform/osx.rb +2 -2
  18. data/lib/vanagon/platform/rpm.rb +1 -1
  19. data/lib/vanagon/platform/solaris_10.rb +1 -1
  20. data/lib/vanagon/platform/solaris_11.rb +1 -1
  21. data/lib/vanagon/platform/windows.rb +241 -0
  22. data/lib/vanagon/project.rb +7 -2
  23. data/lib/vanagon/utilities.rb +14 -5
  24. data/{templates → resources}/Makefile.erb +3 -3
  25. data/{templates → resources}/deb/changelog.erb +0 -0
  26. data/{templates → resources}/deb/conffiles.erb +0 -0
  27. data/{templates → resources}/deb/control.erb +0 -0
  28. data/{templates → resources}/deb/dirs.erb +0 -0
  29. data/{templates → resources}/deb/docs.erb +0 -0
  30. data/{templates → resources}/deb/install.erb +0 -0
  31. data/{templates → resources}/deb/postinst.erb +0 -0
  32. data/{templates → resources}/deb/postrm.erb +0 -0
  33. data/{templates → resources}/deb/preinst.erb +0 -0
  34. data/{templates → resources}/deb/prerm.erb +0 -0
  35. data/{templates → resources}/deb/rules.erb +2 -0
  36. data/{templates → resources}/osx/postinstall.erb +0 -0
  37. data/{templates → resources}/osx/preinstall.erb +0 -0
  38. data/{templates → resources}/osx/project-installer.xml.erb +0 -0
  39. data/{templates → resources}/rpm/project.spec.erb +2 -1
  40. data/{templates → resources}/solaris/10/depend.erb +0 -0
  41. data/{templates → resources}/solaris/10/pkginfo.erb +0 -0
  42. data/{templates → resources}/solaris/10/postinstall.erb +0 -0
  43. data/{templates → resources}/solaris/10/preinstall.erb +0 -0
  44. data/{templates → resources}/solaris/10/preremove.erb +0 -0
  45. data/{templates → resources}/solaris/10/proto.erb +0 -0
  46. data/{templates → resources}/solaris/11/p5m.erb +0 -0
  47. data/resources/windows/nuget/chocolateyInstall.ps1 +28 -0
  48. data/resources/windows/nuget/chocolateyUninstall.ps1 +24 -0
  49. data/resources/windows/nuget/project.nuspec.erb +31 -0
  50. data/resources/windows/wix/service.component.wxs.erb +55 -0
  51. data/spec/lib/vanagon/component/dsl_spec.rb +31 -1
  52. data/spec/lib/vanagon/component/source/git_spec.rb +14 -0
  53. data/spec/lib/vanagon/component/source/http_spec.rb +10 -1
  54. data/spec/lib/vanagon/engine/pooler_spec.rb +1 -1
  55. data/spec/lib/vanagon/platform/windows_spec.rb +43 -0
  56. data/spec/lib/vanagon/utilities_spec.rb +7 -0
  57. metadata +32 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0277e8af73f3165f6e9b9dac09f09afbf92257d1
4
- data.tar.gz: b6d18711b68b4ca2ce845bb1cae01bbf019dfd90
3
+ metadata.gz: 6faf9b7a2f17d4a0d10d3e9a050d066ba92ab7a0
4
+ data.tar.gz: 112f2f90afafb386461ec92ed67238fefddcf76d
5
5
  SHA512:
6
- metadata.gz: b8cccacd01204b1474944ec8975597369883ea354e71e58724802ab27b2581af7a009dd3ab07fe770fadd121180fb35b53bed8ae6bd409b3e031839c5d353d4d
7
- data.tar.gz: a8b4f78d37aa2844f1615b7aded498e6c5ca47c5d34d5a18e229125062928d1c6033d9bb97beb3725a1bfeb2250bf714262af187d2509014b4573e18c95bf338
6
+ metadata.gz: 8edf1722f845927b0ba7858b077bd3c57908f7395beee779aa068552ea7c5e46d7a827416b201b275fb90c947438162f1a0f42004a85662b1716a8e9f264161f
7
+ data.tar.gz: 85a5e28c1954033d3120d3e4ee72582f413982762ba430d3e1ebaa0e38127cfb994d3edfdaf921392a76fd7737ff5a6176469b006ff0a63905da653ed37a2607
data/README.md CHANGED
@@ -106,6 +106,9 @@ A full path on disk for a private ssh key to be used in ssh and rsync
106
106
  communications. This will be used instead of whatever defaults are configured
107
107
  in .ssh/config.
108
108
 
109
+ ##### VANAGON\_SSH\_AGENT
110
+ When set, Vanagon will forward the ssh authentication agent connection.
111
+
109
112
  ##### VMPOOLER\_TOKEN
110
113
  Used in conjunction with the pooler engine, this is a token to pass to the
111
114
  vmpooler to access the API. Without this token, the default lifetime of vms
@@ -1,3 +1,5 @@
1
+ require 'pathname'
2
+
1
3
  class Vanagon
2
4
  class Common
3
5
  class Pathname
@@ -28,7 +30,7 @@ class Vanagon
28
30
  # and exposed through the {#configfile?} method.
29
31
  # @return [Vanagon::Common::Pathname] Returns a new Pathname instance.
30
32
  def initialize(path, mode: nil, owner: nil, group: nil, config: false)
31
- @path = File.expand_path(path)
33
+ @path = ::Pathname.new(path).cleanpath.to_s
32
34
  @mode ||= mode
33
35
  @owner ||= owner
34
36
  @group ||= group
@@ -10,7 +10,7 @@ class Vanagon
10
10
  attr_accessor :name, :version, :source, :url, :configure, :build, :check, :install
11
11
  attr_accessor :environment, :extract_with, :dirname, :build_requires, :build_dir
12
12
  attr_accessor :settings, :platform, :patches, :requires, :service, :options
13
- attr_accessor :directories, :replaces, :provides, :cleanup_source, :environment
13
+ attr_accessor :directories, :replaces, :provides, :cleanup_source
14
14
  attr_accessor :sources, :preinstall_actions, :postinstall_actions
15
15
  attr_accessor :preremove_actions, :postremove_actions
16
16
 
@@ -109,7 +109,7 @@ class Vanagon
109
109
  @source = Vanagon::Component::Source.source(@url, @options, workdir)
110
110
  @source.fetch
111
111
  @source.verify
112
- @extract_with = @source.extract(@platform.tar) if @source.respond_to?(:extract)
112
+ @extract_with = @source.respond_to?(:extract) ? @source.extract(@platform.tar) : ':'
113
113
  @cleanup_source = @source.cleanup if @source.respond_to?(:cleanup)
114
114
  @dirname = @source.dirname
115
115
 
@@ -120,6 +120,9 @@ class Vanagon
120
120
 
121
121
  # If there is no source, we don't want to try to change directories, so we just change to the current directory.
122
122
  @dirname = './'
123
+
124
+ # If there is no source, there is nothing to do to extract
125
+ @extract_with = ':'
123
126
  end
124
127
  end
125
128
 
@@ -158,7 +158,8 @@ class Vanagon
158
158
  # @param default_file [String] path to the default file relative to the source
159
159
  # @param service_name [String] name of the service
160
160
  # @param service_type [String] type of the service (network, application, system, etc)
161
- def install_service(service_file, default_file = nil, service_name = @component.name, service_type: nil)
161
+ # @param service_hash [Hash] hash of options to parse for the service
162
+ def install_service(service_file, default_file = nil, service_name = @component.name, service_type: nil, service_hash: nil)
162
163
  case @component.platform.servicetype
163
164
  when "sysv"
164
165
  target_service_file = File.join(@component.platform.servicedir, service_name)
@@ -183,6 +184,9 @@ class Vanagon
183
184
  @component.service = OpenStruct.new(:name => service_name, :service_command => File.read(service_file).chomp)
184
185
  # Return here because there is no file to install, just a string read in
185
186
  return
187
+ when "windows"
188
+ @component.service = OpenStruct.new(name: service_name, options: service_hash)
189
+ return
186
190
  else
187
191
  fail "Don't know how to install the #{@component.platform.servicetype}. Please teach #install_service how to do this."
188
192
  end
@@ -207,7 +211,7 @@ class Vanagon
207
211
  # @param group [String] group owner of the file
208
212
  def install_file(source, target, mode: '0644', owner: nil, group: nil)
209
213
  @component.install << "#{@component.platform.install} -d '#{File.dirname(target)}'"
210
- @component.install << "cp -p '#{source}' '#{target}'"
214
+ @component.install << "#{@component.platform.copy} -p '#{source}' '#{target}'"
211
215
  @component.add_file Vanagon::Common::Pathname.file(target, mode: mode, owner: owner, group: group)
212
216
  end
213
217
 
@@ -241,7 +245,10 @@ class Vanagon
241
245
  # @param target [String] path to the desired symlink
242
246
  def link(source, target)
243
247
  @component.install << "#{@component.platform.install} -d '#{File.dirname(target)}'"
244
- @component.install << "ln -s '#{source}' '#{target}'"
248
+ # Use a bash conditional to only create the link if it doesn't already point to the correct source.
249
+ # This allows rerunning the install step to be idempotent, rather than failing because the link
250
+ # already exists.
251
+ @component.install << "([[ '#{target}' -ef '#{source}' ]] || ln -s '#{source}' '#{target}')"
245
252
  @component.add_file Vanagon::Common::Pathname.file(target)
246
253
  end
247
254
 
@@ -27,10 +27,10 @@ class Vanagon
27
27
  def fetch
28
28
  puts "Cloning ref '#{@ref}' from url '#{@url}'"
29
29
  Dir.chdir(@workdir) do
30
- git('clone', @url)
30
+ git("clone #{@url}", true)
31
31
  Dir.chdir(dirname) do
32
- git('checkout', @ref)
33
- git('submodule', 'update', '--init', '--recursive')
32
+ git("checkout #{@ref}", true)
33
+ git("submodule update --init --recursive", true)
34
34
  @version = git_version
35
35
  end
36
36
  end
@@ -12,9 +12,6 @@ class Vanagon
12
12
  # Extensions for files we intend to unpack during the build
13
13
  ARCHIVE_EXTENSIONS = '.tar.gz', '.tgz', '.zip'
14
14
 
15
- # Extensions for files we aren't going to unpack during the build
16
- NON_ARCHIVE_EXTENSIONS = '.gem', '.ru', '.txt', '.conf', '.ini', '.gpg', '.rb', '.sh', '.csh', '.xml', '.vim', '.json', '.service'
17
-
18
15
  # Constructor for the Http source type
19
16
  #
20
17
  # @param url [String] url of the http source to fetch
@@ -106,11 +103,9 @@ class Vanagon
106
103
  when ".zip"
107
104
  return "unzip '#{@file}'"
108
105
  end
109
- elsif NON_ARCHIVE_EXTENSIONS.include?(@extension)
110
- # Don't need to unpack gems, ru, txt, conf, ini, gpg
111
- return nil
112
106
  else
113
- fail "Extraction unimplemented for '#{@extension}' in source '#{@file}'. Please teach me."
107
+ # Extension does not appear to be an archive
108
+ return ':'
114
109
  end
115
110
  end
116
111
 
@@ -121,24 +116,25 @@ class Vanagon
121
116
  def cleanup
122
117
  if ARCHIVE_EXTENSIONS.include?(@extension)
123
118
  return "rm #{@file}; rm -r #{dirname}"
124
- elsif NON_ARCHIVE_EXTENSIONS.include?(@extension)
119
+ else
125
120
  # Because dirname will be ./ here, we don't want to try to nuke it
126
121
  return "rm #{@file}"
127
- else
128
- fail "Don't know how to cleanup for '#{@file}' with extension: '#{@extension}'. Please teach me."
129
122
  end
130
123
  end
131
124
 
132
125
  # Returns the extension for @file
133
126
  #
134
127
  # @return [String] the extension of @file
135
- # @raise [RuntimeError] an exception is raised if the extension isn't in the current list
136
128
  def get_extension
137
- extension_match = @file.match(/.*(#{Regexp.union(ARCHIVE_EXTENSIONS + NON_ARCHIVE_EXTENSIONS)})/)
129
+ extension_match = @file.match(/.*(#{Regexp.union(ARCHIVE_EXTENSIONS)})/)
138
130
  unless extension_match
139
- fail "Unrecognized extension for '#{@file}'. Don't know how to extract this format. Please teach me."
131
+ if @file.split('.').last.include?('.')
132
+ return '.' + @file.split('.').last
133
+ else
134
+ # This is the case where the file has no extension
135
+ return @file
136
+ end
140
137
  end
141
-
142
138
  extension_match[1]
143
139
  end
144
140
 
@@ -149,12 +145,8 @@ class Vanagon
149
145
  def dirname
150
146
  if ARCHIVE_EXTENSIONS.include?(@extension)
151
147
  return @file.chomp(@extension)
152
- elsif NON_ARCHIVE_EXTENSIONS.include?(@extension)
153
- # Because we cd into the source dir, using ./ here avoids special casing single file
154
- # sources in the Makefile
155
- return './'
156
148
  else
157
- fail "Don't know how to guess dirname for '#{@file}' with extension: '#{@extension}'. Please teach me."
149
+ return './'
158
150
  end
159
151
  end
160
152
  end
@@ -92,7 +92,7 @@ class Vanagon
92
92
  puts e.backtrace.join("\n")
93
93
  raise e
94
94
  ensure
95
- if @engine == "hardware"
95
+ if @engine.name == "hardware"
96
96
  @engine.teardown
97
97
  end
98
98
  end
@@ -4,13 +4,14 @@ require 'vanagon/errors'
4
4
  class Vanagon
5
5
  class Engine
6
6
  class Base
7
- attr_accessor :target, :remote_workdir
7
+ attr_accessor :target, :remote_workdir, :name
8
8
 
9
9
  def initialize(platform, target = nil)
10
10
  @platform = platform
11
11
  @required_attributes = ["ssh_port"]
12
12
  @target = target if target
13
- @target_user = "root"
13
+ @target_user = @platform.target_user
14
+ @name = 'base'
14
15
  end
15
16
 
16
17
  # This method is used to obtain a vm to build upon
@@ -57,7 +58,7 @@ class Vanagon
57
58
 
58
59
  def retrieve_built_artifact
59
60
  FileUtils.mkdir_p("output")
60
- Vanagon::Utilities.rsync_from("#{@remote_workdir}/output/*", "#{@target_user}@#{@target}", "output", @platform.ssh_port)
61
+ Vanagon::Utilities.rsync_from("#{@remote_workdir}/output/*", "#{@target_user}@#{@target}", "output/", @platform.ssh_port)
61
62
  end
62
63
 
63
64
  # Ensures that the platform defines the attributes that the engine needs to function.
@@ -7,6 +7,7 @@ class Vanagon
7
7
  # the docker engine to work
8
8
  def initialize(platform, target = nil)
9
9
  @docker_cmd = Vanagon::Utilities.find_program_on_path('docker')
10
+ @name = 'docker'
10
11
  super
11
12
  @required_attributes << "docker_image"
12
13
  end
@@ -52,6 +52,7 @@ class Vanagon
52
52
 
53
53
  def initialize(platform, target)
54
54
  Vanagon::Driver.logger.debug "Hardware engine invoked."
55
+ @name = 'hardware'
55
56
  @platform = platform
56
57
  @build_hosts = platform.build_hosts
57
58
  # Redis is the only backend supported in lock_manager currently
@@ -10,6 +10,7 @@ class Vanagon
10
10
  def initialize(platform, target = nil)
11
11
  @platform = platform
12
12
  @target = "local machine"
13
+ @name = 'local'
13
14
  end
14
15
 
15
16
  # Dispatches the command for execution
@@ -11,6 +11,7 @@ class Vanagon
11
11
  @token = load_token
12
12
  super
13
13
  @required_attributes << "vmpooler_template"
14
+ @name = 'pooler'
14
15
  end
15
16
 
16
17
  # This method loads the pooler token from one of two locations
@@ -6,7 +6,7 @@ class Vanagon
6
6
  attr_accessor :build_dependencies, :name, :vmpooler_template, :cflags, :ldflags, :settings
7
7
  attr_accessor :servicetype, :patch, :architecture, :codename, :os_name, :os_version
8
8
  attr_accessor :docker_image, :ssh_port, :rpmbuild, :install, :platform_triple
9
- attr_accessor :package_type, :build_hosts
9
+ attr_accessor :target_user, :package_type, :find, :sort, :build_hosts, :copy
10
10
 
11
11
  # Platform names currently contain some information about the platform. Fields
12
12
  # within the name are delimited by the '-' character, and this regex can be used to
@@ -101,6 +101,10 @@ class Vanagon
101
101
  @ssh_port = 22
102
102
  @provisioning = []
103
103
  @install ||= "install"
104
+ @target_user ||= "root"
105
+ @find ||= "find"
106
+ @sort ||= "sort"
107
+ @copy ||= "cp"
104
108
  end
105
109
 
106
110
  # This allows instance variables to be accessed using the hash lookup syntax
@@ -226,7 +230,7 @@ class Vanagon
226
230
  #
227
231
  # @return [true, false] true if it is a windows variety, false otherwise
228
232
  def is_windows?
229
- return !!@name.match(/^win-.*$/)
233
+ return !!@name.match(/^windows-.*$/)
230
234
  end
231
235
 
232
236
  # Utility matcher to determine is the platform is a linux variety
@@ -29,7 +29,7 @@ class Vanagon
29
29
 
30
30
  # Lots of templates here
31
31
  ["changelog", "conffiles", "control", "docs", "dirs", "install", "preinst", "postinst", "postrm", "prerm", "rules"].each do |deb_file|
32
- erb_file(File.join(VANAGON_ROOT, "templates/deb/#{deb_file}.erb"), File.join(deb_dir, deb_file), false, { :binding => binding })
32
+ erb_file(File.join(VANAGON_ROOT, "resources/deb/#{deb_file}.erb"), File.join(deb_dir, deb_file), false, { :binding => binding })
33
33
  end
34
34
 
35
35
  # These could be templates, but their content is static, so that seems weird.
@@ -7,6 +7,7 @@ require 'vanagon/platform/rpm/eos'
7
7
  require 'vanagon/platform/osx'
8
8
  require 'vanagon/platform/solaris_10'
9
9
  require 'vanagon/platform/solaris_11'
10
+ require 'vanagon/platform/windows'
10
11
  require 'securerandom'
11
12
  require 'uri'
12
13
 
@@ -43,6 +44,8 @@ class Vanagon
43
44
  Vanagon::Platform::Solaris10.new(@name)
44
45
  when /^solaris-11/
45
46
  Vanagon::Platform::Solaris11.new(@name)
47
+ when /^windows-/
48
+ Vanagon::Platform::Windows.new(@name)
46
49
  else
47
50
  fail "Platform not implemented for '#{@name}' yet. Please go do so..."
48
51
  end
@@ -97,6 +100,27 @@ class Vanagon
97
100
  @platform.package_type = pkg_type
98
101
  end
99
102
 
103
+ # Set the path to find for the platform
104
+ #
105
+ # @param find_cmd [String] Full path to the find command for the platform
106
+ def find(find_cmd)
107
+ @platform.find = find_cmd
108
+ end
109
+
110
+ # Set the path to sort for the platform
111
+ #
112
+ # @param sort_cmd [String] Full path to the sort command for the platform
113
+ def sort(sort_cmd)
114
+ @platform.sort = sort_cmd
115
+ end
116
+
117
+ # Set the path to copy for the platform
118
+ #
119
+ # @param copy_cmd [String] Full path to the copy command for the platform
120
+ def copy(copy_cmd)
121
+ @platform.copy = copy_cmd
122
+ end
123
+
100
124
  # Set the path to rpmbuild for the platform
101
125
  #
102
126
  # @param rpmbuild_cmd [String] Full path to rpmbuild with arguments to be used by default
@@ -66,10 +66,10 @@ class Vanagon
66
66
  script_dir = File.join(workdir, "scripts")
67
67
  FileUtils.mkdir_p(script_dir)
68
68
 
69
- erb_file(File.join(VANAGON_ROOT, "templates/osx/project-installer.xml.erb"), File.join(workdir, "#{name}-installer.xml"), false, { :binding => binding })
69
+ erb_file(File.join(VANAGON_ROOT, "resources/osx/project-installer.xml.erb"), File.join(workdir, "#{name}-installer.xml"), false, { :binding => binding })
70
70
 
71
71
  ["postinstall", "preinstall"].each do |script_file|
72
- erb_file(File.join(VANAGON_ROOT, "templates/osx/#{script_file}.erb"), File.join(script_dir, script_file), false, { :binding => binding })
72
+ erb_file(File.join(VANAGON_ROOT, "resources/osx/#{script_file}.erb"), File.join(script_dir, script_file), false, { :binding => binding })
73
73
  FileUtils.chmod 0755, File.join(script_dir, script_file)
74
74
  end
75
75
 
@@ -25,7 +25,7 @@ class Vanagon
25
25
  # @param name [String] name of the project
26
26
  # @param binding [Binding] binding to use in evaluating the packaging templates
27
27
  def generate_packaging_artifacts(workdir, name, binding)
28
- erb_file(File.join(VANAGON_ROOT, "templates/rpm/project.spec.erb"), File.join(workdir, "#{name}.spec"), false, { :binding => binding })
28
+ erb_file(File.join(VANAGON_ROOT, "resources/rpm/project.spec.erb"), File.join(workdir, "#{name}.spec"), false, { :binding => binding })
29
29
  end
30
30
 
31
31
  # Method to derive the package name for the project
@@ -83,7 +83,7 @@ class Vanagon
83
83
  ["pkginfo", "depend", "preinstall", "preremove", "postinstall", "proto"].each do |template|
84
84
  target_dir = File.join(workdir, 'packaging')
85
85
  FileUtils.mkdir_p(target_dir)
86
- erb_file(File.join(VANAGON_ROOT, "templates/solaris/10/#{template}.erb"), File.join(target_dir, template), false, { :binding => binding })
86
+ erb_file(File.join(VANAGON_ROOT, "resources/solaris/10/#{template}.erb"), File.join(target_dir, template), false, { :binding => binding })
87
87
  end
88
88
  end
89
89
 
@@ -43,7 +43,7 @@ class Vanagon
43
43
  def generate_packaging_artifacts(workdir, name, binding)
44
44
  target_dir = File.join(workdir, 'packaging')
45
45
  FileUtils.mkdir_p(target_dir)
46
- erb_file(File.join(VANAGON_ROOT, "templates/solaris/11/p5m.erb"), File.join(target_dir, "#{name}.p5m"), false, { :binding => binding })
46
+ erb_file(File.join(VANAGON_ROOT, "resources/solaris/11/p5m.erb"), File.join(target_dir, "#{name}.p5m"), false, { :binding => binding })
47
47
  end
48
48
 
49
49
  # Generate the scripts required to add a group to the package generated.
@@ -0,0 +1,241 @@
1
+ class Vanagon
2
+ class Platform
3
+ class Windows < Vanagon::Platform
4
+ # The specific bits used to generate a windows package for a given project
5
+ #
6
+ # @param project [Vanagon::Project] project to build a windows package of
7
+ # @return [Array] list of commands required to build a windows package for the given project from a tarball
8
+ def generate_package(project)
9
+ case project.platform.package_type
10
+ when "msi"
11
+ return generate_msi_package(project)
12
+ when "nuget"
13
+ return generate_nuget_package(project)
14
+ else
15
+ raise Vanagon::Error, "I don't know how to build package type '#{project.platform.package_type}' for Windows. Teach me?"
16
+ end
17
+ end
18
+
19
+ # Method to derive the package name for the project
20
+ #
21
+ # @param project [Vanagon::Project] project to name
22
+ # @return [String] name of the windows package for this project
23
+ def package_name(project)
24
+ case project.platform.package_type
25
+ when "msi"
26
+ return msi_package_name(project)
27
+ when "nuget"
28
+ return nuget_package_name(project)
29
+ else
30
+ raise Vanagon::Error, "I don't know how to name package type '#{project.platform.package_type}' for Windows. Teach me?"
31
+ end
32
+ end
33
+
34
+ # Method to generate the files required to build a windows package for the project
35
+ #
36
+ # @param workdir [String] working directory to stage the evaluated templates in
37
+ # @param name [String] name of the project
38
+ # @param binding [Binding] binding to use in evaluating the packaging templates
39
+ def generate_packaging_artifacts(workdir, name, binding)
40
+ case @package_type
41
+ when "msi"
42
+ return generate_msi_packaging_artifacts(workdir, name, binding)
43
+ when "nuget"
44
+ return generate_nuget_packaging_artifacts(workdir, name, binding)
45
+ else
46
+ raise Vanagon::Error, "I don't know how create packaging artifacts for package type '#{project.platform.package_type}' for Windows. Teach me?"
47
+ end
48
+ end
49
+
50
+ # Method to generate the files required to build an MSI package for the project
51
+ #
52
+ # @param workdir [String] working directory to stage the evaluated templates in
53
+ # @param name [String] name of the project
54
+ # @param binding [Binding] binding to use in evaluating the packaging templates
55
+ def generate_msi_packaging_artifacts(workdir, name, binding)
56
+ FileUtils.mkdir_p(File.join(workdir, "wix"))
57
+ erb_file(File.join(VANAGON_ROOT, "resources/windows/wix/service.component.wxs.erb"), File.join(workdir, "wix", "service.#{name}.wxs"), false, { :binding => binding })
58
+ end
59
+
60
+ # Method to generate the files required to build a nuget package for the project
61
+ #
62
+ # @param workdir [String] working directory to stage the evaluated templates in
63
+ # @param name [String] name of the project
64
+ # @param binding [Binding] binding to use in evaluating the packaging templates
65
+ def generate_nuget_packaging_artifacts(workdir, name, binding)
66
+ # nuget templates that do require a name change
67
+ erb_file(File.join(VANAGON_ROOT, "resources/windows/nuget/project.nuspec.erb"), File.join(workdir, "#{name}.nuspec"), false, { :binding => binding })
68
+
69
+ # nuget static resources to be copied into place
70
+ ["chocolateyInstall.ps1", "chocolateyUninstall.ps1"].each do |win_file|
71
+ FileUtils.copy(File.join(VANAGON_ROOT, "resources/windows/nuget/#{win_file}"), File.join(workdir, win_file))
72
+ end
73
+ end
74
+
75
+ # The specific bits used to generate a windows nuget package for a given project
76
+ # Nexus expects packages to be named #{name}-#{version}.nupkg. However, chocolatey
77
+ # will generate them to be #{name}.#{version}.nupkg. So, we have to rename the
78
+ # package after we build it.
79
+ #
80
+ # @param project [Vanagon::Project] project to build a nuget package of
81
+ # @return [Array] list of commands required to build a nuget package for
82
+ # the given project from a tarball
83
+ def generate_nuget_package(project)
84
+ target_dir = project.repo ? output_dir(project.repo) : output_dir
85
+ ["mkdir -p output/#{target_dir}",
86
+ "mkdir -p $(tempdir)/#{project.name}/tools",
87
+ "#{@copy} #{project.name}.nuspec $(tempdir)/#{project.name}/",
88
+ "#{@copy} chocolateyInstall.ps1 chocolateyUninstall.ps1 $(tempdir)/#{project.name}/tools/",
89
+ "#{@copy} file-list $(tempdir)/#{project.name}/tools/file-list.txt",
90
+ "gunzip -c #{project.name}-#{project.version}.tar.gz | '#{@tar}' -C '$(tempdir)/#{project.name}/tools' --strip-components 1 -xf -",
91
+ "(cd $(tempdir)/#{project.name} ; C:/ProgramData/chocolatey/bin/choco.exe pack #{project.name}.nuspec)",
92
+ "#{@copy} $(tempdir)/#{project.name}/#{project.name}-#{@architecture}.#{nuget_package_version(project.version, project.release)}.nupkg ./output/#{target_dir}/#{nuget_package_name(project)}"]
93
+ end
94
+
95
+ # The specific bits used to generate a windows msi package for a given project
96
+ # Have changed this to reflect the overall commands we need to generate the package.
97
+ # Question - should we break this down into some simpler Make tasks ?
98
+ # 1. Heat the directory tree to produce the file list
99
+ # 2. Compile (candle) all the wxs files into wixobj files
100
+ # 3. Run light to produce the final MSI
101
+ #
102
+ # @param project [Vanagon::Project] project to build a msi package of
103
+ # @return [Array] list of commands required to build an msi package for the given project from a tarball
104
+ def generate_msi_package(project)
105
+ target_dir = project.repo ? output_dir(project.repo) : output_dir
106
+ cg_name = "compfiles"
107
+ dir_ref = "INSTALLDIR"
108
+ # Actual array of commands to be written to the Makefile
109
+ ["mkdir -p output/#{target_dir}",
110
+ "mkdir -p $(tempdir)/staging",
111
+ "gunzip -c #{project.name}-#{project.version}.tar.gz | '#{@tar}' -C '$(tempdir)/staging' --strip-components 1 -xf -",
112
+ # Run the Heat command in a single pass
113
+ # Heat command documentation at: http://wixtoolset.org/documentation/manual/v3/overview/heat.html
114
+ # dir <directory> - Traverse directory to find all sub-files and directories.
115
+ # -ke - Keep Empty directories
116
+ # -cg - Component Group Name
117
+ # -gg - Generate GUIDS now
118
+ # -dr - Directory reference to root directories (cannot contains spaces e.g. -dr MyAppDirRef)
119
+ # -sreg - Suppress registry harvesting.
120
+ # -var <variable> - Substitute File/@Source="SourceDir" with a preprocessor or a wix variable
121
+ "cd $(tempdir); \"$$WIX/bin/heat.exe\" dir staging -v -ke -indent 2 -cg #{cg_name} -gg -dr #{dir_ref} -sreg -var var.StageDir -out wix/#{project.name}-harvest.wxs",
122
+ ]
123
+ end
124
+
125
+ # Method to derive the msi (Windows Installer) package name for the project.
126
+ #
127
+ # @param project [Vanagon::Project] project to name
128
+ # @return [String] name of the windows package for this project
129
+ def msi_package_name(project)
130
+ # Decided to use native project version in hope msi versioning doesn't have same resrictions as nuget
131
+ "#{project.name}-#{project.version}.#{project.release}-#{@architecture}.msi"
132
+ end
133
+
134
+ # Method to derive the package name for the project.
135
+ # Neither chocolatey nor nexus know how to deal with architecture, so
136
+ # we are just pretending it's part of the package name.
137
+ #
138
+ # @param project [Vanagon::Project] project to name
139
+ # @return [String] name of the windows package for this project
140
+ def nuget_package_name(project)
141
+ "#{project.name}-#{@architecture}-#{nuget_package_version(project.version, project.release)}.nupkg"
142
+ end
143
+
144
+ # Nuget versioning is awesome!
145
+ #
146
+ # Nuget and chocolatey have some expectations about version numbers.
147
+ #
148
+ # First, if this is a final package (built from a tag), the version must
149
+ # only contain digits with each element of the version separated by
150
+ # periods.
151
+ #
152
+ # If we are creating the version for a prerelease package (built from a
153
+ # commit that does not have a corresponding tag), we have the
154
+ # option to append a string to the version. The string must start with a
155
+ # letter, be separated from the rest of the version with a dash, and
156
+ # contain no punctuation.
157
+ #
158
+ # We assume we are working from a semver tag as the base of our version.
159
+ # If this is a final release, we only have to worry about that tag. We
160
+ # can also include the release number in the package version. If this is
161
+ # a prerelease package, then we assume we have a semver compliant tag,
162
+ # followed by the number of commits beyond the tag and the short sha of
163
+ # the latest change. Because we are working with git, if the version
164
+ # contains a short sha, it will begin with 'g'. We check for this to
165
+ # determine what version type to deliver.
166
+ #
167
+ # Examples of final versions:
168
+ # 1.2.3
169
+ # 1.5.3.1
170
+ #
171
+ # Examples of prerelease versions:
172
+ # 1.2.3.1234-g124dm9302
173
+ # 3.2.5.23-gd329nd
174
+ #
175
+ # @param project [Vanagon::Project] project to version
176
+ # @return [String] the version of the nuget package for this project
177
+ def nuget_package_version(version, release)
178
+ version_elements = version.split('.')
179
+ if version_elements.last.start_with?('g')
180
+ # Version for the prerelease package
181
+ "#{version_elements[0..-2].join('.').gsub(/[a-zA-Z]/, '')}-#{version_elements[-1]}"
182
+ else
183
+ "#{version}.#{release}".gsub(/[a-zA-Z]/, '')
184
+ end
185
+ end
186
+
187
+ # Add a repository (or install Chocolatey)
188
+ # Note - this only prepares the list of commands to be executed once the Platform
189
+ # has been setup
190
+ #
191
+ # @param definition [String] Definition for adding repo, can be a Repo URL (including file:)
192
+ # If file suffix is 'ps1' it is downloaded and executed to install chocolatey
193
+ # @return [Array] Commands to executed after platform startup
194
+ def add_repository(definition)
195
+ definition = URI.parse(definition)
196
+ commands = []
197
+
198
+ if definition.scheme =~ /^(http|ftp|file)/
199
+ if File.extname(definition.path) == '.ps1'
200
+ commands << %(powershell.exe -NoProfile -ExecutionPolicy Bypass -Command 'iex ((new-object net.webclient).DownloadString(\"#{definition}\"))')
201
+ else
202
+ commands << %(C:/ProgramData/chocolatey/bin/choco.exe source add -n #{definition.host}-#{definition.path.gsub('/', '-')} -s "#{definition}" --debug || echo "Oops, it seems that you don't have chocolatey installed on this system. Please ensure it's there by adding something like 'plat.add_repository 'https://chocolatey.org/install.ps1'' to your platform definition.")
203
+ end
204
+ else
205
+ raise Vanagon::Error, "Invalid repo specification #{definition}"
206
+ end
207
+
208
+ commands
209
+ end
210
+
211
+ # Get the expected output dir for the windows packages. This allows us to
212
+ # use some standard tools to ship internally.
213
+ #
214
+ # @param target_repo [String] optional repo target for built packages defined
215
+ # at the project level
216
+ # @return [String] relative path to where windows packages should be staged
217
+ def output_dir(target_repo = "")
218
+ File.join("windows", target_repo, @architecture)
219
+ end
220
+
221
+ # Constructor. Sets up some defaults for the windows platform and calls the parent constructor
222
+ #
223
+ # Mingw varies on where it is installed based on architecture. We want to use which ever is on the system.
224
+ #
225
+ # @param name [String] name of the platform
226
+ # @return [Vanagon::Platform::Windows] the win derived platform with the given name
227
+ def initialize(name)
228
+ @target_user = "Administrator"
229
+ @make = "/usr/bin/make"
230
+ @tar = "/usr/bin/tar"
231
+ @find = "/usr/bin/find"
232
+ @sort = "/usr/bin/sort"
233
+ @num_cores = "/usr/bin/nproc"
234
+ @install = "/usr/bin/install"
235
+ @copy = "/usr/bin/cp"
236
+ @package_type = "msi"
237
+ super(name)
238
+ end
239
+ end
240
+ end
241
+ end