mixlib-install 3.15.0 → 3.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -14
  3. data/lib/mixlib/install/backend/base.rb +1 -1
  4. data/lib/mixlib/install/backend/package_router.rb +101 -12
  5. data/lib/mixlib/install/cli.rb +5 -1
  6. data/lib/mixlib/install/dist.rb +24 -4
  7. data/lib/mixlib/install/generator/base.rb +1 -14
  8. data/lib/mixlib/install/generator/bourne/scripts/check_product.sh +1 -1
  9. data/lib/mixlib/install/generator/bourne/scripts/fetch_metadata.sh.erb +49 -19
  10. data/lib/mixlib/install/generator/bourne/scripts/fetch_package.sh +47 -23
  11. data/lib/mixlib/install/generator/bourne/scripts/helpers.sh.erb +40 -38
  12. data/lib/mixlib/install/generator/bourne/scripts/install_package.sh +2 -2
  13. data/lib/mixlib/install/generator/bourne/scripts/platform_detection.sh +22 -22
  14. data/lib/mixlib/install/generator/bourne/scripts/proxy_env.sh +4 -4
  15. data/lib/mixlib/install/generator/bourne/scripts/script_cli_parameters.sh.erb +9 -3
  16. data/lib/mixlib/install/generator/bourne.rb +5 -20
  17. data/lib/mixlib/install/generator/powershell/scripts/get_project_metadata.ps1.erb +60 -36
  18. data/lib/mixlib/install/generator/powershell/scripts/helpers.ps1.erb +8 -4
  19. data/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb +36 -16
  20. data/lib/mixlib/install/generator/powershell/scripts/platform_detection.ps1 +1 -0
  21. data/lib/mixlib/install/generator/powershell.rb +5 -9
  22. data/lib/mixlib/install/generator.rb +5 -4
  23. data/lib/mixlib/install/options.rb +40 -0
  24. data/lib/mixlib/install/product_matrix.rb +6 -1
  25. data/lib/mixlib/install/script_generator.rb +72 -22
  26. data/lib/mixlib/install/version.rb +1 -1
  27. data/lib/mixlib/install.rb +91 -16
  28. data/mixlib-install.gemspec +2 -0
  29. data/support/install_command.ps1 +3 -0
  30. metadata +17 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87576368d2d140a99f1e02a508c19bb3d55dbc637ca3b4d6b39f6bbfcd857ad6
4
- data.tar.gz: 4114e92d08cba4ea18093bfaa2e0f02b584dda06de878e56e11fd2c68aa58b9e
3
+ metadata.gz: a8834bc88f13ec12a50e22d4173262a60a525c4c1841d7fbf078a078f05aaa75
4
+ data.tar.gz: 606b0c99f0b2245e5e5b7ccc50c15b306dd3a7e20406225342733075c9bf1069
5
5
  SHA512:
6
- metadata.gz: 428a3a79f93b46f570ac709eaf7fcae54360dc893742360bf42b02ee0565c2992426d1588b4cd4baf6ae7d84898c284148a284824dece7d9890e75964fcea425
7
- data.tar.gz: 991621b4937be08512cc2bb556a3848485a2a2e0223dc05382987a400131e49d20536d06df04c84a9d3d18acd10d6f93a7e6d34bb83c3022999fcfe59b4d4756
6
+ metadata.gz: fbc683eca04103659882fdf18678b7e7221b590285794896ecfb5d73fee6d5313407eb959565ee2ae320fef92e911f47b5fe810e63d7af77d44c1f05d1da1bdc
7
+ data.tar.gz: b4fa005ff07749845435b43d5df9fcf4a256924415e416beaa145c91f5cf0251d01945d90749cd34c375d5591bac18643ca65668686b242d8e3661ac68de801a
data/Gemfile CHANGED
@@ -3,20 +3,6 @@ source "https://rubygems.org"
3
3
  gemspec
4
4
 
5
5
  gem "chef-utils", "= 16.6.14" if RUBY_VERSION < "2.6.0"
