-
Notifications
You must be signed in to change notification settings - Fork 51
Description
Development files included in gem distribution due to git ls-files usage
Problem
The current gemspec uses git ls-files to determine which files to include in the gem:
s.files = `git ls-files`.split("\n")This approach has two major problems:
1. Includes unnecessary development files
All git-tracked files are included in the gem, even those that serve no purpose in production:
.github/workflows/ci.yml(GitHub Actions CI configuration).rubocop.yml(RuboCop linting configuration).editorconfig(Editor configuration).rspec(RSpec test configuration).autotest(Autotest configuration).gitignore(Git configuration)CODEOWNERS(GitHub repository feature)Guardfile(Guard file watching configuration)
These files bloat the gem package and can cause issues for downstream packagers (e.g., RPM, DEB packages).
2. Git dependency breaks builds in many environments
Using git ls-files in gemspecs is considered bad practice in the Ruby community because it breaks in environments without git:
- Package builds: Debian/RPM packages are built in clean environments without git → build fails (source)
- Docker containers: Minimal production images without git → "No such file or directory - git" errors
- Git archives: GitHub release tarballs created with
git archivedon't contain.git/directory →git ls-filesfails (source)
Real-world Impact
This issue was discovered while packaging clamp 1.4.0 as an RPM for the Foreman project. The migration from Travis CI to GitHub Actions caused RPM builds to fail:
- Spec file referenced
.travis.yml(removed upstream) → "file not found" error - Spec file didn't exclude
.github/workflows/ci.yml(newly added) → "unpackaged files" error
Downstream packagers must maintain explicit exclusion lists that break whenever upstream adds new development files.
Proposed Solution
Replace git ls-files with pure Ruby Dir globs, as recommended by:
- Ruby Packaging Style Guide
- RuboCop Packaging cops
- Bundler issue #2287: "
git ls-filesin gemspec template is bad practice" - ruby-progressbar issue #54: "Don't rely on git ls-files in gemspec file"
Change:
# Before (problematic)
s.files = `git ls-files`.split("\n")
# After (recommended)
s.files = Dir["lib/**/*", "examples/**/*", "spec/**/*", "*.md", "LICENSE", "Rakefile", "Gemfile", "#{s.name}.gemspec"]Benefits
✅ No git dependency - Works in Docker, package builds, CI environments
✅ Explicit file list - Clear what's included, maintainable
✅ Cleaner gems - Only production-relevant files
✅ Simpler downstream packaging - No need for manual exclusion lists
✅ No breaking changes - All necessary files still included
Additional Context
Note on .gitattributes approach: While .gitattributes with export-ignore works for git archive, it does NOT work with git ls-files (source). Since gemspecs execute git ls-files directly, .gitattributes won't solve this problem.
Ruby community consensus: The Ruby packaging community recommends avoiding any git commands in gemspecs in favor of pure Ruby alternatives (Bundler #2287, RubyGems #4047).
References
- Using
git ls-filesin gemspec template is bad practice - Don't rely on git ls-files in gemspec file
- Pure ruby replacement for
git ls-files - The Packaging Style Guide
- RuboCop Packaging cops documentation
- Avoid using
gitto declare files in gemspec - Excluding files from git archive (why .gitattributes doesn't help here)
- Foreman packaging issue that discovered this