rbs 4.1.0.pre.2-java → 4.1.1.dev.1-java
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/.dockerignore +37 -0
- data/.github/dependabot.yml +1 -1
- data/.github/workflows/bundle-update.yml +2 -2
- data/.github/workflows/c-check.yml +11 -6
- data/.github/workflows/comments.yml +2 -2
- data/.github/workflows/dependabot.yml +1 -1
- data/.github/workflows/jruby.yml +15 -3
- data/.github/workflows/release-gems.yml +164 -0
- data/.github/workflows/ruby.yml +4 -4
- data/.github/workflows/rust.yml +10 -8
- data/.github/workflows/truffleruby.yml +1 -1
- data/.github/workflows/typecheck.yml +2 -2
- data/.github/workflows/wasm.yml +4 -2
- data/.github/workflows/windows.yml +2 -2
- data/.gitignore +3 -2
- data/CHANGELOG.md +88 -0
- data/Dockerfile.jruby +53 -0
- data/Rakefile +340 -60
- data/Steepfile +2 -0
- data/config.yml +2 -0
- data/core/array.rbs +228 -165
- data/core/float.rbs +0 -24
- data/core/match_data.rbs +1 -1
- data/core/pathname.rbs +0 -10
- data/core/ractor.rbs +0 -10
- data/core/rubygems/errors.rbs +4 -1
- data/core/rubygems/requirement.rbs +0 -10
- data/core/rubygems/rubygems.rbs +4 -1
- data/core/rubygems/specification.rbs +8 -0
- data/core/rubygems/version.rbs +0 -160
- data/core/thread.rbs +3 -8
- data/docs/CONTRIBUTING.md +1 -1
- data/docs/release.md +151 -0
- data/ext/rbs_extension/ast_translation.c +2 -2
- data/ext/rbs_extension/legacy_location.c +11 -6
- data/include/rbs/ast.h +4 -4
- data/lib/rbs/parser_aux.rb +4 -2
- data/lib/rbs/prototype/rbi.rb +193 -25
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/wasm/rbs_parser.wasm +0 -0
- data/lib/rbs/wasm/runtime.rb +7 -28
- data/lib/rbs_jars.rb +39 -0
- data/lib/rdoc_plugin/parser.rb +5 -0
- data/rbs.gemspec +16 -3
- data/sig/prototype/rbi.rbs +33 -4
- data/src/ast.c +2 -2
- data/src/lexer.c +97 -93
- data/src/lexer.re +1 -1
- data/src/lexstate.c +6 -2
- data/src/parser.c +6 -3
- data/src/util/rbs_allocator.c +13 -4
- data/stdlib/delegate/0/delegator.rbs +2 -1
- data/stdlib/digest/0/digest.rbs +10 -4
- data/stdlib/erb/0/erb.rbs +1 -1
- data/stdlib/ipaddr/0/ipaddr.rbs +0 -5
- data/stdlib/monitor/0/monitor.rbs +2 -2
- data/stdlib/openssl/0/openssl.rbs +39 -33
- data/stdlib/tempfile/0/manifest.yaml +3 -0
- data/stdlib/timeout/0/timeout.rbs +0 -5
- data/stdlib/uri/0/generic.rbs +0 -5
- data/stdlib/zlib/0/zstream.rbs +0 -1
- data/wasm/README.md +4 -3
- data/wasm/rbs_wasm.c +12 -0
- metadata +25 -14
- data/.github/workflows/milestone.yml +0 -83
- data/lib/rbs/wasm/jars/asm-analysis.jar +0 -0
- data/lib/rbs/wasm/jars/asm-commons.jar +0 -0
- data/lib/rbs/wasm/jars/asm-tree.jar +0 -0
- data/lib/rbs/wasm/jars/asm-util.jar +0 -0
- data/lib/rbs/wasm/jars/asm.jar +0 -0
- data/lib/rbs/wasm/jars/compiler.jar +0 -0
- data/lib/rbs/wasm/jars/log.jar +0 -0
- data/lib/rbs/wasm/jars/runtime.jar +0 -0
- data/lib/rbs/wasm/jars/wasi.jar +0 -0
- data/lib/rbs/wasm/jars/wasm.jar +0 -0
data/Dockerfile.jruby
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# JRuby verification image for RBS.
|
|
2
|
+
#
|
|
3
|
+
# RBS can't load its MRI C extension on JRuby, so it parses through a
|
|
4
|
+
# WebAssembly build of the parser (see lib/rbs/wasm and docs/wasm_serialization.md).
|
|
5
|
+
# This single-stage image installs the WASI SDK, compiles rbs_parser.wasm and
|
|
6
|
+
# downloads the Chicory/ASM jars into ~/.m2 (via jar-dependencies), then runs the
|
|
7
|
+
# test suite on JRuby. It mirrors .github/workflows/jruby.yml but is self-contained.
|
|
8
|
+
#
|
|
9
|
+
# docker build -f Dockerfile.jruby -t rbs-jruby .
|
|
10
|
+
# docker run --rm rbs-jruby # run the test suite
|
|
11
|
+
# docker run --rm -e RBS_PLATFORM=java rbs-jruby \
|
|
12
|
+
# gem build rbs.gemspec # build the -java gem
|
|
13
|
+
#
|
|
14
|
+
# Bundler is intentionally not used: the development Gemfile pulls in CRuby-only
|
|
15
|
+
# C extensions (bigdecimal, stackprof, ...) that cannot build on JRuby. The few
|
|
16
|
+
# gems the suite needs are installed directly, exactly as the CI does.
|
|
17
|
+
|
|
18
|
+
FROM jruby:10.1.1.0-jdk21
|
|
19
|
+
|
|
20
|
+
# Keep in sync with .github/workflows/wasm.yml and .github/workflows/jruby.yml.
|
|
21
|
+
ARG WASI_SDK_VERSION=33
|
|
22
|
+
ARG WASI_SDK_RELEASE=33.0
|
|
23
|
+
|
|
24
|
+
# build-essential supplies cc/make: on JRuby the prism gem builds libprism.so
|
|
25
|
+
# (loaded via FFI) instead of an MRI C extension, so it needs a C toolchain.
|
|
26
|
+
RUN apt-get update \
|
|
27
|
+
&& apt-get install -y --no-install-recommends git curl ca-certificates build-essential \
|
|
28
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
29
|
+
|
|
30
|
+
# The WASI SDK provides clang, the wasi-libc sysroot and the wasm32 compiler-rt
|
|
31
|
+
# builtins that `rake wasm:build` needs to compile src/**/*.c to WebAssembly.
|
|
32
|
+
RUN set -eux; \
|
|
33
|
+
case "$(uname -m)" in \
|
|
34
|
+
x86_64 | amd64) wasi_arch=x86_64 ;; \
|
|
35
|
+
aarch64 | arm64) wasi_arch=arm64 ;; \
|
|
36
|
+
*) echo "unsupported arch: $(uname -m)" >&2; exit 1 ;; \
|
|
37
|
+
esac; \
|
|
38
|
+
mkdir -p /opt/wasi-sdk; \
|
|
39
|
+
curl -fsSL "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_RELEASE}-${wasi_arch}-linux.tar.gz" \
|
|
40
|
+
| tar xz --strip-components=1 -C /opt/wasi-sdk
|
|
41
|
+
ENV WASI_SDK_PATH=/opt/wasi-sdk
|
|
42
|
+
|
|
43
|
+
# Runtime/test gems the suite needs on JRuby (same set as the jruby.yml CI).
|
|
44
|
+
RUN gem install prism rake rake-compiler test-unit rdoc rspec minitest json-schema pry --no-document
|
|
45
|
+
|
|
46
|
+
WORKDIR /rbs
|
|
47
|
+
COPY . .
|
|
48
|
+
|
|
49
|
+
# Compile rbs_parser.wasm (clang is a subprocess, so the build is engine
|
|
50
|
+
# independent) and download the Chicory + ASM jars into ~/.m2 via jar-dependencies.
|
|
51
|
+
RUN rake wasm:jruby_setup wasm:install_jars
|
|
52
|
+
|
|
53
|
+
CMD ["rake", "test"]
|
data/Rakefile
CHANGED
|
@@ -55,8 +55,15 @@ task :confirm_lexer => :lexer do
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
task :confirm_templates => :templates do
|
|
58
|
-
puts "Testing if generated code
|
|
59
|
-
|
|
58
|
+
puts "Testing if generated code is updated with respect to templates"
|
|
59
|
+
|
|
60
|
+
# Every template generates the file it is named after: `templates/<path>.erb` generates `<path>`.
|
|
61
|
+
generated = Dir.glob("templates/**/*.erb").sort.map { _1.delete_prefix("templates/").delete_suffix(".erb") }
|
|
62
|
+
|
|
63
|
+
missing = generated.reject { File.exist?(_1) }
|
|
64
|
+
raise "Templates without a generated file: #{missing.join(", ")}. Is the `templates` task missing an entry?" unless missing.empty?
|
|
65
|
+
|
|
66
|
+
sh "git diff --exit-code -- #{generated.join(" ")}"
|
|
60
67
|
end
|
|
61
68
|
|
|
62
69
|
# Task to format C code using clang-format
|
|
@@ -147,6 +154,9 @@ end
|
|
|
147
154
|
rule %r{^include/(.*)\.c} => 'templates/%X.c.erb' do |t|
|
|
148
155
|
puts "⚠️⚠️⚠️ #{t.name} is older than #{t.source}. You may need to run `rake templates` ⚠️⚠️⚠️"
|
|
149
156
|
end
|
|
157
|
+
rule %r{^ext/(.*)\.c} => 'templates/%X.c.erb' do |t|
|
|
158
|
+
puts "⚠️⚠️⚠️ #{t.name} is older than #{t.source}. You may need to run `rake templates` ⚠️⚠️⚠️"
|
|
159
|
+
end
|
|
150
160
|
|
|
151
161
|
task :annotate do
|
|
152
162
|
sh "bin/generate_docs.sh"
|
|
@@ -495,46 +505,329 @@ NOTES
|
|
|
495
505
|
end
|
|
496
506
|
|
|
497
507
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
major, minor, patch, _pre = RBS::VERSION.split(".", 4)
|
|
501
|
-
major = major.to_i
|
|
502
|
-
minor = minor.to_i
|
|
503
|
-
patch = patch.to_i
|
|
508
|
+
# Pull requests with one of these labels are omitted from the changelog.
|
|
509
|
+
CHANGELOG_SKIP_LABELS = ["skip-changelog"]
|
|
504
510
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
511
|
+
# Resolves the commit-ish the changelog starts from.
|
|
512
|
+
#
|
|
513
|
+
# `version` is a version number, a tag, or any commit-ish. When it is omitted, the latest tag
|
|
514
|
+
# matching `tag_glob` is used, skipping the ones matching `exclude_globs`.
|
|
515
|
+
#
|
|
516
|
+
def resolve_changelog_base(version, tag_glob:, exclude_globs: [])
|
|
517
|
+
require "open3"
|
|
518
|
+
|
|
519
|
+
from =
|
|
520
|
+
if version
|
|
521
|
+
# `4.1.0` and `v4.1.0` both mean the tag `v4.1.0`, while `master` or a SHA is used as is.
|
|
522
|
+
version.match?(/\A\d/) ? "v#{version}" : version
|
|
523
|
+
else
|
|
524
|
+
command = ["git", "describe", "--tags", "--match", tag_glob, "--abbrev=0"]
|
|
525
|
+
exclude_globs.each { |glob| command.push("--exclude", glob) }
|
|
526
|
+
|
|
527
|
+
output, status = Open3.capture2(*command)
|
|
528
|
+
raise "🚨 Cannot detect the latest tag matching `#{tag_glob}`. Give the previous version explicitly." unless status.success?
|
|
529
|
+
output.chomp
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
_, status = Open3.capture2("git", "rev-parse", "--verify", "--quiet", "#{from}^{commit}")
|
|
533
|
+
raise "🚨 No such commit-ish: `#{from}`" unless status.success?
|
|
510
534
|
|
|
511
|
-
|
|
535
|
+
from
|
|
536
|
+
end
|
|
512
537
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
"milestone:\"#{milestone}\" is:merged sort:updated-desc -label:Released"
|
|
522
|
-
]
|
|
538
|
+
# Runs a GraphQL query against the repository of the working directory.
|
|
539
|
+
#
|
|
540
|
+
# `body` is the selection set inside `repository`, so a query can use the `$owner` and `$name`
|
|
541
|
+
# variables. Returns the contents of `data.repository`.
|
|
542
|
+
#
|
|
543
|
+
def changelog_graphql(body)
|
|
544
|
+
require "open3"
|
|
545
|
+
require "json"
|
|
523
546
|
|
|
547
|
+
@changelog_repository ||=
|
|
548
|
+
begin
|
|
549
|
+
output, status = Open3.capture2("gh", "repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner")
|
|
550
|
+
raise status.inspect unless status.success?
|
|
551
|
+
output.chomp.split("/", 2)
|
|
552
|
+
end
|
|
553
|
+
owner, name = @changelog_repository
|
|
554
|
+
|
|
555
|
+
query = <<~GRAPHQL
|
|
556
|
+
query($owner: String!, $name: String!) {
|
|
557
|
+
repository(owner: $owner, name: $name) {
|
|
558
|
+
#{body}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
GRAPHQL
|
|
562
|
+
|
|
563
|
+
output, status = Open3.capture2(
|
|
564
|
+
"gh", "api", "graphql",
|
|
565
|
+
"-f", "query=#{query}",
|
|
566
|
+
"-f", "owner=#{owner}",
|
|
567
|
+
"-f", "name=#{name}",
|
|
568
|
+
binmode: true
|
|
569
|
+
)
|
|
570
|
+
raise status.inspect unless status.success?
|
|
571
|
+
|
|
572
|
+
# GitHub always answers in UTF-8, while the default external encoding follows the locale. Without
|
|
573
|
+
# this, a pull request body with an emoji fails to parse under `LANG=C`, as in GitHub Actions.
|
|
574
|
+
JSON.parse(output.force_encoding(Encoding::UTF_8), symbolize_names: true).dig(:data, :repository)
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
# Lists the commits between `from` and `HEAD`, newest first.
|
|
578
|
+
#
|
|
579
|
+
# Giving `paths` limits the commits to the ones touching the paths.
|
|
580
|
+
#
|
|
581
|
+
def changelog_commits(from, paths: [])
|
|
524
582
|
require "open3"
|
|
583
|
+
|
|
584
|
+
command = ["git", "log", "--format=%H", "#{from}..HEAD"]
|
|
585
|
+
# `--simplify-merges` keeps the default history simplification from following only one parent of
|
|
586
|
+
# a merge commit, which can drop the other side. Note that `--full-history` alone is wrong here:
|
|
587
|
+
# it also lists merge commits that do not touch the paths, bringing back the excluded pull
|
|
588
|
+
# requests. The two flags produce the same commits as the default mode for this repository today.
|
|
589
|
+
command.push("--full-history", "--simplify-merges", "--", *paths) unless paths.empty?
|
|
590
|
+
|
|
525
591
|
output, status = Open3.capture2(*command)
|
|
526
592
|
raise status.inspect unless status.success?
|
|
527
593
|
|
|
528
|
-
|
|
529
|
-
|
|
594
|
+
output.lines.map(&:chomp).reject(&:empty?)
|
|
595
|
+
end
|
|
530
596
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
597
|
+
# Finds the pull requests the commits came from, keeping the order of `commits`.
|
|
598
|
+
#
|
|
599
|
+
# Returns the pull requests for the changelog and the ones omitted by `skip_labels`.
|
|
600
|
+
#
|
|
601
|
+
def changelog_pull_requests(commits, skip_labels: CHANGELOG_SKIP_LABELS)
|
|
602
|
+
pull_requests = {}
|
|
603
|
+
skipped = {}
|
|
604
|
+
|
|
605
|
+
commits.each_slice(50) do |slice|
|
|
606
|
+
# Ask GitHub which pull requests the commits came from, so that any merge strategy -- merge
|
|
607
|
+
# commit, squash, or rebase -- is handled without parsing commit messages.
|
|
608
|
+
aliases = slice.map.with_index do |commit, index|
|
|
609
|
+
<<~GRAPHQL
|
|
610
|
+
c#{index}: object(oid: "#{commit}") {
|
|
611
|
+
... on Commit {
|
|
612
|
+
associatedPullRequests(first: 10) {
|
|
613
|
+
nodes {
|
|
614
|
+
number title url merged
|
|
615
|
+
labels(first: 100) { nodes { name } }
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
GRAPHQL
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
changelog_graphql(aliases.join("\n")).each_value do |commit|
|
|
624
|
+
next unless commit
|
|
625
|
+
|
|
626
|
+
commit.dig(:associatedPullRequests, :nodes).each do |pr|
|
|
627
|
+
next unless pr[:merged]
|
|
628
|
+
|
|
629
|
+
pr = { number: pr[:number], title: pr[:title], url: pr[:url], labels: pr.dig(:labels, :nodes).map { |label| label[:name] } }
|
|
630
|
+
if (pr[:labels] & skip_labels).empty?
|
|
631
|
+
pull_requests[pr[:number]] ||= pr
|
|
632
|
+
else
|
|
633
|
+
skipped[pr[:number]] ||= pr
|
|
634
|
+
end
|
|
635
|
+
end
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
[pull_requests.values, skipped.values]
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
# Fetches the details that help classifying the pull requests: the changed files and the body.
|
|
643
|
+
#
|
|
644
|
+
def changelog_pull_request_details(pull_requests)
|
|
645
|
+
pull_requests.each_slice(50).flat_map do |slice|
|
|
646
|
+
aliases = slice.map do |pr|
|
|
647
|
+
<<~GRAPHQL
|
|
648
|
+
p#{pr[:number]}: pullRequest(number: #{pr[:number]}) {
|
|
649
|
+
body
|
|
650
|
+
author { login }
|
|
651
|
+
files(first: 100) {
|
|
652
|
+
nodes { path }
|
|
653
|
+
pageInfo { hasNextPage }
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
GRAPHQL
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
details = changelog_graphql(aliases.join("\n"))
|
|
660
|
+
|
|
661
|
+
slice.map do |pr|
|
|
662
|
+
detail = details[:"p#{pr[:number]}"] or next pr
|
|
663
|
+
|
|
664
|
+
pr.merge(
|
|
665
|
+
author: detail.dig(:author, :login),
|
|
666
|
+
# The body is a hint for writing the changelog, not a copy source. Keep it short.
|
|
667
|
+
body: detail[:body].to_s.strip.slice(0, 1000),
|
|
668
|
+
files: detail.dig(:files, :nodes).map { |file| file[:path] },
|
|
669
|
+
files_truncated: detail.dig(:files, :pageInfo, :hasNextPage)
|
|
670
|
+
)
|
|
535
671
|
end
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
# Reports the pull requests omitted by their label, so that they do not disappear silently.
|
|
676
|
+
#
|
|
677
|
+
def warn_skipped_pull_requests(skipped, skip_labels)
|
|
678
|
+
return if skipped.empty?
|
|
679
|
+
|
|
680
|
+
numbers = skipped.map { |pr| "##{pr[:number]}" }
|
|
681
|
+
numbers = numbers.take(20).push("and #{numbers.size - 20} more") if numbers.size > 20
|
|
682
|
+
|
|
683
|
+
$stderr.puts
|
|
684
|
+
$stderr.puts " (⏭️ Skipped #{skipped.size} pull request(s) labeled #{skip_labels.map { |label| "`#{label}`" }.join(" or ")}: #{numbers.join(", ")})"
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
# Prints the changelog template listing the pull requests merged between `from` and `HEAD`.
|
|
688
|
+
#
|
|
689
|
+
# The changelog goes to STDOUT and everything else goes to STDERR, so that the output can be
|
|
690
|
+
# piped to another command: `rake gem:changelog | pbcopy`
|
|
691
|
+
#
|
|
692
|
+
def print_changelog(from, paths: [], skip_labels: CHANGELOG_SKIP_LABELS)
|
|
693
|
+
$stderr.puts "🔍 Finding pull requests merged between `#{from}` and `HEAD`..."
|
|
694
|
+
|
|
695
|
+
commits = changelog_commits(from, paths: paths)
|
|
696
|
+
if commits.empty?
|
|
697
|
+
$stderr.puts " (🤔 There is no commit after `#{from}`.)"
|
|
698
|
+
return
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
pull_requests, skipped = changelog_pull_requests(commits, skip_labels: skip_labels)
|
|
702
|
+
|
|
703
|
+
if pull_requests.empty?
|
|
704
|
+
$stderr.puts " (🤔 No pull request is associated to the commits after `#{from}`.)"
|
|
536
705
|
else
|
|
537
|
-
puts
|
|
706
|
+
$stderr.puts
|
|
707
|
+
pull_requests.each do |pr|
|
|
708
|
+
puts "* #{pr[:title]} ([##{pr[:number]}](#{pr[:url]}))"
|
|
709
|
+
end
|
|
710
|
+
$stdout.flush
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
warn_skipped_pull_requests(skipped, skip_labels)
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
# Prints the same pull requests as `print_changelog` as JSON, with the details that help
|
|
717
|
+
# classifying them into the sections of CHANGELOG.md.
|
|
718
|
+
#
|
|
719
|
+
# This is the input for the release automation, so it always prints a valid JSON document.
|
|
720
|
+
#
|
|
721
|
+
def print_changelog_json(from, paths: [], skip_labels: CHANGELOG_SKIP_LABELS)
|
|
722
|
+
require "json"
|
|
723
|
+
|
|
724
|
+
$stderr.puts "🔍 Finding pull requests merged between `#{from}` and `HEAD`..."
|
|
725
|
+
|
|
726
|
+
commits = changelog_commits(from, paths: paths)
|
|
727
|
+
pull_requests, skipped = changelog_pull_requests(commits, skip_labels: skip_labels)
|
|
728
|
+
pull_requests = changelog_pull_request_details(pull_requests)
|
|
729
|
+
|
|
730
|
+
$stderr.puts " (📋 #{pull_requests.size} pull request(s))"
|
|
731
|
+
|
|
732
|
+
puts JSON.pretty_generate(
|
|
733
|
+
{
|
|
734
|
+
from: from,
|
|
735
|
+
to: "HEAD",
|
|
736
|
+
pull_requests: pull_requests,
|
|
737
|
+
skipped: skipped
|
|
738
|
+
}
|
|
739
|
+
)
|
|
740
|
+
$stdout.flush
|
|
741
|
+
|
|
742
|
+
warn_skipped_pull_requests(skipped, skip_labels)
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
namespace :gem do
|
|
746
|
+
# The gem is developed in the whole repository except the Rust crate, which has its own release
|
|
747
|
+
# cycle. Note that this is an *exclusion*, not a list of the directories shipped in the gem:
|
|
748
|
+
# changes in `test/` or `.github/` are part of the gem's changelog too.
|
|
749
|
+
# A constant defined in a `namespace` block is a top-level constant, so it needs the prefix.
|
|
750
|
+
GEM_CHANGELOG_PATHS = [".", ":(exclude)rust"]
|
|
751
|
+
|
|
752
|
+
# The tags a release proper starts *after*, rather than at.
|
|
753
|
+
GEM_PRERELEASE_TAGS = ["v*.pre*", "v*.dev*"]
|
|
754
|
+
|
|
755
|
+
# Where the changelog of the release being prepared starts, derived from `RBS::VERSION`:
|
|
756
|
+
#
|
|
757
|
+
# * `X.Y.Z.pre.N` documents what changed since `X.Y.Z.pre.N-1`, so it starts from the latest tag.
|
|
758
|
+
# * `X.Y.Z` documents the whole cycle, the prereleases included, so it skips the prerelease tags
|
|
759
|
+
# in between and starts from the previous release proper.
|
|
760
|
+
#
|
|
761
|
+
# This is the step that is easy to get wrong by hand: on a release proper the latest tag is a
|
|
762
|
+
# prerelease, so the obvious default would produce only the tail of the cycle. Passing a version
|
|
763
|
+
# explicitly overrides all of it.
|
|
764
|
+
#
|
|
765
|
+
def changelog_base(version)
|
|
766
|
+
excluded = Gem::Version.new(RBS::VERSION).prerelease? ? [] : GEM_PRERELEASE_TAGS
|
|
767
|
+
resolve_changelog_base(version, tag_glob: "v*", exclude_globs: excluded)
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
desc "Generate changelog template from GH pull requests merged since the previous release"
|
|
771
|
+
task :changelog, [:version] do |_task, args|
|
|
772
|
+
print_changelog(changelog_base(args[:version]), paths: GEM_CHANGELOG_PATHS)
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
namespace :changelog do
|
|
776
|
+
desc "Print the pull requests of `gem:changelog` as JSON, with the changed files and body of each"
|
|
777
|
+
task :json, [:version] do |_task, args|
|
|
778
|
+
print_changelog_json(changelog_base(args[:version]), paths: GEM_CHANGELOG_PATHS)
|
|
779
|
+
end
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
desc "Publish the GitHub release for RBS::VERSION, unless it is a `.dev.` version"
|
|
783
|
+
task :gh_release do
|
|
784
|
+
require "open3"
|
|
785
|
+
|
|
786
|
+
version = Gem::Version.new(RBS::VERSION)
|
|
787
|
+
major, minor, *_ = RBS::VERSION.split(".")
|
|
788
|
+
tag = "v#{RBS::VERSION}"
|
|
789
|
+
|
|
790
|
+
# There are three kinds of release: `X.Y.Z`, `X.Y.Z.pre.N`, and `X.Y.Z.dev.N`.
|
|
791
|
+
# The `.dev.N` ones are cut from the development line for people who need a
|
|
792
|
+
# specific change early; they are not written up in the changelog, so there are
|
|
793
|
+
# no notes to publish and nothing worth announcing.
|
|
794
|
+
if version.segments.include?("dev")
|
|
795
|
+
puts "⏭️ #{RBS::VERSION} is a dev release, so there is no GitHub release to publish."
|
|
796
|
+
next
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
# The release is created against an existing tag, so that the artifacts and the
|
|
800
|
+
# notes describe a commit that is already immutable.
|
|
801
|
+
_, status = Open3.capture2("git", "rev-parse", "--verify", "--quiet", "#{tag}^{commit}")
|
|
802
|
+
raise "🚨 No such tag: `#{tag}`. Tag the release before creating the GitHub release." unless status.success?
|
|
803
|
+
|
|
804
|
+
# The topmost section of the changelog is this release, minus its own heading.
|
|
805
|
+
# The encoding is explicit because the default external encoding follows the
|
|
806
|
+
# locale, and the changelog is not ASCII.
|
|
807
|
+
content = File.read(File.join(__dir__, "CHANGELOG.md"), encoding: Encoding::UTF_8)
|
|
808
|
+
section = content.scan(/^## \d.*?(?=^## \d)/m)[0] or raise "🚨 Cannot find a release section in CHANGELOG.md"
|
|
809
|
+
heading, _, body = section.partition("\n")
|
|
810
|
+
heading.include?(RBS::VERSION) or raise "🚨 CHANGELOG.md starts with `#{heading.strip}`, which is not #{RBS::VERSION}"
|
|
811
|
+
|
|
812
|
+
notes = <<~NOTES
|
|
813
|
+
[Release note](https://github.com/ruby/rbs/wiki/Release-Note-#{major}.#{minor})
|
|
814
|
+
|
|
815
|
+
#{body.strip}
|
|
816
|
+
NOTES
|
|
817
|
+
|
|
818
|
+
# Published rather than drafted: the notes are the changelog section that was
|
|
819
|
+
# already reviewed in the release pull request, so there is nothing left to edit.
|
|
820
|
+
command = [
|
|
821
|
+
"gh", "release", "create", tag,
|
|
822
|
+
"--title=#{RBS::VERSION}",
|
|
823
|
+
"--notes=#{notes}"
|
|
824
|
+
]
|
|
825
|
+
command << "--prerelease" if version.prerelease?
|
|
826
|
+
|
|
827
|
+
output, status = Open3.capture2(*command)
|
|
828
|
+
raise "🚨 `gh release create` failed: #{status.inspect}" unless status.success?
|
|
829
|
+
|
|
830
|
+
puts "📝 Released #{tag}: #{output.chomp}"
|
|
538
831
|
end
|
|
539
832
|
end
|
|
540
833
|
|
|
@@ -627,40 +920,27 @@ namespace :wasm do
|
|
|
627
920
|
end
|
|
628
921
|
end
|
|
629
922
|
|
|
630
|
-
# Where the runtime looks for the module
|
|
631
|
-
# RBS::WASM::Runtime). These are build artifacts, bundled into the JRuby gem.
|
|
923
|
+
# Where the runtime looks for the module by default (see RBS::WASM::Runtime).
|
|
632
924
|
JRUBY_WASM_DIR = File.expand_path("lib/rbs/wasm", __dir__)
|
|
633
|
-
CHICORY_VERSION = ENV.fetch("CHICORY_VERSION", "1.7.5")
|
|
634
|
-
# `compiler` is Chicory's AOT compiler (wasm -> JVM bytecode); the asm* jars
|
|
635
|
-
# are the ow2 ASM libraries it depends on. Keep ASM_VERSION in sync with what
|
|
636
|
-
# the pinned Chicory release declares.
|
|
637
|
-
CHICORY_JARS = %w[wasm runtime log wasi compiler].freeze
|
|
638
|
-
ASM_VERSION = ENV.fetch("ASM_VERSION", "9.9.1")
|
|
639
|
-
ASM_JARS = %w[asm asm-tree asm-util asm-commons asm-analysis].freeze
|
|
640
|
-
|
|
641
|
-
desc "Download the Chicory and ASM jars the JRuby runtime needs into lib/rbs/wasm/jars"
|
|
642
|
-
task :vendor_jars do
|
|
643
|
-
require "open-uri"
|
|
644
|
-
require "fileutils"
|
|
645
|
-
|
|
646
|
-
jars_dir = File.join(JRUBY_WASM_DIR, "jars")
|
|
647
|
-
FileUtils.mkdir_p(jars_dir)
|
|
648
|
-
|
|
649
|
-
downloads = CHICORY_JARS.map { |name| ["#{name}.jar", "https://repo1.maven.org/maven2/com/dylibso/chicory/#{name}/#{CHICORY_VERSION}/#{name}-#{CHICORY_VERSION}.jar"] }
|
|
650
|
-
downloads += ASM_JARS.map { |name| ["#{name}.jar", "https://repo1.maven.org/maven2/org/ow2/asm/#{name}/#{ASM_VERSION}/#{name}-#{ASM_VERSION}.jar"] }
|
|
651
|
-
|
|
652
|
-
downloads.each do |filename, url|
|
|
653
|
-
puts "Downloading #{url}"
|
|
654
|
-
URI.open(url) { |io| File.binwrite(File.join(jars_dir, filename), io.read) } # steep:ignore
|
|
655
|
-
end
|
|
656
925
|
|
|
657
|
-
|
|
926
|
+
desc "Download the Chicory/ASM jars into the local Maven repository (~/.m2). Run on JRuby."
|
|
927
|
+
task :install_jars do
|
|
928
|
+
# Resolves the `jar` requirements from rbs.gemspec via Maven and downloads
|
|
929
|
+
# them (and their transitive deps) into ~/.m2, the same way `gem install`
|
|
930
|
+
# does; the jars are not copied into the gem. The platform is forced to java
|
|
931
|
+
# because Jars::Installer skips non-java gems, and write_require_file is false
|
|
932
|
+
# because lib/rbs_jars.rb is hand-maintained (the generator mangles the
|
|
933
|
+
# `com.dylibso.chicory:runtime` artifact id).
|
|
934
|
+
require "jars/installer"
|
|
935
|
+
spec = Gem::Specification.load("rbs.gemspec")
|
|
936
|
+
spec.platform = "java"
|
|
937
|
+
Jars::Installer.new(spec).install_jars(write_require_file: false)
|
|
658
938
|
end
|
|
659
939
|
|
|
660
|
-
desc "
|
|
661
|
-
task :jruby_setup => [:build
|
|
940
|
+
desc "Build rbs_parser.wasm and copy it next to RBS::WASM::Runtime"
|
|
941
|
+
task :jruby_setup => [:build] do
|
|
662
942
|
cp WASM_OUTPUT, File.join(JRUBY_WASM_DIR, "rbs_parser.wasm")
|
|
663
|
-
puts "
|
|
943
|
+
puts "rbs_parser.wasm is ready under #{JRUBY_WASM_DIR}"
|
|
664
944
|
end
|
|
665
945
|
end
|
|
666
946
|
|
data/Steepfile
CHANGED
|
@@ -13,6 +13,8 @@ target :lib do
|
|
|
13
13
|
"lib/rbs/wasm/location.rb",
|
|
14
14
|
"lib/rbs/wasm/runtime.rb",
|
|
15
15
|
"lib/rbs/wasm/parser.rb",
|
|
16
|
+
# jar-dependencies require_jar calls for the JRuby runtime; not type-checked.
|
|
17
|
+
"lib/rbs_jars.rb",
|
|
16
18
|
)
|
|
17
19
|
|
|
18
20
|
library "pathname", "json", "logger", "monitor", "tsort", "uri", 'dbm', 'pstore', 'singleton', 'shellwords', 'fileutils', 'find', 'digest', 'prettyprint', 'yaml', "psych", "securerandom"
|
data/config.yml
CHANGED
|
@@ -769,6 +769,7 @@ nodes:
|
|
|
769
769
|
c_type: rbs_location_range
|
|
770
770
|
- name: type_name
|
|
771
771
|
c_type: rbs_type_name
|
|
772
|
+
optional: true # NULL when the name is left out, to be inferred from the Ruby code
|
|
772
773
|
- name: type_name_location
|
|
773
774
|
c_type: rbs_location_range
|
|
774
775
|
optional: true
|
|
@@ -781,6 +782,7 @@ nodes:
|
|
|
781
782
|
c_type: rbs_location_range
|
|
782
783
|
- name: type_name
|
|
783
784
|
c_type: rbs_type_name
|
|
785
|
+
optional: true # NULL when the name is left out, to be inferred from the Ruby code
|
|
784
786
|
- name: type_name_location
|
|
785
787
|
c_type: rbs_location_range
|
|
786
788
|
optional: true
|