braid 1.1.9 → 1.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/braid/command.rb +38 -15
- data/lib/braid/commands/add.rb +21 -11
- data/lib/braid/commands/diff.rb +27 -10
- data/lib/braid/commands/push.rb +27 -13
- data/lib/braid/commands/remove.rb +17 -4
- data/lib/braid/commands/setup.rb +12 -3
- data/lib/braid/commands/status.rb +17 -8
- data/lib/braid/commands/update.rb +47 -23
- data/lib/braid/commands/upgrade_config.rb +18 -4
- data/lib/braid/config.rb +28 -18
- data/lib/braid/main.rb +47 -33
- data/lib/braid/mirror.rb +40 -29
- data/lib/braid/operations.rb +10 -16
- data/lib/braid/operations_lite.rb +1 -1
- data/lib/braid/sorbet/fake_runtime.rb +19 -0
- data/lib/braid/version.rb +1 -1
- data/lib/braid.rb +2 -10
- metadata +49 -7
@@ -1,12 +1,26 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
module Braid
|
3
3
|
module Commands
|
4
4
|
class UpgradeConfig < Command
|
5
|
+
class Options < T::Struct
|
6
|
+
prop :dry_run, T::Boolean
|
7
|
+
prop :allow_breaking_changes, T::Boolean
|
8
|
+
end
|
9
|
+
|
10
|
+
sig {params(options: Options).void}
|
11
|
+
def initialize(options)
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
sig {returns(Config::ConfigMode)}
|
5
18
|
def config_mode
|
6
19
|
Config::MODE_UPGRADE
|
7
20
|
end
|
8
21
|
|
9
|
-
|
22
|
+
sig {void}
|
23
|
+
def run_internal
|
10
24
|
# Config loading in MODE_UPGRADE will bail out only if the config
|
11
25
|
# version is too new.
|
12
26
|
|
@@ -38,11 +52,11 @@ The following breaking changes will occur:
|
|
38
52
|
MSG
|
39
53
|
end
|
40
54
|
|
41
|
-
if options
|
55
|
+
if @options.dry_run
|
42
56
|
puts <<-MSG
|
43
57
|
Run 'braid upgrade-config#{config.breaking_change_descs.empty? ? '' : ' --allow-breaking-changes'}' to perform the upgrade.
|
44
58
|
MSG
|
45
|
-
elsif !config.breaking_change_descs.empty? &&
|
59
|
+
elsif !config.breaking_change_descs.empty? && !@options.allow_breaking_changes
|
46
60
|
raise BraidError, 'You must pass --allow-breaking-changes to accept the breaking changes.'
|
47
61
|
else
|
48
62
|
config.write_db
|
data/lib/braid/config.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
require 'yaml'
|
3
3
|
require 'json'
|
4
4
|
require 'yaml/store'
|
@@ -84,20 +84,29 @@ module Braid
|
|
84
84
|
sig {returns(T::Array[String])}
|
85
85
|
attr_reader :breaking_change_descs
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@mode = options['mode'] || MODE_MAY_WRITE
|
87
|
+
sig {params(config_file: String, old_config_files: T::Array[String], mode: ConfigMode).void}
|
88
|
+
def initialize(config_file: CONFIG_FILE, old_config_files: [OLD_CONFIG_FILE], mode: MODE_MAY_WRITE)
|
89
|
+
@config_file = config_file
|
90
|
+
old_config_files = old_config_files
|
91
|
+
@mode = mode
|
93
92
|
|
94
93
|
data = load_config(@config_file, old_config_files)
|
95
|
-
@config_existed = !data.nil
|
94
|
+
@config_existed = T.let(!data.nil?, T::Boolean)
|
96
95
|
if !@config_existed
|
97
|
-
@config_version = CURRENT_CONFIG_VERSION
|
98
|
-
@db = {}
|
96
|
+
@config_version = T.let(CURRENT_CONFIG_VERSION, Integer)
|
97
|
+
@db = T.let({}, T::Hash[String, Mirror::MirrorAttributes])
|
99
98
|
elsif data['config_version'].is_a?(Numeric)
|
100
99
|
@config_version = data['config_version']
|
100
|
+
# WARNING (typing): In general, slapping a type annotation on the loaded
|
101
|
+
# data without validating its structure could be misleading as we work
|
102
|
+
# on the rest of the code. In the worst case, it might lead us to
|
103
|
+
# delete useful sanity checks because they trigger Sorbet unreachable
|
104
|
+
# code errors. As of this writing (2024-08-04), we have nothing to lose
|
105
|
+
# because we don't attempt to defend against malformed data anyway, and
|
106
|
+
# there's a benefit to having the type information available. If we add
|
107
|
+
# any validation, consider whether the annotations should be changed to
|
108
|
+
# verify that we did the validation correctly, e.g., by starting with a
|
109
|
+
# type like `T.anything` instead of `T.untyped`.
|
101
110
|
@db = data['mirrors']
|
102
111
|
else
|
103
112
|
# Before config versioning (Braid < 1.1.0)
|
@@ -115,7 +124,7 @@ MSG
|
|
115
124
|
end
|
116
125
|
|
117
126
|
# In all modes, instantiate all mirrors to scan for breaking changes.
|
118
|
-
@breaking_change_descs = []
|
127
|
+
@breaking_change_descs = T.let([], T::Array[String])
|
119
128
|
paths_to_delete = []
|
120
129
|
@db.each do |path, attributes|
|
121
130
|
begin
|
@@ -163,7 +172,7 @@ MSG
|
|
163
172
|
|
164
173
|
end
|
165
174
|
|
166
|
-
sig {params(url: String, options:
|
175
|
+
sig {params(url: String, options: Mirror::Options).returns(Mirror)}
|
167
176
|
def add_from_options(url, options)
|
168
177
|
mirror = Mirror.new_from_options(url, options)
|
169
178
|
|
@@ -171,7 +180,7 @@ MSG
|
|
171
180
|
mirror
|
172
181
|
end
|
173
182
|
|
174
|
-
sig {returns(T::Array[
|
183
|
+
sig {returns(T::Array[String])}
|
175
184
|
def mirrors
|
176
185
|
@db.keys
|
177
186
|
end
|
@@ -213,11 +222,12 @@ MSG
|
|
213
222
|
# Public for upgrade-config command only.
|
214
223
|
sig {void}
|
215
224
|
def write_db
|
216
|
-
new_db = {}
|
225
|
+
new_db = T.let({}, T::Hash[String, Mirror::MirrorAttributes])
|
217
226
|
@db.keys.sort.each do |key|
|
218
|
-
|
227
|
+
attrs = T.must(@db[key])
|
228
|
+
new_db[key] = new_attrs = {}
|
219
229
|
Braid::Mirror::ATTRIBUTES.each do |k|
|
220
|
-
|
230
|
+
new_attrs[k] = attrs[k] if attrs.has_key?(k)
|
221
231
|
end
|
222
232
|
end
|
223
233
|
new_data = {
|
@@ -237,7 +247,7 @@ MSG
|
|
237
247
|
(old_config_files + [config_file]).each do |file|
|
238
248
|
next unless File.exist?(file)
|
239
249
|
begin
|
240
|
-
store = T.
|
250
|
+
store = T.unsafe(YAML::Store).new(file)
|
241
251
|
data = {}
|
242
252
|
store.transaction(true) do
|
243
253
|
store.roots.each do |path|
|
@@ -258,7 +268,7 @@ MSG
|
|
258
268
|
@db[mirror.path] = clean_attributes(mirror.attributes)
|
259
269
|
end
|
260
270
|
|
261
|
-
sig {params(hash:
|
271
|
+
sig {params(hash: Mirror::MirrorAttributes).returns(Mirror::MirrorAttributes)}
|
262
272
|
def clean_attributes(hash)
|
263
273
|
hash.reject { |_, v| v.nil? }
|
264
274
|
end
|
data/lib/braid/main.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# `typed: strict` doesn't seem worth the trouble for this file at this time:
|
2
|
+
# there's not much for us to actually annotate, and it would take a crazy hack
|
3
|
+
# to avoid a Sorbet error on the `@argv` references because Sorbet doesn't seem
|
4
|
+
# to honor `T.bind` for instance variables (TODO: file a Sorbet bug about
|
5
|
+
# that?). Finding an approach to meaningfully check our code that uses the
|
6
|
+
# `main` DSL is a bigger project that we may or may not undertake later.
|
7
|
+
# ~ Matt 2024-08-04
|
8
|
+
#
|
1
9
|
# typed: true
|
2
10
|
|
3
11
|
require 'braid'
|
@@ -31,8 +39,8 @@ T.unsafe(Main).run {
|
|
31
39
|
# The "main" library doesn't provide a way to do this??
|
32
40
|
def check_no_extra_args!
|
33
41
|
if @argv.length > 0
|
34
|
-
|
35
|
-
|
42
|
+
Command.handle_error(
|
43
|
+
BraidError.new('Extra argument(s) passed to command.'))
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
@@ -61,7 +69,7 @@ T.unsafe(Main).run {
|
|
61
69
|
run {
|
62
70
|
check_no_extra_args!
|
63
71
|
Braid.verbose = verbose
|
64
|
-
|
72
|
+
Commands::Add.new(url, Mirror::Options.new(path: local_path, branch: branch, tag: tag, revision: revision, remote_path: path)).run
|
65
73
|
}
|
66
74
|
}
|
67
75
|
|
@@ -86,15 +94,15 @@ T.unsafe(Main).run {
|
|
86
94
|
|
87
95
|
run {
|
88
96
|
check_no_extra_args!
|
89
|
-
options =
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
97
|
+
options = Commands::Update::Options.new(
|
98
|
+
branch: branch,
|
99
|
+
tag: tag,
|
100
|
+
revision: revision,
|
101
|
+
head: head,
|
102
|
+
keep: keep
|
103
|
+
)
|
96
104
|
Braid.verbose = verbose
|
97
|
-
|
105
|
+
Commands::Update.new(local_path, options).run
|
98
106
|
}
|
99
107
|
}
|
100
108
|
|
@@ -115,11 +123,11 @@ T.unsafe(Main).run {
|
|
115
123
|
|
116
124
|
run {
|
117
125
|
check_no_extra_args!
|
118
|
-
options =
|
119
|
-
:
|
120
|
-
|
126
|
+
options = Commands::Remove::Options.new(
|
127
|
+
keep: keep
|
128
|
+
)
|
121
129
|
Braid.verbose = verbose
|
122
|
-
|
130
|
+
Commands::Remove.new(local_path, options).run
|
123
131
|
}
|
124
132
|
}
|
125
133
|
|
@@ -141,12 +149,12 @@ T.unsafe(Main).run {
|
|
141
149
|
if @argv.length > 0 && @argv[0] == '--'
|
142
150
|
@argv.shift
|
143
151
|
end
|
144
|
-
options =
|
145
|
-
|
146
|
-
|
147
|
-
|
152
|
+
options = Commands::Diff::Options.new(
|
153
|
+
keep: keep,
|
154
|
+
git_diff_args: @argv
|
155
|
+
)
|
148
156
|
Braid.verbose = verbose
|
149
|
-
|
157
|
+
Commands::Diff.new(local_path, options).run
|
150
158
|
}
|
151
159
|
}
|
152
160
|
|
@@ -159,12 +167,12 @@ T.unsafe(Main).run {
|
|
159
167
|
|
160
168
|
run {
|
161
169
|
check_no_extra_args!
|
162
|
-
options =
|
163
|
-
|
164
|
-
|
165
|
-
|
170
|
+
options = Commands::Push::Options.new(
|
171
|
+
keep: keep,
|
172
|
+
branch: branch
|
173
|
+
)
|
166
174
|
Braid.verbose = verbose
|
167
|
-
|
175
|
+
Commands::Push.new(local_path, options).run
|
168
176
|
}
|
169
177
|
}
|
170
178
|
|
@@ -179,7 +187,7 @@ T.unsafe(Main).run {
|
|
179
187
|
check_no_extra_args!
|
180
188
|
Braid.verbose = verbose
|
181
189
|
Braid.force = force
|
182
|
-
|
190
|
+
Commands::Setup.new(local_path).run
|
183
191
|
}
|
184
192
|
}
|
185
193
|
|
@@ -188,7 +196,7 @@ T.unsafe(Main).run {
|
|
188
196
|
|
189
197
|
run {
|
190
198
|
check_no_extra_args!
|
191
|
-
puts "braid #{
|
199
|
+
puts "braid #{VERSION}"
|
192
200
|
}
|
193
201
|
}
|
194
202
|
|
@@ -200,7 +208,7 @@ T.unsafe(Main).run {
|
|
200
208
|
run {
|
201
209
|
check_no_extra_args!
|
202
210
|
Braid.verbose = verbose
|
203
|
-
|
211
|
+
Commands::Status.new(local_path).run
|
204
212
|
}
|
205
213
|
}
|
206
214
|
|
@@ -220,6 +228,7 @@ T.unsafe(Main).run {
|
|
220
228
|
optional
|
221
229
|
desc 'Explain the consequences of the upgrade without performing it.'
|
222
230
|
attr :dry_run
|
231
|
+
default false
|
223
232
|
}
|
224
233
|
|
225
234
|
option('allow-breaking-changes') {
|
@@ -228,16 +237,17 @@ T.unsafe(Main).run {
|
|
228
237
|
Perform the upgrade even if it involves breaking changes.
|
229
238
|
DESC
|
230
239
|
attr :allow_breaking_changes
|
240
|
+
default false
|
231
241
|
}
|
232
242
|
|
233
243
|
run {
|
234
244
|
check_no_extra_args!
|
235
|
-
options =
|
236
|
-
|
237
|
-
|
238
|
-
|
245
|
+
options = Commands::UpgradeConfig::Options.new(
|
246
|
+
dry_run: dry_run,
|
247
|
+
allow_breaking_changes: allow_breaking_changes
|
248
|
+
)
|
239
249
|
Braid.verbose = verbose
|
240
|
-
|
250
|
+
Commands::UpgradeConfig.new(options).run
|
241
251
|
}
|
242
252
|
}
|
243
253
|
|
@@ -301,6 +311,7 @@ T.unsafe(Main).run {
|
|
301
311
|
optional
|
302
312
|
desc 'unused option'
|
303
313
|
attr
|
314
|
+
default false
|
304
315
|
}
|
305
316
|
}
|
306
317
|
|
@@ -309,6 +320,7 @@ T.unsafe(Main).run {
|
|
309
320
|
optional
|
310
321
|
desc 'log shell commands'
|
311
322
|
attr
|
323
|
+
default false
|
312
324
|
}
|
313
325
|
}
|
314
326
|
|
@@ -317,6 +329,7 @@ T.unsafe(Main).run {
|
|
317
329
|
optional
|
318
330
|
desc 'force'
|
319
331
|
attr
|
332
|
+
default false
|
320
333
|
}
|
321
334
|
}
|
322
335
|
|
@@ -325,6 +338,7 @@ T.unsafe(Main).run {
|
|
325
338
|
optional
|
326
339
|
desc 'do not remove the remote'
|
327
340
|
attr
|
341
|
+
default false
|
328
342
|
}
|
329
343
|
}
|
330
344
|
|
data/lib/braid/mirror.rb
CHANGED
@@ -14,12 +14,6 @@ module Braid
|
|
14
14
|
"unknown type: #{super}"
|
15
15
|
end
|
16
16
|
end
|
17
|
-
class PathRequired < BraidError
|
18
|
-
sig {returns(String)}
|
19
|
-
def message
|
20
|
-
'path is required'
|
21
|
-
end
|
22
|
-
end
|
23
17
|
class NoTagAndBranch < BraidError
|
24
18
|
sig {returns(String)}
|
25
19
|
def message
|
@@ -32,16 +26,21 @@ module Braid
|
|
32
26
|
sig {returns(String)}
|
33
27
|
attr_reader :path
|
34
28
|
|
35
|
-
# It's going to take significant refactoring to be able to give
|
36
|
-
|
29
|
+
# It's going to take significant refactoring to be able to give
|
30
|
+
# `MirrorAttributes` a type. Encapsulating the `T.untyped` in a type alias
|
31
|
+
# makes it easier to search for all the distinct root causes of untypedness
|
32
|
+
# in the code.
|
33
|
+
MirrorAttributes = T.type_alias { T::Hash[String, T.untyped] }
|
34
|
+
|
35
|
+
sig {returns(MirrorAttributes)}
|
37
36
|
attr_reader :attributes
|
38
37
|
|
39
38
|
BreakingChangeCallback = T.type_alias { T.proc.params(arg0: String).void }
|
40
39
|
|
41
|
-
sig {params(path: String, attributes:
|
40
|
+
sig {params(path: String, attributes: MirrorAttributes, breaking_change_cb: BreakingChangeCallback).void}
|
42
41
|
def initialize(path, attributes = {}, breaking_change_cb = DUMMY_BREAKING_CHANGE_CB)
|
43
42
|
@path = T.let(path.sub(/\/$/, ''), String)
|
44
|
-
@attributes = T.let(attributes.dup,
|
43
|
+
@attributes = T.let(attributes.dup, MirrorAttributes)
|
45
44
|
|
46
45
|
# Not that it's terribly important to check for such an old feature. This
|
47
46
|
# is mainly to demonstrate the RemoveMirrorDueToBreakingChange mechanism
|
@@ -79,19 +78,39 @@ DESC
|
|
79
78
|
@attributes.delete('squashed')
|
80
79
|
end
|
81
80
|
|
82
|
-
|
83
|
-
|
81
|
+
# `Mirror::Options` doubles as the options struct for the `add` command, so
|
82
|
+
# some properties are meaningful only in that context. TODO: Maybe the code
|
83
|
+
# could be organized in a better way.
|
84
|
+
class Options < T::Struct
|
85
|
+
prop :tag, T.nilable(String)
|
86
|
+
prop :branch, T.nilable(String)
|
87
|
+
# NOTE: Used only by the `add` command; ignored by
|
88
|
+
# `Mirror::new_from_options`.
|
89
|
+
prop :revision, T.nilable(Operations::Git::ObjectExpr)
|
90
|
+
prop :path, T.nilable(String)
|
91
|
+
prop :remote_path, T.nilable(String)
|
92
|
+
end
|
93
|
+
|
94
|
+
sig {params(url: String, options: Options).returns(Mirror)}
|
95
|
+
def self.new_from_options(url, options = Options.new)
|
84
96
|
url = url.sub(/\/$/, '')
|
97
|
+
# TODO: Ensure `url` is absolute? The user is probably more likely to
|
98
|
+
# move the downstream repository by itself than to move it along with the
|
99
|
+
# vendor repository. And we definitely don't want to use relative URLs in
|
100
|
+
# the cache.
|
85
101
|
|
86
|
-
raise NoTagAndBranch if options
|
102
|
+
raise NoTagAndBranch if options.tag && options.branch
|
87
103
|
|
88
|
-
tag = options
|
89
|
-
branch = options
|
104
|
+
tag = options.tag
|
105
|
+
branch = options.branch
|
90
106
|
|
91
|
-
path = (options
|
92
|
-
|
107
|
+
path = (options.path || extract_path_from_url(url, options.remote_path)).sub(/\/$/, '')
|
108
|
+
# TODO: Check that `path` is a valid relative path and not something like
|
109
|
+
# '.' or ''. Some of these pathological cases will cause Braid to bail
|
110
|
+
# out later when something else fails, but it would be better to check up
|
111
|
+
# front.
|
93
112
|
|
94
|
-
remote_path = options
|
113
|
+
remote_path = options.remote_path
|
95
114
|
|
96
115
|
attributes = {'url' => url, 'branch' => branch, 'path' => remote_path, 'tag' => tag}
|
97
116
|
self.new(path, attributes)
|
@@ -353,24 +372,16 @@ DESC
|
|
353
372
|
hash
|
354
373
|
end
|
355
374
|
|
356
|
-
|
357
|
-
sig {params(url: String, remote_path: T.nilable(String)).returns(T.nilable(String))}
|
375
|
+
sig {params(url: String, remote_path: T.nilable(String)).returns(String)}
|
358
376
|
def self.extract_path_from_url(url, remote_path)
|
359
377
|
if remote_path
|
360
378
|
return File.basename(remote_path)
|
361
379
|
end
|
362
380
|
|
363
|
-
# Avoid a Sorbet "unreachable code" error.
|
364
|
-
# TODO (typing): Fix this properly. Probably just remove this line?
|
365
|
-
return nil unless T.let(url, T.nilable(String))
|
366
381
|
name = File.basename(url)
|
367
382
|
|
368
|
-
|
369
|
-
|
370
|
-
name[0..-5]
|
371
|
-
else
|
372
|
-
name
|
373
|
-
end
|
383
|
+
# strip .git if present
|
384
|
+
name.delete_suffix('.git')
|
374
385
|
end
|
375
386
|
end
|
376
387
|
end
|
data/lib/braid/operations.rb
CHANGED
@@ -10,26 +10,18 @@ module Braid
|
|
10
10
|
|
11
11
|
module Operations
|
12
12
|
class ShellExecutionError < BraidError
|
13
|
-
|
14
|
-
sig {returns(T.nilable(String))}
|
13
|
+
sig {returns(String)}
|
15
14
|
attr_reader :err, :out
|
16
15
|
|
17
|
-
sig {params(err:
|
18
|
-
def initialize(err
|
16
|
+
sig {params(err: String, out: String).void}
|
17
|
+
def initialize(err, out)
|
19
18
|
@err = err
|
20
19
|
@out = out
|
21
20
|
end
|
22
21
|
|
23
22
|
sig {returns(String)}
|
24
23
|
def message
|
25
|
-
|
26
|
-
# Currently, first_line can be nil if @err was empty, but Sorbet thinks
|
27
|
-
# that the `message` method of an Exception should always return non-nil
|
28
|
-
# (although override checking isn't enforced as of this writing), so
|
29
|
-
# handle nil here. This seems ad-hoc but better than putting in a
|
30
|
-
# `T.must` that we know has a risk of being wrong. Hopefully this will
|
31
|
-
# be fixed better in https://github.com/cristibalan/braid/issues/90.
|
32
|
-
first_line.nil? ? '' : first_line
|
24
|
+
@err
|
33
25
|
end
|
34
26
|
end
|
35
27
|
class VersionTooLow < BraidError
|
@@ -201,7 +193,9 @@ module Braid
|
|
201
193
|
ObjectID = T.type_alias { String }
|
202
194
|
|
203
195
|
# A string containing an expression that can be evaluated to an object ID
|
204
|
-
# by `git rev-parse`.
|
196
|
+
# by `git rev-parse`. This type is enforced even less than `ObjectID` (in
|
197
|
+
# some cases, we apply it directly to user input without validation), but
|
198
|
+
# it still serves to document our intent.
|
205
199
|
ObjectExpr = T.type_alias { String }
|
206
200
|
|
207
201
|
# Get the physical path to a file in the git repository (e.g.,
|
@@ -252,7 +246,7 @@ module Braid
|
|
252
246
|
elsif out.match(/nothing.* to commit/)
|
253
247
|
false
|
254
248
|
else
|
255
|
-
raise ShellExecutionError,
|
249
|
+
raise ShellExecutionError.new(err, out)
|
256
250
|
end
|
257
251
|
end
|
258
252
|
|
@@ -364,7 +358,7 @@ module Braid
|
|
364
358
|
# This can happen if the user runs `braid add` with a `--path` that
|
365
359
|
# doesn't exist. TODO: Make the error message more user-friendly in
|
366
360
|
# that case.
|
367
|
-
raise
|
361
|
+
raise BraidError, 'No tree item exists at the given path'
|
368
362
|
end
|
369
363
|
mode = T.must(m[1])
|
370
364
|
type = T.must(m[2])
|
@@ -374,7 +368,7 @@ module Braid
|
|
374
368
|
elsif type == 'blob'
|
375
369
|
return BlobWithMode.new(hash, mode)
|
376
370
|
else
|
377
|
-
raise
|
371
|
+
raise BraidError, 'Tree item is not a tree or a blob'
|
378
372
|
end
|
379
373
|
end
|
380
374
|
end
|
@@ -22,7 +22,7 @@ module Braid
|
|
22
22
|
).returns(T.type_parameter(:R))
|
23
23
|
}
|
24
24
|
def self.with_modified_environment(dict, &blk)
|
25
|
-
orig_dict = {}
|
25
|
+
orig_dict = T.let({}, T::Hash[String, T.nilable(String)])
|
26
26
|
dict.each { |name, value|
|
27
27
|
orig_dict[name] = ENV[name]
|
28
28
|
ENV[name] = value
|
@@ -74,5 +74,24 @@ module Braid
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
+
class Struct
|
78
|
+
def initialize(**kwargs)
|
79
|
+
# The fake runtime isn't obliged to validate the property names or
|
80
|
+
# types.
|
81
|
+
#
|
82
|
+
# Note: If the caller passed a hash of keyword arguments, Ruby will copy
|
83
|
+
# it, so we don't need to copy `kwargs` again here to avoid aliasing.
|
84
|
+
@attrs = kwargs
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.prop(prop_name, prop_type)
|
88
|
+
define_method(prop_name) {
|
89
|
+
@attrs[prop_name]
|
90
|
+
}
|
91
|
+
define_method(:"#{prop_name}=") { |new_value|
|
92
|
+
@attrs[prop_name] = new_value
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end
|
77
96
|
end
|
78
97
|
end
|
data/lib/braid/version.rb
CHANGED
data/lib/braid.rb
CHANGED
@@ -27,10 +27,7 @@ module Braid
|
|
27
27
|
!!@verbose
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
# apparently `lib/braid/main.rb` passes nil sometimes. Is that easy to fix?
|
32
|
-
# (Ditto with `self.force=` below.)
|
33
|
-
sig {params(new_value: T.nilable(T::Boolean)).void}
|
30
|
+
sig {params(new_value: T::Boolean).void}
|
34
31
|
def self.verbose=(new_value)
|
35
32
|
@verbose = !!new_value
|
36
33
|
end
|
@@ -42,7 +39,7 @@ module Braid
|
|
42
39
|
!!@force
|
43
40
|
end
|
44
41
|
|
45
|
-
sig {params(new_value: T
|
42
|
+
sig {params(new_value: T::Boolean).void}
|
46
43
|
def self.force=(new_value)
|
47
44
|
@force = !!new_value
|
48
45
|
end
|
@@ -59,11 +56,6 @@ module Braid
|
|
59
56
|
|
60
57
|
class BraidError < StandardError
|
61
58
|
extend T::Sig
|
62
|
-
sig {returns(String)}
|
63
|
-
def message
|
64
|
-
value = super
|
65
|
-
value if value != self.class.name
|
66
|
-
end
|
67
59
|
end
|
68
60
|
|
69
61
|
class InternalError < BraidError
|