designshell 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/codeStyleSettings.xml +13 -0
- data/.idea/compiler.xml +25 -0
- data/.idea/copyright/profiles_settings.xml +5 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +11 -0
- data/.idea/modules.xml +11 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vcs.xml +9 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +196 -0
- data/LICENSE +22 -0
- data/README.md +35 -0
- data/README.rdoc +6 -0
- data/Rakefile +46 -0
- data/bin/designshelld.example.sh +5 -0
- data/bin/designshelld.example2.sh +3 -0
- data/bin/designshellserver +22 -0
- data/bin/ds +177 -0
- data/designshell.gemspec +40 -0
- data/designshell.iml +84 -0
- data/designshell.rdoc +5 -0
- data/features/designshell.feature +8 -0
- data/features/step_definitions/designshell_steps.rb +6 -0
- data/features/support/env.rb +15 -0
- data/lib/buzzcore_mods.rb +19 -0
- data/lib/designshell/context.rb +48 -0
- data/lib/designshell/core.rb +78 -0
- data/lib/designshell/deploy_plan.rb +40 -0
- data/lib/designshell/key_chain.rb +40 -0
- data/lib/designshell/repo.rb +112 -0
- data/lib/designshell/repo_server.rb +25 -0
- data/lib/designshell/site_client.rb +141 -0
- data/lib/designshell/utils.rb +18 -0
- data/lib/designshell/version.rb +3 -0
- data/lib/designshell.rb +19 -0
- data/lib/designshellserver/command.rb +180 -0
- data/lib/designshellserver/core.rb +41 -0
- data/lib/designshellserver.rb +17 -0
- data/spec/KeyChain_spec.rb +94 -0
- data/spec/RepoServer_spec.rb +54 -0
- data/spec/Repo_spec.rb +105 -0
- data/spec/build_spec.rb +29 -0
- data/spec/client_to_server_spec.rb +111 -0
- data/spec/designshell_context_spec.rb +18 -0
- data/spec/designshell_core_spec.rb +45 -0
- data/spec/rspec_helper.rb +8 -0
- data/spec/server/deploy_spec.rb +210 -0
- data/spec/server/dummy_spec.rb +52 -0
- data/spec/site_client_spec.rb +90 -0
- data/test/default_test.rb +14 -0
- data/test/test_helper.rb +9 -0
- data/testmart.iml +9 -0
- metadata +390 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module DesignShell
|
2
|
+
module Utils
|
3
|
+
def self.lookupItems(aDeployNode, aKeyChain)
|
4
|
+
result = {}
|
5
|
+
REXML::XPath.each(aDeployNode,'item') do |n|
|
6
|
+
name = n.attribute('name').to_s.to_nil
|
7
|
+
key = n.attribute('key').to_s.to_nil || name
|
8
|
+
next unless name
|
9
|
+
if text = n.text.to_nil # value in node
|
10
|
+
result[name] = text
|
11
|
+
else # value in @params['deploy_creds']
|
12
|
+
result[key] = aKeyChain.get(key.to_sym) if key
|
13
|
+
end
|
14
|
+
end
|
15
|
+
result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/designshell.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'designshell/version'
|
2
|
+
require 'osx_keychain'
|
3
|
+
require 'gli'
|
4
|
+
require 'git'
|
5
|
+
require 'buzzcore'
|
6
|
+
require 'buzzcore_mods'
|
7
|
+
require 'termios'
|
8
|
+
require 'highline/import'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'net/ssh'
|
11
|
+
require 'uri'
|
12
|
+
|
13
|
+
require 'designshell/utils'
|
14
|
+
require 'designshell/context'
|
15
|
+
require 'designshell/key_chain'
|
16
|
+
require 'designshell/repo'
|
17
|
+
require 'designshell/repo_server'
|
18
|
+
require 'designshell/deploy_plan'
|
19
|
+
require 'designshell/core'
|
@@ -0,0 +1,180 @@
|
|
1
|
+
module DesignShellServer
|
2
|
+
class Command
|
3
|
+
|
4
|
+
attr_accessor :core,:context,:line,:command,:id,:params,:repo
|
5
|
+
|
6
|
+
def initialize(aCore,aLine,aCommandName=nil)
|
7
|
+
@core = aCore
|
8
|
+
@context = aCore && aCore.context
|
9
|
+
@line = aLine
|
10
|
+
tl = aLine.dup
|
11
|
+
cmd = tl.extract!(/^[A-Z0-9_]+/)
|
12
|
+
@command = aCommandName || cmd
|
13
|
+
tl.bite! ' '
|
14
|
+
@id = tl.extract!(/^[a-z0-9]+/)
|
15
|
+
tl.bite! ' '
|
16
|
+
@params = ::JSON.parse(tl) if @params = tl.to_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute
|
20
|
+
self.send @command.to_sym
|
21
|
+
end
|
22
|
+
|
23
|
+
def writeline(aString)
|
24
|
+
@context.stdout.puts aString
|
25
|
+
end
|
26
|
+
|
27
|
+
# Prepares repo in cache dir for site
|
28
|
+
# requires params: repo_url,site
|
29
|
+
def prepare_cache # {:url=>'git://github.com/ddssda', :branch=>'master', :commit=>'ad452bcd'}
|
30
|
+
url = @params['repo_url']
|
31
|
+
site = @params['site']
|
32
|
+
wd = @core.working_dir_from_site(site)
|
33
|
+
|
34
|
+
@repo = DesignShell::Repo.new
|
35
|
+
suitable = if File.exists?(wd)
|
36
|
+
@repo.open wd
|
37
|
+
@repo.origin.url==url
|
38
|
+
else
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
if suitable
|
43
|
+
@repo.fetch
|
44
|
+
else
|
45
|
+
if File.exists? wd
|
46
|
+
raise RuntimeError.new('almost did bad delete') if !@core.cache_dir || @core.cache_dir.length<3 || !wd.begins_with?(@core.cache_dir)
|
47
|
+
FileUtils.rm_rf wd
|
48
|
+
end
|
49
|
+
@repo.clone(url, wd)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Switches @repo to given branch and/or commit
|
54
|
+
# Should call prepare_cache first to create @repo
|
55
|
+
# requires params: branch and/or commit
|
56
|
+
def checkout_branch_commit
|
57
|
+
#url = @params['repo_url']
|
58
|
+
#site = @params['site']
|
59
|
+
#wd = @core.working_dir_from_site(site)
|
60
|
+
branch = @params['branch'] || 'master'
|
61
|
+
commit = @params['commit']
|
62
|
+
@repo.checkout(commit,branch)
|
63
|
+
#perhaps use reset --hard here
|
64
|
+
if (commit)
|
65
|
+
@repo.merge(commit,['--ff-only'])
|
66
|
+
else
|
67
|
+
@repo.merge('origin/'+branch,['--ff-only'])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Determines whether to do an incremental or complete deploy and deploys current files in repo working dir to repo_url
|
72
|
+
# requires :
|
73
|
+
# uses: site_client.deploy_status
|
74
|
+
# params: deploy_cred
|
75
|
+
def deploy
|
76
|
+
deployPlanString = @repo.get_file_content('.deploy_plan.xml',@params['commit']||@params['branch'])
|
77
|
+
xmlRoot = XmlUtils.get_xml_root(deployPlanString)
|
78
|
+
# select plan
|
79
|
+
planNode = XmlUtils.single_node(xmlRoot,'plan')
|
80
|
+
# for each deploy
|
81
|
+
deployNode = XmlUtils.single_node(planNode,'deploy')
|
82
|
+
# create client for kind/method
|
83
|
+
@site_client = DesignShell::SiteClient.new({
|
84
|
+
:site_url => @params['site_url'],
|
85
|
+
:site_username => @params['site_username'],
|
86
|
+
:site_password => @params['site_password'],
|
87
|
+
})
|
88
|
+
ds = @site_client.deploy_status
|
89
|
+
site_repo_url = ds && ds['repo_url'].to_nil
|
90
|
+
site_branch = ds && ds['branch'].to_nil
|
91
|
+
site_commit = ds && ds['commit'].to_nil
|
92
|
+
repo_url = @repo.url
|
93
|
+
# @todo must limit uploads to build folder
|
94
|
+
fromPath = MiscUtils.ensure_slashes(XmlUtils.peek_node_value(deployNode,'fromPath','/'),false,true) # eg. /build/bigcommerce effectively selects a subfolder that should be debased
|
95
|
+
toPath = MiscUtils.ensure_slashes(XmlUtils.peek_node_value(deployNode,'toPath','/'),false,true) # eg. / effectively the new base for these files
|
96
|
+
if site_repo_url && site_repo_url==repo_url && site_branch && site_commit
|
97
|
+
# incremental
|
98
|
+
changes = @repo.changesBetweenCommits(site_commit,@repo.head.to_s)
|
99
|
+
uploads,deletes = convertChangesToUploadsDeletes(changes)
|
100
|
+
uploads.delete_if { |fp| !fp.begins_with?(fromPath) }
|
101
|
+
deletes.delete_if { |fp| !fp.begins_with?(fromPath) }
|
102
|
+
@site_client.delete_files(deletes,fromPath,toPath)
|
103
|
+
@site_client.upload_files(@repo.path,uploads,fromPath,toPath)
|
104
|
+
@site_client.deploy_status = {
|
105
|
+
:repo_url => @repo.url,
|
106
|
+
:branch => @repo.branch,
|
107
|
+
:commit => @repo.head.to_s,
|
108
|
+
:fromPath => fromPath,
|
109
|
+
:toPath => toPath
|
110
|
+
}
|
111
|
+
else
|
112
|
+
# complete
|
113
|
+
# for now, just deploy all files in wd, creating folders as necessary
|
114
|
+
# later, delete remote files not in wd except for eg. .deploy-status.xml and perhaps upload folders
|
115
|
+
uploads = MiscUtils.recursive_file_list(@repo.path,false)
|
116
|
+
uploads.delete_if do |fp|
|
117
|
+
!fp.begins_with?(fromPath) || fp.begins_with?('.git/')
|
118
|
+
end
|
119
|
+
@site_client.upload_files(@repo.path,uploads,fromPath,toPath)
|
120
|
+
@site_client.deploy_status = {
|
121
|
+
:repo_url => @repo.url,
|
122
|
+
:branch => @repo.branch,
|
123
|
+
:commit => @repo.head.to_s,
|
124
|
+
:fromPath => fromPath,
|
125
|
+
:toPath => toPath
|
126
|
+
}
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), have their type (i.e. regular file, symlink, submodule, ...) changed (T)
|
131
|
+
def convertChangesToUploadsDeletes(changes)
|
132
|
+
uploads = []
|
133
|
+
deletes = []
|
134
|
+
changes.each do |line|
|
135
|
+
continue if line==""
|
136
|
+
tabi = line.index("\t")
|
137
|
+
status = line[0,tabi]
|
138
|
+
path = line[tabi+1..-1]
|
139
|
+
if status.index('D')
|
140
|
+
deletes << path
|
141
|
+
else
|
142
|
+
uploads << path
|
143
|
+
end
|
144
|
+
end
|
145
|
+
return uploads,deletes
|
146
|
+
end
|
147
|
+
|
148
|
+
def DUMMY
|
149
|
+
id = StringUtils.random_word(8,8)
|
150
|
+
writeline "RECEIVED "+id
|
151
|
+
sleep 1
|
152
|
+
detail = ::JSON.generate({:this=>5, :that=>'ABC'}) #JSON.parse(document) or JSON.generate(data)
|
153
|
+
writeline ['PROGRESS',id,detail].join(' ')
|
154
|
+
sleep 1
|
155
|
+
detail = ::JSON.generate({:result=>123}) #JSON.parse(document) or JSON.generate(data)
|
156
|
+
writeline ['COMPLETE',id,detail].join(' ')
|
157
|
+
end
|
158
|
+
|
159
|
+
def QUICK
|
160
|
+
id = StringUtils.random_word(8,8)
|
161
|
+
writeline "RECEIVED "+id
|
162
|
+
writeline "COMPLETE "+id
|
163
|
+
end
|
164
|
+
|
165
|
+
def DEPLOY # {}
|
166
|
+
id = StringUtils.random_word(8,8)
|
167
|
+
writeline "RECEIVED "+id
|
168
|
+
detail =
|
169
|
+
writeline ['PROGRESS',id,::JSON.generate({:message => 'preparing cache'})].join(' ')
|
170
|
+
prepare_cache
|
171
|
+
writeline ['PROGRESS',id,::JSON.generate({:message => 'checkout'})].join(' ')
|
172
|
+
checkout_branch_commit
|
173
|
+
writeline ['PROGRESS',id,::JSON.generate({:message => 'deploying'})].join(' ')
|
174
|
+
deploy
|
175
|
+
writeline "COMPLETE "+id
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module DesignShellServer
|
2
|
+
class Core
|
3
|
+
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def initialize(aContext)
|
7
|
+
@context = aContext
|
8
|
+
end
|
9
|
+
|
10
|
+
def make_command(aLine)
|
11
|
+
command_name = aLine.scan(/^[A-Z0-9_]+/).pop.to_nil
|
12
|
+
return nil unless command_name && DesignShellServer::Command.instance_methods.include?(command_name)
|
13
|
+
return DesignShellServer::Command.new(self,aLine,command_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
if line = ENV['SSH_ORIGINAL_COMMAND']
|
18
|
+
command = make_command(line)
|
19
|
+
command.execute
|
20
|
+
else
|
21
|
+
@context.stdout.print "\n>"
|
22
|
+
@context.stdin.each_line do |line| line.chomp! "\n"
|
23
|
+
command = make_command(line)
|
24
|
+
command.execute
|
25
|
+
@context.stdout.print "\n>"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_dir
|
31
|
+
@cache_dir ||= MiscUtils.append_slash(@context.credentials[:cache_dir] || MiscUtils.make_temp_dir('DesignShellServer'))
|
32
|
+
end
|
33
|
+
|
34
|
+
def working_dir_from_site(aSite)
|
35
|
+
return nil unless aSite
|
36
|
+
aSite.gsub!(/[^a-zA-Z0-9.\-_]/,'_')
|
37
|
+
File.join(cache_dir,aSite)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'git'
|
2
|
+
require 'buzzcore'
|
3
|
+
require 'buzzcore_mods'
|
4
|
+
require 'net/dav'
|
5
|
+
require 'json'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'uri'
|
8
|
+
|
9
|
+
#require 'termios'
|
10
|
+
#require 'highline/import'
|
11
|
+
|
12
|
+
require 'designshell/context'
|
13
|
+
require 'designshell/site_client'
|
14
|
+
require 'designshell/deploy_plan'
|
15
|
+
require 'designshell/repo'
|
16
|
+
require 'designshellserver/core'
|
17
|
+
require 'designshellserver/command'
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "rspec_helper"
|
3
|
+
|
4
|
+
describe "KeyChain" do
|
5
|
+
|
6
|
+
keyChain = nil
|
7
|
+
|
8
|
+
before do
|
9
|
+
keyChain = DesignShell::KeyChain.new('DesignShellTest')
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should write,read,check value" do
|
13
|
+
value = StringUtils.random_word(8,8)
|
14
|
+
keyChain.set('testKey',value)
|
15
|
+
readValue = keyChain.get('testKey')
|
16
|
+
readValue.should == value
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should support get and set with a prefix" do
|
20
|
+
value1 = StringUtils.random_word(8,8)
|
21
|
+
value2 = StringUtils.random_word(8,8)
|
22
|
+
keyChain.set('testKey',value1)
|
23
|
+
keyChain.set('testKey',value2,'blah')
|
24
|
+
keyChain.get('testKey').should==value1
|
25
|
+
keyChain.get('testKey','blah').should==value2
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should support multiple get" do
|
29
|
+
value1 = StringUtils.random_word(8,8)
|
30
|
+
value2 = StringUtils.random_word(8,8)
|
31
|
+
keyChain.set('testKey1',value1)
|
32
|
+
keyChain.set('testKey2',value2)
|
33
|
+
keyChain.get('testKey1').should==value1
|
34
|
+
keyChain.get('testKey2').should==value2
|
35
|
+
keyChain.get(['testKey1']).should=={'testKey1' => value1}
|
36
|
+
keyChain.get(['testKey2']).should=={'testKey2' => value2}
|
37
|
+
keyChain.get(['testKey1','testKey2']).should=={'testKey1' => value1,'testKey2' => value2}
|
38
|
+
keyChain.get(%w(testKey1 testKey2)).should=={'testKey1' => value1,'testKey2' => value2}
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should support multiple get with a prefix" do
|
42
|
+
value1 = StringUtils.random_word(8,8)
|
43
|
+
value2 = StringUtils.random_word(8,8)
|
44
|
+
keyChain.set('testKey1','')
|
45
|
+
keyChain.set('testKey2','')
|
46
|
+
keyChain.set('testKey1',value1,'prefix1.')
|
47
|
+
keyChain.set('testKey2',value2,'prefix1.')
|
48
|
+
keyChain.get(%w(testKey1 testKey2)).should == {'testKey1' => '', 'testKey2' => ''}
|
49
|
+
keyChain.get(%w(testKey1 testKey2),'prefix1.').should == {'testKey1' => value1, 'testKey2' => value2}
|
50
|
+
keyChain.get(%w(testKey1 testKey2),'prefix1.',true).should == {'prefix1.testKey1' => value1, 'prefix1.testKey2' => value2}
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should support multiple set" do
|
54
|
+
value1 = StringUtils.random_word(8,8)
|
55
|
+
value2 = StringUtils.random_word(8,8)
|
56
|
+
keyChain.set({
|
57
|
+
'testKey1' => value1,
|
58
|
+
'testKey2' => value2
|
59
|
+
})
|
60
|
+
keyChain.get('testKey1').should==value1
|
61
|
+
keyChain.get('testKey2').should==value2
|
62
|
+
end
|
63
|
+
|
64
|
+
DEPLOY_XML = <<EOS
|
65
|
+
<deploy>
|
66
|
+
<kind>BigCommerce</kind>
|
67
|
+
<method>WebDav</method>
|
68
|
+
<fromPath>/build/bigcommerce</fromPath>
|
69
|
+
<toPath>/</toPath>
|
70
|
+
<item name="itemX">some item x</item>
|
71
|
+
<item name="itemY" key="itemYYY">YYY</item>
|
72
|
+
<item name="itemZ"></item>
|
73
|
+
<item name="itemZZ"/>
|
74
|
+
</deploy>
|
75
|
+
EOS
|
76
|
+
|
77
|
+
it "should support DesignShell::Utils.lookupItems" do
|
78
|
+
values = {
|
79
|
+
'itemX' => 'never read this',
|
80
|
+
'itemY' => 'never read this',
|
81
|
+
'itemYYY' => 'YYYYY',
|
82
|
+
'itemZ' => 'ZZZ',
|
83
|
+
'itemZZ' => 'ZZZZZ',
|
84
|
+
}
|
85
|
+
keyChain.set(values)
|
86
|
+
deployXml = XmlUtils.get_xml_root(DEPLOY_XML)
|
87
|
+
result = DesignShell::Utils.lookupItems(deployXml,keyChain)
|
88
|
+
result['itemX'].should=='some item x'
|
89
|
+
result['itemY'].should=='YYY' # text value overrides keychain
|
90
|
+
result['itemZ'].should==values['itemZ']
|
91
|
+
result['itemZZ'].should==values['itemZZ']
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "rspec_helper"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
describe "RepoServer" do
|
6
|
+
|
7
|
+
testFolder = nil
|
8
|
+
before do
|
9
|
+
testFolder = Dir.mktmpdir('Repo_spec-')
|
10
|
+
|
11
|
+
cred = Credentials.new(:designshell)
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
FileUtils.rm_rf testFolder if testFolder
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should list repos" do
|
20
|
+
keyChain = DesignShell::KeyChain.new('DesignShell')
|
21
|
+
#keyChain.set({
|
22
|
+
# :oauth_token => 'OAuth consumer Key',
|
23
|
+
# :oauth_secret => 'OAuth consumer Secret',
|
24
|
+
# :login => 'username, not email address',
|
25
|
+
# :password => 'user password'
|
26
|
+
#},'RepoServer.')
|
27
|
+
repoServer = DesignShell::RepoServer.new
|
28
|
+
# should move values to credentials that looks up keyChain
|
29
|
+
values = keyChain.get([:oauth_token,:oauth_secret,:login,:password],'RepoServer.').symbolize_keys
|
30
|
+
repoServer.setup(values)
|
31
|
+
|
32
|
+
result = repoServer.repos #"[#<Hashie::Mash is_private=true name="test1" owner="buzzware" scm="git" slug="test1">]"
|
33
|
+
result.class.should==Array
|
34
|
+
result.length.should > 0
|
35
|
+
result.first.scm.should=='git'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should clone the first repo" do
|
39
|
+
keyChain = DesignShell::KeyChain.new('DesignShell')
|
40
|
+
repoServer = DesignShell::RepoServer.new
|
41
|
+
values = keyChain.get([:oauth_token,:oauth_secret,:login,:password],'RepoServer.').symbolize_keys
|
42
|
+
repoServer.setup(values)
|
43
|
+
repos = repoServer.repos
|
44
|
+
repos.length.should > 0
|
45
|
+
repo = repos && repos.first
|
46
|
+
|
47
|
+
url = "git@bitbucket.org:#{repo.owner}/#{repo.slug}.git"
|
48
|
+
repo = DesignShell::Repo.new
|
49
|
+
result = repo.clone(url, testFolder)
|
50
|
+
repo.path.should == testFolder
|
51
|
+
repo.branches.class.should == Git::Branches
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/spec/Repo_spec.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "rspec_helper"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
describe "Repo" do
|
6
|
+
|
7
|
+
testFolder = nil
|
8
|
+
before do
|
9
|
+
testFolder = MiscUtils.real_path(Dir.mktmpdir('Repo_spec-'))
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
FileUtils.rm_rf testFolder if testFolder
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
COMMIT_RESULT1 = "[master (root-commit) 6bdd9e1] first commit 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 file1.txt"
|
18
|
+
|
19
|
+
it "commit should create commit object or string message" do
|
20
|
+
repo = DesignShell::Repo.new
|
21
|
+
repo.init testFolder
|
22
|
+
|
23
|
+
file1 = File.join(testFolder,'file1.txt')
|
24
|
+
content11 = '11111'
|
25
|
+
MiscUtils.string_to_file(content11,file1)
|
26
|
+
repo.add 'file1.txt'
|
27
|
+
commit1 = repo.commit_all('first commit')
|
28
|
+
commit1.class.should == Git::Object::Commit
|
29
|
+
|
30
|
+
commit2 = repo.commit_all('second commit without changes')
|
31
|
+
commit2.should == nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "create a repo, add file, commit, change, commit, reset, check" do
|
35
|
+
repo = DesignShell::Repo.new
|
36
|
+
repo.init testFolder
|
37
|
+
repo.path.should == MiscUtils.real_path(testFolder)
|
38
|
+
|
39
|
+
file1 = File.join(testFolder,'file1.txt')
|
40
|
+
content11 = '11111'
|
41
|
+
MiscUtils.string_to_file(content11,file1)
|
42
|
+
repo.git.add 'file1.txt'
|
43
|
+
commit1 = repo.commit_all('first commit')
|
44
|
+
|
45
|
+
content12 = '11111-some more text'
|
46
|
+
MiscUtils.string_to_file(content12,file1)
|
47
|
+
commit2 = repo.commit_all('second commit')
|
48
|
+
|
49
|
+
read_content = MiscUtils.string_from_file(file1)
|
50
|
+
read_content.should == content12
|
51
|
+
|
52
|
+
repo.reset_hard(commit1)
|
53
|
+
|
54
|
+
read_content = MiscUtils.string_from_file(file1)
|
55
|
+
read_content.should == content11
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should download a remote repo and check log, then re-open it and check log again" do
|
59
|
+
repo = DesignShell::Repo.new
|
60
|
+
url = "git@github.com:buzzware/underscore_plus.git"
|
61
|
+
repo.clone(url, testFolder)
|
62
|
+
repo.path.should == testFolder
|
63
|
+
repo.log.first.class.should == Git::Object::Commit
|
64
|
+
|
65
|
+
repo = DesignShell::Repo.new
|
66
|
+
repo.open testFolder
|
67
|
+
repo.path.should == testFolder
|
68
|
+
repo.log.first.class.should == Git::Object::Commit
|
69
|
+
repo.origin.url==url
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should download a remote repo and get diffs between commits" do
|
73
|
+
repo = DesignShell::Repo.new
|
74
|
+
url = "git@github.com:buzzware/underscore_plus.git"
|
75
|
+
repo.clone(url, testFolder)
|
76
|
+
|
77
|
+
commit1 = "4b133ff8825bbd488ba61fa3e3b82a5fa746ac6a"
|
78
|
+
commit2 = "d1b8440dc730ceb4471fbe7c42ccfac94ea12799"
|
79
|
+
changes = repo.changesBetweenCommits(commit1,commit2)
|
80
|
+
changes.should==["A\tunderscore_plus.js"]
|
81
|
+
changes = repo.changesBetweenCommits(commit2,commit1)
|
82
|
+
changes.should==["D\tunderscore_plus.js"]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should get contents of a given file from a given commit" do
|
86
|
+
repo = DesignShell::Repo.new
|
87
|
+
url = "git@github.com:buzzware/underscore_plus.git"
|
88
|
+
repo.clone(url, testFolder)
|
89
|
+
|
90
|
+
commit1 = "4b133ff8825bbd488ba61fa3e3b82a5fa746ac6a"
|
91
|
+
commit2 = "d1b8440dc730ceb4471fbe7c42ccfac94ea12799"
|
92
|
+
file1 = "README.md"
|
93
|
+
file2 = "underscore_plus.js"
|
94
|
+
|
95
|
+
readme = repo.get_file_content(file1,commit1)
|
96
|
+
readme.is_a?(String).should==true
|
97
|
+
readme.size.should > 0
|
98
|
+
code = repo.get_file_content(file2,commit1)
|
99
|
+
code.should==nil
|
100
|
+
code = repo.get_file_content(file2,commit2)
|
101
|
+
code.is_a?(String).should==true
|
102
|
+
code.size.should > 0
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
data/spec/build_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "rspec_helper"
|
3
|
+
|
4
|
+
describe "build" do
|
5
|
+
|
6
|
+
testFolder = nil
|
7
|
+
before do
|
8
|
+
testFolder = Dir.mktmpdir('Repo_spec-')
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
FileUtils.rm_rf testFolder if testFolder
|
13
|
+
end
|
14
|
+
|
15
|
+
#it "should build source folder into build folder" do
|
16
|
+
# context = DesignShell::Context.new()
|
17
|
+
# ds = DesignShell::Core.new(:context => context)
|
18
|
+
# ds.build
|
19
|
+
#end
|
20
|
+
|
21
|
+
#it "should commit the repository" do
|
22
|
+
# context = DesignShell::Context.new()
|
23
|
+
# repo = DesignShell::Repo.new
|
24
|
+
# repo.configure()
|
25
|
+
# repo.clone("git@github.com:buzzware/underscore_plus.git", testFolder)
|
26
|
+
# ds = DesignShell::Core.new(:context => context, :repo => repo)
|
27
|
+
# ds.ensure_repo_open.commit_all(context)
|
28
|
+
#end
|
29
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "rspec_helper"
|
3
|
+
|
4
|
+
describe "client to server interaction" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@key_chain = DesignShell::KeyChain.new('DesignShellTest')
|
8
|
+
@credentials = Credentials.new('designshell')
|
9
|
+
Dir.chdir @credentials[:deploy_repo_path]
|
10
|
+
@context = DesignShell::Context.new(
|
11
|
+
:argv=>[],
|
12
|
+
:env=>{},
|
13
|
+
:stdout=>$stdout,
|
14
|
+
:stdin=>$stdin,
|
15
|
+
:stderr=>$stderr,
|
16
|
+
:key_chain=>@key_chain,
|
17
|
+
:credentials=>@credentials
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should call QUICK and get results" do
|
22
|
+
dash = DesignShell::Core.new(
|
23
|
+
:context => @context
|
24
|
+
)
|
25
|
+
result = dash.call_server_command('QUICK')
|
26
|
+
puts result
|
27
|
+
result.begins_with?('RECEIVED').should == true
|
28
|
+
result.index('COMPLETE').should >= 0
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should call deploy" do
|
32
|
+
dash = DesignShell::Core.new(
|
33
|
+
:context => @context
|
34
|
+
)
|
35
|
+
result = dash.deploy
|
36
|
+
puts result
|
37
|
+
result.begins_with?('RECEIVED').should == true
|
38
|
+
result.index('COMPLETE').should >= 0
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
#it "should connect to SSH server" do
|
44
|
+
# Net::SSH.start( @context.credentials[:deploy_host],nil) do |ssh|
|
45
|
+
#
|
46
|
+
# #result = ssh.exec!("ls")
|
47
|
+
# #puts result
|
48
|
+
#
|
49
|
+
#
|
50
|
+
# #ssh.open_channel{|channel| #get root privelages
|
51
|
+
# ##configure behavior of channel
|
52
|
+
# #channel.on_data{|channel, data|
|
53
|
+
# # puts "#{data}"
|
54
|
+
# # if data =~ /^Password:/
|
55
|
+
# # channel.send_data("#{PASSWORD}\n")
|
56
|
+
# # elsif data =~ /root@/
|
57
|
+
# # channel.exec("tail /some/log/file.txt")
|
58
|
+
# # channel.on_data{"STOP LOOPING, DAMN YOU!"}
|
59
|
+
# # end
|
60
|
+
# #channel.on_close... (etc.)
|
61
|
+
# #
|
62
|
+
# #channel.request_pty do |ch,success|
|
63
|
+
# # if success
|
64
|
+
# # puts "pty successfully obtained"
|
65
|
+
# # else
|
66
|
+
# # puts "could not obtain pty"
|
67
|
+
# # end end
|
68
|
+
# #
|
69
|
+
# #channel.exec("sudoshell"){|channel, win| #custom sudo script.
|
70
|
+
# # if win
|
71
|
+
# # puts "ss command sent"
|
72
|
+
# # else puts "ss command FAIL"
|
73
|
+
# # end
|
74
|
+
# #}
|
75
|
+
# #
|
76
|
+
#
|
77
|
+
# ssh.open_channel do |channel|
|
78
|
+
# channel.on_data do |ch, data|
|
79
|
+
# puts "got stdout: #{data}"
|
80
|
+
# #channel.send_data "something for stdin\n"
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# channel.on_extended_data do |ch, type, data|
|
84
|
+
# puts "got stderr: #{data}"
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# channel.on_close do |ch|
|
88
|
+
# puts "channel is closing!"
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# channel.request_pty do |ch,success|
|
92
|
+
# if success
|
93
|
+
# puts "pty successfully obtained"
|
94
|
+
# else
|
95
|
+
# puts "could not obtain pty"
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
# #sleep 1
|
99
|
+
# result = channel.exec("DEPLOY") do |ch, success|
|
100
|
+
# abort "could not execute command" unless success
|
101
|
+
# end
|
102
|
+
# channel.wait
|
103
|
+
# puts result
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# ssh.loop
|
107
|
+
#
|
108
|
+
# end
|
109
|
+
#end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "rspec_helper"
|
3
|
+
|
4
|
+
describe "DesignShell::Context" do
|
5
|
+
|
6
|
+
it "should find repo path upward" do
|
7
|
+
tempdir = MiscUtils.real_path(MiscUtils.make_temp_dir('designshell_context_spec'))
|
8
|
+
Dir.mkdir(git_dir = File.join(tempdir,'.git'))
|
9
|
+
Dir.mkdir(File.join(tempdir,'one'))
|
10
|
+
orange = File.join(tempdir,'one/apple/orange')
|
11
|
+
FileUtils.mkpath(orange)
|
12
|
+
Dir.mkdir(File.join(tempdir,'two'))
|
13
|
+
Dir.chdir(orange)
|
14
|
+
context = DesignShell::Context.new({})
|
15
|
+
context.pwd.should==orange
|
16
|
+
context.find_git_root.should==tempdir
|
17
|
+
end
|
18
|
+
end
|