lib/braid/mirror.rb in braid-1.1.9 vs lib/braid/mirror.rb in braid-1.1.10

- old
+ new

@@ -12,16 +12,10 @@ sig {returns(String)} def message "unknown type: #{super}" end end - class PathRequired < BraidError - sig {returns(String)} - def message - 'path is required' - end - end class NoTagAndBranch < BraidError sig {returns(String)} def message 'can not specify both tag and branch configuration' end @@ -30,20 +24,25 @@ include Operations::VersionControl sig {returns(String)} attr_reader :path - # It's going to take significant refactoring to be able to give this a type. - sig {returns(T::Hash[String, T.untyped])} + # It's going to take significant refactoring to be able to give + # `MirrorAttributes` a type. Encapsulating the `T.untyped` in a type alias + # makes it easier to search for all the distinct root causes of untypedness + # in the code. + MirrorAttributes = T.type_alias { T::Hash[String, T.untyped] } + + sig {returns(MirrorAttributes)} attr_reader :attributes BreakingChangeCallback = T.type_alias { T.proc.params(arg0: String).void } - sig {params(path: String, attributes: T::Hash[String, T.untyped], breaking_change_cb: BreakingChangeCallback).void} + sig {params(path: String, attributes: MirrorAttributes, breaking_change_cb: BreakingChangeCallback).void} def initialize(path, attributes = {}, breaking_change_cb = DUMMY_BREAKING_CHANGE_CB) @path = T.let(path.sub(/\/$/, ''), String) - @attributes = T.let(attributes.dup, T::Hash[String, T.untyped]) + @attributes = T.let(attributes.dup, MirrorAttributes) # Not that it's terribly important to check for such an old feature. This # is mainly to demonstrate the RemoveMirrorDueToBreakingChange mechanism # in case we want to use it for something else in the future. if !@attributes['type'].nil? && @attributes['type'] != 'git' @@ -77,23 +76,43 @@ DESC end @attributes.delete('squashed') end - sig {params(url: String, options: T.untyped).returns(Mirror)} - def self.new_from_options(url, options = {}) + # `Mirror::Options` doubles as the options struct for the `add` command, so + # some properties are meaningful only in that context. TODO: Maybe the code + # could be organized in a better way. + class Options < T::Struct + prop :tag, T.nilable(String) + prop :branch, T.nilable(String) + # NOTE: Used only by the `add` command; ignored by + # `Mirror::new_from_options`. + prop :revision, T.nilable(Operations::Git::ObjectExpr) + prop :path, T.nilable(String) + prop :remote_path, T.nilable(String) + end + + sig {params(url: String, options: Options).returns(Mirror)} + def self.new_from_options(url, options = Options.new) url = url.sub(/\/$/, '') + # TODO: Ensure `url` is absolute? The user is probably more likely to + # move the downstream repository by itself than to move it along with the + # vendor repository. And we definitely don't want to use relative URLs in + # the cache. - raise NoTagAndBranch if options['tag'] && options['branch'] + raise NoTagAndBranch if options.tag && options.branch - tag = options['tag'] - branch = options['branch'] + tag = options.tag + branch = options.branch - path = (options['path'] || extract_path_from_url(url, options['remote_path'])).sub(/\/$/, '') - raise PathRequired unless path + path = (options.path || extract_path_from_url(url, options.remote_path)).sub(/\/$/, '') + # TODO: Check that `path` is a valid relative path and not something like + # '.' or ''. Some of these pathological cases will cause Braid to bail + # out later when something else fails, but it would be better to check up + # front. - remote_path = options['remote_path'] + remote_path = options.remote_path attributes = {'url' => url, 'branch' => branch, 'path' => remote_path, 'tag' => tag} self.new(path, attributes) end @@ -351,26 +370,18 @@ end end hash end - # TODO (typing): Return should not be nilable - sig {params(url: String, remote_path: T.nilable(String)).returns(T.nilable(String))} + sig {params(url: String, remote_path: T.nilable(String)).returns(String)} def self.extract_path_from_url(url, remote_path) if remote_path return File.basename(remote_path) end - # Avoid a Sorbet "unreachable code" error. - # TODO (typing): Fix this properly. Probably just remove this line? - return nil unless T.let(url, T.nilable(String)) name = File.basename(url) - if File.extname(name) == '.git' - # strip .git - name[0..-5] - else - name - end + # strip .git if present + name.delete_suffix('.git') end end end