Class: Vanagon::Platform::Windows
- Inherits:
-
Vanagon::Platform
- Object
- Vanagon::Platform
- Vanagon::Platform::Windows
- Defined in:
- lib/vanagon/platform/windows.rb
Constant Summary
Constants inherited from Vanagon::Platform
Instance Attribute Summary
Attributes inherited from Vanagon::Platform
#abs_resource_name, #architecture, #aws_ami, #aws_instance_type, #aws_key, #aws_key_name, #aws_region, #aws_shutdown_behavior, #aws_subnet_id, #aws_user_data, #aws_vpc_id, #brew, #build_dependencies, #build_hosts, #cflags, #codename, #copy, #cross_compiled, #defaultdir, #dist, #docker_image, #docker_run_args, #environment, #find, #install, #ldflags, #make, #mktemp, #name, #num_cores, #os_name, #os_version, #output_dir, #package_type, #patch, #platform_triple, #provisioning, #rpmbuild, #sed, #servicedir, #servicetype, #servicetypes, #settings, #shasum, #shell, #sort, #source_output_dir, #ssh_port, #tar, #target_user, #use_docker_exec, #valid_operators, #vmpooler_template
Instance Method Summary collapse
-
#add_repository(definition) ⇒ Array
Add a repository (or install Chocolatey).
-
#copy_from_project(proj_resources, destination, verbose: false) ⇒ Object
Method to recursively copy from a source project resource directory to a destination (wix) work directory.
-
#generate_msi_package(project) ⇒ Array
The specific bits used to generate a windows msi package for a given project.
-
#generate_msi_packaging_artifacts(workdir, name, binding) ⇒ Object
Method to generate the files required to build an MSI package for the project.
-
#generate_nuget_package(project) ⇒ Array
The specific bits used to generate a windows nuget package for a given project.
-
#generate_nuget_packaging_artifacts(workdir, name, binding) ⇒ Object
Method to generate the files required to build a nuget package for the project.
-
#generate_package(project) ⇒ Array
The specific bits used to generate a windows package for a given project.
-
#generate_packaging_artifacts(workdir, name, binding, project) ⇒ Object
Method to generate the files required to build a windows package for the project.
-
#generate_service_bin_dirs(services, project) ⇒ Object
Generate the underlying directory structure of any binary files referenced in services.
-
#generate_wix_dirs(items) ⇒ string
Generate correctly formatted wix elements that match the structure of the itemized input.
-
#generate_wix_from_graph(root) ⇒ Object
Recursively generate wix element structure.
-
#index_of_child(new_child, old_children) ⇒ Object
Find if child element is the same as one of the old_children elements, return that child.
-
#initialize(name) ⇒ Vanagon::Platform::Windows
constructor
Constructor.
-
#insert_child(curr, name, id) ⇒ Object
insert a new object with the name “name” if it doesn’t already exist.
-
#merge_defaults_from_vanagon(vanagon_root, destination, verbose: false) ⇒ Object
Method to merge in the files from the Vanagon (generic) directories.
-
#msi_package_name(project) ⇒ String
Method to derive the msi (Windows Installer) package name for the project.
-
#nuget_package_name(project) ⇒ String
Method to derive the package name for the project.
-
#nuget_package_version(version, release) ⇒ String
Nuget versioning is awesome!.
-
#package_name(project) ⇒ String
Method to derive the package name for the project.
-
#process_templates(wixworkdir, binding) ⇒ Object
Method to transform ERB templates in the work directory.
-
#strip_and_format_path(path, project) ⇒ Object
strip the leading install root and the filename from the service path and replace any \ with /.
-
#wix_product_version(version) ⇒ Object
Grab only the first three values from the version input and strip off any non-digit characters.
Methods inherited from Vanagon::Platform
#[], #add_build_repository, #add_group, #add_user, #generate_compiled_archive, #get_service_dir, #get_service_types, #is_aix?, #is_amazon?, #is_cisco_wrlinux?, #is_cross_compiled?, #is_cross_compiled_linux?, #is_deb?, #is_debian?, #is_el8?, #is_el?, #is_eos?, #is_fedora?, #is_fips?, #is_huaweios?, #is_linux?, #is_macos?, #is_osx?, #is_rpm?, #is_sles?, #is_solaris?, #is_ubuntu?, #is_unix?, #is_windows?, load_platform, #package_override, #provision_with, #validate_operator, #version_munger
Methods included from HashableAttributes
Methods included from Utilities
#erb_file, #erb_string, #ex, #find_program_on_path, #get_md5sum, #get_sum, #http_request, #http_request_code, #http_request_generic, #local_command, #remote_ssh_command, #retry_with_timeout, #rsync_from, #rsync_to, #ssh_command
Constructor Details
#initialize(name) ⇒ Vanagon::Platform::Windows
Constructor. Sets up some defaults for the windows platform and calls the parent constructor
Mingw varies on where it is installed based on architecture. We want to use which ever is on the system.
450 451 452 453 454 455 456 457 458 459 460 461 |
# File 'lib/vanagon/platform/windows.rb', line 450 def initialize(name) @target_user = "Administrator" @make = "/usr/bin/make" @tar = "/usr/bin/tar" @find = "/usr/bin/find" @sort = "/usr/bin/sort" @num_cores = "/usr/bin/nproc" @install = "/usr/bin/install" @copy = "/usr/bin/cp" @package_type = "msi" super(name) end |
Instance Method Details
#add_repository(definition) ⇒ Array
Add a repository (or install Chocolatey)
Note - this only prepares the list of commands to be executed once the Platform has been setup
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/vanagon/platform/windows.rb', line 314 def add_repository(definition) definition = URI.parse(definition) commands = [] if definition.scheme =~ /^(http|ftp|file)/ if File.extname(definition.path) == '.ps1' commands << %(powershell.exe -NoProfile -ExecutionPolicy Bypass -Command '[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;iex ((new-object net.webclient).DownloadString("#{definition}"))') else commands << %(C:/ProgramData/chocolatey/bin/choco.exe source add -n #{definition.host}-#{definition.path.tr('/', '-')} -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.") end else raise Vanagon::Error, "Invalid repo specification #{definition}" end commands end |
#copy_from_project(proj_resources, destination, verbose: false) ⇒ Object
Method to recursively copy from a source project resource directory to a destination (wix) work directory.
strongly suspect the original cp_r command would have done all of this.
82 83 84 |
# File 'lib/vanagon/platform/windows.rb', line 82 def copy_from_project(proj_resources, destination, verbose: false) FileUtils.cp_r(proj_resources, destination, :verbose => verbose) end |
#generate_msi_package(project) ⇒ Array
The specific bits used to generate a windows msi package for a given project
Have changed this to reflect the overall commands we need to generate the package. Question - should we break this down into some simpler Make tasks ?
1. Heat the directory tree to produce the file list
2. Compile (candle) all the wxs files into wixobj files
3. Run light to produce the final MSI
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/vanagon/platform/windows.rb', line 181 def generate_msi_package(project) # rubocop:disable Metrics/AbcSize target_dir = project.repo ? output_dir(project.repo) : output_dir wix_extensions = "-ext WiXUtilExtension -ext WixUIExtension" # Heat command documentation at: http://wixtoolset.org/documentation/manual/v3/overview/heat.html # dir <directory> - Traverse directory to find all sub-files and directories. # -ke - Keep Empty directories # -cg - Component Group Name # -gg - Generate GUIDS now # -srd - Suppress root element generation, we want to reference one of the default root elements # INSTALLDIR or APPDATADIR in the directorylist.wxs file, not a newly generated one. # -sreg - Suppress registry harvesting. # -dr - Root DirectoryRef to point all components to # -var - Replace "SourceDir" in the @source attributes of all components with a preprocessor variable app_heat_flags = " -dr INSTALLDIR -v -ke -indent 2 -cg AppComponentGroup -gg -srd -t wix/filter.xslt -sreg -var var.AppSourcePath " app_heat_flags += " -fips" if project.platform.name =~ /windowsfips-2012r2/ app_source_path = "SourceDir/#{project.settings[:base_dir]}/#{project.settings[:company_id]}/#{project.settings[:product_id]}" # Candle.exe preprocessor vars are required due to the above double run of heat.exe, both runs of heat use # preprocessor variables candle_preprocessor = "-dAppSourcePath=\"#{app_source_path}\" " candle_flags = "-arch #{@architecture} #{wix_extensions}" candle_flags += " -fips" if project.platform.name =~ /windowsfips-2012r2/ # Enable verbose mode for the moment (will be removed for production) # localisation flags to be added light_flags = "-v -cultures:en-us #{wix_extensions}" # "Misc Dir for versions.txt, License file and Icon file" misc_dir = "SourceDir/#{project.settings[:base_dir]}/#{project.settings[:company_id]}/#{project.settings[:product_id]}/misc" # Actual array of commands to be written to the Makefile make_commands = [ "mkdir -p output/#{target_dir}", "mkdir -p $(tempdir)/{SourceDir,wix/wixobj}", "#{@copy} -r wix/* $(tempdir)/wix/", "gunzip -c #{project.name}-#{project.version}.tar.gz | '#{@tar}' -C '$(tempdir)/SourceDir' --strip-components 1 -xf -" ] if project.extra_files_to_sign.any? make_commands << Vanagon::Utilities::ExtraFilesSigner.commands(project, @mktemp, 'SourceDir') end make_commands << [ "mkdir -p $(tempdir)/#{misc_dir}", # Need to use awk here to convert to DOS format so that notepad can display file correctly. "awk 'sub(\"$$\", \"\\r\")' $(tempdir)/SourceDir/bill-of-materials > $(tempdir)/#{misc_dir}/versions.txt", "cd $(tempdir); \"$$WIX/bin/heat.exe\" dir #{app_source_path} #{app_heat_flags} -out wix/#{project.name}-harvest-app.wxs", # Apply Candle command to all *.wxs files - generates .wixobj files in wix directory. # cygpath conversion is necessary as candle is unable to handle posix path specs # the preprocessor variables AppDataSourcePath and ApplicationSourcePath are required due to the -var input to the heat # runs listed above. "cd $(tempdir)/wix/wixobj; for wix_file in `find $(tempdir)/wix -name '*.wxs'`; do \"$$WIX/bin/candle.exe\" #{candle_flags} #{candle_preprocessor} $$(cygpath -aw $$wix_file) || exit 1; done", # run all wix objects through light to produce the msi # the -b flag simply points light to where the SourceDir location is # -loc is required for the UI localization it points to the actual localization .wxl "cd $(tempdir)/wix/wixobj; \"$$WIX/bin/light.exe\" #{light_flags} -b $$(cygpath -aw $(tempdir)) -loc $$(cygpath -aw $(tempdir)/wix/localization/puppet_en-us.wxl) -out $$(cygpath -aw $(workdir)/output/#{target_dir}/#{msi_package_name(project)}) *.wixobj", ] make_commands.flatten end |
#generate_msi_packaging_artifacts(workdir, name, binding) ⇒ Object
Method to generate the files required to build an MSI package for the project
67 68 69 70 71 72 |
# File 'lib/vanagon/platform/windows.rb', line 67 def generate_msi_packaging_artifacts(workdir, name, binding) # Copy the project specific files first copy_from_project("./resources/windows/wix", workdir) merge_defaults_from_vanagon(File.join(VANAGON_ROOT, "resources/windows/wix"), "#{workdir}/wix") process_templates("#{workdir}/wix", binding) end |
#generate_nuget_package(project) ⇒ Array
The specific bits used to generate a windows nuget package for a given project
Nexus expects packages to be named ‘#Vanagon::Platform#name-#version.nupkg`. However, chocolatey will generate them to be `#Vanagon::Platform#name.#version.nupkg`. So, we have to rename the package after we build it.
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/vanagon/platform/windows.rb', line 158 def generate_nuget_package(project) # rubocop:disable Metrics/AbcSize target_dir = project.repo ? output_dir(project.repo) : output_dir ["mkdir -p output/#{target_dir}", "mkdir -p $(tempdir)/#{project.name}/tools", "#{@copy} #{project.name}.nuspec $(tempdir)/#{project.name}/", "#{@copy} chocolateyInstall.ps1 chocolateyUninstall.ps1 $(tempdir)/#{project.name}/tools/", "#{@copy} file-list $(tempdir)/#{project.name}/tools/file-list.txt", "gunzip -c #{project.name}-#{project.version}.tar.gz | '#{@tar}' -C '$(tempdir)/#{project.name}/tools' --strip-components 1 -xf -", "(cd $(tempdir)/#{project.name} ; C:/ProgramData/chocolatey/bin/choco.exe pack #{project.name}.nuspec)", "#{@copy} $(tempdir)/#{project.name}/#{project.name}-#{@architecture}.#{nuget_package_version(project.version, project.release)}.nupkg ./output/#{target_dir}/#{nuget_package_name(project)}"] end |
#generate_nuget_packaging_artifacts(workdir, name, binding) ⇒ Object
Method to generate the files required to build a nuget package for the project
139 140 141 142 143 144 145 146 147 |
# File 'lib/vanagon/platform/windows.rb', line 139 def generate_nuget_packaging_artifacts(workdir, name, binding) # nuget templates that do require a name change erb_file(File.join(VANAGON_ROOT, "resources/windows/nuget/project.nuspec.erb"), File.join(workdir, "#{name}.nuspec"), false, { :binding => binding }) # nuget static resources to be copied into place ["chocolateyInstall.ps1", "chocolateyUninstall.ps1"].each do |win_file| FileUtils.copy(File.join(VANAGON_ROOT, "resources/windows/nuget/#{win_file}"), File.join(workdir, win_file)) end end |
#generate_package(project) ⇒ Array
The specific bits used to generate a windows package for a given project
10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/vanagon/platform/windows.rb', line 10 def generate_package(project) case project.platform.package_type when "msi" return generate_msi_package(project) when "nuget" return generate_nuget_package(project) when "archive" # We don't need to generate the package for archives, return an # empty array return [] else raise Vanagon::Error, "I don't know how to build package type '#{project.platform.package_type}' for Windows. Teach me?" end end |
#generate_packaging_artifacts(workdir, name, binding, project) ⇒ Object
Method to generate the files required to build a windows package for the project
48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/vanagon/platform/windows.rb', line 48 def generate_packaging_artifacts(workdir, name, binding, project) case @package_type when "msi" return generate_msi_packaging_artifacts(workdir, name, binding) when "nuget" return generate_nuget_packaging_artifacts(workdir, name, binding) when "archive" # We don't need to generate packaging artifacts if this is an archive return else raise Vanagon::Error, "I don't know how create packaging artifacts for package type '#{project.platform.package_type}' for Windows. Teach me?" end end |
#generate_service_bin_dirs(services, project) ⇒ Object
Generate the underlying directory structure of any binary files referenced in services.
Note that this function does not generate the structure of the installation directory, only the structure above it.
339 340 341 342 343 344 345 346 347 348 |
# File 'lib/vanagon/platform/windows.rb', line 339 def generate_service_bin_dirs(services, project) # All service files will need a directory reference items = services.map do |svc| { :path => strip_and_format_path(svc.service_file, project), :element_to_add => "<Directory Id=\"#{svc.bindir_id}\" />\n" } end generate_wix_dirs(items) end |
#generate_wix_dirs(items) ⇒ string
Generate correctly formatted wix elements that match the structure of the itemized input
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/vanagon/platform/windows.rb', line 355 def generate_wix_dirs(items) # root refers to the root of an n-ary tree (which we are about to make) root = { :children => [] } # iterate over all paths specified and break each one # in to its specific directories. This will generate_wix_dirs # an n-ary tree structure matching the specs from the input items.each_with_index do |item, item_idx| # Always start at the beginning curr = root names = item[:path].split(File::SEPARATOR) names.each_with_index do |name, names_idx| # We concat the indexes of each loop to name to ensure the ids of all # elements will be unique. curr = insert_child(curr, name, "#{name}_#{item_idx}_#{names_idx}") end # at this point, curr will be the top dir, override the id if # id exists curr[:elements_to_add].push(item[:element_to_add]) end generate_wix_from_graph(root) end |
#generate_wix_from_graph(root) ⇒ Object
Recursively generate wix element structure
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/vanagon/platform/windows.rb', line 418 def generate_wix_from_graph(root) string = '' unless root[:children].empty? root[:children].each do |child| string += "<Directory Name=\"#{child[:name]}\" Id=\"#{child[:id]}\">\n" unless child[:elements_to_add].empty? child[:elements_to_add].each do |element| string += element end end string += generate_wix_from_graph(child) string += "</Directory>\n" end return string end string end |
#index_of_child(new_child, old_children) ⇒ Object
Find if child element is the same as one of the old_children elements, return that child
409 410 411 412 |
# File 'lib/vanagon/platform/windows.rb', line 409 def index_of_child(new_child, old_children) return nil if old_children.empty? old_children.index { |child| child[:name] == new_child[:name] } end |
#insert_child(curr, name, id) ⇒ Object
insert a new object with the name “name” if it doesn’t already exist. Then assign curr to either the new child or the one that already exists here
384 385 386 387 388 389 390 391 392 393 394 |
# File 'lib/vanagon/platform/windows.rb', line 384 def insert_child(curr, name, id) #The Id field will default to name, but be overridden later new_obj = { :name => name, :id => id, :elements_to_add => [], :children => [] } if (child_index = index_of_child(new_obj, curr[:children])) curr = curr[:children][child_index] else curr[:children].push(new_obj) curr = new_obj end curr end |
#merge_defaults_from_vanagon(vanagon_root, destination, verbose: false) ⇒ Object
Method to merge in the files from the Vanagon (generic) directories.
Project specific files take precedence, so since these are copied prior to this function, then this merge operation will ignore existing files
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/vanagon/platform/windows.rb', line 94 def merge_defaults_from_vanagon(vanagon_root, destination, verbose: false) # rubocop:disable Metrics/AbcSize # Will use this Pathname object for relative path calculations in loop below. vanagon_path = Pathname.new(vanagon_root) files = Dir.glob(File.join(vanagon_root, "**/*.*")) files.each do |file| # Get Pathname for incoming file using Pathname library src_pathname = Pathname.new(file).dirname # This Pathname method allows us to effectively "subtract" the leading vanagon_path # from the source filename path. This gives us a pathname fragment that we can # then append to the target directory, preserving the files place in the directory # tree relative to the parent. # See following article for example: # http://stackoverflow.com/questions/12093770/ruby-removing-parts-a-file-path # and http://ruby-doc.org/stdlib-2.1.0/libdoc/pathname/rdoc/Pathname.html#method-i-relative_path_from dest_pathname_fragment = src_pathname.relative_path_from(vanagon_path) target_dir = File.join(destination, dest_pathname_fragment.to_s) # Create the target directory if necessary. FileUtils.mkdir_p(target_dir) # Skip the file copy if either target file or ERB equivalent exists. # This means that any files already in place in the work directory as a # result of being copied from the project specific area will not be # overritten. next if File.exist?(Pathname.new(target_dir) + File.basename(file)) next if File.exist?(Pathname.new(target_dir) + File.basename(file, ".erb")) FileUtils.cp(file, target_dir, :verbose => verbose) end end |
#msi_package_name(project) ⇒ String
Method to derive the msi (Windows Installer) package name for the project.
245 246 247 248 |
# File 'lib/vanagon/platform/windows.rb', line 245 def msi_package_name(project) # Decided to use native project version in hope msi versioning doesn't have same resrictions as nuget "#{project.name}-#{project.version}-#{@architecture}.msi" end |
#nuget_package_name(project) ⇒ String
Method to derive the package name for the project.
Neither chocolatey nor nexus know how to deal with architecture, so we are just pretending it’s part of the package name.
257 258 259 |
# File 'lib/vanagon/platform/windows.rb', line 257 def nuget_package_name(project) "#{project.name}-#{@architecture}-#{nuget_package_version(project.version, project.release)}.nupkg" end |
#nuget_package_version(version, release) ⇒ String
Nuget versioning is awesome!
Nuget and chocolatey have some expectations about version numbers.
First, if this is a final package (built from a tag), the version must only contain digits with each element of the version separated by periods.
If we are creating the version for a prerelease package (built from a commit that does not have a corresponding tag), we have the option to append a string to the version. The string must start with a letter, be separated from the rest of the version with a dash, and contain no punctuation.
We assume we are working from a semver tag as the base of our version. If this is a final release, we only have to worry about that tag. We can also include the release number in the package version. If this is a prerelease package, then we assume we have a semver compliant tag, followed by the number of commits beyond the tag and the short sha of the latest change. Because we are working with git, if the version contains a short sha, it will begin with ‘g’. We check for this to determine what version type to deliver.
Examples of final versions:
* 1.2.3
* 1.5.3.1
Examples of prerelease versions:
* 1.2.3.1234-g124dm9302
* 3.2.5.23-gd329nd
296 297 298 299 300 301 302 303 304 |
# File 'lib/vanagon/platform/windows.rb', line 296 def nuget_package_version(version, release) version_elements = version.split('.') if version_elements.last.start_with?('g') # Version for the prerelease package "#{version_elements[0..-2].join('.').gsub(/[a-zA-Z]/, '')}-#{version_elements[-1]}" else "#{version}.#{release}".gsub(/[a-zA-Z]/, '') end end |
#package_name(project) ⇒ String
Method to derive the package name for the project
29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/vanagon/platform/windows.rb', line 29 def package_name(project) case project.platform.package_type when "msi" return msi_package_name(project) when "nuget" return nuget_package_name(project) when "archive" return "#{project.name}-#{project.version}-archive" else raise Vanagon::Error, "I don't know how to name package type '#{project.platform.package_type}' for Windows. Teach me?" end end |
#process_templates(wixworkdir, binding) ⇒ Object
Method to transform ERB templates in the work directory.
126 127 128 129 130 131 132 |
# File 'lib/vanagon/platform/windows.rb', line 126 def process_templates(wixworkdir, binding) files = Dir.glob(File.join(wixworkdir, "**/*.erb")) files.each do |file| erb_file(file, File.join(File.dirname(file), File.basename(file, ".erb")), false, { :binding => binding }) FileUtils.rm(file) end end |
#strip_and_format_path(path, project) ⇒ Object
strip the leading install root and the filename from the service path and replace any \ with /
401 402 403 404 405 |
# File 'lib/vanagon/platform/windows.rb', line 401 def strip_and_format_path(path, project) formatted_path = path.tr('\\', '\/') path_regex = /\/?SourceDir\/#{project.settings[:base_dir]}\/#{project.settings[:company_id]}\/#{project.settings[:product_id]}\// File.dirname(formatted_path.sub(path_regex, '')) end |
#wix_product_version(version) ⇒ Object
Grab only the first three values from the version input and strip off any non-digit characters.
440 441 442 |
# File 'lib/vanagon/platform/windows.rb', line 440 def wix_product_version(version) version.split(".").first(3).collect { |value| value.gsub(/[^0-9]/, '') }.join(".") end |