The project is in a healthy, maintained state
Checks your Gemfile.lock against the RubyGems API to ensure no gems are younger than a specified number of days
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 13.0
~> 3.0
~> 1.21

Runtime

>= 2.0
 Project Readme

Bundler::AgeGate

A Bundler plugin that enforces minimum gem age requirements by checking your Gemfile.lock against the RubyGems API.

Why?

Freshly released gems may contain bugs or security vulnerabilities that haven't been discovered yet. This plugin helps you implement a "waiting period" policy before adopting new gem versions in production environments.

Installation

Install the plugin:

bundle plugin install bundler-age_gate

Or add it to your project's plugin list:

bundle plugin install bundler-age_gate --local_git=/path/to/bundler-age_gate

Usage

Check that all gems in your Gemfile.lock are at least 7 days old:

bundle age_check

Specify a custom minimum age (in days):

bundle age_check 14

Check for 30-day minimum:

bundle age_check 30

Clean up exceptions that are no longer needed:

bundle age_gate_cleanup

This command removes exceptions for gems that are now old enough to pass the age gate without an exception.

Example output:

🔍 Checking 3 exception(s) for cleanup...

✅ rails (7.1.3.1) - Released 15 days ago (7 days required) - Removing
⏳ activerecord (7.1.4) - Released 3 days ago (7 days required) - Keeping
⚠️  custom-gem (1.0.0) - Not in Gemfile.lock (keeping exception)

✅ Removed 1 exception(s) from .bundler-age-gate.yml
📝 2 exception(s) remaining

Configuration

Create a .bundler-age-gate.yml file in your project root to customise behaviour:

# Minimum age in days (default: 7)
minimum_age_days: 7

# Audit log path (default: .bundler-age-gate.log)
audit_log_path: .bundler-age-gate.log

# Approved exceptions
exceptions:
  - gem: rails
    version: 7.1.3.1
    reason: "Critical security patch for CVE-2024-12345"
    approved_by: security-team
    expires: 2026-02-15

Exception Workflow

  1. Developer encounters age gate violation
  2. Request exception approval (security team, staff engineer, etc.)
  3. Add approved exception to .bundler-age-gate.yml
  4. Include reason, approver, and expiry date
  5. Commit configuration with approval documented

Exception fields:

  • gem (required): Gem name
  • version (optional): Specific version, omit for all versions
  • reason (required): Explanation for exception
  • approved_by (required): Who approved this
  • expires (optional): Expiry date (YYYY-MM-DD)

Audit Logging

All checks are logged to .bundler-age-gate.log in JSON format:

{
  "timestamp": "2026-01-22T13:45:00Z",
  "result": "pass",
  "violations_count": 0,
  "checked_gems_count": 80,
  "exceptions_used": 1,
  "violations": []
}

Compliance benefits:

  • Track all security checks
  • Audit exception usage
  • Demonstrate policy compliance
  • Investigate historical violations

Multi-Source Support

Configure different age requirements for different gem sources (public vs private):

minimum_age_days: 7  # Global default

sources:
  # Public RubyGems - strict
  - name: rubygems
    url: https://rubygems.org
    api_endpoint: https://rubygems.org/api/v1/versions/%s.json
    minimum_age_days: 7

  # Internal GitHub Packages - less strict
  - name: github-internal
    url: https://rubygems.pkg.github.com/your-org
    api_endpoint: https://rubygems.pkg.github.com/your-org/api/v1/versions/%s.json
    minimum_age_days: 3
    auth_token: ${GITHUB_TOKEN}  # Environment variable

  # Internal Artifactory - very permissive
  - name: artifactory
    url: https://artifactory.company.com/api/gems
    api_endpoint: https://artifactory.company.com/api/gems/api/v1/versions/%s.json
    minimum_age_days: 1
    auth_token: ${ARTIFACTORY_API_KEY}

Benefits:

  • Stricter requirements for public gems (supply chain risk)
  • Permissive for internal gems (trusted sources)
  • Per-source API endpoints for private registries
  • Authentication support via environment variables
  • CLI override still applies globally: bundle age_check 14 enforces 14 days for ALL sources