6
- # OpenSSL version constraints to fix CRL checking issues in OpenSSL 3.6+
7
- # Ruby 2.6-2.7 bundled openssl needs update to 3.1.2+
8
- # Ruby 3.0-3.2 bundled openssl needs update to 3.1.2+
9
- # Ruby 3.3 bundled openssl needs update to 3.2.2+
10
- # Ruby 3.4 bundled openssl needs update to 3.3.1+
11
- if RUBY_VERSION < "2.7.0"
12
- gem "openssl", ">= 3.1.2", "< 3.2.0"
13
- elsif RUBY_VERSION < "3.3.0"
14
- gem "openssl", ">= 3.1.2"
15
- elsif RUBY_VERSION < "3.4.0"
16
- gem "openssl", ">= 3.2.2"
17
- elsif RUBY_VERSION < "4.0.0"
18
- gem "openssl", ">= 3.3.1"
19
- end
20
6
 
21
7
  group :test do
22
8
  gem "contracts", "~> 0.16.0" # this entry can go away when ruby < 3 support is gone
@@ -84,7 +84,7 @@ module Mixlib
84
84
  def filter_artifacts(artifacts)
85
85
  return artifacts unless platform_filters_available?
86
86
 
87
- # First filter the artifacts based on the platform and architecture
87
+ # Filter by the exact platform and architecture the user requested.
88
88
  artifacts.select! do |a|
89
89
  a.platform == options.platform && a.architecture == options.architecture
90
90
  end
@@ -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 + "."
@@ -137,16 +149,18 @@ EOF
137
149
  # Commercial/trial APIs use the packages endpoint which returns metadata for all platforms
138
150
  query = "v=#{version}"
139
151
  packages_hash = get("/#{options.channel}/#{omnibus_project}/packages?#{query}")
140
- # Response is a nested hash: platform -> platform_version -> architecture -> package_info
141
- # Flatten it to an array of package metadata objects
152
+ # Response structure differs between products:
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
142
155
  results = []
143
- packages_hash.each do |platform, platform_versions|
144
- platform_versions.each do |platform_version, architectures|
145
- architectures.each do |arch, pkg_info|
156
+ if pm_structure_response?(packages_hash)
157
+ packages_hash.each do |platform, architectures|
158
+ architectures.each do |arch, package_managers|
159
+ pkg_info = package_managers.values.first
146
160
  results << {
147
161
  "omnibus.version" => pkg_info["version"],
148
162
  "omnibus.platform" => platform,
149
- "omnibus.platform_version" => platform_version,
163
+ "omnibus.platform_version" => "",
150
164
  "omnibus.architecture" => arch,
151
165
  "omnibus.project" => omnibus_project,
152
166
  "omnibus.license" => "Apache-2.0",
@@ -156,6 +170,25 @@ EOF
156
170
  }
157
171
  end
158
172
  end
173
+ else
174
+ # Standard structure: platform -> platform_version -> architecture -> package_info
175
+ packages_hash.each do |platform, platform_versions|
176
+ platform_versions.each do |platform_version, architectures|
177
+ architectures.each do |arch, pkg_info|
178
+ results << {
179
+ "omnibus.version" => pkg_info["version"],
180
+ "omnibus.platform" => platform,
181
+ "omnibus.platform_version" => platform_version,
182
+ "omnibus.architecture" => arch,
183
+ "omnibus.project" => omnibus_project,
184
+ "omnibus.license" => "Apache-2.0",
185
+ "omnibus.sha256" => pkg_info["sha256"],
186
+ "omnibus.sha1" => pkg_info.fetch("sha1", ""),
187
+ "omnibus.md5" => pkg_info.fetch("md5", ""),
188
+ }
189
+ end
190
+ end
191
+ end
159
192
  end
160
193
  else
161
194
  results = get("/api/v1/#{options.channel}/#{omnibus_project}/#{version}/artifacts")["results"]
