Skip to content

Instantly share code, notes, and snippets.

@glennsarti
Last active May 28, 2019 05:34
Show Gist options
  • Save glennsarti/cd1b1a6bfc8cc9ba8b94ac9071a20c49 to your computer and use it in GitHub Desktop.
Save glennsarti/cd1b1a6bfc8cc9ba8b94ac9071a20c49 to your computer and use it in GitHub Desktop.
PDK MSI POC
# Copy the source files
$WIX = 'C:\Program Files (x86)\WiX Toolset v3.11'
& robocopy "C:\Source\VanagonSource\SourceDir" "C:\Source\Vanagon\SourceDir" /s /e /copy:dat /r:1 /w:1 /MIR
& robocopy "C:\Source\VanagonSource\wix" "C:\Source\Vanagon\wix" /s /e /copy:dat /r:1 /w:1 /MIR
& cmd /c del C:\Source\Vanagon\*.* /q
Push-Location C:\Source\Vanagon
& $WIX\bin\heat.exe dir SourceDir/ProgramFiles64Folder/PuppetLabs/DevelopmentKit -dr INSTALLDIR -v -ke -indent 2 -cg AppComponentGroup -gg -srd -t wix/filter.xslt -sreg -var var.AppSourcePath -out wix/pdk-harvest-app.wxs
New-Item -Path 'wix\wixobj' -ItemType Directory | Out-Null
Push-Location 'wix\wixobj'
Get-ChildItem -Path '..' -Filter *.wxs -Recurse | % {
& $WIX\bin\candle.exe -arch x64 -ext WiXUtilExtension -ext WixUIExtension -dAppSourcePath="SourceDir/ProgramFiles64Folder/PuppetLabs/DevelopmentKit" $_.FullName
}
& $WIX\bin\light.exe -v -cultures:en-us -ext WiXUtilExtension -ext WixUIExtension -b C:\Source\Vanagon -loc C:\Source\Vanagon\wix\localization\puppet_en-us.wxl -out C:\Source\pdk-1.11.0.0-AllFiles-x64.msi *.wixobj
Pop-Location
Pop-Location
# cd /var/tmp/tmp.7YUWHKmsBm; "$WIX/bin/heat.exe" dir SourceDir/ProgramFiles64Folder/PuppetLabs/DevelopmentKit -dr INSTALLDIR -v -ke -indent 2 -cg AppComponentGroup -gg -srd -t wix/filter.xslt -sreg -var var.AppSourcePath -out wix/pdk-harvest-app.wxs
# Windows Installer XML Toolset Toolset Harvester version 3.10.2.2516
# Copyright (c) Outercurve Foundation. All rights reserved.
# cd /var/tmp/tmp.7YUWHKmsBm/wix/wixobj; for wix_file in `find /var/tmp/tmp.7YUWHKmsBm/wix -name '*.wxs'`; do "$WIX/bin/candle.exe" -arch x64 -ext WiXUtilExtension -ext WixUIExtension -dAppSourcePath="SourceDir/ProgramFiles64Folder/PuppetLabs/DevelopmentKit" $(cygpath -aw $wix_file) || exit 1; done
# Windows Installer XML Toolset Compiler version 3.10.2.2516
# Copyright (c) Outercurve Foundation. All rights reserved.
# cd /var/tmp/tmp.7YUWHKmsBm/wix/wixobj; "$WIX/bin/light.exe" -v -cultures:en-us -ext WiXUtilExtension -ext WixUIExtension -b $(cygpath -aw /var/tmp/tmp.7YUWHKmsBm) -loc $(cygpath -aw /var/tmp/tmp.7YUWHKmsBm/wix/localization/puppet_en-us.wxl) -out $(cygpath -aw /var/tmp/tmp.txkfJvIYoX/output/windows/pdk-1.11.0.0.pre.32.g25d6e34-x64.msi) *.wixobj
# Copy the source files
$ThisDir = $PSScriptRoot
$WIX = 'C:\Program Files (x86)\WiX Toolset v3.11'
# Create directory
$SourceDir = 'C:\Source\Vanagon\SourceDir'
$TarballDir = "$SourceDir\ProgramFiles64Folder\PuppetLabs\DevelopmentKit\share\install-tarballs"
$rootDir = "$SourceDir\ProgramFiles64Folder\PuppetLabs\DevelopmentKit"
& robocopy "C:\Source\VanagonSource\SourceDir" "C:\Source\Vanagon\SourceDir" /s /e /copy:dat /r:1 /w:1 /MIR /NFL /NDL /NJH /NJS /nc /ns /np
& robocopy "C:\Source\VanagonSource\wix" "C:\Source\Vanagon\wix" /s /e /copy:dat /r:1 /w:1 /MIR /NFL /NDL /NJH /NJS /nc /ns /np
& cmd /c del C:\Source\Vanagon\*.* /q
if (!(Test-Path -Path $TarballDir)) { New-Item -Path $TarballDir -ItemType Directory | Out-Null }
Copy-Item -Path "$ThisDir\extract_all.rb" -Destination $TarballDir -Force -Confirm:$false | Out-Null
Copy-Item -Path "$ThisDir\customactions.wxs" -Destination "$SourceDir\..\wix" -Force -Confirm:$false | Out-Null
Copy-Item -Path "$ThisDir\sequences.wxs" -Destination "$SourceDir\..\wix" -Force -Confirm:$false | Out-Null
Get-ChildItem -Path "$SourceDir\ProgramFiles64Folder\PuppetLabs\DevelopmentKit\private\ruby" | % {
$rubyDir = $_.FullName
$rubyVersion = $_.Name
$rubyMinorVer = $_.Name.split('.',3)[0] + '.' + $_.Name.split('.',3)[1] + '.0'
$PuppetRubyDir = "$SourceDir\ProgramFiles64Folder\PuppetLabs\DevelopmentKit\private\puppet\ruby\$rubyMinorVer"
$CacheRubyDir = "$SourceDir\ProgramFiles64Folder\PuppetLabs\DevelopmentKit\share\cache\ruby\$rubyMinorVer"
$relativeRubyDir = $rubyDir.Replace($RootDir + '\','')
$relativePuppetRubyDir = $PuppetRubyDir.Replace($RootDir + '\','')
$relativeCacheRubyDir = $CacheRubyDir.Replace($RootDir + '\','')
Push-Location $rootDir
$tarball = "$TarballDir\ruby-$rubyVersion.tar"
$targz = "$TarballDir\ruby-$rubyVersion.tgz"
if (Test-Path -Path $tarball) { Remove-Item -Path $tarball -Force -Confirm:$false | Out-Null }
if (Test-Path -Path $targz) { Remove-Item -Path $targz -Force -Confirm:$false | Out-Null }
if ((Get-ChildItem -Path "$rubyDir\lib\ruby\gems\$rubyMinorVer\gems" -Filter pdk-* | Measure-Object).Count -gt 0) {
Write-Host "$RubyVersion has the PDK gem in it. Ignoring the RubyDir"
# Create the tarfile
& 7za a -ttar -sdel -spf2 $tarball $relativePuppetRubyDir $relativeCacheRubyDir
} else {
# Create the tarfile
& 7za a -ttar -sdel -spf2 $tarball $relativeRubyDir $relativePuppetRubyDir $relativeCacheRubyDir
}
# Create the tar.gz
& 7za a -tgzip $targz $tarball
# Cleanup
if (Test-Path -Path $tarball) { Remove-Item -Path $tarball -Force -Confirm:$false | Out-Null }
Pop-Location
}
Push-Location C:\Source\Vanagon
& $WIX\bin\heat.exe dir SourceDir/ProgramFiles64Folder/PuppetLabs/DevelopmentKit -dr INSTALLDIR -v -ke -indent 2 -cg AppComponentGroup -gg -srd -t wix/filter.xslt -sreg -var var.AppSourcePath -out wix/pdk-harvest-app.wxs
New-Item -Path 'wix\wixobj' -ItemType Directory | Out-Null
Push-Location 'wix\wixobj'
Get-ChildItem -Path '..' -Filter *.wxs -Recurse | % {
& $WIX\bin\candle.exe -arch x64 -ext WiXUtilExtension -ext WixUIExtension -dAppSourcePath="SourceDir/ProgramFiles64Folder/PuppetLabs/DevelopmentKit" $_.FullName
}
& $WIX\bin\light.exe -v -cultures:en-us -ext WiXUtilExtension -ext WixUIExtension -b C:\Source\Vanagon -loc C:\Source\Vanagon\wix\localization\puppet_en-us.wxl -out C:\Source\pdk-1.11.0.0-Tarballs-x64.msi *.wixobj
Pop-Location
Pop-Location
# $TarballDir = "C:\Program Files\Puppet Labs\DevelopmentKit\share\install-tarballs"
# $rootDir = "C:\Program Files\Puppet Labs\DevelopmentKit"
# Get-ChildItem $TarballDir -Filter *.tgz | % {
# $tgz = $_.Fullname
# $tarball = "$TarballDir\$($_.Basename).tar"
# # Extract the tarball file
# & 7za e $tgz -y "-o$TarballDir"
# # Extract the tarball
# & 7za x -y `"-o$rootDir`" $tarball
# }
<?xml version="1.0" encoding="windows-1252"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Fragment>
<ComponentGroup Id="FragmentCustomActions" />
<!-- Properties for existing Ini Values - expanding the remembered properties pattern just a bit
-->
<!-- Custom Actions to handle command line property values that override
remembered property values -->
<!-- INSTALLDIR -->
<CustomAction
Id="SaveCmdLineInstallDir"
Property="CMDLINE_INSTALLDIR"
Value="[INSTALLDIR]"
Execute="firstSequence" />
<CustomAction
Id="SetFromCmdLineInstallDir"
Property="INSTALLDIR"
Value="[CMDLINE_INSTALLDIR]"
Execute="firstSequence" />
<!-- INSTALLDIR is not available for Deferred Custom Actions. So we need to save it for later user -->
<CustomAction Id="SetPropertyForExtractTarballs" Property="ExtractTarballs" Value="[INSTALLDIR]" />
<CustomAction
Id="ExtractTarballs"
Script="vbscript"
Execute="deferred"
Return="check"
Impersonate="no">
<![CDATA[
On Error Resume Next
' Example INSTALLDIR property. Its value is 'C:\Program Files\Puppet Labs\DevelopmentKit\'.
' As this is deferred, use the CustomActionData property instead
Dim InstallDir : InstallDir = Session.Property("CustomActionData")
' https://docs.microsoft.com/en-us/windows/desktop/msi/session-message
Const msiMessageTypeError = &H01000000
Const msiMessageTypeWarning = &H02000000
Const msiMessageTypeInfo = &H04000000
' https://docs.microsoft.com/en-us/windows/desktop/msi/return-values-of-jscript-and-vbscript-custom-actions
Const IDOK = 1
Const IDABORT = 3
Dim wshShell : Set wshShell = CreateObject("WScript.Shell")
Dim fso : Set fso = CreateObject("Scripting.FileSystemObject")
Sub Log (Message, IsError)
' Logs through cscript
If IsObject(WScript) Then
If IsObject(WScript.StdErr) And IsError = True Then
WScript.StdErr.WriteLine(Message)
ElseIf IsObject(WScript.StdOut) Then
WScript.StdOut.WriteLine(Message)
End If
End If
' Logs through MSI
If IsObject(Session) Then
' https://docs.microsoft.com/en-us/windows/desktop/msi/installer-createrecord
Dim logRecord : Set logRecord = Installer.CreateRecord(1) ' 1 entry
logRecord.StringData(1) = Message ' Set Index 1
Dim kind : kind = msiMessageTypeInfo
If IsError = True Then kind = msiMessageTypeError
Session.Message kind, logRecord
End If
End Sub
' Executes command, sending its stdout / stderr to the WScript host
Function ExecuteCommand (Command)
Dim output: output = ""
Log "Executing Command : " & Command, False
Dim tempFilePath : tempFilePath = wshShell.ExpandEnvironmentStrings("%TEMP%\" + fso.GetTempName())
' https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/windows-scripting/d5fk67ky%28v%3dvs.84%29
' intWindows Style - 0 - Hides the window and activates another window.
' bWaitOnReturn - True - waits for program termination
Dim exitCode : exitCode = wshShell.Run(Command & " > """ & tempFilePath & """ 2>&1", 0, True)
Dim outFile : Set outFile = fso.OpenTextFile(tempFilePath)
Do While Not outFile.AtEndOfStream
Log outFile.ReadLine(), false
Loop
outFile.Close()
fso.DeleteFile(tempFilePath)
If exitCode <> 0 Then
Log "Execution Failed With Code: " & exitCode, True
ExecuteCommand = False
else
ExecuteCommand = True
End If
End Function
Function GetRubyDirectory(RootDirectory)
GetRubyDirectory = ""
' Find a ruby environment with the PDK gem
Dim RubyFolder : Set RubyFolder = fso.GetFolder(RootDirectory)
For Each RubyVerSubfolder in RubyFolder.SubFolders
Dim RubyFullVersion : RubyFullVersion = RubyVerSubfolder.Name
Dim arrRubyVersion : arrRubyVersion = Split(RubyFullVersion, ".")
Dim RubyMinorVersion : RubyMinorVersion = arrRubyVersion(0) + "." + arrRubyVersion(1) + ".0"
Dim GemFolderPath : GemFolderPath = RubyVerSubfolder.Path + "\lib\ruby\gems\" + RubyMinorVersion + "\gems"
if fso.FolderExists(GemFolderPath) Then
Dim FoundPDK : FoundPDK = false
For Each GemFolder in fso.GetFolder(GemFolderPath).SubFolders
FoundPDK = FoundPDK OR Left(GemFolder.Name,4) = "pdk-"
Next
If FoundPDK Then
GetRubyDirectory = RubyFullVersion
End If
End If
Next
End Function
' Mainline
Log "InstallDir is " + InstallDir, false
' Based on equivalent PowerShell script at;
' https://github.com/puppetlabs/pdk-vanagon/blob/07a4ee7c29ba630ffbec6d87388688b6de90fbfd/resources/files/windows/PuppetDevelopmentKit/PuppetDevelopmentKit.psm1
Dim DEVKIT_BASEDIR
Dim RUBY_DIR
Dim SSL_CERT_FILE
Dim SSL_CERT_DIR
DEVKIT_BASEDIR = fso.GetFolder(InstallDir).ShortPath
Dim RubyVersion : RubyVersion = GetRubyDirectory(DEVKIT_BASEDIR + "\private\ruby")
If RubyVersion = "" Then
Log "Could not find a suitable ruby environment", true
WScript.Quit IDABORT
Else
Log "Ruby " + RubyVersion + " has the PDK gem", false
End If
RUBY_DIR = DEVKIT_BASEDIR + "\private\ruby\" + RubyVersion
SSL_CERT_FILE = DEVKIT_BASEDIR + "\ssl\cert.pem"
SSL_CERT_DIR = DEVKIT_BASEDIR + "\ssl\certs"
Log "DEVKIT_BASEDIR is " + DEVKIT_BASEDIR, false
Log "RUBY_DIR is " + RUBY_DIR, false
Log "SSL_CERT_FILE is " + SSL_CERT_FILE, false
Log "SSL_CERT_DIR is " + SSL_CERT_DIR, false
Dim RubyPath : RubyPath = RUBY_DIR + "\bin\ruby.exe"
Dim comspec : comspec = wshShell.ExpandEnvironmentStrings("%comspec%")
Dim ProcessEnv : Set ProcessEnv = wshShell.Environment( "PROCESS" )
Log "Creating process level environment variables...", false
ProcessEnv("DEVKIT_BASEDIR") = DEVKIT_BASEDIR
ProcessEnv("RUBY_DIR") = RUBY_DIR
ProcessEnv("SSL_CERT_FILE") = SSL_CERT_FILE
ProcessEnv("SSL_CERT_DIR") = SSL_CERT_DIR
ProcessEnv("PDK_DEBUG") = "True"
Dim ExtractScript : ExtractScript = DEVKIT_BASEDIR + "\share\install-tarballs\extract_all.rb"
If Not(fso.FileExists(ExtractScript)) Then
Log "Extract script " & ExtractScript & " could not be found", true
End If
' TODO: This doesn't work. Need to use a binary key in the MSI and a function name
' http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/How-to-use-CustomAction-with-a-VBScript-td700420.html
if ExecuteCommand(comspec & " /C " & RubyPath & " -S -- " & ExtractScript) Then
WScript.Quit IDOK
Else
WScript.Quit IDABORT
End If
]]>
</CustomAction>
</Fragment>
</Wix>
# require 'zlib'
# require 'minitar'
# require 'pathname'
# def logmessage(message)
# puts message if ENV['PDK_DEBUG']
# end
# # Adapted from the minitar extraction in the Puppet Module Tool
# # https://github.com/puppetlabs/puppet/blob/983154f7e29a2a50d416d889a6fed012b9b12399/lib/puppet/module_tool/tar/mini.rb
# # References to `Puppet` and extra logging removed.
# def unpack(sourcefile, destdir, _)
# Zlib::GzipReader.open(sourcefile) do |reader|
# #Archive::Tar::Minitar.unpack(reader, destdir, find_valid_files(reader)) do |action, name, stats|
# Archive::Tar::Minitar.unpack(reader, destdir) do |action, name, stats|
# case action
# when :dir
# validate_entry(destdir, name)
# set_dir_mode!(stats)
# # logmessage("Extracting: #{destdir}/#{name}")
# when :file_start
# # Octal string of the old file mode.
# validate_entry(destdir, name)
# set_file_mode!(stats)
# # logmessage("Extracting: #{destdir}/#{name}")
# end
# set_default_user_and_group!(stats)
# stats
# end
# end
# end
# EXECUTABLE = 0755
# NOT_EXECUTABLE = 0644
# USER_EXECUTE = 0100
# def set_dir_mode!(stats)
# if stats.key?(:mode)
# # This is only the case for `pack`, so this code will not run.
# stats[:mode] = EXECUTABLE
# elsif stats.key?(:entry)
# old_mode = stats[:entry].instance_variable_get(:@mode)
# if old_mode.is_a?(Integer)
# stats[:entry].instance_variable_set(:@mode, EXECUTABLE)
# end
# end
# end
# # Sets a file mode to 0755 if the file is executable by the user.
# # Sets a file mode to 0644 if the file mode is set (non-Windows).
# def sanitized_mode(old_mode)
# old_mode & USER_EXECUTE != 0 ? EXECUTABLE : NOT_EXECUTABLE
# end
# def set_file_mode!(stats)
# if stats.key?(:mode)
# # This is only the case for `pack`, so this code will not run.
# stats[:mode] = sanitized_mode(stats[:mode])
# elsif stats.key?(:entry)
# old_mode = stats[:entry].instance_variable_get(:@mode)
# # If the user can execute the file, set 0755, otherwise 0644.
# if old_mode.is_a?(Integer)
# new_mode = sanitized_mode(old_mode)
# stats[:entry].instance_variable_set(:@mode, new_mode)
# end
# end
# end
# # Sets UID and GID to 0 for standardization.
# def set_default_user_and_group!(stats)
# stats[:uid] = 0
# stats[:gid] = 0
# end
# # Find all the valid files in tarfile.
# #
# # This check was mainly added to ignore 'x' and 'g' flags from the PAX
# # standard but will also ignore any other non-standard tar flags.
# # tar format info: https://pic.dhe.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxa500%2Ftaf.htm
# # pax format info: https://pic.dhe.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxa500%2Fpxarchfm.htm
# def find_valid_files(tarfile)
# Archive::Tar::Minitar.open(tarfile).collect do |entry|
# flag = entry.typeflag
# if flag.nil? || flag =~ /[[:digit:]]/ && (0..7).include?(flag.to_i)
# entry.full_name
# else
# logmessage "Invalid tar flag '#{flag}' will not be extracted: #{entry.name}"
# next
# end
# end
# end
# def validate_entry(destdir, path)
# if Pathname.new(path).absolute?
# raise "Invalid path in package #{path}, #{destdir}"
# end
# path = File.expand_path File.join(destdir, path)
# if path !~ /\A#{Regexp.escape destdir}/
# raise "Invalid path in package #{path}, #{destdir}"
# end
# end
# script_dir = __dir__
# dest_dir = File.expand_path(File.join(script_dir, '..', '..'))
# Dir.glob(File.join(script_dir, '*.tgz')) do |targz_filename|
# logmessage("Extracting #{targz_filename} to #{dest_dir} ...")
# unpack(targz_filename, dest_dir, nil)
# end
#---
# Idea from https://stackoverflow.com/questions/856891/unzip-zip-tar-tag-gz-files-with-ruby
# Altered using https://github.com/puppetlabs/puppet/blob/983154f7e29a2a50d416d889a6fed012b9b12399/lib/puppet/module_tool/tar/mini.rb
require 'fileutils'
require 'rubygems/package'
require 'zlib'
TAR_LONGLINK = '././@LongLink'
def logmessage(message)
puts message if ENV['PDK_DEBUG']
end
EXECUTABLE = 0755
NOT_EXECUTABLE = 0644
USER_EXECUTE = 0100
def sanitized_mode(old_mode)
old_mode & USER_EXECUTE != 0 ? EXECUTABLE : NOT_EXECUTABLE
end
def unpack(sourcefile, destdir, _)
Gem::Package::TarReader.new( Zlib::GzipReader.open sourcefile ) do |tar|
dest = nil
tar.each do |entry|
if entry.full_name == TAR_LONGLINK
dest = File.join destdir, entry.read.strip
next
end
dest ||= File.join destdir, entry.full_name
if entry.directory? || (entry.header.typeflag == '' && entry.full_name.end_with?('/'))
#logmessage("dir #{dest}")
File.delete dest if File.file? dest
# set_dir_mode! will always set this to EXECUTABLE - ref
FileUtils.mkdir_p dest, :mode => EXECUTABLE, :verbose => false
elsif entry.file? || (entry.header.typeflag == '' && !entry.full_name.end_with?('/'))
#logmessage "file #{dest}"
FileUtils.rm_rf dest if File.directory? dest
File.open dest, "wb" do |f|
f.print entry.read
end
FileUtils.chmod sanitized_mode(entry.header.mode), dest, :verbose => false
#elsif entry.header.typeflag == '2' #Symlink!
# File.symlink entry.header.linkname, dest
else
logmessage("Unkown tar entry: #{entry.full_name} type: #{entry.header.typeflag}.")
end
dest = nil
end
end
end
script_dir = __dir__
dest_dir = File.expand_path(File.join(script_dir, '..', '..'))
Dir.glob(File.join(script_dir, '*.tgz')) do |targz_filename|
logmessage("Extracting #{targz_filename} to #{dest_dir} ...")
unpack(targz_filename, dest_dir, nil)
end
<?xml version="1.0" encoding="windows-1252"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Fragment>
<ComponentGroup Id="FragmentSequences" />
<!-- First, save off properties specified on the command line Second,
restore the properties set by the command line overriding the recalled
value from the registry -->
<InstallUISequence>
<!-- INSTALLDIR -->
<Custom Action='SaveCmdLineInstallDir' Before='AppSearch' />
<Custom Action='SetFromCmdLineInstallDir' After='AppSearch'>
CMDLINE_INSTALLDIR
</Custom>
</InstallUISequence>
<InstallExecuteSequence>
<!-- INSTALLDIR -->
<Custom Action='SaveCmdLineInstallDir' Before='AppSearch' />
<Custom Action='SetFromCmdLineInstallDir' After='AppSearch'>
CMDLINE_INSTALLDIR
</Custom>
<Custom Action="SetPropertyForExtractTarballs" Before="InstallInitialize">NOT REMOVE</Custom>
<Custom Action='ExtractTarballs' After='InstallFiles'>
NOT REMOVE
</Custom>
</InstallExecuteSequence>
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize" />
</Fragment>
</Wix>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment