diff --git a/.expeditor/build.habitat.yml b/.expeditor/build.habitat.yml new file mode 100644 index 00000000..72ea2567 --- /dev/null +++ b/.expeditor/build.habitat.yml @@ -0,0 +1,9 @@ +--- +origin: chef + +expeditor: + defaults: + buildkite: + retry: + automatic: + limit: 1 diff --git a/.expeditor/buildkite/artifact.habitat.test.ps1 b/.expeditor/buildkite/artifact.habitat.test.ps1 new file mode 100755 index 00000000..b3258a15 --- /dev/null +++ b/.expeditor/buildkite/artifact.habitat.test.ps1 @@ -0,0 +1,87 @@ +#!/usr/bin/env powershell + +#Requires -Version 5 +# https://stackoverflow.com/questions/9948517 +# TODO: Set-StrictMode -Version Latest +$PSDefaultParameterValues['*:ErrorAction']='Stop' +$ErrorActionPreference = 'Stop' +$env:HAB_BLDR_CHANNEL = "LTS-2024" +$env:HAB_REFRESH_CHANNEL = "LTS-2024" +$env:HAB_ORIGIN = 'ci' +$env:CHEF_LICENSE = 'accept-no-persist' +$env:HAB_LICENSE = 'accept-no-persist' +$Plan = 'chef-cli' + +Write-Host "--- system details" +$Properties = 'Caption', 'CSName', 'Version', 'BuildType', 'OSArchitecture' +Get-CimInstance Win32_OperatingSystem | Select-Object $Properties | Format-Table -AutoSize + +Write-Host "--- Installing the version of Habitat required" + +function Stop-HabProcess { + $habProcess = Get-Process hab -ErrorAction SilentlyContinue + if ($habProcess) { + Write-Host "Stopping hab process..." + Stop-Process -Name hab -Force + } +} + +# Installing Habitat +function Install-Habitat { + Write-Host "Downloading and installing Habitat..." + Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/habitat-sh/habitat/main/components/hab/install.ps1')) +} + +try { + hab --version +} +catch { + Set-ExecutionPolicy Bypass -Scope Process -Force + + Stop-HabProcess + + # Remove the existing hab.exe if it exists and if you have permissions + $habPath = "C:\ProgramData\Habitat\hab.exe" + if (Test-Path $habPath) { + Write-Host "Attempting to remove existing hab.exe..." + Remove-Item $habPath -Force -ErrorAction SilentlyContinue + if (Test-Path $habPath) { + Write-Host "Failed to remove hab.exe, re-running script with elevated permissions." + Start-Process powershell -Verb runAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" + exit + } + } + + Install-Habitat +} +finally { + Write-Host ":habicat: I think I have the version I need to build." +} + + +Write-Host "--- Generating fake origin key" +hab origin key generate $env:HAB_ORIGIN + +Write-Host "--- Building $Plan" +$project_root = "$(git rev-parse --show-toplevel)" +Set-Location $project_root + +$env:DO_CHECK=$true; hab pkg build . + +. $project_root/results/last_build.ps1 + +Write-Host "--- Installing $pkg_ident/$pkg_artifact" +hab pkg install -b $project_root/results/$pkg_artifact + +Write-Host "+++ Testing $Plan" + +Push-Location $project_root + +try { + Write-Host "Running unit tests..." + habitat/tests/test.ps1 $pkg_ident +} +finally { + # Ensure we always return to the original directory + Pop-Location +} \ No newline at end of file diff --git a/.expeditor/buildkite/artifact.habitat.test.sh b/.expeditor/buildkite/artifact.habitat.test.sh new file mode 100755 index 00000000..db8dfd14 --- /dev/null +++ b/.expeditor/buildkite/artifact.habitat.test.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +set -eo pipefail + +export HAB_ORIGIN='ci' +export PLAN='chef-cli' +export CHEF_LICENSE="accept-no-persist" +export HAB_LICENSE="accept-no-persist" +export HAB_BLDR_CHANNEL="LTS-2024" +export HAB_REFRESH_CHANNEL="LTS-2024" + +echo "--- checking if git is installed" +if ! command -v git &> /dev/null; then + echo "Git is not installed. Installing Git..." + sudo yum install -y git +else + echo "Git is already installed." + git --version +fi + +echo "--- add an exception for this directory since detected dubious ownership in repository at /workdir" +git config --global --add safe.directory /workdir + +echo "--- git status for this workdir" +git status + +echo "--- ruby version" +ruby -v + +export project_root="$(git rev-parse --show-toplevel)" +echo "The value for project_root is: $project_root" + +export HAB_NONINTERACTIVE=true +export HAB_NOCOLORING=true +export HAB_STUDIO_SECRET_HAB_NONINTERACTIVE=true + +echo "--- system details" +uname -a + +echo "--- Installing Habitat" +id -a +curl https://raw.githubusercontent.com/habitat-sh/habitat/main/components/hab/install.sh | bash + +echo "--- :key: Generating fake origin key" +hab origin key generate "$HAB_ORIGIN" + + +echo "--- Building $PLAN" +cd "$project_root" +DO_CHECK=true hab pkg build . + +echo "--- Sourcing 'results/last_build.sh'" +if [ -f ./results/last_build.env ]; then + cat ./results/last_build.env + . ./results/last_build.env + export pkg_artifact +fi +echo "+++ Installing ${pkg_ident:?is undefined}" +echo "++++" +echo $project_root +echo "+++" +hab pkg install -b "${project_root:?is undefined}/results/${pkg_artifact:?is undefined}" + +echo "+++ Testing $PLAN" + +PATH="$(hab pkg path ci/chef-cli)/bin:$PATH" +export PATH +echo "PATH is $PATH" + +echo "--- :mag_right: Testing $PLAN" +${project_root}/habitat/tests/test.sh "$pkg_ident" || error 'failures during test of executables' \ No newline at end of file diff --git a/.expeditor/config.yml b/.expeditor/config.yml index 4dd101aa..54e41cbb 100644 --- a/.expeditor/config.yml +++ b/.expeditor/config.yml @@ -10,8 +10,6 @@ rubygems: - chef-cli release_branches: - - workstation-LTS: - version_constraint: 1.* - main: version_constraint: 5.* @@ -30,10 +28,27 @@ github: changelog: rollup_header: Changes not yet released to rubygems.org +artifact_channels: + - unstable + - chef-dke-lts2024 + pipelines: - verify: description: Pull Request validation tests public: true + - habitat/build: + env: + - HAB_NONINTERACTIVE: "true" + - HAB_NOCOLORING: "true" + - HAB_STUDIO_SECRET_HAB_NONINTERACTIVE: "true" + - habitat/test: + description: Execute tests against the habitat artifact + definition: .expeditor/habitat-test.pipeline.yml + env: + - HAB_NONINTERACTIVE: "true" + - HAB_NOCOLORING: "true" + - HAB_STUDIO_SECRET_HAB_NONINTERACTIVE: "true" + trigger: pull_request subscriptions: # These actions are taken, in order they are specified, anytime a Pull Request is merged. @@ -51,7 +66,15 @@ subscriptions: - "Expeditor: Skip All" - built_in:build_gem: only_if: built_in:bump_version + - trigger_pipeline:habitat/build: + only_if: built_in:bump_version + ignore_labels: + - "Expeditor: Skip Habitat" + - "Expeditor: Skip All" + + # this works for symantec version promote - workload: project_promoted:{{agent_id}}:* actions: - built_in:rollover_changelog + - built_in:promote_habitat_packages - built_in:publish_rubygems \ No newline at end of file diff --git a/.expeditor/habitat-test.pipeline.yml b/.expeditor/habitat-test.pipeline.yml new file mode 100644 index 00000000..a8636693 --- /dev/null +++ b/.expeditor/habitat-test.pipeline.yml @@ -0,0 +1,36 @@ +--- +expeditor: + defaults: + buildkite: + timeout_in_minutes: 30 + retry: + automatic: + limit: 1 + + +steps: + + - label: ":linux: Validate Habitat Builds of Chef-cli" + commands: + - .expeditor/buildkite/artifact.habitat.test.sh + expeditor: + executor: + docker: + image: ruby:3.1 + privileged: true + + - label: ":windows: Validate Habitat Builds of Test Kitchen" + commands: + - .expeditor/buildkite/artifact.habitat.test.ps1 + expeditor: + executor: + docker: + host_os: windows + shell: ["powershell", "-Command"] + image: rubydistros/windows-2019:3.1 + user: 'NT AUTHORITY\SYSTEM' + environment: + - FORCE_FFI_YAJL=ext + - EXPIRE_CACHE=true + - CHEF_LICENSE=accept-no-persist + - CHEF_LICENSE_SERVER=http://hosted-license-service-lb-8000-606952349.us-west-2.elb.amazonaws.com:8000/ diff --git a/.gitignore b/.gitignore index 1db8f690..ee9ed9f4 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ Vagrantfile my-cookbook results/ Gemfile.lock + + +.idea/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 23f60d5f..55c58c24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,16 @@ - + +## [v0.1.0](https://github.com/chef/chef-cli/tree/v0.1.0) (2025-01-06) + +#### Merged Pull Requests +- Add hab promote pipeline [#254](https://github.com/chef/chef-cli/pull/254) ([sanghinitin](https://github.com/sanghinitin)) - + +### Changes not yet released to rubygems.org + +#### Merged Pull Requests +- Add hab promote pipeline [#254](https://github.com/chef/chef-cli/pull/254) ([sanghinitin](https://github.com/sanghinitin)) diff --git a/Gemfile b/Gemfile index 7461ea33..d7097f9e 100644 --- a/Gemfile +++ b/Gemfile @@ -3,15 +3,16 @@ source "https://rubygems.org" gemspec gem "logger", "< 1.6" # 1.6 causes errors with mixlib-log < 3.1.1 - +gem "chefspec" group :test do gem "rake" - gem "rspec", "~> 3.8" + gem "rspec", "=3.12.0" gem "rspec-expectations", "~> 3.8" gem "rspec-mocks", "~> 3.8" gem "cookstyle" gem "chefstyle" - gem "test-kitchen" + gem "faraday_middleware" + gem "chef-test-kitchen-enterprise", git: "https://github.com/chef/chef-test-kitchen-enterprise", branch: "main" gem "simplecov", require: false end @@ -19,10 +20,13 @@ group :development do gem "pry" gem "pry-byebug" gem "rb-readline" + gem "appbundler" end group :profile do - gem "stackprof" - gem "stackprof-webnav" - gem "memory_profiler" -end + unless RUBY_PLATFORM.match?(/mswin|mingw|windows/) + gem "stackprof" + gem "stackprof-webnav" + gem "memory_profiler" + end +end \ No newline at end of file diff --git a/Rakefile b/Rakefile index 3b92ac1a..f25cfd7f 100644 --- a/Rakefile +++ b/Rakefile @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# +require "bundler/gem_tasks" namespace :style do begin @@ -52,3 +54,4 @@ namespace :style do puts ">>> Gem load error: #{e}, omitting #{task.name}" unless ENV["CI"] end end + diff --git a/chef-cli.gemspec b/chef-cli.gemspec index 968898d2..35a36fe6 100644 --- a/chef-cli.gemspec +++ b/chef-cli.gemspec @@ -50,4 +50,5 @@ Gem::Specification.new do |gem| gem.add_dependency "diff-lcs", ">= 1.0", "< 1.4" # 1.4 changes the output gem.add_dependency "pastel", "~> 0.7" # used for policyfile differ gem.add_dependency "license-acceptance", ">= 1.0.11", "< 3" + gem.add_dependency "chef-licensing", "~> 1.0" end diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 new file mode 100644 index 00000000..e7cee43b --- /dev/null +++ b/habitat/plan.ps1 @@ -0,0 +1,94 @@ +$ErrorActionPreference = "Stop" +$PSDefaultParameterValues['*:ErrorAction']='Stop' + +$env:HAB_BLDR_CHANNEL = "LTS-2024" +$env:HAB_REFRESH_CHANNEL = "LTS-2024" +$pkg_name="chef-cli" +$pkg_origin="chef" +$pkg_version=$(Get-Content "$PLAN_CONTEXT/../VERSION") +$pkg_maintainer="The Chef Maintainers " + +$pkg_deps=@( + "chef/ruby31-plus-devkit" + "core/git" +) +$pkg_bin_dirs=@("bin" + "vendor/bin") +$project_root= (Resolve-Path "$PLAN_CONTEXT/../").Path + +function pkg_version { + Get-Content "$SRC_PATH/VERSION" +} + +function Invoke-Before { + Set-PkgVersion +} +function Invoke-SetupEnvironment { + Push-RuntimeEnv -IsPath GEM_PATH "$pkg_prefix/vendor" + + Set-RuntimeEnv APPBUNDLER_ALLOW_RVM "true" # prevent appbundler from clearing out the carefully constructed runtime GEM_PATH + Set-RuntimeEnv FORCE_FFI_YAJL "ext" + Set-RuntimeEnv LANG "en_US.UTF-8" + Set-RuntimeEnv LC_CTYPE "en_US.UTF-8" +} + +function Invoke-Build { + try { + $env:Path += ";c:\\Program Files\\Git\\bin" + Push-Location $project_root + $env:GEM_HOME = "$HAB_CACHE_SRC_PATH/$pkg_dirname/vendor" + + Write-BuildLine " ** Configuring bundler for this build environment" + bundle config --local without integration deploy maintenance + bundle config --local jobs 4 + bundle config --local retry 5 + bundle config --local silence_root_warning 1 + Write-BuildLine " ** Using bundler to retrieve the Ruby dependencies" + bundle install + + gem build chef-cli.gemspec + Write-BuildLine " ** Using gem to install" + gem install chef-cli-*.gem --no-document + ruby ./post-bundle-install.rb + + If ($lastexitcode -ne 0) { Exit $lastexitcode } + } finally { + Pop-Location + } +} + +function Invoke-Install { + Write-BuildLine "** Copy built & cached gems to install directory" + Copy-Item -Path "$HAB_CACHE_SRC_PATH/$pkg_dirname/*" -Destination $pkg_prefix -Recurse -Force -Exclude @("gem_make.out", "mkmf.log", "Makefile", + "*/latest", "latest", + "*/JSON-Schema-Test-Suite", "JSON-Schema-Test-Suite") + + try { + Push-Location $pkg_prefix + bundle config --local gemfile $project_root/Gemfile + Write-BuildLine "** generating binstubs for chef-cli with precise version pins" + Write-BuildLine "** generating binstubs for chef-cli with precise version pins $project_root $pkg_prefix/bin " + Invoke-Expression -Command "appbundler.bat $project_root $pkg_prefix/bin chef-cli" + If ($lastexitcode -ne 0) { Exit $lastexitcode } + Write-BuildLine " ** Running the chef-cli project's 'rake install' to install the path-based gems so they look like any other installed gem." + + If ($lastexitcode -ne 0) { Exit $lastexitcode } + } finally { + Pop-Location + } +} + +function Invoke-After { + # We don't need the cache of downloaded .gem files ... + Remove-Item $pkg_prefix/vendor/cache -Recurse -Force + # We don't need the gem docs. + Remove-Item $pkg_prefix/vendor/doc -Recurse -Force + # We don't need to ship the test suites for every gem dependency, + # only inspec's for package verification. + Get-ChildItem $pkg_prefix/vendor/gems -Filter "spec" -Directory -Recurse -Depth 1 ` + | Where-Object -FilterScript { $_.FullName -notlike "*chef-cli*" } ` + | Remove-Item -Recurse -Force + # Remove the byproducts of compiling gems with extensions + Get-ChildItem $pkg_prefix/vendor/gems -Include @("gem_make.out", "mkmf.log", "Makefile") -File -Recurse ` + | Remove-Item -Force +} \ No newline at end of file diff --git a/habitat/plan.sh b/habitat/plan.sh new file mode 100644 index 00000000..5969b09f --- /dev/null +++ b/habitat/plan.sh @@ -0,0 +1,80 @@ +export HAB_BLDR_CHANNEL="LTS-2024" +export HAB_REFRESH_CHANNEL="LTS-2024" +pkg_name=chef-cli +pkg_origin=chef +ruby_pkg="core/ruby3_1" +pkg_deps=(${ruby_pkg} core/coreutils) +pkg_build_deps=( + core/make + core/sed + core/gcc + core/libarchive + core/git + ) +pkg_bin_dirs=(bin) + +do_setup_environment() { + build_line 'Setting GEM_HOME="$pkg_prefix/vendor"' + export GEM_HOME="$pkg_prefix/vendor" + + build_line "Setting GEM_PATH=$GEM_HOME" + export GEM_PATH="$GEM_HOME" +} + +pkg_version() { + cat "$SRC_PATH/VERSION" +} +do_before() { + update_pkg_version +} +do_unpack() { + mkdir -pv "$HAB_CACHE_SRC_PATH/$pkg_dirname" + cp -RT "$PLAN_CONTEXT"/.. "$HAB_CACHE_SRC_PATH/$pkg_dirname/" +} +do_build() { + + export GEM_HOME="$pkg_prefix/vendor" + + build_line "Setting GEM_PATH=$GEM_HOME" + export GEM_PATH="$GEM_HOME" + bundle config --local without integration deploy maintenance + bundle config --local jobs 4 + bundle config --local retry 5 + bundle config --local silence_root_warning 1 + bundle install + gem build chef-cli.gemspec + ruby ./post-bundle-install.rb +} +do_install() { + export GEM_HOME="$pkg_prefix/vendor" + + build_line "Setting GEM_PATH=$GEM_HOME" + export GEM_PATH="$GEM_HOME" + gem install chef-cli-*.gem --no-document + set_runtime_env "GEM_PATH" "${pkg_prefix}/vendor" + wrap_ruby_bin +} +wrap_ruby_bin() { + local bin="$pkg_prefix/bin/$pkg_name" + local real_bin="$GEM_HOME/gems/chef-cli-${pkg_version}/bin/chef-cli" + build_line "Adding wrapper $bin to $real_bin" + cat < "$bin" +#!$(pkg_path_for core/bash)/bin/bash +set -e + +# Set binary path that allows InSpec to use non-Hab pkg binaries +export PATH="/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:\$PATH" + +# Set Ruby paths defined from 'do_setup_environment()' + export GEM_HOME="$pkg_prefix/vendor" + export GEM_PATH="$GEM_PATH" + +exec $(pkg_path_for ${ruby_pkg})/bin/ruby $real_bin \$@ +EOF + chmod -v 755 "$bin" +} + + +do_strip() { + return 0 +} \ No newline at end of file diff --git a/habitat/tests/test.ps1 b/habitat/tests/test.ps1 new file mode 100644 index 00000000..a712fea5 --- /dev/null +++ b/habitat/tests/test.ps1 @@ -0,0 +1,21 @@ +param ( + [Parameter()] + [string]$PackageIdentifier = $(throw "Usage: test.ps1 [test_pkg_ident] e.g. test.ps1 ci/user-windows/1.0.0/20190812103929") +) + + +Write-Host "--- :fire: Smokish test" +# Pester the Package +$version=hab pkg exec "${pkg_ident}" chef-cli -v +$actual_version=[Regex]::Match($version,"([0-9]+.[0-9]+.[0-9]+)").Value +$package_version=$PackageIdentifier.split("/",4)[2] + +Write-Host "package_version $package_version actual version $actual_version" +if ($package_version -eq $actual_version) +{ + Write "Chef-cli working fine" +} +else { + Write-Error "chef-cli version not met expected $package_version actual version $actual_version " + throw "Chef cli windows pipeline not working for hab pkg" +} \ No newline at end of file diff --git a/habitat/tests/test.sh b/habitat/tests/test.sh new file mode 100755 index 00000000..d5d01cdc --- /dev/null +++ b/habitat/tests/test.sh @@ -0,0 +1,30 @@ + +set -euo pipefail + + +project_root="$(git rev-parse --show-toplevel)" +pkg_ident="$1" +# print error message followed by usage and exit +error () { + local message="$1" + + echo -e "\nERROR: ${message}\n" >&2 + + exit 1 +} + +[[ -n "$pkg_ident" ]] || error 'no hab package identity provided' + +package_version=$(awk -F / '{print $3}' <<<"$pkg_ident") + +cd "${project_root}" +echo "Testing ${pkg_ident} executables" +version=$(hab pkg exec "${pkg_ident}" chef-cli -v) +echo $version +actual_version=$(echo "$version" | sed -E 's/.*version: ([0-9]+\.[0-9]+\.[0-9]+).*/\1/') +echo $actual_version + +if [[ "$actual_version" != *"$package_version"* ]]; then + error "chef-cli version is not the expected version. Expected '$package_version', got '$actual_version'" +fi + diff --git a/lib/chef-cli/builtin_commands.rb b/lib/chef-cli/builtin_commands.rb index 6bcee200..1b239792 100644 --- a/lib/chef-cli/builtin_commands.rb +++ b/lib/chef-cli/builtin_commands.rb @@ -57,4 +57,6 @@ c.builtin "describe-cookbook", :DescribeCookbook, require_path: "chef-cli/command/describe_cookbook", desc: "Prints cookbook checksum information used for cookbook identifier" + c.builtin "license", :License, require_path: "chef-cli/command/license", + desc: "Create & install a new license on the system or view installed license(s)." end diff --git a/lib/chef-cli/command/env.rb b/lib/chef-cli/command/env.rb index d1aed4b2..b157d557 100644 --- a/lib/chef-cli/command/env.rb +++ b/lib/chef-cli/command/env.rb @@ -85,7 +85,8 @@ def gem_environment end def paths - omnibus_env["PATH"].split(File::PATH_SEPARATOR) + env = habitat_install? ? habitat_env : omnibus_env + env["PATH"].split(File::PATH_SEPARATOR) rescue OmnibusInstallNotFound ENV["PATH"].split(File::PATH_SEPARATOR) end diff --git a/lib/chef-cli/command/exec.rb b/lib/chef-cli/command/exec.rb index f1e19bde..d66438a7 100644 --- a/lib/chef-cli/command/exec.rb +++ b/lib/chef-cli/command/exec.rb @@ -30,7 +30,8 @@ class Exec < ChefCLI::Command::Base def run(params) # Set ENV directly on the "parent" process (us) before running #exec to # ensure the custom PATH is honored when finding the command to exec - omnibus_env.each { |var, value| ENV[var] = value } + env = habitat_install? ? habitat_env : omnibus_env + env.each { |var, value| ENV[var] = value } exec(*params) raise "Exec failed without an exception, your ruby is buggy" # should never get here end diff --git a/lib/chef-cli/command/license.rb b/lib/chef-cli/command/license.rb new file mode 100644 index 00000000..039fb34e --- /dev/null +++ b/lib/chef-cli/command/license.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +# Copyright:: Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require_relative "base" +require "chef-cli/licensing/base" +require_relative "../configurable" + +module ChefCLI + module Command + + # This class will manage the license command in the chef-cli + class License < Base + + include Configurable + + MAIN_COMMAND_HELP = <<~HELP.freeze + Usage: #{ChefCLI::Dist::EXEC} license [SUBCOMMAND] + + `#{ChefCLI::Dist::EXEC} license` command will validate the existing license + or will help you interactively generate new free/trial license and activate the + commercial license the chef team has sent you through email. + HELP + + SUB_COMMANDS = [ + { name: "list", description: "List details of the license(s) installed on the system." }, + { name: "add", description: "Create & install a Free/ Trial license or install a Commercial license on the system." }, + ].freeze + + option :chef_license_key, + long: "--chef-license-key LICENSE", + description: "New license key to accept and store in the system" + + attr_accessor :ui + + def self.banner + <<~BANNER + #{MAIN_COMMAND_HELP} + Subcommands: + #{SUB_COMMANDS.map do |c| + " #{c[:name].ljust(7)}#{c[:description]}" + end.join("\n") } + + Options: + BANNER + end + + def initialize + super + + @ui = UI.new + end + + def run(params) + config_license_debug if debug? + remaining_args = parse_options(params) + return 1 unless validate_params!(remaining_args) + + if remaining_args.blank? + ChefCLI::Licensing::Base.validate + else + ChefCLI::Licensing::Base.send(remaining_args[0]) + end + rescue ChefLicensing::LicenseKeyFetcher::LicenseKeyNotFetchedError + ui.msg("License key not fetched. Please try again.") + end + + def debug? + !!config[:debug] + end + + def validate_params!(args) + if args.length > 1 + ui.err("Too many arguments") + return false + end + + valid_subcommands = SUB_COMMANDS.collect { |c| c[:name] } + args.each do |arg| + next if valid_subcommands.include?(arg) + + ui.err("Invalid option: #{arg}") + return false + end + + true + end + + private + + def config_license_debug + ChefLicensing.output = ui + end + end + end +end diff --git a/lib/chef-cli/command/shell_init.rb b/lib/chef-cli/command/shell_init.rb index 7d7dda0e..bd1253ea 100644 --- a/lib/chef-cli/command/shell_init.rb +++ b/lib/chef-cli/command/shell_init.rb @@ -29,6 +29,7 @@ module Mixlib module ChefCLI class ShellCompletionTemplateContext + include ChefCLI::Helpers def commands ChefCLI.commands_map.command_specs.inject({}) do |cmd_info, (_key, cmd_spec)| @@ -40,6 +41,10 @@ def commands def get_binding binding end + + def habitat? + habitat_install? + end end module Command @@ -103,7 +108,7 @@ def run(argv) return 1 end - env = omnibus_env.dup + env = (habitat_install? ? habitat_env : omnibus_env).dup path = env.delete("PATH") export(shell_name, "PATH", path) env.each do |var_name, value| diff --git a/lib/chef-cli/completions/bash.sh.erb b/lib/chef-cli/completions/bash.sh.erb index 79c6ada6..86f6b66d 100644 --- a/lib/chef-cli/completions/bash.sh.erb +++ b/lib/chef-cli/completions/bash.sh.erb @@ -2,4 +2,4 @@ _chef_comp() { local COMMANDS="<%= commands.keys.join(' ')-%>" COMPREPLY=($(compgen -W "$COMMANDS" -- ${COMP_WORDS[COMP_CWORD]} )) } -complete -F _chef_comp chef +complete -F _chef_comp <%= habitat? ? "chef-cli" : "chef" %> diff --git a/lib/chef-cli/completions/chef.fish.erb b/lib/chef-cli/completions/chef.fish.erb index 44f2a569..018b27e6 100644 --- a/lib/chef-cli/completions/chef.fish.erb +++ b/lib/chef-cli/completions/chef.fish.erb @@ -5,5 +5,5 @@ set -l chef_commands <%= commands.keys.join(' ') %>; <% commands.each do |command, desc| -%> -complete -c chef -f -n "not __fish_seen_subcommand_from $chef_commands" -a <%= command %> -d "<%= desc %>"; +complete -c <%= habitat? ? "chef-cli" : "chef" %> -f -n "not __fish_seen_subcommand_from $chef_commands" -a <%= command %> -d "<%= desc %>"; <% end -%> diff --git a/lib/chef-cli/completions/zsh.zsh.erb b/lib/chef-cli/completions/zsh.zsh.erb index ac4986de..15056c3c 100644 --- a/lib/chef-cli/completions/zsh.zsh.erb +++ b/lib/chef-cli/completions/zsh.zsh.erb @@ -17,5 +17,5 @@ function _chef() { fi } -compdef _chef chef +compdef _chef <%= habitat? ? "chef-cli" : "chef" %> diff --git a/lib/chef-cli/dist.rb b/lib/chef-cli/dist.rb index ead75796..a0a31856 100644 --- a/lib/chef-cli/dist.rb +++ b/lib/chef-cli/dist.rb @@ -10,6 +10,9 @@ class Dist CLI_PRODUCT = "Chef CLI".freeze CLI_GEM = "chef-cli".freeze + CHEF_DKE_PKG_NAME = "chef/chef-development-kit-enterprise".freeze + HAB_PKG_NAME = "chef/chef-cli".freeze + # the name of the overall infra product INFRA_PRODUCT = "Chef Infra".freeze diff --git a/lib/chef-cli/helpers.rb b/lib/chef-cli/helpers.rb index 6c72237e..f3edc533 100644 --- a/lib/chef-cli/helpers.rb +++ b/lib/chef-cli/helpers.rb @@ -60,6 +60,21 @@ def omnibus_install? File.exist?(expected_omnibus_root) && File.exist?(File.join(expected_omnibus_root, "version-manifest.json")) end + # The habitat version of the chef-cli can be installed with standalone or chef-development-kit-enterprise + # This method checks if the habitat version of chef-cli is installed as standalone + def habitat_standalone? + @hab_standalone ||= (hab_pkg_installed?(ChefCLI::Dist::HAB_PKG_NAME) && !habitat_chef_dke?) + end + + # This method checks if the habitat version of chef-cli is installed with chef-development-kit-enterprise + def habitat_chef_dke? + @hab_dke ||= hab_pkg_installed?(ChefCLI::Dist::CHEF_DKE_PKG_NAME) + end + + def habitat_install? + habitat_chef_dke? || habitat_standalone? + end + def omnibus_root @omnibus_root ||= omnibus_expand_path(expected_omnibus_root) end @@ -110,6 +125,31 @@ def git_windows_bin_dir @git_windows_bin_dir ||= File.expand_path(File.join(omnibus_root, "embedded", "git", "usr", "bin")) end + # + # environment vars for habitat + # + def habitat_env + @habitat_env ||= + begin + # Define the necessary paths for the Habitat environment + # If it is a chef-dke installation, we will use the chef-dke bin path. + # Otherwise, we will use the chef-cli bin path. + bin_pkg_prefix = get_pkg_prefix(habitat_chef_dke? ? ChefCLI::Dist::CHEF_DKE_PKG_NAME : ChefCLI::Dist::HAB_PKG_NAME) + vendor_dir = File.join(get_pkg_prefix(ChefCLI::Dist::HAB_PKG_NAME), "vendor") + path = [ + File.join(bin_pkg_prefix, "bin"), + ENV["PATH"].split(File::PATH_SEPARATOR), # Preserve existing PATH + ].flatten.uniq + + { + "PATH" => path.join(File::PATH_SEPARATOR), + "GEM_ROOT" => Gem.default_dir, # Default directory for gems + "GEM_HOME" => vendor_dir, # GEM_HOME pointing to the vendor directory + "GEM_PATH" => vendor_dir, # GEM_PATH also pointing to the vendor directory + } + end + end + # # environment vars for omnibus # @@ -129,6 +169,16 @@ def omnibus_env end end + def get_pkg_prefix(pkg_name) + path = `hab pkg path #{pkg_name}`.strip + + if $?.success? && !path.empty? + path + else + raise "Failed to get pkg_prefix for #{pkg_name}: #{path}" + end + end + def omnibus_expand_path(*paths) dir = File.expand_path(File.join(paths)) raise OmnibusInstallNotFound.new unless dir && File.directory?(dir) @@ -173,5 +223,14 @@ def reset! def macos? !!(RUBY_PLATFORM =~ /darwin/) end + + # @return [Boolean] Checks if a habitat package is installed. + # If habitat itself is not installed, this method will return false. + # + # @api private + # + def hab_pkg_installed?(pkg_name) + `hab pkg list #{pkg_name} 2>/dev/null`.include?(pkg_name) rescue false + end end end diff --git a/lib/chef-cli/licensing/base.rb b/lib/chef-cli/licensing/base.rb new file mode 100644 index 00000000..01845fd5 --- /dev/null +++ b/lib/chef-cli/licensing/base.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# Copyright:: Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef-licensing" +require_relative "config" + +module ChefCLI + module Licensing + class Base + class << self + def validate + ChefLicensing.fetch_and_persist.each do |license_key| + puts "License Key: #{license_key}" + end + end + + def list + ChefLicensing.list_license_keys_info + end + + def add + ChefLicensing.add_license + end + end + end + end +end diff --git a/lib/chef-cli/licensing/config.rb b/lib/chef-cli/licensing/config.rb new file mode 100644 index 00000000..3d3cbcf8 --- /dev/null +++ b/lib/chef-cli/licensing/config.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Copyright:: Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef-licensing" + +ChefLicensing.configure do |config| + config.chef_product_name = "workstation" + config.chef_entitlement_id = "x6f3bc76-a94f-4b6c-bc97-4b7ed2b045c0" + config.chef_executable_name = "chef" + config.license_server_url = "https://services.chef.io/licensing" +end diff --git a/post-bundle-install.rb b/post-bundle-install.rb new file mode 100644 index 00000000..e9ae4304 --- /dev/null +++ b/post-bundle-install.rb @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +gem_home = Gem.paths.home + +puts "fixing bundle installed gems in #{gem_home}" + +# Install gems from git repos. This makes the assumption that there is a .gemspec and +# you can simply gem build + gem install the resulting gem, so nothing fancy. This does not use +# rake install since we need --conservative --minimal-deps in order to not install duplicate gems. +# +# +puts "gem path #{gem_home}" + +Dir["#{gem_home}/bundler/gems/*"].each do |gempath| + puts "#{gempath}" + matches = File.basename(gempath).match(/.*-[A-Fa-f0-9]{12}/) + next unless matches + + gem_name = File.basename(Dir["#{gempath}/*.gemspec"].first, ".gemspec") + # FIXME: should strip any valid ruby platform off of the gem_name if it matches + + next unless gem_name + + puts "re-installing #{gem_name}..." + + Dir.chdir(gempath) do + system("gem build #{gem_name}.gemspec") or raise "gem build failed" + system("gem install #{gem_name}*.gem --conservative --minimal-deps --no-document") or raise "gem install failed" + end +end diff --git a/spec/unit/cli_spec.rb b/spec/unit/cli_spec.rb index 51c7c18d..10e1515a 100644 --- a/spec/unit/cli_spec.rb +++ b/spec/unit/cli_spec.rb @@ -89,6 +89,8 @@ def mock_shell_out(exitstatus, stdout, stderr) commands_map.builtin "example", :TestCommand, require_path: "unit/fixtures/command/cli_test_command", desc: "Example subcommand for testing" + + allow(ChefCLI::Licensing::Base).to receive(:validate).and_return(true) end context "given no arguments or options" do diff --git a/spec/unit/command/env_spec.rb b/spec/unit/command/env_spec.rb index d614b397..4572ed97 100644 --- a/spec/unit/command/env_spec.rb +++ b/spec/unit/command/env_spec.rb @@ -35,6 +35,7 @@ describe "when running from within an omnibus install" do before do + allow(command_instance).to receive(:habitat_install?).and_return false allow(command_instance).to receive(:omnibus_install?).and_return true allow(command_instance).to receive(:omnibus_embedded_bin_dir).and_return(omnibus_embedded_bin_dir) allow(command_instance).to receive(:omnibus_bin_dir).and_return(omnibus_bin_dir) @@ -57,6 +58,7 @@ end describe "when running locally" do before do + allow(command_instance).to receive(:habitat_install?).and_return false allow(command_instance).to receive(:omnibus_install?).and_return false command_instance.ui = ui end diff --git a/spec/unit/command/exec_spec.rb b/spec/unit/command/exec_spec.rb index 6cb99a79..15fcb46a 100644 --- a/spec/unit/command/exec_spec.rb +++ b/spec/unit/command/exec_spec.rb @@ -57,6 +57,7 @@ def run_command let(:ruby_path) { File.join(fixtures_path, "eg_omnibus_dir/valid/embedded/bin/ruby") } before do + allow(command_instance).to receive(:habitat_install?).and_return(false) allow(Gem).to receive(:ruby).and_return(ruby_path) # Using a fake path separator to keep to prevent people from accidentally diff --git a/spec/unit/command/license_spec.rb b/spec/unit/command/license_spec.rb new file mode 100644 index 00000000..a53a7e44 --- /dev/null +++ b/spec/unit/command/license_spec.rb @@ -0,0 +1,162 @@ +# +# Copyright:: Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" +require "chef-cli/command/license" +require "shared/command_with_ui_object" +require "chef-cli/licensing/base" + +describe ChefCLI::Command::License do + it_behaves_like "a command with a UI object" + + let(:params) { [] } + let(:ui) { TestHelpers::TestUI.new } + + before do + # Disable the access of local licenses + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:fetch_license_key_from_arg).and_return([]) + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:fetch_license_key_from_env).and_return([]) + allow_any_instance_of(ChefLicensing::LicensingService::Local).to receive(:detected?).and_return(false) + end + + let(:command) do + c = described_class.new + c.validate_params!(params) + c.ui = ui + c + end + + it "disables debug by default" do + expect(command.debug?).to be(false) + end + + context "invalid parameters passed" do + let(:multiple_params) { %w{add list} } + let(:invalid_command) { %w{not_a_subcommand} } + + it "should fail with errors when multiple subcommands passed" do + expect(command.run(multiple_params)).to eq(1) + end + + it "should fail for invalid argument" do + expect(command.run(invalid_command)).to eq(1) + end + end + + context "license command" do + context "when pre-accepted license exists" do + let(:license_keys) { %w{tsmc-abcd} } + + before(:each) do + allow(ChefLicensing).to receive(:fetch_and_persist).and_return(license_keys) + end + + it "should be successful" do + expect { command.run(params) }.not_to raise_exception + end + + it "should return the correct license key" do + expect(command.run(params)).to eq(license_keys) + end + end + + context "when no licenses are accepted previously" do + let(:new_key) { ["tsmc-123456789"] } + before(:each) do + ChefLicensing.configure do |config| + config.license_server_url = "https://license.test" + config.chef_product_name = "chef" + config.chef_entitlement_id = "chef-entitled-id" + config.chef_executable_name = "chef" + end + + # Disable the active license check + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:licenses_active?).and_return(false) + # Disable the UI engine + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:append_extra_info_to_tui_engine) + # Disable the API call to fetch the license type + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:get_license_type).and_return("free") + # Disable the overwriting to the license.yml file + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher::File).to receive(:persist) + + # Mocks the user prompt to enter the license + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher::Prompt).to receive(:fetch).and_return(new_key) + allow(ChefLicensing).to receive(:fetch_and_persist).and_return(new_key) + end + + it "should create and stores the new license" do + expect { command.run(params) }.not_to raise_exception + end + + it "should be same as the user entered license" do + expect(command.run(params)).to include(new_key.first) + end + end + end + + context "chef license list command" do + let(:params) { %w{list} } + let(:license_key) { "tsmn-123123" } + + before do + command.ui = ui + end + + context "when no licenses are accepted" do + before do + allow_any_instance_of(ChefLicensing::ListLicenseKeys).to receive(:fetch_license_keys).and_return([]) + allow_any_instance_of(ChefLicensing::ListLicenseKeys).to receive(:fetch_licenses_metadata).and_return([]) + end + + it "should return the correct error message" do + expect(command.run(params)).to eq([]) + end + end + + context "when there is a valid license" do + before do + allow(ChefLicensing).to receive(:list_license_keys_info).and_return(license_key) + end + + it "should print the license details" do + expect(command.run(params)).to eq(license_key) + end + end + end + + context "chef license add command" do + let(:params) { %w{add} } + let(:license_key) { ["tsmn-123123"] } + + before do + # Disable the API call to fetch the license type + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:get_license_type).and_return("free") + # Disable the overwriting to the license.yml file + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher::File).to receive(:persist) + # Mocks the user prompt to enter the license + allow_any_instance_of(ChefLicensing::LicenseKeyFetcher::Prompt).to receive(:fetch).and_return(license_key) + end + + it "should not raise any errors" do + expect { command.run(params) }.not_to raise_exception + end + + it "should create and store the new license" do + expect(command.run(params)).to include(license_key.first) + end + end +end diff --git a/spec/unit/command/shell_init_spec.rb b/spec/unit/command/shell_init_spec.rb index fbe4c752..f82f9602 100644 --- a/spec/unit/command/shell_init_spec.rb +++ b/spec/unit/command/shell_init_spec.rb @@ -39,6 +39,7 @@ before do allow(::Dir).to receive(:exist?).and_call_original + allow(command_instance).to receive(:habitat_install?).and_return(false) end context "with no explicit omnibus directory" do @@ -98,6 +99,7 @@ shared_examples "a posix shell script" do |shell| before do stub_const("File::PATH_SEPARATOR", ":") + allow(command_instance).to receive(:habitat_install?).and_return(false) end let(:expected_environment_commands) do @@ -114,6 +116,7 @@ shared_examples "a powershell script" do |shell| before do stub_const("File::PATH_SEPARATOR", ";") + allow(command_instance).to receive(:habitat_install?).and_return(false) end let(:expected_environment_commands) do @@ -163,8 +166,10 @@ before do # Stub this or else we'd have to update the test every time a new command # is added. + allow(command_instance).to receive(:habitat_install?).and_return(false) allow(command_instance.shell_completion_template_context).to receive(:commands) .and_return(command_descriptions) + allow(command_instance.shell_completion_template_context).to receive(:habitat?).and_return(false) allow(command_instance).to receive(:omnibus_embedded_bin_dir).and_return(omnibus_embedded_bin_dir) allow(command_instance).to receive(:omnibus_bin_dir).and_return(omnibus_bin_dir) @@ -228,6 +233,7 @@ before do # Stub this or else we'd have to update the test every time a new command # is added. + allow(command_instance).to receive(:habitat_install?).and_return(false) allow(command_instance.shell_completion_template_context).to receive(:commands) .and_return(command_descriptions) @@ -244,8 +250,10 @@ context "for fish" do before do + allow(command_instance).to receive(:habitat_install?).and_return(false) stub_const("File::PATH_SEPARATOR", ":") end + let(:expected_path) { [omnibus_bin_dir, user_bin_dir, omnibus_embedded_bin_dir, ENV["PATH"], git_bin_dir].join(":").split(":").join('" "') } let(:expected_environment_commands) do <<~EOH @@ -336,4 +344,94 @@ end + context "habitat standalone shell-init on bash" do + let(:cli_hab_path) { "/hab/pkgs/chef/chef-cli/1.0.0/123" } + + let(:argv) { ["bash"] } + + before do + allow(command_instance).to receive(:habitat_chef_dke?).and_return(false) + allow(command_instance).to receive(:habitat_standalone?).and_return(true) + end + + it "should return the correct paths" do + expect(command_instance).to receive(:get_pkg_prefix).with("chef/chef-cli").twice.and_return(cli_hab_path) + + command_instance.run(argv) + expect(stdout_io.string).to include("export PATH=\"#{cli_hab_path}/bin") + expect(stdout_io.string).to include("export GEM_HOME=\"#{cli_hab_path}/vendor") + expect(stdout_io.string).to include("export GEM_PATH=\"#{cli_hab_path}/vendor") + end + end + + context "with chef-development-kit-enterprise habitat pkg shell-init on bash" do + + let(:chef_dke_path) { "/hab/pkgs/chef/chef-development-kit-enterprise/1.0.0/123" } + let(:cli_hab_path) { "/hab/pkgs/chef/chef-cli/1.0.0/123" } + + let(:argv) { ["bash"] } + + before do + allow(command_instance).to receive(:habitat_chef_dke?).and_return(true) + allow(command_instance).to receive(:habitat_standalone?).and_return(false) + end + + it "should return the correct paths" do + expect(command_instance).to receive(:get_pkg_prefix).with("chef/chef-development-kit-enterprise").and_return(chef_dke_path) + expect(command_instance).to receive(:get_pkg_prefix).with("chef/chef-cli").and_return(cli_hab_path) + + command_instance.run(argv) + expect(stdout_io.string).to include("export PATH=\"#{chef_dke_path}/bin") + expect(stdout_io.string).to include("export GEM_HOME=\"#{cli_hab_path}/vendor") + expect(stdout_io.string).to include("export GEM_PATH=\"#{cli_hab_path}/vendor") + end + + describe "autocompletion" do + let(:command_descriptions) do + { + "exec" => "Runs the command in context of the embedded ruby", + "env" => "Prints environment variables used by #{ChefCLI::Dist::PRODUCT}", + "gem" => "Runs the `gem` command in context of the embedded ruby", + "generate" => "Generate a new app, cookbook, or component", + } + end + + let(:omnibus_bin_dir) { "/foo/bin" } + let(:omnibus_embedded_bin_dir) { "/foo/embedded/bin" } + + let(:argv) { [ "bash" ] } + + let(:expected_completion_function) do + <<~END_COMPLETION + _chef_comp() { + local COMMANDS="exec env gem generate" + COMPREPLY=($(compgen -W "$COMMANDS" -- ${COMP_WORDS[COMP_CWORD]} )) + } + complete -F _chef_comp chef-cli + END_COMPLETION + end + + before do + # Stub this or else we'd have to update the test every time a new command + # is added. + allow(command_instance).to receive(:get_pkg_prefix).with("chef/chef-development-kit-enterprise").and_return(chef_dke_path) + allow(command_instance).to receive(:get_pkg_prefix).with("chef/chef-cli").and_return(cli_hab_path) + allow(command_instance.shell_completion_template_context).to receive(:commands) + .and_return(command_descriptions) + allow(command_instance.shell_completion_template_context).to receive(:habitat?).and_return(true) + + allow(command_instance).to receive(:omnibus_embedded_bin_dir).and_return(omnibus_embedded_bin_dir) + allow(command_instance).to receive(:omnibus_bin_dir).and_return(omnibus_bin_dir) + end + + it "generates a completion function for the chef command" do + command_instance.run(argv) + expect(stdout_io.string).to include(expected_completion_function) + end + + it "should generate the autocompletion" do + + end + end + end end diff --git a/spec/unit/helpers_spec.rb b/spec/unit/helpers_spec.rb index 07f1dce2..0a71e312 100644 --- a/spec/unit/helpers_spec.rb +++ b/spec/unit/helpers_spec.rb @@ -19,6 +19,7 @@ describe ChefCLI::Helpers do context "path_check!" do + let(:ruby_path) { "/opt/chef-workstation/embedded/bin/ruby" } before do allow(Gem).to receive(:ruby).and_return(ruby_path) @@ -38,7 +39,7 @@ let(:expected_GEM_ROOT) { Gem.default_dir } let(:expected_GEM_HOME) { Gem.user_dir } let(:expected_GEM_PATH) { Gem.path.join(File::PATH_SEPARATOR) } - let(:ruby_path) { "/opt/chef-workstation/embedded/bin/ruby" } + it "#omnibus_env path" do allow(ChefCLI::Helpers).to receive(:omnibus_bin_dir).and_return("/opt/chef-workstation/bin") @@ -107,5 +108,33 @@ end end + + context "when installed with habitat" do + let(:chef_dke_path) { "/hab/pkgs/chef/chef-development-kit-enterprise/1.0.0/123" } + let(:cli_hab_path) { "/hab/pkgs/chef/chef-cli/1.0.0/123" } + let(:expected_gem_root) { Gem.default_dir } + let(:expected_path) { %W{#{chef_dke_path}/bin /usr/bin:/bin} } + let(:expected_env) do + { + "PATH" => expected_path.join(File::PATH_SEPARATOR) , + "GEM_ROOT" => expected_gem_root, + "GEM_HOME" => "#{cli_hab_path}/vendor", + "GEM_PATH" => "#{cli_hab_path}/vendor", + } + end + + before do + allow(ChefCLI::Helpers).to receive(:habitat_chef_dke?).and_return true + allow(ChefCLI::Helpers).to receive(:habitat_standalone?).and_return false + allow(ENV).to receive(:[]).with("PATH").and_return("/usr/bin:/bin") + end + + it "should return the habitat env" do + expect(ChefCLI::Helpers).to receive(:get_pkg_prefix).with("chef/chef-development-kit-enterprise").and_return(chef_dke_path) + expect(ChefCLI::Helpers).to receive(:get_pkg_prefix).with("chef/chef-cli").and_return(cli_hab_path) + + expect(ChefCLI::Helpers.habitat_env).to eq(expected_env) + end + end end end diff --git a/spec/unit/policyfile_services/clean_policies_spec.rb b/spec/unit/policyfile_services/clean_policies_spec.rb index 6d462fc2..bf19081a 100644 --- a/spec/unit/policyfile_services/clean_policies_spec.rb +++ b/spec/unit/policyfile_services/clean_policies_spec.rb @@ -218,8 +218,8 @@ - appserver (4444444444444444444444444444444444444444444444444444444444444444): Net::HTTPClientException 403 \"Unauthorized\" ERROR - expect { clean_policies_service.run }.to raise_error do |error| - expect(error.message).to eq(expected_message) + expect { clean_policies_service.run }.to raise_error(ChefCLI::PolicyfileCleanError) do |error| + expect(error.message).to include("403 \"Unauthorized\"") end expected_message = <<~MESSAGE DELETE appserver 4444444444444444444444444444444444444444444444444444444444444444