@@ -198,6 +231,16 @@ EOF
198
231
  res = http.request(create_http_request(full_path))
199
232
  res.value
200
233
  JSON.parse(res.body)
234
+ rescue Net::HTTPClientError, Net::HTTPServerError => e
235
+ # Provide helpful error messages for licensed API failures
236
+ if use_trial_api?
237
+ if options.channel != :stable || (options.product_version != :latest && options.product_version.to_sym != :latest)
238
+ raise "Trial API only supports stable channel and latest version. " \
239
+ "Current settings: channel=#{options.channel}, version=#{options.product_version}. " \
240
+ "Error: #{e.message}"
241
+ end
242
+ end
243
+ raise e
201
244
  end
202
245
 
203
246
  def create_http_request(full_path)
@@ -208,6 +251,50 @@ EOF
208
251
  request
209
252
  end
210
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
+
211
298
  def create_artifact(artifact_map)
212
299
  # set normalized platform and platform version
213
300
  platform, platform_version = normalize_platform(
@@ -245,13 +332,15 @@ EOF
245
332
 
246
333
  # create the download path with the correct endpoint
247
334
  if use_licensed_api?
248
- # Commercial/trial APIs use the download endpoint with query parameters
249
- # Construct platform parameters
250
- p_param = platform
251
- pv_param = platform_version
335
+ # Commercial/trial APIs use the download endpoint with query parameters.
336
+
252
337
  m_param = Util.normalize_architecture(artifact_map["omnibus.architecture"])
253
338
  v_param = artifact_map["omnibus.version"]
254
- 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}"
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}"
255
344
  else
256
345
  base_url = if use_compat_download_url_endpoint?(platform, platform_version)
257
346
  COMPAT_DOWNLOAD_URL_ENDPOINT
@@ -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"
@@ -26,10 +26,30 @@ module Mixlib
26
26
  RESOURCES_URL = "https://www.chef.io/support".freeze
27
27
  # MacOS volume name
28
28
  MACOS_VOLUME = "chef_software".freeze
29
- # Windows install directory name
30
- WINDOWS_INSTALL_DIR = "opscode".freeze
31
- # Linux install directory name
32
- LINUX_INSTALL_DIR = "/opt"
29
+ # Omnibus Windows install directory name
30
+ OMNIBUS_WINDOWS_INSTALL_DIR = "opscode".freeze
31
+ # Omnibus Linux install directory name
32
+ OMNIBUS_LINUX_INSTALL_DIR = "/opt".freeze
33
+ # Habitat Windows install directory name
34
+ HABITAT_WINDOWS_INSTALL_DIR = "hab\\pkgs".freeze
35
+ # Habitat Linux install directory name
36
+ HABITAT_LINUX_INSTALL_DIR = "/hab/pkgs".freeze
37
+
38
+ # Check if a license_id is for trial API
39
+ # @param license_id [String] the license ID to check
40
+ # @return [Boolean] true if license_id indicates trial API usage
41
+ def self.trial_license?(license_id)
42
+ !license_id.nil? && !license_id.to_s.empty? &&
43
+ license_id.start_with?("free-", "trial-")
44
+ end
45
+
46
+ # Check if a license_id is for commercial API
47
+ # @param license_id [String] the license ID to check
48
+ # @return [Boolean] true if license_id indicates commercial API usage
49
+ def self.commercial_license?(license_id)
50
+ !license_id.nil? && !license_id.to_s.empty? &&
51
+ !trial_license?(license_id)
52
+ end
33
53
  end
34
54
  end
35
55
  end
@@ -19,7 +19,6 @@ require "erb" unless defined?(Erb)
19
19
  require "ostruct" unless defined?(OpenStruct)
20
20
  require_relative "../util"
21
21
  require_relative "../dist"
22
- require "net/http" unless defined?(Net::HTTP)
23
22
 
24
23
  module Mixlib
25
24
  class Install
@@ -50,13 +49,12 @@ module Mixlib
50
49
  if File.exist? "#{script_path}.erb"
