Skip to content

Commit 9656239

Browse files
authored
modernize shell testing (#418)
* modernize shell testing Signed-off-by: Corey Hemminger <hemminger@hotmail.com>
1 parent 315c5b6 commit 9656239

34 files changed

+1995
-217
lines changed

.github/copilot-instructions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Mixlib::Install is a library for interacting with Chef Software Inc's software d
5454
- **Charset**: UTF-8
5555
- **Trailing Whitespace**: Remove (trim_trailing_whitespace: true)
5656
- **Final Newline**: Always include
57+
- **Emojis**: Never use emojis in any code, comments, output messages, test assertions, or documentation
5758

5859
### File Headers
5960
All Ruby files should include the Apache 2.0 license header:
@@ -356,6 +357,7 @@ When adding or modifying commercial/trial API functionality:
356357
1. **Don't add dependencies without version constraints** - Especially for Ruby 2.6+ support
357358
1. **Don't assume filename in URL** - Commercial/trial APIs use Content-Disposition headers
358359
1. **Don't break temp file download approach** - Required for license_id support across all download methods
360+
1. **Don't use emojis** - Never use emojis in code, comments, output messages, or documentation
359361

360362
## Documentation Requirements
361363

.github/workflows/integration.yml

Lines changed: 126 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ jobs:
4242
with:
4343
ruby-version: ${{ matrix.ruby }}
4444
bundler-cache: true
45+
- name: Update openssl on macOS
46+
if: runner.os == 'macOS'
47+
run: |
48+
brew update
49+
brew upgrade openssl@3 || true
4550
- name: Chef Omnibus download Test
4651
run: bundle exec mixlib-install download chef
4752
- name: Chef Licensed download Test
@@ -194,10 +199,10 @@ jobs:
194199
cat install_licensed.sh
195200
- name: Verify license_id is present in script
196201
run: |
197-
if grep -q "license_id='test-license-12345'" install_licensed.sh; then
198-
echo "License ID found pre-set in script"
202+
if grep -q 'license_id="test-license-12345"' install_licensed.sh; then
203+
echo "License ID found pre-set in script"
199204
else
200-
echo "License ID not found in script"
205+
echo "License ID not found in script"
201206
exit 1
202207
fi
203208
test-install-ps1-with-license:
@@ -227,9 +232,124 @@ jobs:
227232
- name: Verify license_id is present in script
228233
run: |
229234
$content = Get-Content install_licensed.ps1 -Raw
230-
if ($content -match "install -license_id 'test-license-12345'") {
231-
Write-Host "License ID found in install command"
235+
if ($content -match "license_id = 'test-license-12345'") {
236+
Write-Host "License ID found in install command"
232237
} else {
233-
Write-Host "License ID not found in script"
238+
Write-Host "License ID not found in script"
234239
exit 1
235240
}
241+
test-script-generator-unix:
242+
name: Test ScriptGenerator on Linux
243+
runs-on: ubuntu-latest
244+
steps:
245+
- name: Checkout code
246+
uses: actions/checkout@v6
247+
- name: Setup Ruby
248+
uses: ruby/setup-ruby@v1
249+
with:
250+
ruby-version: 3.4
251+
bundler-cache: true
252+
- name: Test ScriptGenerator without license_id
253+
run: |
254+
bundle exec ruby -e "
255+
require 'bundler/setup'
256+
require 'mixlib/install/script_generator'
257+
258+
# Generate script without license_id
259+
generator = Mixlib::Install::ScriptGenerator.new('latest', false, {
260+
omnibus_url: 'https://omnitruck.chef.io/install.sh'
261+
})
262+
script = generator.install_command
263+
264+
puts '=== Generated Script (no license) ==='
265+
puts script
266+
puts '==================================='
267+
268+
# Verify expected content
269+
raise 'Missing chef_omnibus_url' unless script.include?('chef_omnibus_url')
270+
raise 'Missing version' unless script.include?('version=')
271+
puts 'Script generated successfully without license_id'
272+
"
273+
- name: Test ScriptGenerator with license_id
274+
run: |
275+
bundle exec ruby -e "
276+
require 'bundler/setup'
277+
require 'mixlib/install/script_generator'
278+
279+
# Generate script with license_id
280+
generator = Mixlib::Install::ScriptGenerator.new('latest', false, {
281+
omnibus_url: 'https://omnitruck.chef.io/install.sh',
282+
license_id: 'test-license-unix-123'
283+
})
284+
script = generator.install_command
285+
286+
puts '=== Generated Script (with license) ==='
287+
puts script
288+
puts '========================================'
289+
290+
# Verify license_id is in install_flags
291+
raise 'Missing license_id in install_flags' unless script.include?('-l test-license-unix-123')
292+
puts 'License ID correctly added to install_flags'
293+
"
294+
test-script-generator-windows:
295+
name: Test ScriptGenerator on Windows
296+
runs-on: windows-latest
297+
steps:
298+
- name: Enable long paths on Windows
299+
run: |
300+
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force -ErrorAction SilentlyContinue
301+
git config --global core.longpaths true
302+
- name: Checkout code
303+
uses: actions/checkout@v6
304+
- name: Setup Ruby
305+
uses: ruby/setup-ruby@v1
306+
with:
307+
ruby-version: 3.4
308+
bundler-cache: true
309+
- name: Create test script without license_id
310+
run: |
311+
@"
312+
require 'bundler/setup'
313+
require 'mixlib/install/script_generator'
314+
315+
generator = Mixlib::Install::ScriptGenerator.new('latest', true, {
316+
omnibus_url: 'https://omnitruck.chef.io/install.sh'
317+
})
318+
319+
script = generator.install_command
320+
puts '=== Generated Script (no license) ==='
321+
puts script
322+
puts '==================================='
323+
324+
unless script.include?('chef_metadata_url') && script.include?('version')
325+
raise 'Missing required variables'
326+
end
327+
328+
puts 'Script generated successfully without license_id'
329+
"@ | Out-File -FilePath test_no_license.rb -Encoding utf8
330+
- name: Test ScriptGenerator without license_id
331+
run: bundle exec ruby test_no_license.rb
332+
- name: Create test script with license_id
333+
run: |
334+
@"
335+
require 'bundler/setup'
336+
require 'mixlib/install/script_generator'
337+
338+
generator = Mixlib::Install::ScriptGenerator.new('latest', true, {
339+
omnibus_url: 'https://omnitruck.chef.io/install.sh',
340+
license_id: 'test-license-win-456'
341+
})
342+
343+
script = generator.install_command
344+
puts '=== Generated Script (with license) ==='
345+
puts script
346+
puts '========================================'
347+
348+
unless script.include?('$license_id = "test-license-win-456"')
349+
raise 'Missing license_id variable'
350+
end
351+
352+
puts 'License ID correctly added as PowerShell variable'
353+
"@ | Out-File -FilePath test_with_license.rb -Encoding utf8
354+
- name: Test ScriptGenerator with license_id
355+
run: bundle exec ruby test_with_license.rb

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,4 +452,4 @@
452452

453453
## [1.0.0]
454454
- Ability to query product artifacts from multiple channels
455-
- Ability to generate installation scripts for `sh` and `ps1`
455+
- Ability to generate installation scripts for `sh` and `ps1`

Gemfile

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,6 @@ source "https://rubygems.org"
33
gemspec
44

55
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
206

217
group :test do
228
gem "contracts", "~> 0.16.0" # this entry can go away when ruby < 3 support is gone

lib/mixlib/install.rb

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,9 @@ def root
159159
# This only works for chef and chefdk but they are the only projects
160160
# we are supporting as of now.
161161
if options.for_ps1?
162-
"$env:systemdrive\\#{Mixlib::Install::Dist::WINDOWS_INSTALL_DIR}\\#{options.product_name}"
162+
"$env:systemdrive\\#{Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR}\\#{options.product_name}"
163163
else
164-
"/opt/#{options.product_name}"
164+
"#{Mixlib::Install::Dist::OMNIBUS_LINUX_INSTALL_DIR}/#{options.product_name}"
165165
end
166166
end
167167

@@ -176,9 +176,9 @@ def current_version
176176
# chef-server -> /opt/opscode). But this is OK for now since
177177
# chef & chefdk are the only supported products.
178178
version_manifest_file = if options.for_ps1?
179-
"$env:systemdrive\\#{Mixlib::Install::Dist::WINDOWS_INSTALL_DIR}\\#{options.product_name}\\version-manifest.json"
179+
"$env:systemdrive\\#{Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR}\\#{options.product_name}\\version-manifest.json"
180180
else
181-
"/opt/#{options.product_name}/version-manifest.json"
181+
"#{Mixlib::Install::Dist::OMNIBUS_LINUX_INSTALL_DIR}/#{options.product_name}/version-manifest.json"
182182
end
183183

184184
if File.exist? version_manifest_file
@@ -258,8 +258,25 @@ def self.detect_platform_ps1
258258
# ------------------
259259
# base_url [String]
260260
# url pointing to the omnitruck to be queried by the script.
261+
# license_id [String]
262+
# license ID for commercial or trial API access.
263+
# If license_id starts with 'free-' or 'trial-', trial API defaults are enforced.
261264
#
262265
def self.install_sh(context = {})
266+
# Apply trial API defaults if license_id indicates trial
267+
if context[:license_id] && Mixlib::Install::Dist.trial_license?(context[:license_id])
268+
# Warn and override if non-compliant values provided
269+
if context[:channel] && context[:channel].to_s != "stable"
270+
warn "WARNING: Trial API only supports 'stable' channel. Changing from '#{context[:channel]}' to 'stable'."
271+
context[:channel] = "stable"
272+
end
273+
274+
if context[:version] && !["latest", nil].include?(context[:version].to_s)
275+
warn "WARNING: Trial API only supports 'latest' version. Changing from '#{context[:version]}' to 'latest'."
276+
context[:version] = "latest"
277+
end
278+
end
279+
263280
Mixlib::Install::Generator::Bourne.install_sh(context)
264281
end
265282

@@ -269,8 +286,25 @@ def self.install_sh(context = {})
269286
# ------------------
270287
# base_url [String]
271288
# url pointing to the omnitruck to be queried by the script.
289+
# license_id [String]
290+
# license ID for commercial or trial API access.
291+
# If license_id starts with 'free-' or 'trial-', trial API defaults are enforced.
272292
#
273293
def self.install_ps1(context = {})
294+
# Apply trial API defaults if license_id indicates trial
295+
if context[:license_id] && Mixlib::Install::Dist.trial_license?(context[:license_id])
296+
# Warn and override if non-compliant values provided
297+
if context[:channel] && context[:channel].to_s != "stable"
298+
warn "WARNING: Trial API only supports 'stable' channel. Changing from '#{context[:channel]}' to 'stable'."
299+
context[:channel] = "stable"
300+
end
301+
302+
if context[:version] && !["latest", nil].include?(context[:version].to_s)
303+
warn "WARNING: Trial API only supports 'latest' version. Changing from '#{context[:version]}' to 'latest'."
304+
context[:version] = "latest"
305+
end
306+
end
307+
274308
Mixlib::Install::Generator::PowerShell.install_ps1(context)
275309
end
276310
end

lib/mixlib/install/dist.rb

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,30 @@ class Dist
2626
RESOURCES_URL = "https://www.chef.io/support".freeze
2727
# MacOS volume name
2828
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
3353
end
3454
end
3555
end

lib/mixlib/install/generator.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ module Mixlib
2222
class Install
2323
class Generator
2424
def self.install_command(options)
25-
klass = options.for_ps1? ? PowerShell : Bourne
26-
meth = options.options[:new_omnibus_download_url] ? :install_sh_from_upstream : :install_command
27-
28-
klass.new(options).send(meth)
25+
if options.for_ps1?
26+
PowerShell.new(options).install_command
27+
else
28+
Bourne.new(options).install_command
29+
end
2930
end
3031
end
3132
end

lib/mixlib/install/generator/base.rb

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
require "ostruct" unless defined?(OpenStruct)
2020
require_relative "../util"
2121
require_relative "../dist"
22-
require "net/http" unless defined?(Net::HTTP)
2322

2423
module Mixlib
2524
class Install
@@ -50,13 +49,21 @@ def self.get_script(name, context = {})
5049
if File.exist? "#{script_path}.erb"
5150
# Default values to use incase they are not set in the context
5251
context[:project_name] ||= Mixlib::Install::Dist::PROJECT_NAME.freeze
53-
context[:base_url] ||= Mixlib::Install::Dist::OMNITRUCK_ENDPOINT.freeze
52+
context[:base_url] ||= if context[:license_id]
53+
if Mixlib::Install::Dist.trial_license?(context[:license_id])
54+
Mixlib::Install::Dist::TRIAL_API_ENDPOINT.freeze
55+
else
56+
Mixlib::Install::Dist::COMMERCIAL_API_ENDPOINT.freeze
57+
end
58+
else
59+
Mixlib::Install::Dist::OMNITRUCK_ENDPOINT.freeze
60+
end
5461
context[:default_product] ||= Mixlib::Install::Dist::DEFAULT_PRODUCT.freeze
5562
context[:bug_url] ||= Mixlib::Install::Dist::BUG_URL.freeze
5663
context[:support_url] ||= Mixlib::Install::Dist::SUPPORT_URL.freeze
5764
context[:resources_url] ||= Mixlib::Install::Dist::RESOURCES_URL.freeze
5865
context[:macos_dir] ||= Mixlib::Install::Dist::MACOS_VOLUME.freeze
59-
context[:windows_dir] ||= Mixlib::Install::Dist::WINDOWS_INSTALL_DIR.freeze
66+
context[:windows_dir] ||= Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR.freeze
6067
context[:user_agent_string] = Util.user_agent_string(context[:user_agent_headers])
6168

6269
context_object = OpenStruct.new(context).instance_eval { binding }
@@ -69,17 +76,6 @@ def self.get_script(name, context = {})
6976
def get_script(name, context = {})
7077
self.class.get_script(name, context)
7178
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
8379
end
8480
end
8581
end

0 commit comments

Comments
 (0)