pointer 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []