From 73b49cca38bdaafce7262d7ffa855373a7904675 Mon Sep 17 00:00:00 2001 From: Sachin Jain Date: Mon, 1 Jun 2026 12:35:11 +0530 Subject: [PATCH] Use appbundler for habitat packaging - Overhauled habitat/plan.sh to use appbundler for binstub generation - Added binstub_patch.rb for runtime GEM_PATH configuration - Added cleanup_lint_roller.rb to remove stray Gemfile.lock from vendored gems - Added .github directory cleanup in both plan.sh and plan.ps1 to avoid CVE false positives - Simplified Habitat installation in artifact.habitat.test.ps1 (removed version pinning) - Removed redundant project root logging from Windows test script - Replaced wrap_ruby_bin with appbundler + libexec wrapper pattern - Added do_prepare, do_after, do_end lifecycle hooks in plan.sh Signed-off-by: Sachin Jain --- .../buildkite/artifact.habitat.test.ps1 | 22 +---- binstub_patch.rb | 4 + cleanup_lint_roller.rb | 32 ++++++++ habitat/plan.ps1 | 3 + habitat/plan.sh | 82 ++++++++++++------- 5 files changed, 93 insertions(+), 50 deletions(-) create mode 100644 binstub_patch.rb create mode 100644 cleanup_lint_roller.rb diff --git a/.expeditor/buildkite/artifact.habitat.test.ps1 b/.expeditor/buildkite/artifact.habitat.test.ps1 index de029a3..b190559 100644 --- a/.expeditor/buildkite/artifact.habitat.test.ps1 +++ b/.expeditor/buildkite/artifact.habitat.test.ps1 @@ -9,7 +9,6 @@ $env:HAB_BLDR_CHANNEL = 'base-2025' $env:HAB_REFRESH_CHANNEL = "base-2025" $env:CHEF_LICENSE = 'accept-no-persist' $env:HAB_LICENSE = 'accept-no-persist' -$HabitatVersion = if ($env:HAB_VERSION) { $env:HAB_VERSION } else { '1.6.1245' } $Plan = 'chef-vault' Write-Host "--- system details" @@ -27,20 +26,8 @@ function Stop-HabProcess { } function Install-Habitat { - param( - [Parameter(Mandatory = $true)] - [string]$Version - ) - Write-Host "Downloading and installing Habitat version $Version..." - $installScriptUrl = 'https://raw.githubusercontent.com/habitat-sh/habitat/main/components/hab/install.ps1' - $installScriptPath = Join-Path $env:TEMP "hab-install-$Version.ps1" - Invoke-WebRequest -Uri $installScriptUrl -OutFile $installScriptPath - try { - & $installScriptPath -Version $Version - } - finally { - Remove-Item $installScriptPath -Force -ErrorAction SilentlyContinue - } + 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 { @@ -63,7 +50,7 @@ catch { } } - Install-Habitat -Version $HabitatVersion + Install-Habitat Write-Host "******************************************************************" Write-Host "** What is My Hab Version after installation? $(hab --version)" Write-Host "******************************************************************" @@ -80,9 +67,6 @@ Write-Host "--- Generating fake origin key" hab origin key generate $env:HAB_ORIGIN Write-Host "--- Building $Plan" -Write-Host "******************************************************************" -Write-Host "** What is My Project Root as determined by git rev? $(git rev-parse --show-toplevel)" -Write-Host "******************************************************************" $project_root = "$(git rev-parse --show-toplevel)" Set-Location $project_root diff --git a/binstub_patch.rb b/binstub_patch.rb new file mode 100644 index 0000000..0059942 --- /dev/null +++ b/binstub_patch.rb @@ -0,0 +1,4 @@ +unless ENV["APPBUNDLER_ALLOW_RVM"] + ENV["APPBUNDLER_ALLOW_RVM"] = "true" + ENV["GEM_PATH"] = [File.expand_path(File.join(__dir__, "..", "vendor")), ENV["GEM_PATH"]].compact.join(File::PATH_SEPARATOR) +end diff --git a/cleanup_lint_roller.rb b/cleanup_lint_roller.rb new file mode 100644 index 0000000..5ec96d8 --- /dev/null +++ b/cleanup_lint_roller.rb @@ -0,0 +1,32 @@ +#!/usr/bin/env ruby +# Removes stray Gemfile.lock files shipped inside gems to +# appease security scanners. +require "rubygems" + +# List of gems that ship with Gemfile.lock files that should be removed +GEMS_WITH_LOCKFILES = %w{lint_roller stackprof-webnav chef-vault}.freeze + +def cleanup_gem_lockfile(gem_name) + puts "Cleaning up #{gem_name} Gemfile.lock..." + specs = Gem::Specification.find_all_by_name(gem_name) + + if specs.empty? + puts " No #{gem_name} gem installed" + return + end + + specs.each do |spec| + gemfile_lock_path = File.join(spec.gem_dir, "Gemfile.lock") + if File.exist?(gemfile_lock_path) + puts " Removing #{gemfile_lock_path}" + File.delete(gemfile_lock_path) + puts " Successfully removed #{gem_name} Gemfile.lock" + else + puts " No Gemfile.lock found in #{spec.gem_dir}" + end + end +rescue StandardError => e + warn " Warning: Failed to clean up #{gem_name} Gemfile.lock: #{e.message}" +end + +GEMS_WITH_LOCKFILES.each { |gem_name| cleanup_gem_lockfile(gem_name) } diff --git a/habitat/plan.ps1 b/habitat/plan.ps1 index 8bd51fc..8f99e1b 100644 --- a/habitat/plan.ps1 +++ b/habitat/plan.ps1 @@ -126,6 +126,9 @@ function Invoke-After { # 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 -ErrorAction SilentlyContinue + # Remove .github directories from vendored gems to avoid CVE false positives + Get-ChildItem $pkg_prefix/vendor/gems -Filter ".github" -Directory -Recurse ` + | Remove-Item -Recurse -Force Write-BuildLine " chef vault done removing all cache" # Reset ErrorActionPreference in the script scope so Habitat's own diff --git a/habitat/plan.sh b/habitat/plan.sh index 06eda73..e40c4ed 100644 --- a/habitat/plan.sh +++ b/habitat/plan.sh @@ -1,14 +1,12 @@ export HAB_BLDR_CHANNEL="base-2025" export HAB_REFRESH_CHANNEL="base-2025" -ruby_pkg="core/ruby3_4" pkg_name="chef-vault" pkg_origin="chef" pkg_maintainer="The Chef Maintainers " pkg_description="Gem that allows you to encrypt a Chef Data Bag Item using the public keys of a list of chef nodes. This allows only those chef nodes to decrypt the encrypted values." pkg_license=('Apache-2.0') -pkg_bin_dirs=( - bin -) +ruby_pkg="core/ruby3_4" +pkg_deps=(${ruby_pkg} core/coreutils) pkg_build_deps=( core/make core/bash @@ -16,16 +14,22 @@ pkg_build_deps=( core/gcc core/libarchive ) -pkg_deps=(${ruby_pkg} core/coreutils) +pkg_bin_dirs=(bin) pkg_svc_user=root do_setup_environment() { - build_line 'Setting GEM_HOME="$pkg_prefix/vendor"' - export GEM_HOME="$pkg_prefix/vendor" + push_runtime_env GEM_PATH "${pkg_prefix}/vendor" - build_line "Setting GEM_PATH=$GEM_HOME" - export GEM_PATH="$GEM_HOME" + set_runtime_env APPBUNDLER_ALLOW_RVM "true" # prevent appbundler from clearing out the carefully constructed runtime GEM_PATH + set_runtime_env LANG "en_US.UTF-8" + set_runtime_env LC_CTYPE "en_US.UTF-8" +} + +do_prepare() { + if [[ ! -f /usr/bin/env ]]; then + ln -s "$(pkg_interpreter_for core/coreutils bin/env)" /usr/bin/env + fi } pkg_version() { @@ -42,7 +46,6 @@ do_unpack() { } do_build() { - export GEM_HOME="$pkg_prefix/vendor" build_line "Setting GEM_PATH=$GEM_HOME" @@ -53,11 +56,12 @@ do_build() { bundle config --local silence_root_warning 1 bundle install gem build chef-vault.gemspec + ruby ./cleanup_lint_roller.rb } do_install() { - # Copy NOTICE.TXT to the package directory + # Copy NOTICE to the package directory if [[ -f "$PLAN_CONTEXT/../NOTICE" ]]; then build_line "Copying NOTICE to package directory" cp "$PLAN_CONTEXT/../NOTICE" "$pkg_prefix/" @@ -70,36 +74,52 @@ do_install() { build_line "Setting GEM_PATH=$GEM_HOME" export GEM_PATH="$GEM_HOME" gem install chef-vault-*.gem --no-document - wrap_ruby_chef_vault - set_runtime_env "GEM_PATH" "${pkg_prefix}/vendor" -} + ruby ./cleanup_lint_roller.rb -wrap_ruby_chef_vault() { - local bin="$pkg_prefix/bin/chef-vault" - local real_bin="$GEM_HOME/gems/chef-vault-${pkg_version}/bin/chef-vault" - wrap_bin_with_ruby "$bin" "$real_bin" -} + build_line "** fixing binstub shebangs" + fix_interpreter "${pkg_prefix}/vendor/bin/*" "$ruby_pkg" bin/ruby + + build_line "** generating binstubs for chef-vault with precise version pins" + "${pkg_prefix}/vendor/bin/appbundler" . "$pkg_prefix/bin" chef-vault -wrap_bin_with_ruby() { - local bin="$1" - local real_bin="$2" - build_line "Adding wrapper $bin to $real_bin" - cat < "$bin" + build_line "** patching binstubs to allow running directly" + for binstub in ${pkg_prefix}/bin/*; do + sed -i "/require \"rubygems\"/r ${PLAN_CONTEXT}/../binstub_patch.rb" "$binstub" + done + + build_line "** creating wrapper for runtime environment" + mkdir -p "$pkg_prefix/libexec" + mv "$pkg_prefix/bin/chef-vault" "$pkg_prefix/libexec/chef-vault" + cat < "$pkg_prefix/bin/chef-vault" #!$(pkg_path_for core/bash)/bin/bash set -e -# Set binary path that allows chef-vault 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 PATH="$(pkg_path_for ${ruby_pkg})/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:$pkg_prefix/vendor/bin:\$PATH" export GEM_HOME="$pkg_prefix/vendor" -export GEM_PATH="$GEM_PATH" +export GEM_PATH="$pkg_prefix/vendor" -exec $(pkg_path_for ${ruby_pkg})/bin/ruby $real_bin \$@ +exec $(pkg_path_for ${ruby_pkg})/bin/ruby $pkg_prefix/libexec/chef-vault "\$@" EOF - chmod -v 755 "$bin" + chmod -v 755 "$pkg_prefix/bin/chef-vault" + + rm -rf $GEM_PATH/cache/ + rm -rf $GEM_PATH/bundler + rm -rf $GEM_PATH/doc +} + +do_after() { + build_line "Removing .github directories from vendored gems..." + find "$pkg_prefix/vendor/gems" -type d -name ".github" \ + | while read github_dir; do rm -rf "$github_dir"; done } do_strip() { return 0 } + +do_end() { + if [[ "$(readlink /usr/bin/env)" = "$(pkg_interpreter_for core/coreutils bin/env)" ]]; then + build_line "Removing the symlink we created for '/usr/bin/env'" + rm /usr/bin/env + fi +}