krausefx-shenzhen 0.14.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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +68 -0
- data/LICENSE +19 -0
- data/README.md +198 -0
- data/Rakefile +9 -0
- data/lib/shenzhen.rb +4 -0
- data/lib/shenzhen/agvtool.rb +17 -0
- data/lib/shenzhen/commands.rb +16 -0
- data/lib/shenzhen/commands/build.rb +213 -0
- data/lib/shenzhen/commands/distribute.rb +30 -0
- data/lib/shenzhen/commands/info.rb +94 -0
- data/lib/shenzhen/plistbuddy.rb +9 -0
- data/lib/shenzhen/plugins/crashlytics.rb +82 -0
- data/lib/shenzhen/plugins/deploygate.rb +97 -0
- data/lib/shenzhen/plugins/fir.rb +145 -0
- data/lib/shenzhen/plugins/ftp.rb +181 -0
- data/lib/shenzhen/plugins/hockeyapp.rb +119 -0
- data/lib/shenzhen/plugins/itunesconnect.rb +142 -0
- data/lib/shenzhen/plugins/pgyer.rb +135 -0
- data/lib/shenzhen/plugins/rivierabuild.rb +81 -0
- data/lib/shenzhen/plugins/s3.rb +139 -0
- data/lib/shenzhen/plugins/testfairy.rb +99 -0
- data/lib/shenzhen/version.rb +3 -0
- data/lib/shenzhen/xcodebuild.rb +99 -0
- data/shenzhen.gemspec +37 -0
- metadata +264 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'openssl'
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday_middleware'
|
5
|
+
|
6
|
+
module Shenzhen::Plugins
|
7
|
+
module RivieraBuild
|
8
|
+
class Client
|
9
|
+
HOSTNAME = 'apps.rivierabuild.com'
|
10
|
+
|
11
|
+
def initialize(api_token)
|
12
|
+
@api_token = api_token
|
13
|
+
@connection = Faraday.new(:url => "https://#{HOSTNAME}", :request => { :timeout => 120 }) do |builder|
|
14
|
+
builder.request :multipart
|
15
|
+
builder.request :url_encoded
|
16
|
+
builder.response :json, :content_type => /\bjson$/
|
17
|
+
builder.use FaradayMiddleware::FollowRedirects
|
18
|
+
builder.adapter :net_http
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def upload_build(ipa, options)
|
23
|
+
options[:file] = Faraday::UploadIO.new(ipa, 'application/octet-stream') if ipa and File.exist?(ipa)
|
24
|
+
|
25
|
+
@connection.post do |req|
|
26
|
+
req.url("/api/upload")
|
27
|
+
req.body = options
|
28
|
+
end.on_complete do |env|
|
29
|
+
yield env[:status], env[:body] if block_given?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
command :'distribute:rivierabuild' do |c|
|
37
|
+
c.syntax = "ipa distribute:rivierabuild [options]"
|
38
|
+
c.summary = "Distribute an .ipa file over RivieraBuild"
|
39
|
+
c.description = ""
|
40
|
+
c.option '-f', '--file FILE', ".ipa file for the build"
|
41
|
+
c.option '-k', '--key KEY', "API KEY. Available at https://apps.rivierabuild.com/settings"
|
42
|
+
c.option '-a', '--availability AVAILABILITY', "For how long the build will be available? More info: http://api.rivierabuild.com"
|
43
|
+
c.option '-p', '--passcode PASSCODE', "Optional passcode required to install the build on a device"
|
44
|
+
c.option '-n', '--note NOTE', "Release notes for the build, Markdown"
|
45
|
+
c.option '--commit-sha SHA', "The Git commit SHA for this build"
|
46
|
+
c.option '--app-id', "Riviera Build Application ID"
|
47
|
+
|
48
|
+
c.action do |args, options|
|
49
|
+
determine_file! unless @file = options.file
|
50
|
+
say_warning "Missing or unspecified .ipa file" unless @file and File.exist?(@file)
|
51
|
+
|
52
|
+
determine_rivierabuild_api_token! unless @api_token = options.key || ENV['RIVIERA_API_KEY']
|
53
|
+
say_error "Missing API Token" and abort unless @api_token
|
54
|
+
|
55
|
+
determine_availability! unless @availability = options.availability
|
56
|
+
say_error "Missing availability" and abort unless @availability
|
57
|
+
|
58
|
+
parameters = {}
|
59
|
+
parameters[:api_key] = @api_token
|
60
|
+
parameters[:availability] = @availability
|
61
|
+
parameters[:passcode] = options.passcode if options.passcode
|
62
|
+
parameters[:app_id] = options.app_id if options.app_id
|
63
|
+
parameters[:note] = options.note if options.note
|
64
|
+
parameters[:commit_sha] = options.commit_sha if options.commit_sha
|
65
|
+
|
66
|
+
client = Shenzhen::Plugins::RivieraBuild::Client.new(@api_token)
|
67
|
+
response = client.upload_build(@file, parameters)
|
68
|
+
case response.status
|
69
|
+
when 200...300
|
70
|
+
say_ok "Build successfully uploaded to RivieraBuild: #{response.body['file_url']}"
|
71
|
+
else
|
72
|
+
say_error "Error uploading to RivieraBuild: #{response.body}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def determine_rivierabuild_api_token!
|
79
|
+
@api_token ||= ask "API Key:"
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module Shenzhen::Plugins
|
4
|
+
module S3
|
5
|
+
class Client
|
6
|
+
def initialize(access_key_id, secret_access_key, region)
|
7
|
+
@s3 = AWS::S3.new(:access_key_id => access_key_id,
|
8
|
+
:secret_access_key => secret_access_key,
|
9
|
+
:region => region)
|
10
|
+
end
|
11
|
+
|
12
|
+
def upload_build(ipa, options)
|
13
|
+
path = expand_path_with_substitutions_from_ipa_plist(ipa, options[:path]) if options[:path]
|
14
|
+
|
15
|
+
@s3.buckets.create(options[:bucket]) if options[:create]
|
16
|
+
|
17
|
+
bucket = @s3.buckets[options[:bucket]]
|
18
|
+
|
19
|
+
uploaded_urls = []
|
20
|
+
|
21
|
+
files = []
|
22
|
+
files << ipa
|
23
|
+
files << options[:dsym] if options[:dsym]
|
24
|
+
files.each do |file|
|
25
|
+
basename = File.basename(file)
|
26
|
+
key = path ? File.join(path, basename) : basename
|
27
|
+
File.open(file) do |descriptor|
|
28
|
+
obj = bucket.objects.create(key, descriptor, :acl => options[:acl])
|
29
|
+
uploaded_urls << obj.public_url.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
uploaded_urls
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def expand_path_with_substitutions_from_ipa_plist(ipa, path)
|
39
|
+
substitutions = path.scan(/\{CFBundle[^}]+\}/)
|
40
|
+
return path if substitutions.empty?
|
41
|
+
|
42
|
+
Dir.mktmpdir do |dir|
|
43
|
+
system "unzip -q #{ipa} -d #{dir} 2> /dev/null"
|
44
|
+
|
45
|
+
plist = Dir["#{dir}/**/*.app/Info.plist"].last
|
46
|
+
|
47
|
+
substitutions.uniq.each do |substitution|
|
48
|
+
key = substitution[1...-1]
|
49
|
+
value = Shenzhen::PlistBuddy.print(plist, key)
|
50
|
+
|
51
|
+
path.gsub!(Regexp.new(substitution), value) if value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
return path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
command :'distribute:s3' do |c|
|
62
|
+
c.syntax = "ipa distribute:s3 [options]"
|
63
|
+
c.summary = "Distribute an .ipa file over Amazon S3"
|
64
|
+
c.description = ""
|
65
|
+
|
66
|
+
c.example '', '$ ipa distribute:s3 -f ./file.ipa -a accesskeyid --bucket bucket-name'
|
67
|
+
|
68
|
+
c.option '-f', '--file FILE', ".ipa file for the build"
|
69
|
+
c.option '-d', '--dsym FILE', "zipped .dsym package for the build"
|
70
|
+
c.option '-a', '--access-key-id ACCESS_KEY_ID', "AWS Access Key ID"
|
71
|
+
c.option '-s', '--secret-access-key SECRET_ACCESS_KEY', "AWS Secret Access Key"
|
72
|
+
c.option '-b', '--bucket BUCKET', "S3 bucket"
|
73
|
+
c.option '--[no-]create', "Create bucket if it doesn't already exist"
|
74
|
+
c.option '-r', '--region REGION', "Optional AWS region (for bucket creation)"
|
75
|
+
c.option '--acl ACL', "Uploaded object permissions e.g public_read (default), private, public_read_write, authenticated_read"
|
76
|
+
c.option '--source-dir SOURCE', "Optional source directory e.g. ./build"
|
77
|
+
c.option '-P', '--path PATH', "S3 'path'. Values from Info.plist will be substituded for keys wrapped in {} \n\t\t eg. \"/path/to/folder/{CFBundleVersion}/\" could be evaluated as \"/path/to/folder/1.0.0/\""
|
78
|
+
|
79
|
+
c.action do |args, options|
|
80
|
+
Dir.chdir(options.source_dir) if options.source_dir
|
81
|
+
|
82
|
+
determine_file! unless @file = options.file
|
83
|
+
say_error "Missing or unspecified .ipa file" and abort unless @file and File.exist?(@file)
|
84
|
+
|
85
|
+
determine_dsym! unless @dsym = options.dsym
|
86
|
+
say_error "Specified dSYM.zip file doesn't exist" if @dsym and !File.exist?(@dsym)
|
87
|
+
|
88
|
+
determine_access_key_id! unless @access_key_id = options.access_key_id
|
89
|
+
say_error "Missing AWS Access Key ID" and abort unless @access_key_id
|
90
|
+
|
91
|
+
determine_secret_access_key! unless @secret_access_key = options.secret_access_key
|
92
|
+
say_error "Missing AWS Secret Access Key" and abort unless @secret_access_key
|
93
|
+
|
94
|
+
determine_bucket! unless @bucket = options.bucket
|
95
|
+
say_error "Missing bucket" and abort unless @bucket
|
96
|
+
|
97
|
+
determine_region! unless @region = options.region
|
98
|
+
|
99
|
+
determine_acl! unless @acl = options.acl
|
100
|
+
say_error "Missing ACL" and abort unless @acl
|
101
|
+
|
102
|
+
@path = options.path
|
103
|
+
|
104
|
+
client = Shenzhen::Plugins::S3::Client.new(@access_key_id, @secret_access_key, @region)
|
105
|
+
|
106
|
+
begin
|
107
|
+
urls = client.upload_build @file, {:bucket => @bucket, :create => !!options.create, :acl => @acl, :dsym => @dsym, :path => @path}
|
108
|
+
urls.each { |url| say_ok url}
|
109
|
+
say_ok "Build successfully uploaded to S3"
|
110
|
+
rescue => exception
|
111
|
+
say_error "Error while uploading to S3: #{exception}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def determine_access_key_id!
|
118
|
+
@access_key_id ||= ENV['AWS_ACCESS_KEY_ID']
|
119
|
+
@access_key_id ||= ask "Access Key ID:"
|
120
|
+
end
|
121
|
+
|
122
|
+
def determine_secret_access_key!
|
123
|
+
@secret_access_key ||= ENV['AWS_SECRET_ACCESS_KEY']
|
124
|
+
@secret_access_key ||= ask "Secret Access Key:"
|
125
|
+
end
|
126
|
+
|
127
|
+
def determine_bucket!
|
128
|
+
@bucket ||= ENV['S3_BUCKET']
|
129
|
+
@bucket ||= ask "S3 Bucket:"
|
130
|
+
end
|
131
|
+
|
132
|
+
def determine_region!
|
133
|
+
@region ||= ENV['AWS_REGION']
|
134
|
+
end
|
135
|
+
|
136
|
+
def determine_acl!
|
137
|
+
@acl ||= "public_read"
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'openssl'
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday_middleware'
|
5
|
+
|
6
|
+
module Shenzhen::Plugins
|
7
|
+
module TestFairy
|
8
|
+
class Client
|
9
|
+
HOSTNAME = 'app.testfairy.com'
|
10
|
+
|
11
|
+
def initialize(api_key)
|
12
|
+
@api_key = api_key
|
13
|
+
@connection = Faraday.new(:url => "https://#{HOSTNAME}") do |builder|
|
14
|
+
builder.request :multipart
|
15
|
+
builder.request :url_encoded
|
16
|
+
builder.response :json, :content_type => /\bjson$/
|
17
|
+
builder.use FaradayMiddleware::FollowRedirects
|
18
|
+
builder.adapter :net_http
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def upload_build(ipa, options)
|
23
|
+
options[:file] = Faraday::UploadIO.new(ipa, 'application/octet-stream') if ipa and File.exist?(ipa)
|
24
|
+
|
25
|
+
if symbols_file = options.delete(:symbols_file)
|
26
|
+
options[:symbols_file] = Faraday::UploadIO.new(symbols_file, 'application/octet-stream')
|
27
|
+
end
|
28
|
+
|
29
|
+
@connection.post do |req|
|
30
|
+
req.url("/api/upload/")
|
31
|
+
req.body = options
|
32
|
+
end.on_complete do |env|
|
33
|
+
yield env[:status], env[:body] if block_given?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
command :'distribute:testfairy' do |c|
|
41
|
+
c.syntax = "ipa distribute:testfairy [options]"
|
42
|
+
c.summary = "Distribute an .ipa file over TestFairy"
|
43
|
+
c.description = ""
|
44
|
+
c.option '-f', '--file FILE', ".ipa file for the build"
|
45
|
+
c.option '-d', '--dsym FILE', "zipped .dsym package for the build"
|
46
|
+
c.option '-a', '--key KEY', "API Key. Available at https://app.testfairy.com/settings for details."
|
47
|
+
c.option '-c', '--comment COMMENT', "Comment for the build"
|
48
|
+
c.option '--tester-groups GROUPS', 'Comma-separated list of tester groups to be notified on the new build. Or "all" to notify all testers.'
|
49
|
+
c.option '--metrics METRICS', "Comma-separated list of metrics to record"
|
50
|
+
c.option '--max-duration DURATION', 'Maximum session recording length, eg 20m or 1h. Default is "10m". Maximum 24h.'
|
51
|
+
c.option '--video ACTIVE', 'Video recording settings "on", "off" or "wifi" for recording video only when wifi is available. Default is "on".'
|
52
|
+
c.option '--video-quality QUALITY', 'Video quality settings, "high", "medium" or "low". Default is "high".'
|
53
|
+
c.option '--video-rate RATE', 'Video rate recording in frames per second, default is "1.0".'
|
54
|
+
c.option '--icon-watermark ADD', 'Add a small watermark to app icon. Default is "off".'
|
55
|
+
|
56
|
+
c.action do |args, options|
|
57
|
+
determine_file! unless @file = options.file
|
58
|
+
say_warning "Missing or unspecified .ipa file" unless @file and File.exist?(@file)
|
59
|
+
|
60
|
+
determine_dsym! unless @dsym = options.dsym
|
61
|
+
say_warning "Specified dSYM.zip file doesn't exist" if @dsym and !File.exist?(@dsym)
|
62
|
+
|
63
|
+
determine_testfairy_api_key! unless @api_key = options.key || ENV['TESTFAIRY_API_KEY']
|
64
|
+
say_error "Missing API Key" and abort unless @api_key
|
65
|
+
|
66
|
+
determine_notes! unless @comment = options.comment
|
67
|
+
say_error "Missing release comment" and abort unless @comment
|
68
|
+
|
69
|
+
parameters = {}
|
70
|
+
# Required
|
71
|
+
parameters[:api_key] = @api_key
|
72
|
+
# Optional
|
73
|
+
parameters[:comment] = @comment
|
74
|
+
parameters[:symbols_file] = @dsym if @dsym
|
75
|
+
parameters[:testers_groups] = options.testers_groups if options.testers_groups
|
76
|
+
parameters[:'max-duration'] = options.max_duration if options.max_duration
|
77
|
+
parameters[:video] = options.video if options.video
|
78
|
+
parameters[:'video-quality'] = options.video_quality if options.video_quality
|
79
|
+
parameters[:'video-rate'] = options.video_rate if options.video_rate
|
80
|
+
parameters[:'icon-watermark'] = options.icon_watermark if options.icon_watermark
|
81
|
+
parameters[:metrics] = options.metrics if options.metrics
|
82
|
+
|
83
|
+
|
84
|
+
client = Shenzhen::Plugins::TestFairy::Client.new(@api_key)
|
85
|
+
response = client.upload_build(@file, parameters)
|
86
|
+
case response.status
|
87
|
+
when 200...300
|
88
|
+
say_ok "Build successfully uploaded to TestFairy"
|
89
|
+
else
|
90
|
+
say_error "Error uploading to TestFairy: #{response.body}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def determine_testfairy_api_key!
|
97
|
+
@api_key ||= ask "API Key:"
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Shenzhen::XcodeBuild
|
4
|
+
class Info < OpenStruct; end
|
5
|
+
class Settings < OpenStruct
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(hash = {})
|
9
|
+
super
|
10
|
+
self.targets = hash.keys
|
11
|
+
end
|
12
|
+
|
13
|
+
def members
|
14
|
+
self.targets
|
15
|
+
end
|
16
|
+
|
17
|
+
def each
|
18
|
+
members.each do |target|
|
19
|
+
yield target, send(target)
|
20
|
+
end
|
21
|
+
|
22
|
+
self
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Error < StandardError; end
|
27
|
+
class NilOutputError < Error; end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
def info(*args)
|
31
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
32
|
+
output = `xcrun xcodebuild -list #{(args + args_from_options(options)).join(" ")} 2>&1`
|
33
|
+
|
34
|
+
raise Error.new $1 if /^xcodebuild\: error\: (.+)$/ === output
|
35
|
+
|
36
|
+
return nil unless /\S/ === output
|
37
|
+
|
38
|
+
lines = output.split(/\n/)
|
39
|
+
info, group = {}, nil
|
40
|
+
|
41
|
+
info[:project] = lines.shift.match(/\"(.+)\"\:/)[1] rescue nil
|
42
|
+
|
43
|
+
lines.each do |line|
|
44
|
+
if /\:$/ === line
|
45
|
+
group = line.strip[0...-1].downcase.gsub(/\s+/, '_')
|
46
|
+
info[group] = []
|
47
|
+
next
|
48
|
+
end
|
49
|
+
|
50
|
+
unless group.nil? or /\.$/ === line
|
51
|
+
info[group] << line.strip
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
info.each do |group, values|
|
56
|
+
next unless Array === values
|
57
|
+
values.delete("") and values.uniq!
|
58
|
+
end
|
59
|
+
|
60
|
+
Info.new(info)
|
61
|
+
end
|
62
|
+
|
63
|
+
def settings(*args)
|
64
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
65
|
+
output = `xcrun xcodebuild #{(args + args_from_options(options)).join(" ")} -showBuildSettings 2> /dev/null`
|
66
|
+
|
67
|
+
return nil unless /\S/ === output
|
68
|
+
|
69
|
+
raise Error.new $1 if /^xcodebuild\: error\: (.+)$/ === output
|
70
|
+
|
71
|
+
lines = output.split(/\n/)
|
72
|
+
|
73
|
+
settings, target = {}, nil
|
74
|
+
lines.each do |line|
|
75
|
+
case line
|
76
|
+
when /Build settings for action build and target \"?([^":]+)/
|
77
|
+
target = $1
|
78
|
+
settings[target] = {}
|
79
|
+
else
|
80
|
+
key, value = line.split(/\=/).collect(&:strip)
|
81
|
+
settings[target][key] = value if target
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Settings.new(settings)
|
86
|
+
end
|
87
|
+
|
88
|
+
def version
|
89
|
+
output = `xcrun xcodebuild -version`
|
90
|
+
output.scan(/([\d+\.?]+)/).flatten.first rescue nil
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def args_from_options(options = {})
|
96
|
+
options.reject{|key, value| value.nil?}.collect{|key, value| "-#{key} '#{value}'"}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/shenzhen.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require "shenzhen/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "krausefx-shenzhen"
|
8
|
+
s.authors = ["Mattt Thompson"]
|
9
|
+
s.email = "m@mattt.me"
|
10
|
+
s.license = "MIT"
|
11
|
+
s.homepage = "http://nomad-cli.com"
|
12
|
+
s.version = Shenzhen::VERSION
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
s.summary = "Shenzhen"
|
15
|
+
s.description = "CLI for Building & Distributing iOS Apps (.ipa Files)"
|
16
|
+
|
17
|
+
s.add_dependency "commander", "~> 4.3"
|
18
|
+
s.add_dependency "highline", ">= 1.7.2"
|
19
|
+
s.add_dependency "terminal-table", "~> 1.4.5"
|
20
|
+
s.add_dependency "json", "~> 1.8"
|
21
|
+
s.add_dependency "faraday", "~> 0.8.9"
|
22
|
+
s.add_dependency "faraday_middleware", "~> 0.9"
|
23
|
+
s.add_dependency "dotenv", ">= 0.7"
|
24
|
+
s.add_dependency "aws-sdk", "~> 1.0"
|
25
|
+
s.add_dependency "net-sftp", "~> 2.1.2"
|
26
|
+
s.add_dependency "plist", "~> 3.1.0"
|
27
|
+
s.add_dependency "rubyzip", "~> 1.1"
|
28
|
+
s.add_dependency "security", "~> 0.1.3"
|
29
|
+
|
30
|
+
s.add_development_dependency "rspec"
|
31
|
+
s.add_development_dependency "rake"
|
32
|
+
|
33
|
+
s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
|
34
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
35
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
end
|