rbs 1.4.0 → 1.6.1
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 +52 -0
- data/Gemfile +2 -0
- data/Steepfile +9 -1
- data/core/builtin.rbs +1 -1
- data/core/file.rbs +3 -1
- data/core/global_variables.rbs +3 -3
- data/core/io/wait.rbs +37 -0
- data/core/io.rbs +6 -4
- data/core/ractor.rbs +779 -0
- data/core/string_io.rbs +3 -5
- 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/digest/0/digest.rbs +418 -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 +24 -3
@@ -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
|
|
data/lib/rbs/validator.rb
CHANGED
@@ -56,7 +56,10 @@ module RBS
|
|
56
56
|
|
57
57
|
def validate_type_alias(entry:)
|
58
58
|
@type_alias_dependency ||= TypeAliasDependency.new(env: env)
|
59
|
-
|
59
|
+
if @type_alias_dependency.circular_definition?(entry.decl.name)
|
60
|
+
location = entry.decl.location or raise
|
61
|
+
raise RecursiveTypeAliasError.new(alias_names: [entry.decl.name], location: location)
|
62
|
+
end
|
60
63
|
end
|
61
64
|
end
|
62
65
|
end
|
data/lib/rbs/version.rb
CHANGED
data/lib/rbs.rb
CHANGED
data/sig/builtin_names.rbs
CHANGED
data/sig/cli.rbs
CHANGED
@@ -2,6 +2,7 @@ module RBS
|
|
2
2
|
class CLI
|
3
3
|
class LibraryOptions
|
4
4
|
attr_accessor core_root: Pathname?
|
5
|
+
attr_accessor config_path: Pathname?
|
5
6
|
|
6
7
|
attr_reader libs: Array[String]
|
7
8
|
attr_reader dirs: Array[String]
|
@@ -63,6 +64,10 @@ module RBS
|
|
63
64
|
|
64
65
|
def run_test: (Array[String], LibraryOptions) -> void
|
65
66
|
|
67
|
+
def run_collection: (Array[String], LibraryOptions) -> void
|
68
|
+
|
66
69
|
def test_opt: (LibraryOptions) -> String?
|
70
|
+
|
71
|
+
def collection_options: (Array[String]) -> OptionParser
|
67
72
|
end
|
68
73
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module RBS
|
2
|
+
module Collection
|
3
|
+
module Sources
|
4
|
+
def self.from_config_entry: (source_entry) -> _Source
|
5
|
+
|
6
|
+
interface _Source
|
7
|
+
def has?: (Config::gem_entry) -> boolish
|
8
|
+
def versions: (Config::gem_entry) -> Array[String]
|
9
|
+
def install: (dest: Pathname, config_entry: Config::gem_entry, stdout: CLI::_IO) -> void
|
10
|
+
def to_lockfile: () -> source_entry
|
11
|
+
end
|
12
|
+
|
13
|
+
type source_entry = Git::source_entry
|
14
|
+
| Stdlib::source_entry
|
15
|
+
| Rubygems::source_entry
|
16
|
+
|
17
|
+
class Git
|
18
|
+
METADATA_FILENAME: String
|
19
|
+
|
20
|
+
type source_entry = {
|
21
|
+
'type' => 'git',
|
22
|
+
'name' => String,
|
23
|
+
'remote' => String,
|
24
|
+
'revision' => String,
|
25
|
+
'repo_dir' => String?,
|
26
|
+
}
|
27
|
+
|
28
|
+
class CommandError < StandardError
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader name: String
|
32
|
+
attr_reader remote: String
|
33
|
+
attr_reader repo_dir: String
|
34
|
+
|
35
|
+
def initialize: (name: String, revision: String, remote: String, repo_dir: String?) -> untyped
|
36
|
+
|
37
|
+
def has?: (Config::gem_entry) -> bool
|
38
|
+
|
39
|
+
def versions: (Config::gem_entry) -> Array[String]
|
40
|
+
|
41
|
+
def install: (dest: Pathname, config_entry: Config::gem_entry, stdout: CLI::_IO) -> void
|
42
|
+
|
43
|
+
def to_lockfile: () -> source_entry
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def _install: (dest: Pathname , config_entry: Config::gem_entry) -> void
|
48
|
+
|
49
|
+
def setup!: (revision: String) -> void
|
50
|
+
|
51
|
+
def need_to_fetch?: (String revision ) -> bool
|
52
|
+
|
53
|
+
def git_dir: () -> Pathname
|
54
|
+
|
55
|
+
def gem_repo_dir: () -> Pathname
|
56
|
+
|
57
|
+
def with_revision: [T] () { () -> T } -> T
|
58
|
+
|
59
|
+
def resolved_revision: () -> String
|
60
|
+
|
61
|
+
def resolve_revision: () -> String
|
62
|
+
|
63
|
+
def git: (*String cmd) -> String
|
64
|
+
|
65
|
+
def sh!: (*String cmd) -> String
|
66
|
+
|
67
|
+
def format_config_entry: (Config::gem_entry) -> String
|
68
|
+
end
|
69
|
+
|
70
|
+
# signatures that are bundled in rbs gem under the stdlib/ directory
|
71
|
+
class Stdlib
|
72
|
+
type source_entry = {
|
73
|
+
'type' => 'stdlib',
|
74
|
+
}
|
75
|
+
|
76
|
+
# polyfill of singleton module
|
77
|
+
def self.instance: () -> instance
|
78
|
+
|
79
|
+
def has?: (Config::gem_entry) -> bool
|
80
|
+
|
81
|
+
def versions: (Config::gem_entry) -> Array[String]
|
82
|
+
|
83
|
+
def install: (dest: Pathname, config_entry: Config::gem_entry, stdout: CLI::_IO) -> void
|
84
|
+
|
85
|
+
def to_lockfile: () -> source_entry
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def gem_dir: (Config::gem_entry) -> Pathname
|
90
|
+
end
|
91
|
+
|
92
|
+
# sig/ directory
|
93
|
+
class Rubygems
|
94
|
+
type source_entry = {
|
95
|
+
'type' => 'rubygems',
|
96
|
+
}
|
97
|
+
|
98
|
+
# polyfill of singleton module
|
99
|
+
def self.instance: () -> instance
|
100
|
+
|
101
|
+
def has?: (Config::gem_entry) -> boolish
|
102
|
+
def versions: (Config::gem_entry) -> Array[String]
|
103
|
+
def install: (dest: Pathname, config_entry: Config::gem_entry, stdout: CLI::_IO) -> void
|
104
|
+
def to_lockfile: () -> source_entry
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def gem_sig_path: (Config::gem_entry) -> [Gem::Specification, Pathname]?
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|