mixlib-install 3.16.0 → 3.19.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d34030b7986d54d5391add8b932400aad83b2623f9a03a60667d54f82de8c50
4
- data.tar.gz: e86a1612a3de84529fc03193e02d8e290609e389dd33bbeeef4d55fa929662fd
3
+ metadata.gz: 9317d5892b2221b3989eb249438e5d3910c622e4d310ee49f529889b70f44246
4
+ data.tar.gz: 9a4537be33415b1a7422eeae3f0afed215b024a078996d6462bdbebbe16096cc
5
5
  SHA512:
6
- metadata.gz: d978a599bd28442d1c2324d15e6e7898b312a30453c3e6cc778145f1afa2b3ff219ab632cc486b240c7052863777dfdb9d7d3049c699ade7a92319efc50eebcf
7
- data.tar.gz: b68a1acc6f92a826a74ab867c2e6db32e31d96442d34c3154330d2a8c554a2e1d780f2d0148d7b18fdea185542711e490c16eca7aa9c3ac1df45eb701cba78b8
6
+ metadata.gz: 9f3d84b117b34746ab9f8e7c69e7073467b510df45ff53698ff93b7f26d4192f9576b9e8944a9b649eeceb2779a0afb3f86be6687c09d1a8b1ffe1bd27c334a7
7
+ data.tar.gz: db4b6d3633476bf36df4b2882bd2a66c3c319bf367b9ec09aa85e80b0236a3e711d979d62f8e274ee34f70cce446d9bbba4d27b09714d21d294534dd7fb7a511
@@ -84,23 +84,9 @@ module Mixlib
84
84
  def filter_artifacts(artifacts)
85
85
  return artifacts unless platform_filters_available?
86
86
 
87
- # For chef-ice, normalize the platform for comparison since chef-ice uses
88
- # normalized platform names (linux, macos, windows) in the API response
89
- comparison_platform = if options.product_name == "chef-ice"
90
- Util.normalize_platform_for_commercial(options.platform)
91
- else
92
- options.platform
93
- end
94
-
95
- # First filter the artifacts based on the platform and architecture
87
+ # Filter by the exact platform and architecture the user requested.
96
88
  artifacts.select! do |a|
97
- a.platform == comparison_platform && a.architecture == options.architecture
98
- end
99
-
100
- # For chef-ice, platform_version is not used (it's empty in responses)
101
- # so we should return the first match if available
102
- if options.product_name == "chef-ice"
103
- return artifacts.first if artifacts.any?
89
+ a.platform == options.platform && a.architecture == options.architecture
104
90
  end
105
91
 
106
92
  # Now we are going to filter based on platform_version.
@@ -33,12 +33,19 @@ module Mixlib
33
33
 
34
34
  COMPAT_DOWNLOAD_URL_ENDPOINT = "http://packages.chef.io".freeze
35
35
 
36
+ # Architecture strings that appear as level-2 keys in PM-structure packages
37
+ # responses (platform -> arch -> pm -> ...) vs version strings in standard
38
+ # responses (platform -> version -> arch -> ...).
39
+ KNOWN_ARCHITECTURES = %w{x86_64 aarch64 i386 arm64 ppc64 ppc64le s390x universal x86}.freeze
40
+
36
41
  # Create filtered list of artifacts
37
42
  #
38
43
  # @return [Array<ArtifactInfo>] list of artifacts for the configured
39
44
  # channel, product name, and product version.
40
45
  def available_artifacts
41
- artifacts = if options.latest_version? || options.partial_version?
46
+ artifacts = if use_licensed_api? && platform_filters_available?
47
+ artifact_from_licensed_metadata
48
+ elsif options.latest_version? || options.partial_version?
42
49
  latest_version
43
50
  else
44
51
  artifacts_for_version(options.product_version)
@@ -78,10 +85,12 @@ module Mixlib
78
85
 
79
86
  # Circumvent early when there are no product artifacts in a specific channel
80
87
  if items.empty?
88
+ hint = options.license_id ? "" : "For habitat based products please provide a license ID with -L or set CHEF_LICENSE_KEY."
81
89
  raise ArtifactsNotFound, <<-EOF
82
90
  No artifacts found matching criteria.
83
91
  product name: #{options.product_name}
84
92
  channel: #{options.channel}
93
+ #{hint}
85
94
  EOF
86
95
  end
87
96
 
@@ -106,6 +115,9 @@ EOF
106
115
  #
107
116
  # @return [Array<ArtifactInfo>] Array of info about found artifacts
108
117
  def latest_version
118
+ # Trial API only supports "latest" as the version — skip version resolution
119
+ return artifacts_for_version("latest") if use_trial_api?
120
+
109
121
  product_versions = if options.partial_version?
110
122
  v = options.product_version
111
123
  partial_version = v.end_with?(".") ? v : v + "."
@@ -138,27 +150,24 @@ EOF
138
150
  query = "v=#{version}"
139
151
  packages_hash = get("/#{options.channel}/#{omnibus_project}/packages?#{query}")
140
152
  # Response structure differs between products:
141
- # - For chef-ice: platform -> architecture -> package_manager -> package_info
142
- # - For other products: platform -> platform_version -> architecture -> package_info
153
+ # - PM-structure products (e.g. chef-ice, inspec-enterprise): platform -> architecture -> package_manager -> package_info
154
+ # - Standard products: platform -> platform_version -> architecture -> package_info
143
155
  results = []
144
- if omnibus_project == "chef-ice"
145
- # chef-ice structure: platform -> architecture -> package_manager -> package_info
156
+ if pm_structure_response?(packages_hash)
146
157
  packages_hash.each do |platform, architectures|
147
158
  architectures.each do |arch, package_managers|
148
- package_managers.each do |pm, pkg_info|
149
- results << {
150
- "omnibus.version" => pkg_info["version"],
151
- "omnibus.platform" => platform,
152
- "omnibus.platform_version" => "",
153
- "omnibus.architecture" => arch,
154
- "omnibus.project" => omnibus_project,
155
- "omnibus.license" => "Apache-2.0",
156
- "omnibus.sha256" => pkg_info["sha256"],
157
- "omnibus.sha1" => pkg_info.fetch("sha1", ""),
158
- "omnibus.md5" => pkg_info.fetch("md5", ""),
159
- "omnibus.package_manager" => pm,
160
- }
161
- end
159
+ pkg_info = package_managers.values.first
160
+ results << {
161
+ "omnibus.version" => pkg_info["version"],
162
+ "omnibus.platform" => platform,
163
+ "omnibus.platform_version" => "",
164
+ "omnibus.architecture" => arch,
165
+ "omnibus.project" => omnibus_project,
166
+ "omnibus.license" => "Apache-2.0",
167
+ "omnibus.sha256" => pkg_info["sha256"],
168
+ "omnibus.sha1" => pkg_info.fetch("sha1", ""),
169
+ "omnibus.md5" => pkg_info.fetch("md5", ""),
170
+ }
162
171
  end
163
172
  end
164
173
  else
@@ -242,6 +251,50 @@ EOF
242
251
  request
243
252
  end
244
253
 
