Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: rails/spring
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.1.0
Choose a base ref
...
head repository: rails/spring
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref

Commits on Mar 5, 2021

  1. Copy the full SHA
    bacf4d6 View commit details

Commits on Nov 24, 2021

  1. Drop dependency on StringIO

    StringIO was extracted as a gem, so spring can no
    longer use it for the same reason it can't use the `json`
    gem.
    
    Otherwise you'll see warnings like:
    
    ```
    ...stringio.bundle: warning: already initialized constant StringIO::VERSION
    ```
    
    This can be reproduced on Ruby 3.0, by installing `stringio 3.0.1`
    and starting a Rails app with spring.
    byroot committed Nov 24, 2021
    Copy the full SHA
    262bbd3 View commit details
  2. Merge pull request #656 from casperisfine/drop-stringio-dep

    Drop dependency on StringIO
    byroot authored Nov 24, 2021
    Copy the full SHA
    4ae1afd View commit details

Commits on Nov 25, 2021

  1. Avoid trying to mutate frozen backtraces

    Closes #609.
    rafaelfranca committed Nov 25, 2021
    Copy the full SHA
    ae6e2a8 View commit details
  2. Copy the full SHA
    cb6aeb4 View commit details
  3. Prepare to 3.1.1

    rafaelfranca committed Nov 25, 2021
    Copy the full SHA
    1d6c712 View commit details
  4. Copy the full SHA
    3e7e996 View commit details
  5. Remove bundler deprecation by using the right method

    We want the environment before bundler was activated, so we can call
    with_original_env.
    rafaelfranca committed Nov 25, 2021
    Copy the full SHA
    4140d67 View commit details
  6. Copy the full SHA
    7df4eee View commit details

Commits on Nov 26, 2021

  1. Copy the full SHA
    e5c3345 View commit details
  2. Merge pull request #658 from deivid-rodriguez/fix_variable_name

    Fix variable name in gemspec
    matthewd authored Nov 26, 2021
    Copy the full SHA
    fcd8a0f View commit details

Commits on Dec 2, 2021

  1. Stop depending on Set

    It is now a gem, and since spring load before bundler
    if you have multiple `set` versions installed, you might
    load the wrong one causing already defined constant warnings.
    byroot committed Dec 2, 2021
    Copy the full SHA
    7f4ddf9 View commit details
  2. Merge pull request #659 from casperisfine/drop-set-dependency

    Stop depending on Set
    byroot authored Dec 2, 2021
    Copy the full SHA
    98a7631 View commit details

Commits on Dec 10, 2021

  1. Copy the full SHA
    f4319c2 View commit details
  2. Copy the full SHA
    df7788f View commit details
  3. Make sure the bundler environment is correct

    We need an unbundled env and after removing spring we need to make
    sure the Gemfile.lock is updated.
    rafaelfranca committed Dec 10, 2021
    Copy the full SHA
    bb4a46e View commit details
  4. Copy the full SHA
    2988f37 View commit details
  5. Copy the full SHA
    a766ced View commit details
  6. Merge pull request #661 from rails/rm-test-7.0-drop-ruby-2.6

    Drop support to Ruby < 2.7 and Rails < 6.0
    rafaelfranca authored Dec 10, 2021
    Copy the full SHA
    a81ef6e View commit details
  7. Modernize spring binstubs and disable it in production

    Even if the gem is installed, spring now only enables in development and
    test.
    rafaelfranca committed Dec 10, 2021
    Copy the full SHA
    02649d9 View commit details
  8. Merge pull request #662 from rails/rm-new-format-binstub

    Modernize spring binstubs and disable it in production
    rafaelfranca authored Dec 10, 2021
    Copy the full SHA
    ba9d63a View commit details
  9. Prepare to 4.0.0

    rafaelfranca committed Dec 10, 2021
    Copy the full SHA
    a318a18 View commit details

Commits on Dec 22, 2021

  1. Stop requiring Set

    nvasilevski committed Dec 22, 2021
    Copy the full SHA
    20ddb2e View commit details
  2. Merge pull request #665 from nvasilevski/do-not-require-set

    Stop requiring Set
    byroot authored Dec 22, 2021
    Copy the full SHA
    c45b7ee View commit details

