Skip to content

Publish only a whitelist of files in the gem - #52

Merged
Fivell merged 4 commits into
masterfrom
chore/trim-packaged-gem
Sep 30, 2026
Merged

Fivell merged 4 commits into
masterfrom
chore/trim-packaged-gem

Conversation

@Fivell

@Fivell Fivell commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

What

files is now an explicit whitelist, instead of publishing everything tracked minus whatever someone remembered to exclude.

`git ls-files -z -- lib exe README.md LICENSE.txt CHANGELOG.md`.split("\x0")

A reject list only removes what someone named. That is how CI config, the VuePress site ended up in the published gem — each needed its own pattern, and none was added until an audit went looking. A whitelist inverts the default: a new directory does not reach consumers until it is listed.

Every root Rails::Engine loads from is listed, present in this repo or not. A whitelist fails quietly — git ls-files -- config against a tree with no config/ exits 0 and prints nothing — so the day someone adds config/initializers/foo.rb the gem would install, boot, and never run it. Naming the roots up front costs nothing (git ls-files on a missing path is a no-op) and removes that trapdoor.

Selected through git ls-files rather than Dir[...] so the artifact stays tracked-only — an untracked or generated file under lib/ cannot leak into a release — and so files carries no directory entries.

Verified

$ gem build capybara_active_admin.gemspec
46 files / 20K   ->   29 files / 16K

All 17 files that leave the package:

.github/workflows/ci.yml
.github/workflows/codeql.yml
.gitignore
.rspec
.rubocop.yml
CODE_OF_CONDUCT.md
Gemfile
Rakefile
bin/console
bin/setup
capybara_active_admin.gemspec
docs/.vuepress/config.js
docs/.vuepress/public/.keep
docs/.vuepress/styles/index.styl
docs/README.md
docs/guide/README.md
package.json

Nothing is added. The 26 files under lib/ — the entire runtime payload — are unchanged:

lib/capybara_active_admin.rb
lib/capybara/active_admin.rb
lib/capybara/active_admin/actions.rb
lib/capybara/active_admin/actions/attributes_table.rb
lib/capybara/active_admin/actions/form.rb
lib/capybara/active_admin/actions/layout.rb
lib/capybara/active_admin/actions/table.rb
lib/capybara/active_admin/finders.rb
lib/capybara/active_admin/finders/attributes_table.rb
lib/capybara/active_admin/finders/form.rb
lib/capybara/active_admin/finders/layout.rb
lib/capybara/active_admin/finders/table.rb
lib/capybara/active_admin/matchers.rb
lib/capybara/active_admin/matchers/attributes_table.rb
lib/capybara/active_admin/matchers/form.rb
lib/capybara/active_admin/matchers/layout.rb
lib/capybara/active_admin/matchers/table.rb
lib/capybara/active_admin/rspec.rb
lib/capybara/active_admin/selectors.rb
lib/capybara/active_admin/selectors/attributes_table.rb
lib/capybara/active_admin/selectors/form.rb
lib/capybara/active_admin/selectors/layout.rb
lib/capybara/active_admin/selectors/table.rb
lib/capybara/active_admin/test_helpers.rb
lib/capybara/active_admin/util.rb
lib/capybara/active_admin/version.rb

Nine files that no consumer can reach: the VuePress site under docs/
(config.js, styles/index.styl, two READMEs), bin/setup and bin/console
(this gem's executables come from exe/, so dropping bin/ changes
nothing), the two .github/workflows, and yarn.lock.

Packaged: 46 files / 20 KB -> 37 files / 16 KB. lib/ and exe/ intact.
The reject list only removes directories someone remembered to name.
That is how spec/, .github/, screen/ and img/ got published in the
first place — each needed a new pattern, and none was added until an
audit went looking.

A whitelist inverts the default: a new directory in the repo does not
reach consumers until it is listed. Same shape the sibling gems
activeadmin-oidc and credit_card_validations already use.

Drops the remaining dev-only files the reject form kept:

  .gitignore .rspec .rubocop.yml capybara_active_admin.gemspec CODE_OF_CONDUCT.md Gemfile package.json Rakefile

Packaged: 37 -> 29 files. The runtime payload — everything
under lib/, app/, vendor/, config/ and exe/ — is byte-identical to
before, verified by diffing the built .gem both ways.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The implementation removes additional files and can unintentionally package untracked local files.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Updates gem packaging to exclude development-only content through an explicit file whitelist.

Changes:

  • Replaces the tracked-file reject list with a package whitelist.
  • Limits packaged content to library files, executables, and essential documentation.
File Description
capybara_active_admin.gemspec Defines the new package whitelist.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread capybara_active_admin.gemspec Outdated
`Dir[...]` globs the working tree, so any untracked or generated file
under lib/, app/ or vendor/ would be published in a release — the build
artifact depended on the releaser's local checkout. The reject form it
replaced was tracked-only; this restores that property while keeping
the whitelist.

`git ls-files -- <paths>` also returns files only, where `Dir["**/*"]`
returns directory entries too, so `files` no longer carries entries
RubyGems just ignores.

Built .gem is byte-for-byte the same file list as the Dir[] version.
@Fivell Fivell changed the title Trim dev-only paths out of the packaged gem Publish only a whitelist of files in the gem Sep 30, 2026
CI dies every few runs with

  Ferrum::ProcessTimeoutError: Browser did not produce websocket url
  within 15 seconds

`process_timeout` is already env-driven here, so CI just sets
CAPYBARA_PROCESS_TIMEOUT=60 rather than changing the default that
developers get locally.

This is a mitigation and the comment says so. What the logs rule out is
a cold-start problem: the failing `visit` is never the first in the
run, and in the last occurrence the dummy app logged a request from a
live browser three seconds into the window that was timing out — a
restart racing a browser that has not finished dying. The suite does
not reproduce it locally (15 consecutive clean full-suite runs).
@Fivell
Fivell merged commit 9d87b09 into master Sep 30, 2026
37 checks passed
@Fivell
Fivell deleted the chore/trim-packaged-gem branch September 30, 2026 13:57
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.

2 participants