Skip to content

Commit

Permalink
Add RBS signature files to support static type checking
Browse files Browse the repository at this point in the history
And also add CI for RBS
  • Loading branch information
ksss committed Dec 11, 2023
1 parent b221544 commit 4ded452
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 1 deletion.
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,32 @@ permissions:
contents: read

jobs:
sig:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby: [3.2]

steps:
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}

- uses: actions/checkout@v4

- name: Install gems
run: |
bundle config set --local with 'build signature'
bundle install
- name: SDK Build
run: bundle exec rake build

- name: rbs validate
run: bundle exec rake rbs:validate

test:
runs-on: ubuntu-latest
strategy:
Expand Down
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ group :benchmark do
gem 'benchmark'
gem 'memory_profiler'
end

group :signature do
gem 'rbs', platforms: :ruby
end
2 changes: 2 additions & 0 deletions gems/aws-sdk-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Feature - Add RBS signature files to support static type checking.

3.190.0 (2023-11-29)
------------------

Expand Down
2 changes: 1 addition & 1 deletion gems/aws-sdk-core/aws-sdk-core.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
spec.homepage = 'https://github.com/aws/aws-sdk-ruby'
spec.license = 'Apache-2.0'
spec.require_paths = ['lib']
spec.files = Dir['LICENSE.txt', 'CHANGELOG.md', 'VERSION', 'lib/**/*.rb', 'ca-bundle.crt']
spec.files = Dir['LICENSE.txt', 'CHANGELOG.md', 'VERSION', 'lib/**/*.rb', 'ca-bundle.crt', 'sig/**/*.rbs']

spec.add_dependency('jmespath', '~> 1', '>= 1.6.1') # necessary for secure jmespath JSON parsing
spec.add_dependency('aws-partitions', '~> 1', '>= 1.651.0') # necessary for new endpoint resolution
Expand Down
7 changes: 7 additions & 0 deletions gems/aws-sdk-core/sig/aws-sdk-core.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Aws
attr_reader self.config: Hash[Symbol, untyped]

def self.config=: (Hash[Symbol, untyped] config) -> Hash[Symbol, untyped]

def self.use_bundled_cert!: () -> String
end
9 changes: 9 additions & 0 deletions gems/aws-sdk-core/sig/aws-sdk-core/client_stubs.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Aws
module ClientStubs
def stub_responses: (Symbol operation_name, *untyped stubs) -> void

def api_requests: () -> Array[{ operation_name: Symbol, params: untyped, context: untyped }]

def stub_data: (Symbol operation_name, untyped data) -> untyped
end
end
22 changes: 22 additions & 0 deletions gems/aws-sdk-core/sig/aws-sdk-core/errors.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Aws
module Errors
class NonSupportedRubyVersionError < RuntimeError
end

# The base class for all errors returned by an Amazon Web Service.
# All ~400 level client errors and ~500 level server errors are raised
# as service errors. This indicates it was an error returned from the
# service and not one generated by the client.
class ServiceError < RuntimeError
def initialize: (untyped context, String message, ?untyped data) -> void

attr_reader code: String

attr_reader context: untyped

attr_reader data: untyped

attr_accessor self.code: String
end
end
end
21 changes: 21 additions & 0 deletions gems/aws-sdk-core/sig/aws-sdk-core/resources/collection.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Aws
module Resources
class Collection[T]
include Enumerable[T]

def initialize: (Enumerable[Enumerable[T]] batches, ?size: Integer, ?limit: Integer) -> void

def each: () -> Enumerator[T, untyped]
| () { (T) -> untyped } -> Enumerator[T, untyped]

def size: () -> Integer?

alias length size

def first: () -> T?
| (Integer) -> self

def limit: (Integer) -> self
end
end
end
4 changes: 4 additions & 0 deletions gems/aws-sdk-core/sig/aws-sdk-core/structure.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Aws
class EmptyStructure
end
end
20 changes: 20 additions & 0 deletions gems/aws-sdk-core/sig/aws-sdk-core/waiters/errors.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Aws
module Waiters
module Errors
class WaiterFailed < StandardError
end

class FailureStateError < WaiterFailed
end

class TooManyAttemptsError < WaiterFailed
end

class UnexpectedError < WaiterFailed
end

class NoSuchWaiterError < ArgumentError
end
end
end
end
25 changes: 25 additions & 0 deletions gems/aws-sdk-core/sig/seahorse/client/base.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Seahorse
module Client
class Base
include HandlerBuilder

def self.new: (?untyped options) -> instance
def self.add_plugin: (untyped plugin) -> untyped
def self.remove_plugin: (untyped plugin) -> untyped
def self.clear_plugins: () -> untyped
def self.set_plugins: (Array[untyped] plugins) -> untyped
def self.plugins: () -> Array[untyped]
def self.api: () -> untyped
def self.set_api: (untyped api) -> untyped
def self.define: (?untyped options) -> untyped

attr_reader config: untyped

attr_reader handlers: untyped

def build_request: (_ToS operation_name, ?untyped params) -> untyped

def operation_names: () -> Array[Symbol]
end
end
end
16 changes: 16 additions & 0 deletions gems/aws-sdk-core/sig/seahorse/client/handler_builder.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Seahorse
module Client
# This module provides the ability to add handlers to a class or
# module. The including class or extending module must respond to
# `#handlers`, returning a {HandlerList}.
module HandlerBuilder
def handle_request: (*untyped) { (untyped context) -> void } -> untyped

def handle_response: (*untyped) { (untyped resp) -> void } -> untyped

def handle: (*untyped) ?{ (untyped context) -> void } -> untyped

alias handler handle
end
end
end
26 changes: 26 additions & 0 deletions gems/aws-sdk-core/sig/seahorse/client/response.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Seahorse
module Client
# <!--
# RBS does not support Delegator.
# the behavior is mimicked `Seahorse::Client::Response` as much as possible.
# -->
interface _Response[DATA]
def context: () -> untyped

def data: () -> DATA?
def data=: (DATA?) -> DATA?

def error: () -> ::StandardError?
def error=: (::StandardError?) -> ::StandardError?

def checksum_validated: () -> ::String?

def on: (Integer) { (self) -> void } -> self
| (Range[Integer]) { (self) -> void } -> self

def on_success: () { (self) -> void } -> self

def successful?: () -> bool
end
end
end
22 changes: 22 additions & 0 deletions tasks/rbs.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace :rbs do
task :validate do
require 'rubygems'

all_gems = Dir.glob("gems/*").map{File.basename(_1)}.to_set

sigs = []
Dir.glob('gems/*/sig').each do |dir|
sdk_gem = dir.sub(%r{gems/(.*)/sig}, '\1')
spec = Gem::Specification::load("gems/#{sdk_gem}/#{sdk_gem}.gemspec")
deps = spec.dependencies.select do |d|
all_gems.include?(d.name) && File.directory?("gems/#{d.name}/sig")
end.map { |d| d.name }

puts "Validating gem `#{sdk_gem}` with deps #{deps}"
sh("bundle exec rbs #{deps.map{"-I gems/#{_1}/sig"}.join(' ')} -I #{dir} validate --silent") do |ok, _|
sigs << File.basename(File.dirname(dir)) unless ok
end
end
abort('one or more rbs validate failed: %s' % [sigs.join(', ')]) unless sigs.empty?
end
end

0 comments on commit 4ded452

Please sign in to comment.