Skip to content

kettle-jem: harden macOS runners in the current.yml workflow template - #109

Merged
pboling merged 8 commits into
mainfrom
kettle-jem-macos-hardening
Nov 4, 2026
Merged

pboling merged 8 commits into
mainfrom
kettle-jem-macos-hardening

Conversation

@pboling

@pboling pboling commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Companion to structuredmerge/structuredmerge#24, for the workflows kettle-jem generates.

Apple's /etc/ssl/cert.pem still carries eMudhra-related roots and Apple's git/curl/python3 read it (galadran/we-have-a-problem.com#157). macOS lanes in generated CI therefore start by removing them.

  • Only current.yml.example has macOS lanes (its OS matrix comes from the project's platforms setting; every other workflow template is hard-coded to Ubuntu), so that is the only template changed.
  • New token KJ|CI:MACOS_HARDENING_STEP, filled by github_actions_macos_hardening_step: an inline, macOS-only first step before checkout (checkout runs git). Like the Windows guard, it is emitted only when macos is a declared platform (including the default linux, macos), and resolves to nothing otherwise, so no dead if: and no stray blank line.
  • Script packaged at lib/kettle/jem/ci/harden_macos_ca_bundle.sh (copy of the one in structuredmerge/structuredmerge, tested there against a bundle containing an eMudhra root): matches by subject, logs each removed fingerprint, refuses an implausibly small bundle, verifies the on-disk result and fails if a matching cert remains.

Specs: 6 new in jem_platforms_config_spec.rb (empty without macos; step shape; inlined script verbatim and valid YAML; wired before checkout in the template; resolved workflow order; unchanged output without macos) plus the two existing specs that resolve the template's tokens now supply the new token. kettle-jem suite: 773 examples, 4 failures, the same 4 that fail without this change here because they read /code/fixtures.

Not done / caveats: the script has only run on Linux (BSD awk / LibreSSL unverified until a real macOS runner executes a generated workflow; the structuredmerge dry run in #24 exercises the same script). Existing destinations pick this up on their next templating run. The family's own CI (current.yml in this repo and the 34 gems) is not changed here.

Adds a first, macOS-only step (before checkout, since checkout runs git) that removes the eMudhra-related roots from /etc/ssl/cert.pem, generated only when macos is a declared platform so no dead step is emitted otherwise. The script is packaged under lib/kettle/jem/ci and inlined into the workflow; it matches by subject, logs removed fingerprints and verifies the result.
…p self-gem deletion

Two CI fixes, verified locally against a built 0.2.1 core gem:

- benchmark-dev and tslp-cold-start failed in Setup Ruby: tree_haver's
  runtime dependency structuredmerge-core ~> 0.2 no longer resolves from
  any registry (the kernel is pre-publication by design), so
  "tree_haver cannot be used" aborted appraisal install. Both lanes now
  checkout the kernel at main, compile its Ruby binding, and set
  STRUCTUREDMERGE_CORE_DEV, which is the documented selection switch
  (TYPED_CORE_CI.md) that wires core through nomono from packages/ruby.

- structural_merge_template_spec failed: gemfile_path_source_override_names
  exempted every path-declared gem in a template from dependency pruning,
  including the package's own gem, so 'gem "example", path: "."' survived
  and delete_dependency_declarations stopped reporting it. The exemption
  now never covers the package's own gem, in both the aggregate and the
  modular template policy (introduced by 35f6900/4eb138300 on Oct 31).

Local evidence: 773-example kettle-jem suite passes except the three
documented /code/fixtures-environmental specs; appraisals_and_gemfile_template_spec
76/76; the failing structural spec passes; .github/tests 9/9.
bundle is only on PATH once setup-ruby-flash runs; the compile step
before it aborted with exit 127 on both lanes.
The ast-merge, bash-merge, go-merge, json-merge, rust-merge and
typescript-merge suites reference fixture files and contract updates
that landed in structuredmerge-fixtures after the pinned revision
(ad5fd0b6): 16 files were missing outright and source_line_disposition.json
was 51/23 lines behind the shared per-line rubric the suites assert.
Advance the pin to fixtures main (9c6e4bb0).
The previous fix compiled the kernel binding and set
STRUCTUREDMERGE_CORE_DEV, but only the appraisal gemfiles (which eval
templating_local.gemfile) honor that switch. Setup Ruby resolves
Appraisal.root.gemfile first, and tree_haver's runtime dependency on
structuredmerge-core ~> 0.2 still raced the registry. Add the same
path wiring to both root baselines so the root install resolves core
from the kernel checkout's packages/ruby.
The previous commit hand-rolled a STRUCTUREDMERGE_CORE_DEV path block
into both Appraisal.root.gemfiles — reimplementing what kettle-jem
already templates. ast-crispr-toml's root gemfile is the established
shape (70d776c, ef07e57, dbd43d7): a generator_only/K_JEM_TEMPLATING
gate evals gemfiles/modular/templating.gemfile, and a frozen plain-Bundler
copy does the same when STRUCTUREDMERGE_DEV is engaged, because Appraisal's
root resolution runs without generator_only. templating.gemfile then routes
STRUCTUREDMERGE_CORE_DEV through templating_local's nomono subdirs wiring.

Bring tree_haver's and ast-merge-git's root baselines to that shape
instead of duplicating the wiring inline. Both now resolve
structuredmerge-core from the kernel checkout under CI's env, and both
keep registry-only resolution when the toggles are unset.

Verified locally: toggles on, `bundle lock` resolves core 0.2.1 from the
kernel path in both gems; toggles off, both still resolve.
The templating eval now activates templating_local.gemfile in the root
baseline, and its bootstrap `Kernel.send(:gem, 'nomono', ...)` requires
nomono >= 1.1.18 while the runner's rv GEM_PATH only carries 1.1.14.
The gem-suite and deps lanes already solve this through setup-ruby-flash's
`pre-bundle-gems: nomono --source ...`; add the same to the TSLP and
benchmark lanes (mirrors 1810f87's "install nomono before Gemfile
evaluation" for the contract tests).
The job-level BUNDLE_GEMFILE (the appraisal root baseline) leaked into
the compile step, so its bundle install built the consumer bundle
instead of rake-compiler: 'Please install rake-compiler'. Pin the step's
BUNDLE_GEMFILE to kernel/packages/ruby/Gemfile, mirroring how the
typed-core job compiles.
@pboling
pboling merged commit 61fccc5 into main Nov 4, 2026
109 checks passed
@pboling
pboling deleted the kettle-jem-macos-hardening branch November 4, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant