lib/rbs/collection/sources/git.rb in rbs-2.8.4 vs lib/rbs/collection/sources/git.rb in rbs-3.0.0.dev.1
- old
+ new
@@ -11,68 +11,82 @@
include Base
METADATA_FILENAME = '.rbs_meta.yaml'
class CommandError < StandardError; end
- attr_reader :name, :remote, :repo_dir
+ attr_reader :name, :remote, :repo_dir, :revision
def initialize(name:, revision:, remote:, repo_dir:)
@name = name
@remote = remote
@repo_dir = repo_dir || 'gems'
-
- setup!(revision: revision)
+ @revision = revision
+ @need_setup = true
end
- def has?(config_entry)
- gem_name = config_entry['name']
- gem_repo_dir.join(gem_name).directory?
+ def has?(name, version)
+ setup! do
+ if version
+ (gems_versions[name] || Set[]).include?(version)
+ else
+ gems_versions.key?(name)
+ end
+ end
end
- def versions(config_entry)
- gem_name = config_entry['name']
- gem_repo_dir.join(gem_name).glob('*/').map { |path| path.basename.to_s }
+ def versions(name)
+ setup! do
+ versions = gems_versions[name] or raise "Git source `#{name}` doesn't have `#{name}`"
+ versions.sort
+ end
end
- def install(dest:, config_entry:, stdout:)
- gem_name = config_entry['name']
- version = config_entry['version'] or raise
- gem_dir = dest.join(gem_name, version)
+ def install(dest:, name:, version:, stdout:)
+ setup!()
+ gem_dir = dest.join(name, version)
+
if gem_dir.directory?
- if (prev = YAML.load_file(gem_dir.join(METADATA_FILENAME))) == config_entry
- stdout.puts "Using #{format_config_entry(config_entry)}"
+ prev = load_metadata(dir: gem_dir)
+
+ if prev == metadata_content(name: name, version: version)
+ stdout.puts "Using #{format_config_entry(name, version)}"
else
- # @type var prev: RBS::Collection::Config::gem_entry
- stdout.puts "Updating to #{format_config_entry(config_entry)} from #{format_config_entry(prev)}"
+ stdout.puts "Updating to #{format_config_entry(name, version)} from #{format_config_entry(prev["name"], prev["version"])}"
FileUtils.remove_entry_secure(gem_dir.to_s)
- _install(dest: dest, config_entry: config_entry)
+ _install(dest: dest, name: name, version: version)
end
else
- stdout.puts "Installing #{format_config_entry(config_entry)}"
- _install(dest: dest, config_entry: config_entry)
+ stdout.puts "Installing #{format_config_entry(name, version)}"
+ _install(dest: dest, name: name, version: version)
end
end
- def manifest_of(config_entry)
- gem_name = config_entry['name']
- version = config_entry['version'] or raise
- gem_dir = gem_repo_dir.join(gem_name, version)
-
- manifest_path = gem_dir.join('manifest.yaml')
- YAML.safe_load(manifest_path.read) if manifest_path.exist?
+ def manifest_of(name, version)
+ setup! do
+ path = File.join(repo_dir, name, version, 'manifest.yaml')
+ content = git('cat-file', '-p', "#{resolved_revision}:#{path}")
+ YAML.safe_load(content)
+ rescue CommandError
+ if has?(name, version)
+ nil
+ else
+ raise
+ end
+ end
end
- private def _install(dest:, config_entry:)
- gem_name = config_entry['name']
- version = config_entry['version'] or raise
- dest = dest.join(gem_name, version)
- dest.mkpath
- src = gem_repo_dir.join(gem_name, version)
+ private def _install(dest:, name:, version:)
+ # Should checkout that revision to support symlinks
+ git("reset", "--hard", resolved_revision)
- cp_r(src, dest)
- dest.join(METADATA_FILENAME).write(YAML.dump(config_entry))
+ dir = dest.join(name, version)
+ dir.mkpath
+ src = gem_repo_dir.join(name, version)
+
+ cp_r(src, dir)
+ write_metadata(dir: dir, name: name, version: version)
end
private def cp_r(src, dest)
Find.find(src) do |file_src|
file_src = Pathname(file_src)
@@ -95,44 +109,41 @@
'remote' => remote,
'repo_dir' => repo_dir,
}
end
- private def format_config_entry(config_entry)
- name = config_entry['name']
- v = config_entry['version']
-
+ private def format_config_entry(name, version)
rev = resolved_revision[0..10]
desc = "#{name}@#{rev}"
- "#{name}:#{v} (#{desc})"
+ "#{name}:#{version} (#{desc})"
end
- private def setup!(revision:)
- git_dir.mkpath
- if git_dir.join('.git').directory?
- if need_to_fetch?(revision)
- git 'fetch', 'origin'
+ private def setup!
+ if @need_setup
+ git_dir.mkpath
+ if git_dir.join('.git').directory?
+ if need_to_fetch?(revision)
+ git 'fetch', 'origin'
+ end
+ else
+ begin
+ # git v2.27.0 or greater
+ git 'clone', '--filter=blob:none', remote, git_dir.to_s, chdir: nil
+ rescue CommandError
+ git 'clone', remote, git_dir.to_s, chdir: nil
+ end
end
- else
- begin
- # git v2.27.0 or greater
- git 'clone', '--filter=blob:none', remote, git_dir.to_s, chdir: nil
- rescue CommandError
- git 'clone', remote, git_dir.to_s, chdir: nil
- end
- end
- begin
- git 'checkout', "origin/#{revision}" # for branch name as `revision`
- rescue CommandError
- git 'checkout', revision
+ @need_setup = false
end
+
+ yield if block_given?
end
private def need_to_fetch?(revision)
- return true unless revision.match?(/\A[a-f0-9]{40}\z/)
+ return true unless commit_hash?
begin
git('cat-file', '-e', revision)
false
rescue CommandError
@@ -152,28 +163,90 @@
private def gem_repo_dir
git_dir.join @repo_dir
end
- private def resolved_revision
- @resolved_revision ||= resolve_revision
+ def resolved_revision
+ @resolved_revision ||=
+ begin
+ if commit_hash?
+ revision
+ else
+ setup! { git('rev-parse', revision).chomp }
+ end
+ end
end
- private def resolve_revision
- git('rev-parse', 'HEAD').chomp
+ private def commit_hash?
+ revision.match?(/\A[a-f0-9]{40}\z/)
end
private def git(*cmd, **opt)
sh! 'git', *cmd, **opt
end
+ private def git?(*cmd, **opt)
+ git(*cmd, **opt)
+ rescue CommandError
+ nil
+ end
+
private def sh!(*cmd, **opt)
RBS.logger.debug "$ #{cmd.join(' ')}"
opt = { chdir: git_dir }.merge(opt).compact
(__skip__ = Open3.capture3(*cmd, **opt)).then do |out, err, status|
raise CommandError, "Unexpected status #{status.exitstatus}\n\n#{err}" unless status.success?
out
+ end
+ end
+
+ def metadata_content(name:, version:)
+ {
+ "name" => name,
+ "version" => version,
+ "source" => to_lockfile
+ }
+ end
+
+ def write_metadata(dir:, name:, version:)
+ dir.join(METADATA_FILENAME).write(
+ YAML.dump(
+ metadata_content(name: name, version: version)
+ )
+ )
+ end
+
+ def load_metadata(dir:)
+ # @type var content: Hash[String, untyped]
+ content = YAML.load_file(dir.join(METADATA_FILENAME).to_s)
+ _ = content.slice("name", "version", "source")
+ end
+
+ private def gems_versions
+ @gems_versions ||= begin
+ repo_path = Pathname(repo_dir)
+
+ paths = git('ls-tree', '--full-tree', '-dr', '--name-only', '-z', resolved_revision, File.join(repo_dir, "")).split("\0").map {|line| Pathname(line) }
+
+ # @type var versions: Hash[String, Set[String]]
+ versions = {}
+
+ paths.each do |full_path|
+ path = full_path.relative_path_from(repo_path)
+
+ gem_name, version = path.descend.take(2)
+
+ if gem_name
+ versions[gem_name.to_s] ||= Set[]
+
+ if version
+ versions[gem_name.to_s] << version.basename.to_s
+ end
+ end
+ end
+
+ versions
end
end
end
end
end