How it works:

  1. Plugin reads Gemfile.lock to determine each gem's source
  2. Applies per-source minimum age requirements
  3. Queries appropriate API endpoint with authentication
  4. Reports violations with source context

How It Works

  1. Parses your Gemfile.lock using Bundler's built-in parser
  2. Queries the RubyGems API for each gem's release date
  3. Compares the release date against your specified minimum age
  4. Reports any violations and exits with status code 1 if violations are found

Note: Currently only supports rubygems.org. Private gem servers or GitHub packages are not yet supported. See Roadmap for planned features.

Features

  • Efficient API usage: Caches API responses to avoid duplicate requests
  • Progress indicator: Prints a dot for each gem checked
  • Clear output: Shows violating gems with release dates and age
  • Graceful error handling: Skips gems that can't be checked (API errors, network issues)
  • CI-friendly: Returns appropriate exit codes (0 for pass, 1 for fail)
  • Configuration file: Customise settings via .bundler-age-gate.yml
  • Exception handling: Approve specific gems with documented reasons
  • Audit logging: Compliance-ready logs for all checks
  • Enterprise-ready: Designed for organisation-wide rollout

Performance

Parallel gem checking with configurable concurrency (default: 8 workers).

Configure via .bundler-age-gate.yml:

max_workers: 8  # Recommended for most projects (range: 1-16)

Set max_workers: 1 to disable parallelisation if needed.

Example Output

🔍 Checking gem ages (minimum: 7 days)...
📅 Cutoff date: 2026-01-15

Checking 143 gems...
Progress: ...............................................

⚠️  Found 2 gem(s) younger than 7 days:

  ❌ rails (7.1.3)
     Released: 2026-01-20 (2 days ago)

  ❌ activerecord (7.1.3)
     Released: 2026-01-20 (2 days ago)

⛔ Age gate check FAILED

Use Cases

  • Production deployments: Ensure stability by waiting for community feedback
  • Security compliance: Enforce policies requiring "battle-tested" versions
  • CI pipelines: Add as a check in your continuous integration workflow
  • Risk management: Reduce exposure to zero-day vulnerabilities in new releases

CI/CD Integration

GitHub Actions

# .github/workflows/gem-security-check.yml
- name: Install bundler-age_gate
  run: |
    gem install bundler-age_gate
    bundle plugin install bundler-age_gate

- name: Check gem ages
  run: bundle age_check 7

Full example: See examples/github-actions.yml for complete workflow with PR comments and artefact upload.

GitLab CI

# .gitlab-ci.yml
gem-age-gate-check:
  script:
    - bundle plugin install bundler-age_gate
    - bundle age_check 7

Full example: See examples/gitlab-ci.yml

CircleCI

# .circleci/config.yml
- run:
    name: Check gem ages
    command: bundle age_check 7

Full example: See examples/circleci-config.yml

Pre-commit Hooks

Shell Script

# .git/hooks/pre-commit
#!/bin/bash
if git diff --cached --name-only | grep -q "^Gemfile.lock$"; then
  bundle age_check 7
fi

Installation: See examples/pre-commit

Pre-commit Framework

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: bundler-age-gate
        name: Check gem ages
        entry: bundle age_check 7
        language: system
        files: ^Gemfile\.lock$

Full example: See examples/.pre-commit-config.yaml

Development

After checking out the repo:

bundle install

To test locally:

bundle exec rake

Roadmap

All planned features have been implemented! 🎉

  • Private gem server support: ✅ Implemented in v0.3.0
  • Multi-source detection: ✅ Implemented in v0.3.0
  • Per-source age requirements: ✅ Implemented in v0.3.0
  • Authentication for private sources: ✅ Implemented in v0.3.0
  • Exception handling: ✅ Implemented in v0.2.0
  • Audit logging: ✅ Implemented in v0.2.0
  • CI/CD integration examples: ✅ Implemented in v0.2.0
  • Transitive dependency checking: ✅ Already included (checks entire Gemfile.lock)

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/NikoRoberts/bundler-age_gate.

License

The gem is available as open source under the terms of the MIT License.