Class: Omnibus::Packager::WindowsBase

Inherits:
Base
  • Object
show all
Defined in:
lib/omnibus/packagers/windows_base.rb

Direct Known Subclasses

APPX, MSI

Constant Summary collapse

DEFAULT_TIMESTAMP_SERVERS =
["http://timestamp.digicert.com",
"http://timestamp.verisign.com/scripts/timestamp.dll"].freeze

Constants included from Util

Util::SHELLOUT_OPTIONS

Constants included from NullArgumentable

NullArgumentable::NULL

Instance Attribute Summary

Attributes inherited from Base

#project

Instance Method Summary collapse

Methods inherited from Base

build, #exclusions, id, #id, #initialize, #install_dir, #package_name, #package_path, #resource_path, #resources_path, #run!, setup, #skip_packager, #staging_dir, #staging_dir_path

Methods included from Util

#compiler_safe_path, #copy_file, #create_directory, #create_file, #create_link, included, #path_key, #remove_directory, #remove_file, #retry_block, #shellout, #shellout!, #windows_safe_path

Methods included from Templating

included, #render_template, #render_template_content

Methods included from Sugarable

extended, included, #node

Methods included from NullArgumentable

included, #null?

Methods included from Logging

included

Methods included from Instrumentation

#measure

Methods included from Digestable

#digest, #digest_directory, included

Constructor Details

This class inherits a constructor from Omnibus::Packager::Base

Instance Method Details

#algorithmObject



93
94
95
# File 'lib/omnibus/packagers/windows_base.rb', line 93

def algorithm
  signing_identity[:algorithm]
end

#cert_store_nameObject



97
98
99
# File 'lib/omnibus/packagers/windows_base.rb', line 97

def cert_store_name
  signing_identity[:store]
end

#certificate_subjectString

Get the certificate subject of the signing identity

Returns:

  • (String)


159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/omnibus/packagers/windows_base.rb', line 159

def certificate_subject
  return "CN=#{project.package_name}" unless signing_identity

  store = machine_store? ? "LocalMachine" : "CurrentUser"
  cmd = [].tap do |arr|
    arr << "powershell.exe"
    arr << "-ExecutionPolicy Bypass"
    arr << "-NoProfile"
    arr << "-Command (Get-Item Cert:/#{store}/#{cert_store_name}/#{thumbprint}).Subject"
  end.join(" ")

  shellout!(cmd).stdout.strip
end

#machine_store?Boolean

Returns:

  • (Boolean)


105
106
107
# File 'lib/omnibus/packagers/windows_base.rb', line 105

def machine_store?
  signing_identity[:machine_store]
end

#sign_package(package_file) ⇒ Object

Iterates through available timestamp servers and tries to sign the file with with each server, stopping after the first to succeed. If none succeed, an exception is raised.



114
115
116
117
118
119
120
121
# File 'lib/omnibus/packagers/windows_base.rb', line 114

def sign_package(package_file)
  success = false
  timestamp_servers.each do |ts|
    success = try_sign(package_file, ts)
    break if success
  end
  raise FailedToSignWindowsPackage.new unless success
end

#signing_identity(thumbprint = NULL, params = NULL) ⇒ Hash{:thumbprint => String, :store => String, :timestamp_servers => Array[String]}

Set the signing certificate name

Examples:

signing_identity 'FooCert'
signing_identity 'FooCert', store: 'BarStore'

Parameters:

  • thumbprint (String) (defaults to: NULL)

    the thumbprint of the certificate in the certificate store

  • params (Hash<Symbol, String>) (defaults to: NULL)

    an optional hash that defines the parameters for the singing identity

Options Hash (params):

  • :store (String) — default: My

    The name of the certificate store which contains the certificate

  • :timestamp_servers (Array<String>, String)

    A trusted timestamp server or a list of truested timestamp servers to be tried. They are tried in the order provided.

  • :machine_store (TrueClass, FalseClass) — default: false

    If set to true, the local machine store will be searched for a valid certificate. Otherwise, the current user store is used

    Setting nothing will default to trying [‘timestamp.digicert.com’, ‘timestamp.verisign.com/scripts/timestamp.dll’]

Returns:

  • (Hash{:thumbprint => String, :store => String, :timestamp_servers => Array[String]})


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/omnibus/packagers/windows_base.rb', line 48

def signing_identity(thumbprint = NULL, params = NULL)
  unless null?(thumbprint)
    @signing_identity = {}
    unless thumbprint.is_a?(String)
      raise InvalidValue.new(:signing_identity, "be a String")
    end

    @signing_identity[:thumbprint] = thumbprint

    if !null?(params)
      unless params.is_a?(Hash)
        raise InvalidValue.new(:params, "be a Hash")
      end

      valid_keys = %i{store timestamp_servers machine_store algorithm}
      invalid_keys = params.keys - valid_keys
      unless invalid_keys.empty?
        raise InvalidValue.new(:params, "contain keys from [#{valid_keys.join(", ")}]. "\
                               "Found invalid keys [#{invalid_keys.join(", ")}]")
      end

      if !params[:machine_store].nil? && !(
         params[:machine_store].is_a?(TrueClass) ||
         params[:machine_store].is_a?(FalseClass))
        raise InvalidValue.new(:params, "contain key :machine_store of type TrueClass or FalseClass")
      end
    else
      params = {}
    end

    @signing_identity[:store] = params[:store] || "My"
    @signing_identity[:algorithm] = params[:algorithm] || "SHA256"
    servers = params[:timestamp_servers] || DEFAULT_TIMESTAMP_SERVERS
    @signing_identity[:timestamp_servers] = [servers].flatten
    @signing_identity[:machine_store] = params[:machine_store] || false
  end

  @signing_identity
end

#thumbprintObject



89
90
91
# File 'lib/omnibus/packagers/windows_base.rb', line 89

def thumbprint
  signing_identity[:thumbprint]
end

#timestamp_serversObject



101
102
103
# File 'lib/omnibus/packagers/windows_base.rb', line 101

def timestamp_servers
  signing_identity[:timestamp_servers]
end

#try_sign(package_file, url) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/omnibus/packagers/windows_base.rb', line 123

def try_sign(package_file, url)
  cmd = [].tap do |arr|
    arr << "signtool.exe"
    arr << "sign /v"
    arr << "/t #{url}"
    arr << "/fd #{algorithm}"
    arr << "/sm" if machine_store?
    arr << "/s #{cert_store_name}"
    arr << "/sha1 #{thumbprint}"
    arr << "/d #{project.package_name}"
    arr << "\"#{package_file}\""
  end.join(" ")
  status = shellout(cmd)
  if status.exitstatus != 0
    log.warn(log_key) do
      <<-EOH.strip
            Failed to add timestamp with timeserver #{url}

            STDOUT
            ------
            #{status.stdout}

            STDERR
            ------
            #{status.stderr}
      EOH
    end
  end
  status.exitstatus == 0
end

#windows_package_versionString

Parse and return the version from the Omnibus::Project#build_version.

A project’s build_version looks something like:

dev builds => 11.14.0-alpha.1+20140501194641.git.94.561b564
           => 0.0.0+20140506165802.1

rel builds => 11.14.0.alpha.1 || 11.14.0

The appx and msi version specs expects a version that looks like X.Y.Z.W where X, Y, Z & W are all 32 bit integers.

Returns:

  • (String)


188
189
190
191
# File 'lib/omnibus/packagers/windows_base.rb', line 188

def windows_package_version
  major, minor, patch = project.build_version.split(/[.+-]/)
  [major, minor, patch, project.build_iteration].join(".")
end