Ruby - Continuous Deployment #9
Workflow file for this run
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
| name: Ruby - Continuous Deployment | |
| on: | |
| push: | |
| tags: | |
| - "v*.*" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "The release version of GLIDE, formatted as *.*.* or *.*.*-rc*" | |
| required: true | |
| publish_gem: | |
| description: "Publish to RubyGems" | |
| required: true | |
| type: boolean | |
| default: false | |
| concurrency: | |
| group: ruby-cd-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| id-token: write | |
| contents: read | |
| jobs: | |
| load-platform-matrix: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| PLATFORM_MATRIX: ${{ steps.load-platform-matrix.outputs.PLATFORM_MATRIX }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Load platform matrix | |
| id: load-platform-matrix | |
| shell: bash | |
| run: | | |
| export PLATFORM_MATRIX=$(jq -c '.' < .github/json_matrices/build-matrix.json) | |
| echo "PLATFORM_MATRIX=${PLATFORM_MATRIX}" >> $GITHUB_OUTPUT | |
| echo "Loaded platform matrix: ${PLATFORM_MATRIX}" | |
| set-release-version: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| RELEASE_VERSION: ${{ steps.release-version.outputs.RELEASE_VERSION }} | |
| steps: | |
| - name: Set the release version | |
| id: release-version | |
| shell: bash | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| R_VERSION="${{ github.event.inputs.version }}" | |
| else | |
| # Remove 'v' prefix from tag | |
| R_VERSION="${GITHUB_REF_NAME#v}" | |
| fi | |
| echo "Release version: $R_VERSION" | |
| echo "RELEASE_VERSION=$R_VERSION" >> $GITHUB_OUTPUT | |
| - name: Validate version format | |
| run: | | |
| VERSION="${{ steps.release-version.outputs.RELEASE_VERSION }}" | |
| if ! echo "$VERSION" | grep -Pq '^\d+\.\d+\.\d+(-rc\d+)?$'; then | |
| echo "Invalid version format: $VERSION" | |
| echo "Expected format: X.Y.Z or X.Y.Z-rcN" | |
| exit 1 | |
| fi | |
| build-native-libraries: | |
| needs: [set-release-version, load-platform-matrix] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| host: ${{ fromJson(needs.load-platform-matrix.outputs.PLATFORM_MATRIX) }} | |
| runs-on: ${{ matrix.host.RUNNER }} | |
| env: | |
| RELEASE_VERSION: ${{ needs.set-release-version.outputs.RELEASE_VERSION }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install protoc (protobuf) | |
| uses: arduino/setup-protoc@v3 | |
| with: | |
| version: "29.1" | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build native library | |
| working-directory: valkey-glide/ffi | |
| run: | | |
| cargo build --release | |
| - name: Upload native library artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: native-lib-${{ matrix.host.TARGET }} | |
| path: valkey-glide/ffi/target/release/libglide_ffi.so | |
| if-no-files-found: error | |
| build-and-publish-gem: | |
| needs: [set-release-version, load-platform-matrix, build-native-libraries] | |
| runs-on: ubuntu-latest | |
| env: | |
| RELEASE_VERSION: ${{ needs.set-release-version.outputs.RELEASE_VERSION }} | |
| PLATFORM_MATRIX: ${{ needs.load-platform-matrix.outputs.PLATFORM_MATRIX }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Ruby | |
| uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: "3.3" | |
| bundler-cache: true | |
| - name: Download all native library artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: native-lib-* | |
| path: native-libs | |
| - name: Organize native libraries for gem | |
| run: | | |
| echo "Creating native library directories from matrix..." | |
| for target in $(echo '${{ env.PLATFORM_MATRIX }}' | jq -r '.[].TARGET'); do | |
| echo "Creating directory for $target" | |
| mkdir -p "lib/valkey/native/$target" | |
| echo "Copying native library for $target" | |
| cp "native-libs/native-lib-$target/libglide_ffi.so" "lib/valkey/native/$target/" | |
| done | |
| echo "Native libraries organized:" | |
| find lib/valkey/native -type f -exec ls -lh {} \; | |
| - name: Update gem version | |
| run: | | |
| sed -i "s/VERSION = .*/VERSION = \"${{ env.RELEASE_VERSION }}\"/" lib/valkey/version.rb | |
| echo "Updated version.rb:" | |
| cat lib/valkey/version.rb | |
| - name: Update gemspec to include native libraries | |
| run: | | |
| # The gemspec uses git ls-files, but native libs aren't in git | |
| # We need to modify the gemspec to include them | |
| ruby -e ' | |
| content = File.read("valkey.gemspec") | |
| # Find the spec.files assignment and add native libs | |
| new_content = content.gsub( | |
| /spec\.files = IO\.popen.*?end\s*end/m, | |
| <<~RUBY.chomp | |
| spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| | |
| ls.readlines("\\x0", chomp: true).reject do |f| | |
| (f == gemspec) || | |
| f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile valkey-glide/]) | |
| end | |
| end + Dir.glob("lib/valkey/native/**/*").reject { |f| File.directory?(f) } | |
| RUBY | |
| ) | |
| File.write("valkey.gemspec", new_content) | |
| ' | |
| echo "Updated gemspec:" | |
| cat valkey.gemspec | |
| - name: Build gem | |
| id: build-gem | |
| run: | | |
| gem build valkey.gemspec | |
| echo "Built gem:" | |
| ls -lh *.gem | |
| # Capture the actual gem filename (RubyGems may transform version format) | |
| GEM_FILE=$(ls *.gem | head -1) | |
| echo "GEM_FILE=${GEM_FILE}" >> $GITHUB_OUTPUT | |
| echo "Gem file: ${GEM_FILE}" | |
| - name: Verify gem contents | |
| run: | | |
| gem unpack ${{ steps.build-gem.outputs.GEM_FILE }} --target=gem-contents | |
| echo "Gem contents:" | |
| find gem-contents -type f | head -50 | |
| echo "" | |
| echo "Native libraries in gem:" | |
| find gem-contents -name "libglide_ffi.*" -exec ls -lh {} \; | |
| - name: Upload gem artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ruby-gem | |
| path: "*.gem" | |
| if-no-files-found: error | |
| - name: Publish to RubyGems | |
| id: publish | |
| if: ${{ github.event_name == 'push' || inputs.publish_gem == true }} | |
| env: | |
| GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} | |
| run: | | |
| gem push ${{ steps.build-gem.outputs.GEM_FILE }} | |
| echo "published=true" >> $GITHUB_OUTPUT | |
| outputs: | |
| published: ${{ steps.publish.outputs.published || 'false' }} | |
| test-gem: | |
| needs: [set-release-version, load-platform-matrix, build-and-publish-gem] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| host: ${{ fromJson(needs.load-platform-matrix.outputs.PLATFORM_MATRIX) }} | |
| runs-on: ${{ matrix.host.RUNNER }} | |
| env: | |
| RELEASE_VERSION: ${{ needs.set-release-version.outputs.RELEASE_VERSION }} | |
| steps: | |
| - name: Checkout (for test files) | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Ruby | |
| uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: "3.3" | |
| bundler-cache: false # We'll handle bundle install manually | |
| - name: Install test dependencies | |
| env: | |
| TEST_INSTALLED_GEM: "true" | |
| run: | | |
| # With TEST_INSTALLED_GEM, Gemfile skips gemspec so we only get test deps | |
| bundle install | |
| - name: Start Valkey | |
| run: | | |
| docker run -d --name valkey-test -p 6379:6379 valkey/valkey:8 | |
| sleep 5 | |
| docker exec valkey-test valkey-cli ping | |
| - name: Download gem artifact (unpublished) | |
| if: ${{ needs.build-and-publish-gem.outputs.published != 'true' }} | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ruby-gem | |
| path: . | |
| - name: Install gem from artifact (unpublished) | |
| if: ${{ needs.build-and-publish-gem.outputs.published != 'true' }} | |
| run: | | |
| GEM_FILE=$(ls *.gem | head -1) | |
| echo "Installing gem from artifact: ${GEM_FILE}" | |
| gem install "${GEM_FILE}" | |
| echo "Installed gem:" | |
| gem list valkey | |
| - name: Install gem from RubyGems (published) | |
| if: ${{ needs.build-and-publish-gem.outputs.published == 'true' }} | |
| run: | | |
| echo "Installing published gem from RubyGems..." | |
| # Wait a bit for RubyGems to index the new version | |
| sleep 30 | |
| gem install valkey-rb --version "${{ env.RELEASE_VERSION }}" || gem install valkey-rb --pre | |
| echo "Installed gem:" | |
| gem list valkey | |
| - name: Test gem loading | |
| run: | | |
| ruby -e " | |
| require 'valkey' | |
| puts 'Valkey gem loaded successfully!' | |
| puts 'Platform info:' | |
| info = Valkey::Bindings.platform_info | |
| puts \" OS: #{info[:os]}\" | |
| puts \" Arch: #{info[:arch]}\" | |
| puts \" Platform dir: #{info[:platform_dir]}\" | |
| " | |
| - name: Run standalone tests | |
| env: | |
| TEST_INSTALLED_GEM: "true" | |
| run: | | |
| # Use bundle exec to get test dependencies (minitest, etc.) | |
| # but TEST_INSTALLED_GEM ensures we use the installed gem, not local lib/ | |
| bundle exec rake test:valkey | |
| - name: Stop Valkey | |
| if: always() | |
| run: docker stop valkey-test && docker rm valkey-test || true |