Commits on Jan 2, 2022

  1. Fix check when no command is supplied

    Closes #666.
    mernen committed Jan 2, 2022
    Copy the full SHA
    942f53e View commit details

Commits on Jan 20, 2022

  1. Copy the full SHA
    7fcb742 View commit details
  2. Copy the full SHA
    9b8ba21 View commit details
  3. Merge pull request #667 from mernen/main

    rails: fix check when no command is supplied
    rafaelfranca authored Jan 20, 2022
    Copy the full SHA
    c83c31b View commit details

Commits on Feb 6, 2022

  1. Defer digest loading

    theodorton committed Feb 6, 2022
    Copy the full SHA
    a259761 View commit details

Commits on Feb 25, 2022

  1. Merge pull request #672 from onelittle/defer-digest-loading

    Lazy-load digest dependency
    rafaelfranca authored Feb 25, 2022
    Copy the full SHA
    184ecda View commit details

Commits on May 2, 2022

  1. Copy the full SHA
    41f0d21 View commit details

Commits on May 30, 2022

  1. Copy the full SHA
    6bfe665 View commit details

Commits on Sep 13, 2022

  1. Merge pull request #623 from mvz/fix-freezes

    Redirect STDERR to /dev/null during eager_preload
    rafaelfranca authored Sep 13, 2022
    Copy the full SHA
    a5d2911 View commit details