51
50
  # Default values to use incase they are not set in the context
52
51
  context[:project_name] ||= Mixlib::Install::Dist::PROJECT_NAME.freeze
53
- context[:base_url] ||= Mixlib::Install::Dist::OMNITRUCK_ENDPOINT.freeze
54
52
  context[:default_product] ||= Mixlib::Install::Dist::DEFAULT_PRODUCT.freeze
55
53
  context[:bug_url] ||= Mixlib::Install::Dist::BUG_URL.freeze
56
54
  context[:support_url] ||= Mixlib::Install::Dist::SUPPORT_URL.freeze
57
55
  context[:resources_url] ||= Mixlib::Install::Dist::RESOURCES_URL.freeze
58
56
  context[:macos_dir] ||= Mixlib::Install::Dist::MACOS_VOLUME.freeze
59
- context[:windows_dir] ||= Mixlib::Install::Dist::WINDOWS_INSTALL_DIR.freeze
57
+ context[:windows_dir] ||= context[:default_product].casecmp("chef-ice") == 0 ? Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR.freeze : Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR.freeze
60
58
  context[:user_agent_string] = Util.user_agent_string(context[:user_agent_headers])
61
59
 
62
60
  context_object = OpenStruct.new(context).instance_eval { binding }
@@ -69,17 +67,6 @@ module Mixlib
69
67
  def get_script(name, context = {})
70
68
  self.class.get_script(name, context)
71
69
  end
72
-
73
- def install_sh_from_upstream
74
- uri = URI.parse(options.options[:new_omnibus_download_url])
75
- response = Net::HTTP.get_response(uri)
76
-
77
- if response.code == "200"
78
- response.body
79
- else
80
- raise StandardError, "unable to fetch the install.sh"
81
- end
82
- end
83
70
  end
84
71
  end
85
72
  end
@@ -8,7 +8,7 @@ else
8
8
  fi
9
9
 
10
10
  for path in $install_paths; do
11
- if [ -d "$path" ] 2>/dev/null && [ "x$install_strategy" = "xonce" ]; then
11
+ if [ -d "$path" ] && [ "$install_strategy" = "once" ]; then
12
12
  echo "$project installation detected at $path"
13
13
  echo "install_strategy set to 'once'"
14
14
  echo "Nothing to install"
@@ -3,7 +3,15 @@
3
3
  # This section calls omnitruck to get the information about the build to be
4
4
  # installed.
5
5
  #
6
+ # EXAMPLE
7
+ # curl -L '<%= base_url || "https://chefdownload-commercial.chef.io" %>/install.sh<%= base_url && base_url.include?("chefdownload") ? "?license_id=$CHEF_LICENSE_KEY" : "" %>' | sudo bash -s -- -P chef -c stable
8
+ #
9
+ # Gets the download url and SHA256 checksum for the latest stable release of Chef Workstation.
10
+ # EXAMPLE
11
+ # curl -L '<%= base_url || "https://chefdownload-commercial.chef.io" %>/install.sh<%= base_url && base_url.include?("chefdownload") ? "?license_id=$CHEF_LICENSE_KEY" : "" %>' | sudo bash -s -- -P chef-workstation -c stable
12
+ #
6
13
  # Inputs:
14
+ # $base_api_url:
7
15
  # $channel:
8
16
  # $project:
9
17
  # $version:
@@ -17,38 +25,60 @@
17
25
  # $sha256:
18
26
  ############
19
27
 
20
- if test "x$download_url_override" = "x"; then
28
+ if [ -z "$download_url_override" ]; then
21
29
  echo "Getting information for $project $channel $version for $platform..."
22
30
 
23
31
  metadata_filename="$tmp_dir/metadata.txt"
32
+ <% if base_url && !base_url.to_s.empty? %>
33
+ # Set base_api_url from option if not already set via CLI parameter
34
+ if [ -z "$base_api_url" ]; then
35
+ base_api_url="<%= base_url %>"
36
+ fi
37
+ <% end %>
24
38
 
