towel 0.1.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/.gitignore +11 -0
- data/.gitmodules +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +19 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/extensions/towel-minitest/.gitignore +8 -0
- data/extensions/towel-minitest/Gemfile +6 -0
- data/extensions/towel-minitest/README.md +43 -0
- data/extensions/towel-minitest/Rakefile +10 -0
- data/extensions/towel-minitest/bin/console +14 -0
- data/extensions/towel-minitest/bin/setup +8 -0
- data/extensions/towel-minitest/examples/.towel.toml +37 -0
- data/extensions/towel-minitest/examples/parallel_test.rb +29 -0
- data/extensions/towel-minitest/examples/simple_spec.rb +25 -0
- data/extensions/towel-minitest/examples/simple_test.rb +35 -0
- data/extensions/towel-minitest/lib/minitest/towel_plugin.rb +12 -0
- data/extensions/towel-minitest/lib/towel/minitest/reporter.rb +102 -0
- data/extensions/towel-minitest/lib/towel/minitest/version.rb +5 -0
- data/extensions/towel-minitest/lib/towel/minitest.rb +13 -0
- data/extensions/towel-minitest/test/test_helper.rb +4 -0
- data/extensions/towel-minitest/test/towel/minitest_test.rb +11 -0
- data/extensions/towel-minitest/towel-minitest.gemspec +41 -0
- data/extensions/towel-rspec/.gitignore +9 -0
- data/extensions/towel-rspec/.rspec +1 -0
- data/extensions/towel-rspec/Gemfile +6 -0
- data/extensions/towel-rspec/README.md +43 -0
- data/extensions/towel-rspec/Rakefile +10 -0
- data/extensions/towel-rspec/bin/console +14 -0
- data/extensions/towel-rspec/bin/setup +8 -0
- data/extensions/towel-rspec/examples/.rspec +2 -0
- data/extensions/towel-rspec/examples/simple_spec.rb +22 -0
- data/extensions/towel-rspec/lib/towel/rspec/formatter.rb +63 -0
- data/extensions/towel-rspec/lib/towel/rspec/version.rb +5 -0
- data/extensions/towel-rspec/lib/towel/rspec.rb +13 -0
- data/extensions/towel-rspec/spec/spec_helper.rb +100 -0
- data/extensions/towel-rspec/towel-rspec.gemspec +41 -0
- data/generated/.gitignore +1 -0
- data/generated/towel/v1alpha/collector_pb.rb +61 -0
- data/generated/towel/v1alpha/collector_services_pb.rb +41 -0
- data/generated/towel/v1alpha/towel_pb.rb +88 -0
- data/lib/towel/configuration.rb +82 -0
- data/lib/towel/environment.rb +64 -0
- data/lib/towel/log.rb +100 -0
- data/lib/towel/log_io.rb +71 -0
- data/lib/towel/session.rb +199 -0
- data/lib/towel/version.rb +3 -0
- data/lib/towel.rb +30 -0
- data/towel.gemspec +45 -0
- metadata +214 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
module Towel
|
2
|
+
# Helpers to collect the environment-specific details for invocations.
|
3
|
+
module Environment
|
4
|
+
# Environment variable that, if set, defines the source control management
|
5
|
+
# revision that is reported to Towel. See `Invocation.revision` in
|
6
|
+
# towel.proto for more details.
|
7
|
+
REVISION_ENV = "TOWEL_REVISION".freeze
|
8
|
+
|
9
|
+
# Environment variable that, if set, defines what other environment
|
10
|
+
# variables should be redacted.
|
11
|
+
REDACT_ENV = "TOWEL_REDACT".freeze
|
12
|
+
|
13
|
+
# Value that is substituted for environment variables that have been
|
14
|
+
# redacted.
|
15
|
+
REDACTED = "REDACTED".freeze
|
16
|
+
|
17
|
+
# The current platform that Towel is running on.
|
18
|
+
def self.platform
|
19
|
+
platform = Towel::V1alpha::Platform.new
|
20
|
+
uname = Etc.uname
|
21
|
+
platform.os = uname[:sysname]
|
22
|
+
platform.os_version = uname[:release]
|
23
|
+
platform.arch = uname[:machine]
|
24
|
+
return platform
|
25
|
+
end
|
26
|
+
|
27
|
+
# The hostname of this machine.
|
28
|
+
def self.hostname
|
29
|
+
Socket.gethostname
|
30
|
+
end
|
31
|
+
|
32
|
+
# The OS user that this process is running as.
|
33
|
+
def self.user
|
34
|
+
Etc.getlogin
|
35
|
+
end
|
36
|
+
|
37
|
+
# The current working directory.
|
38
|
+
def self.working_dir
|
39
|
+
Dir.getwd
|
40
|
+
end
|
41
|
+
|
42
|
+
# The arguments used to launch this process.
|
43
|
+
def self.argv
|
44
|
+
[$0].concat(ARGV)
|
45
|
+
end
|
46
|
+
|
47
|
+
# The environment variables set for this process.
|
48
|
+
def self.env_vars
|
49
|
+
redacted = ENV.fetch(REDACT_ENV, "").split(",").map(&:strip)
|
50
|
+
ENV.to_h do |name, value|
|
51
|
+
if redacted.include?(name)
|
52
|
+
[name, REDACTED]
|
53
|
+
else
|
54
|
+
[name, value]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# The version control system (VCS) revision for the code that is running.
|
60
|
+
def self.revision
|
61
|
+
ENV.fetch(REVISION_ENV, "")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/towel/log.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require "concurrent-ruby"
|
2
|
+
|
3
|
+
module Towel
|
4
|
+
class Log
|
5
|
+
# Logs must be associated with an invocation.
|
6
|
+
NAME_FORMAT = /invocations\/[^\/]+\/logs\/[^\/]+/
|
7
|
+
|
8
|
+
# How long to wait for more log entries before calling `AppendLog`. This
|
9
|
+
# tries to batch log lines together as opposed to making a call for each
|
10
|
+
# log line individually.
|
11
|
+
FLUSH_INTERVAL_SECONDS = 0.1
|
12
|
+
|
13
|
+
# How many log lines to send at a time. If there are more than this, they
|
14
|
+
# will be broken up into several `AppendLog` calls.
|
15
|
+
FLUSH_COUNT = 100
|
16
|
+
|
17
|
+
attr_reader :name
|
18
|
+
|
19
|
+
def initialize(name, stub)
|
20
|
+
unless name =~ NAME_FORMAT
|
21
|
+
raise ArgumentError, "Log name #{name} is invalid"
|
22
|
+
end
|
23
|
+
raise ArgumentError, "Stub must be provided" unless stub
|
24
|
+
@name = name
|
25
|
+
@stub = stub
|
26
|
+
@entries = Queue.new
|
27
|
+
@entry_id = Concurrent::AtomicFixnum.new(-1)
|
28
|
+
@flush_mutex = Mutex.new
|
29
|
+
@flush_timer_mutex = Mutex.new
|
30
|
+
@flush_timer = nil
|
31
|
+
|
32
|
+
# Create the log
|
33
|
+
log = Towel::V1alpha::Log.new
|
34
|
+
log.name = @name
|
35
|
+
request = Towel::V1alpha::CreateLogRequest.new
|
36
|
+
request.log = log
|
37
|
+
@stub.create_log(request)
|
38
|
+
end
|
39
|
+
|
40
|
+
def context
|
41
|
+
Thread.current["towel_context_#{object_id}"]
|
42
|
+
end
|
43
|
+
|
44
|
+
def context=(context)
|
45
|
+
Thread.current["towel_context_#{object_id}"] = context
|
46
|
+
end
|
47
|
+
|
48
|
+
def <<(line)
|
49
|
+
entry = Towel::V1alpha::LogEntry.new
|
50
|
+
entry.entry_id = @entry_id.increment
|
51
|
+
entry.log_time = Time.now.utc
|
52
|
+
entry.parent = context if context
|
53
|
+
entry.contents = line
|
54
|
+
|
55
|
+
@entries << entry
|
56
|
+
|
57
|
+
# Set up a flush to happen momentarily, allowing other lines to possibly
|
58
|
+
# be flushed together with this one.
|
59
|
+
@flush_timer_mutex.synchronize do
|
60
|
+
unless @flush_timer
|
61
|
+
@flush_timer = Concurrent::ScheduledTask.execute(
|
62
|
+
FLUSH_INTERVAL_SECONDS
|
63
|
+
) { flush }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def close
|
69
|
+
flush
|
70
|
+
Thread.current["towel_context_#{object_id}"] = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Ensure that entries get written out to Towel.
|
74
|
+
def flush
|
75
|
+
@flush_mutex.synchronize do
|
76
|
+
# Acknowledge the flush timer by unsetting it, if it exists.
|
77
|
+
@flush_timer_mutex.synchronize do
|
78
|
+
@flush_timer.cancel if @flush_timer
|
79
|
+
@flush_timer = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
until @entries.empty?
|
83
|
+
request = Towel::V1alpha::AppendLogRequest.new
|
84
|
+
request.name = @name
|
85
|
+
i = 0
|
86
|
+
while i < FLUSH_COUNT
|
87
|
+
i += 1
|
88
|
+
begin
|
89
|
+
entry = @entries.pop(true)
|
90
|
+
rescue ThreadError # Raised if the queue is empty
|
91
|
+
break
|
92
|
+
end
|
93
|
+
request.entries << entry
|
94
|
+
end
|
95
|
+
@stub.append_log(request)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/towel/log_io.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Towel
|
2
|
+
# An IO-like object that logs to Towel. Sits on top of Log. This relationship
|
3
|
+
# is inverted compared to most IO class structures, but log lines/entries are
|
4
|
+
# the fundamental unit of Towel. This class is merely an adapter to collect
|
5
|
+
# logs from IO streams like `$stdout` and `$stderr`.
|
6
|
+
class LogIO
|
7
|
+
def initialize(log)
|
8
|
+
raise ArgumentError, "Log must be specified" unless log
|
9
|
+
@log = log
|
10
|
+
@buffer = ""
|
11
|
+
@write_mutex = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def context=(context)
|
15
|
+
@log.context = context
|
16
|
+
end
|
17
|
+
|
18
|
+
def putc(c)
|
19
|
+
if c.is_a?(String)
|
20
|
+
write(c[0])
|
21
|
+
else
|
22
|
+
write(c.chr)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def puts(*args)
|
27
|
+
if args.empty?
|
28
|
+
write($/)
|
29
|
+
else
|
30
|
+
args.each do |arg|
|
31
|
+
arg = arg.to_s
|
32
|
+
if arg.end_with?($/)
|
33
|
+
write(arg)
|
34
|
+
else
|
35
|
+
write(arg, $/)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def write(*args)
|
42
|
+
@write_mutex.synchronize do
|
43
|
+
bytes = 0
|
44
|
+
args.each do |arg|
|
45
|
+
@buffer << arg
|
46
|
+
bytes += arg.bytesize
|
47
|
+
end
|
48
|
+
|
49
|
+
remaining = ""
|
50
|
+
@buffer.each_line do |line|
|
51
|
+
if line.end_with?($/)
|
52
|
+
@log << line
|
53
|
+
else
|
54
|
+
remaining = line
|
55
|
+
end
|
56
|
+
end
|
57
|
+
@buffer = remaining
|
58
|
+
|
59
|
+
bytes
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def flush
|
64
|
+
@log.flush
|
65
|
+
end
|
66
|
+
|
67
|
+
def close
|
68
|
+
@log.close
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
module Towel
|
2
|
+
# A session keeps track of an invocation throughout its lifespan. It is meant
|
3
|
+
# to be a helper to framework-specific implementations by providing a more
|
4
|
+
# convenient API over the raw gRPC calls.
|
5
|
+
class Session
|
6
|
+
def initialize(config = nil)
|
7
|
+
@config = config || Towel::Configuration.read
|
8
|
+
@stub = create_stub
|
9
|
+
@project = [
|
10
|
+
"organizations",
|
11
|
+
@config["collector"]["organization"],
|
12
|
+
"projects",
|
13
|
+
@config["collector"]["project"]
|
14
|
+
].join "/"
|
15
|
+
@invocation = nil
|
16
|
+
@groups = {}
|
17
|
+
@group_id = -1
|
18
|
+
@results = {}
|
19
|
+
@result_id = -1
|
20
|
+
@logs = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a new invocation. Returns the invocation URL at which users may
|
24
|
+
# view results. This must be called before other methods.
|
25
|
+
def create_invocation
|
26
|
+
invocation = Towel::V1alpha::Invocation.new
|
27
|
+
invocation.project = @project
|
28
|
+
invocation.labels["language"] = "ruby"
|
29
|
+
@config["labels"].each {|k, v| invocation.labels[k] = v }
|
30
|
+
invocation.start_time = Time.now.utc
|
31
|
+
|
32
|
+
# Add environment-specific information
|
33
|
+
invocation.platform = Towel::Environment.platform
|
34
|
+
invocation.hostname = Towel::Environment.hostname
|
35
|
+
invocation.user = Towel::Environment.user
|
36
|
+
invocation.working_dir = Towel::Environment.working_dir
|
37
|
+
invocation.argv.concat(Towel::Environment.argv)
|
38
|
+
Towel::Environment.env_vars.each do |name, value|
|
39
|
+
invocation.env_vars[name] = value
|
40
|
+
end
|
41
|
+
invocation.revision = Towel::Environment.revision
|
42
|
+
|
43
|
+
request = Towel::V1alpha::CreateInvocationRequest.new
|
44
|
+
request.invocation = invocation
|
45
|
+
|
46
|
+
@invocation = @stub.create_invocation(request)
|
47
|
+
|
48
|
+
@invocation.url
|
49
|
+
end
|
50
|
+
|
51
|
+
# Creates a result under a specified group and mark it as running. Returns
|
52
|
+
# the resource name of the result, which can be used to set a Log context.
|
53
|
+
def create_result(group, name, display_name: nil)
|
54
|
+
group_id = find_or_create_group(group)
|
55
|
+
|
56
|
+
result = Towel::V1alpha::Result.new
|
57
|
+
|
58
|
+
if @results.key?([group_id, name])
|
59
|
+
raise Error, "Result already has been created for '#{name}'"
|
60
|
+
else
|
61
|
+
result_id = @result_id += 1
|
62
|
+
@results[[group_id, name]] = result_id
|
63
|
+
end
|
64
|
+
|
65
|
+
result.name = [
|
66
|
+
@invocation.name,
|
67
|
+
"groups",
|
68
|
+
group_id,
|
69
|
+
"results",
|
70
|
+
result_id
|
71
|
+
].join "/"
|
72
|
+
result.display_name = display_name || name
|
73
|
+
result.start_time = Time.now.utc
|
74
|
+
result.state = Towel::V1alpha::ResultState::RUNNING
|
75
|
+
|
76
|
+
request = Towel::V1alpha::CreateResultRequest.new
|
77
|
+
request.result = result
|
78
|
+
|
79
|
+
@stub.create_result(request)
|
80
|
+
|
81
|
+
["groups", group_id, "results", result_id].join "/"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Updates a result with its outcome.
|
85
|
+
def update_result(group, name, state:, duration: nil, description: nil)
|
86
|
+
group_id = find_group(group)
|
87
|
+
unless @results.key?([group_id, name])
|
88
|
+
raise Error, "Cannot find result '#{name}'"
|
89
|
+
end
|
90
|
+
result_id = @results[[group_id, name]]
|
91
|
+
|
92
|
+
result = Towel::V1alpha::Result.new
|
93
|
+
update_mask = Google::Protobuf::FieldMask.new
|
94
|
+
|
95
|
+
result.name = [
|
96
|
+
@invocation.name,
|
97
|
+
"groups",
|
98
|
+
group_id,
|
99
|
+
"results",
|
100
|
+
result_id
|
101
|
+
].join "/"
|
102
|
+
|
103
|
+
update_mask.paths << "state"
|
104
|
+
result.state = state
|
105
|
+
|
106
|
+
if !duration.nil?
|
107
|
+
update_mask.paths << "duration"
|
108
|
+
result.duration = duration
|
109
|
+
end
|
110
|
+
|
111
|
+
if !description.nil?
|
112
|
+
update_mask.paths << "description"
|
113
|
+
result.description = description
|
114
|
+
end
|
115
|
+
|
116
|
+
request = Towel::V1alpha::UpdateResultRequest.new
|
117
|
+
request.result = result
|
118
|
+
request.update_mask = update_mask
|
119
|
+
|
120
|
+
@stub.update_result(request)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Create a new log with a given name. This can be used as-is or wrapped in a
|
124
|
+
# LogIO wrapper and used in place of a IO stream.
|
125
|
+
def create_log(name)
|
126
|
+
raise Error, "Log '#{name}' already exists" if @logs.key?(name)
|
127
|
+
resource_name = [@invocation.name, "logs", name].join "/"
|
128
|
+
@logs[name] = Log.new(resource_name, @stub)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Mark an invocation as finished (ran to termination without being
|
132
|
+
# cancelled). Do not call this and `#cancel` on a given Session. All
|
133
|
+
# outstanding logs will be flushed and closed as part of this.
|
134
|
+
def finish_invocation
|
135
|
+
@logs.each { |name, log| log.close }
|
136
|
+
|
137
|
+
request = Towel::V1alpha::FinishInvocationRequest.new
|
138
|
+
request.name = @invocation.name
|
139
|
+
request.end_time = Time.now.utc
|
140
|
+
|
141
|
+
@stub.finish_invocation(request)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Mark an invocation as cancelled. Do not call this and `#finish` on a
|
145
|
+
# given Session.
|
146
|
+
def cancel_invocation
|
147
|
+
request = Towel::V1alpha::CancelInvocationRequest.new
|
148
|
+
request.name = @invocation.name
|
149
|
+
|
150
|
+
@stub.cancel_invocation(request)
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
# Returns a Collector stub that the rest of this class can use to
|
156
|
+
# communicate with Towel.
|
157
|
+
def create_stub
|
158
|
+
ssl_credentials = GRPC::Core::ChannelCredentials.new
|
159
|
+
bearer_credentials = GRPC::Core::CallCredentials.new(proc {
|
160
|
+
{ authorization: "Bearer #{@config["auth"]["api_key"]}" }
|
161
|
+
})
|
162
|
+
credentials = ssl_credentials.compose(bearer_credentials)
|
163
|
+
|
164
|
+
Towel::V1alpha::Collector::Stub.new(
|
165
|
+
@config["collector"]["address"],
|
166
|
+
credentials
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Finds a group by name and attempts to create it if it does not exist.
|
171
|
+
# Returns the ID of the group. Most users of Session will call
|
172
|
+
# `#create_result` instead, which calls this behind the scenes.
|
173
|
+
def find_or_create_group(name)
|
174
|
+
if @groups.key?(name)
|
175
|
+
@groups[name]
|
176
|
+
else
|
177
|
+
# This group doesn't exist yet, so let's create it.
|
178
|
+
group_id = @group_id += 1
|
179
|
+
|
180
|
+
group = Towel::V1alpha::Group.new
|
181
|
+
group.name = [@invocation.name, "groups", group_id].join "/"
|
182
|
+
group.display_name = name
|
183
|
+
request = Towel::V1alpha::CreateGroupRequest.new
|
184
|
+
request.group = group
|
185
|
+
|
186
|
+
@stub.create_group(request)
|
187
|
+
@groups[name] = group_id
|
188
|
+
group_id
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Finds a previously created group by name. Raises `Towel::Error` if the
|
193
|
+
# group does not exist.
|
194
|
+
def find_group(name)
|
195
|
+
raise Error, "Cannot find group '#{name}'" unless @groups.key?(name)
|
196
|
+
@groups[name]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
data/lib/towel.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Standard library
|
2
|
+
require "etc"
|
3
|
+
require "socket"
|
4
|
+
|
5
|
+
# Gems
|
6
|
+
require "grpc"
|
7
|
+
require "toml"
|
8
|
+
|
9
|
+
# Generated Protocol Buffer/gRPC definitions
|
10
|
+
require "towel/v1alpha/collector_pb"
|
11
|
+
require "towel/v1alpha/collector_services_pb"
|
12
|
+
require "towel/v1alpha/towel_pb"
|
13
|
+
|
14
|
+
require "towel/configuration"
|
15
|
+
require "towel/environment"
|
16
|
+
require "towel/log"
|
17
|
+
require "towel/log_io"
|
18
|
+
require "towel/session"
|
19
|
+
require "towel/version"
|
20
|
+
|
21
|
+
module Towel
|
22
|
+
class Error < StandardError; end
|
23
|
+
|
24
|
+
# Returns whether Towel has been disabled.
|
25
|
+
def self.disabled?
|
26
|
+
%w[DISABLE_TOWEL TOWEL_DISABLE].any? do |key|
|
27
|
+
ENV.key?(key) && ENV[key] =~ /1|t(rue)?|y(es)?|disabled?/i
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/towel.gemspec
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "towel/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "towel"
|
8
|
+
spec.version = Towel::VERSION
|
9
|
+
spec.authors = ["Andrew Smith"]
|
10
|
+
spec.email = ["andrew@velvet.software"]
|
11
|
+
|
12
|
+
spec.summary = "Towel is a test result collector"
|
13
|
+
spec.description = <<END
|
14
|
+
Towel collects test results as they run, reporting them to towel.dev for easier
|
15
|
+
inspection, debugging, and sharing.
|
16
|
+
END
|
17
|
+
spec.homepage = "https://towel.dev"
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
if spec.respond_to?(:metadata)
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
22
|
+
spec.metadata["source_code_uri"] = "https://bitbucket.org/VelvetSoftware/towel-ruby"
|
23
|
+
spec.metadata["changelog_uri"] = "https://bitbucket.org/VelvetSoftware/towel-ruby/src/master/CHANGELOG.md"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Specify which files should be added to the gem when it is released.
|
27
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
28
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
29
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
30
|
+
end
|
31
|
+
spec.files += Dir["generated/**/*"]
|
32
|
+
spec.bindir = "exe"
|
33
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
34
|
+
spec.require_paths = ["lib", "generated"]
|
35
|
+
|
36
|
+
spec.add_dependency "concurrent-ruby", "~> 1.1"
|
37
|
+
spec.add_dependency "grpc", "~> 1.22"
|
38
|
+
spec.add_dependency "toml", "~> 0.2"
|
39
|
+
|
40
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
41
|
+
spec.add_development_dependency "fakefs", "~> 0.20"
|
42
|
+
spec.add_development_dependency "grpc-tools", "~> 1.22"
|
43
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
44
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
45
|
+
end
|