From 543ff7abffe6995ac50fc071e81d8febc4f7cc5c Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Wed, 9 Oct 2024 16:53:14 -0500 Subject: [PATCH] GitHub workflow for building gems --- .github/workflows/build-gems.yml | 90 +++++++++++++++++++ .github/workflows/ci.yml | 34 ++----- temporalio/.rubocop.yml | 4 - temporalio/Gemfile | 18 ++++ temporalio/Rakefile | 45 +++++++++- temporalio/ext/src/client.rs | 6 +- temporalio/lib/temporalio/internal/bridge.rb | 8 ++ .../lib/temporalio/internal/bridge/client.rb | 1 - .../lib/temporalio/internal/bridge/runtime.rb | 2 + .../lib/temporalio/internal/bridge/testing.rb | 1 - temporalio/lib/temporalio/version.rb | 2 +- temporalio/temporalio.gemspec | 20 +---- 12 files changed, 175 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/build-gems.yml diff --git a/.github/workflows/build-gems.yml b/.github/workflows/build-gems.yml new file mode 100644 index 00000000..1f7af442 --- /dev/null +++ b/.github/workflows/build-gems.yml @@ -0,0 +1,90 @@ +name: Build Gems +on: + pull_request: + push: + branches: + - main + - "releases/*" + +jobs: + build-gems: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rubyPlatform: ["aarch64-linux", "x86_64-linux", "arm64-darwin", "x86_64-darwin", "x64-mingw-ucrt"] + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Setup Ruby and Rust + uses: oxidize-rb/actions/setup-ruby-and-rust@v1 + with: + ruby-version: "3.3" + bundler-cache: true + cargo-cache: true + cargo-vendor: true + working-directory: ./temporalio + cache-version: v1-${{ matrix.rubyPlatform }} + + - name: Cross compile gems + uses: oxidize-rb/actions/cross-gem@v1 + id: cross-gem + with: + platform: ${{ matrix.rubyPlatform }} + ruby-versions: "3.1,3.2,3.3" + working-directory: ./temporalio + + - name: Upload gems + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.rubyPlatform }}-gem + path: ${{ steps.cross-gem.outputs.gem-path }} + + smoke-test-gems: + needs: + - build-gems + strategy: + fail-fast: false + matrix: + # TODO(cretz): Enable Linux ARM. See ci.yaml comment for why we can't right now. + os: [ubuntu-latest, macos-intel, macos-latest, windows-latest] + include: + - os: ubuntu-latest + rubyPlatform: x86_64-linux + - os: macos-intel + runsOn: macos-12 + rubyPlatform: x86_64-darwin + - os: macos-latest + rubyPlatform: arm64-darwin + - os: windows-latest + rubyPlatform: x64-mingw-ucrt + runs-on: ${{ matrix.runsOn || matrix.os }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Download gems + uses: actions/download-artifact@v4 + with: + name: ${{ matrix.rubyPlatform }}-gem + path: local-gem + + - name: Setup Ruby + uses: oxidize-rb/actions/setup-ruby-and-rust@v1 + with: + ruby-version: "3.3" + bundler-cache: true + cargo-cache: false + + - name: Install bundle + working-directory: ./temporalio + run: bundle install + + - name: Run smoke test + working-directory: ./temporalio + run: bundle exec rake smoke_test_gem[../local-gem/*-${{ matrix.rubyPlatform }}.gem] \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef4dce98..a1ad0403 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,18 +11,15 @@ jobs: strategy: fail-fast: true matrix: - # TODO(cretz): Enable Windows (it's slow) - # # TODO(cretz): Enable Linux ARM. It's not natively supported with setup-ruby (see - # https://github.com/ruby/setup-ruby#supported-platforms). So we need to set ruby-version to 'none' per + # https://github.com/ruby/setup-ruby#supported-platforms and https://github.com/ruby/setup-ruby/issues/577). + # So we need to set ruby-version to 'none' per # https://github.com/oxidize-rb/actions/tree/main/setup-ruby-and-rust and install Ruby ourselves maybe. See # https://github.com/ruby/setup-ruby?tab=readme-ov-file#using-self-hosted-runners. The error states: # Error: The current runner (ubuntu-24.04-arm64) was detected as self-hosted because the platform does not match a GitHub-hosted runner image (or that image is deprecated and no longer supported). # In such a case, you should install Ruby in the $RUNNER_TOOL_CACHE yourself, for example using https://github.com/rbenv/ruby-build # You can take inspiration from this workflow for more details: https://github.com/ruby/ruby-builder/blob/master/.github/workflows/build.yml - # - #os: [ubuntu-latest, ubuntu-arm, macos-intel, macos-arm, windows-latest] - os: [ubuntu-latest, macos-intel, macos-arm] + os: [ubuntu-latest, macos-latest, windows-latest] # Earliest and latest supported rubyVersion: ["3.1", "3.3"] @@ -30,11 +27,7 @@ jobs: - os: ubuntu-latest rubyVersion: "3.3" checkTarget: true - - os: macos-intel - runsOn: macos-12 - - os: macos-arm - runsOn: macos-14 - runs-on: ${{ matrix.runsOn || matrix.os }} + runs-on: ${{ matrix.os }} steps: - name: Checkout repository uses: actions/checkout@v2 @@ -47,11 +40,7 @@ jobs: ruby-version: ${{ matrix.rubyVersion }} bundler-cache: true cargo-cache: true - - - name: Setup Rust cache - uses: Swatinem/rust-cache@v2 - with: - workspaces: temporalio/ext -> temporalio/target + working-directory: ./temporalio # Needed for tests currently - name: Install Go @@ -59,18 +48,15 @@ jobs: with: go-version: stable - - name: Install protoc + # Needed because gRPC tools does not have a macOS protoc binary + # currently, see https://github.com/grpc/grpc/issues/25755 + - name: Install protoc for mac + if: ${{ matrix.os == 'macos-latest' }} uses: arduino/setup-protoc@v3 with: - # TODO(cretz): Can upgrade proto when https://github.com/arduino/setup-protoc/issues/99 fixed version: "23.x" repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Lint Rust - if: ${{ matrix.checkTarget }} - working-directory: ./temporalio - run: cargo clippy && cargo fmt --check - - name: Install bundle working-directory: ./temporalio run: bundle install @@ -85,5 +71,3 @@ jobs: - name: Lint, compile, test Ruby working-directory: ./temporalio run: bundle exec rake TESTOPTS="--verbose" - - # TODO(cretz): Build gem and smoke test against separate dir diff --git a/temporalio/.rubocop.yml b/temporalio/.rubocop.yml index 3003a1e5..a2d6266f 100644 --- a/temporalio/.rubocop.yml +++ b/temporalio/.rubocop.yml @@ -16,10 +16,6 @@ AllCops: # Keep cop rule settings in alphabetical order. For each rule setting, provide # justification for the change from default. -# We want development dependencies in the gemspec -Gemspec/DevelopmentDependencies: - EnforcedStyle: gemspec - # We want our classes in a certain order Layout/ClassStructure: Enabled: true diff --git a/temporalio/Gemfile b/temporalio/Gemfile index 7f4f5e95..bca320ea 100644 --- a/temporalio/Gemfile +++ b/temporalio/Gemfile @@ -3,3 +3,21 @@ source 'https://rubygems.org' gemspec + +group :development do + gem 'activemodel' + gem 'activerecord' + gem 'async' + gem 'base64' + gem 'grpc', '>= 1.65.0.pre2' + gem 'grpc-tools' + gem 'minitest' + gem 'rake' + gem 'rake-compiler' + gem 'rbs', '~> 3.5.3' + gem 'rb_sys', '~> 0.9.63' + gem 'rubocop' + gem 'sqlite3', '~> 1.4' + gem 'steep', '~> 1.7.1' + gem 'yard' +end diff --git a/temporalio/Rakefile b/temporalio/Rakefile index 0d0f8ecb..1fd62b98 100644 --- a/temporalio/Rakefile +++ b/temporalio/Rakefile @@ -11,7 +11,7 @@ task build: :compile GEMSPEC = Gem::Specification.load('temporalio.gemspec') RbSys::ExtensionTask.new('temporalio_bridge', GEMSPEC) do |ext| - ext.lib_dir = 'lib/temporalio' + ext.lib_dir = 'lib/temporalio/internal/bridge' end require 'rake/testtask' @@ -23,6 +23,19 @@ Rake::TestTask.new(:test) do |t| t.test_files = FileList['test/**/*_test.rb'] end +def add_protoc_to_path + tools_spec = Gem::Specification.find_by_name('grpc-tools') + cpu = RbConfig::CONFIG['host_cpu'] + cpu = 'x86_64' if cpu == 'x64' + os = RbConfig::CONFIG['host_os'] + os = 'windows' if os.start_with?('mingw') + protoc_path = "#{tools_spec.gem_dir}/bin/#{cpu}-#{os}" + separator = os == 'windows' ? ';' : ':' + ENV['PATH'] = "#{ENV.fetch('PATH', nil)}#{separator}#{protoc_path}" +end + +add_protoc_to_path + require 'rubocop/rake_task' RuboCop::RakeTask.new @@ -332,4 +345,32 @@ Rake::Task[:build].enhance([:copy_parent_files]) do rm ['LICENSE', 'README.md'] end -task default: ['rubocop', 'yard', 'compile', 'rbs:install_collection', 'steep', 'test'] +task :rust_lint do + sh 'cargo', 'clippy' + sh 'cargo', 'fmt', '--check' +end + +task :smoke_test_gem, [:gem_file_glob] do |_t, args| + # Install gem + gem_file_glob = args[:gem_file_glob] + raise 'Gem file glob not found' unless gem_file_glob + + gem_files = Dir.glob(gem_file_glob) + raise "Unable to find single gem file, found #{gem_file.length}" unless gem_files.length == 1 + + sh 'gem', 'install', '--verbose', '--local', gem_files.first + + # Move the installed gem to the front of the load path + $LOAD_PATH.unshift("#{Gem::Specification.find_by_name('temporalio').gem_dir}/lib") + + # Create a local environment and start a workflow + require 'temporalio/client' + require 'temporalio/testing/workflow_environment' + + Temporalio::Testing::WorkflowEnvironment.start_local do |env| + handle = env.client.start_workflow('MyWorkflow', id: 'my-workflow', task_queue: 'my-task-queue') + puts "Created workflow with run ID: #{handle.result_run_id}" + end +end + +task default: ['rubocop', 'yard', 'rbs:install_collection', 'steep', 'rust_lint', 'compile', 'test'] diff --git a/temporalio/ext/src/client.rs b/temporalio/ext/src/client.rs index e9315b43..3aed0577 100644 --- a/temporalio/ext/src/client.rs +++ b/temporalio/ext/src/client.rs @@ -61,13 +61,13 @@ macro_rules! rpc_call { if $call.retry { let mut core_client = $client.core.clone(); let req = $call.into_request()?; - crate::client::rpc_resp($client, $block, async move { + $crate::client::rpc_resp($client, $block, async move { $trait::$call_name(&mut core_client, req).await }) } else { let mut core_client = $client.core.clone().into_inner(); let req = $call.into_request()?; - crate::client::rpc_resp($client, $block, async move { + $crate::client::rpc_resp($client, $block, async move { $trait::$call_name(&mut core_client, req).await }) } @@ -237,7 +237,7 @@ impl RpcFailure { } pub fn details(&self) -> Option { - if self.status.details().len() == 0 { + if self.status.details().is_empty() { None } else { Some(RString::from_slice(self.status.details())) diff --git a/temporalio/lib/temporalio/internal/bridge.rb b/temporalio/lib/temporalio/internal/bridge.rb index 8b532299..a41f8546 100644 --- a/temporalio/lib/temporalio/internal/bridge.rb +++ b/temporalio/lib/temporalio/internal/bridge.rb @@ -1,5 +1,13 @@ # frozen_string_literal: true +# Use Ruby-version-specific Rust library if present +begin + RUBY_VERSION =~ /(\d+\.\d+)/ + require "temporalio/internal/bridge/#{Regexp.last_match(1)}/temporalio_bridge" +rescue LoadError + require 'temporalio/internal/bridge/temporalio_bridge' +end + module Temporalio module Internal # @!visibility private diff --git a/temporalio/lib/temporalio/internal/bridge/client.rb b/temporalio/lib/temporalio/internal/bridge/client.rb index 1b979ef7..5067122f 100644 --- a/temporalio/lib/temporalio/internal/bridge/client.rb +++ b/temporalio/lib/temporalio/internal/bridge/client.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'temporalio/internal/bridge' -require 'temporalio/temporalio_bridge' module Temporalio module Internal diff --git a/temporalio/lib/temporalio/internal/bridge/runtime.rb b/temporalio/lib/temporalio/internal/bridge/runtime.rb index 5814d827..19c03b3a 100644 --- a/temporalio/lib/temporalio/internal/bridge/runtime.rb +++ b/temporalio/lib/temporalio/internal/bridge/runtime.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'temporalio/internal/bridge' + module Temporalio module Internal module Bridge diff --git a/temporalio/lib/temporalio/internal/bridge/testing.rb b/temporalio/lib/temporalio/internal/bridge/testing.rb index 0c78ac1d..a97cdbdc 100644 --- a/temporalio/lib/temporalio/internal/bridge/testing.rb +++ b/temporalio/lib/temporalio/internal/bridge/testing.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'temporalio/internal/bridge' -require 'temporalio/temporalio_bridge' module Temporalio module Internal diff --git a/temporalio/lib/temporalio/version.rb b/temporalio/lib/temporalio/version.rb index 9e93e9e1..eca4d4c4 100644 --- a/temporalio/lib/temporalio/version.rb +++ b/temporalio/lib/temporalio/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Temporalio - VERSION = '0.2.0' + VERSION = '0.2.0-alpha1' end diff --git a/temporalio/temporalio.gemspec b/temporalio/temporalio.gemspec index db09160c..d84eee2a 100644 --- a/temporalio/temporalio.gemspec +++ b/temporalio/temporalio.gemspec @@ -12,13 +12,11 @@ Gem::Specification.new do |spec| spec.homepage = 'https://github.com/temporalio/sdk-ruby' spec.license = 'MIT' spec.required_ruby_version = '>= 3.1.0' - spec.required_rubygems_version = '>= 3.3.11' spec.metadata['homepage_uri'] = spec.homepage spec.metadata['source_code_uri'] = 'https://github.com/temporalio/sdk-ruby' - spec.files = Dir['lib/**/*.rb', 'ext/**/*.*', 'Cargo.lock', 'Cargo.toml', 'Gemfile', 'Rakefile', - 'temporalio.gemspec', 'LICENSE', 'README.md'] + spec.files = Dir['lib/**/*.rb', 'LICENSE', 'README.md', 'Cargo.*'] spec.bindir = 'exe' spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } @@ -27,20 +25,4 @@ Gem::Specification.new do |spec| spec.metadata['rubygems_mfa_required'] = 'true' spec.add_dependency 'google-protobuf', '>= 3.27.0' - - spec.add_development_dependency 'activemodel' - spec.add_development_dependency 'activerecord' - spec.add_development_dependency 'async' - spec.add_development_dependency 'base64' - spec.add_development_dependency 'grpc', '>= 1.65.0.pre2' - spec.add_development_dependency 'grpc-tools' - spec.add_development_dependency 'minitest' - spec.add_development_dependency 'rake' - spec.add_development_dependency 'rake-compiler' - spec.add_development_dependency 'rbs', '~> 3.5.3' - spec.add_development_dependency 'rb_sys', '~> 0.9.63' - spec.add_development_dependency 'rubocop' - spec.add_development_dependency 'sqlite3', '~> 1.4' - spec.add_development_dependency 'steep', '~> 1.7.1' - spec.add_development_dependency 'yard' end