From d73bee4f7a3c9b04fd8bf0ab87ce596c93645dca Mon Sep 17 00:00:00 2001 From: Corey Hemminger Date: Mon, 13 Jul 2026 19:57:07 -0500 Subject: [PATCH 1/6] feat: distinguish between omnibus and habitat based products Signed-off-by: Corey Hemminger --- lib/mixlib/install/product.rb | 34 ++++++++-- lib/mixlib/install/product_matrix.rb | 7 +- lib/mixlib/install/script_generator.rb | 18 ++--- spec/functional/mixlib/install/cli_spec.rb | 2 +- .../mixlib/install/product_matrix_spec.rb | 50 +++++++++----- spec/unit/mixlib/install/product_spec.rb | 67 +++++++++++++++++++ .../mixlib/install/script_generator_spec.rb | 17 +++++ 7 files changed, 165 insertions(+), 30 deletions(-) diff --git a/lib/mixlib/install/product.rb b/lib/mixlib/install/product.rb index a289caed..9fd17e3c 100644 --- a/lib/mixlib/install/product.rb +++ b/lib/mixlib/install/product.rb @@ -37,6 +37,10 @@ def initialize(key, &block) :github_repo, :downloads_product_page_url, :api_url, + :distribution_type, + :hab_origin, + :hab_builder_url, + :hab_package_name ] # @@ -80,20 +84,42 @@ def initialize(key, &block) def default_value_for(prop) case prop when :install_path - "/opt/#{package_name}" + if distribution_type == 'habitat' + "/hab/pkgs/#{hab_origin}/#{hab_package_name}" + else + "/opt/#{package_name}" + end when :omnibus_project package_name when :downloads_product_page_url - "#{Mixlib::Install::Dist::DOWNLOADS_PAGE}/#{product_key}" + "#{Mixlib::Install::Dist::DOWNLOADS_PAGE}" when :github_repo "#{Mixlib::Install::Dist::GITHUB_ORG}/#{product_key}" when :api_url ENV.fetch("PACKAGE_ROUTER_ENDPOINT", Mixlib::Install::Dist::PRODUCT_ENDPOINT) - else - nil + when :distribution_type + 'omnibus' + when :hab_origin + 'chef' + when :hab_builder_url + 'https://bldr.habitat.sh' + when :hab_package_name + package_name end end + def habitat? + distribution_type == 'habitat' + end + + def omnibus? + distribution_type == 'omnibus' + end + + def hab_package_url + "#{hab_builder_url}/#/pkgs/#{hab_origin}/#{hab_package_name}/latest" + end + # # Return all known omnibus project names for a product # diff --git a/lib/mixlib/install/product_matrix.rb b/lib/mixlib/install/product_matrix.rb index 69ad8cea..fbb18f07 100644 --- a/lib/mixlib/install/product_matrix.rb +++ b/lib/mixlib/install/product_matrix.rb @@ -50,6 +50,8 @@ product "chef-ice" do product_name "Chef Infra Client Enterprise" package_name "chef-ice" + distribution_type 'habitat' + hab_package_name 'chef-infra-client' end product "chef-foundation" do @@ -111,7 +113,8 @@ product "chef-workstation-enterprise" do product_name "Chef Workstation Enterprise" package_name "chef-workstation-enterprise" - github_repo "chef/chef-workstation" + distribution_type 'habitat' + hab_package_name 'chef-workstation' end product "chefdk" do @@ -164,6 +167,8 @@ product "inspec-enterprise" do product_name "Chef InSpec Enterprise" package_name "inspec-enterprise" + distribution_type 'habitat' + hab_package_name 'inspec' end product "mac-bootstrapper" do diff --git a/lib/mixlib/install/script_generator.rb b/lib/mixlib/install/script_generator.rb index f1757a90..83c1e3be 100644 --- a/lib/mixlib/install/script_generator.rb +++ b/lib/mixlib/install/script_generator.rb @@ -20,6 +20,7 @@ require_relative "util" require_relative "generator/powershell" require_relative "dist" +require_relative "product_matrix" require "cgi" module Mixlib @@ -99,14 +100,15 @@ def initialize(version, powershell = false, opts = {}) parse_opts(opts) - # Update root for chef-ice to use Habitat install directories - if @project&.casecmp("chef-ice") == 0 - @root = if powershell - "$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\chef\\chef-infra-client\\*\\*" - else - "#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/chef/chef-infra-client/*/*" - end - end + # Update root based on product distribution type (habitat vs omnibus) + product = PRODUCT_MATRIX.lookup(@project) if @project + return unless product&.habitat? + + @root = if powershell + "$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\#{product.hab_origin}\\#{product.hab_package_name}\\*\\*" + else + "#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/#{product.hab_origin}/#{product.hab_package_name}/*/*" + end end def install_command diff --git a/spec/functional/mixlib/install/cli_spec.rb b/spec/functional/mixlib/install/cli_spec.rb index 60acd6de..c65aa670 100644 --- a/spec/functional/mixlib/install/cli_spec.rb +++ b/spec/functional/mixlib/install/cli_spec.rb @@ -111,7 +111,7 @@ end end - context "with output option", :focus do + context "with output option" do let(:args) { "-o #{File.join(test_temp_dir, 'script.sh')}" } it "writes to a file" do diff --git a/spec/unit/mixlib/install/product_matrix_spec.rb b/spec/unit/mixlib/install/product_matrix_spec.rb index 1bc948c6..ad4167f1 100644 --- a/spec/unit/mixlib/install/product_matrix_spec.rb +++ b/spec/unit/mixlib/install/product_matrix_spec.rb @@ -155,22 +155,6 @@ end end - context "for chef-workstation-enterprise product" do - let(:chef_workstation_enterprise_product) { PRODUCT_MATRIX.lookup("chef-workstation-enterprise") } - - it "exists in the product matrix" do - expect(chef_workstation_enterprise_product).not_to be_nil - end - - it "has correct product_name" do - expect(chef_workstation_enterprise_product.product_name).to eq("Chef Workstation Enterprise") - end - - it "has correct package_name" do - expect(chef_workstation_enterprise_product.package_name).to eq("chef-workstation-enterprise") - end - end - context "for chef-server product" do let(:chef_server_product) { PRODUCT_MATRIX.lookup("chef-server") } @@ -256,4 +240,38 @@ end end end + + describe "habitat product distribution type" do + it "chef-ice has distribution_type habitat" do + expect(PRODUCT_MATRIX.lookup("chef-ice").distribution_type).to eq("habitat") + end + + it "chef-ice has hab_package_name chef-infra-client" do + expect(PRODUCT_MATRIX.lookup("chef-ice").hab_package_name).to eq("chef-infra-client") + end + + it "chef-workstation-enterprise has distribution_type habitat" do + expect(PRODUCT_MATRIX.lookup("chef-workstation-enterprise").distribution_type).to eq("habitat") + end + + it "chef-workstation-enterprise has hab_package_name chef-workstation" do + expect(PRODUCT_MATRIX.lookup("chef-workstation-enterprise").hab_package_name).to eq("chef-workstation") + end + + it "inspec-enterprise has distribution_type habitat" do + expect(PRODUCT_MATRIX.lookup("inspec-enterprise").distribution_type).to eq("habitat") + end + + it "inspec-enterprise has hab_package_name inspec" do + expect(PRODUCT_MATRIX.lookup("inspec-enterprise").hab_package_name).to eq("inspec") + end + + it "chef product has distribution_type omnibus" do + expect(PRODUCT_MATRIX.lookup("chef").distribution_type).to eq("omnibus") + end + + it "chef product omnibus? returns true" do + expect(PRODUCT_MATRIX.lookup("chef").omnibus?).to be true + end + end end diff --git a/spec/unit/mixlib/install/product_spec.rb b/spec/unit/mixlib/install/product_spec.rb index 945af4f2..ecbd6a57 100644 --- a/spec/unit/mixlib/install/product_spec.rb +++ b/spec/unit/mixlib/install/product_spec.rb @@ -447,4 +447,71 @@ it_behaves_like "automate and delivery products" end + + context "habitat product support" do + let(:omnibus_product) do + Mixlib::Install::Product.new("chef") do + product_name "Chef Infra Client" + package_name "chef" + end + end + + let(:habitat_product) do + Mixlib::Install::Product.new("chef-ice") do + product_name "Chef Infra Client Enterprise" + package_name "chef-ice" + distribution_type "habitat" + hab_package_name "chef-infra-client" + end + end + + it "distribution_type defaults to omnibus" do + expect(omnibus_product.distribution_type).to eq("omnibus") + end + + it "hab_origin defaults to chef" do + expect(omnibus_product.hab_origin).to eq("chef") + end + + it "hab_builder_url defaults to https://bldr.habitat.sh" do + expect(omnibus_product.hab_builder_url).to eq("https://bldr.habitat.sh") + end + + it "hab_package_name defaults to package_name" do + expect(omnibus_product.hab_package_name).to eq("chef") + end + + it "habitat? returns false for omnibus product" do + expect(omnibus_product.habitat?).to be false + end + + it "habitat? returns true for habitat product" do + expect(habitat_product.habitat?).to be true + end + + it "omnibus? returns true for omnibus product" do + expect(omnibus_product.omnibus?).to be true + end + + it "omnibus? returns false for habitat product" do + expect(habitat_product.omnibus?).to be false + end + + it "install_path for omnibus product is /opt/" do + expect(omnibus_product.install_path).to eq("/opt/chef") + end + + it "install_path for habitat product is /hab/pkgs/chef/" do + expect(habitat_product.install_path).to eq("/hab/pkgs/chef/chef-infra-client") + end + + it "hab_package_url returns correct URL" do + expect(habitat_product.hab_package_url).to eq("https://bldr.habitat.sh/#/pkgs/chef/chef-infra-client/latest") + end + + it "hab_package_name explicit override works when product key differs from package name" do + expect(habitat_product.hab_package_name).to eq("chef-infra-client") + expect(habitat_product.package_name).to eq("chef-ice") + end + end end diff --git a/spec/unit/mixlib/install/script_generator_spec.rb b/spec/unit/mixlib/install/script_generator_spec.rb index 55dc1388..014756cd 100644 --- a/spec/unit/mixlib/install/script_generator_spec.rb +++ b/spec/unit/mixlib/install/script_generator_spec.rb @@ -64,6 +64,23 @@ expect(install.root).to eq("/hab/pkgs/chef/chef-infra-client/*/*") end end + + describe "habitat product root uses metadata-driven paths" do + it "chef-ice unix root uses hab_origin and hab_package_name from product metadata" do + install = described_class.new("1.2.1", false, project: "chef-ice") + expect(install.root).to include("/hab/pkgs/chef/chef-infra-client") + end + + it "chef-ice windows root uses hab_origin and hab_package_name from product metadata" do + install = described_class.new("1.2.1", true, project: "chef-ice") + expect(install.root).to match(/\A\$env:systemdrive.*hab.pkgs.chef.chef-infra-client/i) + end + + it "non-habitat product uses omnibus path" do + install = described_class.new("1.2.1", false, project: "chef") + expect(install.root).to eq("/opt/chef") + end + end end describe "parses the options hash" do From 3c89ffc67c1dd3913a450b6412784bdb176a3d74 Mon Sep 17 00:00:00 2001 From: Corey Hemminger Date: Mon, 13 Jul 2026 19:56:17 -0500 Subject: [PATCH 2/6] feat: add chef-workstation-enterprise product to product matrix Signed-off-by: Corey Hemminger --- lib/mixlib/install/product_matrix.rb | 6 ++++++ spec/unit/mixlib/install/product_matrix_spec.rb | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/mixlib/install/product_matrix.rb b/lib/mixlib/install/product_matrix.rb index 73fd16c8..69ad8cea 100644 --- a/lib/mixlib/install/product_matrix.rb +++ b/lib/mixlib/install/product_matrix.rb @@ -108,6 +108,12 @@ github_repo "chef/chef-workstation" end + product "chef-workstation-enterprise" do + product_name "Chef Workstation Enterprise" + package_name "chef-workstation-enterprise" + github_repo "chef/chef-workstation" + end + product "chefdk" do product_name "Chef Development Kit" package_name "chefdk" diff --git a/spec/unit/mixlib/install/product_matrix_spec.rb b/spec/unit/mixlib/install/product_matrix_spec.rb index 785c2843..1bc948c6 100644 --- a/spec/unit/mixlib/install/product_matrix_spec.rb +++ b/spec/unit/mixlib/install/product_matrix_spec.rb @@ -155,6 +155,22 @@ end end + context "for chef-workstation-enterprise product" do + let(:chef_workstation_enterprise_product) { PRODUCT_MATRIX.lookup("chef-workstation-enterprise") } + + it "exists in the product matrix" do + expect(chef_workstation_enterprise_product).not_to be_nil + end + + it "has correct product_name" do + expect(chef_workstation_enterprise_product.product_name).to eq("Chef Workstation Enterprise") + end + + it "has correct package_name" do + expect(chef_workstation_enterprise_product.package_name).to eq("chef-workstation-enterprise") + end + end + context "for chef-server product" do let(:chef_server_product) { PRODUCT_MATRIX.lookup("chef-server") } From 5430a806a1675a91ed4194a063b62142c8faefbd Mon Sep 17 00:00:00 2001 From: Corey Hemminger Date: Mon, 13 Jul 2026 20:16:52 -0500 Subject: [PATCH 3/6] update product-matrix.md Signed-off-by: Corey Hemminger --- PRODUCT_MATRIX.md | 1 + lib/mixlib/install/product_matrix.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/PRODUCT_MATRIX.md b/PRODUCT_MATRIX.md index d64f9175..499b9a45 100644 --- a/PRODUCT_MATRIX.md +++ b/PRODUCT_MATRIX.md @@ -12,6 +12,7 @@ | Chef Infra Server HA Provisioning for AWS | chef-server-ha-provisioning | | Chef Infra Client MacOS Universal | chef-universal | | Chef Workstation | chef-workstation | +| Chef Workstation Enterprise | chef-workstation-enterprise | | Chef Development Kit | chefdk | | Chef Compliance | compliance | | Delivery | delivery | diff --git a/lib/mixlib/install/product_matrix.rb b/lib/mixlib/install/product_matrix.rb index 69ad8cea..cb142728 100644 --- a/lib/mixlib/install/product_matrix.rb +++ b/lib/mixlib/install/product_matrix.rb @@ -111,7 +111,7 @@ product "chef-workstation-enterprise" do product_name "Chef Workstation Enterprise" package_name "chef-workstation-enterprise" - github_repo "chef/chef-workstation" + github_repo "chef/chef-workstation-enterprise" end product "chefdk" do From 9eccc9301fee40cab35ac6816c78502f08bebc9f Mon Sep 17 00:00:00 2001 From: Corey Hemminger Date: Tue, 14 Jul 2026 17:09:04 -0500 Subject: [PATCH 4/6] chefstyle lint fixes Signed-off-by: Corey Hemminger --- lib/mixlib/install/product.rb | 12 ++++++------ lib/mixlib/install/product_matrix.rb | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/mixlib/install/product.rb b/lib/mixlib/install/product.rb index 9fd17e3c..28552a83 100644 --- a/lib/mixlib/install/product.rb +++ b/lib/mixlib/install/product.rb @@ -84,7 +84,7 @@ def initialize(key, &block) def default_value_for(prop) case prop when :install_path - if distribution_type == 'habitat' + if distribution_type == "habitat" "/hab/pkgs/#{hab_origin}/#{hab_package_name}" else "/opt/#{package_name}" @@ -98,22 +98,22 @@ def default_value_for(prop) when :api_url ENV.fetch("PACKAGE_ROUTER_ENDPOINT", Mixlib::Install::Dist::PRODUCT_ENDPOINT) when :distribution_type - 'omnibus' + "omnibus" when :hab_origin - 'chef' + "chef" when :hab_builder_url - 'https://bldr.habitat.sh' + "https://bldr.habitat.sh" when :hab_package_name package_name end end def habitat? - distribution_type == 'habitat' + distribution_type == "habitat" end def omnibus? - distribution_type == 'omnibus' + distribution_type == "omnibus" end def hab_package_url diff --git a/lib/mixlib/install/product_matrix.rb b/lib/mixlib/install/product_matrix.rb index fbb18f07..4bdfabbd 100644 --- a/lib/mixlib/install/product_matrix.rb +++ b/lib/mixlib/install/product_matrix.rb @@ -50,8 +50,8 @@ product "chef-ice" do product_name "Chef Infra Client Enterprise" package_name "chef-ice" - distribution_type 'habitat' - hab_package_name 'chef-infra-client' + distribution_type "habitat" + hab_package_name "chef-infra-client" end product "chef-foundation" do @@ -113,8 +113,8 @@ product "chef-workstation-enterprise" do product_name "Chef Workstation Enterprise" package_name "chef-workstation-enterprise" - distribution_type 'habitat' - hab_package_name 'chef-workstation' + distribution_type "habitat" + hab_package_name "chef-workstation" end product "chefdk" do @@ -167,8 +167,8 @@ product "inspec-enterprise" do product_name "Chef InSpec Enterprise" package_name "inspec-enterprise" - distribution_type 'habitat' - hab_package_name 'inspec' + distribution_type "habitat" + hab_package_name "inspec" end product "mac-bootstrapper" do From 9b5a7c7b5a596df5db7955e47353b7c22fa09bd2 Mon Sep 17 00:00:00 2001 From: Corey Hemminger Date: Tue, 14 Jul 2026 17:39:56 -0500 Subject: [PATCH 5/6] refactor: replace chef-ice path conditionals with product.habitat? metadata lookup Signed-off-by: Corey Hemminger --- lib/mixlib/install.rb | 26 ++++++++----------- lib/mixlib/install/generator/base.rb | 7 ++++- .../scripts/install_project.ps1.erb | 13 ++++++---- .../mixlib/install/generator/base_spec.rb | 16 ++++++++++++ spec/unit/mixlib/install_spec.rb | 22 ++++++++++++++++ 5 files changed, 63 insertions(+), 21 deletions(-) diff --git a/lib/mixlib/install.rb b/lib/mixlib/install.rb index 048cb797..1e8934f8 100644 --- a/lib/mixlib/install.rb +++ b/lib/mixlib/install.rb @@ -26,6 +26,7 @@ require_relative "install/generator/bourne" require_relative "install/generator/powershell" require_relative "install/dist" +require_relative "install/product_matrix" module Mixlib class Install @@ -177,16 +178,15 @@ def download_artifact(directory = Dir.pwd) # Returns the base installation directory for the given options # # @return [String] the installation directory for the project + # habitat products use Habitat install directories # def root - # This only works for chef and chefdk but they are the only projects - # we are supporting as of now. - # chef-ice uses Habitat install directories - if options.product_name.casecmp("chef-ice") == 0 + product = PRODUCT_MATRIX.lookup(options.product_name) if options.product_name + if product&.habitat? if options.for_ps1? - "$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\chef\\chef-infra-client\\*\\*" + "$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\#{product.hab_origin}\\#{product.hab_package_name}\\*\\*" else - "#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/chef/chef-infra-client/*/*" + "#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/#{product.hab_origin}/#{product.hab_package_name}/*/*" end else if options.for_ps1? @@ -200,19 +200,15 @@ def root # # Returns the current version of the installed product. # Returns nil if the product is not installed. + # habitat products use Habitat install directories # def current_version - # Note that this logic does not work for products other than - # chef & chefdk since version-manifest is created under the - # install directory which can be different than the product name (e.g. - # chef-server -> /opt/opscode). But this is OK for now since - # chef & chefdk are the only supported products. - # chef-ice uses Habitat install directories - version_manifest_file = if options.product_name.casecmp("chef-ice") == 0 + product = PRODUCT_MATRIX.lookup(options.product_name) if options.product_name + version_manifest_file = if product&.habitat? if options.for_ps1? - "$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\chef\\chef-infra-client\\*\\*\\version-manifest.json" + "$env:systemdrive\\#{Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR}\\#{product.hab_origin}\\#{product.hab_package_name}\\*\\*\\version-manifest.json" else - "#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/chef/chef-infra-client/*/*/version-manifest.json" + "#{Mixlib::Install::Dist::HABITAT_LINUX_INSTALL_DIR}/#{product.hab_origin}/#{product.hab_package_name}/*/*/version-manifest.json" end else if options.for_ps1? diff --git a/lib/mixlib/install/generator/base.rb b/lib/mixlib/install/generator/base.rb index 48d6a761..ddf33db5 100644 --- a/lib/mixlib/install/generator/base.rb +++ b/lib/mixlib/install/generator/base.rb @@ -19,6 +19,7 @@ require "ostruct" unless defined?(OpenStruct) require_relative "../util" require_relative "../dist" +require_relative "../product_matrix" module Mixlib class Install @@ -54,7 +55,11 @@ def self.get_script(name, context = {}) context[:support_url] ||= Mixlib::Install::Dist::SUPPORT_URL.freeze context[:resources_url] ||= Mixlib::Install::Dist::RESOURCES_URL.freeze context[:macos_dir] ||= Mixlib::Install::Dist::MACOS_VOLUME.freeze - 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 + hab_product = PRODUCT_MATRIX.lookup(context[:default_product]) if context[:default_product] + context[:windows_dir] ||= hab_product&.habitat? ? Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR.freeze : Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR.freeze + context[:is_habitat] = hab_product&.habitat? || false + context[:hab_origin] = hab_product&.hab_origin || '' + context[:hab_package_name] = hab_product&.hab_package_name || '' context[:user_agent_string] = Util.user_agent_string(context[:user_agent_headers]) context_object = OpenStruct.new(context).instance_eval { binding } diff --git a/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb b/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb index 739bcee7..4df30b98 100644 --- a/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb +++ b/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb @@ -74,19 +74,22 @@ function Install-Project { } # Check for product installation in various locations - if ($project -eq 'chef' -or $project -eq 'chef-ice') { - # For chef or chef-ice, look for chef-infra-client paths +<% if is_habitat %> + $install_locations = @( + "$env:systemdrive\hab\pkgs\<%= hab_origin %>\<%= hab_package_name %>\*\*\bin", + "$env:systemdrive\<%= windows_dir %>\<%= hab_origin %>\bin" + ) +<% else %> + if ($project -eq 'chef') { $install_locations = @( - "$env:systemdrive\hab\pkgs\chef\chef-infra-client\*\*\bin", "$env:systemdrive\<%= windows_dir %>\chef\bin" ) } else { - # For other products, look for product-specific paths $install_locations = @( - "$env:systemdrive\hab\pkgs\chef\$project\*\*\bin", "$env:systemdrive\<%= windows_dir %>\$project\bin" ) } +<% end %> foreach ($path in $install_locations) { $resolved = Get-Item $path -ErrorAction SilentlyContinue | Select-Object -First 1 if ($resolved -and (Test-Path $resolved.FullName -PathType Container) -and ($install_strategy -eq 'once')) { diff --git a/spec/unit/mixlib/install/generator/base_spec.rb b/spec/unit/mixlib/install/generator/base_spec.rb index 2c434353..28b1cc36 100644 --- a/spec/unit/mixlib/install/generator/base_spec.rb +++ b/spec/unit/mixlib/install/generator/base_spec.rb @@ -176,6 +176,22 @@ def self.script_base_path expect(script).to include("dir=hab\\pkgs") end + it "uses habitat directory for chef-workstation-enterprise" do + context = { default_product: "chef-workstation-enterprise" } + script = test_generator_class.get_script("windows_dir.sh", context) + + expect(script).to include("dir=hab\\pkgs") + end + + it "sets is_habitat, hab_origin, hab_package_name for habitat product" do + context = { default_product: "chef-ice" } + test_generator_class.get_script("windows_dir.sh", context) + + expect(context[:is_habitat]).to eq(true) + expect(context[:hab_origin]).to eq("chef") + expect(context[:hab_package_name]).to eq("chef-infra-client") + end + it "uses omnibus directory for chef" do context = { default_product: "chef" } script = test_generator_class.get_script("windows_dir.sh", context) diff --git a/spec/unit/mixlib/install_spec.rb b/spec/unit/mixlib/install_spec.rb index 6247dffb..6e478c1c 100644 --- a/spec/unit/mixlib/install_spec.rb +++ b/spec/unit/mixlib/install_spec.rb @@ -97,6 +97,28 @@ end end end + + context "with chef-workstation-enterprise product" do + let(:product_name) { "chef-workstation-enterprise" } + let(:version_manifest_file) { "/hab/pkgs/chef/chef-workstation/*/*/version-manifest.json" } + + it "should use Habitat install directory path" do + expect(installer.root).to eq("/hab/pkgs/chef/chef-workstation/*/*") + end + + context "when chef-workstation-enterprise is installed" do + before do + expect(File).to receive(:exist?).with(version_manifest_file).and_return(true) + expect(File).to receive(:read).with(version_manifest_file).and_wrap_original do |m, path| + m.call(File.join(VERSION_MANIFEST_DIR, "/opt/chef/version-manifest.json")) + end + end + + it "should report version correctly" do + expect(installer.current_version).to eq("12.4.3") + end + end + end end context "checking for upgrades", :vcr do From 1fbd3edfcd66b44f93afaef4e462c3586e49cf33 Mon Sep 17 00:00:00 2001 From: Corey Hemminger Date: Tue, 14 Jul 2026 18:17:38 -0500 Subject: [PATCH 6/6] additional hab pkg updates Signed-off-by: Corey Hemminger --- lib/mixlib/install/generator/base.rb | 15 ++-- lib/mixlib/install/generator/powershell.rb | 2 +- .../scripts/install_project.ps1.erb | 25 ++++--- .../mixlib/install/generator/base_spec.rb | 34 +++------ spec/unit/mixlib/install/generator_spec.rb | 71 +++++++++++++++++++ 5 files changed, 106 insertions(+), 41 deletions(-) diff --git a/lib/mixlib/install/generator/base.rb b/lib/mixlib/install/generator/base.rb index ddf33db5..76fccf3d 100644 --- a/lib/mixlib/install/generator/base.rb +++ b/lib/mixlib/install/generator/base.rb @@ -55,11 +55,16 @@ def self.get_script(name, context = {}) context[:support_url] ||= Mixlib::Install::Dist::SUPPORT_URL.freeze context[:resources_url] ||= Mixlib::Install::Dist::RESOURCES_URL.freeze context[:macos_dir] ||= Mixlib::Install::Dist::MACOS_VOLUME.freeze - hab_product = PRODUCT_MATRIX.lookup(context[:default_product]) if context[:default_product] - context[:windows_dir] ||= hab_product&.habitat? ? Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR.freeze : Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR.freeze - context[:is_habitat] = hab_product&.habitat? || false - context[:hab_origin] = hab_product&.hab_origin || '' - context[:hab_package_name] = hab_product&.hab_package_name || '' + context[:habitat_products] ||= begin + PRODUCT_MATRIX.products.each_with_object({}) do |pname, h| + p = PRODUCT_MATRIX.lookup(pname) + h[pname] = { origin: p.hab_origin, package_name: p.hab_package_name } if p.habitat? + end + end + context[:habitat_windows_dir] ||= + Mixlib::Install::Dist::HABITAT_WINDOWS_INSTALL_DIR.freeze + context[:omnibus_windows_dir] ||= + Mixlib::Install::Dist::OMNIBUS_WINDOWS_INSTALL_DIR.freeze context[:user_agent_string] = Util.user_agent_string(context[:user_agent_headers]) context_object = OpenStruct.new(context).instance_eval { binding } diff --git a/lib/mixlib/install/generator/powershell.rb b/lib/mixlib/install/generator/powershell.rb index 5db4d52c..6172d6cf 100644 --- a/lib/mixlib/install/generator/powershell.rb +++ b/lib/mixlib/install/generator/powershell.rb @@ -47,7 +47,7 @@ def install_command install_project_module = [] install_project_module << get_script("helpers.ps1", user_agent_headers: options.user_agent_headers) install_project_module << get_script("get_project_metadata.ps1", license_id: options.license_id, base_url: options.base_url) - install_project_module << get_script("install_project.ps1", license_id: options.license_id) + install_project_module << get_script("install_project.ps1", license_id: options.license_id, default_product: options.product_name) install_command = [] install_command << ps1_modularize(install_project_module.join("\n"), "Installer-Module") install_command << render_command diff --git a/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb b/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb index 4df30b98..6739a3a6 100644 --- a/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb +++ b/lib/mixlib/install/generator/powershell/scripts/install_project.ps1.erb @@ -73,23 +73,25 @@ function Install-Project { $license_id = $env:CHEF_LICENSE_KEY } + # Habitat products mapped to their Habitat Builder origin/package names + $habitat_products = @{ +<% habitat_products.each do |pname, meta| %> + '<%= pname %>' = @{ origin = '<%= meta[:origin] %>'; package_name = '<%= meta[:package_name] %>' } +<% end %> + } + # Check for product installation in various locations -<% if is_habitat %> - $install_locations = @( - "$env:systemdrive\hab\pkgs\<%= hab_origin %>\<%= hab_package_name %>\*\*\bin", - "$env:systemdrive\<%= windows_dir %>\<%= hab_origin %>\bin" - ) -<% else %> - if ($project -eq 'chef') { + if ($habitat_products.ContainsKey($project)) { + $hab_meta = $habitat_products[$project] $install_locations = @( - "$env:systemdrive\<%= windows_dir %>\chef\bin" + "$env:systemdrive\hab\pkgs\$($hab_meta.origin)\$($hab_meta.package_name)\*\*\bin", + "$env:systemdrive\<%= habitat_windows_dir %>\$($hab_meta.origin)\bin" ) } else { $install_locations = @( - "$env:systemdrive\<%= windows_dir %>\$project\bin" + "$env:systemdrive\<%= omnibus_windows_dir %>\$project\bin" ) } -<% end %> foreach ($path in $install_locations) { $resolved = Get-Item $path -ErrorAction SilentlyContinue | Select-Object -First 1 if ($resolved -and (Test-Path $resolved.FullName -PathType Container) -and ($install_strategy -eq 'once')) { @@ -256,7 +258,8 @@ Function Install-ChefMsi($msi, $addlocal) { Function Install-ChefAppx($appx, $project) { Add-AppxPackage -Path $appx -ErrorAction Stop $package = (Get-AppxPackage -Name $project).InstallLocation - $installRoot = "$env:SystemDrive/<%= windows_dir %>" + # AppX packages are always omnibus-style installs, never habitat + $installRoot = "$env:SystemDrive/<%= omnibus_windows_dir %>" $omnibusRoot = Join-Path $installRoot $project if(!(Test-Path $installRoot)) { diff --git a/spec/unit/mixlib/install/generator/base_spec.rb b/spec/unit/mixlib/install/generator/base_spec.rb index 28b1cc36..c9a08195 100644 --- a/spec/unit/mixlib/install/generator/base_spec.rb +++ b/spec/unit/mixlib/install/generator/base_spec.rb @@ -160,7 +160,7 @@ def self.script_base_path before do @temp_dir = Dir.mktmpdir @script_path = File.join(@temp_dir, "windows_dir.sh.erb") - File.write(@script_path, "dir=<%= windows_dir %>") + File.write(@script_path, "hab=<%= habitat_windows_dir %>\nomnibus=<%= omnibus_windows_dir %>") allow(test_generator_class).to receive(:script_base_path).and_return(@temp_dir) end @@ -169,34 +169,22 @@ def self.script_base_path FileUtils.rm_rf(@temp_dir) if @temp_dir end - it "uses habitat directory for chef-ice" do - context = { default_product: "chef-ice" } - script = test_generator_class.get_script("windows_dir.sh", context) - - expect(script).to include("dir=hab\\pkgs") - end - - it "uses habitat directory for chef-workstation-enterprise" do - context = { default_product: "chef-workstation-enterprise" } - script = test_generator_class.get_script("windows_dir.sh", context) - - expect(script).to include("dir=hab\\pkgs") - end - - it "sets is_habitat, hab_origin, hab_package_name for habitat product" do + it "sets habitat_products, habitat_windows_dir, omnibus_windows_dir" do context = { default_product: "chef-ice" } test_generator_class.get_script("windows_dir.sh", context) - expect(context[:is_habitat]).to eq(true) - expect(context[:hab_origin]).to eq("chef") - expect(context[:hab_package_name]).to eq("chef-infra-client") + expect(context[:habitat_products]).to be_a(Hash) + expect(context[:habitat_products]["chef-ice"]).to eq({ origin: "chef", package_name: "chef-infra-client" }) + expect(context[:habitat_windows_dir]).to be_a(String) + expect(context[:omnibus_windows_dir]).to be_a(String) end - it "uses omnibus directory for chef" do - context = { default_product: "chef" } + it "renders habitat_windows_dir and omnibus_windows_dir in scripts" do + context = { default_product: "chef-ice" } script = test_generator_class.get_script("windows_dir.sh", context) - expect(script).to include("dir=opscode") + expect(script).to include("hab=hab\\pkgs") + expect(script).to include("omnibus=opscode") end end end @@ -252,7 +240,6 @@ def self.script_base_path support=<%= support_url %> resources=<%= resources_url %> macos=<%= macos_dir %> - windows=<%= windows_dir %> SCRIPT allow(test_generator_class).to receive(:script_base_path).and_return(@temp_dir) @@ -273,7 +260,6 @@ def self.script_base_path expect(script).to include("support=https://www.chef.io/support/tickets") expect(script).to include("resources=https://www.chef.io/support") expect(script).to include("macos=chef_software") - expect(script).to include("windows=opscode") end end end diff --git a/spec/unit/mixlib/install/generator_spec.rb b/spec/unit/mixlib/install/generator_spec.rb index e692a43b..52d71937 100644 --- a/spec/unit/mixlib/install/generator_spec.rb +++ b/spec/unit/mixlib/install/generator_spec.rb @@ -681,6 +681,77 @@ end end + context "habitat install-location hashtable for chef-ice" do + let(:add_options) do + { + product_name: "chef-ice", + shell_type: :ps1, + license_id: "test-license-key-123", + } + end + + it_behaves_like "the correct ps1 script" + + it "contains the habitat_products hashtable with chef-ice entry" do + expect(install_script).to include("'chef-ice' = @{ origin = 'chef'; package_name = 'chef-infra-client' }") + end + + it "uses runtime ContainsKey check not compile-time ERB branching" do + expect(install_script).to include("$habitat_products.ContainsKey($project)") + expect(install_script).not_to include("is_habitat") + end + + it "includes hab\\pkgs path in the habitat install location logic" do + expect(install_script).to include("hab\\pkgs\\$($hab_meta.origin)\\$($hab_meta.package_name)") + end + end + + context "habitat install-location hashtable for chef-workstation-enterprise" do + let(:add_options) do + { + product_name: "chef-workstation-enterprise", + shell_type: :ps1, + license_id: "test-license-key-123", + } + end + + it_behaves_like "the correct ps1 script" + + it "contains the habitat_products hashtable with chef-workstation-enterprise entry" do + expect(install_script).to include("'chef-workstation-enterprise' = @{ origin = 'chef'; package_name = 'chef-workstation' }") + end + + it "uses runtime ContainsKey check" do + expect(install_script).to include("$habitat_products.ContainsKey($project)") + end + end + + context "omnibus product (chef) does not use habitat paths" do + let(:add_options) do + { + product_name: "chef", + shell_type: :ps1, + license_id: "test-license-key-123", + } + end + + it_behaves_like "the correct ps1 script" + + it "still includes the habitat_products hashtable (runtime check)" do + expect(install_script).to include("$habitat_products = @{") + expect(install_script).to include("$habitat_products.ContainsKey($project)") + end + + it "does not hardcode habitat paths" do + expect(install_script).not_to include("\\hab\\pkgs\\chef\\chef-infra-client") + end + + it "includes omnibus install path via elseif branch" do + expect(install_script).to include("elseif ($project -eq 'chef')") + expect(install_script).to include("opscode\\chef\\bin") + end + end + context "optional package_manager parameter for PowerShell" do let(:add_options) do {