254
+ # Detects whether a packages API response uses PM-structure format.
255
+ # Standard: platform -> platform_version -> architecture -> pkg_info
256
+ #
257
+ # Detection: PM-structure has architecture strings (x86_64, aarch64, …) as
258
+ # the second-level keys; standard has version strings (20.04, 18.04, …).
259
+ def pm_structure_response?(packages_hash)
260
+ first_platform = packages_hash.values.first
261
+ return false unless first_platform.is_a?(Hash)
262
+
263
+ first_platform.keys.any? { |k| KNOWN_ARCHITECTURES.include?(k) }
264
+ end
265
+
266
+ # Fetches a single artifact from the licensed API metadata endpoint.
267
+ # Used when a platform is specified, giving the server full context to
268
+ # derive the package manager and return the correct sha256 — no pm needed.
269
+ def artifact_from_licensed_metadata
270
+ version = (options.latest_version? || options.partial_version?) ? "latest" : options.product_version
271
+ query = "v=#{version}&p=#{options.platform}&m=#{Util.normalize_architecture(options.architecture)}"
272
+ query += "&pv=#{options.platform_version}" unless options.platform_version.to_s.empty?
273
+
274
+ begin
275
+ metadata = get("/#{options.channel}/#{omnibus_project}/metadata?#{query}")
276
+ rescue Net::HTTPServerException => e
277
+ return [] if e.message.match?(/400|404/)
278
+
279
+ raise e
280
+ end
281
+
282
+ return [] if metadata.nil? || metadata.empty?
283
+
284
+ artifact_map = {
285
+ "omnibus.version" => metadata["version"],
286
+ "omnibus.platform" => options.platform,
287
+ "omnibus.platform_version" => options.platform_version.to_s,
288
+ "omnibus.architecture" => options.architecture,
289
+ "omnibus.project" => omnibus_project,
290
+ "omnibus.license" => "Apache-2.0",
291
+ "omnibus.sha256" => metadata["sha256"],
292
+ "omnibus.sha1" => metadata.fetch("sha1", ""),
293
+ "omnibus.md5" => metadata.fetch("md5", ""),
294
+ }
295
+ [create_artifact(artifact_map)]
296
+ end
297
+
245
298
  def create_artifact(artifact_map)
246
299
  # set normalized platform and platform version
247
300
  platform, platform_version = normalize_platform(
@@ -279,22 +332,15 @@ EOF
279
332
 
280
333
  # create the download path with the correct endpoint
281
334
  if use_licensed_api?
282
- # Commercial/trial APIs use the download endpoint with query parameters
283
- # Construct platform parameters
284
- p_param = platform
285
- pv_param = platform_version
335
+ # Commercial/trial APIs use the download endpoint with query parameters.
336
+
286
337
  m_param = Util.normalize_architecture(artifact_map["omnibus.architecture"])
287
338
  v_param = artifact_map["omnibus.version"]
288
-
289
- # For chef-ice, use normalized platform names and add package manager parameter
290
- if omnibus_project == "chef-ice"
291
- p_param = Util.normalize_platform_for_commercial(platform)
292
- # Use package_manager from artifact_map if available, otherwise determine it
293
- pm_param = artifact_map.fetch("omnibus.package_manager", Util.determine_package_manager(options.platform))
294
- download_url = "#{endpoint}/#{options.channel}/#{omnibus_project}/download?v=#{v_param}&license_id=#{options.license_id}&m=#{m_param}&p=#{p_param}&pm=#{pm_param}"
295
- else
296
- download_url = "#{endpoint}/#{options.channel}/#{omnibus_project}/download?p=#{p_param}&pv=#{pv_param}&m=#{m_param}&v=#{v_param}&license_id=#{options.license_id}"
297
- end
339
+ # Use the user's actual platform (e.g. "ubuntu", "el") so the server can derive pm.
340
+ p_param = options.platform || platform
341
+ pv_param = options.platform_version || platform_version
342
+ pv_part = pv_param.to_s.empty? ? "" : "&pv=#{pv_param}"
343
+ download_url = "#{endpoint}/#{options.channel}/#{omnibus_project}/download?p=#{p_param}#{pv_part}&m=#{m_param}&v=#{v_param}&license_id=#{options.license_id}"
298
344
  else
299
345
  base_url = if use_compat_download_url_endpoint?(platform, platform_version)
300
346
  COMPAT_DOWNLOAD_URL_ENDPOINT
@@ -349,7 +395,7 @@ EOF
349
395
  end
350
396
 
351
397
  def use_trial_api?
352
- !options.license_id.nil? && !options.license_id.to_s.empty? && options.license_id.start_with?("free-", "trial-")
398
+ !options.license_id.nil? && !options.license_id.to_s.empty? && options.license_id.start_with?("trial-")
353
399
  end
354
400
 
355
401
  def use_commercial_api?
@@ -31,8 +31,12 @@ module Mixlib
31
31
  end
32
32
 
33
33
  desc "list-versions PRODUCT_NAME CHANNEL", "list available version for a product/channel"
34
+ option :license_id,
35
+ desc: "License ID for commercial/trial API",
36
+ aliases: ["-L"]
34
37
  def list_versions(product_name, channel)
35
- say Mixlib::Install.available_versions(product_name, channel).join("\n")
38
+ lid = options[:license_id] || ENV["CHEF_LICENSE_KEY"]
39
+ say Mixlib::Install.available_versions(product_name, channel, license_id: lid).join("\n")
36
40
  end
37
41
 
38
42
  desc "list-products", "list available products"
@@ -37,15 +37,15 @@ module Mixlib
37
37
 
38
38
  # Check if a license_id is for trial API
39
39
  # @param license_id [String] the license ID to check
40
- # @return [Boolean] true if license_id indicates trial API usage
40
+ # @return [Boolean] true if license_id starts with 'trial-' (trial API usage)
41
41
  def self.trial_license?(license_id)
42
42
  !license_id.nil? && !license_id.to_s.empty? &&
43
- license_id.start_with?("free-", "trial-")
43
+ license_id.start_with?("trial-")
44
44
  end
45
45
 
46
46
  # Check if a license_id is for commercial API
47
47
  # @param license_id [String] the license ID to check
48
- # @return [Boolean] true if license_id indicates commercial API usage
48
+ # @return [Boolean] true if license_id is non-empty and does not start with 'trial-'
49
49
  def self.commercial_license?(license_id)
50
50
  !license_id.nil? && !license_id.to_s.empty? &&
51
51
  !trial_license?(license_id)
@@ -25,46 +25,6 @@
25
25
  # $sha256:
26
26
  ############
27
27
 
28
- # Determine package manager based on platform for commercial API
29
- determine_package_manager() {
30
- case "$platform" in
31
- el|centos|rhel|fedora|amazon|rocky|opensuse*|sles|scientific)
32
- echo "rpm"
33
- ;;
34
- debian|ubuntu|linuxmint|raspbian)
35
- echo "deb"
36
- ;;
37
- mac_os_x|macos|solaris*|smartos|freebsd|aix|omnios)
38
- echo "tar"
39
- ;;
40
- *)
41
- echo "tar"
42
- ;;
43
- esac
44
- }
45
-
46
- # Normalize platform name for commercial API
47
- normalize_platform_name() {
48
- case "$platform" in
49
- el|centos|rhel|fedora|rocky|scientific)
50
- echo "linux"
51
- ;;
52
- mac_os_x|macos)
53
- echo "macos"
54
- ;;
55
- debian|ubuntu|linuxmint|raspbian)
56
- echo "linux"
57
- ;;
58
- freebsd|aix|solaris*|smartos|omnios)
59
- echo "unix"
60
- ;;
61
- *)
62
- # For anything else, use linux as default
63
- echo "linux"
64
- ;;
65
- esac
66
- }
67
-
68
28
  if [ -z "$download_url_override" ]; then
69
29
  echo "Getting information for $project $channel $version for $platform..."
70
30
 
@@ -79,9 +39,9 @@ if [ -z "$download_url_override" ]; then
79
39
  # Use commercial API if license_id is provided, otherwise use omnitruck
80
40
  if [ -z "$base_api_url" ]; then
81
41
  if [ -n "$license_id" ]; then
82
- # Check if license_id starts with 'free-' or 'trial-' for trial API
42
+ # Check if license_id starts with 'trial-' for trial API
83
43
  case "$license_id" in
84
- free-*|trial-*)
44
+ trial-*)
85
45
  # Trial API endpoint
86
46
  base_api_url="<%= Mixlib::Install::Dist::TRIAL_API_ENDPOINT %>"
87
47
  ;;
@@ -96,21 +56,21 @@ if [ -z "$download_url_override" ]; then
96
56
  fi
97
57
  fi
98
58
 
99
- # For chef-ice product, add platform (p), machine (m), and package_manager (pm) parameters
100
59
  if [ -n "$license_id" ]; then
