diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..eb0d1da --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +name: Run Test + +on: + push: + branches: + - master + pull_request: + +jobs: + rake: + runs-on: ubuntu-latest + + strategy: + matrix: + ruby-version: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3'] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + - name: Run Rake Task + run: bundle exec rake + + - name: Upload Capistrano log + if: always() + uses: actions/upload-artifact@v4 + with: + name: capistrano-log-${{ matrix.ruby-version }} + path: test/log/*.log diff --git a/.gitignore b/.gitignore index 7c45f23..fd271a3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .idea .yardoc Gemfile.lock +!test/app/Gemfile.lock InstalledFiles _yardoc coverage @@ -15,5 +16,7 @@ rdoc spec/reports test/tmp test/version_tmp +test/log/* +!test/log/.gitkeep tmp *.iml diff --git a/Gemfile b/Gemfile index 27f6c33..b8dc8e6 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,5 @@ source 'https://rubygems.org' # Specify your gem's dependencies in capistrano-puma.gemspec gemspec +gem 'minitest' + diff --git a/Rakefile b/Rakefile index 2995527..074d59f 100644 --- a/Rakefile +++ b/Rakefile @@ -1 +1,11 @@ require "bundler/gem_tasks" + +require 'rake/testtask' + +Rake::TestTask.new do |t| + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +task default: :test diff --git a/lib/capistrano/puma.rb b/lib/capistrano/puma.rb index e861101..8fc2cf4 100644 --- a/lib/capistrano/puma.rb +++ b/lib/capistrano/puma.rb @@ -53,7 +53,11 @@ def compiled_template_puma(from, role) File.expand_path("../templates/#{from}.rb.erb", __FILE__) ].detect { |path| File.file?(path) } erb = File.read(file) - StringIO.new(ERB.new(erb, trim_mode: '-').result(binding)) + if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.6') + StringIO.new(ERB.new(erb, nil, '-').result(binding)) + else + StringIO.new(ERB.new(erb, trim_mode: '-').result(binding)) + end end def template_puma(from, to, role) diff --git a/lib/capistrano/tasks/systemd.rake b/lib/capistrano/tasks/systemd.rake index 2b9f169..3e5902a 100644 --- a/lib/capistrano/tasks/systemd.rake +++ b/lib/capistrano/tasks/systemd.rake @@ -52,7 +52,7 @@ namespace :puma do git_plugin.execute_systemd("enable", fetch(:puma_service_unit_name) + ".socket") if fetch(:puma_enable_socket_service) if fetch(:puma_systemctl_user) != :system && fetch(:puma_enable_lingering) - execute :loginctl, "enable-linger", fetch(:puma_lingering_user) + sudo "loginctl enable-linger #{fetch(:puma_lingering_user)}" end end end diff --git a/test/Capfile b/test/Capfile new file mode 100644 index 0000000..5d09e40 --- /dev/null +++ b/test/Capfile @@ -0,0 +1,40 @@ +# Load DSL and set up stages +require "capistrano/setup" + +# Include default deployment tasks +require "capistrano/deploy" + +# Load the SCM plugin appropriate to your project: +# +# require "capistrano/scm/hg" +# install_plugin Capistrano::SCM::Hg +# or +# require "capistrano/scm/svn" +# install_plugin Capistrano::SCM::Svn +# or +require "capistrano/scm/git" +install_plugin Capistrano::SCM::Git + +# Include tasks from other gems included in your Gemfile +# +# For documentation on these, see for example: +# +# https://github.com/capistrano/rvm +# https://github.com/capistrano/rbenv +# https://github.com/capistrano/chruby +# https://github.com/capistrano/bundler +# https://github.com/capistrano/rails +# https://github.com/capistrano/passenger +# +# require "capistrano/rvm" +# require "capistrano/rbenv" +# require "capistrano/chruby" +# require "capistrano/bundler" +# require "capistrano/rails/assets" +# require "capistrano/rails/migrations" +# require "capistrano/passenger" +require 'capistrano/puma' +install_plugin Capistrano::Puma +install_plugin Capistrano::Puma::Systemd +# Load custom tasks from `lib/capistrano/tasks` if you have any defined +Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r } diff --git a/test/Dockerfile b/test/Dockerfile new file mode 100644 index 0000000..455391a --- /dev/null +++ b/test/Dockerfile @@ -0,0 +1,14 @@ +FROM ruby:3.3 + +RUN apt-get update && apt-get install -y systemd systemd-sysv openssh-server sudo && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN groupadd --system --gid 1000 willnet && \ + useradd willnet --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ + chown -R willnet:willnet /var +RUN echo "willnet ALL=(ALL) NOPASSWD:ALL" | tee /etc/sudoers.d/willnet +RUN echo 'willnet:password' | chpasswd + +EXPOSE 22 +CMD ["/lib/systemd/systemd"] diff --git a/test/app/Gemfile b/test/app/Gemfile new file mode 100644 index 0000000..10044f4 --- /dev/null +++ b/test/app/Gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "sinatra", "~> 4.0" +gem "puma", "~> 6.4" diff --git a/test/app/Gemfile.lock b/test/app/Gemfile.lock new file mode 100644 index 0000000..643b1c0 --- /dev/null +++ b/test/app/Gemfile.lock @@ -0,0 +1,34 @@ +GEM + remote: https://rubygems.org/ + specs: + base64 (0.2.0) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) + nio4r (2.7.3) + puma (6.4.2) + nio4r (~> 2.0) + rack (3.1.3) + rack-protection (4.0.0) + base64 (>= 0.1.0) + rack (>= 3.0.0, < 4) + rack-session (2.0.0) + rack (>= 3.0.0) + ruby2_keywords (0.0.5) + sinatra (4.0.0) + mustermann (~> 3.0) + rack (>= 3.0.0, < 4) + rack-protection (= 4.0.0) + rack-session (>= 2.0.0, < 3) + tilt (~> 2.0) + tilt (2.3.0) + +PLATFORMS + arm64-darwin-23 + ruby + +DEPENDENCIES + puma (~> 6.4) + sinatra (~> 4.0) + +BUNDLED WITH + 2.5.11 diff --git a/test/app/config.ru b/test/app/config.ru new file mode 100644 index 0000000..ac0f24d --- /dev/null +++ b/test/app/config.ru @@ -0,0 +1,7 @@ +require 'sinatra' + +get '/' do + 'Hello, Puma' +end + +run Sinatra::Application diff --git a/test/app/config/puma.rb b/test/app/config/puma.rb new file mode 100644 index 0000000..219165f --- /dev/null +++ b/test/app/config/puma.rb @@ -0,0 +1 @@ +port 3000 diff --git a/test/config/deploy.rb b/test/config/deploy.rb new file mode 100644 index 0000000..e6ecd5d --- /dev/null +++ b/test/config/deploy.rb @@ -0,0 +1,10 @@ +lock "~> 3.19.1" + +set :application, "capistrano-puma" +set :repo_url, "https://github.com/seuros/capistrano-puma.git" +set :repo_tree, 'test/app' + +set :branch, ENV['BRANCH'] || 'master' + +# Without the following settings, puma will fail to start. +append :linked_dirs, "log" diff --git a/test/config/deploy/production.rb b/test/config/deploy/production.rb new file mode 100644 index 0000000..b33a826 --- /dev/null +++ b/test/config/deploy/production.rb @@ -0,0 +1,8 @@ +server "localhost", user: "willnet", roles: %w{app db web}, port: 8022 + +set :ssh_options, { + user: 'willnet', + password: 'password', + forward_agent: false, + auth_methods: %w(password) +} diff --git a/test/deploy_test.rb b/test/deploy_test.rb new file mode 100644 index 0000000..b95e062 --- /dev/null +++ b/test/deploy_test.rb @@ -0,0 +1,51 @@ +require 'minitest/autorun' +require 'net/http' + +Minitest.after_run do + DeployTest.container_id && system("docker stop #{DeployTest.container_id}") +end + +class DeployTest < Minitest::Test + class << self + attr_accessor :container_id + end + + def self.before_suite + system 'docker build -t capistrano-puma-test-server test' + self.container_id = `docker run -d --privileged -p 8022:22 -p 3000:3000 capistrano-puma-test-server` + sleep 1 + end + + before_suite + + def retry_get_response(uri, limit = 5) + response = nil + limit.times do + begin + response = Net::HTTP.get_response(URI.parse(uri)) + rescue Errno::ECONNRESET, EOFError + sleep 1 + else + break + end + end + response + end + + def test_deploy + Dir.chdir('test') do + system 'cap production puma:install' + system 'cap production deploy' + response = retry_get_response('http://localhost:3000') + assert_equal '200', response.code + system 'cap production puma:stop' + sleep 1 + assert_raises(Errno::ECONNRESET) do + Net::HTTP.get_response(URI.parse('http://localhost:3000')) + end + system 'cap production puma:start' + response = retry_get_response('http://localhost:3000') + assert_equal '200', response.code + end + end +end diff --git a/test/log/.gitkeep b/test/log/.gitkeep new file mode 100644 index 0000000..e69de29