pointer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ /.idea
19
+
20
+ # Do not commit this file, it has root password
21
+ /config/pointer.rb
22
+
23
+ /test_repo
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pointer.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Slava Vishnyakov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Pointer
2
+
3
+ Super-simple deployment! (Yes, I'll write a README)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'pointer'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install pointer
18
+
19
+ ## Contributing
20
+
21
+ 1. Fork it
22
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
23
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
24
+ 4. Push to the branch (`git push origin my-new-feature`)
25
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/TODO.md ADDED
@@ -0,0 +1,34 @@
1
+ ## Issues
2
+
3
+ vagrant for tests
4
+ touch: cannot touch `tmp/restart.txt': No such file or directory (via deploy)
5
+
6
+ automate bitbicket deployment key installation
7
+ что если nginx уже поставлен?
8
+ support apache
9
+ что если apache уже поставлен?
10
+ support capistrano
11
+ check that mina installed
12
+ ufw
13
+ fail2ban
14
+ postgres
15
+ mysql
16
+ mongo ?
17
+ revoke sudo nopasswd
18
+ webhook acceptor - min processes
19
+ Assumes 'remote.origin'
20
+ Fix locales (/etc/environment) on Ubuntu
21
+ что может пойти не так с публично доступным deploy? add md5=...
22
+ remove all https://raw.github.com/slava-vishnyakov/useful-stuff/master/init.d-nginx.conf
23
+ detect bitbucket keys https://bitbucket.org/username/test2/admin/deploy-keys
24
+
25
+ once installed postgres will not allow second creation
26
+ local git hosting on server? bad idea, but really easy
27
+
28
+ @ssh.exec! -> expect_success
29
+ session identifier shared
30
+ "your website is at"
31
+
32
+ check for gem pg in Gemfile
33
+
34
+ backups?
data/bin/pointer ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pointer'
4
+
5
+ config_file = 'config/pointer.rb'
6
+
7
+ @options = {}
8
+ def set(name, value)
9
+ @options[name] = value
10
+ end
11
+
12
+ if ARGV[0] == 'init'
13
+ FileUtils.mkdir('config') unless File.exists?('config')
14
+
15
+ if File.exists? (config_file)
16
+ puts "File '#{config_file}' already exists"
17
+ exit
18
+ else
19
+ FileUtils.cp(File.expand_path(File.dirname(__FILE__)) + '/../config/pointer.rb.default', config_file)
20
+ puts "Please edit '#{config_file}'"
21
+ end
22
+
23
+ unless File.exists?('.git')
24
+ puts "Please run it from Git repository"
25
+ exit
26
+ end
27
+
28
+ unless File.exists?('config.ru')
29
+ puts "'config.ru' file is required"
30
+ exit
31
+ end
32
+
33
+ gitignore = File.exists?('.gitignore') ? IO.read('.gitignore') : ''
34
+
35
+ unless gitignore.include? '/' + config_file
36
+ gitignore = gitignore.rstrip + "\n\n# Do not commit this file, it has root password\n" + '/' + config_file
37
+ IO.write('.gitignore', gitignore)
38
+ end
39
+
40
+ elsif ARGV[0] == 'deploy'
41
+ require './' + config_file
42
+ Pointer::EasyDeploy.new.run! @options
43
+
44
+ elsif ARGV[0] == 'ssh'
45
+ require './' + config_file
46
+ system("ssh -i #{@options[:private_key]} -p '#{@options[:ssh_port]}' #{@options[:rails_user]}@#{@options[:ssh_host]}")
47
+
48
+ elsif ARGV[0] == 'ssh:log'
49
+ require './' + config_file
50
+ system("ssh -i #{@options[:private_key]} -p '#{@options[:ssh_port]}' #{@options[:rails_user]}@#{@options[:ssh_host]} \"tail --lines=1000 #{@options[:site_dir]}/current/log/production.log\" | less")
51
+
52
+ else
53
+ puts "You can run those:"
54
+ puts " pointer init"
55
+ puts " pointer deploy"
56
+ puts " pointer ssh"
57
+ puts " pointer ssh:log"
58
+ end
@@ -0,0 +1,60 @@
1
+ # What is your website name?
2
+
3
+ host = 'website.com'
4
+ set :host, host
5
+
6
+ # Where do we deploy this project?
7
+ # Usually same as above, so we use the same variable
8
+
9
+ set :ssh_host, host
10
+
11
+ # Yes, we need super-user for now, in future - maybe sudoer will suffice
12
+ # Don't worry, this file (config/pointer.rb) will not be commited to your Git repo
13
+ # this file is needed only once to config the server, so your root password
14
+ # will not be compromised
15
+
16
+ set :ssh_user, 'root'
17
+ set :ssh_port, 22
18
+ set :password, 'password_of_root'
19
+
20
+ # Your public and private key that will be used for deployment
21
+
22
+ set :public_key, "~/.ssh/id_rsa.pub"
23
+ set :private_key, "~/.ssh/id_rsa"
24
+
25
+ # Less privileged user that you will use to deploy the project
26
+
27
+ rails_user = 'rails'
28
+ set :rails_user, rails_user
29
+
30
+ # Configuration of your website, by default - it's same as ssh_host
31
+
32
+ set :site_host, host
33
+ set :site_port, 80
34
+ set :site_dir, "/home/#{rails_user}/applications/#{host}"
35
+
36
+ # Do we plan to host your site on BitBucket?
37
+ # BitBucket is the only reputable repo hosting that I know that allows free private repositories
38
+
39
+ set :git_repo, 'git@bitbucket.org:repo/repo.git'
40
+
41
+ # Currently only NginxPassenger is supported
42
+ # in future :apache will be supported too
43
+
44
+ set :web_server, :nginx
45
+
46
+ # For now we only support mina,
47
+ # in future we will support :capistrano
48
+
49
+ set :deployer, :mina
50
+
51
+ # Do you need to setup automatic updates using WebHook,
52
+ # so that when you push the repo - it gets automatically deployed
53
+
54
+ set :deployer_application, true
55
+
56
+ # Do we need to install and create a database for you?
57
+ # For now only :postgres is supported
58
+ # The database will be created and attached via database.yml
59
+
60
+ set :database, :postgres
data/lib/pointer.rb ADDED
@@ -0,0 +1,74 @@
1
+ require "pointer/version"
2
+ require 'active_support/secure_random'
3
+
4
+ Dir[File.expand_path(File.dirname(__FILE__) + '/pointer/**.rb')].each do |file|
5
+ require file
6
+ end
7
+
8
+ module Pointer
9
+ class EasyDeploy
10
+ include Postgres
11
+ include Helpers
12
+ include Variables
13
+ include SshHelpers
14
+ include BitBucket
15
+ include DeployerApplication
16
+ include Mina
17
+ include NginxPassenger
18
+ include RailsUser
19
+ include Rvm
20
+
21
+ def run!(options)
22
+ @options = options
23
+
24
+ assert_git()
25
+ check_mina_prereqs()
26
+
27
+ with_root_ssh do
28
+ # delete_rails_user()
29
+ create_rails_user()
30
+ upload_public_key()
31
+ add_sudo()
32
+ end
33
+
34
+ with_ssh do
35
+ test_connection()
36
+
37
+ install_rvm()
38
+
39
+ if nginx
40
+ install_passenger()
41
+ create_site_config()
42
+ end
43
+
44
+ if mina
45
+ mina_init()
46
+ end
47
+
48
+ if postgres
49
+ @db_config = install_postgres()
50
+ end
51
+
52
+ print_deploy_key()
53
+
54
+ if bitbucket
55
+ add_bit_bucket_host()
56
+ end
57
+
58
+ if mina
59
+ mina_deploy()
60
+ end
61
+
62
+ if deployer_application
63
+ install_deployer()
64
+ end
65
+
66
+ revoke_sudo()
67
+ end
68
+ end
69
+
70
+
71
+ end
72
+ end
73
+
74
+
@@ -0,0 +1,25 @@
1
+ module Pointer
2
+ module BitBucket
3
+ def add_bit_bucket_host()
4
+ what "Use this as deploy key"
5
+ puts '----'
6
+ puts get_file_contents('/home/rails/.ssh/id_rsa.pub')
7
+ puts '----'
8
+
9
+ if bitbucket
10
+ if @ssh.exec!('bash -lc "ssh -o StrictHostKeyChecking=no git@bitbucket.org"') =~ /Permission denied/
11
+ puts "Press ENTER when you added this deploy key to repository".red
12
+ STDIN.readline()
13
+ end
14
+ else
15
+ puts "You need to ssh into your machine as 'ssh #{rails_user}@#{host} -p #{port}'"
16
+ puts "then connect via ssh to your repository hosting, like so 'ssh git@github.com' and accept the key"
17
+ puts "Then you need to install the deploy key above"
18
+ puts "Press ENTER when you added this deploy key to repository".red
19
+ STDIN.readline()
20
+ end
21
+ end
22
+
23
+
24
+ end
25
+ end
@@ -0,0 +1,52 @@
1
+ module Pointer
2
+ module DeployerApplication
3
+ def install_deployer()
4
+ deployer_port = site_port.to_i + 8000
5
+ # TODO: detect if mina and sinatra already installed
6
+ puts rvm! "gem install mina"
7
+ puts rvm! "gem install sinatra"
8
+
9
+ local_key = get_file_contents('/home/rails/.ssh/id_rsa.pub')
10
+ ensure_file_contains("/home/rails/.ssh/authorized_keys", local_key)
11
+ puts @ssh.exec! "ssh -o StrictHostKeyChecking=no rails@#{host} pwd"
12
+
13
+ puts @ssh.exec! "mkdir -p applications/pointer/#{host}-#{site_port}/public"
14
+
15
+
16
+ server = "
17
+ server {
18
+ listen #{deployer_port};
19
+ server_name #{host};
20
+ passenger_enabled on;
21
+ passenger_user rails;
22
+ passenger_max_requests 10;
23
+ root /home/rails/applications/pointer/#{host}-#{site_port}/public;
24
+ }
25
+ "
26
+
27
+ put_file_contents("/opt/nginx/conf/rails-sites/pointer-#{host}-#{deployer_port}.conf", server)
28
+
29
+ mtime = "#{host}-#{site_port}"
30
+ config_ru = "
31
+ require 'sinatra'
32
+
33
+ post '/deploy/#{mtime}' do
34
+ `cd / && mina -f /home/rails/applications/pointer/#{host}-#{site_port}/deploy.rb deploy` # < /dev/null > /dev/null && echo OK
35
+ end
36
+
37
+ run Sinatra::Application
38
+ "
39
+
40
+ put_file_contents("applications/pointer/#{host}-#{site_port}/deploy.rb", IO.read('./config/deploy.rb'))
41
+ put_file_contents("applications/pointer/#{host}-#{site_port}/config.ru", config_ru)
42
+ puts @ssh.exec!('sudo service nginx restart')
43
+
44
+ hook_url = "http://#{host}:#{deployer_port}/deploy/#{mtime}"
45
+ puts "WebHook (POST) address: %s" % [hook_url.green]
46
+ puts "Add this to GitHub/BitBucket as WebHook/POST service, so that your code is automatically deployed on every push"
47
+ puts "Run, for example: curl -X POST #{hook_url}"
48
+ end
49
+
50
+
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ require 'colorize'
2
+
3
+ module Pointer
4
+ module Helpers
5
+ def remote_repo()
6
+ `git config --get remote.origin.url`.gsub(/^ssh:\/\/(.*?)\//, '\\1:').strip
7
+ end
8
+
9
+ def assert_git()
10
+ unless File.exists?('.git')
11
+ raise "Please run from a Git repository (use private Git hosting from bitbucket.org for example)"
12
+ end
13
+
14
+ if remote_repo.to_s == ''
15
+ raise "This tool assumes you have git remote branch, named #{'origin'.green}"
16
+ end
17
+ end
18
+
19
+ def what(string)
20
+ puts "# #{string}".green
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,89 @@
1
+ module Pointer
2
+ module Mina
3
+ def check_mina_prereqs
4
+ if File.exists?('config/deploy.rb') and not ARGV.include?('--unlink-mina')
5
+ user_has_mina_config()
6
+ end
7
+ end
8
+
9
+ def mina_deploy()
10
+ what "[local -> remote] mina setup"
11
+
12
+ mina_setup = `mina setup </dev/null || echo "[ERROR]"`
13
+ puts mina_setup
14
+ if mina_setup.include? '[ERROR]'
15
+ raise "mina setup failed"
16
+ end
17
+
18
+ remote_db_config_file = "#{@options[:site_dir]}/shared/config/database.yml"
19
+
20
+ if @db_config
21
+ puts "I have a database config, putting it there"
22
+ put_file_contents(remote_db_config_file, @db_config)
23
+ else
24
+ if file_absent(remote_db_config_file)
25
+ db_config = IO.read(@options[:site_dir] + "/shared/config/database.yml")
26
+ put_file_contents(remote_db_config_file, db_config)
27
+ end
28
+ end
29
+
30
+ what "[local -> remote] mina deploy"
31
+
32
+ mina_deploy = `mina deploy </dev/null || echo "[ERROR]"`
33
+ puts mina_deploy
34
+ if mina_deploy.include? '[ERROR]'
35
+ raise "mina deploy failed"
36
+ end
37
+
38
+ what "done"
39
+ end
40
+
41
+ def mina_init()
42
+ if ARGV.include? '--unlink-mina'
43
+ puts "Unlinking mina config".on_red
44
+ File.unlink('config/deploy.rb') if File.exists? 'config/deploy.rb'
45
+ end
46
+
47
+ rvm_version = rvm!('rvm-prompt').strip
48
+
49
+ #`gem install mina`
50
+ what "[local] mina init"
51
+ mina_init = `mina init </dev/null || echo "[ERROR]"`
52
+ if mina_init.include? '[ERROR]'
53
+ puts mina_init
54
+ user_has_mina_config()
55
+ end
56
+
57
+ config = IO.read('config/deploy.rb')
58
+ config = config.gsub("# require 'mina/rvm'", "require 'mina/rvm'")
59
+ config = config.gsub("set :domain, 'foobar.com'", "set :domain, #{@options[:site_host].inspect}")
60
+ config = config.gsub("set :deploy_to, '/var/www/foobar.com'", "set :deploy_to, #{@options[:site_dir].inspect}")
61
+ config = config.gsub("# set :user, 'foobar'", " set :user, '#{rails_user}'")
62
+ config = config.gsub("# invoke :'rvm:use[ruby-1.9.3-p125@default]'", "invoke :'rvm:use[#{rvm_version}]'")
63
+ config = config.gsub("queue %[-----> Be sure to edit 'shared/config/database.yml'.]", "")
64
+ puts "Be sure to edit 'shared/config/database.yml'".on_red
65
+ config = config.gsub("set :repository, 'git://...'", "set :repository, #{remote_repo.inspect}",)
66
+ #config = config.gsub("set :shared_paths, ['config/database.yml', 'log']", "set :shared_paths, ['log', 'sqlite']",)
67
+
68
+ #database_symlink = "\n" +
69
+ # ' queue %[rm "#{deploy_to}/current/config/database.yml"]' + "\n" +
70
+ # ' queue %[ln -s "#{deploy_to}/shared/config/database.yml" "#{deploy_to}/current/config/database.yml"]' + "\n" +
71
+ # #' queue %[cat "#{deploy_to}/shared/config/database.yml"]' + "\n" +
72
+ # "\n "
73
+ #
74
+ #config = config.gsub("queue 'touch tmp/restart.txt'", database_symlink + "queue 'touch tmp/restart.txt'")
75
+ config = config.gsub("queue 'touch tmp/restart.txt'", 'queue "mkdir tmp; touch tmp/restart.txt"')
76
+ IO.write('config/deploy.rb', config)
77
+ # puts "Run #{'mina setup'.red} and #{'mina deploy'.red}"
78
+
79
+ `ssh -o StrictHostKeyChecking=no #{rails_user}@#{host} pwd`
80
+
81
+ end
82
+
83
+ def user_has_mina_config
84
+ puts "Run %s if you already have everything set up" % ["mina deploy".green]
85
+ puts "or run with --unlink-mina if you want to force mina config generation"
86
+ exit
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,63 @@
1
+ module Pointer
2
+ module NginxPassenger
3
+ def install_passenger()
4
+ unless file_exists('/opt/nginx')
5
+ what 'Install nginx/passenger'
6
+ rvm!('gem install passenger')
7
+ @ssh.exec!('sudo mkdir /opt')
8
+ @ssh.exec!('sudo mkdir /opt/nginx')
9
+ @ssh.exec!('sudo chown rails:rails /opt/nginx')
10
+ rvm!('passenger-install-nginx-module --auto --auto-download --prefix=/opt/nginx')
11
+
12
+ what "Installing nginx init.d"
13
+ @ssh.exec!('sudo wget https://raw.github.com/slava-vishnyakov/useful-stuff/master/init.d-nginx.conf -O /etc/init.d/nginx')
14
+
15
+ what "Changing nginx.conf permissions"
16
+ @ssh.exec!('sudo chmod o+x /etc/init.d/nginx')
17
+
18
+ what "update-rc.d"
19
+ @ssh.exec!('sudo update-rc.d nginx defaults')
20
+
21
+ what "Installing nginx.conf"
22
+ @ssh.exec!('mv /opt/nginx/conf/nginx.conf /opt/nginx/conf/nginx.conf-orig')
23
+ @ssh.exec!('wget https://raw.github.com/slava-vishnyakov/useful-stuff/master/nginx.conf -O /opt/nginx/conf/nginx.conf')
24
+
25
+ what "Replacing passenger_ruby and passenger_root with actual Passenger data"
26
+ orig_file = get_file_contents '/opt/nginx/conf/nginx.conf-orig'
27
+ new_file = get_file_contents '/opt/nginx/conf/nginx.conf'
28
+ new_file.sub! /passenger_ruby (.*?);/, orig_file.match(/passenger_ruby (.*?);/)[0]
29
+ new_file.sub! /passenger_root (.*?);/, orig_file.match(/passenger_root (.*?);/)[0]
30
+ put_file_contents '/opt/nginx/conf/nginx.conf', new_file
31
+
32
+ what "Creating /opt/nginx/conf/rails-sites"
33
+ @ssh.exec!('mkdir /opt/nginx/conf/rails-sites')
34
+
35
+ what "Starting nginx"
36
+ @ssh.exec!('sudo service nginx start')
37
+ end
38
+ end
39
+
40
+ def create_site_config
41
+ config = "
42
+ server {
43
+ listen #{@options[:site_port]};
44
+ server_name #{@options[:site_host]};
45
+ passenger_enabled on;
46
+ root #{@options[:site_dir]}/current/public;
47
+ passenger_user rails;
48
+ passenger_max_requests 500;
49
+ }
50
+ "
51
+ config_file = "/opt/nginx/conf/rails-sites/#{@options[:site_host]}-#{@options[:site_port]}.conf"
52
+
53
+ if file_absent(config_file)
54
+ put_file_contents(config_file, config)
55
+ end
56
+
57
+ if @ssh.exec!("sudo /opt/nginx/sbin/nginx -t") =~ /test is successful/
58
+ @ssh.exec!("sudo service nginx reload")
59
+ end
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,43 @@
1
+ module Pointer
2
+ module Postgres
3
+ def install_postgres
4
+ # install_postgres_database
5
+ unless @ssh.exec! 'which psql'
6
+ # official apt repo does not support 12.10 (quantal) yet :(
7
+ puts @ssh.exec! 'sudo apt-get install -y libpq-dev'
8
+ puts @ssh.exec! 'sudo apt-get install -y software-properties-common'
9
+ puts @ssh.exec! 'sudo add-apt-repository ppa:pitti/postgresql'
10
+ puts @ssh.exec! 'sudo apt-get update'
11
+ puts @ssh.exec! 'sudo apt-get install -y postgresql-9.2'
12
+ end
13
+
14
+ # create_database
15
+
16
+ if file_absent("#{@options[:site_dir]}/shared/config/database.yml")
17
+ o = [('a'..'z'), ('A'..'Z'), ('0'..'9')].map { |i| i.to_a }.flatten
18
+
19
+ # TODO: save these on server??
20
+ password = (0...16).map { o[rand(o.length)] }.join
21
+ username = 'user_' + host.gsub(/[^a-z0-9]/, '_')
22
+ database = 'db_' + host.gsub(/[^a-z0-9]/, '_')
23
+
24
+ # sudo sudo -u postgres - because we only have password-less sudo to root
25
+ puts @ssh.exec!("sudo sudo -u postgres psql -c \"CREATE ROLE #{username} WITH CREATEDB LOGIN PASSWORD '#{password}'\"")
26
+ puts @ssh.exec!("sudo sudo -u postgres psql -c \"CREATE DATABASE #{database} OWNER #{username}\"");
27
+
28
+ # create database.yml
29
+ "production:\n" +
30
+ " adapter: postgresql\n" +
31
+ " host: 127.0.0.1\n" +
32
+ " encoding: utf8\n" +
33
+ " database: #{database}\n" +
34
+ " username: #{username}\n" +
35
+ " password: #{password}\n" +
36
+ ''
37
+ else
38
+ nil
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,48 @@
1
+ module Pointer
2
+ module RailsUser
3
+ def delete_rails_user
4
+ puts @ssh.exec!("userdel rails -f -r")
5
+ end
6
+
7
+ def create_rails_user
8
+ if @ssh.exec!("id rails") =~ /No such user/
9
+ expect_empty @ssh.exec!("useradd rails -d /home/#{rails_user} -m -s /bin/bash")
10
+ expect_empty @ssh.exec!("usermod -a -G sudo rails")
11
+ what "User created: rails"
12
+ else
13
+ what "User exists: rails"
14
+ end
15
+ end
16
+
17
+ def upload_public_key
18
+ ssh_dir = "/home/#{rails_user}/.ssh"
19
+ authorized_keys_file = ssh_dir + '/authorized_keys'
20
+
21
+ what "Uploading private key"
22
+ @ssh.exec!("mkdir #{ssh_dir}")
23
+ ensure_file_contains(authorized_keys_file, IO.read(File.expand_path(public_key)))
24
+ expect_empty @ssh.exec!("chown rails:rails #{ssh_dir}")
25
+ expect_empty @ssh.exec!("chown rails:rails #{authorized_keys_file}")
26
+ expect_empty @ssh.exec!("chmod 0700 #{ssh_dir}")
27
+ expect_empty @ssh.exec!("chmod 0600 #{authorized_keys_file}")
28
+ end
29
+
30
+ def add_sudo
31
+ sudo_string = "rails ALL = NOPASSWD:ALL"
32
+ ensure_file_contains('/etc/sudoers', sudo_string)
33
+ end
34
+
35
+ def revoke_sudo
36
+ puts "revoke_sudo is not implemented"
37
+ end
38
+
39
+ def print_deploy_key
40
+ id_rsa = '/home/rails/.ssh/id_rsa'
41
+ if file_absent(id_rsa)
42
+ what "Generating ssh key"
43
+ puts @ssh.exec!("ssh-keygen -q -t rsa -f #{id_rsa} -N ''")
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ module Pointer
2
+ module Rvm
3
+ def install_rvm
4
+ if file_absent("/home/#{rails_user}/.rvm/scripts/rvm")
5
+ what "Update system"
6
+ puts @ssh.exec!("sudo apt-get -qq -y update")
7
+ what "Install build stuff"
8
+ puts @ssh.exec!("sudo apt-get -qq -y install curl libcurl4-gnutls-dev git nodejs build-essential openssl " +
9
+ " libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev " +
10
+ " libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf " +
11
+ " libc6-dev ncurses-dev automake libtool bison subversion pkg-config libgdbm-dev libffi-dev"
12
+ )
13
+ what "Actually install RVM (this takes some time)"
14
+ puts @ssh.exec!("\\curl -L https://get.rvm.io | bash -s stable --ruby=#{ruby_version}")
15
+
16
+ rvm!("rvm use #{ruby_version} --default")
17
+ ensure_file_contains('/home/rails/.gemrc', 'gem: --no-ri --no-rdoc')
18
+ end
19
+ end
20
+
21
+ def rvm!(command)
22
+ @ssh.exec!(". /home/#{rails_user}/.rvm/scripts/rvm && (#{command})")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,84 @@
1
+ require 'tempfile'
2
+ require 'net/ssh'
3
+ require 'net/scp'
4
+ require 'shellwords'
5
+
6
+ module Pointer
7
+ module SshHelpers
8
+ def get_file_contents(file_name)
9
+ @ssh.exec!("cat #{file_name.shellescape}")
10
+ end
11
+
12
+ def put_file_contents(file_name, string)
13
+ file = Tempfile.new('ssh')
14
+ file.write(string)
15
+ file.rewind
16
+ @ssh.scp.upload!(file.path, file_name)
17
+ end
18
+
19
+ def ensure_file_contains(file_name, string)
20
+ contents = file_absent(file_name) ? '' : get_file_contents(file_name).to_s
21
+ unless contents.include? string
22
+ unless contents.end_with? "\n"
23
+ contents += "\n"
24
+ end
25
+
26
+ contents += string
27
+
28
+ put_file_contents(file_name, contents)
29
+ end
30
+ end
31
+
32
+ def ensure_file_not_contains(file_name, string)
33
+ contents = file_absent(file_name) ? '' : get_file_contents(file_name).to_s
34
+
35
+ if contents and contents.include? string
36
+ contents.gsub!(string, '')
37
+ put_file_contents(file_name, contents)
38
+ end
39
+ end
40
+
41
+ def file_absent(file_name)
42
+ @ssh.exec!("ls #{file_name.shellescape}") =~ /No such file or directory/
43
+ end
44
+
45
+ def file_exists(file_name)
46
+ not file_absent(file_name)
47
+ end
48
+
49
+ def expect_empty(string)
50
+ if string
51
+ puts string
52
+ exit
53
+ end
54
+ end
55
+
56
+ def with_ssh
57
+ Net::SSH.start(host, rails_user, port: port, :keys => [private_key], :paranoid => false) do |ssh|
58
+ @ssh = ssh
59
+ yield
60
+ @ssh = 'Connection closed'
61
+ end
62
+ end
63
+
64
+ def with_root_ssh
65
+ Net::SSH.start(host, user, port: port, :password => password, :paranoid => false) do |ssh|
66
+ @ssh = ssh
67
+ yield
68
+ @ssh = 'Connection closed'
69
+ end
70
+ end
71
+
72
+ def test_connection
73
+ connection_test = `ssh -o StrictHostKeyChecking=no #{rails_user}@#{host} -p #{port} -i #{private_key} "echo OK"`
74
+ if connection_test.strip == "OK"
75
+ what "Connected via private key OK"
76
+ else
77
+ puts connection_test.on_red
78
+ raise "I cannot connect via private key!"
79
+ end
80
+ end
81
+
82
+
83
+ end
84
+ end
@@ -0,0 +1,59 @@
1
+ module Pointer
2
+ module Variables
3
+ def rails_user
4
+ @options[:rails_user]
5
+ end
6
+
7
+ def private_key
8
+ @options[:private_key]
9
+ end
10
+
11
+ def public_key
12
+ @options[:public_key]
13
+ end
14
+
15
+ def password
16
+ @options[:password]
17
+ end
18
+
19
+ def user
20
+ @options[:ssh_user]
21
+ end
22
+
23
+ def host
24
+ @options[:ssh_host]
25
+ end
26
+
27
+ def port
28
+ @options[:ssh_port]
29
+ end
30
+
31
+ def ruby_version
32
+ @options[:ruby_version] || "1.9.3"
33
+ end
34
+
35
+ def site_port
36
+ @options[:site_port].to_i || 80
37
+ end
38
+
39
+ def nginx
40
+ @options[:web_server] == :nginx
41
+ end
42
+
43
+ def bitbucket
44
+ @options[:git_repo].include? '@bitbucket.org'
45
+ end
46
+
47
+ def mina
48
+ @options[:deployer] == :mina
49
+ end
50
+
51
+ def deployer_application
52
+ @options[:deployer_application]
53
+ end
54
+
55
+ def postgres
56
+ @options[:database] == :postgres
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module Pointer
2
+ VERSION = "0.0.1"
3
+ end
data/pointer.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pointer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pointer"
8
+ spec.version = Pointer::VERSION
9
+ spec.authors = ["Slava Vishnyakov"]
10
+ spec.email = ["bomboze@gmail.com"]
11
+ spec.description = %q{Quick deploy}
12
+ spec.summary = %q{Quick deploy}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_dependency "net-ssh"
25
+ spec.add_dependency "net-scp"
26
+ spec.add_dependency "colorize"
27
+ spec.add_dependency "active_support"
28
+ end
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pointer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Slava Vishnyakov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: net-ssh
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: net-scp
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: colorize
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: active_support
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Quick deploy
111
+ email:
112
+ - bomboze@gmail.com
113
+ executables:
114
+ - pointer
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .gitignore
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - TODO.md
124
+ - bin/pointer
125
+ - config/pointer.rb.default
126
+ - lib/pointer.rb
127
+ - lib/pointer/bit_bucket.rb
128
+ - lib/pointer/deployer_application.rb
129
+ - lib/pointer/helpers.rb
130
+ - lib/pointer/mina.rb
131
+ - lib/pointer/nginx_passenger.rb
132
+ - lib/pointer/postgres.rb
133
+ - lib/pointer/rails_user.rb
134
+ - lib/pointer/rvm.rb
135
+ - lib/pointer/ssh_helpers.rb
136
+ - lib/pointer/variables.rb
137
+ - lib/pointer/version.rb
138
+ - pointer.gemspec
139
+ homepage: ''
140
+ licenses:
141
+ - MIT
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ segments:
153
+ - 0
154
+ hash: -4480820461649365695
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ! '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ segments:
162
+ - 0
163
+ hash: -4480820461649365695
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 1.8.23
167
+ signing_key:
168
+ specification_version: 3
169
+ summary: Quick deploy
170
+ test_files: []