Repository: ubicloud/ubicloud
Pull request #3214: Ch+firmware selectable front matter
Author: login: fdr
Created: 2025-04-29T23:04:04Z | Merged: 2025-04-29T23:45:24Z

This  is maybe one third of the line churn of #3180, that I chipped out and condensed. From what I can tell, its deployment should yield no semantic changes. I did some minor editorialization, mostly on tiny affairs like converting allow() to expect() in cases where the test was constrained (so a mock is self-obsoleting if it goes unused)
<!-- ELLIPSIS_HIDDEN -->

----

> [!IMPORTANT]
> Refactor codebase to improve maintainability by introducing new classes, updating methods, and modifying tests without changing functionality.
> 
>   - **Behavior**:
>     - Replaces `COVERAGE=rhizome bundle exec rspec -O /dev/null rhizome` with `bundle exec rake rhizome_spec` in `.github/workflows/ci.yml`.
>     - Introduces `rhizome_spec` task in `Rakefile` to run rhizome tests.
>     - Updates `ArchClass` to use symbols for architecture detection and refactors `render` method.
>   - **Classes and Methods**:
>     - Moves `HASHES` constant outside `sha_256` method in `download_cloud_hypervisor.rb`.
>     - Refactors `CloudHypervisor` module to use `Firmware` and `Version` classes instead of `FirmwareClass` and `VersionClass`.
>     - Updates `package_url` method in `cert_server_setup.rb` and `spdk_setup.rb` to use `Arch.sym`.
>   - **Renames**:
>     - Renames `CloudHypervisor::FirmwareClass` to `CloudHypervisor::Firmware`.
>     - Renames `CloudHypervisor::VersionClass` to `CloudHypervisor::Version`.
>   - **Tests**:
>     - Updates tests in `cert_server_setup_spec.rb`, `cloud_hypervisor_spec.rb`, and `spdk_setup_spec.rb` to reflect refactored code.
>     - Removes `arch_class_spec.rb` as it is no longer needed.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=ubicloud%2Fubicloud&utm_source=github&utm_medium=referral)<sup> for 918c3363d9a7cb2c7fc27aa7768954ad127e307c. You can [customize](https://app.ellipsis.dev/ubicloud/settings/summaries) this summary. It will automatically update as commits are pushed.</sup>

<!-- ELLIPSIS_HIDDEN -->


Diff:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bb26d26a22..4136b7ab62 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -113,7 +113,7 @@ jobs:
       run: bundle exec rake
 
     - name: Run dataplane tests
-      run: COVERAGE=rhizome bundle exec rspec -O /dev/null rhizome
+      run: bundle exec rake rhizome_spec
 
     - name: Archive code coverage results
       if: failure()
diff --git a/Rakefile b/Rakefile
index b76b06e14f..a821414260 100644
--- a/Rakefile
+++ b/Rakefile
@@ -247,6 +247,11 @@ ensure
   File.delete(output_file) if File.file?(output_file)
 end
 
+desc "Run rhizome (data plane) tests"
+task "rhizome_spec" do
+  sh "COVERAGE=rhizome bundle exec rspec -O /dev/null rhizome"
+end
+
 desc "Update cli spec golden files"
 task "update_golden_files" do
   sh "mv spec/routes/api/cli/spec-output-files/*.txt spec/routes/api/cli/spec-output-files/.txt spec/routes/api/cli/golden-files/"
diff --git a/prog/download_cloud_hypervisor.rb b/prog/download_cloud_hypervisor.rb
index 022d816f43..a628206148 100644
--- a/prog/download_cloud_hypervisor.rb
+++ b/prog/download_cloud_hypervisor.rb
@@ -38,14 +38,18 @@ def sha256_ch_remote
     nap 15
   end
 
+  HASHES = {
+    ["ch-bin", "x64", "35.1"] => "e8426b0733248ed559bea64eb04d732ce8a471edc94807b5e2ecfdfc57136ab4",
+    ["ch-remote", "x64", "35.1"] => "337bd88183f6886f1c7b533499826587360f23168eac5aabf38e6d6b977c93b0",
+    ["ch-bin", "arm64", "35.1"] => "071a0b4918565ce81671ecd36d65b87351c85ea9ca0fbf73d4a67ec810efe606",
+    ["ch-remote", "arm64", "35.1"] => "355cdb1e2af7653a15912c66f7c76c922ca788fd33d77f6f75846ff41278e249",
+    ["ch-bin", "x64", "45.0"] => "362d42eb464e2980d7b41109a214f8b1518b4e1f8e7d8c227b67c19d4581c250",
+    ["ch-remote", "x64", "45.0"] => "11a050087d279f9b5860ddbf2545fda43edf93f9b266440d0981932ee379c6ec",
+    ["ch-bin", "arm64", "45.0"] => "3a8073379d098817d54f7c0ab25a7734b88b070a98e5d820ab39e244b35b5e5e",
+    ["ch-remote", "arm64", "45.0"] => "a4b736ce82f5e2fc4a92796a9a443f243ef69f4970dad1e1772bd841c76c3301"
+  }
+
   def sha_256(bin)
-    hashes = {
-      ["ch-bin", "x64", "35.1"] => "e8426b0733248ed559bea64eb04d732ce8a471edc94807b5e2ecfdfc57136ab4",
-      ["ch-remote", "x64", "35.1"] => "337bd88183f6886f1c7b533499826587360f23168eac5aabf38e6d6b977c93b0",
-      ["ch-bin", "arm64", "35.1"] => "071a0b4918565ce81671ecd36d65b87351c85ea9ca0fbf73d4a67ec810efe606",
-      ["ch-remote", "arm64", "35.1"] => "355cdb1e2af7653a15912c66f7c76c922ca788fd33d77f6f75846ff41278e249"
-    }
-
-    hashes[[bin, vm_host.arch, version]]
+    HASHES[[bin, vm_host.arch, version]]
   end
 end
diff --git a/rhizome/Gemfile.lock b/rhizome/Gemfile.lock
index 98250e6aeb..3eae400548 100644
--- a/rhizome/Gemfile.lock
+++ b/rhizome/Gemfile.lock
@@ -1,6 +1,7 @@
 GEM
   remote: https://rubygems.org/
   specs:
+    base64 (0.2.0)
     diff-lcs (1.5.0)
     netaddr (2.0.6)
     rspec (3.12.0)
@@ -22,6 +23,7 @@ PLATFORMS
   x86_64-linux
 
 DEPENDENCIES
+  base64
   netaddr
   rspec
 
diff --git a/rhizome/common/lib/arch_class.rb b/rhizome/common/lib/arch_class.rb
index 8226f2567d..7e0873d2f7 100644
--- a/rhizome/common/lib/arch_class.rb
+++ b/rhizome/common/lib/arch_class.rb
@@ -4,14 +4,14 @@
 
 ArchClass = Struct.new(:sym) {
   def self.from_system
-    new case RbConfig::CONFIG.fetch("target_cpu").downcase
-    when /arm64|aarch64/
-      "arm64"
-    when /amd64|x86_64|x64/
-      "x64"
+    new case RbConfig::CONFIG.fetch("target_cpu")
+    when /arm64|aarch64/i
+      :arm64
+    when /amd64|x86_64|x64/i
+      :x64
     else
       fail "BUG: could not detect architecture"
-    end.intern
+    end
   end
 
   def arm64?
@@ -22,13 +22,7 @@ def x64?
     sym == :x64
   end
 
-  def render(x64: sym, arm64: sym)
-    if x64?
-      x64
-    elsif arm64?
-      arm64
-    else
-      fail "BUG: could not detect architecture"
-    end.to_s
+  def render(x64:, arm64:)
+    {x64:, arm64:}.fetch(sym)
   end
 }
diff --git a/rhizome/common/spec/arch_class_spec.rb b/rhizome/common/spec/arch_class_spec.rb
index 6f3fadb3a5..439e3843c5 100644
--- a/rhizome/common/spec/arch_class_spec.rb
+++ b/rhizome/common/spec/arch_class_spec.rb
@@ -8,10 +8,5 @@
       expect(described_class.new(:x64).render(x64: "test-x64", arm64: "test-arm64")).to eq("test-x64")
       expect(described_class.new(:arm64).render(x64: "test-x64", arm64: "test-arm64")).to eq("test-arm64")
     end
-
-    it "has default representations for nil input to render" do
-      expect(described_class.new(:x64).render).to eq("x64")
-      expect(described_class.new(:arm64).render).to eq("arm64")
-    end
   end
 end
diff --git a/rhizome/host/Gemfile b/rhizome/host/Gemfile
index 2107c88c45..99fc247c0e 100644
--- a/rhizome/host/Gemfile
+++ b/rhizome/host/Gemfile
@@ -2,6 +2,8 @@
 
 source "https://rubygems.org"
 
+gem "base64"
+
 group :test do
   gem "rspec"
 end
diff --git a/rhizome/host/bin/download-cloud-hypervisor b/rhizome/host/bin/download-cloud-hypervisor
index 7130ff0448..4ae925c96e 100755
--- a/rhizome/host/bin/download-cloud-hypervisor
+++ b/rhizome/host/bin/download-cloud-hypervisor
@@ -19,4 +19,4 @@ unless (sha256_ch_remote = ARGV.shift)
   exit 1
 end
 
-CloudHypervisor::VersionClass.new(version, sha256_ch_bin, sha256_ch_remote).download
+CloudHypervisor::Version.new(version, sha256_ch_bin, sha256_ch_remote).download
diff --git a/rhizome/host/bin/download-firmware b/rhizome/host/bin/download-firmware
index 72d040eee0..eeeaa8e58a 100755
--- a/rhizome/host/bin/download-firmware
+++ b/rhizome/host/bin/download-firmware
@@ -14,4 +14,4 @@ unless (sha256 = ARGV.shift)
   exit 1
 end
 
-CloudHypervisor::FirmwareClass.new(version, sha256).download
+CloudHypervisor::Firmware.new(version, sha256).download
diff --git a/rhizome/host/bin/prep_host.rb b/rhizome/host/bin/prep_host.rb
index 9400192f92..635fd422fe 100755
--- a/rhizome/host/bin/prep_host.rb
+++ b/rhizome/host/bin/prep_host.rb
@@ -53,10 +53,10 @@
 r "timedatectl set-timezone UTC"
 
 # Download cloud hypervisor binaries.
-CloudHypervisor::VERSION.download
+CloudHypervisor::Version::DEFAULT.download
 
 # Download firmware binaries.
-CloudHypervisor::FIRMWARE.download
+CloudHypervisor::Firmware::DEFAULT.download
 
 # Err towards listing ('l') and not restarting services by default,
 # otherwise a stray keystroke when using "apt install" for unrelated
diff --git a/rhizome/host/lib/cert_server_setup.rb b/rhizome/host/lib/cert_server_setup.rb
index 39a4e0e1cb..c258b928f8 100644
--- a/rhizome/host/lib/cert_server_setup.rb
+++ b/rhizome/host/lib/cert_server_setup.rb
@@ -48,10 +48,8 @@ def vm_server_path
   end
 
   def package_url
-    Arch.render(
-      x64: "https://github.com/ubicloud/metadata-endpoint/releases/download/v#{server_version}/metadata-endpoint_Linux_x86_64.tar.gz",
-      arm64: "https://github.com/ubicloud/metadata-endpoint/releases/download/v#{server_version}/metadata-endpoint_Linux_arm64.tar.gz"
-    )
+    arch = Arch.render(x64: "x86_64", arm64: "arm64")
+    "https://github.com/ubicloud/metadata-endpoint/releases/download/v#{server_version}/metadata-endpoint_Linux_#{arch}.tar.gz"
   end
 
   def setup
diff --git a/rhizome/host/lib/cloud_hypervisor.rb b/rhizome/host/lib/cloud_hypervisor.rb
index 158ddf19db..95f167438d 100644
--- a/rhizome/host/lib/cloud_hypervisor.rb
+++ b/rhizome/host/lib/cloud_hypervisor.rb
@@ -4,7 +4,13 @@
 require_relative "../../common/lib/arch"
 
 module CloudHypervisor
-  FirmwareClass = Struct.new(:version, :sha256) {
+  class Firmware < Struct.new(:version, :sha256)
+    DEFAULT = if Arch.x64?
+      new("202311", "e31738aacd3d68d30f8f9a4d09711cca3dfb414e8910dc3af90c50f36885380a")
+    else
+      new("202211", "482f428f782591d7c2222e0bc8240d25fb200fb21fd984b3339c85979d94b4d8")
+    end
+
     def url
       "https://github.com/ubicloud/build-edk2-firmware/releases/download/edk2-stable#{version}-#{Arch.sym}/CLOUDHV-#{Arch.sym}.fd"
     end
@@ -20,25 +26,34 @@ def path
     def download
       return if File.exist?(path)
       FileUtils.mkdir_p(firmware_root)
-      sha256_curl = nil
+
       safe_write_to_file(path) do |f|
-        sha256_curl = curl_file(url, f.path)
-        fail "Invalid SHA-256 digest" unless sha256 == sha256_curl
+        unless curl_file(url, f.path) == sha256
+          fail "Invalid SHA-256 digest"
+        end
       end
-      sha256_curl
+
+      sha256
     end
-  }
+  end
 
-  FIRMWARE = FirmwareClass.new(Arch.render(x64: "202311", arm64: "202211"),
-    Arch.render(x64: "e31738aacd3d68d30f8f9a4d09711cca3dfb414e8910dc3af90c50f36885380a", arm64: "482f428f782591d7c2222e0bc8240d25fb200fb21fd984b3339c85979d94b4d8"))
+  class Version < Struct.new(:version, :sha256_ch_bin, :sha256_ch_remote)
+    DEFAULT = if Arch.x64?
+      new("35.1", "e8426b0733248ed559bea64eb04d732ce8a471edc94807b5e2ecfdfc57136ab4", "337bd88183f6886f1c7b533499826587360f23168eac5aabf38e6d6b977c93b0")
+    else
+      new("35.1", "071a0b4918565ce81671ecd36d65b87351c85ea9ca0fbf73d4a67ec810efe606", "355cdb1e2af7653a15912c66f7c76c922ca788fd33d77f6f75846ff41278e249")
+    end
+
+    def url_for(type)
+      "https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/v#{version}/#{type}-static#{"-aarch64" if Arch.arm64?}"
+    end
 
-  VersionClass = Struct.new(:version, :sha256_ch_bin, :sha256_ch_remote) {
     def ch_remote_url
-      "https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/v#{version}/ch-remote" + Arch.render(x64: "-static", arm64: "-static-aarch64")
+      url_for("ch-remote")
     end
 
     def cloud_hypervisor_url
-      "https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/v#{version}/cloud-hypervisor" + Arch.render(x64: "-static", arm64: "-static-aarch64")
+      url_for("cloud-hypervisor")
     end
 
     def dir
@@ -61,18 +76,14 @@ def download
     def download_file(url, path, sha256)
       return if File.exist?(path)
       FileUtils.mkdir_p(dir)
-      sha256_curl = nil
+
       safe_write_to_file(path) do |f|
-        sha256_curl = curl_file(url, f.path)
-        fail "Invalid SHA-256 digest" unless sha256 == sha256_curl
+        unless curl_file(url, f.path) == sha256
+          fail "Invalid SHA-256 digest"
+        end
       end
+
       FileUtils.chmod "a+x", path
     end
-  }
-
-  VERSION = if Arch.arm64?
-    VersionClass.new("35.1", "071a0b4918565ce81671ecd36d65b87351c85ea9ca0fbf73d4a67ec810efe606", "355cdb1e2af7653a15912c66f7c76c922ca788fd33d77f6f75846ff41278e249")
-  elsif Arch.x64?
-    VersionClass.new("35.1", "e8426b0733248ed559bea64eb04d732ce8a471edc94807b5e2ecfdfc57136ab4", "337bd88183f6886f1c7b533499826587360f23168eac5aabf38e6d6b977c93b0")
   end
 end
diff --git a/rhizome/host/lib/spdk_setup.rb b/rhizome/host/lib/spdk_setup.rb
index 08894bcb68..18e081b61e 100644
--- a/rhizome/host/lib/spdk_setup.rb
+++ b/rhizome/host/lib/spdk_setup.rb
@@ -50,17 +50,9 @@ def rpc_sock
   end
 
   def package_url(os_version:)
-    arch = if Arch.arm64?
-      "arm64"
-    elsif Arch.x64?
-      "x64"
-    else
-      fail "BUG: unexpected architecture"
-    end
-
     case @spdk_version
     when "v23.09-ubi-0.3"
-      "https://github.com/ubicloud/bdev_ubi/releases/download/spdk-23.09-ubi-0.3/ubicloud-spdk-#{os_version}-#{arch}.tar.gz"
+      "https://github.com/ubicloud/bdev_ubi/releases/download/spdk-23.09-ubi-0.3/ubicloud-spdk-#{os_version}-#{Arch.sym}.tar.gz"
     else
       fail "BUG: unsupported SPDK version"
     end
diff --git a/rhizome/host/lib/vm_setup.rb b/rhizome/host/lib/vm_setup.rb
index 144e18607d..0909442371 100644
--- a/rhizome/host/lib/vm_setup.rb
+++ b/rhizome/host/lib/vm_setup.rb
@@ -686,9 +686,9 @@ def install_systemd_unit(max_vcpus, cpu_topology, mem_gib, storage_params, nics,
 NetworkNamespacePath=/var/run/netns/#{@vm_name}
 ExecStartPre=/usr/bin/rm -f #{vp.ch_api_sock}
 
-ExecStart=#{CloudHypervisor::VERSION.bin} -v \
+ExecStart=#{CloudHypervisor::Version::DEFAULT.bin} -v \
 --api-socket path=#{vp.ch_api_sock} \
---kernel #{CloudHypervisor::FIRMWARE.path} \
+--kernel #{CloudHypervisor::Firmware::DEFAULT.path} \
 #{disk_params.join("\n")}
 --disk path=#{vp.cloudinit_img} \
 --console off --serial file=#{vp.serial_log} \
@@ -697,7 +697,7 @@ def install_systemd_unit(max_vcpus, cpu_topology, mem_gib, storage_params, nics,
 #{pci_device_params} \
 #{net_params.join(" \\\n")}
 
-ExecStop=#{CloudHypervisor::VERSION.ch_remote_bin} --api-socket #{vp.ch_api_sock} shutdown-vmm
+ExecStop=#{CloudHypervisor::Version::DEFAULT.ch_remote_bin} --api-socket #{vp.ch_api_sock} shutdown-vmm
 Restart=no
 User=#{@vm_name}
 Group=#{@vm_name}
diff --git a/rhizome/host/spec/cert_server_setup_spec.rb b/rhizome/host/spec/cert_server_setup_spec.rb
index d89c607db9..6c1010fb5e 100644
--- a/rhizome/host/spec/cert_server_setup_spec.rb
+++ b/rhizome/host/spec/cert_server_setup_spec.rb
@@ -44,7 +44,7 @@
 
   describe "#download_server" do
     it "downloads the server, extracts it, and removes the tarball" do
-      expect(Arch).to receive(:render).with(x64: "https://github.com/ubicloud/metadata-endpoint/releases/download/v0.1.5/metadata-endpoint_Linux_x86_64.tar.gz", arm64: "https://github.com/ubicloud/metadata-endpoint/releases/download/v0.1.5/metadata-endpoint_Linux_arm64.tar.gz").and_return("https://github.com/ubicloud/metadata-endpoint/releases/download/v0.1.5/metadata-endpoint_Linux_arm64.tar.gz")
+      expect(Arch).to receive(:render).with(x64: "x86_64", arm64: "arm64").and_return("arm64")
       expect(cert_server_setup).to receive(:r).with("curl -L3 -o /tmp/metadata-endpoint-0.1.5.tar.gz https://github.com/ubicloud/metadata-endpoint/releases/download/v0.1.5/metadata-endpoint_Linux_arm64.tar.gz")
       expect(FileUtils).to receive(:mkdir_p).with("/opt/metadata-endpoint-0.1.5")
       expect(FileUtils).to receive(:cd).with("/opt/metadata-endpoint-0.1.5")
@@ -53,7 +53,7 @@
     end
 
     it "downloads the server for x64" do
-      expect(Arch).to receive(:render).with(x64: "https://github.com/ubicloud/metadata-endpoint/releases/download/v0.1.5/metadata-endpoint_Linux_x86_64.tar.gz", arm64: "https://github.com/ubicloud/metadata-endpoint/releases/download/v0.1.5/metadata-endpoint_Linux_arm64.tar.gz").and_return("https://github.com/ubicloud/metadata-endpoint/releases/download/v0.1.5/metadata-endpoint_Linux_x86_64.tar.gz")
+      expect(Arch).to receive(:render).with(x64: "x86_64", arm64: "arm64").and_return("x86_64")
       expect(cert_server_setup).to receive(:r).with("curl -L3 -o /tmp/metadata-endpoint-0.1.5.tar.gz https://github.com/ubicloud/metadata-endpoint/releases/download/v0.1.5/metadata-endpoint_Linux_x86_64.tar.gz")
       expect(FileUtils).to receive(:mkdir_p).with("/opt/metadata-endpoint-0.1.5")
       expect(FileUtils).to receive(:cd).with("/opt/metadata-endpoint-0.1.5")
diff --git a/rhizome/host/spec/cloud_hypervisor_spec.rb b/rhizome/host/spec/cloud_hypervisor_spec.rb
index 6e85e1b2dc..1cd64dc043 100644
--- a/rhizome/host/spec/cloud_hypervisor_spec.rb
+++ b/rhizome/host/spec/cloud_hypervisor_spec.rb
@@ -4,7 +4,7 @@
 
 RSpec.describe CloudHypervisor do
   context "when downloading firmware" do
-    subject(:fw) { CloudHypervisor::FirmwareClass.new("202402", "thesha") }
+    subject(:fw) { CloudHypervisor::Firmware.new("202402", "thesha") }
 
     let(:gh_fw_url) { "https://github.com/ubicloud/build-edk2-firmware/releases/download/edk2-stable202402-x64/CLOUDHV-x64.fd" }
     let(:firmware_path) { "/opt/fw/CLOUDHV-202402.fd" }
@@ -41,7 +41,7 @@
   end
 
   context "when downloading cloud-hypervisor" do
-    subject(:ch) { CloudHypervisor::VersionClass.new("35.1", "sha_ch", "sha_remote") }
+    subject(:ch) { CloudHypervisor::Version.new("35.1", "sha_ch", "sha_remote") }
 
     let(:gh_ch_remote_url) { "https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/v35.1/ch-remote-static" }
     let(:gh_ch_bin_url) { "https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/v35.1/cloud-hypervisor-static" }
diff --git a/rhizome/host/spec/spdk_setup_spec.rb b/rhizome/host/spec/spdk_setup_spec.rb
index 5f598251ba..e99e9fad49 100644
--- a/rhizome/host/spec/spdk_setup_spec.rb
+++ b/rhizome/host/spec/spdk_setup_spec.rb
@@ -32,18 +32,13 @@
   end
 
   describe "#package_url" do
-    it "fails if arch is unexpected" do
-      allow(Arch).to receive_messages(arm64?: false, x64?: false)
-      expect { spdk_setup.package_url(os_version: "ubuntu-22.04") }.to raise_error RuntimeError, "BUG: unexpected architecture"
-    end
-
     it "returns a valid url for x64" do
-      allow(Arch).to receive_messages(arm64?: false, x64?: true)
+      expect(Arch).to receive(:sym).and_return(:x64)
       expect(spdk_setup.package_url(os_version: "ubuntu-22.04")).to match(/https.*x64.*tar.gz/)
     end
 
     it "returns a valid url for arm64" do
-      allow(Arch).to receive_messages(arm64?: true, x64?: false)
+      expect(Arch).to receive(:sym).and_return(:arm64)
       expect(spdk_setup.package_url(os_version: "ubuntu-22.04")).to match(/https.*arm64.*tar.gz/)
     end
   end
@@ -51,7 +46,8 @@
   describe "#install_package" do
     it "can install the package" do
       expect(spdk_setup).to receive(:package_url).and_return("package_url")
-      allow(spdk_setup).to receive(:install_path).and_return("install_path")
+      expect(spdk_setup).to receive(:puts).with("Downloading SPDK package from package_url")
+      expect(spdk_setup).to receive(:install_path).and_return("install_path").at_least(:once)
       expect(spdk_setup).to receive(:r).with(/curl -L3 -o .* package_url/)
       expect(FileUtils).to receive(:mkdir_p).with("install_path")
       expect(FileUtils).to receive(:cd).with("install_path")


Reviews:
user: login: ellipsis-dev[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T23:12:56Z
user: login: fdr
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-29T23:16:25Z
user: login: jeremyevans
body: Looks good.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T23:30:50Z

Review comments:
diff_hunk: @@ -22,13 +22,7 @@ def x64?
     sym == :x64
   end
 
-  def render(x64: sym, arm64: sym)
-    if x64?
-      x64
-    elsif arm64?
-      arm64
-    else
-      fail "BUG: could not detect architecture"
-    end.to_s
+  def render(x64:, arm64:)
path: rhizome/common/lib/arch_class.rb
user: login: ellipsis-dev[bot]
body: Consider adding unit tests to cover the new behavior of the updated `#render` method, which now uses explicit keyword arguments and returns a value based on the symbol, since the previous test file was removed.

created_at: 2025-04-29T23:12:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,13 +22,7 @@ def x64?
     sym == :x64
   end
 
-  def render(x64: sym, arm64: sym)
-    if x64?
-      x64
-    elsif arm64?
-      arm64
-    else
-      fail "BUG: could not detect architecture"
-    end.to_s
+  def render(x64:, arm64:)
path: rhizome/common/lib/arch_class.rb
user: login: fdr
body: Meh, probably okay, or at least, that's how I interpreted the file being deleted upstream in a460ddb1d6583059cca4b6d70b54eef74af2313e
created_at: 2025-04-29T23:16:25Z
author_association: COLLABORATOR
diff_hunk: @@ -22,13 +22,7 @@ def x64?
     sym == :x64
   end
 
-  def render(x64: sym, arm64: sym)
-    if x64?
-      x64
-    elsif arm64?
-      arm64
-    else
-      fail "BUG: could not detect architecture"
-    end.to_s
+  def render(x64:, arm64:)
path: rhizome/common/lib/arch_class.rb
user: login: jeremyevans
body: The deletion of the tests for the method was because the method was removed (then added back in a later commit).  Since it isn't being removed, I don't think it is necessary to delete the tests.
created_at: 2025-04-29T23:28:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: jeremyevans
created_at: 2025-04-29T23:20:30Z
body: This looks like this is based on an old version of the `jeremy-ch-upgrade` branch, as it is missing the features added by the commits to that branch yesterday and this morning.
author_association: CONTRIBUTOR
user: login: fdr
created_at: 2025-04-29T23:25:34Z
body: > This looks like this is based on an old version of the `jeremy-ch-upgrade` branch, as it is missing the features added by the commits to that branch yesterday and this morning.

well it's definitely short on features, because it's "front matter," I was trying to shake out less intertwined parts of the patch, since the original branch kind of wavers back and forth as I requested changes or easily separate opportunistic changes.
author_association: COLLABORATOR
user: login: fdr
created_at: 2025-04-29T23:33:36Z
body: re: tests, okay, I'll bring them back in and update them a bit
author_association: COLLABORATOR