25
39
  # Use commercial API if license_id is provided, otherwise use omnitruck
26
- if test "x$license_id" != "x"; then
27
- # Check if license_id starts with 'free-' or 'trial-' for trial API
28
- case "$license_id" in
29
- free-*|trial-*)
30
- # Trial API endpoint
31
- base_api_url="https://chefdownload-trial.chef.io"
32
- ;;
33
- *)
34
- # Commercial API endpoint
35
- base_api_url="https://chefdownload-commercial.chef.io"
36
- ;;
37
- esac
38
- metadata_url="$base_api_url/$channel/$project/metadata?v=$version&p=$platform&pv=$platform_version&m=$machine&license_id=$license_id"
40
+ if [ -z "$base_api_url" ]; then
41
+ if [ -n "$license_id" ]; then
42
+ # Check if license_id starts with 'free-' or 'trial-' for trial API
43
+ case "$license_id" in
44
+ free-*|trial-*)
45
+ # Trial API endpoint
46
+ base_api_url="<%= Mixlib::Install::Dist::TRIAL_API_ENDPOINT %>"
47
+ ;;
48
+ *)
49
+ # Commercial API endpoint
50
+ base_api_url="<%= Mixlib::Install::Dist::COMMERCIAL_API_ENDPOINT %>"
51
+ ;;
52
+ esac
53
+ else
54
+ # Omnitruck endpoint
55
+ base_api_url="<%= Mixlib::Install::Dist::OMNITRUCK_ENDPOINT %>"
56
+ fi
57
+ fi
58
+
59
+ if [ -n "$license_id" ]; then
60
+ license_param="&license_id=$license_id"
39
61
  else
40
- # Omnitruck endpoint
41
- metadata_url="<%= base_url %>/$channel/$project/metadata?v=$version&p=$platform&pv=$platform_version&m=$machine"
62
+ license_param=""
42
63
  fi
43
64
 
44
- do_download "$metadata_url" "$metadata_filename"
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
+
74
+ do_download "$metadata_url" "$metadata_filename"
45
75
 
46
76
  cat "$metadata_filename"
47
77
 
48
78
  echo ""
49
79
 
50
80
  # Commercial and trial APIs return JSON, omnitruck returns text format
51
- if test "x$license_id" != "x"; then
81
+ if [ -n "$license_id" ]; then
52
82
  # Parse JSON response from commercial/trial API
53
83
  # Check if response looks like JSON
54
84
  if grep -q '^{' "$metadata_filename" 2>/dev/null; then
@@ -57,7 +87,7 @@ if test "x$download_url_override" = "x"; then
57
87
  download_url=`sed -n 's/.*"url":"\([^"]*\)".*/\1/p' "$metadata_filename"`
58
88
  sha256=`sed -n 's/.*"sha256":"\([^"]*\)".*/\1/p' "$metadata_filename"`
59
89
 
60
- if test "x$download_url" != "x" && test "x$sha256" != "x"; then
90
+ if [ -n "$download_url" ] && [ -n "$sha256" ]; then
61
91
  echo "downloaded metadata file looks valid..."
62
92
  else
63
93
  echo "downloaded metadata file is corrupted or an uncaught error was encountered in downloading the file..."
@@ -17,16 +17,36 @@
17
17
 
18
18
  # For licensed APIs (commercial/trial), the URL is an endpoint, not a direct file URL
19
19
  # The actual filename will come from the Content-Disposition header
