From b66ef2eecfe35a11996c29329a23ae1707145b28 Mon Sep 17 00:00:00 2001 From: Marc Best Date: Wed, 3 Jun 2026 10:33:56 +0100 Subject: [PATCH 1/3] Add Bundler (RubyGems) native cooldown documentation Bundler 4.0.13 introduced native cooldown support. Add a Ruby Ecosystem section documenting the --cooldown flag, bundle config, per-source Gemfile declaration, and BUNDLE_COOLDOWN env var; update the "Other ecosystems" prose and Quick reference table accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0fa77b2..de29619 100644 --- a/README.md +++ b/README.md @@ -353,6 +353,52 @@ export COOLDOWN_MINUTES=4320 # 3 days, in minutes cargo cooldown build ``` +## Ruby Ecosystem + +### Bundler + +[Bundler](https://bundler.io/) introduced a native cooldown feature in version 4.0.13. The cooldown value is always a +non-negative integer number of days (a string, float, negative number, or array is rejected). For example, to apply a +three-day cooldown to a single command: + +```bash +bundle install --cooldown 3 +``` + +The `--cooldown` flag is supported by the `install`, `update`, `add`, and `outdated` commands. + +To set it for the current project, execute: + +```bash +bundle config set cooldown 3 +``` + +Or globally for all projects: + +```bash +bundle config set --global cooldown 3 +``` + +You can also declare a cooldown per-source directly in the `Gemfile`: + +```ruby +source "https://rubygems.org", cooldown: 3 +``` + +Or use the following environment variable: + +```bash +export BUNDLE_COOLDOWN=3 +``` + +When multiple settings apply, the override hierarchy is: command-line flag > configuration setting > per-source +`Gemfile` declaration. Passing `0` disables the cooldown for that run. + +Bundler fails open: it only holds back versions it can prove are too new. Versions lacking a `created_at` timestamp +(older servers, v1-format registries, some private gems) remain resolvable. See the +[RubyGems blog announcement](https://blog.rubygems.org/2026/06/03/cooldown-let-new-gems-be-vetted.html) for more +information. + ## Scala / JVM Ecosystem ### Scala Steward @@ -419,10 +465,11 @@ locking your dependencies to exact versions, and configuring cooldowns in Depend Maven/Gradle (Java) don't have native cooldowns either, but the third-party [Scala Steward](#scala-jvm-ecosystem) bot described above can apply cooldowns to Maven/Gradle projects (though it's not heavily used outside of Scala). -RubyGems/Bundler (Ruby) and Swift Package Manager don't have native cooldowns either, and no open requests exist -requesting this feature as of today. +Swift Package Manager doesn't have native cooldowns either, and no open request exists requesting this feature as of +today. RubyGems/Bundler (Ruby) now supports cooldowns natively as of Bundler 4.0.13 +(see [Ruby Ecosystem](#ruby-ecosystem) above). -One exception worth noting: the community-run [gem.coop package index](https://gem.coop), an alternative to RubyGems, +One related note: the community-run [gem.coop package index](https://gem.coop), an alternative to RubyGems, enforces a 48-hour delay on newly published gems at the registry level. ## Dependency update bots @@ -603,13 +650,14 @@ RUN cooldowns.sh check | Bun | Relative durations | `minimumReleaseAge = 259200` in `bunfig.toml` | | Deno | Relative durations | `minimumDependencyAge: "P3D"` in `deno.json` | | Cargo | Third-party only | `cargo cooldown ` via `cargo-cooldown` crate | +| Bundler | Integer days (4.0.13+) | `bundle config set cooldown 3` / `--cooldown 3` | | Scala Steward | Relative durations (0.38.0+) | `updates.cooldown.minimumAge = "3 days"` in `.scala-steward.conf` | | VS Code | Not available | Pin dependencies and review updates manually | | Go | Not available | Dependabot/Renovate only | | Maven/Gradle | Not available | Dependabot/Renovate only | | NuGet | Not available | Dependabot/Renovate only | | Composer | Not available | Dependabot/Renovate only | -| RubyGems | Not available | gem.coop proxy / Dependabot/Renovate | +| RubyGems | Integer days (Bundler 4.0.13+) | `bundle config set cooldown 3` / `--cooldown 3` | ## Conclusion @@ -624,6 +672,7 @@ with zero ongoing effort after initial setup. Pick a number, configure it, and s ## Changelog +- **2026-06-03**: Added Bundler (RubyGems) cooldown documentation. - **2026-06-01**: Added VS Code documentation. - **2026-05-27**: Added Scala Steward cooldown documentation. - **2026-05-26**: Added pixi documentation. From 6b75bda38cac2b68f794d6309cadc0ccd126cad2 Mon Sep 17 00:00:00 2001 From: Marc Best Date: Thu, 4 Jun 2026 22:04:41 +0100 Subject: [PATCH 2/3] Address review: tidy "Other ecosystems" prose and quick-ref table - Move the Swift Package Manager note into the preceding paragraph and keep the Maven/Gradle paragraph focused on its Scala Steward alternative; drop the redundant Bundler note (covered in the Ruby Ecosystem section). - Quick-reference table: phrase the Bundler row as "Relative durations (4.0.13+)" for consistency with the other rows. - Drop the separate RubyGems row; the table itemizes tools, not indexes. --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index de29619..8329036 100644 --- a/README.md +++ b/README.md @@ -459,15 +459,13 @@ These language ecosystems currently offer no native cooldown support. There's an [open proposal](https://github.com/golang/go/issues/76485) for Go, but it hasn't been accepted. [NuGet](https://github.com/NuGet/Home/issues/14657), [Composer](https://github.com/composer/composer/issues/12633), and -[Hex](https://github.com/hexpm/hex/issues/1113) also have open feature requests. Your best bet is +[Hex](https://github.com/hexpm/hex/issues/1113) also have open feature requests. Swift Package Manager doesn't have +native cooldowns either, and no open request exists requesting this feature as of today. Your best bet is locking your dependencies to exact versions, and configuring cooldowns in Dependabot or Renovate for automated updates (see below). Maven/Gradle (Java) don't have native cooldowns either, but the third-party [Scala Steward](#scala-jvm-ecosystem) bot described above can apply cooldowns to Maven/Gradle projects (though it's not heavily used outside of Scala). -Swift Package Manager doesn't have native cooldowns either, and no open request exists requesting this feature as of -today. RubyGems/Bundler (Ruby) now supports cooldowns natively as of Bundler 4.0.13 -(see [Ruby Ecosystem](#ruby-ecosystem) above). One related note: the community-run [gem.coop package index](https://gem.coop), an alternative to RubyGems, enforces a 48-hour delay on newly published gems at the registry level. @@ -650,14 +648,13 @@ RUN cooldowns.sh check | Bun | Relative durations | `minimumReleaseAge = 259200` in `bunfig.toml` | | Deno | Relative durations | `minimumDependencyAge: "P3D"` in `deno.json` | | Cargo | Third-party only | `cargo cooldown ` via `cargo-cooldown` crate | -| Bundler | Integer days (4.0.13+) | `bundle config set cooldown 3` / `--cooldown 3` | +| Bundler | Relative durations (4.0.13+) | `bundle config set cooldown 3` / `--cooldown 3` | | Scala Steward | Relative durations (0.38.0+) | `updates.cooldown.minimumAge = "3 days"` in `.scala-steward.conf` | | VS Code | Not available | Pin dependencies and review updates manually | | Go | Not available | Dependabot/Renovate only | | Maven/Gradle | Not available | Dependabot/Renovate only | | NuGet | Not available | Dependabot/Renovate only | | Composer | Not available | Dependabot/Renovate only | -| RubyGems | Integer days (Bundler 4.0.13+) | `bundle config set cooldown 3` / `--cooldown 3` | ## Conclusion From 08ca914c8e7f477dcb83c80a8a9acc8e17dc32b0 Mon Sep 17 00:00:00 2001 From: Marc Best Date: Thu, 4 Jun 2026 22:37:28 +0100 Subject: [PATCH 3/3] Add bundler support to cooldowns.sh and smoke test Implement native Bundler cooldown support in the helper script via the BUNDLE_COOLDOWN env-var export (Bundler >= 4.0.13): set_bundler, check_bundler, dispatch, relevance detection, duration formatting, and the usage/header docs. Modeled on set_yarn, so it configures without requiring bundler to be installed. - tests/smoke-test.sh: add bundler to the configure-and-check loop. - smoke-tests.yml: unset BUNDLE_COOLDOWN on the macOS leg for hermeticity. - README: complete the cooldowns.sh tool table (adds bundler, plus the previously-missing poetry row) and re-align it. --- .github/workflows/smoke-tests.yml | 2 +- README.md | 22 +++++----- cooldowns.sh | 68 ++++++++++++++++++++++++++++--- tests/smoke-test.sh | 2 +- 4 files changed, 77 insertions(+), 17 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 48fca88..3bf6c5e 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -53,7 +53,7 @@ jobs: export HOME=$(mktemp -d) unset ZDOTDIR export SHELL=/bin/zsh - unset UV_EXCLUDE_NEWER YARN_NPM_MINIMAL_AGE_GATE COOLDOWN_MINUTES PIP_UPLOADED_PRIOR_TO 2>/dev/null || true + unset UV_EXCLUDE_NEWER YARN_NPM_MINIMAL_AGE_GATE COOLDOWN_MINUTES PIP_UPLOADED_PRIOR_TO BUNDLE_COOLDOWN 2>/dev/null || true trap 'rm -rf "$HOME"' EXIT bash -s "$HOME/.zshrc" < "$script" ) diff --git a/README.md b/README.md index 8329036..b171521 100644 --- a/README.md +++ b/README.md @@ -581,16 +581,18 @@ cooldowns.sh set npm 7d Each `set` command writes a user-wide configuration for that tool. Project-level configs are not modified. The exact location depends on the tool: -| Tool | Method | Location | -|-------|--------------------------------------------------|-------------------------------------------------| -| pip | Env var export (26.1+) or shell wrapper (older) | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | -| uv | Env var export | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | -| npm | `.npmrc` key | `~/.npmrc` | -| pnpm | `.npmrc` key | `~/.npmrc` | -| yarn | Env var export | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | -| bun | `bunfig.toml` key | `~/.bunfig.toml` | -| deno | Shell aliases | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | -| cargo | Env var export (requires `cargo-cooldown` crate) | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | +| Tool | Method | Location | +|---------|--------------------------------------------------|------------------------------------------------| +| pip | Env var export (26.1+) or shell wrapper (older) | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | +| uv | Env var export | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | +| poetry | `poetry config` setting | `~/.config/pypoetry/config.toml` | +| npm | `.npmrc` key | `~/.npmrc` | +| pnpm | `.npmrc` key | `~/.npmrc` | +| yarn | Env var export | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | +| bun | `bunfig.toml` key | `~/.bunfig.toml` | +| deno | Shell aliases | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | +| cargo | Env var export (requires `cargo-cooldown` crate) | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | +| bundler | Env var export (requires Bundler >= 4.0.13) | `/etc/profile.d/cooldowns.sh` (or `~/.bashrc`) | Tools that use profile scripts write to `/etc/profile.d/cooldowns.sh` if the directory exists and is writable, otherwise they fall back to `~/.bashrc`. diff --git a/cooldowns.sh b/cooldowns.sh index a16996e..3a08777 100755 --- a/cooldowns.sh +++ b/cooldowns.sh @@ -13,11 +13,12 @@ # cooldowns.sh check # # Changelog: +# 2026-06-04 Added bundler support (BUNDLE_COOLDOWN export, Bundler >= 4.0.13) # 2026-06-01 Added poetry support (solver.min-release-age, poetry >= 2.4.0) # 2026-05-28 Use pnpm config set --global for pnpm to avoid npm unknown-key warnings # 2026-05-07 Added pip 26.1+ duration format support (e.g. P3D) # -# Supported tools: pip, uv, poetry, npm, pnpm, yarn, bun, deno, cargo +# Supported tools: pip, uv, poetry, npm, pnpm, yarn, bun, deno, cargo, bundler # # Where configs are written: # pip Shell wrapper (pip < 26.1) or env var export (pip >= 26.1) @@ -32,6 +33,8 @@ # bun minimumReleaseAge in ~/.bunfig.toml # deno Aliases in /etc/profile.d/cooldowns.sh (or ~/.zshrc / ~/.bashrc) # cargo COOLDOWN_MINUTES export in /etc/profile.d/cooldowns.sh (or ~/.zshrc / ~/.bashrc) +# bundler BUNDLE_COOLDOWN export in /etc/profile.d/cooldowns.sh (or ~/.zshrc / ~/.bashrc) +# (requires Bundler >= 4.0.13) # # Profile scripts fall back to the user's rc file (~/.zshrc or ~/.bashrc, based # on $SHELL) when /etc/profile.d is not writable. @@ -188,6 +191,7 @@ duration_for_tool() { deno) echo "P${days}D" ;; # ISO 8601 yarn) echo $(( days * 24 * 60 )) ;; # minutes cargo) echo $(( days * 24 * 60 )) ;; # minutes + bundler) echo "$days" ;; # integer days *) echo "$days" ;; esac } @@ -531,10 +535,41 @@ SHELL echo " Install the crate with: cargo install cargo-cooldown" } +set_bundler() { + local days="$1" + # Bundler reads BUNDLE_COOLDOWN at runtime; the export is harmless without + # bundler installed, so (like yarn) we don't gate on `command -v bundle`. + local value + value=$(duration_for_tool "$days" bundler) + ensure_profile_dir + + if ! find_in_profiles "cooldowns:bundler:start" &>/dev/null; then + if [[ -n "${BUNDLE_COOLDOWN:-}" ]]; then + echo "bundler: BUNDLE_COOLDOWN is already set to '$BUNDLE_COOLDOWN', skipping" + return + fi + local existing_file + if existing_file=$(find_in_profiles "BUNDLE_COOLDOWN="); then + echo "bundler: BUNDLE_COOLDOWN is already configured in $existing_file, skipping" + return + fi + fi + + clean_previous bundler "$PROFILE_SCRIPT" + + cat >> "$PROFILE_SCRIPT" << SHELL +# cooldowns:bundler:start +export BUNDLE_COOLDOWN="$value" +# cooldowns:bundler:end +SHELL + echo "bundler: set BUNDLE_COOLDOWN=$value in $PROFILE_SCRIPT" + echo " note: requires Bundler >= 4.0.13. Per-project you can instead run 'bundle config set cooldown $value' or add 'cooldown: $value' to a Gemfile source." +} + do_set() { if [[ $# -lt 2 ]]; then echo "usage: cooldowns.sh set " >&2 - echo "tools: pip, uv, poetry, npm, pnpm, yarn, bun, deno, cargo" >&2 + echo "tools: pip, uv, poetry, npm, pnpm, yarn, bun, deno, cargo, bundler" >&2 return 1 fi @@ -552,9 +587,10 @@ do_set() { bun) set_bun "$days" ;; deno) set_deno "$days" ;; cargo) set_cargo "$days" ;; + bundler) set_bundler "$days" ;; *) echo "error: unknown tool '$tool'" >&2 - echo "supported: pip, uv, poetry, npm, pnpm, yarn, bun, deno, cargo" >&2 + echo "supported: pip, uv, poetry, npm, pnpm, yarn, bun, deno, cargo, bundler" >&2 return 1 ;; esac @@ -839,6 +875,24 @@ check_cargo() { record cargo $STATUS_MISSING "no cooldown configured" } +check_bundler() { + if [[ -n "${BUNDLE_COOLDOWN:-}" ]]; then + record bundler $STATUS_OK "BUNDLE_COOLDOWN=$BUNDLE_COOLDOWN (${BUNDLE_COOLDOWN}d)" + return + fi + + local profile_file + if profile_file=$(find_in_profiles "cooldowns:bundler:start") \ + || profile_file=$(find_in_profiles "BUNDLE_COOLDOWN="); then + local val + val=$(extract_kv BUNDLE_COOLDOWN "$profile_file" || echo "") + record bundler $STATUS_OK "BUNDLE_COOLDOWN=$val (${val}d) in $profile_file (not yet sourced)" + return + fi + + record bundler $STATUS_MISSING "no cooldown configured" +} + tool_is_relevant() { local tool="$1" command -v "$tool" &>/dev/null && return 0 @@ -857,6 +911,8 @@ tool_is_relevant() { find_in_profiles "COOLDOWN_MINUTES=" &>/dev/null && return 0 ;; yarn) [[ -n "${YARN_NPM_MINIMAL_AGE_GATE:-}" ]] && return 0 find_in_profiles "YARN_NPM_MINIMAL_AGE_GATE=" &>/dev/null && return 0 ;; + bundler) [[ -n "${BUNDLE_COOLDOWN:-}" ]] && return 0 + find_in_profiles "BUNDLE_COOLDOWN=" &>/dev/null && return 0 ;; esac return 1 } @@ -867,7 +923,7 @@ do_check() { local any_checked=false - for tool in pip uv poetry npm pnpm yarn bun deno cargo; do + for tool in pip uv poetry npm pnpm yarn bun deno cargo bundler; do if tool_is_relevant "$tool"; then any_checked=true "check_${tool}" @@ -922,7 +978,7 @@ commands: set Configure cooldown for a package manager check Check cooldown status for all installed tools -tools: pip, uv, poetry, npm, pnpm, yarn, bun, deno, cargo +tools: pip, uv, poetry, npm, pnpm, yarn, bun, deno, cargo, bundler duration examples: 3d, "3 days", 7d, 1d @@ -938,6 +994,8 @@ where configs are written (all user-wide; project-level configs are not modified deno shell aliases /etc/profile.d/cooldowns.sh (or ~/.zshrc / ~/.bashrc) cargo env var export /etc/profile.d/cooldowns.sh (or ~/.zshrc / ~/.bashrc) (requires cargo-cooldown crate; use 'cargo cooldown ') + bundler env var export /etc/profile.d/cooldowns.sh (or ~/.zshrc / ~/.bashrc) + (requires Bundler >= 4.0.13) Fallback chooses ~/.zshrc or ~/.bashrc based on $SHELL. diff --git a/tests/smoke-test.sh b/tests/smoke-test.sh index dfd681b..903b149 100755 --- a/tests/smoke-test.sh +++ b/tests/smoke-test.sh @@ -39,7 +39,7 @@ if command -v pnpm &>/dev/null; then fi configured_tools=() -for t in pip uv poetry npm pnpm yarn bun deno cargo; do +for t in pip uv poetry npm pnpm yarn bun deno cargo bundler; do set_out=$(cooldowns.sh set "$t" 7d) echo "$set_out" if ! echo "$set_out" | grep -q "skipping" || echo "$set_out" | grep -q "already"; then