rigup 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 074cf53541cc8dda276109cd786eed5e4c013455
4
+ data.tar.gz: ba808e6dd94614a4f0cc179fe3dccd29f1c1f517
5
+ SHA512:
6
+ metadata.gz: d9ab780149ca55a6dca8bf1067bd723c00c532219e39196bcd8b5429c556a0bbccb790230c504c5fad0d638db0eb773436853df67dd5738d5b55f91dbd774127
7
+ data.tar.gz: a28e3901c130731e5792aa0683d11ec45534bd39840f4e4883b3861a88c33b545418c8cd2a4f1e711bbadb12f493181fc0fb227347f2375ca2376176f1be3876
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1 @@
1
+ Toolset for deployment
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/rigup ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require_relative '../lib/rigup.rb'
4
+
5
+ Rigup::Cli.start(ARGV)
@@ -0,0 +1,32 @@
1
+ require 'rigup'
2
+
3
+ # This file will be called deploy.thor and lives in the root of the project repositiory, so it is version controlled
4
+ # and can be freely modified to suit the project requirements
5
+ class Deploy < Rigup::DeployBase # from gem, sets context and loads rigup.yml into config from site_dir
6
+
7
+ # You are free to modify these two tasks to suit your requirements
8
+
9
+ desc 'install','install the freshly delivered files'
10
+ def install
11
+
12
+ # select_suffixed_file("/config/database.yml")
13
+ # select_suffixed_file("/yore.config.xml")
14
+ # select_suffixed_file("/config/app_config.xml")
15
+ # select_suffixed_file("/system/apache.site")
16
+ #make_public_cache_dir("/public/cache")
17
+
18
+ # ensure_link("/log","/log")
19
+ # ensure_link("/pids","/tmp/pids")
20
+ # ensure_link("/uploads","/tmp/uploads")
21
+ # ensure_link("/system","/public/system")
22
+
23
+ #run "touch /log/production.log && chown : /log/production.log && chmod 666 /log/production.log"
24
+ end
25
+
26
+ desc 'restart','restart the web server'
27
+ def restart
28
+ # run "touch " + File.join("current/tmp/restart.txt",@site_dir) # && chown user:group current/tmp/restart.txt"
29
+ # run "/etc/init.d/apache2 restart --force-reload"
30
+ end
31
+
32
+ end
data/lib/rigup/cli.rb ADDED
@@ -0,0 +1,159 @@
1
+ module Rigup
2
+ class Cli < Thor
3
+
4
+ include Rigup::Runability
5
+ include Rigup::InstallUtils
6
+
7
+ attr_reader :context, :release_path
8
+
9
+ no_commands do
10
+
11
+ def init(aPath=nil,aConfig={})
12
+ return if @initialised
13
+ @initialised = true
14
+ aPath ||= aConfig[:site_dir] || Dir.pwd
15
+ @site_dir = Buzztools::File.real_path(aPath) rescue File.expand_path(aPath)
16
+ @releases_path = File.join(@site_dir,'releases')
17
+ if File.exists?(f=File.join(@site_dir,'rigup.yml'))
18
+ file_config = YAML.load(String.from_file(f))
19
+ aConfig.merge!(file_config)
20
+ end
21
+ config = Rigup::Config.new(aConfig.merge(site_dir: @site_dir))
22
+ @context = Rigup::Context.new(
23
+ config: config,
24
+ logger: ::Logger.new(STDOUT),
25
+ pwd: Dir.pwd
26
+ )
27
+ end
28
+
29
+ def site_dir
30
+ @site_dir
31
+ end
32
+
33
+ def cache_dir
34
+ File.join(@site_dir,'shared','cached-copy')
35
+ end
36
+
37
+ def config
38
+ @context.config
39
+ end
40
+
41
+ def repo
42
+ @repo ||= GitRepo.new(@context)
43
+ end
44
+
45
+ # Prepares repo in cache dir for site
46
+ # requires params: repo_url,site
47
+ def prepare_cache # {:url=>'git://github.com/ddssda', :branch=>'master', :commit=>'ad452bcd'}
48
+ url = config[:git_url]
49
+ wd = cache_dir
50
+
51
+ suitable = if File.exists?(wd)
52
+ repo.open wd
53
+ repo.origin.url==url
54
+ else
55
+ false
56
+ end
57
+
58
+ if suitable
59
+ repo.fetch
60
+ else
61
+ if File.exists? wd
62
+ #raise RuntimeError.new('almost did bad delete') if !@core.cache_dir || @core.cache_dir.length<3 || !wd.begins_with?(@core.cache_dir)
63
+ FileUtils.rm_rf wd
64
+ end
65
+ repo.clone(url, wd)
66
+ end
67
+ end
68
+
69
+ # Switches @repo to given branch and/or commit
70
+ # Should call prepare_cache first to create @repo
71
+ # requires params: branch and/or commit
72
+ def checkout_branch_commit
73
+ branch = config[:branch] || 'master'
74
+ commit = config[:commit]
75
+ repo.open(cache_dir)
76
+ repo.checkout(commit,branch)
77
+ #perhaps use reset --hard here
78
+ if (commit)
79
+ repo.merge(commit,['--ff-only'])
80
+ else
81
+ repo.merge('origin/'+branch,['--ff-only'])
82
+ end
83
+ end
84
+
85
+ def update_cache(aPath=nil)
86
+ prepare_cache
87
+ checkout_branch_commit
88
+ end
89
+
90
+ def release
91
+ release = Time.now.strftime('%Y%m%d%H%M%S')
92
+ @release_path = File.expand_path(release,@releases_path)
93
+ repo.open(cache_dir)
94
+ repo.export(@release_path)
95
+ release_config = context.config.to_hash
96
+ release_config[:commit] = repo.head.sha
97
+ release_config[:branch] = repo.branch
98
+ release_config.to_yaml.to_file(File.join(@release_path,'rigup.yml'))
99
+ return @release_path
100
+ end
101
+
102
+ def link_live
103
+ ensure_link(@release_path,File.expand_path(File.join(site_dir,'current')))
104
+ end
105
+
106
+ def cleanup
107
+ @releases = run("ls -x #{@releases_path}").split.sort
108
+ count = (@keep_releases || 3).to_i
109
+ if count >= @releases.length
110
+ @context.logger.info "no old releases to clean up"
111
+ else
112
+ @context.logger.info "keeping #{count} of #{@releases.length} deployed releases"
113
+
114
+ directories = (@releases - @releases.last(count)).map { |r|
115
+ File.join(@releases_path, r)
116
+ }.join(" ")
117
+
118
+ run "rm -rf #{directories}"
119
+ end
120
+ end
121
+
122
+ def call_release_command(aCommand)
123
+ return unless cmdline = config["#{aCommand}_command".to_sym].to_s.strip.to_nil
124
+ run cmdline, @release_path
125
+ end
126
+
127
+ end
128
+
129
+ public
130
+
131
+ desc "new GIT_URL [PATH]", "setup new site"
132
+ def new(aGitUrl,aPath=nil)
133
+ aPath ||= File.basename(aGitUrl,'.git')
134
+ init(
135
+ aPath,
136
+ git_url: aGitUrl
137
+ )
138
+ FileUtils.mkdir_p(site_dir)
139
+ FileUtils.mkdir_p(File.join(site_dir,'releases'))
140
+ FileUtils.mkdir_p(File.join(site_dir,'shared'))
141
+
142
+ #+ create rigup.yml if doesn't exist, including option values
143
+ context.config.to_hash.filter_exclude(:site_dir).to_yaml.to_file(File.join(site_dir,'rigup.yml'))
144
+ end
145
+
146
+ desc "deploy [PATH]", "deploy the given site"
147
+ def deploy(aPath=nil)
148
+ init(aPath)
149
+ update_cache
150
+ release
151
+ call_release_command(:install) # call install_command if defined eg. defaults to "thor deploy:install" eg. make changes to files
152
+ call_release_command(:block)
153
+ link_live
154
+ call_release_command(:restart) # call restart_command, defaults to "thor deploy:restart" eg. restart passenger
155
+ call_release_command(:unblock)
156
+ cleanup
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,22 @@
1
+ module Rigup
2
+
3
+ class Config < Buzztools::Config
4
+
5
+ DEFAULTS = {
6
+ site_dir: String,
7
+ git_url: String,
8
+ branch: String,
9
+ commit: String,
10
+ stage: 'live', # or 'staging' or 'development'
11
+ block_command: nil,
12
+ install_command: 'thor deploy:install',
13
+ restart_command: 'thor deploy:restart',
14
+ unblock_command: nil,
15
+ }
16
+
17
+ def initialize(aValues=nil)
18
+ super(DEFAULTS,aValues)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,59 @@
1
+ module Rigup
2
+ class Context
3
+
4
+ attr_reader :config, :options, :argv, :env, :stdout, :stdin, :stderr, :key_chain, :credentials, :logger, :pwd, :variant
5
+ attr_writer :pwd
6
+
7
+ def sudo
8
+ ''
9
+ end
10
+
11
+ def initialize(aValues=nil)
12
+ return if !aValues
13
+
14
+ #is_client = !!(aValues[:key_chain] || aValues[:global_options] || aValues[:options])
15
+ @config = aValues[:config]
16
+ @pwd = Buzztools::File.real_path(aValues[:pwd] || (@config && @config[:folder]) || Dir.pwd)
17
+ @options = aValues[:options]
18
+ @argv = aValues[:argv]
19
+ @env = aValues[:env]
20
+ @stdout = aValues[:stdout]
21
+ @stdin = aValues[:stdin]
22
+ @stderr = aValues[:stderr]
23
+ @key_chain = aValues[:key_chain]
24
+ @credentials = aValues[:credentials]
25
+ @logger = aValues[:logger]
26
+ @variant = aValues[:variant]
27
+ end
28
+
29
+ def git_root
30
+ @git_root ||= find_git_root
31
+ end
32
+
33
+ # http://thinkingdigitally.com/archive/capturing-output-from-puts-in-ruby/
34
+ #class SimpleSemParserTest < Test::Unit::TestCase
35
+ # def test_set_stmt_write
36
+ # out = capture_stdout do
37
+ # parser = SimpleSemParser.new
38
+ # parser.parse('set write, "Hello World!"').execute
39
+ # end
40
+ # assert_equal "Hello World!\n", out.string
41
+ # end
42
+ #end
43
+ def capture_stdout
44
+ stdout_before = @stdout
45
+ out = StringIO.new
46
+ @stdout = out
47
+ yield
48
+ return out.string
49
+ ensure
50
+ @stdout = stdout_before
51
+ end
52
+
53
+ def find_git_root
54
+ git_folder = BuzzTools::File.find_upwards(@pwd,'.git')
55
+ return git_folder && git_folder.chomp('/.git')
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,33 @@
1
+ module Rigup
2
+
3
+ class DeployBase < Thor
4
+
5
+ include Rigup::Runability
6
+ include Rigup::InstallUtils
7
+
8
+ no_commands do
9
+ def initialize(*args)
10
+ super
11
+ @release_path = Dir.pwd
12
+ @site_dir = File.expand_path('../..')
13
+ @shared_path = File.expand_path('shared',@site_dir)
14
+ config = {}
15
+ if File.exists?(f=File.join(@release_path,'rigup.yml'))
16
+ file_config = YAML.load(String.from_file(f))
17
+ config.merge!(file_config)
18
+ end
19
+ @context = Rigup::Context.new(
20
+ config: Rigup::Config.new(config),
21
+ logger: ::Logger.new(STDOUT),
22
+ pwd: Dir.pwd,
23
+ )
24
+ end
25
+
26
+ def config
27
+ @context.config
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,148 @@
1
+ require 'git'
2
+
3
+ module Rigup
4
+ class GitRepo
5
+
6
+ include Rigup::Runability
7
+
8
+ attr_reader :git,:configured
9
+
10
+ GIT_METHODS = [:commit,:add,:reset_hard,:path,:clone,:log,:size,:branches,:status,:remotes,:pull,:fetch,:push,:merge]
11
+
12
+ def initialize(aContext=nil)
13
+ @context = (aContext || Rigup::Context.new)
14
+ end
15
+
16
+ def method_missing(sym, *args, &block)
17
+ if @git && GIT_METHODS.include?(sym)
18
+ @git.send sym, *args, &block
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def open(aPath)
25
+ @git = ::Git.open(aPath)
26
+ end
27
+
28
+ def init(*args)
29
+ @git = ::Git.init(*args)
30
+ end
31
+
32
+ def clone(aUrl,aPath)
33
+ @git = ::Git::clone(aUrl,aPath)
34
+ end
35
+
36
+ def open?
37
+ !!@git
38
+ end
39
+
40
+ def empty?
41
+ !@git.branches[0]
42
+ end
43
+
44
+ def status
45
+ result = @git.lib.command_lines('status',['--porcelain'])
46
+ result
47
+ end
48
+
49
+ def changes?
50
+ raise RuntimeError.new('Repository must be open') unless open?
51
+ status.length > 0
52
+ end
53
+
54
+ def commit_all(*args)
55
+ result = begin
56
+ @git.commit_all(*args)
57
+ rescue ::Git::GitExecuteError => e
58
+ if e.message.index("nothing to commit (working directory clean)")
59
+ nil
60
+ else
61
+ raise e
62
+ end
63
+ end
64
+ result = commitFromString(result)
65
+ result
66
+ end
67
+
68
+ # "[master (root-commit) 6bdd9e1] first commit 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 file1.txt"
69
+ def commitFromString(aString)
70
+ return nil if !aString || aString.empty?
71
+ sha = aString.scan(/ ([a-f0-9]+)\]/).flatten.first
72
+ @git.gcommit(sha)
73
+ end
74
+
75
+ def path
76
+ @git.dir.path
77
+ end
78
+
79
+ def origin
80
+ @git.remotes.find {|r| r.name=='origin'}
81
+ end
82
+
83
+ def url
84
+ (o = origin) && o.url
85
+ end
86
+
87
+ def checkout(commit=nil,branch=nil)
88
+ specific_commit = !!commit && !commit.index('HEAD')
89
+ if specific_commit
90
+ @git.checkout commit
91
+ else
92
+ branch ||= 'master'
93
+ @git.checkout(branch)
94
+ end
95
+ end
96
+
97
+ # http://stackoverflow.com/questions/2866358/git-checkout-only-files-without-repository
98
+ # http://www.clientcide.com/best-practices/exporting-files-from-git-similar-to-svn-export/
99
+ def export(aDest)
100
+ #rsync --exclude=.git /source/directory/ user@remote-server.com:/target-directory
101
+ FileUtils.cp_r(path,aDest)
102
+ FileUtils.rm_rf(File.expand_path('.git',aDest))
103
+ end
104
+
105
+ def branch
106
+ @git.current_branch
107
+ end
108
+
109
+ def sha
110
+ head.sha
111
+ end
112
+
113
+ def head
114
+ @git.log.first
115
+ end
116
+
117
+ # ::Git --no-pager diff --name-status 26bb87c3981 191d64820f2b
118
+ # result is array of paths prefixed with status letter then a tab
119
+ # see http://git-scm.com/docs/git-diff under --diff-filter=
120
+ # Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), have their type (i.e. regular file, symlink, submodule, ...) changed (T)
121
+ def changesBetweenCommits(aFromCommit, aToCommit)
122
+ @git.lib.command_lines('diff',['--name-status',aFromCommit,aToCommit])
123
+ end
124
+
125
+ # Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), have their type (i.e. regular file, symlink, submodule, ...) changed (T)
126
+ def self.convertChangesToUploadsDeletes(changes)
127
+ uploads = []
128
+ deletes = []
129
+ changes.each do |line|
130
+ continue if line==""
131
+ tabi = line.index("\t")
132
+ status = line[0,tabi]
133
+ path = line[tabi+1..-1]
134
+ if status.index('D')
135
+ deletes << path
136
+ else
137
+ uploads << path
138
+ end
139
+ end
140
+ return uploads,deletes
141
+ end
142
+
143
+ def get_file_content(aPath,aCommitOrBranch=nil)
144
+ @git.lib.command('show',[[aCommitOrBranch||'master',aPath].join(':')]) rescue nil
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,94 @@
1
+ module Rigup
2
+ module InstallUtils
3
+
4
+ def select_suffixed_file(aFile,aExtendedExtension=false)
5
+ ext = Buzztools::File.extension(aFile,aExtendedExtension)
6
+ no_ext = Buzztools::File.no_extension(aFile,aExtendedExtension)
7
+ dir = File.dirname(aFile)
8
+ run "#{@context.sudo} mv -f #{no_ext}.#{@context.config[:stage]}.#{ext} #{aFile}"
9
+ run "#{@context.sudo} rm -f #{no_ext}.*.#{ext}"
10
+ end
11
+
12
+ # Especially for modifiying behaviour eg. of FCKEditor without upsetting the standard files
13
+ # eg. create a public_override folder that duplicates the same structure as public,
14
+ # and contains the modified files. On deployment call
15
+ # override_folder("#{@release_path}/public") # equiv to override_folder("#{@release_path}/public", "#{@release_path}/public_override")
16
+ # and the files in public_override will be copied over public, then public_override removed
17
+ def override_folder(aFolder,aOverrideFolder=nil,aRemove=true)
18
+ aFolder = aFolder.desuffix('/')
19
+ aOverrideFolder ||= (aFolder+'_override')
20
+ run "#{@context.sudo} cp -vrf #{aOverrideFolder}/* #{aFolder}/"
21
+ run "#{@context.sudo} rm -rf #{aOverrideFolder}" if aRemove
22
+ end
23
+
24
+
25
+ # set standard permissions for web sites - readonly for apache user
26
+ def permissions_for_web(aPath,aUser=nil,aGroup=nil,aHideScm=nil)
27
+ aUser ||= @user
28
+ aGroup ||= @group
29
+
30
+ run "#{@context.sudo} chown -R #{aUser}:#{aGroup} #{aPath.ensure_suffix('/')}"
31
+ run_for_all("chmod 755",aPath,:dirs) # !!! perhaps reduce other permissions
32
+ run_for_all("chmod 644",aPath,:files)
33
+ run_for_all("chmod g+s",aPath,:dirs)
34
+ case aHideScm
35
+ when :svn then run_for_all("chmod -R 700",aPath,:dirs,"*/.svn")
36
+ end
37
+ end
38
+
39
+ # run this after permissions_for_web() on dirs that need to be writable by group (apache)
40
+ def permissions_for_web_writable(aPath)
41
+ run "chmod -R g+w #{aPath.ensure_suffix('/')}"
42
+ run_for_all("chmod -R 700",aPath,:dirs,"*/.svn")
43
+ end
44
+
45
+ def internal_permissions(aPath,aKind)
46
+ case aKind
47
+ when 'rails' then
48
+ permissions_for_web(aPath,@user,@group,true)
49
+
50
+ run_for_all("chmod +x",File.join(aPath,'script'),:files)
51
+
52
+
53
+ uploads = @shared_path+'/uploads'
54
+ make_public_cache_dir(uploads)
55
+ #if File.exists?(uploads)
56
+ # permissions_for_web(uploads,@user,@group,true)
57
+ # permissions_for_web_writable(uploads)
58
+ #end
59
+ #permissions_for_web_writable("#{aPath}/tmp")
60
+ make_public_cache_dir("#{aPath}/tmp")
61
+
62
+ run "#{@context.sudo} chown #{@apache_user} #{aPath}/config/environment.rb" unless DEV_MODE # very important for passenger, which uses the owner of this file to run as
63
+
64
+ when 'spree' then
65
+ internal_permissions(aPath,'rails')
66
+ when 'browsercms' then
67
+ internal_permissions(aPath,'rails')
68
+ end
69
+ end
70
+
71
+ def apply_permissions(aPath=nil,aKind=nil)
72
+ aPath ||= @release_path
73
+ aKind ||= @kind || 'rails'
74
+ internal_permissions(aPath, aKind)
75
+ end
76
+
77
+ def ensure_link(aTo,aFrom) #,aDir=nil,aUserGroup=nil,aSudo='')
78
+ raise "Must supply from" if !aFrom
79
+ cmd = []
80
+ #cmd << "cd #{aDir}" if aDir
81
+ cmd << "#{@context.sudo} rm -rf #{aFrom}"
82
+ cmd << "#{@context.sudo} ln -sf #{aTo} #{aFrom}"
83
+ #cmd << "#{@context.sudo} chown -h #{aUserGroup} #{aFrom}" if aUserGroup
84
+ run cmd.join(' && ')
85
+ end
86
+
87
+ def make_public_cache_dir(aStartPath)
88
+ run "#{@context.sudo} mkdir -p #{aStartPath}"
89
+ permissions_for_web(aStartPath)
90
+ permissions_for_web_writable(aStartPath)
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,32 @@
1
+ module Rigup
2
+ module Runability
3
+
4
+ def run(aCommand,aDir=nil)
5
+ @context.logger.debug aCommand
6
+ response = ::POpen4::shell(aCommand,aDir || @context.pwd)
7
+ raise Error, "Command Failed" unless (response && response[:exitcode]==0)
8
+ return response[:stdout]
9
+ end
10
+
11
+ def run_for_all(aCommand,aPath,aFilesOrDirs,aPattern=nil,aInvertPattern=false,aSudo=false)
12
+ #run "#{sudo} find . -wholename '*/.svn' -prune -o -type d -print0 |xargs -0 #{sudo} chmod 750"
13
+ #sudo find . -type f -exec echo {} \;
14
+ cmd = []
15
+ cmd << "sudo" if aSudo
16
+ cmd << "find #{aPath.ensure_suffix('/')}"
17
+ cmd << "-wholename '#{aPattern}'" if aPattern
18
+ cmd << "-prune -o" if aInvertPattern
19
+ cmd << case aFilesOrDirs.to_s[0,1]
20
+ when 'f' then '-type f'
21
+ when 'd' then '-type d'
22
+ else ''
23
+ end
24
+ cmd << "-exec"
25
+ cmd << aCommand
26
+ cmd << "'{}' \\;"
27
+ cmd = cmd.join(' ')
28
+ run cmd
29
+ end
30
+
31
+ end
32
+ end
data/lib/rigup/svn.rb ADDED
@@ -0,0 +1,80 @@
1
+ module Rigup
2
+ module Svn
3
+
4
+ def svnInfo(aPath)
5
+ cmdresult = run "svn info \"#{aPath}\""
6
+ cmdresult = cmdresult.split("\n")
7
+ result = {}
8
+ cmdresult.each do |line|
9
+ parts = line.split(': ')
10
+ next if parts.length!=2 || !parts[0] || !parts[1]
11
+ result[parts[0]] = parts[1]
12
+ end
13
+
14
+ if (url = result['URL']) && (root = result['Repository Root']) && url.start_with?(root)
15
+ result['Short URL'] = url[root.length]
16
+ end
17
+ result
18
+ end
19
+
20
+ def svnCmd(aCommand,aSourceServer,aSourceUrl,aDestPath,aOptions = null)
21
+ "svn #{aCommand} \"#{aSourceServer ? File.join(aSourceServer,aSourceUrl) : aSourceUrl}\" \"#{aDestPath}\" #{aOptions || ''}"
22
+ end
23
+
24
+ def svnCheckoutCmd(aConfig,aCommmand='checkout',aOptions=nil)
25
+ options = []
26
+ options << aOptions if aOptions
27
+ options << '--username '+aConfig['vcs_username'] if aConfig['vcs_username']
28
+ options << '--password '+aConfig['vcs_password'] if aConfig['vcs_password']
29
+ options << '--revision '+aConfig['revision'] if aConfig['revision']
30
+ options = options.join ' '
31
+
32
+ rep = aConfig['repository'].to_s.chomp('/').to_nil
33
+ if rep && (branch = aConfig['branch'])
34
+ branch = '/'+branch unless branch.start_with? '/'
35
+ url = File.join(branch,aConfig['source'].to_s) if aConfig['source']
36
+ else
37
+ url = aConfig['source']
38
+ end
39
+ url = url.chomp('/')
40
+
41
+ svnCmd(aCommmand,rep,url,aConfig['destination'],options)
42
+ end
43
+
44
+ def createSvnConfig(aMergeOptions = nil)
45
+ result = {
46
+ 'repository' => @repository,
47
+ 'source' => @vcs_app_path,
48
+ 'branch' => @branch,
49
+ 'destination' => @release_path,
50
+ 'revision' => @revision
51
+ }
52
+ result.merge!(aMergeOptions) if aMergeOptions
53
+ result
54
+ end
55
+
56
+ def folderIsSvn(aPath)
57
+ return false unless File.exists?(aPath)
58
+ info = svnInfo(aPath)
59
+ return !!(info && info['URL'])
60
+ end
61
+
62
+ # ensure cache_path is an svn repo and is up to date. Checkout if not
63
+ def ensureSvnCacheUpdate(cache_path,svnConfig)
64
+ svnConfig = svnConfig.merge('destination' => cache_path)
65
+ if File.exists?(cache_path)
66
+ raise Error('Dir exists but not a svn folder') unless folderIsSvn(cache_path)
67
+ run "svn revert --non-interactive --recursive \"#{cache_path}\""
68
+ run svnCheckoutCmd(
69
+ svnConfig,
70
+ 'switch',
71
+ '--non-interactive'
72
+ )
73
+ else
74
+ MiscUtils.mkdir?(MiscUtils.path_parent(cache_path))
75
+ run svnCheckoutCmd(svnConfig,'checkout','--non-interactive')
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ module Rigup
2
+ VERSION = "0.0.2"
3
+ end
data/lib/rigup.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'thor'
2
+ require 'buzztools'
3
+ require 'logger'
4
+ require 'yaml'
5
+ require 'buzztools/extras/shell_extras'
6
+
7
+ require_relative "rigup/version"
8
+ require_relative "rigup/config"
9
+ require_relative "rigup/context"
10
+ require_relative "rigup/runability"
11
+ require_relative "rigup/install_utils"
12
+ require_relative "rigup/git_repo"
13
+ require_relative "rigup/cli"
14
+ require_relative "rigup/deploy_base"
data/notes.txt ADDED
@@ -0,0 +1,19 @@
1
+ From site root :
2
+
3
+ > cd /var/www
4
+ > rigup new repo_url mysite
5
+ + make dir mysite
6
+ + create rigup.yml if doesn't exist, including option values
7
+ + make dir mysite/releases
8
+ + make dir mysite/shared
9
+
10
+ > rigup deploy mysite
11
+
12
+ + git clone repo_url mysite/releases/20140328234123
13
+ + installing
14
+
15
+
16
+ > cd mysite
17
+ > rigup update
18
+ + using plan rigup.yml
19
+
data/rigup.gemspec ADDED
@@ -0,0 +1,47 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rigup/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rigup"
7
+ s.version = Rigup::VERSION
8
+ s.authors = ["Gary McGhee"]
9
+ s.email = ["contact@buzzware.com.au"]
10
+ s.homepage = "http://github.com/buzzware/rigup"
11
+ s.summary = "Toolset for deployment"
12
+ s.description = s.summary
13
+
14
+ ignores = File.readlines(".gitignore").grep(/\S+/).map {|line| line.chomp }
15
+ dotfiles = [".gitignore"]
16
+ s.files = Dir["**/*"].reject {|f| File.directory?(f) || ignores.any? {|i| File.fnmatch(i, f) } } + dotfiles
17
+ s.bindir = 'bin'
18
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
20
+
21
+ s.add_dependency 'bundler', '~>1.5.3'
22
+ s.add_dependency 'thor', '~> 0.19.1'
23
+
24
+
25
+ s.add_development_dependency 'rspec', '~>2.14.0'
26
+ #s.add_development_dependency('rspec')
27
+
28
+ s.add_development_dependency('rake')
29
+ #s.add_development_dependency('rspec-core')
30
+ #s.add_development_dependency('rdoc')
31
+ #s.add_development_dependency('aruba')
32
+ #s.add_development_dependency('debugger')
33
+ #s.add_runtime_dependency('gli','2.5.0')
34
+ #s.add_runtime_dependency('termios')
35
+ #s.add_runtime_dependency('highline')
36
+ s.add_runtime_dependency('git')
37
+ #s.add_runtime_dependency('middleman')
38
+ s.add_runtime_dependency('buzztools')
39
+ s.add_runtime_dependency('POpen4')
40
+ # https://github.com/geemus/formatador
41
+ #s.add_runtime_dependency('bitbucket_rest_api')
42
+ #s.add_runtime_dependency('osx_keychain')
43
+ #s.add_runtime_dependency('json')
44
+ #s.add_runtime_dependency('net_dav')
45
+ #s.add_runtime_dependency('net-ssh')
46
+ #s.add_runtime_dependency('system_timer')
47
+ end
data/rigup.iml ADDED
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="RUBY_MODULE" version="4">
3
+ <component name="DBNavigator.Module.ConnectionManager">
4
+ <connections />
5
+ </component>
6
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
7
+ <exclude-output />
8
+ <content url="file://$MODULE_DIR$">
9
+ <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
10
+ </content>
11
+ <orderEntry type="inheritedJdk" />
12
+ <orderEntry type="sourceFolder" forTests="false" />
13
+ <orderEntry type="library" scope="PROVIDED" name="POpen4 (v0.1.4, RVM: ruby-2.0.0-p247) [gem]" level="application" />
14
+ <orderEntry type="library" scope="PROVIDED" name="Platform (v0.4.0, RVM: ruby-2.0.0-p247) [gem]" level="application" />
15
+ <orderEntry type="library" scope="PROVIDED" name="bundler (v1.5.3, RVM: ruby-2.0.0-p247) [gem]" level="application" />
16
+ <orderEntry type="library" scope="PROVIDED" name="buzztools (v0.0.4, RVM: ruby-2.0.0-p247) [gem]" level="application" />
17
+ <orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.2.5, RVM: ruby-2.0.0-p247) [gem]" level="application" />
18
+ <orderEntry type="library" scope="PROVIDED" name="git (v1.2.6, RVM: ruby-2.0.0-p247) [gem]" level="application" />
19
+ <orderEntry type="library" scope="PROVIDED" name="open4 (v1.3.3, RVM: ruby-2.0.0-p247) [gem]" level="application" />
20
+ <orderEntry type="library" scope="PROVIDED" name="rake (v10.2.0, RVM: ruby-2.0.0-p247) [gem]" level="application" />
21
+ <orderEntry type="library" scope="PROVIDED" name="rspec (v2.14.1, RVM: ruby-2.0.0-p247) [gem]" level="application" />
22
+ <orderEntry type="library" scope="PROVIDED" name="rspec-core (v2.14.8, RVM: ruby-2.0.0-p247) [gem]" level="application" />
23
+ <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v2.14.5, RVM: ruby-2.0.0-p247) [gem]" level="application" />
24
+ <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v2.14.6, RVM: ruby-2.0.0-p247) [gem]" level="application" />
25
+ <orderEntry type="library" scope="PROVIDED" name="thor (v0.19.1, RVM: ruby-2.0.0-p247) [gem]" level="application" />
26
+ </component>
27
+ </module>
28
+
data/spec/new_spec.rb ADDED
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ #require 'file_utils'
3
+
4
+ describe 'rigup' do
5
+
6
+ before :each do
7
+ @old_dir = Dir.pwd
8
+ Dir.chdir(@dir = Dir.mktmpdir("new_spec"))
9
+ end
10
+
11
+ after :each do
12
+ Dir.chdir(@old_dir)
13
+ end
14
+
15
+ def new_site_process(aGitUrl)
16
+ @script = Rigup::Cli.new
17
+ @script.invoke(:new, [aGitUrl, 'mysite'])
18
+ Dir.exists?('mysite/releases').should be
19
+ Dir.exists?('mysite/shared').should be
20
+ File.exists?('mysite/rigup.yml').should be
21
+
22
+ @config = YAML.load_file('mysite/rigup.yml')
23
+ @config[:git_url].should == aGitUrl
24
+ end
25
+
26
+ def mock_get_repo(aUrl,aPath)
27
+ basename = File.basename(aUrl,'.git')
28
+ src = "~/repos/underscore_plus" unless File.exists?(src = "~/repos/#{basename}")
29
+ FileUtils.cp_r(File.expand_path(src),File.expand_path(aPath))
30
+ end
31
+
32
+ def stub_out_github
33
+ ::Rigup::GitRepo.any_instance.stub(:clone) do |aUrl,aPath|
34
+ mock_get_repo(aUrl,aPath)
35
+ end
36
+ end
37
+
38
+ RELEASE_INSTALL = <<-EOS
39
+ class Deploy < Thor
40
+
41
+ desc 'install','install the freshly delivered files'
42
+ def install
43
+ puts 'install'
44
+ end
45
+
46
+ desc 'restart','restart the web server'
47
+ def restart
48
+ puts 'restart'
49
+ end
50
+
51
+ end
52
+ EOS
53
+
54
+ describe "Given new project" do
55
+
56
+ it "deploy should update_cache, install and link_live" do
57
+ #Rigup::Cli.any_instance.stub(:restart => true, :install => true)
58
+ new_site_process('https://github.com/buzzware/underscore_plus.git')
59
+ ::Rigup::GitRepo.any_instance.stub(:clone) do |aUrl,aPath|
60
+ mock_get_repo(aUrl,aPath)
61
+ RELEASE_INSTALL.to_file(File.join(aPath,'deploy.thor'))
62
+ end
63
+
64
+ @script2 = Rigup::Cli.new
65
+ @script2.invoke(:deploy, ['mysite'])
66
+
67
+ Dir.exists?('mysite/shared/cached-copy').should be
68
+ Dir.exists?('mysite/shared/cached-copy/.git').should be
69
+ Dir.exists?('mysite/shared/cached-copy/.git').should be
70
+
71
+ Dir.exists?('mysite/current').should be
72
+
73
+ entries = Dir.entries('mysite/releases')
74
+ entries.length.should == 3
75
+ release_path = entries.last
76
+ release_path.should =~ /#{Time.now.strftime('%Y%m%d%H')}/
77
+
78
+ # Release method should write a rigup.yml including branch, commit, stage etc (should not be in repo)
79
+ # Rigup::DeployBase then reads this and makes it accessible to install and restart methods
80
+ # Then we can do things like displaying git commit hash on page as the site knows its own full identity
81
+ release_config = YAML.load_file("mysite/releases/#{release_path}/rigup.yml")
82
+ release_config.should be_a Hash
83
+
84
+ cache_repo = ::Rigup::GitRepo.new(Rigup::Context.new)
85
+ cache_repo.open('mysite/shared/cached-copy')
86
+
87
+ release_config[:branch].should be
88
+ release_config[:branch].should == cache_repo.branch
89
+ release_config[:commit].should be
90
+ release_config[:commit].should == cache_repo.sha
91
+ release_config[:stage].should == 'live'
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'rspec'
4
+ require 'rigup'
5
+ require_relative 'spec_utils'
6
+
7
+ include Rigup
8
+
9
+ #RSpec.configure do |config|
10
+ # def config.escaped_path(*parts)
11
+ # Regexp.compile(parts.join('[\\\/]'))
12
+ # end unless config.respond_to? :escaped_path
13
+ #end
14
+
15
+ # Include support files.
16
+ #Dir["#{File.expand_path('../', __FILE__)}/support/**/*.rb"].each { |f| require f }
File without changes
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rigup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Gary McGhee
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.5.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.5.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.19.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.19.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 2.14.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.14.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: git
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: buzztools
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: POpen4
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Toolset for deployment
112
+ email:
113
+ - contact@buzzware.com.au
114
+ executables:
115
+ - rigup
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - bin/rigup
120
+ - example deploy.thor
121
+ - Gemfile
122
+ - lib/rigup/cli.rb
123
+ - lib/rigup/config.rb
124
+ - lib/rigup/context.rb
125
+ - lib/rigup/deploy_base.rb
126
+ - lib/rigup/git_repo.rb
127
+ - lib/rigup/install_utils.rb
128
+ - lib/rigup/runability.rb
129
+ - lib/rigup/svn.rb
130
+ - lib/rigup/version.rb
131
+ - lib/rigup.rb
132
+ - notes.txt
133
+ - Rakefile
134
+ - README.md
135
+ - rigup.gemspec
136
+ - rigup.iml
137
+ - spec/new_spec.rb
138
+ - spec/spec_helper.rb
139
+ - spec/spec_utils.rb
140
+ - .gitignore
141
+ homepage: http://github.com/buzzware/rigup
142
+ licenses: []
143
+ metadata: {}
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 2.1.5
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: Toolset for deployment
164
+ test_files:
165
+ - spec/new_spec.rb
166
+ - spec/spec_helper.rb
167
+ - spec/spec_utils.rb