-
Notifications
You must be signed in to change notification settings - Fork 54
ci: speed up RBS validation without Steep internals #333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+388
−20
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c83fe2f
ci: speed up RBS validation
jbeckwith-oai 95b5608
ci: preserve Steep fallback workers
jbeckwith-oai b339aae
ci: parse RBS files before consolidation
jbeckwith-oai ea5d75b
fix: guard RBS consolidation against NUL bytes
jbeckwith-oai 8b6a690
fix: recognize all RBS use directive boundaries
jbeckwith-oai 1a8c449
fix: detect multiline RBS magic directives
jbeckwith-oai File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| #!/usr/bin/env ruby | ||
| # frozen_string_literal: true | ||
|
|
||
| require "fileutils" | ||
| require "open3" | ||
| require "pathname" | ||
| require "tmpdir" | ||
|
|
||
| module OpenAI | ||
| module RBSValidation | ||
| # These directives change name resolution on a per-file basis, so signatures | ||
| # containing them cannot be safely combined and must take the reference path. | ||
| FILE_SCOPED_DIRECTIVE = /^\s*(?:use\b|#\s*resolve-type-names\s*:)/ | ||
|
|
||
| module_function | ||
|
|
||
| def run(root: Pathname.pwd, stdout: $stdout, stderr: $stderr, env: ENV.to_h) | ||
| steepfile = root.join("Steepfile") | ||
| raise Errno::ENOENT, steepfile unless steepfile.file? | ||
|
|
||
| signatures = signature_paths(root) | ||
| raise "No RBS files found under #{root.join('sig')}" if signatures.empty? | ||
|
|
||
| status = | ||
| if signatures.any? { requires_reference_check?(_1) } | ||
| run_reference_check(root, stdout: stdout, stderr: stderr, env: env) | ||
| else | ||
| fast_status = run_parse_check(root, env: env) | ||
| fast_status = run_consolidated_check(root, steepfile, signatures, env: env) if fast_status.success? | ||
| unless fast_status.success? | ||
| stdout.puts( | ||
| "Fast RBS validation failed; checking original signature files for exact diagnostics." | ||
| ) | ||
| fast_status = run_reference_check(root, stdout: stdout, stderr: stderr, env: env) | ||
| end | ||
| fast_status | ||
| end | ||
|
|
||
| if status.success? | ||
| noun = signatures.one? ? "file" : "files" | ||
| stdout.puts("Validated #{signatures.size} RBS #{noun} with no errors.") | ||
| 0 | ||
| else | ||
| 1 | ||
| end | ||
| rescue StandardError => e | ||
| message = | ||
| if e.respond_to?(:detailed_message) | ||
| e.detailed_message(highlight: false) | ||
| else | ||
| "#{e.class}: #{e.message}" | ||
| end | ||
| stderr.puts(message) | ||
| 1 | ||
| end | ||
|
|
||
| def signature_paths(root) | ||
| Dir.glob(root.join("sig/**/*.rbs")).map { Pathname(_1) } | ||
| end | ||
| private_class_method :signature_paths | ||
|
|
||
| def requires_reference_check?(path) | ||
| contents = path.binread | ||
| # RBS 3.9 treats NUL as EOF. In a combined buffer, that would hide every | ||
| # declaration from later files even though the originals parse separately. | ||
| # Match the complete file because RBS's magic-comment grammar permits its | ||
| # whitespace matcher to span lines (for example, "#\nresolve-type-names:"). | ||
| contents.include?("\0") || contents.match?(FILE_SCOPED_DIRECTIVE) | ||
| end | ||
| private_class_method :requires_reference_check? | ||
|
|
||
| def run_parse_check(root, env:) | ||
| _stdout, _stderr, status = Open3.capture3(env, "rbs", "parse", "sig", chdir: root.to_s) | ||
| status | ||
| end | ||
| private_class_method :run_parse_check | ||
|
|
||
| def run_consolidated_check(root, steepfile, signatures, env:) | ||
| Dir.mktmpdir("openai-rbs-validation") do |directory| | ||
| temporary_root = Pathname(directory) | ||
| temporary_sig = temporary_root.join("sig") | ||
| temporary_sig.mkpath | ||
| temporary_steepfile = temporary_root.join("Steepfile") | ||
| FileUtils.cp(steepfile, temporary_steepfile) | ||
| # Steep validates reopened namespaces once per input file. Combining the | ||
| # generated declarations avoids that repeated work; Steep still performs | ||
| # all parsing and semantic validation through its public CLI. | ||
| concatenate(signatures, temporary_sig.join("all.rbs")) | ||
|
jbeckwith-oai marked this conversation as resolved.
|
||
|
|
||
| command = [env.fetch("STEEP_COMMAND", "steep"), "check", "--no-type-check", "--jobs=1"] | ||
| command << "--steepfile=#{temporary_steepfile}" | ||
| _stdout, _stderr, status = Open3.capture3(env, *command, chdir: root.to_s) | ||
| status | ||
| end | ||
| end | ||
| private_class_method :run_consolidated_check | ||
|
|
||
| def concatenate(signatures, destination) | ||
| destination.open("wb") do |combined| | ||
| signatures.each do |path| | ||
| contents = path.binread | ||
| combined.write(contents) | ||
|
jbeckwith-oai marked this conversation as resolved.
|
||
| combined.write("\n") unless contents.end_with?("\n") | ||
| combined.write("\n") | ||
| end | ||
| end | ||
| end | ||
| private_class_method :concatenate | ||
|
|
||
| def run_reference_check(root, stdout:, stderr:, env:) | ||
| command = [env.fetch("STEEP_COMMAND", "steep"), "check", "--no-type-check"] | ||
| command << "--jobs=#{env.fetch('STEEP_JOBS')}" if env.key?("STEEP_JOBS") | ||
| command << "--format=github" if env.key?("CI") | ||
| output, errors, status = Open3.capture3(env, *command, chdir: root.to_s) | ||
| stdout.print(output) | ||
| stderr.print(errors) | ||
| status | ||
| end | ||
| private_class_method :run_reference_check | ||
| end | ||
| end | ||
|
|
||
| exit(OpenAI::RBSValidation.run) if $PROGRAM_NAME == __FILE__ | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.