Add platform-specific gem dependencies - #44
Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses platform-specific runtime dependencies by publishing separate gem artifacts per platform (Ruby, universal-linux, universal-mingw-ucrt) so RubyGems can select the correct dependency set at install time, and adds CI/test coverage around artifact validation.
Changes:
- Introduces platform-specific gemspecs for universal-linux and universal-mingw-ucrt and removes platform-conditional deps from the main gemspec.
- Adds Rake tasks and a new packaging-focused test suite to build and validate gem artifacts (platform + runtime deps).
- Updates CI to build/check artifacts and updates docs/changelog to reflect platform-specific dependency selection.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test_net_ping.rb | Runs the new gem packaging test suite as part of the overall test entrypoint. |
| test/test_net_ping_gem_packaging.rb | Adds tests that build artifacts, validate platform/runtime deps, and exercise new Rake tasks. |
| README.md | Updates prerequisites text to reflect platform-selected dependencies via gem artifacts. |
| Rakefile | Adds multi-artifact build, validation (gem:check), and install logic for platform-specific artifacts. |
| net-ping.gemspec | Removes platform-conditional runtime deps and excludes generated .gem files from spec.files. |
| net-ping-universal-linux.gemspec | Adds a Linux platform gemspec that includes cap2 at runtime. |
| net-ping-universal-mingw-ucrt.gemspec | Adds a Windows (mingw-ucrt) platform gemspec that includes win32-security at runtime. |
| Gemfile | Ensures Windows development/test installs include win32-security. |
| CHANGES | Documents the upcoming release behavior change for platform-specific dependency selection. |
| .github/workflows/test.yml | Extends CI to build and validate gem artifacts after tests. |
Comments suppressed due to low confidence (1)
Rakefile:76
gem:checkcurrently trusts the artifact filename from EXPECTED_GEM_ARTIFACTS and doesn’t verify the artifact exists. If the artifact name changes (e.g., version bump) or artifacts weren’t built, this will raise a less helpful exception. It’s more robust to pair loaded specs with expectations by gemspec path and usespec.file_nameas the artifact to validate (and abort with a clear message if missing).
EXPECTED_GEM_ARTIFACTS.each do |_, expectation|
artifact, expected_platform, expected_dependencies = expectation
packaged_spec = Gem::Package.new(artifact).spec
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3ab0e2d to
ca31ae2
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
README.md:7
- The Windows platform gem now installs both
win32-securityandwin32ole(per the mingw-ucrt gemspec and packaging expectations), but the prerequisites list only mentionswin32-security. Addingwin32olehere keeps the documentation aligned with what RubyGems will actually install for Windows users (and avoids confusion when WMI ping functionality requires it).
* win32-security (installed from the Windows gem artifact)
* cap2 (installed from the Linux gem artifact)
726c343 to
bc8e0ca
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Rakefile:85
gem:checkcallsGem::Specification.load(path).file_namewithout checking whether the gemspec loaded successfully and whether the artifact exists. If a gemspec can’t be loaded or artifacts weren’t built, this will raise aNoMethodError/IO error instead of failing with a clear, actionable message.
EXPECTED_GEM_METADATA.each do |path, expectation|
expected_platform, expected_dependencies = expectation
artifact = Gem::Specification.load(path).file_name
packaged_spec = Gem::Package.new(artifact).spec
README.md:7
- The prerequisites list documents the platform-selected dependencies, but it omits
win32ole, which is now also installed from the Windows gem artifact (and is required bylib/net/ping/wmi.rb). This can mislead Windows users about what gets installed automatically.
* win32-security (installed from the Windows gem artifact)
* cap2 (installed from the Linux gem artifact)
bc8e0ca to
6a0d20e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (4)
net-ping-universal-linux.gemspec:1
- The overlay gemspec loads the base gemspec using a relative path. If someone runs
gem buildfrom a different working directory,Gem::Specification.load('net-ping.gemspec')may fail to find the base spec. Use a path relative to this file (__dir__) so builds are deterministic regardless of CWD.
spec = Gem::Specification.load('net-ping.gemspec').dup
net-ping-universal-mingw-ucrt.gemspec:1
- The overlay gemspec loads the base gemspec using a relative path. If someone runs
gem buildfrom a different working directory,Gem::Specification.load('net-ping.gemspec')may fail to find the base spec. Use a path relative to this file (__dir__) so builds are deterministic regardless of CWD.
spec = Gem::Specification.load('net-ping.gemspec').dup
Rakefile:86
gem:checkassumesGem::Specification.load(path)always succeeds; if a gemspec is missing or fails to load, this will raise aNoMethodErroronfile_nameinstead of failing with a clear message. Abort explicitly when a gemspec can't be loaded.
EXPECTED_GEM_METADATA.each do |path, expectation|
expected_platform, expected_dependencies = expectation
artifact = Gem::Specification.load(path).file_name
packaged_spec = Gem::Package.new(artifact).spec
actual_dependencies = packaged_spec.runtime_dependencies.each_with_object({}) do |dependency, memo|
Rakefile:63
- The task description says it "Create[s] the net-ping gem", but the task now builds three platform-specific artifacts. Updating the description helps avoid confusion when discovering tasks via
rake -T.
desc 'Create the net-ping gem'
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6a0d20e to
2ff75bf
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (2)
test/test_net_ping_gem_packaging.rb:50
omitinside the per-spec loop aborts the entire test on the first non-round-trippable platform string, which also skips the runtime-dependency assertions for the remaining artifacts. This can let dependency metadata regressions slip by on older RubyGems versions.
Instead of omitting the whole test, skip only the platform assertion when round-tripping is unsupported and still assert the dependency hash.
if platform_round_trips_for_test?(expected_platform)
assert_equal(expected_platform, packaged_spec.platform.to_s, path)
else
omit("this RubyGems (#{Gem::VERSION}) cannot round-trip the #{expected_platform.inspect} platform string")
end
Rakefile:88
gem:checkassumesGem::Specification.load(path)always returns a spec. If a gemspec is missing/invalid (or someone runsrake gem:checkwithout having all files present), this will raiseNoMethodErroronnil.file_namerather than failing with a clear message.
Fail fast with an explicit abort when the gemspec can't be loaded, and also consider a clear error when the built artifact file is missing.
EXPECTED_GEM_METADATA.each do |path, expectation|
expected_platform, expected_dependencies = expectation
artifact = Gem::Specification.load(path).file_name
packaged_spec = Gem::Package.new(artifact).spec
actual_dependencies = packaged_spec.runtime_dependencies.each_with_object({}) do |dependency, memo|
Ruby 2.7's bundled RubyGems doesn't recognize the "mingw-ucrt" OS token, so re-parsing the platform string during gem packaging normalizes it to "universal-unknown" instead of "universal-mingw-ucrt". Skip that specific platform check when the running RubyGems can't round-trip the string, in both gem:check and its test coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2ff75bf to
a1556a2
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Rakefile:85
gem:checkcallsGem::Specification.load(path).file_namewithout checking whether the gemspec actually loaded. IfGem::Specification.loadreturns nil (missing file, syntax error, etc.), this will raise aNoMethodErrorand produce a hard-to-read stack trace instead of a clear failure.
Load the spec once, abort with a clear message when it can’t be loaded, and optionally fail fast when the expected artifact file is missing.
EXPECTED_GEM_METADATA.each do |path, expectation|
expected_platform, expected_dependencies = expectation
artifact = Gem::Specification.load(path).file_name
packaged_spec = Gem::Package.new(artifact).spec
README.md:8
- The prerequisites list now mentions platform-specific gems, but it doesn’t explicitly state that RubyGems will automatically select the correct platform-specific artifact during
gem install net-ping. As written, it can be read as “install these gems manually on every platform”, which contradicts the goal of making dependency selection automatic.
Consider rewording this section to describe the automatic selection, and list the platform-specific dependencies per platform.
## Prerequisites
* ffi
* win32-security (installed from the Windows gem artifact)
* win32ole (installed from the Windows gem artifact)
* cap2 (installed from the Linux gem artifact)
Resolves #31.
Publishes separate Ruby, Linux, and mingw-ucrt gem artifacts so ICMP dependencies are selected by platform. Adds artifact metadata validation to CI and packaging tests.
Spec
Platform-specific gem dependencies
Goal
Resolve issue #31 by publishing platform-specific
net-pinggems whoseruntime dependencies accurately describe the requirements for ICMP support.
The solution also corrects the equivalent Linux
cap2metadata omission.Scope
The release produces three gems with the same name and version:
rubyuniversal-linuxcap2 (>= 0.2.2)universal-mingw-ucrtwin32-security (>= 0.2.0),win32ole (>= 1.8.8)Only the current RubyInstaller
mingw-ucrtplatform is supported for Windows.Legacy
mingw32is out of scope.The CI workflow builds and validates these gems, but does not publish them.
The existing release process remains responsible for pushing all three files
to RubyGems.
Design
net-ping.gemspecbecomes the OS-independent Ruby gemspec. It must notinspect the host OS or add OS-specific runtime dependencies.
net-ping-universal-linux.gemspecloads the base specification, changes itsplatform to
universal-linux, and addscap2. The platform is intentionallyCPU-independent so RubyGems can select it for supported Linux CPU variants.
net-ping-universal-mingw-ucrt.gemspecloads the same base specification,changes its platform to
universal-mingw-ucrt, and addswin32-security.The Rake gem namespace explicitly enumerates the three gemspecs.
gem:createcleans old gem artifacts and builds all three.
gem:installselects thespecific generated specification compatible with the local platform, falling
back to the Ruby specification when no platform-specific specification
matches, then installs its artifact.
Verification
Add
gem:check, which reads each generated gem through RubyGems and failswhen its platform or runtime dependencies differ from this specification:
ruby; neithercap2norwin32-security.universal-linux; includescap2.universal-mingw-ucrt; includeswin32-security.The existing GitHub Actions matrix continues to run the test suite. It also
runs
gem:createandgem:checkon both Ubuntu and Windows. A missing,malformed, or incorrectly attributed dependency causes the job to fail.
Documentation
Update the prerequisite/install documentation to state that RubyGems chooses
the platform-specific package and installs the Linux or Windows ICMP
dependency automatically.
Add a
CHANGESentry describing the corrected platform-specific dependencymetadata.
Non-goals
mingw32.Plan
Platform-specific Gem Dependencies Implementation Plan
Goal: Build and validate RubyGems artifacts that install
cap2on Linux andwin32-securityon current Windows RubyInstaller systems.Architecture: Keep
net-ping.gemspecOS-independent and create two small gemspec overlays for Linux andmingw-ucrt. Centralize artifact generation and metadata validation in the Rake gem namespace, then run that validation in the existing Linux/Windows CI matrix.Tech Stack: Ruby, RubyGems (
Gem::Specification,Gem::Package,Gem::Platform), Rake, test-unit, GitHub Actions.Global Constraints
net-pinggems:ruby,universal-linux, anduniversal-mingw-ucrt.universal-linuxmust addcap2 (>= 0.2.2);universal-mingw-ucrtmust addwin32-security (>= 0.2.0).mingw-ucrtonly; legacymingw32is out of scope.File Structure
net-ping.gemspecnet-ping-universal-linux.gemspecuniversal-linuxand thecap2runtime dependency.net-ping-universal-mingw-ucrt.gemspecuniversal-mingw-ucrtand thewin32-securityruntime dependency.Rakefiletest/test_net_ping_gem_packaging.rbtest/test_net_ping.rb.github/workflows/test.ymlREADME.mdCHANGESTask 1: Define and test platform gem specifications
Files:
net-ping-universal-linux.gemspecnet-ping-universal-mingw-ucrt.gemspectest/test_net_ping_gem_packaging.rbnet-ping.gemspec:1-40test/test_net_ping.rbInterfaces:
Consumes:
net-ping.gemspec, which supplies the sharedGem::Specification.Produces: three loadable gemspecs whose
platformandruntime_dependenciesdescribe their target platform.Step 1: Write the failing package-metadata test
Create
test/test_net_ping_gem_packaging.rb. Load every gemspec before building any artifact sospec.filescannot include an artifact created for an earlier specification. Build the loaded specifications into temporary.gemfiles, open each usingGem::Package, and assert the platform and runtime dependencies:Add
require 'test_net_ping_gem_packaging'totest/test_net_ping.rb.Run:
bundle exec ruby -Itest test/test_net_ping_gem_packaging.rbExpected: failure because the Linux and Windows overlay gemspec files do not exist.
Remove the
File::ALT_SEPARATOR,RbConfig, andRUBY_PLATFORMconditional runtime dependency logic fromnet-ping.gemspec; leave only shared metadata and development dependencies.Create
net-ping-universal-linux.gemspec:Create
net-ping-universal-mingw-ucrt.gemspec:Use the project’s existing block-style
Gem::Specification.newformat for the base gemspec. Verify the installed RubyGems version accepts the two platform strings withGem::Platform.new.Run:
bundle exec ruby -Itest test/test_net_ping_gem_packaging.rbExpected: PASS; all three artifacts have the expected platform and exactly the expected runtime dependencies.
Run:
Expected: PASS.
Task 2: Make Rake build, validate, and install platform artifacts deterministically
Files:
Rakefile:6-29Interfaces:
Consumes: the three gemspec paths from Task 1.
Produces:
gem:createbuilds all artifacts;gem:checkaborts on invalid artifact metadata;gem:installinstalls the local platform’s specific artifact or the Ruby fallback.Step 1: Add a failing artifact-validation command
Add
gem:checkto thegemnamespace. It must load the three expected gem files withGem::Package, map each runtime dependency toname => requirement.to_s, and abort if the platform or dependency hash differs from:Run it before changing
gem:create:bundle exec rake clean gem:create gem:checkExpected: FAIL because
gem:createstill builds only one artifact.Define the shared list once in
Rakefile:Update
gem:createto load all files before it builds any artifact:Retain the existing RubyGems pre-2.0 builder branch if support for it is still required. In either branch, load every specification before beginning the build loop.
Implement
gem:checkusingGem::Package.new(file).spec; useabortwith the artifact filename and expected/actual values when a check fails.Update
gem:installto load the same specification list and select:Install
spec.file_name, notDir['*.gem'].first; abort when no Ruby fallback specification is found.Run:
bundle exec rake gem:create gem:checkExpected: PASS and creation of
net-ping-<version>.gem,net-ping-<version>-universal-linux.gem, andnet-ping-<version>-universal-mingw-ucrt.gem.Run in a temporary gem repository so the developer's normal gem installation
is unchanged:
Expected on Linux:
universal-linux; on Windows:universal-mingw-ucrt; onother platforms:
ruby.Run:
Expected: PASS.
Task 3: Enforce packaging in CI and document the behavior
Files:
.github/workflows/test.yml:19-28README.md:4-15CHANGES:1-13Interfaces:
Consumes:
gem:createandgem:checkfrom Task 2.Produces: CI verification on both matrix operating systems and accurate end-user dependency documentation.
Step 1: Add the CI packaging verification step
After the existing
bundle exec rake teststep, add:Do not add RubyGems credentials, publishing actions, or tag triggers.
Replace the prerequisite list’s unconditional
win32-security (MS Windows only)wording with text that states RubyGems installswin32-securityfromthe Windows artifact and
cap2from the Linux artifact when installingnet-ping. Keep the installation commandgem install net-ping.At the beginning of
CHANGES, add:Run:
Expected: both commands PASS.
Run:
Expected: only the planned packaging, CI, test, and documentation changes;
no whitespace errors and no git commit.
Plan Self-Review
their dependencies; Task 2 validates metadata and selects a local artifact;
Task 3 runs validation in CI and documents the behavior. CI publishing,
legacy
mingw32, and runtime protocol changes are excluded.implementation step remains.
versions, and Rake task names are identical across all tasks.