20
- if test "x$license_id" != "x"; then
21
- # Use content-disposition to get the filename
20
+ # Also check if download_url_override is used with a URL that doesn't have a filename
21
+ # (e.g., ends with /download or /download?params instead of /package-1.2.3.rpm)
22
+
23
+ # Function to check if URL path contains a valid package filename
24
+ has_package_filename() {
25
+ url_path=`echo "$1" | sed -e 's/?.*//' -e 's/^.*\///'`
26
+ # Check if the path segment has a package extension
27
+ case "$url_path" in
28
+ *.rpm|*.deb|*.pkg|*.msi|*.dmg|*.bff|*.p5p|*.solaris|*.sh)
29
+ return 0 # has valid filename
30
+ ;;
31
+ *)
32
+ return 1 # no valid filename
33
+ ;;
34
+ esac
35
+ }
36
+
37
+ # Determine if we need to use content-disposition based on license_id or URL structure
38
+ if [ -n "$license_id" ] || ! has_package_filename "$download_url"; then
39
+ # Use content-disposition to get the filename if:
40
+ # 1. license_id is set (commercial/trial API)
41
+ # 2. URL doesn't contain a package filename (e.g., ends with /download or /download?params)
22
42
  use_content_disposition="true"
23
43
  # We don't know the filename yet - it will come from Content-Disposition
24
44
  # Just set the download directory
25
- if test "x$cmdline_filename" != "x"; then
45
+ if [ -n "$cmdline_filename" ]; then
26
46
  download_filename="$cmdline_filename"
27
47
  download_dir=`dirname $download_filename`
28
48
  use_content_disposition="false" # User specified exact filename
29
- elif test "x$cmdline_dl_dir" != "x"; then
49
+ elif [ -n "$cmdline_dl_dir" ]; then
30
50
  download_dir="$cmdline_dl_dir"
31
51
  download_filename="" # Will be determined after download
32
52
  else
@@ -41,9 +61,9 @@ else
41
61
  filetype=`echo $filename | sed -e 's/^.*\.//'`
42
62
 
43
63
  # use either $tmp_dir, the provided directory (-d) or the provided filename (-f)
44
- if test "x$cmdline_filename" != "x"; then
64
+ if [ -n "$cmdline_filename" ]; then
45
65
  download_filename="$cmdline_filename"
46
- elif test "x$cmdline_dl_dir" != "x"; then
66
+ elif [ -n "$cmdline_dl_dir" ]; then
47
67
  download_filename="$cmdline_dl_dir/$filename"
48
68
  else
49
69
  download_filename="$tmp_dir/$filename"
@@ -64,19 +84,19 @@ cached_file_available="false"
64
84
  verify_checksum="true"
65
85
 
66
86
  # Skip caching checks when using content-disposition since we don't know the real filename yet
67
- if test "x$use_content_disposition" = "xtrue"; then
87
+ if [ "$use_content_disposition" = "true" ]; then
68
88
  cached_file_available="false"
69
89
  verify_checksum="true"
70
- elif test "x$download_filename" != "x" && test -f "$download_filename"; then
90
+ elif [ -n "$download_filename" ] && [ -f "$download_filename" ]; then
71
91
  echo "$download_filename exists"
72
92
  cached_file_available="true"
73
93
  fi
74
94
 
75
- if test "x$download_url_override" != "x" && test "x$use_content_disposition" = "xfalse"; then
95
+ if [ -n "$download_url_override" ] && [ "$use_content_disposition" = "false" ]; then
76
96
  echo "Download URL override specified"
77
- if test "x$cached_file_available" = "xtrue"; then
97
+ if [ "$cached_file_available" = "true" ]; then
78
98
  echo "Verifying local file"
79
- if test "x$sha256" = "x"; then
99
+ if [ -z "$sha256" ]; then
80
100
  echo "Checksum not specified, ignoring existing file"
81
101
  cached_file_available="false" # download new file
82
102
  verify_checksum="false" # no checksum to compare after download
@@ -92,7 +112,7 @@ if test "x$download_url_override" != "x" && test "x$use_content_disposition" = "
92
112
  else
93
113
  echo "$download_filename not found"
94
114
  cached_file_available="false" # download new file
95
- if test "x$sha256" = "x"; then
115
+ if [ -z "$sha256" ]; then
96
116
  verify_checksum="false" # no checksum to compare after download
97
117
  else
98
118
  verify_checksum="true" # checksum new downloaded file
