azdeploy 1.0.0
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.
- checksums.yaml +7 -0
- data/azdeploy.gemspec +12 -0
- data/lib/azdeploy.rb +24 -0
- data/lib/build.rb +70 -0
- data/lib/install.rb +164 -0
- data/lib/transform.rb +198 -0
- data/test/test_azdeploy.rb +4 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: be5422007de61780aa459e834d63cf996d6fda32
|
4
|
+
data.tar.gz: 601aa46de58bb4eb17158a2e8224630061388fcc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e23c42da14ece36254964e73d03b413ca3807f8bb19e245df3883c2526406142a40a028606c48526444f14158b86fa86a919d7c21b349476470af4d6a1f0a794
|
7
|
+
data.tar.gz: ea6b688efcc233f74c08bf09e901fcd6ea9ca94b975d79220221fa9d7b737a6126ab9871d8b91c905d91348326b0e4256448e814031e464b9bdb805bce4831b9
|
data/azdeploy.gemspec
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'azdeploy'
|
3
|
+
s.version = '1.0.0'
|
4
|
+
s.date = '2015-02-04'
|
5
|
+
s.summary = 'Setup and scripting support for .Net project builds'
|
6
|
+
s.description = 'Azure Deployment Gem. Provides easy setup and deployment scripting support for .Net project builds'
|
7
|
+
s.authors = ['Suresh Batta']
|
8
|
+
s.email = 'subatta@hotmail.com'
|
9
|
+
s.files = Dir['{test,lib,docs}/**/*'] + ['azdeploy.gemspec']
|
10
|
+
s.homepage = 'http://rubygems.org/gems/azdeploy'
|
11
|
+
s.license = 'MIT'
|
12
|
+
end
|
data/lib/azdeploy.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'install.rb'
|
2
|
+
require_relative 'transform.rb'
|
3
|
+
require_relative 'build.rb'
|
4
|
+
|
5
|
+
# installation
|
6
|
+
installers = [
|
7
|
+
'Ruby', 'Devkit', 'Doxygen', 'Gems'
|
8
|
+
]
|
9
|
+
|
10
|
+
installers.each { |method|
|
11
|
+
exit if !send("check#{method}")
|
12
|
+
}
|
13
|
+
|
14
|
+
require 'rubygems'
|
15
|
+
require 'nokogiri'
|
16
|
+
require 'azure'
|
17
|
+
|
18
|
+
# config transform
|
19
|
+
transform
|
20
|
+
|
21
|
+
# includes for rake build
|
22
|
+
include FileTest
|
23
|
+
require 'albacore'
|
24
|
+
require 'semver'
|
data/lib/build.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
def copy_output_files(fromDir, filePattern, outDir)
|
2
|
+
FileUtils.mkdir_p outDir unless exists?(outDir)
|
3
|
+
Dir.glob(File.join(fromDir, filePattern)){|file|
|
4
|
+
copy(file, outDir) if File.file?(file)
|
5
|
+
}
|
6
|
+
end
|
7
|
+
|
8
|
+
def project_outputs(props)
|
9
|
+
props[:projects].map{ |p| "src/#{p}/bin/#{BUILD_CONFIG}/#{p}.dll" }.
|
10
|
+
concat( props[:projects].map{ |p| "src/#{p}/bin/#{BUILD_CONFIG}/#{p}.exe" } ).
|
11
|
+
find_all{ |path| exists?(path) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_commit_hash_and_date
|
15
|
+
begin
|
16
|
+
commit = `git log -1 --pretty=format:%H`
|
17
|
+
git_date = `git log -1 --date=iso --pretty=format:%ad`
|
18
|
+
commit_date = DateTime.parse( git_date ).strftime("%Y-%m-%d %H%M%S")
|
19
|
+
rescue
|
20
|
+
commit = "git unavailable"
|
21
|
+
end
|
22
|
+
|
23
|
+
[commit, commit_date]
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_files stage, what_dlls, nuspec, folder='lib'
|
27
|
+
[['net35', 'net-3.5'], ['net40', 'net-4.0'], ['net45', 'net-4.5']].each{|fw|
|
28
|
+
takeFrom = File.join(stage, fw[1], what_dlls)
|
29
|
+
Dir.glob(takeFrom).each do |f|
|
30
|
+
nuspec.file(f.gsub("/", "\\"), "#{folder}\\#{fw[0]}")
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def commit_data
|
36
|
+
begin
|
37
|
+
commit = `git rev-parse --short HEAD`.chomp()[0,6]
|
38
|
+
git_date = `git log -1 --date=iso --pretty=format:%ad`
|
39
|
+
commit_date = DateTime.parse( git_date ).strftime("%Y-%m-%d %H%M%S")
|
40
|
+
rescue Exception => e
|
41
|
+
puts e.inspect
|
42
|
+
commit = (ENV['BUILD_VCS_NUMBER'] || "000000")[0,6]
|
43
|
+
commit_date = Time.new.strftime("%Y-%m-%d %H%M%S")
|
44
|
+
end
|
45
|
+
[commit, commit_date]
|
46
|
+
end
|
47
|
+
|
48
|
+
def waitfor(&block)
|
49
|
+
checks = 0
|
50
|
+
|
51
|
+
until block.call || checks >10
|
52
|
+
sleep 0.5
|
53
|
+
checks += 1
|
54
|
+
end
|
55
|
+
|
56
|
+
raise 'Waitfor timeout expired. Make sure that you aren\'t running something from the build output folders, or that you have browsed to it through Explorer.' if checks > 10
|
57
|
+
end
|
58
|
+
|
59
|
+
def cleantask(props)
|
60
|
+
|
61
|
+
FileUtils.rm_rf props[:output]
|
62
|
+
waitfor { !exists?(props[:output]) }
|
63
|
+
|
64
|
+
FileUtils.rm_rf props[:artifacts]
|
65
|
+
waitfor { !exists?(props[:artifacts]) }
|
66
|
+
|
67
|
+
Dir.mkdir props[:output]
|
68
|
+
Dir.mkdir props[:artifacts]
|
69
|
+
|
70
|
+
end
|
data/lib/install.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
def downloadFile(sourcePath, filePath, localFileLocation)
|
4
|
+
Net::HTTP.start(sourcePath) do |http|
|
5
|
+
resp = http.get(filePath)
|
6
|
+
f = open(localFileLocation, 'wb')
|
7
|
+
begin
|
8
|
+
requestPath = "http://#{sourcePath}#{filePath}"
|
9
|
+
puts "Downloading #{requestPath}..."
|
10
|
+
http.request_get(requestPath) do |resp|
|
11
|
+
resp.read_body do |segment|
|
12
|
+
f.write(segment)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
rescue
|
16
|
+
puts $!
|
17
|
+
return false
|
18
|
+
ensure
|
19
|
+
f.close()
|
20
|
+
end
|
21
|
+
end
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
|
25
|
+
def installFile(cmd, versionedExeFile)
|
26
|
+
begin
|
27
|
+
installerOutput = `#{cmd}`
|
28
|
+
puts installerOutput
|
29
|
+
rescue
|
30
|
+
puts $!
|
31
|
+
return false
|
32
|
+
ensure
|
33
|
+
puts 'Cleaning up...'
|
34
|
+
cmd = "#{versionedExeFile}"
|
35
|
+
installerOutput = `del #{cmd}`
|
36
|
+
puts installerOutput
|
37
|
+
end
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
|
41
|
+
def gemExists(name, version)
|
42
|
+
begin
|
43
|
+
gem name, version
|
44
|
+
rescue Gem::LoadError
|
45
|
+
puts "failed to load gem #{name} -v #{version}"
|
46
|
+
Gem.clear_paths
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
|
52
|
+
# Ruby version check
|
53
|
+
def checkRuby
|
54
|
+
puts 'Checking ruby version...'
|
55
|
+
# check if expected ruby version exists. If not download and install ruby
|
56
|
+
rubyVersion = "#{RUBY_VERSION}"
|
57
|
+
localVersion = "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
|
58
|
+
expectedVersion = '2.0.0-p481'
|
59
|
+
defaultInstallPath = 'Ruby200'
|
60
|
+
if (!rubyVersion.nil? && localVersion != expectedVersion)
|
61
|
+
puts "Uninstall incompatible version of ruby: #{localVersion} and install version: #{expectedVersion}"
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
if (rubyVersion.nil?)
|
65
|
+
puts 'Expected ruby version #{expectedVersion} not found'
|
66
|
+
|
67
|
+
sourcePath = 'dl.bintray.com'
|
68
|
+
versionedExeFile = "rubyinstaller-#{expectedVersion}.exe"
|
69
|
+
filePath = "/oneclick/rubyinstaller/#{versionedExeFile}?direct"
|
70
|
+
return false if !downloadFile(sourcePath, filePath, versionedExeFile)
|
71
|
+
|
72
|
+
puts 'Ruby installation in progress...'
|
73
|
+
cmd = "#{versionedExeFile} /silent /tasks=\"assocfiles,modpath\""
|
74
|
+
puts cmd
|
75
|
+
return false if !installFile(cmd, versionedExeFile)
|
76
|
+
end
|
77
|
+
puts 'Ruby ok'
|
78
|
+
return true
|
79
|
+
end
|
80
|
+
|
81
|
+
### devkit version check
|
82
|
+
def checkDevkit
|
83
|
+
# check if devkit exists
|
84
|
+
puts 'Checking devkit version...'
|
85
|
+
rubyPath = `where.exe ruby.exe`
|
86
|
+
rubyPath['bin\ruby.exe'] = 'devkit'
|
87
|
+
rubyPath = rubyPath.sub("\n", '') #doublequotes required for Line break, gotcha
|
88
|
+
if (!File.directory?(rubyPath))
|
89
|
+
puts 'devkit not found'
|
90
|
+
# if not download, install and setup devkit
|
91
|
+
puts 'Downloading devkit...'
|
92
|
+
|
93
|
+
sourcePath = 'cdn.rubyinstaller.org'
|
94
|
+
versionedExeFile = 'DevKit-mingw64-32-4.7.2-20130224-1151-sfx.exe'
|
95
|
+
filePath = "/archives/devkits/#{versionedExeFile}?direct"
|
96
|
+
return false if !downloadFile(sourcePath, filePath, versionedExeFile)
|
97
|
+
|
98
|
+
puts 'Devkit installation in progress...'
|
99
|
+
cmd = "#{versionedExeFile} -y -o\"#{rubyPath}\"" #no space after -o, gotcha
|
100
|
+
puts cmd
|
101
|
+
return false if !installFile(cmd, versionedExeFile)
|
102
|
+
|
103
|
+
puts 'Setting up devkit...'
|
104
|
+
Dir.chdir "#{rubyPath}"
|
105
|
+
cmd = 'ruby dk.rb init'
|
106
|
+
cmdoutput = `#{cmd}`
|
107
|
+
puts cmdoutput
|
108
|
+
if !cmdoutput.to_s.include?('Initialization complete!')
|
109
|
+
puts 'Error: could not initialize devkit.'
|
110
|
+
return false
|
111
|
+
end
|
112
|
+
|
113
|
+
cmd = 'ruby dk.rb review'
|
114
|
+
cmdoutput = `#{cmd}`
|
115
|
+
puts cmdoutput
|
116
|
+
if !cmdoutput.to_s.include?('DevKit functionality will be injected')
|
117
|
+
puts 'Error: devkit review failed'
|
118
|
+
return false
|
119
|
+
end
|
120
|
+
|
121
|
+
cmd = 'ruby dk.rb install'
|
122
|
+
cmdoutput = `#{cmd}`
|
123
|
+
puts cmdoutput
|
124
|
+
puts 'Restart console since environment variables are now updated'
|
125
|
+
|
126
|
+
return false
|
127
|
+
end
|
128
|
+
|
129
|
+
puts 'devkit ok'
|
130
|
+
return true
|
131
|
+
end
|
132
|
+
|
133
|
+
# doxygen check
|
134
|
+
def checkDoxygen
|
135
|
+
# download doxygen
|
136
|
+
return true
|
137
|
+
end
|
138
|
+
|
139
|
+
# check and install required gems
|
140
|
+
def checkGems
|
141
|
+
puts 'Checking required gems...'
|
142
|
+
gemlist = {
|
143
|
+
'albacore' => '0.3.6',
|
144
|
+
'semver' => '1.0.1',
|
145
|
+
'nokogiri' => '1.6.4.1',
|
146
|
+
'azure' => '0.6.4'
|
147
|
+
}
|
148
|
+
gemlist.each {|key, value|
|
149
|
+
if !gemExists(key, "=#{value}")
|
150
|
+
begin
|
151
|
+
puts "Installing missing gem: #{key}"
|
152
|
+
cmd = "gem install #{key} -v #{value}"
|
153
|
+
cmdoutput = `#{cmd}`
|
154
|
+
puts cmdoutput
|
155
|
+
rescue
|
156
|
+
puts 'gem install failed'
|
157
|
+
puts $!
|
158
|
+
return false # don't continue if even one gem install fails
|
159
|
+
end
|
160
|
+
end
|
161
|
+
}
|
162
|
+
# all gems installed
|
163
|
+
return true
|
164
|
+
end
|
data/lib/transform.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
def get(key)
|
2
|
+
begin
|
3
|
+
result = @svc.get_entity(@table, @env, key)
|
4
|
+
rescue
|
5
|
+
puts $!
|
6
|
+
puts "key : #{@key}"
|
7
|
+
puts
|
8
|
+
end
|
9
|
+
|
10
|
+
result
|
11
|
+
end
|
12
|
+
|
13
|
+
def getAll
|
14
|
+
begin
|
15
|
+
query = { :filter => "PartitionKey eq '#{@env}'" }
|
16
|
+
result = @svc.query_entities(@table, query)
|
17
|
+
rescue
|
18
|
+
puts $!
|
19
|
+
puts "Env : #{@env}"
|
20
|
+
puts
|
21
|
+
end
|
22
|
+
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
def getValue(key)
|
27
|
+
value = ''
|
28
|
+
@settings.each { |i|
|
29
|
+
if i.properties['RowKey'] == key
|
30
|
+
value = i.properties['setting']
|
31
|
+
break
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
value
|
36
|
+
end
|
37
|
+
|
38
|
+
def transformAppSettings
|
39
|
+
# go to each file and replace value of matching appSettings key
|
40
|
+
@configFiles.each{|file|
|
41
|
+
doc = Nokogiri::XML(File.read(file))
|
42
|
+
puts "Processing file: #{file}"
|
43
|
+
@settings.each { |i|
|
44
|
+
key = i.properties['RowKey']
|
45
|
+
node = doc.at_css "appSettings/add[@key='#{key}']"
|
46
|
+
if !node.nil?
|
47
|
+
oldVal = node['value']
|
48
|
+
#puts "Old value: #{oldVal}"
|
49
|
+
node['value'] = i.properties['setting']
|
50
|
+
newVal = node['value']
|
51
|
+
#puts "New value: #{newVal}"
|
52
|
+
end
|
53
|
+
}
|
54
|
+
File.write(file, doc.to_xml)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def transformServiceModelConfig
|
59
|
+
@configFiles.each{|file|
|
60
|
+
doc = Nokogiri::XML(File.read(file))
|
61
|
+
doc.xpath('//system.serviceModel').each do |node|
|
62
|
+
#puts node
|
63
|
+
if file.end_with?('app.config') || file.end_with?('App.config')
|
64
|
+
node.replace(getValue('system.ServiceModel.Client')) if !node.nil?
|
65
|
+
elsif file.end_with?('Web.config')
|
66
|
+
node.replace(getValue('system.ServiceModel.Service')) if !node.nil?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
File.write(file, doc.to_xml)
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def transformSystemWebCompilationAttribs
|
74
|
+
@configFiles.each{|file|
|
75
|
+
doc = Nokogiri::XML(File.read(file))
|
76
|
+
node = doc.at_css "compilation"
|
77
|
+
if !node.nil?
|
78
|
+
#puts node
|
79
|
+
node.xpath('//@debug').remove
|
80
|
+
node.xpath('//@tempDirectory').remove
|
81
|
+
#puts node
|
82
|
+
File.write(file, doc.to_xml)
|
83
|
+
end
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def transformCsdef
|
88
|
+
csdef = Dir.glob("**/*.csdef")
|
89
|
+
csdef.each{ |file|
|
90
|
+
doc = Nokogiri::XML(File.read(file))
|
91
|
+
|
92
|
+
node = doc.at_css "ServiceDefinition"
|
93
|
+
node['name'] = ENV['ServiceName'] if !node.nil?
|
94
|
+
|
95
|
+
node = doc.at_css "WebRole"
|
96
|
+
node['name'] = ENV['ServiceName'] if !node.nil?
|
97
|
+
|
98
|
+
node = doc.at_css "Certificates"
|
99
|
+
node.replace(getValue('Certificates_csdef')) if !node.nil?
|
100
|
+
|
101
|
+
node = doc.at_css "Endpoints"
|
102
|
+
node.replace(getValue('Endpoints')) if !node.nil?
|
103
|
+
|
104
|
+
node = doc.at_css "Bindings"
|
105
|
+
node.replace(getValue('Bindings')) if !node.nil?
|
106
|
+
|
107
|
+
File.write(file, doc.to_xml)
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
def transformCscfg
|
112
|
+
csdef = Dir.glob("**/*.cscfg")
|
113
|
+
csdef.each{ |file|
|
114
|
+
doc = Nokogiri::XML(File.read(file))
|
115
|
+
|
116
|
+
node = doc.at_css "ServiceConfiguration"
|
117
|
+
node['serviceName'] = ENV['ServiceName'] if !node.nil?
|
118
|
+
|
119
|
+
node = doc.at_css "Role"
|
120
|
+
node['name'] = ENV['ServiceName'] if !node.nil?
|
121
|
+
|
122
|
+
node = doc.at_css "Certificates"
|
123
|
+
node.replace(getValue('Certificates_cscfg')) if !node.nil?
|
124
|
+
|
125
|
+
File.write(file, doc.to_xml)
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def transformDiagnosticsCfg
|
130
|
+
csdef = Dir.glob("**/*.wadcfgx")
|
131
|
+
csdef.each{ |file|
|
132
|
+
doc = Nokogiri::XML(File.read(file))
|
133
|
+
|
134
|
+
node = doc.at_css "PrivateConfig/StorageAccount"
|
135
|
+
node['name'] = getValue('StorageAccount')
|
136
|
+
node['key'] = getValue('StorageAccountKey')
|
137
|
+
node = doc.at_css "StorageAccount"
|
138
|
+
node.content = getValue('StorageAccount')
|
139
|
+
|
140
|
+
File.write(file, doc.to_xml)
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
def transform
|
145
|
+
|
146
|
+
# environment invoked
|
147
|
+
@env = ENV['env'] || 'noenv'
|
148
|
+
if @env == 'noenv'
|
149
|
+
puts 'Environment name required to transform. No configuration changes will be done...'
|
150
|
+
return false
|
151
|
+
end
|
152
|
+
puts "Transforming config for environment: #{@env} ..."
|
153
|
+
|
154
|
+
# azure table storage account where settings reside
|
155
|
+
Azure.config.storage_account_name = ENV['StorageAccount']
|
156
|
+
Azure.config.storage_access_key = ENV['StorageAccountKey']
|
157
|
+
@table = ENV['ConfigSettingsTable']
|
158
|
+
|
159
|
+
# table service
|
160
|
+
@svc = Azure::TableService.new
|
161
|
+
|
162
|
+
# get all settings for environment
|
163
|
+
@settings = getAll
|
164
|
+
|
165
|
+
# get config templates
|
166
|
+
puts "Obtaining templates..."
|
167
|
+
csdefTemplate = getValue('ServiceDefinitionTemplate')
|
168
|
+
File.write('ServiceDefinition.csdef', csdefTemplate)
|
169
|
+
cscfgTemplate = getValue('ServiceConfigurationTemplate')
|
170
|
+
File.write('ServiceConfiguration.cscfg', cscfgTemplate)
|
171
|
+
|
172
|
+
# start updating config files
|
173
|
+
|
174
|
+
# find all App.config and web.config files
|
175
|
+
@configFiles = Dir.glob("**/app.config")
|
176
|
+
@configFiles.concat(Dir.glob("**/web.config"))
|
177
|
+
@configFiles.concat(Dir.glob("**/RuntimeWeb/*Web.dll.config"))
|
178
|
+
|
179
|
+
puts "Replacing app settings..."
|
180
|
+
transformAppSettings
|
181
|
+
|
182
|
+
puts "Removing debug compilation attributes..."
|
183
|
+
transformSystemWebCompilationAttribs
|
184
|
+
|
185
|
+
puts "Transforming csdef..."
|
186
|
+
transformCsdef
|
187
|
+
|
188
|
+
puts "Transforming cscfg..."
|
189
|
+
transformCscfg
|
190
|
+
|
191
|
+
puts "Transforming diagnostics cfg..."
|
192
|
+
transformDiagnosticsCfg
|
193
|
+
|
194
|
+
puts "Replacing service model settings..."
|
195
|
+
transformServiceModelConfig
|
196
|
+
|
197
|
+
return true
|
198
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: azdeploy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Suresh Batta
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-04 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Azure Deployment Gem. Provides easy setup and deployment scripting support
|
14
|
+
for .Net project builds
|
15
|
+
email: subatta@hotmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- test/test_azdeploy.rb
|
21
|
+
- lib/azdeploy.rb
|
22
|
+
- lib/build.rb
|
23
|
+
- lib/install.rb
|
24
|
+
- lib/transform.rb
|
25
|
+
- azdeploy.gemspec
|
26
|
+
homepage: http://rubygems.org/gems/azdeploy
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.0.14
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Setup and scripting support for .Net project builds
|
50
|
+
test_files: []
|