Commits on Sep 15, 2022

  1. add requirement for Bundler 2.1+

    Spring 4.0.0 uses with_unbundled_env which is only
    available with Bundler 2.1 (see
    https://bundler.io/v2.1/whats_new.html#helper-deprecations)
    
    This has caused issues with some people using e.g Bundler v1
    #663
    timdiggins committed Sep 15, 2022
    Copy the full SHA
    e22d8af View commit details

Commits on Sep 20, 2022

  1. Merge pull request #681 from timdiggins/show-bundler-compat-in-docs

    add requirement for Bundler 2.1+
    rafaelfranca authored Sep 20, 2022
    Copy the full SHA
    7022377 View commit details
  2. Merge pull request #674 from thewoolleyman/main

    Add support for SPRING_QUIET environment variable.
    rafaelfranca authored Sep 20, 2022
    Copy the full SHA
    67492f2 View commit details
  3. No need to memoize

    The env just have precedence of the config if it is `false`.
    rafaelfranca committed Sep 20, 2022
    Copy the full SHA
    1908710 View commit details
  4. Merge pull request #679 from inayuky/fix-db-change-command

    Fix rails db:system:change command failure issue
    rafaelfranca authored Sep 20, 2022
    Copy the full SHA
    3e582ea View commit details
  5. Download gems that are not present, like net-imap dependencies

    Some of those dependencies are part of Ruby already, so they aren't
    installed locally. If we use local bundler will skip downloading them
    but will not be able to find them anyway, failing.
    rafaelfranca committed Sep 20, 2022
    Copy the full SHA
    5c70765 View commit details

Commits on Sep 21, 2022

  1. Copy the full SHA
    26ca9ba View commit details
  2. Test with Ruby 3.1

    rafaelfranca committed Sep 21, 2022
    Copy the full SHA
    420ede1 View commit details
  3. Copy the full SHA
    6c6ca77 View commit details
  4. Merge pull request #682 from rails/rm-fix-test

    Fix tests
    rafaelfranca authored Sep 21, 2022
    Copy the full SHA
    828c875 View commit details
  5. Copy the full SHA
    39cb7af View commit details
  6. Prepare for 4.1.0

    rafaelfranca committed Sep 21, 2022
    Copy the full SHA
    42d9896 View commit details

Commits on Sep 22, 2022

  1. Update Gemfile

    rafaelfranca authored Sep 22, 2022
    Copy the full SHA
    b01b769 View commit details

Commits on Dec 5, 2022

  1. Copy the full SHA
    2eec2ca View commit details

Commits on Dec 6, 2022

  1. Copy the full SHA
    4581232 View commit details

Commits on Jan 9, 2023

  1. Fix the Kernel#raise patch to be Ruby 3.2 compatible

    `raise` can take a `cause:` keyword argument. This somehow
    worked until 3.1 but now longer does in 3.2.
    byroot committed Jan 9, 2023
    Copy the full SHA
    21e272f View commit details
  2. Merge pull request #691 from casperisfine/ruby-3.2-raise

    Fix the Kernel#raise patch to be Ruby 3.2 compatible
    byroot authored Jan 9, 2023
    Copy the full SHA
    77fbbfb View commit details
29 changes: 14 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -6,21 +6,24 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: [ '2.7', '3.0', 'head' ]
rails: [ '6.0', '6.1', 'edge' ]
ruby: [ '3.1', '3.2', 'head' ]
rails: [ '7.1', 'edge' ]
rubyopt: [""]
include:
- ruby: '2.6'
rails: '5.2'
- ruby: '2.6'
rails: '6.0'
- ruby: '2.6'
rails: '6.1'
- ruby: '3.3'
rails: '7.1'
rubyopt: "--enable-frozen-string-literal"
exclude:
- ruby: '3.1'
rails: 'edge'
rubyopt: ''

env:
RAILS_VERSION: ${{ matrix.rails }}
RUBYOPT: ${{ matrix.rubyopt }}

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Ruby
uses: ruby/setup-ruby@v1
@@ -29,14 +32,10 @@ jobs:
bundler-cache: true

- name: Run unit tests
run: bundle exec rake test:unit
timeout-minutes: 3

- name: Run unit tests
run: bundle exec rake test:unit
run: bundle exec rake test:unit RUBYOPT="${{ matrix.rubyopt }}"
timeout-minutes: 3

- name: Run acceptance tests
run: bundle exec rake test:acceptance
run: bundle exec rake test:acceptance RUBYOPT="${{ matrix.rubyopt }}"
timeout-minutes: 10
if: ${{ matrix.rails != 'edge' && matrix.ruby != 'head' }} # Acceptance tests use `gem install rails && rails new`
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
## Next Release

* Fix reloading issue in Ruby 3.3.

## 4.2.1

* Added `Spring.connect_timeout` and `Spring.boot_timeout` to allow to increase timeout for larger apps.
* `Spring.connect_timeout` now default to `5` seconds instead of `1`.

## 4.2.0

* Added `Spring.reset_on_env` to define environment variables which should cause spring to reload when they change.
* Added `Spring.spawn_on_env` to define environment variables that should cause spring to start a distinct server.
* Better handle a case where the server could hang.

## 4.1.3

* Bring back `Spring::Watcher::Abstract#synchronize` method to fix compatibility with `spring-watcher-listen`.

## 4.1.2

* Drop dependency on `mutex_m`. Avoid issues with loading the wrong version before bundler kicks in.

## 4.1.1

* Fix a small compatibility issue with Ruby 3.2 causing `Kernel#raise` to not accept a `cause`.

## 4.1.0

* Fix bug which makes commands to freeze when the Rails application is writing to STDERR.
* Fix rails db:system:change command failure issue.
* Defer digest loading.
* Add support for `SPRING_QUIET` environment variable.

## 4.0.0

* Stop requiring `set` before bundler can select the proper version. This could result in
`already defined constant` warnings during boot (#659).
* Require Ruby 2.7.
* Require Rails 6.0.

## 3.1.1

* Fix compatibility issues with code that raises exceptions with frozen backtraces.
* Better support Ruby 3.0.

## 3.1.0

* Fix bug which makes rails consoles to hang at exit when multiple of them are open (#647).
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ acceptance tests again, which will generate a new app.
You can set the `RAILS_VERSION` environment variable:

```
$ RAILS_VERSION="~> 3.2.0" rake test:acceptance
$ RAILS_VERSION="6.1" rake test:acceptance
```

The apps in `test/apps` will be named based on the rails version, and the
7 changes: 6 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -3,8 +3,13 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in spring.gemspec
gemspec

gem "rake"
gem "bump"

if ENV["RAILS_VERSION"] == "edge"
gem "activesupport", github: "rails/rails", branch: "main"
elsif ENV["RAILS_VERSION"]
gem "activesupport", ENV["RAILS_VERSION"]
gem "activesupport", "~> #{ENV["RAILS_VERSION"]}.0"
else
gem "activesupport"
end
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -16,9 +16,9 @@ boot it every time you run a test, rake task or migration.

## Compatibility

* Ruby versions: MRI 2.5, MRI 2.6
* Rails versions: 5.2, 6.0 (Spring is installed by default when you do
`rails new` to generate your application)
* Ruby versions: MRI 2.7, MRI 3.0, MRI 3.1, MRI 3.2
* Rails versions: 6.0, 6.1, 7.0
* Bundler v2.1+

Spring makes extensive use of `Process.fork`, so won't be able to
provide a speed up on platforms which don't support forking (Windows, JRuby).
@@ -71,11 +71,14 @@ Rails directory.
Spring reloads application code, and therefore needs the application to have
reloading enabled.

Please, make sure `config.cache_classes` is `false` in the environments that
Ensure that `config.enable_reloading` is `true` in the environments that
Spring manages. That setting is typically configured in
`config/environments/*.rb`. In particular, make sure it is `false` for the
`config/environments/*.rb`. In particular, make sure it is `true` for the
`test` environment.

Note: in versions of Rails before 7.1, the setting is called `cache_classes`,
and it needs to be `false` for Spring to work.

### Usage

For this walkthrough I've generated a new Rails application, and run
@@ -389,6 +392,12 @@ a command runs:
Spring.quiet = true
```

You can also set the initial state of the `quiet` configuration option to true
by setting the `SPRING_QUIET` environment variable before executing Spring.
This is useful if you want to set quiet mode when invoking the Spring executable
in a subprocess, and cannot or prefer not to set it programmatically
via the `Spring.quiet` option in `~/.spring.rb` or the app's `config/spring.rb`.

### Environment variables

The following environment variables are used by Spring:
@@ -413,6 +422,8 @@ The following environment variables are used by Spring:
the long-running Spring server process. By default, this is related to
the socket path; if the socket path is `/foo/bar/spring.sock` the
pidfile will be `/foo/bar/spring.pid`.
* `SPRING_QUIET` - If set, the initial state of the `Spring.quiet`
configuration option will default to `true`.
* `SPRING_SERVER_COMMAND` - The command to run to start up the Spring
server when it is not already running. Defaults to `spring _[version]_
server --background`.
61 changes: 44 additions & 17 deletions lib/spring/application.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
require "spring/boot"
require "set"
require "pty"

module Spring
@@ -11,8 +10,8 @@ def initialize(manager, original_env, spring_env = Env.new)
@original_env = original_env
@spring_env = spring_env
@mutex = Mutex.new
@waiting = Set.new
@clients = Set.new
@waiting = {}
@clients = {}
@preloaded = false
@state = :initialized
@interrupt = IO.pipe
@@ -29,6 +28,11 @@ def state!(val)
@interrupt.last.write "."
end

def spawn_env
env = JSON.load(ENV["SPRING_SPAWN_ENV"].dup).map { |key, value| "#{key}=#{value}" }
env.join(", ") if env.any?
end

def app_env
ENV['RAILS_ENV']
end
@@ -92,8 +96,8 @@ def preload

require Spring.application_root_path.join("config", "application")

unless Rails.respond_to?(:gem_version) && Rails.gem_version >= Gem::Version.new('5.2.0')
raise "Spring only supports Rails >= 5.2.0"
unless Rails.respond_to?(:gem_version) && Rails.gem_version >= Gem::Version.new('6.0.0')
raise "Spring only supports Rails >= 6.0.0"
end

Rails::Application.initializer :ensure_reloading_is_enabled, group: :all do
@@ -120,6 +124,11 @@ def preload

if defined?(Rails) && Rails.application
watcher.add Rails.application.paths["config/initializers"]
Rails::Engine.descendants.each do |engine|
if engine.root.to_s.start_with?(Rails.root.to_s)
watcher.add engine.paths["config/initializers"].expanded
end
end
watcher.add Rails.application.paths["config/database"]
if secrets_path = Rails.application.paths["config/secrets"]
watcher.add secrets_path
@@ -128,7 +137,12 @@ def preload
end

def eager_preload
with_pty { preload }
with_pty do
# we can't see stderr and there could be issues when it's overflown
# see https://github.com/rails/spring/issues/396
STDERR.reopen("/dev/null")
preload
end
end

def run
@@ -150,7 +164,7 @@ def serve(client)
log "got client"
manager.puts

@clients << client
@clients[client] = true

_stdout, stderr, _stdin = streams = 3.times.map { client.recv_io }
[STDOUT, STDERR, STDIN].zip(streams).each { |a, b| a.reopen(b) }
@@ -181,7 +195,7 @@ def serve(client)
pid = fork {
# Make sure to close other clients otherwise their graceful termination
# will be impossible due to reference from this fork.
@clients.select { |c| c != client }.each(&:close)
@clients.each_key { |c| c.close if c != client }

Process.setsid
IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
@@ -245,7 +259,7 @@ def terminate
if exiting?
# Ensure that we do not ignore subsequent termination attempts
log "forced exit"
@waiting.each { |pid| Process.kill("TERM", pid) }
@waiting.each_key { |pid| Process.kill("TERM", pid) }
Kernel.exit
else
state! :terminating
@@ -298,13 +312,26 @@ def shush_backtraces
Kernel.module_eval do
old_raise = Kernel.method(:raise)
remove_method :raise
define_method :raise do |*args|
begin
old_raise.call(*args)
ensure
if $!
lib = File.expand_path("..", __FILE__)
$!.backtrace.reject! { |line| line.start_with?(lib) }
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.2.0')
define_method :raise do |*args, **kwargs|
begin
old_raise.call(*args, **kwargs)
ensure
if $!
lib = File.expand_path("..", __FILE__)
$!.backtrace.reject! { |line| line.start_with?(lib) } unless $!.backtrace.frozen?
end
end
end
else
define_method :raise do |*args|
begin
old_raise.call(*args)
ensure
if $!
lib = File.expand_path("..", __FILE__)
$!.backtrace.reject! { |line| line.start_with?(lib) } unless $!.backtrace.frozen?
end
end
end
end
@@ -337,7 +364,7 @@ def reset_streams
end

def wait(pid, streams, client)
@mutex.synchronize { @waiting << pid }
@mutex.synchronize { @waiting[pid] = true }

# Wait in a separate thread so we can run multiple commands at once
Spring.failsafe_thread {
12 changes: 9 additions & 3 deletions lib/spring/application/boot.rb
Original file line number Diff line number Diff line change
@@ -11,9 +11,15 @@

Signal.trap("TERM") { app.terminate }

Spring::ProcessTitleUpdater.run { |distance|
"spring app | #{app.app_name} | started #{distance} ago | #{app.app_env} mode"
}
Spring::ProcessTitleUpdater.run do |distance|
attributes = [
app.app_name,
"started #{distance} ago",
"#{app.app_env} mode",
app.spawn_env,
].compact
"spring app | #{attributes.join(" | ")}"
end

app.eager_preload if ENV.delete("SPRING_PRELOAD") == "1"
app.run
12 changes: 8 additions & 4 deletions lib/spring/application_manager.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module Spring
class ApplicationManager
attr_reader :pid, :child, :app_env, :spring_env, :status
attr_reader :pid, :child, :app_env, :spawn_env, :spring_env, :status

def initialize(app_env, spring_env)
def initialize(app_env, spawn_env, spring_env)
@app_env = app_env
@spawn_env = spawn_env
@spring_env = spring_env
@mutex = Mutex.new
@state = :running
@@ -100,7 +101,9 @@ def start_child(preload = false)
"RAILS_ENV" => app_env,
"RACK_ENV" => app_env,
"SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV),
"SPRING_PRELOAD" => preload ? "1" : "0"
"SPRING_PRELOAD" => preload ? "1" : "0",
"SPRING_SPAWN_ENV" => JSON.dump(spawn_env),
**spawn_env,
},
"ruby",
*(bundler_dir != RbConfig::CONFIG["rubylibdir"] ? ["-I", bundler_dir] : []),
@@ -123,7 +126,8 @@ def start_wait_thread(pid, child)
# as if it does we're no longer interested in the child
loop do
IO.select([child])
break if child.recv(1, Socket::MSG_PEEK).empty?
peek = child.recv(1, Socket::MSG_PEEK)
break if peek.nil? || peek.empty?
sleep 0.01
end

Loading