rbs 1.5.1 → 1.6.0
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/.github/dependabot.yml +10 -0
- data/CHANGELOG.md +25 -0
- data/Gemfile +1 -0
- data/Steepfile +9 -1
- data/core/io.rbs +2 -0
- data/docs/collection.md +116 -0
- data/lib/rbs/builtin_names.rb +1 -0
- data/lib/rbs/cli.rb +93 -2
- data/lib/rbs/collection/cleaner.rb +29 -0
- data/lib/rbs/collection/config/lockfile_generator.rb +95 -0
- data/lib/rbs/collection/config.rb +85 -0
- data/lib/rbs/collection/installer.rb +27 -0
- data/lib/rbs/collection/sources/git.rb +147 -0
- data/lib/rbs/collection/sources/rubygems.rb +40 -0
- data/lib/rbs/collection/sources/stdlib.rb +38 -0
- data/lib/rbs/collection/sources.rb +22 -0
- data/lib/rbs/collection.rb +13 -0
- data/lib/rbs/environment_loader.rb +12 -0
- data/lib/rbs/errors.rb +2 -0
- data/lib/rbs/repository.rb +13 -7
- data/lib/rbs/validator.rb +4 -1
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs.rb +1 -0
- data/sig/builtin_names.rbs +1 -0
- data/sig/cli.rbs +5 -0
- data/sig/collection/cleaner.rbs +13 -0
- data/sig/collection/collections.rbs +112 -0
- data/sig/collection/config.rbs +69 -0
- data/sig/collection/installer.rbs +15 -0
- data/sig/collection.rbs +4 -0
- data/sig/environment_loader.rbs +3 -0
- data/sig/polyfill.rbs +12 -3
- data/sig/repository.rbs +4 -0
- data/stdlib/objspace/0/objspace.rbs +406 -0
- data/stdlib/openssl/0/openssl.rbs +1 -1
- data/stdlib/tempfile/0/tempfile.rbs +270 -0
- data/steep/Gemfile.lock +10 -10
- metadata +21 -3
@@ -0,0 +1,85 @@
|
|
1
|
+
module RBS
|
2
|
+
module Collection
|
3
|
+
|
4
|
+
# This class represent the configration file.
|
5
|
+
class Config
|
6
|
+
class CollectionNotAvailable < StandardError
|
7
|
+
def initialize
|
8
|
+
super <<~MSG
|
9
|
+
rbs collection is not initialized.
|
10
|
+
Run `rbs collection install` to install RBSs from collection.
|
11
|
+
MSG
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
PATH = Pathname('rbs_collection.yaml')
|
16
|
+
|
17
|
+
# Generate a rbs lockfile from Gemfile.lock to `config_path`.
|
18
|
+
# If `with_lockfile` is true, it respects existing rbs lockfile.
|
19
|
+
def self.generate_lockfile(config_path:, gemfile_lock_path:, with_lockfile: true)
|
20
|
+
LockfileGenerator.generate(config_path: config_path, gemfile_lock_path: gemfile_lock_path, with_lockfile: with_lockfile)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.from_path(path)
|
24
|
+
new(YAML.load(path.read), config_path: path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.lockfile_of(config_path)
|
28
|
+
lock_path = to_lockfile_path(config_path)
|
29
|
+
from_path lock_path if lock_path.exist?
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.to_lockfile_path(config_path)
|
33
|
+
config_path.sub_ext('.lock' + config_path.extname)
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(data, config_path:)
|
37
|
+
@data = data
|
38
|
+
@config_path = config_path
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_gem(gem)
|
42
|
+
gems << gem
|
43
|
+
end
|
44
|
+
|
45
|
+
def gem(gem_name)
|
46
|
+
gems.find { |gem| gem['name'] == gem_name }
|
47
|
+
end
|
48
|
+
|
49
|
+
def repo_path
|
50
|
+
@config_path.dirname.join @data['path']
|
51
|
+
end
|
52
|
+
|
53
|
+
def sources
|
54
|
+
@sources ||= (
|
55
|
+
@data['sources']
|
56
|
+
.map { |c| Sources.from_config_entry(c) }
|
57
|
+
.push(Sources::Stdlib.instance)
|
58
|
+
.push(Sources::Rubygems.instance)
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def dump_to(io)
|
63
|
+
YAML.dump(@data, io)
|
64
|
+
end
|
65
|
+
|
66
|
+
def gems
|
67
|
+
@data['gems'] ||= []
|
68
|
+
end
|
69
|
+
|
70
|
+
# It raises an error when there are non-available libraries
|
71
|
+
def check_rbs_availability!
|
72
|
+
raise CollectionNotAvailable unless repo_path.exist?
|
73
|
+
|
74
|
+
gems.each do |gem|
|
75
|
+
case gem['source']['type']
|
76
|
+
when 'git'
|
77
|
+
meta_path = repo_path.join(gem['name'], gem['version'], Sources::Git::METADATA_FILENAME)
|
78
|
+
raise CollectionNotAvailable unless meta_path.exist?
|
79
|
+
raise CollectionNotAvailable unless gem == YAML.load(meta_path.read)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RBS
|
2
|
+
module Collection
|
3
|
+
class Installer
|
4
|
+
attr_reader :lockfile
|
5
|
+
attr_reader :stdout
|
6
|
+
|
7
|
+
def initialize(lockfile_path:, stdout: $stdout)
|
8
|
+
@lockfile = Config.from_path(lockfile_path)
|
9
|
+
@stdout = stdout
|
10
|
+
end
|
11
|
+
|
12
|
+
def install_from_lockfile
|
13
|
+
install_to = lockfile.repo_path
|
14
|
+
lockfile.gems.each do |config_entry|
|
15
|
+
source_for(config_entry).install(dest: install_to, config_entry: config_entry, stdout: stdout)
|
16
|
+
end
|
17
|
+
stdout.puts "It's done! #{lockfile.gems.size} gems' RBSs now installed."
|
18
|
+
end
|
19
|
+
|
20
|
+
private def source_for(config_entry)
|
21
|
+
@source_for ||= {}
|
22
|
+
key = config_entry['source'] or raise
|
23
|
+
@source_for[key] ||= Sources.from_config_entry(key)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'digest/sha2'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module RBS
|
5
|
+
module Collection
|
6
|
+
module Sources
|
7
|
+
class Git
|
8
|
+
METADATA_FILENAME = '.rbs_meta.yaml'
|
9
|
+
|
10
|
+
class CommandError < StandardError; end
|
11
|
+
|
12
|
+
attr_reader :name, :remote, :repo_dir
|
13
|
+
|
14
|
+
def initialize(name:, revision:, remote:, repo_dir:)
|
15
|
+
@name = name
|
16
|
+
@remote = remote
|
17
|
+
@repo_dir = repo_dir || 'gems'
|
18
|
+
|
19
|
+
setup!(revision: revision)
|
20
|
+
end
|
21
|
+
|
22
|
+
def has?(config_entry)
|
23
|
+
gem_name = config_entry['name']
|
24
|
+
gem_repo_dir.join(gem_name).directory?
|
25
|
+
end
|
26
|
+
|
27
|
+
def versions(config_entry)
|
28
|
+
gem_name = config_entry['name']
|
29
|
+
gem_repo_dir.join(gem_name).glob('*/').map { |path| path.basename.to_s }
|
30
|
+
end
|
31
|
+
|
32
|
+
def install(dest:, config_entry:, stdout:)
|
33
|
+
gem_name = config_entry['name']
|
34
|
+
version = config_entry['version'] or raise
|
35
|
+
gem_dir = dest.join(gem_name, version)
|
36
|
+
|
37
|
+
if gem_dir.directory?
|
38
|
+
if (prev = YAML.load_file(gem_dir.join(METADATA_FILENAME))) == config_entry
|
39
|
+
stdout.puts "Using #{format_config_entry(config_entry)}"
|
40
|
+
else
|
41
|
+
# @type var prev: RBS::Collection::Config::gem_entry
|
42
|
+
stdout.puts "Updating to #{format_config_entry(config_entry)} from #{format_config_entry(prev)}"
|
43
|
+
FileUtils.remove_entry_secure(gem_dir.to_s)
|
44
|
+
_install(dest: dest, config_entry: config_entry)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
stdout.puts "Installing #{format_config_entry(config_entry)}"
|
48
|
+
_install(dest: dest, config_entry: config_entry)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private def _install(dest:, config_entry:)
|
53
|
+
gem_name = config_entry['name']
|
54
|
+
version = config_entry['version'] or raise
|
55
|
+
dest = dest.join(gem_name)
|
56
|
+
dest.mkpath
|
57
|
+
src = gem_repo_dir.join(gem_name, version)
|
58
|
+
|
59
|
+
FileUtils.cp_r(src, dest)
|
60
|
+
dest.join(version, METADATA_FILENAME).write(YAML.dump(config_entry))
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_lockfile
|
64
|
+
{
|
65
|
+
'type' => 'git',
|
66
|
+
'name' => name,
|
67
|
+
'revision' => resolved_revision,
|
68
|
+
'remote' => remote,
|
69
|
+
'repo_dir' => repo_dir,
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
private def format_config_entry(config_entry)
|
74
|
+
name = config_entry['name']
|
75
|
+
v = config_entry['version']
|
76
|
+
|
77
|
+
rev = resolved_revision[0..10]
|
78
|
+
desc = "#{name}@#{rev}"
|
79
|
+
|
80
|
+
"#{name}:#{v} (#{desc})"
|
81
|
+
end
|
82
|
+
|
83
|
+
private def setup!(revision:)
|
84
|
+
git_dir.mkpath
|
85
|
+
if git_dir.join('.git').directory?
|
86
|
+
if need_to_fetch?(revision)
|
87
|
+
git 'fetch', 'origin'
|
88
|
+
end
|
89
|
+
else
|
90
|
+
git 'clone', remote, git_dir.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
begin
|
94
|
+
git 'checkout', "origin/#{revision}" # for branch name as `revision`
|
95
|
+
rescue CommandError
|
96
|
+
git 'checkout', revision
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private def need_to_fetch?(revision)
|
101
|
+
return true unless revision.match?(/\A[a-f0-9]{40}\z/)
|
102
|
+
|
103
|
+
begin
|
104
|
+
git('cat-file', '-e', revision)
|
105
|
+
false
|
106
|
+
rescue CommandError
|
107
|
+
true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private def git_dir
|
112
|
+
@git_dir ||= (
|
113
|
+
base = Pathname(ENV['XDG_CACHE_HOME'] || File.expand_path("~/.cache"))
|
114
|
+
dir = base.join('rbs', Digest::SHA256.hexdigest(remote))
|
115
|
+
dir.mkpath
|
116
|
+
dir
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
private def gem_repo_dir
|
121
|
+
git_dir.join @repo_dir
|
122
|
+
end
|
123
|
+
|
124
|
+
private def resolved_revision
|
125
|
+
@resolved_revision ||= resolve_revision
|
126
|
+
end
|
127
|
+
|
128
|
+
private def resolve_revision
|
129
|
+
git('rev-parse', 'HEAD').chomp
|
130
|
+
end
|
131
|
+
|
132
|
+
private def git(*cmd)
|
133
|
+
sh! 'git', *cmd
|
134
|
+
end
|
135
|
+
|
136
|
+
private def sh!(*cmd)
|
137
|
+
RBS.logger.debug "$ #{cmd.join(' ')}"
|
138
|
+
(__skip__ = Open3.capture3(*cmd, chdir: git_dir)).then do |out, err, status|
|
139
|
+
raise CommandError, "Unexpected status #{status.exitstatus}\n\n#{err}" unless status.success?
|
140
|
+
|
141
|
+
out
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module RBS
|
4
|
+
module Collection
|
5
|
+
module Sources
|
6
|
+
# Signatures that are inclduded in gem package as sig/ directory.
|
7
|
+
class Rubygems
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def has?(config_entry)
|
11
|
+
gem_sig_path(config_entry)
|
12
|
+
end
|
13
|
+
|
14
|
+
def versions(config_entry)
|
15
|
+
spec, _ = gem_sig_path(config_entry)
|
16
|
+
spec or raise
|
17
|
+
[spec.version.to_s]
|
18
|
+
end
|
19
|
+
|
20
|
+
def install(dest:, config_entry:, stdout:)
|
21
|
+
# Do nothing because stdlib RBS is available by default
|
22
|
+
name = config_entry['name']
|
23
|
+
version = config_entry['version'] or raise
|
24
|
+
_, from = gem_sig_path(config_entry)
|
25
|
+
stdout.puts "Using #{name}:#{version} (#{from})"
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_lockfile
|
29
|
+
{
|
30
|
+
'type' => 'rubygems',
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
private def gem_sig_path(config_entry)
|
35
|
+
RBS::EnvironmentLoader.gem_sig_path(config_entry['name'], config_entry['version'])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module RBS
|
4
|
+
module Collection
|
5
|
+
module Sources
|
6
|
+
# signatures that are bundled in rbs gem under the stdlib/ directory
|
7
|
+
class Stdlib
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def has?(config_entry)
|
11
|
+
gem_dir(config_entry).exist?
|
12
|
+
end
|
13
|
+
|
14
|
+
def versions(config_entry)
|
15
|
+
gem_dir(config_entry).glob('*/').map { |path| path.basename.to_s }
|
16
|
+
end
|
17
|
+
|
18
|
+
def install(dest:, config_entry:, stdout:)
|
19
|
+
# Do nothing because stdlib RBS is available by default
|
20
|
+
name = config_entry['name']
|
21
|
+
version = config_entry['version'] or raise
|
22
|
+
from = gem_dir(config_entry) / version
|
23
|
+
stdout.puts "Using #{name}:#{version} (#{from})"
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_lockfile
|
27
|
+
{
|
28
|
+
'type' => 'stdlib',
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
private def gem_dir(config_entry)
|
33
|
+
Repository::DEFAULT_STDLIB_ROOT.join(config_entry['name'])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative './sources/git'
|
2
|
+
require_relative './sources/stdlib'
|
3
|
+
require_relative './sources/rubygems'
|
4
|
+
|
5
|
+
module RBS
|
6
|
+
module Collection
|
7
|
+
module Sources
|
8
|
+
def self.from_config_entry(source_entry)
|
9
|
+
case source_entry['type']
|
10
|
+
when 'git', nil # git source by default
|
11
|
+
__skip__ = Git.new(**source_entry.slice('name', 'revision', 'remote', 'repo_dir').transform_keys(&:to_sym))
|
12
|
+
when 'stdlib'
|
13
|
+
Stdlib.instance
|
14
|
+
when 'rubygems'
|
15
|
+
Rubygems.instance
|
16
|
+
else
|
17
|
+
raise
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
require_relative './collection/sources'
|
5
|
+
require_relative './collection/config'
|
6
|
+
require_relative './collection/config/lockfile_generator'
|
7
|
+
require_relative './collection/installer'
|
8
|
+
require_relative './collection/cleaner'
|
9
|
+
|
10
|
+
module RBS
|
11
|
+
module Collection
|
12
|
+
end
|
13
|
+
end
|
@@ -47,6 +47,18 @@ module RBS
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
def add_collection(collection_config)
|
51
|
+
warn "warning: rbs collection is experimental, and the behavior may change until RBS v2.0"
|
52
|
+
|
53
|
+
collection_config.check_rbs_availability!
|
54
|
+
|
55
|
+
repository.add(collection_config.repo_path)
|
56
|
+
|
57
|
+
collection_config.gems.each do |gem|
|
58
|
+
add(library: gem['name'], version: gem['version'])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
50
62
|
def has_library?(library:, version:)
|
51
63
|
if self.class.gem_sig_path(library, version) || repository.lookup(library, version)
|
52
64
|
true
|
data/lib/rbs/errors.rb
CHANGED
data/lib/rbs/repository.rb
CHANGED
@@ -55,13 +55,8 @@ module RBS
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def find_best_version(version)
|
58
|
-
|
59
|
-
|
60
|
-
if v = version_names.reverse.bsearch {|v| v <= version ? true : false }
|
61
|
-
versions[v]
|
62
|
-
else
|
63
|
-
oldest_version
|
64
|
-
end
|
58
|
+
best_version = Repository.find_best_version(version, version_names)
|
59
|
+
versions[best_version]
|
65
60
|
end
|
66
61
|
|
67
62
|
def empty?
|
@@ -89,6 +84,17 @@ module RBS
|
|
89
84
|
end
|
90
85
|
end
|
91
86
|
|
87
|
+
def self.find_best_version(version, candidates)
|
88
|
+
candidates = candidates.sort
|
89
|
+
return candidates.last || raise unless version
|
90
|
+
|
91
|
+
if v = candidates.reverse.bsearch {|v| v <= version ? true : false }
|
92
|
+
v
|
93
|
+
else
|
94
|
+
candidates.first or raise
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
92
98
|
def add(dir)
|
93
99
|
dirs << dir
|
94
100
|
|