diff --git a/Gemfile b/Gemfile
index 5b7a6c7db..91610629b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,8 +8,8 @@ gem "activesupport"
gem "faraday-retry"
gem "octokit"
gem "omniauth-github"
+gem "puma"
gem "sinatra"
-gem "unicorn"
group :development do
gem "guard"
diff --git a/Gemfile.lock b/Gemfile.lock
index 11d3cccb9..c99618508 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -47,7 +47,6 @@ GEM
concurrent-ruby (~> 1.0)
json (2.7.1)
jwt (2.7.1)
- kgio (2.11.4)
language_server-protocol (3.17.0.3)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
@@ -62,6 +61,7 @@ GEM
nenv (0.3.0)
net-http (0.4.1)
uri
+ nio4r (2.7.0)
notiffany (0.1.3)
nenv (~> 0.1)
shellany (~> 0.0)
@@ -93,6 +93,8 @@ GEM
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.4)
+ puma (6.4.2)
+ nio4r (~> 2.0)
racc (1.7.3)
rack (3.0.8)
rack-protection (4.0.0)
@@ -101,7 +103,6 @@ GEM
rack-session (2.0.0)
rack (>= 3.0.0)
rainbow (3.1.1)
- raindrops (0.20.1)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
@@ -142,9 +143,6 @@ GEM
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
- unicorn (6.1.0)
- kgio (~> 2.6)
- raindrops (~> 0.7)
uri (0.13.0)
version_gem (1.1.3)
@@ -158,9 +156,9 @@ DEPENDENCIES
guard-process
octokit
omniauth-github
+ puma
rubocop
sinatra
- unicorn
RUBY VERSION
ruby 3.2.3p157
diff --git a/Guardfile b/Guardfile
index ed535fff2..8ac6a5e95 100644
--- a/Guardfile
+++ b/Guardfile
@@ -4,5 +4,5 @@ guard "process", name: "server", command: "script/server" do
watch "config.ru"
watch "Gemfile.lock"
watch "web/app.rb"
- watch "web/unicorn.rb"
+ watch "web/puma.rb"
end
diff --git a/README.md b/README.md
index 9622aadf2..b8014ceaa 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,7 @@ Strap is also available as a Docker image on [Docker Hub (`mikemcquaid/strap`)](
- `GITHUB_KEY`: the GitHub.com Application Client ID.
- `GITHUB_SECRET`: the GitHub.com Application Client Secret.
- `SESSION_SECRET`: the secret used for cookie session storage.
-- `WEB_CONCURRENCY`: the number of Unicorn (web server) processes to run (defaults to 3).
+- `WEB_CONCURRENCY`: the number of Puma (web server) threads to run (defaults to 3).
- `STRAP_ISSUES_URL`: the URL where users should file issues (defaults to no URL).
- `STRAP_BEFORE_INSTALL`: instructions displayed in the web application for users to follow before installing Strap (wrapped in `
` tags).
- `CUSTOM_HOMEBREW_TAP`: an optional Homebrew tap to install with `brew tap`. Specify multiple arguments to brew tap by separating values with spaces.
diff --git a/script/server b/script/server
index 45aaa55e4..69a6a45fc 100755
--- a/script/server
+++ b/script/server
@@ -4,4 +4,4 @@ set -euo pipefail
cd "$(dirname "$0")/.."
export PORT="${PORT:-3000}"
-bundle exec unicorn --port "$PORT" --config-file web/unicorn.rb
+bundle exec puma --port "$PORT" --config web/puma.rb
diff --git a/script/tests b/script/tests
index 5513dc437..44e857d92 100755
--- a/script/tests
+++ b/script/tests
@@ -1,7 +1,10 @@
#!/bin/bash
set -xeuo pipefail
+
cd "$(dirname "$0")/.."
+export PUMA_PIDFILE="puma.pid"
+
tests_cleanup() {
if [[ -n ${SERVER_PID-} ]]; then
kill "${SERVER_PID}"
@@ -11,14 +14,12 @@ tests_cleanup() {
trap "tests_cleanup" EXIT
script/bootstrap
-script/server &>/dev/null &
-SERVER_PID="$!"
+script/server &
+sleep 5
-sleep 15
+SERVER_PID=$(cat "${PUMA_PIDFILE}")
curl -O http://localhost:3000/strap.sh
curl -sSL -D - -o /dev/null http://localhost:3000/strap.sh | grep -i "x-frame-options: DENY"
kill "$SERVER_PID"
-wait "$SERVER_PID" || true
-unset SERVER_PID
diff --git a/web/puma.rb b/web/puma.rb
new file mode 100644
index 000000000..f3be60b2c
--- /dev/null
+++ b/web/puma.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+# Puma can serve each request in a thread from an internal thread pool.
+# The `threads` method setting takes two numbers: a minimum and maximum.
+# Any libraries that use thread pools should be configured to match
+# the maximum value specified for Puma.
+max_threads_count = ENV.fetch("WEB_CONCURRENCY", 3)
+min_threads_count = ENV.fetch("WEB_CONCURRENCY") { max_threads_count }
+threads min_threads_count, max_threads_count
+
+# Specifies the `worker_timeout` threshold that Puma will use to wait before
+# terminating a worker in development environments.
+worker_timeout 3600 if ENV.fetch("RACK_ENV", "development") == "development"
+
+# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
+port ENV.fetch("PORT", 3000)
+
+# Specifies the `environment` that Puma will run in.
+environment ENV.fetch("RACK_ENV", "development")
+
+# Optionally specifies the `pidfile` that Puma will use.
+if (puma_pidfile = ENV.fetch("PUMA_PIDFILE", nil))
+ pidfile puma_pidfile
+end
+
+# needed to avoid multiple workers from having different session secrets
+require "securerandom"
+ENV["SESSION_SECRET"] = SecureRandom.hex(64) unless ENV["SESSION_SECRET"]
diff --git a/web/unicorn.rb b/web/unicorn.rb
deleted file mode 100644
index 7201a594d..000000000
--- a/web/unicorn.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-worker_processes ENV.fetch("WEB_CONCURRENCY", 3).to_i
-
-# needed to avoid multiple workers from having different session secrets
-require "securerandom"
-ENV["SESSION_SECRET"] = SecureRandom.hex(64) unless ENV["SESSION_SECRET"]