101
- if [ "$project" = "chef-ice" ]; then
102
- package_manager=$(determine_package_manager)
103
- platform_param=$(normalize_platform_name)
104
- metadata_url="$base_api_url/$channel/$project/metadata?license_id=$license_id&v=$version&m=$machine&p=$platform_param&pm=$package_manager"
105
- else
106
- # For other products, use standard parameters
107
- metadata_url="$base_api_url/$channel/$project/metadata?license_id=$license_id&v=$version&p=$platform&pv=$platform_version&m=$machine"
108
- fi
60
+ license_param="&license_id=$license_id"
109
61
  else
110
- # Omnitruck URL parameters without license_id
111
- metadata_url="$base_api_url/$channel/$project/metadata?v=$version&p=$platform&pv=$platform_version&m=$machine"
62
+ license_param=""
112
63
  fi
113
64
 
65
+ if [ -n "$package_manager" ]; then
66
+ pm_param="&pm=$package_manager"
67
+ else
68
+ pm_param=""
69
+ fi
70
+
71
+ # Build the metadata URL.
72
+ metadata_url="$base_api_url/$channel/$project/metadata?v=$version&p=$platform&pv=$platform_version&m=$machine${license_param}${pm_param}"
73
+
114
74
  do_download "$metadata_url" "$metadata_filename"
115
75
 
116
76
  cat "$metadata_filename"
@@ -141,7 +101,7 @@ if [ -z "$download_url_override" ]; then
141
101
  fi
142
102
  else
143
103
  # Parse text response from omnitruck
144
- if grep '^url' $metadata_filename > /dev/null && grep '^sha256' $metadata_filename > /dev/null; then
104
+ if grep '^url' "$metadata_filename" > /dev/null && grep '^sha256' "$metadata_filename" > /dev/null; then
145
105
  echo "downloaded metadata file looks valid..."
146
106
  download_url=`awk '$1 == "url" { print $2 }' "$metadata_filename"`
147
107
  sha256=`awk '$1 == "sha256" { print $2 }' "$metadata_filename"`
@@ -44,21 +44,41 @@ if [ -n "$license_id" ] || ! has_package_filename "$download_url"; then
44
44
  # Just set the download directory
45
45
  if [ -n "$cmdline_filename" ]; then
46
46
  download_filename="$cmdline_filename"
47
- download_dir=`dirname $download_filename`
47
+ download_dir=`dirname "$download_filename"`
48
48
  use_content_disposition="false" # User specified exact filename
49
+
50
+ # Extract filetype from filename (not path) to avoid issues with dots in directory names
51
+ cmdline_basename=`basename "$cmdline_filename"`
52
+ case "$cmdline_basename" in
53
+ *.*) filetype="${cmdline_basename##*.}" ;;
54
+ *)
55
+ echo "Error: -f must include the full package filename (including extension, e.g. .rpm/.deb/.msi)"
56
+ exit 1
57
+ ;;
58
+ esac
59
+
60
+ # Validate against known package types
61
+ case "$filetype" in
62
+ rpm|deb|pkg|msi|dmg|bff|p5p|solaris|sh) : ;;
63
+ *)
64
+ echo "Error: Unknown package filetype '$filetype' derived from -f '$cmdline_filename'"
65
+ exit 1
66
+ ;;
67
+ esac
49
68
  elif [ -n "$cmdline_dl_dir" ]; then
50
69
  download_dir="$cmdline_dl_dir"
51
70
  download_filename="" # Will be determined after download
71
+ filetype="" # Will be determined after we get the actual filename
52
72
  else
53
73
  download_dir="$tmp_dir"
54
74
  download_filename="" # Will be determined after download
75
+ filetype="" # Will be determined after we get the actual filename
55
76
  fi
56
- filetype="" # Will be determined after we get the actual filename
57
77
  else
58
78
  # Traditional omnitruck URLs have the filename in the URL
59
79
  use_content_disposition="false"
60
- filename=`echo $download_url | sed -e 's/?.*//' | sed -e 's/^.*\///'`
61
- filetype=`echo $filename | sed -e 's/^.*\.//'`
80
+ filename=`echo "$download_url" | sed -e 's/?.*//' | sed -e 's/^.*\///'`
81
+ filetype=`echo "$filename" | sed -e 's/^.*\.//'`
62
82
 
63
83
  # use either $tmp_dir, the provided directory (-d) or the provided filename (-f)
64
84
  if [ -n "$cmdline_filename" ]; then
@@ -68,9 +88,9 @@ else
68
88
  else
69
89
  download_filename="$tmp_dir/$filename"
70
90
  fi
71
- download_dir=`dirname $download_filename`
91
+ download_dir=`dirname "$download_filename"`
72
92
  fi
73
- (umask 077 && mkdir -p $download_dir) || exit 1
93
+ (umask 077 && mkdir -p "$download_dir") || exit 1
74
94
 
75
95
  # check if we have that file locally available and if so verify the checksum
76
96
  # Use cases
@@ -135,17 +155,24 @@ if [ "$cached_file_available" != "true" ]; then
135
155
  if [ -f "$tmp_dir/stderr" ]; then
136
156
  # Method 1: Try to extract filename from content-disposition header
137
157
  # Format: content-disposition: attachment; filename="chef-18.8.54-1.el9.x86_64.rpm"
138
- actual_filename=`grep -i 'content-disposition' $tmp_dir/stderr | sed -n 's/.*filename="\([^"]*\)".*/\1/p' | head -1`
158
+ # Some servers omit the quotes: content-disposition: attachment; filename=chef-18.8.54-1.el9.x86_64.rpm
159
+ actual_filename=`grep -i 'content-disposition' "$tmp_dir"/stderr | sed -n 's/.*filename="\([^"]*\)".*/\1/p' | head -1`
160
+ if [ -z "$actual_filename" ]; then
161
+ actual_filename=`grep -i 'content-disposition' "$tmp_dir"/stderr | sed -n 's/.*filename=\([^;[:space:]]*\).*/\1/p' | head -1`
162
+ fi
139
163
 
140
164
  # Method 2: If content-disposition failed, try to extract from location redirect header
141
165
  # Format: location: https://packages.chef.io/files/stable/chef/18.8.54/el/9/chef-18.8.54-1.el9.x86_64.rpm?licenseId=...
142
166
  if [ -z "$actual_filename" ]; then
143
- actual_filename=`grep -i '^location:' $tmp_dir/stderr | head -1 | sed 's/.*\///' | sed 's/?.*//'`
167
+ actual_filename=`grep -i '^location:' "$tmp_dir"/stderr | head -1 | sed 's/.*\///' | sed 's/?.*//'`
144
168
  fi
145
169
 
146
170
  # Method 3: Try extracting from any URL-like pattern in stderr
171
+ # Exclude any line mentioning our own local temp_download path - wget logs it
172
+ # (e.g. "Saving to: ..." / "... saved") and it lives under tmp_dir, which is
173
+ # named install.sh.<pid> and can spuriously match the ".sh" extension below.
147
174
  if [ -z "$actual_filename" ]; then
148
- actual_filename=`grep -i '\.rpm\|\.deb\|\.pkg\|\.msi\|\.dmg' $tmp_dir/stderr | sed -n 's/.*\/\([^/?]*\.\(rpm\|deb\|pkg\|msi\|dmg\)\).*/\1/p' | head -1`
175
+ actual_filename=`grep -i '\.rpm\|\.deb\|\.pkg\|\.msi\|\.dmg\|\.bff\|\.p5p\|\.solaris\|\.sh' "$tmp_dir"/stderr | grep -vF "$temp_download" | sed -n 's/.*\/\([^/?]*\.\(rpm\|deb\|pkg\|msi\|dmg\|bff\|p5p\|solaris\|sh\)\).*/\1/p' | head -1`
149
176
  fi
150
177
  fi
151
178
 
