Skip to content

Development files included in gem distribution due to git ls-files usage #125

@ogajduse

Description

@ogajduse

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 archive don't contain .git/ directory → git ls-files fails (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:

  1. Spec file referenced .travis.yml (removed upstream) → "file not found" error
  2. 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:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions