braid 1.1.8 → 1.1.10
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 +4 -4
- data/lib/braid/command.rb +39 -15
- data/lib/braid/commands/add.rb +22 -12
- data/lib/braid/commands/diff.rb +27 -10
- data/lib/braid/commands/push.rb +33 -18
- 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 +70 -44
- data/lib/braid/operations.rb +235 -79
- data/lib/braid/operations_lite.rb +1 -1
- data/lib/braid/sorbet/fake_runtime.rb +22 -0
- data/lib/braid/version.rb +1 -1
- data/lib/braid.rb +2 -10
- metadata +46 -4
@@ -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)
|
@@ -107,7 +126,7 @@ DESC
|
|
107
126
|
branch.nil? && tag.nil?
|
108
127
|
end
|
109
128
|
|
110
|
-
sig {params(commit:
|
129
|
+
sig {params(commit: Operations::Git::ObjectExpr).returns(T::Boolean)}
|
111
130
|
def merged?(commit)
|
112
131
|
# tip from spearce in #git:
|
113
132
|
# `test z$(git merge-base A B) = z$(git rev-parse --verify A)`
|
@@ -115,9 +134,7 @@ DESC
|
|
115
134
|
!!base_revision && git.merge_base(commit, base_revision) == commit
|
116
135
|
end
|
117
136
|
|
118
|
-
|
119
|
-
# Braid::Operations::Git::TreeItem.
|
120
|
-
sig {params(revision: String).returns(T.untyped)}
|
137
|
+
sig {params(revision: String).returns(Operations::Git::TreeItem)}
|
121
138
|
def upstream_item_for_revision(revision)
|
122
139
|
git.get_tree_item(revision, self.remote_path)
|
123
140
|
end
|
@@ -211,16 +228,28 @@ DESC
|
|
211
228
|
git.remote_url(remote) == cached_url
|
212
229
|
end
|
213
230
|
|
214
|
-
sig {returns(
|
231
|
+
sig {returns(Operations::Git::ObjectID)}
|
215
232
|
def base_revision
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
|
220
|
-
|
221
|
-
|
233
|
+
# TODO (typing): We think `revision` should always be non-nil here these
|
234
|
+
# days and we can completely drop the `inferred_revision` code, but we're
|
235
|
+
# waiting for a better time to actually make this runtime behavior change
|
236
|
+
# and accept any risk of breakage
|
237
|
+
# (https://github.com/cristibalan/braid/pull/105/files#r857150464).
|
238
|
+
#
|
239
|
+
# Temporary variable
|
240
|
+
# (https://sorbet.org/docs/flow-sensitive#limitations-of-flow-sensitivity)
|
241
|
+
revision1 = revision
|
242
|
+
if revision1
|
243
|
+
git.rev_parse(revision1)
|
222
244
|
else
|
223
|
-
inferred_revision
|
245
|
+
# NOTE: Given that `inferred_revision` does appear to return nil on one
|
246
|
+
# code path, using this `T.must` and giving `base_revision` a
|
247
|
+
# non-nilable return type presents a theoretical risk of leading us to
|
248
|
+
# make changes to callers that break things at runtime. But we judge
|
249
|
+
# this a lesser evil than making the return type nilable and changing
|
250
|
+
# all callers to type-check successfully with that when we hope to
|
251
|
+
# revert the change soon anyway.
|
252
|
+
T.must(inferred_revision)
|
224
253
|
end
|
225
254
|
end
|
226
255
|
|
@@ -310,7 +339,12 @@ DESC
|
|
310
339
|
|
311
340
|
sig {returns(String)}
|
312
341
|
def remote
|
313
|
-
|
342
|
+
# Ensure that we replace any characters in the mirror path that might be
|
343
|
+
# problematic in a Git ref name. Theoretically, this may introduce
|
344
|
+
# collisions between mirrors, but we don't expect that to be much of a
|
345
|
+
# problem because Braid doesn't keep remotes by default after a command
|
346
|
+
# exits.
|
347
|
+
"#{branch || tag || 'revision'}_braid_#{path}".gsub(/[^-A-Za-z0-9]/, '_')
|
314
348
|
end
|
315
349
|
|
316
350
|
private
|
@@ -322,8 +356,8 @@ DESC
|
|
322
356
|
|
323
357
|
sig {returns(T.nilable(String))}
|
324
358
|
def inferred_revision
|
325
|
-
local_commits = git.rev_list('HEAD', "-- #{path}").split("\n")
|
326
|
-
remote_hashes = git.rev_list("--pretty=format
|
359
|
+
local_commits = git.rev_list(['HEAD', "-- #{path}"]).split("\n")
|
360
|
+
remote_hashes = git.rev_list(["--pretty=format:%T", remote]).split('commit ').map do |chunk|
|
327
361
|
chunk.split("\n", 2).map { |value| value.strip }
|
328
362
|
end
|
329
363
|
hash = T.let(nil, T.nilable(String))
|
@@ -338,24 +372,16 @@ DESC
|
|
338
372
|
hash
|
339
373
|
end
|
340
374
|
|
341
|
-
|
342
|
-
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)}
|
343
376
|
def self.extract_path_from_url(url, remote_path)
|
344
377
|
if remote_path
|
345
378
|
return File.basename(remote_path)
|
346
379
|
end
|
347
380
|
|
348
|
-
# Avoid a Sorbet "unreachable code" error.
|
349
|
-
# TODO (typing): Fix this properly. Probably just remove this line?
|
350
|
-
return nil unless T.let(url, T.nilable(String))
|
351
381
|
name = File.basename(url)
|
352
382
|
|
353
|
-
|
354
|
-
|
355
|
-
name[0..-5]
|
356
|
-
else
|
357
|
-
name
|
358
|
-
end
|
383
|
+
# strip .git if present
|
384
|
+
name.delete_suffix('.git')
|
359
385
|
end
|
360
386
|
end
|
361
387
|
end
|