@@ -160,6 +187,10 @@ if [ "$cached_file_available" != "true" ]; then
160
187
  actual_filename="chef_${version}-1_${machine}.deb"
161
188
  elif [ "$platform" = "mac_os_x" ]; then
162
189
  actual_filename="chef-${version}.dmg"
190
+ elif [ "$platform" = "aix" ]; then
191
+ actual_filename="chef-${version}.bff"
192
+ elif [ "$platform" = "solaris2" ]; then
193
+ actual_filename="chef-${version}.solaris"
163
194
  else
164
195
  actual_filename="chef-${version}.pkg"
165
196
  fi
@@ -171,7 +202,7 @@ if [ "$cached_file_available" != "true" ]; then
171
202
  mv "$temp_download" "$download_filename"
172
203
 
173
204
  # Extract filetype from actual filename
174
- filetype=`echo $actual_filename | sed -e 's/^.*\.//'`
205
+ filetype=`echo "$actual_filename" | sed -e 's/^.*\.//'`
175
206
 
176
207
  echo "Downloaded as: $download_filename (type: $filetype)"
177
208
  else
@@ -30,7 +30,7 @@
30
30
 
31
31
  # Check whether a command exists - returns 0 if it does, 1 if it does not
32
32
  exists() {
33
- if command -v $1 >/dev/null 2>&1
33
+ if command -v "$1" >/dev/null 2>&1
34
34
  then
35
35
  return 0
36
36
  else
@@ -66,7 +66,7 @@ unable_to_retrieve_package() {
66
66
  echo "Download URL: $download_url"
67
67
  fi
68
68
  if [ -n "$stderr_results" ]; then
69
- echo "\nDEBUG OUTPUT FOLLOWS:\n$stderr_results"
69
+ printf '\nDEBUG OUTPUT FOLLOWS:\n%s\n' "$stderr_results"
70
70
  fi
71
71
  exit 1
72
72
  }
@@ -98,7 +98,7 @@ http_404_error() {
98
98
  echo "Download URL: $download_url"
99
99
  fi
100
100
  if [ -n "$stderr_results" ]; then
101
- echo "\nDEBUG OUTPUT FOLLOWS:\n$stderr_results"
101
+ printf '\nDEBUG OUTPUT FOLLOWS:\n%s\n' "$stderr_results"
102
102
  fi
103
103
  exit 1
104
104
  }
@@ -106,9 +106,9 @@ http_404_error() {
106
106
  capture_tmp_stderr() {
107
107
  # spool up /tmp/stderr from all the commands we called
108
108
  if [ -f "$tmp_dir/stderr" ]; then
109
- output=`cat $tmp_dir/stderr`
109
+ output=`cat "$tmp_dir"/stderr`
110
110
  stderr_results="${stderr_results}\nSTDERR from $1:\n\n$output\n"
111
- rm $tmp_dir/stderr
111
+ rm "$tmp_dir"/stderr
112
112
  fi
113
113
  }
114
114
 
@@ -117,13 +117,13 @@ do_wget() {
117
117
  echo "trying wget..."
118
118
  # If filename is empty, use --content-disposition to get filename from server
119
119
  if [ -z "$2" ]; then
120
- wget --user-agent="User-Agent: <%= user_agent_string %>" --content-disposition "$1" 2>$tmp_dir/stderr
120
+ wget -S --user-agent="User-Agent: <%= user_agent_string %>" --content-disposition "$1" 2>"$tmp_dir"/stderr
121
121
  else
122
- wget --user-agent="User-Agent: <%= user_agent_string %>" -O "$2" "$1" 2>$tmp_dir/stderr
122
+ wget -S --user-agent="User-Agent: <%= user_agent_string %>" -O "$2" "$1" 2>"$tmp_dir"/stderr
123
123
  fi
124
124
  rc=$?
125
125
  # check for 404
126
- if grep "ERROR 404" $tmp_dir/stderr 2>&1 >/dev/null; then
126
+ if grep "ERROR 404" "$tmp_dir"/stderr >/dev/null 2>&1; then
127
127
  echo "ERROR 404"
128
128
  http_404_error
129
129
  fi
@@ -148,15 +148,15 @@ do_curl() {
148
148
  echo "trying curl..."
149
149
  # If filename is empty, use -O and -J to get filename from Content-Disposition header
150
150
  if [ -z "$2" ]; then
151
- curl -A "User-Agent: <%= user_agent_string %>" --retry 5 -sL -D $tmp_dir/stderr -O -J "$1"
151
+ curl -A "User-Agent: <%= user_agent_string %>" --retry 5 -sL --fail -D "$tmp_dir"/stderr -O -J "$1"
152
152
  rc=$?
153
153
  else
154
- curl -A "User-Agent: <%= user_agent_string %>" --retry 5 -sL -D $tmp_dir/stderr "$1" > "$2"
154
+ curl -A "User-Agent: <%= user_agent_string %>" --retry 5 -sL --fail -D "$tmp_dir"/stderr "$1" > "$2"
155
155
  rc=$?
156
156
  fi
157
157
 
158
- # check for 404
159
- if grep "404 Not Found" $tmp_dir/stderr 2>&1 >/dev/null; then
158
+ # check for 404 (matches both HTTP/1.1 "404 Not Found" and HTTP/2 "404")
159
+ if grep "404" "$tmp_dir"/stderr >/dev/null 2>&1; then
160
160
  echo "ERROR 404"
161
161
  http_404_error
162
162
  fi
@@ -179,7 +179,7 @@ do_curl() {
179
179
  # do_fetch URL FILENAME
180
180
  do_fetch() {
181
181
  echo "trying fetch..."
182
- fetch --user-agent="User-Agent: <%= user_agent_string %>" -o "$2" "$1" 2>$tmp_dir/stderr
182
+ fetch --user-agent="User-Agent: <%= user_agent_string %>" -o "$2" "$1" 2>"$tmp_dir"/stderr
183
183
  # check for bad return status
184
184
  [ $? -ne 0 ] && return 1
185
185
  return 0
@@ -188,12 +188,16 @@ do_fetch() {
188
188
  # do_perl URL FILENAME
189
189
  do_perl() {
190
190
  echo "trying perl..."
191
- perl -e 'use LWP::Simple; getprint($ARGV[0]);' "$1" > "$2" 2>$tmp_dir/stderr
191
+ perl -e 'use LWP::Simple; getprint($ARGV[0]);' "$1" > "$2" 2>"$tmp_dir"/stderr
192
192
  rc=$?
193
- # check for 404
194
- if grep "404 Not Found" $tmp_dir/stderr 2>&1 >/dev/null; then
195
- echo "ERROR 404"
196
- http_404_error
193
+ # check for HTTP error status codes (4xx/5xx)
194
+ if grep "^[45][0-9][0-9] " "$tmp_dir"/stderr >/dev/null 2>&1; then
195
+ if grep "404 Not Found" "$tmp_dir"/stderr >/dev/null 2>&1; then
196
+ echo "ERROR 404"
197
+ http_404_error
198
+ fi
199
+ capture_tmp_stderr "perl"
200
+ return 1
197
201
  fi
198
202
 
199
203
  # check for bad return status or empty output
@@ -208,10 +212,10 @@ do_perl() {
208
212
  # do_python URL FILENAME
209
213
  do_python() {
210
214
  echo "trying python..."
211
- python -c "import sys,urllib2; sys.stdout.write(urllib2.urlopen(urllib2.Request(sys.argv[1], headers={ 'User-Agent': '<%= user_agent_string %>' })).read())" "$1" > "$2" 2>$tmp_dir/stderr
215
+ python -c "import sys,urllib2; sys.stdout.write(urllib2.urlopen(urllib2.Request(sys.argv[1], headers={ 'User-Agent': '<%= user_agent_string %>' })).read())" "$1" > "$2" 2>"$tmp_dir"/stderr
212
216
  rc=$?
213
217
  # check for 404
214
- if grep "HTTP Error 404" $tmp_dir/stderr 2>&1 >/dev/null; then
218
+ if grep "HTTP Error 404" "$tmp_dir"/stderr >/dev/null 2>&1; then
215
219
  echo "ERROR 404"
216
220
  http_404_error
217
221
  fi
@@ -228,12 +232,12 @@ do_python() {
228
232
  do_checksum() {
229
233
  if exists sha256sum; then
230
234
  echo "Comparing checksum with sha256sum..."
231
- checksum=`sha256sum $1 | awk '{ print $1 }'`
235
+ checksum=`sha256sum "$1" | awk '{ print $1 }'`
232
236
  [ "$checksum" = "$2" ]
233
237
  return $?
234
238
  elif exists shasum; then
235
239
  echo "Comparing checksum with shasum..."
236
- checksum=`shasum -a 256 $1 | awk '{ print $1 }'`
240
+ checksum=`shasum -a 256 "$1" | awk '{ print $1 }'`
237
241
  [ "$checksum" = "$2" ]
238
242
  return $?
239
243
  else
@@ -247,11 +251,11 @@ do_download() {
247
251
  echo "downloading $1"
248
252
  echo " to file $2"
249
253
 
250
- url=`echo $1`
254
+ url="$1"
251
255
  if [ "$platform" = "solaris2" ]; then
252
256
  if [ "$platform_version" = "5.9" ] || [ "$platform_version" = "5.10" ]; then
253
257
  # solaris 9 lacks openssl, solaris 10 lacks recent enough credentials - your base O/S is completely insecure, please upgrade
254
- url=`echo $url | sed -e 's/https/http/'`
258
+ url=`echo "$url" | sed -e 's/https/http/'`
255
259
  fi
256
260
  fi
257
261
 
@@ -259,23 +263,23 @@ do_download() {
259
263
  # perl, in particular may be present but LWP::Simple may not be installed
260
264
 
261
265
  if exists wget; then
262
- do_wget $url $2 && return 0
266
+ do_wget "$url" "$2" && return 0
263
267
  fi
264
268
 
265
269
  if exists curl; then
266
- do_curl $url $2 && return 0
270
+ do_curl "$url" "$2" && return 0
267
271
  fi
268
272
 
269
273
  if exists fetch; then
270
- do_fetch $url $2 && return 0
274
+ do_fetch "$url" "$2" && return 0
271
275
  fi
272
276
 
273
277
  if exists perl; then
274
- do_perl $url $2 && return 0
278
+ do_perl "$url" "$2" && return 0
275
279
  fi
276
280
 
277
281
  if exists python; then
278
- do_python $url $2 && return 0
282
+ do_python "$url" "$2" && return 0
279
283
  fi
280
284
 
281
285
  unable_to_retrieve_package
@@ -285,6 +289,8 @@ do_download() {
285
289
  # TYPE is "rpm", "deb", "solaris", "sh", etc.
286
290
  install_file() {
287
291
  echo "Installing $project $version"
292
+ echo "File type: $1"
293
+ echo "Install file: $2"
288
294
  case "$1" in
289
295
  "rpm")
290
296
  if [ "$platform" = "nexus" ] || [ "$platform" = "ios_xr" ]; then
@@ -305,11 +311,11 @@ install_file() {
305
311
  ;;
306
312
  "solaris")
307
313
  echo "installing with pkgadd..."
308
- echo "conflict=nocheck" > $tmp_dir/nocheck
309
- echo "action=nocheck" >> $tmp_dir/nocheck
310
- echo "mail=" >> $tmp_dir/nocheck
311
- pkgrm -a $tmp_dir/nocheck -n $project >/dev/null 2>&1 || true
312
- pkgadd -G -n -d "$2" -a $tmp_dir/nocheck $project
314
+ echo "conflict=nocheck" > "$tmp_dir"/nocheck
315
+ echo "action=nocheck" >> "$tmp_dir"/nocheck
316
+ echo "mail=" >> "$tmp_dir"/nocheck
317
+ pkgrm -a "$tmp_dir"/nocheck -n "$project" >/dev/null 2>&1 || true
318
+ pkgadd -G -n -d "$2" -a "$tmp_dir"/nocheck "$project"
313
319
  ;;
314
320
  "pkg")
315
321
  echo "installing with installer..."
@@ -319,7 +325,7 @@ install_file() {
319
325
  echo "installing dmg file..."
320
326
  hdiutil detach "/Volumes/<%= macos_dir %>" >/dev/null 2>&1 || true
321
327
  hdiutil attach "$2" -mountpoint "/Volumes/<%= macos_dir %>"
322
- cd / && /usr/sbin/installer -pkg `find "/Volumes/<%= macos_dir %>" -name \*.pkg` -target /
328
+ cd / && /usr/sbin/installer -pkg "`find "/Volumes/<%= macos_dir %>" -name \*.pkg`" -target /
323
329
  hdiutil detach "/Volumes/<%= macos_dir %>"
324
330
  ;;
325
331
  "sh" )
@@ -328,7 +334,7 @@ install_file() {
328
334
  ;;
329
335
  "p5p" )
330
336
  echo "installing p5p package..."
331
- pkg install -g "$2" $project
337
+ pkg install -g "$2" "$project"
332
338
  ;;
333
339
  *)
334
340
  echo "Unknown filetype: $1"
@@ -350,7 +356,7 @@ else
350
356
  fi
351
357
  # secure-ish temp dir creation without having mktemp available (DDoS-able but not exploitable)
352
358
  tmp_dir="$tmp/install.sh.$$"
353
- (umask 077 && mkdir $tmp_dir) || exit 1
359
+ (umask 077 && mkdir "$tmp_dir") || exit 1
354
360
 
355
361
  ############
356
362
  # end of helpers.sh
@@ -22,7 +22,7 @@ if [ -z "$version" ] && [ "$CI" != "true" ]; then
22
22
  echo
23
23
  fi
24
24
 
25
- install_file $filetype "$download_filename"
25
+ install_file "$filetype" "$download_filename"
26
26
 
27
27
  if [ -n "$tmp_dir" ]; then
28
28
  rm -r "$tmp_dir"
@@ -26,7 +26,7 @@ machine=`uname -m`
26
26
  os=`uname -s`
27
27
 
28
28
  if [ -f "/etc/lsb-release" ] && grep DISTRIB_ID /etc/lsb-release >/dev/null && ! grep wrlinux /etc/lsb-release >/dev/null; then
29
- platform=`grep DISTRIB_ID /etc/lsb-release | cut -d "=" -f 2 | tr '[A-Z]' '[a-z]'`
29
+ platform=`grep DISTRIB_ID /etc/lsb-release | cut -d "=" -f 2 | tr 'A-Z' 'a-z'`
30
30
  platform_version=`grep DISTRIB_RELEASE /etc/lsb-release | cut -d "=" -f 2`
31
31
 
32
32
  if [ "$platform" = '"cumulus linux"' ]; then
@@ -44,11 +44,11 @@ elif [ -f "/etc/Eos-release" ]; then
44
44
  platform_version=`awk '{print $4}' /etc/Eos-release`
45
45
  machine="i386"
46
46
  elif [ -f "/etc/redhat-release" ]; then
47
- platform=`sed 's/^\(.\+\) release.*/\1/' /etc/redhat-release | tr '[A-Z]' '[a-z]'`
47
+ platform=`sed 's/^\(.\+\) release.*/\1/' /etc/redhat-release | tr 'A-Z' 'a-z'`
48
48
  platform_version=`sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/redhat-release`
49
49
 
50
50
  if [ "$platform" = "rocky linux" ]; then
51
- source /etc/os-release
51
+ . /etc/os-release
52
52
  os="${REDHAT_SUPPORT_PRODUCT}"
53
53
  platform_version="${ROCKY_SUPPORT_PRODUCT_VERSION}"
54
54
  platform=$ID
@@ -62,8 +62,8 @@ elif [ -f "/etc/redhat-release" ]; then
62
62
  fi
63
63
 
64
64
  elif [ -f "/etc/system-release" ]; then
65
- platform=`sed 's/^\(.\+\) release.\+/\1/' /etc/system-release | tr '[A-Z]' '[a-z]'`
66
- platform_version=`sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/system-release | tr '[A-Z]' '[a-z]'`
65
+ platform=`sed 's/^\(.\+\) release.\+/\1/' /etc/system-release | tr 'A-Z' 'a-z'`
66
+ platform_version=`sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/system-release | tr 'A-Z' 'a-z'`
67
67
  case $platform in amazon*) # sh compat method of checking for a substring
68
68
  . /etc/os-release
69
69
  platform_version=$VERSION_ID
@@ -71,7 +71,6 @@ elif [ -f "/etc/system-release" ]; then
71
71
  case $platform_version in
72
72
  "2022"|"2023")
73
73
  platform="amazon"
74
- platform_version=$platform_version
75
74
  ;;
76
75
  "2")
77
76
  platform="el"
@@ -120,7 +119,7 @@ elif [ "$os" = "AIX" ]; then
120
119
  elif [ -f "/etc/os-release" ]; then
121
120
  . /etc/os-release
122
121
  if [ -n "$CISCO_RELEASE_INFO" ]; then
123
- . $CISCO_RELEASE_INFO
122
+ . "$CISCO_RELEASE_INFO"
124
123
  fi
125
124
 
126
125
  platform=$ID
@@ -150,7 +149,7 @@ fi
150
149
  # now the complete responsibility of the server-side endpoints
151
150
  #
152
151
 
153
- major_version=`echo $platform_version | cut -d. -f1`
152
+ major_version=`echo "$platform_version" | cut -d. -f1`
154
153
  case $platform in
155
154
  # FIXME: should remove this case statement completely
156
155
  "el")
@@ -6,7 +6,6 @@
6
6
  if [ -n "$https_proxy" ]; then
7
7
  echo "setting https_proxy: $https_proxy"
8
8
  HTTPS_PROXY=$https_proxy
9
- https_proxy=$https_proxy
10
9
  export HTTPS_PROXY
11
10
  export https_proxy
12
11
  fi
@@ -14,7 +13,6 @@ fi
14
13
  if [ -n "$http_proxy" ]; then
15
14
  echo "setting http_proxy: $http_proxy"
16
15
  HTTP_PROXY=$http_proxy
17
- http_proxy=$http_proxy
18
16
  export HTTP_PROXY
19
17
  export http_proxy
20
18
  fi
@@ -22,7 +20,6 @@ fi
22
20
  if [ -n "$ftp_proxy" ]; then
23
21
  echo "setting ftp_proxy: $ftp_proxy"
24
22
  FTP_PROXY=$ftp_proxy
25
- ftp_proxy=$ftp_proxy
26
23
  export FTP_PROXY
27
24
  export ftp_proxy
28
25
  fi
@@ -30,7 +27,6 @@ fi
30
27
  if [ -n "$no_proxy" ]; then
31
28
  echo "setting no_proxy: $no_proxy"
32
29
  NO_PROXY=$no_proxy
33
- no_proxy=$no_proxy
34
30
  export NO_PROXY
35
31
  export no_proxy
36
32
  fi
@@ -7,6 +7,8 @@
7
7
  # $version: Requested version to be installed.
8
8
  # $channel: Channel to install the product from
9
9
  # $project: Project to be installed
10
+ # $package_manager: (Optional) Package manager override (e.g. rpm, deb, tar, msi).
11
+ # If omitted, the server derives it from the platform (p) parameter.
10
12
  # $cmdline_filename: Name of the package downloaded on local disk.
11
13
  # $cmdline_dl_dir: Name of the directory downloaded package will be saved to on local disk.
12
14
  # $install_strategy: Method of package installations. default strategy is to always install upon exec. Set to "once" to skip if project is installed
@@ -18,9 +20,10 @@
18
20
  # Defaults
19
21
  channel="stable"
20
22
  project="<%= default_product %>"
23
+ package_manager=""
21
24
  <%= "license_id=\"#{license_id}\"" if license_id && !license_id.to_s.empty? %>
22
25
 
23
- while getopts pnv:b:c:f:P:d:s:l:a:L: opt
26
+ while getopts pnv:b:c:f:P:d:s:l:a:L:i: opt
24
27
  do
25
28
  case "$opt" in
26
29
 
@@ -36,14 +39,15 @@ do
36
39
  l) download_url_override="$OPTARG";;
37
40
  a) checksum="$OPTARG";;
38
41
  L) license_id="$OPTARG";;
42
+ i) package_manager="$OPTARG";;
39
43
  \?) # unknown flag
40
44
  echo >&2 \
41
- "usage: $0 [-P project] [-b base_api_url] [-c release_channel] [-v version] [-f filename | -d download_dir] [-s install_strategy] [-l download_url_override] [-a checksum] [-L license_id]"
45
+ "usage: $0 [-P project] [-b base_api_url] [-c release_channel] [-v version] [-f filename | -d download_dir] [-s install_strategy] [-l download_url_override] [-a checksum] [-L license_id] [-i package_manager]"
42
46
  exit 1;;
43
47
  esac
44
48
  done
45
49
 
46
- shift `expr $OPTIND - 1`
50
+ shift "`expr $OPTIND - 1`"
47
51
 
48
52
  # Use CHEF_LICENSE_KEY environment variable if license_id not provided via CLI
49
53
  if [ -z "$license_id" ] && [ -n "$CHEF_LICENSE_KEY" ]; then
@@ -17,7 +17,6 @@ function Get-ProjectMetadata {
17
17
  # Base url to retrieve metadata from.
18
18
  [uri]
19
19
  $base_server_uri,
20
- [string]
21
20
  # Project to install
22
21
  [string]
23
22
  $project = '<%= default_product %>',
@@ -42,7 +41,10 @@ function Get-ProjectMetadata {
42
41
  $architecture = 'auto',
43
42
  # License ID for commercial API access
44
43
  [string]
45
- $license_id <%= "= '#{license_id}'" if license_id && !license_id.to_s.empty? %>
44
+ $license_id <%= "= '#{license_id}'" if license_id && !license_id.to_s.empty? %>,
45
+ # Package manager override (e.g. msi, zip). If omitted, the server derives it from platform.
46
+ [string]
47
+ $package_manager
46
48
  )
47
49
 
48
50
  # The following legacy switches are just aliases for the current channel
@@ -72,8 +74,8 @@ function Get-ProjectMetadata {
72
74
 
73
75
  if ([string]::IsNullOrEmpty($base_server_uri)) {
74
76
  if (-not [string]::IsNullOrEmpty($license_id)) {
75
- # Check if license_id starts with 'free-' or 'trial-' for trial API
76
- if ($license_id -match '^(free-|trial-)') {
77
+ # Check if license_id starts with 'trial-' for trial API
78
+ if ($license_id -match '^(trial-)') {
77
79
  # Trial API endpoint
78
80
  $base_server_uri = "<%= Mixlib::Install::Dist::TRIAL_API_ENDPOINT %>"
79
81
  }
@@ -88,23 +90,28 @@ function Get-ProjectMetadata {
88
90
  }
89
91
  }
90
92
 
91
- # For chef-ice product, add platform (p), machine (m), and package_manager (pm) parameters
93
+ # Only include pm param when the caller explicitly passed -package_manager;
94
+ # the server derives it from the platform (p) parameter otherwise.
95
+
96
+ if (-not [string]::IsNullOrEmpty($package_manager)) {
97
+ $pm_param = "&pm=$package_manager"
98
+ }
99
+ else {
100
+ $pm_param = ""
101
+ }
102
+
103
+ # License param (appended only when a license_id is present)
104
+ $license_param = ""
92
105
  if (-not [string]::IsNullOrEmpty($license_id)) {
93
- if ($project -eq "chef-ice") {
94
- $package_manager = "msi"
95
- $platform_param = "windows"
96
- $metadata_url = "$base_server_uri$channel/$project/metadata?license_id=$license_id&v=$version&m=$architecture&p=$platform_param&pm=$package_manager"
97
- }
98
- else {
99
- # For other products, use standard parameters
100
- $metadata_url = "$base_server_uri$channel/$project/metadata?license_id=$license_id&v=$version&p=$platform&pv=$platform_version&m=$architecture"
101
- }
106
+ $license_param = "&license_id=$license_id"
102
107
  }
103
108
  else {
104
- # Omnitruck URL parameters without license_id
105
- $metadata_url = "$base_server_uri$channel/$project/metadata?v=$version&p=$platform&pv=$platform_version&m=$architecture"
109
+ $license_param = ""
106
110
  }
107
111
 
112
+ # Build the metadata URL.
113
+ $metadata_url = "$base_server_uri$channel/$project/metadata?v=$version&p=$platform&pv=$platform_version&m=$architecture$license_param$pm_param"
114
+
108
115
  Write-Host "Downloading $project details from $metadata_url"
109
116
  $response = (Get-WebContent $metadata_url).trim()
110
117
 
@@ -127,7 +134,7 @@ function Get-ProjectMetadata {
127
134
  } else {
128
135
  # Parse text response from omnitruck
129
136
  $package_metadata = $response -split '\n' |
130
- foreach { $hash = @{} } {$key, $value = $_ -split '\s+'; $hash.Add($key, $value)} {$hash}
137
+ ForEach-Object -Begin { $hash = @{} } -Process { $key, $value = $_ -split '\s+'; $hash.Add($key, $value) } -End { $hash }
131
138
  }
132
139
 
133
140
  Write-Host "Project details: "
@@ -2,7 +2,10 @@
2
2
  # Set strict error handling to ensure errors cause script failures
3
3
  $ErrorActionPreference = 'Stop'
4
4
 
5
- try { [Console]::OutputEncoding = New-Object -typename System.Text.ASCIIEncoding } catch { }
5
+ try { [Console]::OutputEncoding = New-Object -typename System.Text.ASCIIEncoding } catch {
6
+ # non-interactive/redirected hosts may not allow setting this; safe to ignore
7
+ Write-Verbose "Unable to set console output encoding: $($_.Exception.Message)"
8
+ }
6
9
  [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls,Tls11,Tls12'
7
10
 
8
11
  function Get-PlatformVersion {
@@ -36,11 +39,11 @@ function Get-PlatformArchitecture {
36
39
  }
37
40
 
38
41
  function Get-Win32OS {
39
- if(!$global:win32OS)
42
+ if(!$script:win32OS)
40
43
  {
41
- $global:win32OS = Get-WMIQuery win32_operatingsystem
44
+ $script:win32OS = Get-WMIQuery win32_operatingsystem
42
45
  }
43
- $global:win32OS
46
+ $script:win32OS
44
47
  }
45
48
 
46
49
  function New-Uri {
@@ -88,7 +91,7 @@ function Get-WebContentOnFullNet {
88
91
  $bypassList = $env:no_proxy
89
92
 
90
93
 
91
- if($bypassList -ne $null){
94
+ if($null -ne $bypassList){
92
95
 
93
96
  $bypassList = $bypassList.split(",")
94
97
  $proxy.BypassList = $byPassList
@@ -147,7 +150,7 @@ function Get-WebContentOnCore {
147
150
  $copyStreamOp = $response.Content.CopyToAsync($downloadedFileStream)
148
151
  $copyStreamOp.Wait()
149
152
  $downloadedFileStream.Close()
150
- if ($copyStreamOp.Exception -ne $null) {
153
+ if ($null -ne $copyStreamOp.Exception) {
151
154
  throw $copyStreamOp.Exception
152
155
  }
153
156
 
@@ -173,7 +176,7 @@ function Test-ProjectPackage {
173
176
  param ($Path, $Algorithm = 'SHA256', $Hash)
174
177
  if (!$env:Valid_ProjectPackage){
175
178
  Write-Host "Testing the $Algorithm hash for $path."
176
- $ActualHash = (Custom-GetFileHash -Algorithm $Algorithm -Path $Path).Hash.ToLower()
179
+ $ActualHash = (Get-CustomFileHash -Algorithm $Algorithm -Path $Path).Hash.ToLower()
177
180
 
178
181
  Write-Host "`tDesired Hash - '$Hash'"
179
182
  Write-Host "`tActual Hash - '$ActualHash'"
@@ -185,13 +188,13 @@ function Test-ProjectPackage {
185
188
  return $env:Valid_ProjectPackage
186
189
  }
187
190
 
188
- function Custom-GetFileHash ($Path, $Algorithm) {
191
+ function Get-CustomFileHash ($Path, $Algorithm) {
189
192
  function disposable($o){($o -is [IDisposable]) -and (($o | get-member | foreach-object {$_.name}) -contains 'Dispose')}
190
193
  function use($obj, [scriptblock]$sb){try {& $sb} catch [exception]{throw $_} finally {if (disposable $obj) {$obj.Dispose()}} }
191
194
  $Path = (resolve-path $Path).providerpath
192
195
  $hash = @{Algorithm = $Algorithm; Path = $Path}
193
196
  use ($c = Get-SHA256Converter) {
194
- use ($in = (gi $Path).OpenRead()) {
197
+ use ($in = (Get-Item $Path).OpenRead()) {
195
198
  $hash.Hash = ([BitConverter]::ToString($c.ComputeHash($in))).Replace("-", "").ToUpper()
196
199
  }
197
200
  }
@@ -199,7 +202,7 @@ function Custom-GetFileHash ($Path, $Algorithm) {
199
202
  }
200
203
 
201
204
  function Get-SHA256Converter {
202
- if ($(Is-FIPS) -ge 1) {
205
+ if ($(Test-Fips) -ge 1) {
203
206
  New-Object -TypeName Security.Cryptography.SHA256Cng
204
207
  } else {
205
208
  if($PSVersionTable.PSEdition -eq 'Core') {
@@ -211,7 +214,7 @@ function Get-SHA256Converter {
211
214
  }
212
215
  }
213
216
 
214
- function Is-FIPS {
217
+ function Test-Fips {
215
218
  if (!$env:fips){
216
219
  $env:fips = (Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy).Enabled
217
220
  }
@@ -62,7 +62,10 @@ function Install-Project {
62
62
  $base_server_uri,
63
63
  # License ID for commercial API access
64
64
  [string]
65
- $license_id <%= "= '#{license_id}'" if license_id && !license_id.to_s.empty? %>
65
+ $license_id <%= "= '#{license_id}'" if license_id && !license_id.to_s.empty? %>,
66
+ # Package manager override (e.g. msi, zip). If omitted, the server derives it from platform.
67
+ [string]
68
+ $package_manager
66
69
  )
67
70
 
68
71
  # Use CHEF_LICENSE_KEY environment variable if license_id not provided
@@ -106,7 +109,7 @@ function Install-Project {
106
109
  $download_url = $download_url_override
107
110
  $sha256 = $checksum
108
111
  } else {
109
- $package_metadata = Get-ProjectMetadata -project $project -channel $channel -version $version -prerelease:$prerelease -nightlies:$nightlies -architecture $architecture -base_server_uri $base_server_uri -license_id $license_id
112
+ $package_metadata = Get-ProjectMetadata -project $project -channel $channel -version $version -prerelease:$prerelease -nightlies:$nightlies -architecture $architecture -base_server_uri $base_server_uri -license_id $license_id -package_manager $package_manager
110
113
  $download_url = $package_metadata.url
111
114
  $sha256 = $package_metadata.sha256
112
115
  }
@@ -108,6 +108,12 @@ PRODUCT_MATRIX = Mixlib::Install::ProductMatrix.new do
108
108
  github_repo "chef/chef-workstation"
109
109
  end
110
110
 
111
+ product "chef-workstation-enterprise" do
112
+ product_name "Chef Workstation Enterprise"
113
+ package_name "chef-workstation-enterprise"
114
+ github_repo "chef/chef-workstation-enterprise"
115
+ end
116
+
111
117
  product "chefdk" do
112
118
  product_name "Chef Development Kit"
113
119
  package_name "chefdk"
@@ -155,6 +161,11 @@ PRODUCT_MATRIX = Mixlib::Install::ProductMatrix.new do
155
161
  package_name "inspec"
156
162
  end
157
163
 
164
+ product "inspec-enterprise" do
165
+ product_name "Chef InSpec Enterprise"
166
+ package_name "inspec-enterprise"
167
+ end
168
+
158
169
  product "mac-bootstrapper" do
159
170
  product_name "Habitat Mac Bootstrapper"
160
171
  package_name "mac-bootstrapper"
@@ -188,6 +199,11 @@ PRODUCT_MATRIX = Mixlib::Install::ProductMatrix.new do
188
199
  downloads_product_page_url :not_available
189
200
  end
190
201
 
202
+ product "migrate-ice" do
203
+ product_name "Chef Infra-Client migration tool"
204
+ package_name "migrate-ice"
205
+ end
206
+
191
207
  product "omnibus-toolchain" do
192
208
  product_name "Omnibus Toolchain"
193
209
  package_name "omnibus-toolchain"
@@ -240,7 +240,7 @@ module Mixlib
240
240
  # Use custom base_url if provided, otherwise determine from license type
241
241
  endpoint_base = if @base_url
242
242
  @base_url
243
- elsif license_id.start_with?("free-", "trial-")
243
+ elsif license_id.start_with?("trial-")
244
244
  Mixlib::Install::Dist::TRIAL_API_ENDPOINT
245
245
  else
246
246
  Mixlib::Install::Dist::COMMERCIAL_API_ENDPOINT
@@ -259,7 +259,7 @@ module Mixlib
259
259
  # Use custom base_url if provided, otherwise determine from license type
260
260
  endpoint_base = if @base_url
261
261
  @base_url
262
- elsif license_id.start_with?("free-", "trial-")
262
+ elsif license_id.start_with?("trial-")
263
263
  Mixlib::Install::Dist::TRIAL_API_ENDPOINT
264
264
  else
265
265
  Mixlib::Install::Dist::COMMERCIAL_API_ENDPOINT
@@ -278,14 +278,7 @@ module Mixlib
278
278
  url = "#{base}#{endpoint}"
279
279
  end
280
280
 
281
- # chef-ice uses different parameters than chef
282
- if @project.casecmp("chef-ice") == 0
283
- # For chef-ice: p (platform), m (machine), pm (package_manager)
284
- url << "?p=windows&m=$platform_architecture&pm=msi"
285
- else
286
- # For chef and other products: p (platform), pv (platform_version), m (machine)
287
- url << "?p=windows&m=$platform_architecture&pv=$platform_version"
288
- end
281
+ url << "?p=windows&m=$platform_architecture&pv=$platform_version"
289
282
  url << "&v=#{CGI.escape(version)}" unless %w{latest true nightlies}.include?(version)
290
283
  url << "&prerelease=true" if prerelease
291
284
  url << "&nightlies=true" if nightlies
@@ -172,53 +172,6 @@ module Mixlib
172
172
  architecture
173
173
  end
174
174
  end
175
-
176
- #
177
- # Determines package manager based on platform for commercial API
178
- #
179
- # @param [String] platform
180
- #
181
- # @return String [package_manager] (rpm, deb, tar, msi, dmg)
182
- def determine_package_manager(platform)
183
- case platform
184
- when /^el/, /^centos/, /^rhel/, /^fedora/, /^amazon/, /^rocky/, /^opensuse/, /^sles/, /^scientific/
185
- "rpm"
186
- when /^debian/, /^ubuntu/, /^linuxmint/, /^raspbian/
187
- "deb"
188
- when /^mac_os_x/, /^macos/
189
- "dmg"
190
- when /^windows/
191
- "msi"
192
- when /^solaris/, /^smartos/, /^freebsd/, /^aix/, /^omnios/
193
- "tar"
194
- else
195
- # Default to tar for unknown platforms
196
- "tar"
197
- end
198
- end
199
-
200
- #
201
- # Normalizes platform name for commercial API (chef-ice)
202
- # Maps specific platform names to generic categories
203
- #
204
- # @param [String] platform
205
- #
206
- # @return String [normalized_platform] (linux, macos, windows)
207
- def normalize_platform_for_commercial(platform)
208
- case platform
209
- when /^el/, /^centos/, /^rhel/, /^fedora/, /^rocky/, /^scientific/, /^debian/, /^ubuntu/, /^linuxmint/, /^raspbian/, /^opensuse/, /^sles/, /^amazon/
210
- "linux"
211
- when /^mac_os_x/, /^macos/
212
- "macos"
213
- when /^windows/
214
- "windows"
215
- when /^freebsd/, /^aix/, /^solaris/, /^smartos/, /^omnios/
216
- "unix"
217
- else
218
- # Default to linux for unknown platforms
219
- "linux"
220
- end
221
- end
222
175
  end
223
176
  end
224
177
  end
@@ -1,5 +1,5 @@
1
1
  module Mixlib
2
2
  class Install
3
- VERSION = "3.16.0"
3
+ VERSION = "3.19.2"
4
4
  end
5
5
  end
@@ -63,14 +63,18 @@ module Mixlib
63
63
  #
64
64
  # @param [String, Symbol] channel
65
65
  #
66
+ # @param [String] license_id (optional)
67
+ #
66
68
  # @return [Array<String>] list of available versions for the given
67
69
  # product_name and channel.
68
- def self.available_versions(product_name, channel)
70
+ def self.available_versions(product_name, channel, license_id: nil)
71
+ opts = {
72
+ product_name: product_name,
73
+ channel: channel.to_sym,
74
+ }
75
+ opts[:license_id] = license_id if license_id
69
76
  Backend.available_versions(
70
- Mixlib::Install::Options.new(
71
- product_name: product_name,
72
- channel: channel.to_sym
73
- )
77
+ Mixlib::Install::Options.new(opts)
74
78
  )
75
79
  end
76
80
 
@@ -297,7 +301,7 @@ module Mixlib
297
301
  # url pointing to the omnitruck to be queried by the script.
298
302
  # license_id [String]
299
303
  # license ID for commercial or trial API access.
300
- # If license_id starts with 'free-' or 'trial-', trial API defaults are enforced.
304
+ # If license_id starts with 'trial-', trial API defaults are enforced.
301
305
  #
302
306
  def self.install_sh(context = {})
303
307
  # Apply trial API defaults if license_id indicates trial
@@ -325,7 +329,7 @@ module Mixlib
325
329
  # url pointing to the omnitruck to be queried by the script.
326
330
  # license_id [String]
327
331
  # license ID for commercial or trial API access.
328
- # If license_id starts with 'free-' or 'trial-', trial API defaults are enforced.
332
+ # If license_id starts with 'trial-', trial API defaults are enforced.
329
333
  #
330
334
  def self.install_ps1(context = {})
331
335
  # Apply trial API defaults if license_id indicates trial
@@ -19,5 +19,7 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_dependency "mixlib-shellout"
21
21
  spec.add_dependency "mixlib-versioning"
22
+ spec.add_dependency "ostruct"
22
23
  spec.add_dependency "thor"
24
+ spec.required_ruby_version = ">= 2.6"
23
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixlib-install
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.16.0
4
+ version: 3.19.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thom May
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2026-02-24 00:00:00.000000000 Z
12
+ date: 2026-08-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mixlib-shellout
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: ostruct
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: thor
44
58
  requirement: !ruby/object:Gem::Requirement
@@ -110,7 +124,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
124
  requirements:
111
125
  - - ">="
112
126
  - !ruby/object:Gem::Version
113
- version: '0'
127
+ version: '2.6'
114
128
  required_rubygems_version: !ruby/object:Gem::Requirement
115
129
  requirements:
116
130
  - - ">="