diff --git a/Gemfile b/Gemfile index 30c8aab..9f05540 100644 --- a/Gemfile +++ b/Gemfile @@ -33,6 +33,7 @@ gem 'permanent_records' gem 'simple_form', '~> 2' gem 'open4' gem 'ansible' +gem "puma" # While these are not needed by Strano itself, without them installed, any project # that requires them will die when Strano tries to run a cap task. By using @@ -54,7 +55,6 @@ group :development do gem 'guard-rspec' gem 'guard-bundler' gem 'debugger' - gem 'thin' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 50ab8ea..947cc33 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -57,22 +57,19 @@ GEM commonjs (0.2.5) connection_pool (0.9.1) crack (0.3.1) - daemons (1.1.9) database_cleaner (0.7.2) - debugger (1.2.2) + debugger (1.6.0) columnize (>= 0.3.1) - debugger-linecache (~> 1.1.1) - debugger-ruby_core_source (~> 1.1.5) - debugger-linecache (1.1.2) - debugger-ruby_core_source (>= 1.1.1) - debugger-ruby_core_source (1.1.5) + debugger-linecache (~> 1.2.0) + debugger-ruby_core_source (~> 1.2.1) + debugger-linecache (1.2.0) + debugger-ruby_core_source (1.2.2) delayed_job (3.0.2) activesupport (~> 3.0) diff-lcs (1.1.3) dotiw (1.1.1) actionpack (~> 3) erubis (2.7.0) - eventmachine (1.0.0) execjs (1.3.0) multi_json (~> 1.0) factory_girl (3.0.0) @@ -159,6 +156,8 @@ GEM permanent_records (2.1.2) activerecord polyglot (0.3.3) + puma (2.0.1) + rack (>= 1.1, < 2.0) rack (1.4.3) rack-cache (1.2) rack (>= 0.4) @@ -235,10 +234,6 @@ GEM temple (0.4.0) therubyracer (0.10.2) libv8 (~> 3.3.10) - thin (1.5.0) - daemons (>= 1.0.9) - eventmachine (>= 0.12.6) - rack (>= 1.0.0) thor (0.14.6) tilt (1.3.3) treetop (1.4.12) @@ -293,6 +288,7 @@ DEPENDENCIES omniauth-github open4 permanent_records + puma rails (~> 3.2.11) rspec-rails sass-rails (~> 3.2.3) @@ -302,7 +298,6 @@ DEPENDENCIES sinatra slim sqlite3 - thin twitter-bootstrap-rails uglifier (>= 1.0.3) vcr (~> 2) diff --git a/README.md b/README.md index 00d8552..ab7d0e5 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,14 @@ Background Processing Background processing of tasks and repo management is taken care of by the excellent [Sidekiq](https://github.com/mperham/sidekiq). Run the queue like this: - bundle exec sidekiq + bundle exec rake sidekiq:start You can then monitor your queue at `http://YOUR-STRANO-APP/sidekiq`. Check out the [Sidekiq Wiki](https://github.com/mperham/sidekiq/wiki) for assistance on Sidekiq and its options. +You can stop Sidekiq like this: + + bundle exec rake sidekiq:stop + License ------- diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..6366ca7 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,114 @@ +#!/usr/bin/env puma + +# Start Puma with next command: +# RAILS_ENV=production bundle exec puma -e production -C ./config/puma.rb + +application_path = '.' + +# The directory to operate out of. +# +# The default is the current directory. +# +directory application_path + +# Set the environment in which the rack's app will run. +# +# The default is “development”. +# +environment = :production + +# Daemonize the server into the background. Highly suggest that +# this be combined with “pidfile” and “stdout_redirect”. +# +# The default is “false”. +# +daemonize true + +# Store the pid of the server in the file at “path”. +# +pidfile "#{application_path}/tmp/pids/puma.pid" + +# Use “path” as the file to store the server info state. This is +# used by “pumactl” to query and control the server. +# +state_path "#{application_path}/tmp/pids/puma.state" + +# Redirect STDOUT and STDERR to files specified. The 3rd parameter +# (“append”) specifies whether the output is appended, the default is +# “false”. +# +stdout_redirect "#{application_path}/log/puma.stdout.log", "#{application_path}/log/puma.stderr.log" +# stdout_redirect '/u/apps/lolcat/log/stdout', '/u/apps/lolcat/log/stderr', true + +# Disable request logging. +# +# The default is “false”. +# +# quiet + +# Configure “min” to be the minimum number of threads to use to answer +# requests and “max” the maximum. +# +# The default is “0, 16”. +# +# threads 0, 16 + +# Bind the server to “url”. “tcp://”, “unix://” and “ssl://” are the only +# accepted protocols. +# +# The default is “tcp://0.0.0.0:9292”. +# +# bind 'tcp://0.0.0.0:9292' +bind "unix://#{application_path}/tmp/sockets/strano.socket" + +# Instead of “bind 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'” you +# can also use the “ssl_bind” option. +# +# ssl_bind '127.0.0.1', '9292', { key: path_to_key, cert: path_to_cert } + +# Code to run before doing a restart. This code should +# close log files, database connections, etc. +# +# This can be called multiple times to add code each time. +# +# on_restart do +# puts 'On restart...' +# end + +# Command to use to restart puma. This should be just how to +# load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments +# to puma, as those are the same as the original process. +# +# restart_command '/u/app/lolcat/bin/restart_puma' + +# === Cluster mode === + +# How many worker processes to run. +# +# The default is “0”. +# +# workers 2 + +# Code to run when a worker boots to setup the process before booting +# the app. +# +# This can be called multiple times to add hooks. +# +# on_worker_boot do +# puts 'On worker boot...' +# end + +# === Puma control rack application === + +# Start the puma control rack application on “url”. This application can +# be communicated with to control the main server. Additionally, you can +# provide an authentication token, so all requests to the control server +# will need to include that token as a query parameter. This allows for +# simple authentication. +# +# Check out https://github.com/puma/puma/blob/master/lib/puma/app/status.rb +# to see what the app has available. +# +# activate_control_app 'unix:///var/run/pumactl.sock' +# activate_control_app 'unix:///var/run/pumactl.sock', { auth_token: '12345' } +# activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true } diff --git a/doc/install/ubuntu_debian.md b/doc/install/ubuntu_debian.md new file mode 100644 index 0000000..720060e --- /dev/null +++ b/doc/install/ubuntu_debian.md @@ -0,0 +1,172 @@ +Installing Strano on Debian or Ubuntu +===================================== + +The following instructions will guide you through running Strano on Debian or Ubuntu Linux, using [Nginx](http://wiki.nginx.org/Main) and [Puma](http://puma.io)/ + +These instructions assume you will be running Strano as user `deploy`, with Strano installed to `/home/deploy/strano`. If this is not the case, edit `/etc/init.d/strano`, and `config/puma.rb`, and the Nginx configuration accordingly. + +(It is probably possible with some minor tweaking to run under another web server, such as Apache, or Linux distro, such as CentOS or RHEL. If you have done so, it would be appreciated if you could provide instructions for your setup. Fork the project and submit a pull request with complete installation instructions.) + + +TODO +---- + +Document creating SSH key, adding to Github, configuring strano.yml. + + +Install Dependencies +-------------------- + + sudo apt-get update + sudo apt-get install redis-server nginx + +(On Debian, `sudo` is not installed by default; run `apt-get install sudo` as root if necessary). + + +Create deploy User +------------------ + +Create user `deploy` for running Strano with the following command: + + sudo adduser --disabled-login --gecos 'Deploy' deploy + +Since this user will be used to deploy your applications, add it to the web server group (such as `www-data`): + + sudo usermod -a -G www-data deploy + + +Create GitHub Application +------------------------- + +Strano will use GitHub to authenticate users and to access your project source code. + +1. Log on to Github + +2. For personal projects, [create a new developer application](https://github.com/settings/applications/new). + + For organization projects, go to https://github.com/organizations/MY-ORGANIZATION/settings/applications/new . + +3. Enter the following: + + **Application Name:** Strano + + **Main URL:** http://strano.myserver.com/ + + **Callback URL:** http://strano.myserver.com/ + + Substitute the hostname you will use to access Strano. You may also serve it via HTTPS. This need not be a public server. It can be behind a firewall, with a private DNS name, if you wish. + +4. Note the **Client ID** and **Client Secret**. You will need these later to configure Strano. + + +Install Ruby +------------ + +Strano requires Ruby 1.9.3. The recommended way to install Ruby is with [RVM](https://rvm.io): + + sudo su - deploy + \curl -L https://get.rvm.io | bash -s stable --ruby=1.9.3 + +This will take several minutes. When in completes, install [Bundler](http://gembundler.com): + + source /home/deploy/.rvm/scripts/rvm + gem install bundler + + +Install Strano +-------------- + + # switch to deploy user, if necessary + sudo su - deploy + + git clone git://github.com/joelmoss/strano.git + cd strano + + bundle install + +(If you get an error message about `debugger-linecache`, run `bundle update debugger`, then re-run `bundle install`). + +Precompile Rails assets (otherwise, you'll get an error like `ActionView::Template::Error (application.css isn't precompiled):` when running Strano): + + rake assets:precompile + +Create the production database. **Be sure to run this command only once, or you'll overwrite your Strano database**: + + bundle exec rake db:setup RAILS_ENV=production + + +Configure Strano +---------------- + +Strano needs a database to store its data. For now, we'll simply use Sqlite, but for production, you should switch to another database, such as MySQL or PostgreSQL. + + cp config/database.example.yml config/database.yml + cp config/strano.example.yml config/strano.yml + +Edit `config/strano.yml`, and set these values: + + public_ssh_key: MYPUBLICKEY + github_key: + github_secret: + + +Install Init Script +------------------- + + sudo cp script/init.d/strano /etc/init.d/ + sudo chmod +x /etc/init.d/strano + sudo update-rc.d strano defaults 21 + + +Configurate Nginx +----------------- + +Other web servers may also be used, though these instructions assume you will be running Nginx. + +Create a file named `/etc/nginx/sites-enabled/strano` with the following content (substitute your web hostname for `strano.myserver.com`): + + upstream strano { + server unix:/home/deploy/strano/tmp/sockets/strano.socket; + } + + server { + listen *:80 default_server; + server_name strano.myserver.com; + root /home/deploy/strano/public; + + access_log /var/log/nginx/strano_access.log; + error_log /var/log/nginx/strano_error.log; + + location / { + try_files $uri $uri/index.html $uri.html @strano; + } + + location @strano { + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; + + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + + proxy_pass http://strano; + } + } + +Enable this Nginx site: + + cd /etc/nginx/sites-enabled/ + sudo ln -sfn ../sites-available/strano . + +Reload Nginx configuration: + + sudo service nginx reload + + +Run Strano +---------- + + sudo service strano start + +Now, test the application by going to http://strano.myhost.com . You should be able to log on using your GitHub credentials, and import any of your Github projects into Strano. diff --git a/lib/tasks/sidekiq.rake b/lib/tasks/sidekiq.rake new file mode 100644 index 0000000..f0da4c2 --- /dev/null +++ b/lib/tasks/sidekiq.rake @@ -0,0 +1,15 @@ +namespace :sidekiq do + desc "Strano | Stop sidekiq" + task :stop do + system "bundle exec sidekiqctl stop #{pidfile}" + end + + desc "Strano | Start sidekiq" + task :start do + system "nohup bundle exec sidekiq -q runner,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &" + end + + def pidfile + Rails.root.join("tmp", "pids", "sidekiq.pid") + end +end diff --git a/script/init.d/strano b/script/init.d/strano new file mode 100644 index 0000000..cf86fcc --- /dev/null +++ b/script/init.d/strano @@ -0,0 +1,136 @@ +#! /bin/bash + +# Strano +# App Version: 2.2 +# Adapted from https://github.com/gitlabhq/gitlab-recipes/blob/5-1-stable/init.d/gitlab + +### BEGIN INIT INFO +# Provides: strano +# Required-Start: $local_fs $remote_fs $network $syslog redis-server +# Required-Stop: $local_fs $remote_fs $network $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Strano +# Description: Strano +### END INIT INFO + +USERNAME=deploy +APP_ROOT="/home/$USERNAME/strano" +DAEMON_OPTS="-C $APP_ROOT/config/puma.rb -e production" +PID_PATH="$APP_ROOT/tmp/pids" +SOCKET_PATH="$APP_ROOT/tmp/sockets" +WEB_SERVER_PID="$PID_PATH/puma.pid" +SIDEKIQ_PID="$PID_PATH/sidekiq.pid" +STOP_SIDEKIQ="RAILS_ENV=production bundle exec rake sidekiq:stop" +START_SIDEKIQ="RAILS_ENV=production bundle exec rake sidekiq:start" +NAME="Strano" +DESC="Strano" + +check_pid(){ + if [ -f $WEB_SERVER_PID ]; then + PID=`cat $WEB_SERVER_PID` + SPID=`cat $SIDEKIQ_PID` + STATUS=`ps aux | grep $PID | grep -v grep | wc -l` + else + STATUS=0 + PID=0 + fi +} + +start() { + cd $APP_ROOT + check_pid + if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then + # Program is running, exit with error code 1. + echo "Error! $DESC is currently running!" + exit 1 + else + if [ `whoami` = root ]; then + sudo -u $USERNAME -H bash -l -c "mkdir -p $SOCKET_PATH" + if [ ! -S $SOCKET_PATH/ssh-agent.sock ]; then + sudo -u $USERNAME -H bash -l -c "eval \`ssh-agent -a $SOCKET_PATH/ssh-agent.sock\`; ssh-add" + fi + sudo -u $USERNAME -H bash -l -c "RAILS_ENV=production bundle exec puma $DAEMON_OPTS" + sudo -u $USERNAME -H bash -l -c "mkdir -p $PID_PATH && $START_SIDEKIQ > /dev/null 2>&1 &" + echo "$DESC started" + fi + fi +} + +stop() { + cd $APP_ROOT + check_pid + if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then + ## Program is running, stop it. + kill -QUIT `cat $WEB_SERVER_PID` + sudo -u $USERNAME -H bash -l -c "mkdir -p $PID_PATH && $STOP_SIDEKIQ > /dev/null 2>&1 &" + rm "$WEB_SERVER_PID" >> /dev/null + echo "$DESC stopped" + else + ## Program is not running, exit with error. + echo "Error! $DESC not started!" + exit 1 + fi +} + +restart() { + cd $APP_ROOT + check_pid + if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then + echo "Restarting $DESC..." + kill -USR2 `cat $WEB_SERVER_PID` + sudo -u $USERNAME -H bash -l -c "mkdir -p $PID_PATH && $STOP_SIDEKIQ > /dev/null 2>&1 &" + if [ `whoami` = root ]; then + sudo -u $USERNAME -H bash -l -c "mkdir -p $PID_PATH && $START_SIDEKIQ > /dev/null 2>&1 &" + fi + echo "$DESC restarted." + else + echo "Error, $NAME not running!" + exit 1 + fi +} + +status() { + cd $APP_ROOT + check_pid + if [ "$PID" -ne 0 -a "$STATUS" -ne 0 ]; then + echo "$DESC / Puma with PID $PID is running." + echo "$DESC / Sidekiq with PID $SPID is running." + else + echo "$DESC is not running." + exit 1 + fi +} + +## Check to see if we are running as root first. +## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" + exit 1 +fi + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + reload|force-reload) + echo -n "Reloading $NAME configuration: " + kill -HUP `cat $PID` + echo "done." + ;; + status) + status + ;; + *) + echo "Usage: sudo service strano {start|stop|restart|reload}" >&2 + exit 1 + ;; +esac + +exit 0