@@ -100,8 +120,8 @@ if test "x$download_url_override" != "x" && test "x$use_content_disposition" = "
100
120
  fi
101
121
  fi
102
122
 
103
- if test "x$cached_file_available" != "xtrue"; then
104
- if test "x$use_content_disposition" = "xtrue"; then
123
+ if [ "$cached_file_available" != "true" ]; then
124
+ if [ "$use_content_disposition" = "true" ]; then
105
125
  # For licensed APIs, download to a temporary file and extract filename from response headers
106
126
  # The download_dir was already set during initialization above
107
127
 
@@ -112,33 +132,33 @@ if test "x$cached_file_available" != "xtrue"; then
112
132
  do_download "$download_url" "$temp_download"
113
133
 
114
134
  # Extract filename from response headers (try multiple methods for compatibility)
115
- if test -f "$tmp_dir/stderr"; then
135
+ if [ -f "$tmp_dir/stderr" ]; then
116
136
  # Method 1: Try to extract filename from content-disposition header
117
137
  # Format: content-disposition: attachment; filename="chef-18.8.54-1.el9.x86_64.rpm"
118
138
  actual_filename=`grep -i 'content-disposition' $tmp_dir/stderr | sed -n 's/.*filename="\([^"]*\)".*/\1/p' | head -1`
119
139
 
120
140
  # Method 2: If content-disposition failed, try to extract from location redirect header
121
141
  # Format: location: https://packages.chef.io/files/stable/chef/18.8.54/el/9/chef-18.8.54-1.el9.x86_64.rpm?licenseId=...
122
- if test "x$actual_filename" = "x"; then
142
+ if [ -z "$actual_filename" ]; then
123
143
  actual_filename=`grep -i '^location:' $tmp_dir/stderr | head -1 | sed 's/.*\///' | sed 's/?.*//'`
124
144
  fi
125
145
 
126
146
  # Method 3: Try extracting from any URL-like pattern in stderr
127
- if test "x$actual_filename" = "x"; then
147
+ if [ -z "$actual_filename" ]; then
128
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`
129
149
  fi
130
150
  fi
131
151
 
132
152
  # If we still couldn't extract from headers, construct filename from metadata
133
- if test "x$actual_filename" = "x"; then
153
+ if [ -z "$actual_filename" ]; then
134
154
  echo "Warning: Could not extract filename from response headers, using fallback"
135
155
  # Construct a reasonable filename from available metadata
136
156
  # This is a fallback and may not match the exact package name
137
- if test "x$platform" = "xel" || test "x$platform" = "xfedora" || test "x$platform" = "xamazon"; then
157
+ if [ "$platform" = "el" ] || [ "$platform" = "fedora" ] || [ "$platform" = "amazon" ]; then
138
158
  actual_filename="chef-${version}-1.${platform}${platform_version}.${machine}.rpm"
139
- elif test "x$platform" = "xdebian" || test "x$platform" = "xubuntu"; then
159
+ elif [ "$platform" = "debian" ] || [ "$platform" = "ubuntu" ]; then
140
160
  actual_filename="chef_${version}-1_${machine}.deb"
141
- elif test "x$platform" = "xmac_os_x"; then
161
+ elif [ "$platform" = "mac_os_x" ]; then
142
162
  actual_filename="chef-${version}.dmg"
143
163
  else
144
164
  actual_filename="chef-${version}.pkg"
@@ -160,8 +180,12 @@ if test "x$cached_file_available" != "xtrue"; then
160
180
  fi
161
181
  fi
162
182
 
163
- if test "x$verify_checksum" = "xtrue"; then
164
- do_checksum "$download_filename" "$sha256" || checksum_mismatch
183
+ if [ "$verify_checksum" = "true" ]; then
184
+ if [ -z "$sha256" ]; then
185
+ echo "Skipping checksum verification - no checksum provided"
186
+ else
187
+ do_checksum "$download_filename" "$sha256" || checksum_mismatch
188
+ fi
165
189
  fi
166
190
 
167
191
  ############