braid 1.1.8 → 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 +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
|