diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0ebbc99..ef724e7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: bazelrc: common --announce_rc --color=yes repository-cache: true - run: bazel run :buildifier.check - - run: bazel test ... + - run: bazel test --verbose_failures --test_output=errors --test_summary=detailed ... - if: failure() && runner.debug == '1' uses: mxschmitt/action-tmate@v3 diff --git a/MODULE.bazel b/MODULE.bazel index 9bdc7067..ab58c85b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -12,14 +12,16 @@ module( # the versions resolved in users repositories. bazel_dep(name = "bazel_features", version = "1.9.0") bazel_dep(name = "bazel_skylib", version = "1.3.0") +bazel_dep(name = "cgrindel_bazel_starlib", version = "0.27.0") bazel_dep(name = "platforms", version = "0.0.5") bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "rules_java", version = "7.2.0") +bazel_dep(name = "rules_shell", version = "0.4.1") +bazel_dep(name = "buildifier_prebuilt", version = "8.2.1.1") # Ruleset development dependencies. bazel_dep(name = "aspect_bazel_lib", version = "2.22.0", dev_dependency = True) bazel_dep(name = "bazel_skylib_gazelle_plugin", version = "1.9.0", dev_dependency = True) -bazel_dep(name = "buildifier_prebuilt", version = "8.2.1.1", dev_dependency = True) bazel_dep(name = "gazelle", version = "0.47.0", dev_dependency = True) bazel_dep(name = "rules_go", version = "0.59.0", dev_dependency = True) bazel_dep(name = "stardoc", version = "0.8.0", dev_dependency = True) diff --git a/README.md b/README.md index ca4ca3ff..dc7c7f91 100755 --- a/README.md +++ b/README.md @@ -130,24 +130,23 @@ For faster MRI installation on Linux and macOS, you can use prebuilt Ruby binaries from [rv-ruby][19] instead of compiling from source. This significantly reduces installation time and ensures consistent, portable Ruby environments. -**WORKSPACE:** +**Configure rv-ruby Downloads** -```bazel -load("@rules_ruby//ruby:deps.bzl", "rb_register_toolchains") +To securely download and properly cache the Ruby binaries, the `ruby.toolchain` +declaration must be updated with the `rv_version` and `rv_checksums` attributes. -rb_register_toolchains( - version = "3.4.8", - rv_version = "20251225", - rv_checksums = { - "linux-x86_64": "f36cef10365d370e0867f0c3ac36e457a26ab04f3cfbbd7edb227a18e6e9b3c3", - "linux-arm64": "0c08c35a99f10817643d548f98012268c5433ae25a737ab4d6751336108a941d", - "macos-x86_64": "e9da39082d1dd8502d322c850924d929bc45b7a1e35da593a5606c00673218d4", - "macos-arm64": "cd9d7a1428076bfcc6c2ca3c0eb69b8e671e9b48afb4c351fa4a84927841ffef", - }, -) +We have provided the `generate_rv_checksums` utility to add/update these +attributes for you. The utility needs to know the `rv-ruby` version to use +(https://github.com/spinel-coop/rv-ruby/releases) and the version of Ruby to +download. By default, it will use the Ruby version specified in the +`.ruby-version` file. + +```bash +bazel run @rules_ruby//tools/generate_rv_checksums -- 20251225 ``` -**Bzlmod:** +After running the utility, the toolchain declaration in your `MODULE.bazel` +should look something like the following: ```bazel ruby = use_extension("@rules_ruby//ruby:extensions.bzl", "ruby") @@ -156,26 +155,39 @@ ruby.toolchain( version_file = "//:.ruby-version", rv_version = "20251225", rv_checksums = { - "linux-x86_64": "f36cef10365d370e0867f0c3ac36e457a26ab04f3cfbbd7edb227a18e6e9b3c3", "linux-arm64": "0c08c35a99f10817643d548f98012268c5433ae25a737ab4d6751336108a941d", - "macos-x86_64": "e9da39082d1dd8502d322c850924d929bc45b7a1e35da593a5606c00673218d4", + "linux-x86_64": "f36cef10365d370e0867f0c3ac36e457a26ab04f3cfbbd7edb227a18e6e9b3c3", "macos-arm64": "cd9d7a1428076bfcc6c2ca3c0eb69b8e671e9b48afb4c351fa4a84927841ffef", + "macos-x86_64": "e9da39082d1dd8502d322c850924d929bc45b7a1e35da593a5606c00673218d4", }, ) ``` -**Important:** When using rv-ruby, you must exclude default gems with C extensions -from `rb_bundle_fetch` as these are pre-compiled in the rv-ruby binary: +**Configure Excluded Gems** + +When using `rv-ruby`, you must exclude _default_ gems with C extensions from +`bundle_fetch` as these are pre-compiled in the `rv-ruby` binary. You may see +compilation errors if you do not exclude these gems. + +We have provided the `generate_excluded_gems` utility to update the declaration +for you. + +```bash +bazel run @rules_ruby//tools/generate_excluded_gems +``` + +The utility reads the Ruby version being used and checks +https://raw.githubusercontent.com/janlelis/stdgems/main/default_gems.json to +determine which gems should be excluded. The utility adds/updates the +`excluded_gems` attribute with the correct list of gems. The `bundle_fetch` +declaration will look something like the following: ```bazel -rb_bundle_fetch( +ruby.bundle_fetch( name = "bundle", gemfile = "//:Gemfile", gemfile_lock = "//:Gemfile.lock", excluded_gems = [ - # Default gems with C extensions from https://stdgems.org/3.4.8 - # These are pre-compiled in rv-ruby with portable dependencies. - # IMPORTANT: These gems must also be pinned in your Gemfile. "date", "digest", "etc", "fcntl", "fiddle", "io-console", "io-nonblock", "io-wait", "json", "openssl", "pathname", "prism", "psych", @@ -184,15 +196,20 @@ rb_bundle_fetch( ) ``` -Find the list of default gems for your Ruby version at https://stdgems.org/\ -(e.g., https://stdgems.org/3.4.8 for Ruby 3.4.8). Only exclude gems with C -extensions. Bundled gems should NOT be excluded. +> [!NOTE] +> You can find an HTML-rendered list of the default gems for a Ruby version at +> https://stdgems.org/\ (e.g., https://stdgems.org/3.4.8 for Ruby +> 3.4.8). **Notes:** -- rv-ruby is only supported on Linux and macOS (x86_64 and arm64). +- `rv-ruby` is only supported on Linux and macOS (x86_64 and arm64). - On Windows, the toolchain automatically falls back to RubyInstaller. -- Find available rv-ruby releases at https://github.com/spinel-coop/rv-ruby/releases +- Find available `rv-ruby` releases at + https://github.com/spinel-coop/rv-ruby/releases +- The utilities support `--name` to target specific toolchains/bundles and + `--module-bazel` to specify a custom MODULE.bazel path. +- Run utilities with `--dry-run` to preview changes without modifying files. ### JRuby diff --git a/tools/generate_excluded_gems/BUILD.bazel b/tools/generate_excluded_gems/BUILD.bazel new file mode 100644 index 00000000..5a8331a4 --- /dev/null +++ b/tools/generate_excluded_gems/BUILD.bazel @@ -0,0 +1,48 @@ +load("@rules_shell//shell:sh_binary.bzl", "sh_binary") +load("@rules_shell//shell:sh_test.bzl", "sh_test") + +sh_binary( + name = "generate_excluded_gems", + srcs = ["generate_excluded_gems.sh"], + data = [ + "@buildifier_prebuilt//buildozer", + ], + visibility = ["//visibility:public"], + deps = [ + "@cgrindel_bazel_starlib//shlib/lib:fail", + "@rules_shell//shell/runfiles", + ], +) + +sh_test( + name = "generate_excluded_gems_test", + srcs = ["generate_excluded_gems_test.sh"], + data = [ + "testdata/default_gems.json", + "testdata/expected_excluded_gems_output.txt", + ":generate_excluded_gems", + ], + deps = [ + "@cgrindel_bazel_starlib//shlib/lib:assertions", + "@rules_shell//shell/runfiles", + ], +) + +sh_test( + name = "generate_excluded_gems_integration_test", + srcs = ["generate_excluded_gems_integration_test.sh"], + data = [ + "testdata/default_gems.json", + ":generate_excluded_gems", + ], + deps = [ + "@cgrindel_bazel_starlib//shlib/lib:assertions", + "@rules_shell//shell/runfiles", + ], +) + +filegroup( + name = "all_files", + srcs = glob(["**/*"]), + visibility = ["//:__subpackages__"], +) diff --git a/tools/generate_excluded_gems/generate_excluded_gems.sh b/tools/generate_excluded_gems/generate_excluded_gems.sh new file mode 100755 index 00000000..5887529b --- /dev/null +++ b/tools/generate_excluded_gems/generate_excluded_gems.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash + +# Generates excluded_gems for ruby.bundle_fetch() and updates MODULE.bazel. + +# --- begin runfiles.bash initialization v3 --- +# Copy-pasted from the Bazel Bash runfiles library v3. +set -uo pipefail +set +e +f=bazel_tools/tools/bash/runfiles/runfiles.bash +# shellcheck disable=SC1090 +source "${RUNFILES_DIR:-/dev/null}/${f}" 2>/dev/null \ + || source "$(grep -sm1 "^${f} " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null \ + || source "$0.runfiles/${f}" 2>/dev/null \ + || source "$(grep -sm1 "^${f} " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || source "$(grep -sm1 "^${f} " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || { + echo >&2 "ERROR: ${BASH_SOURCE[0]} cannot find ${f}" + exit 1 + } +f= +set -e +# --- end runfiles.bash initialization v3 --- + +# MARK - Dependencies + +fail_sh_location=cgrindel_bazel_starlib/shlib/lib/fail.sh +fail_sh="$(rlocation "${fail_sh_location}")" \ + || (echo >&2 "Failed to locate ${fail_sh_location}" && exit 1) +# shellcheck disable=SC1090 +source "${fail_sh}" + +# Locate buildozer via runfiles +buildozer_location=buildifier_prebuilt/buildozer/buildozer +buildozer="$(rlocation "${buildozer_location}")" \ + || (echo >&2 "Failed to locate ${buildozer_location}" && exit 1) + +# MARK - Default Values + +# Default values +dry_run=false +name="bundle" +module_bazel="${BUILD_WORKSPACE_DIRECTORY:-.}/MODULE.bazel" +ruby_version="" + +# MARK - Functions + +# Read .ruby-version file +read_ruby_version() { + local version_file="${BUILD_WORKSPACE_DIRECTORY}/.ruby-version" + if [[ ! -f ${version_file} ]]; then + fail "Error: .ruby-version not found and --ruby-version not specified" + fi + tr -d '[:space:]' <"${version_file}" +} + +# Extract minor version from a Ruby version string (e.g., 3.4.8 -> 3.4) +# Ruby uses MAJOR.MINOR.PATCH versioning, and stdgems data is organized by +# minor version since default gems are typically consistent within a minor +# release series. +get_minor_version() { + local version="$1" + echo "${version}" | cut -d. -f1,2 +} + +# MARK - Argument Handling + +# Parse arguments +while (("$#")); do + case "${1}" in + --dry-run) + dry_run=true + shift + ;; + --ruby-version) + ruby_version="${2}" + shift 2 + ;; + --name) + name="${2}" + shift 2 + ;; + --module-bazel) + module_bazel="${2}" + shift 2 + ;; + -*) + fail "Error: Unknown option: ${1}" + ;; + *) + fail "Error: Unexpected argument: ${1}" + ;; + esac +done + +# Get Ruby version +if [[ -z ${ruby_version} ]]; then + ruby_version="$(read_ruby_version)" +fi + +# Get minor version +minor_version=$(get_minor_version "${ruby_version}") + +# MARK - Retrieve stdgems data + +# Fetch stdgems data +stdgems_url="${STDGEMS_URL:-https://raw.githubusercontent.com/janlelis/stdgems/main/default_gems.json}" +response=$(curl -sL --max-time 30 "${stdgems_url}") + +# Filter for native gems that exist for this Ruby version +# The jq query: +# 1. Select gems where native == true +# 2. Select gems where versions contains the minor version key +# 3. Extract the gem name +# 4. Sort +excluded_gems=$(echo "${response}" | jq -r --arg version "${minor_version}" \ + '.gems[] | select(.native == true) | select(.versions | has($version)) | .gem' \ + | sort) + +# Check if we found any gems +if [[ -z ${excluded_gems} ]]; then + fail <<-EOT +Error: No native gems found for Ruby ${ruby_version} (${minor_version}) +This Ruby version may not be supported in stdgems data +EOT +fi + +# MARK - Update MODULE.bazel + +# Generate output for dry-run or display +output="excluded_gems = [\n" +while IFS= read -r gem; do + output+=" \"${gem}\",\n" +done <<<"${excluded_gems}" +output+="]," + +if [[ ${dry_run} == "true" ]]; then + # Dry-run: just output the excluded gems + echo -e "${output}" + exit 0 +fi + +# Construct list of gems for buildozer 'add' command +# Convert newline-separated list to space-separated +gem_list="" +while IFS= read -r gem; do + gem_list+=" ${gem}" +done <<<"${excluded_gems}" + +# Update MODULE.bazel using buildozer +buildozer_cmd=( + "${buildozer}" + -types ruby.bundle_fetch + "remove excluded_gems" + "add excluded_gems${gem_list}" + "${module_bazel}:${name}" +) +if ! "${buildozer_cmd[@]}" 2>/dev/null; then + fail <<-EOT +Failed to update ${module_bazel} + +Buildozer command failed. This could mean: +- The file doesn't exist at ${module_bazel} +- No ruby.bundle_fetch() call was found with name="${name}" +- The file has syntax errors + +You can use --dry-run to see what would be updated: +$(echo -e "${output}") +EOT +fi + +echo "Successfully updated excluded_gems in ${module_bazel}" diff --git a/tools/generate_excluded_gems/generate_excluded_gems_integration_test.sh b/tools/generate_excluded_gems/generate_excluded_gems_integration_test.sh new file mode 100755 index 00000000..4f6f9875 --- /dev/null +++ b/tools/generate_excluded_gems/generate_excluded_gems_integration_test.sh @@ -0,0 +1,175 @@ +#!/usr/bin/env bash + +# Integration tests for generate_excluded_gems.sh that verify buildozer +# updates work + +# --- begin runfiles.bash initialization v3 --- +# Copy-pasted from the Bazel Bash runfiles library v3. +set -uo pipefail +set +e +f=bazel_tools/tools/bash/runfiles/runfiles.bash +# shellcheck disable=SC1090 +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null \ + || source "$0.runfiles/$f" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || { + echo >&2 "ERROR: ${BASH_SOURCE[0]} cannot find $f" + exit 1 + } +f= +set -e +# --- end runfiles.bash initialization v3 --- + +# MARK - Locate Deps + +assertions_sh_location=cgrindel_bazel_starlib/shlib/lib/assertions.sh +assertions_sh="$(rlocation "${assertions_sh_location}")" \ + || (echo >&2 "Failed to locate ${assertions_sh_location}" && exit 1) +# shellcheck disable=SC1090 +source "${assertions_sh}" + +generate_excluded_gems_location=rules_ruby/tools/generate_excluded_gems/generate_excluded_gems.sh +generate_excluded_gems="$(rlocation "${generate_excluded_gems_location}")" + +mock_response_location=rules_ruby/tools/generate_excluded_gems/testdata/default_gems.json +mock_response="$(rlocation "${mock_response_location}")" + +# MARK - Cleanup + +# Collect temp directories for cleanup +temp_dirs=() +cleanup_temp_dirs() { + for dir in "${temp_dirs[@]:-}"; do + rm -rf "${dir}" + done +} +trap cleanup_temp_dirs EXIT + +# MARK - Tests + +# Test: Buildozer updates MODULE.bazel correctly +test_buildozer_updates() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Create a minimal Bazel workspace + cat >WORKSPACE.bazel <<'EOF' +# Empty workspace for testing +EOF + + cat >MODULE.bazel <<'EOF' +module(name = "test_workspace") + +ruby = use_extension("@rules_ruby//ruby:extensions.bzl", "ruby") + +ruby.bundle_fetch( + name = "bundle", + gemfile = "//:Gemfile", + gemfile_lock = "//:Gemfile.lock", +) + +use_repo(ruby, "bundle") +EOF + + # Mock the stdgems response + export STDGEMS_URL="file://${mock_response}" + + # Run the script WITHOUT --dry-run + "${generate_excluded_gems}" --ruby-version 3.4.8 --module-bazel MODULE.bazel + + # Verify MODULE.bazel was updated + local module_content + module_content=$(cat MODULE.bazel) + + # Check excluded_gems was set with expected gems + assert_match "excluded_gems = \[" "${module_content}" \ + "MODULE.bazel should contain excluded_gems list" + assert_match '"date"' "${module_content}" \ + "MODULE.bazel should contain date gem" + assert_match '"digest"' "${module_content}" \ + "MODULE.bazel should contain digest gem" + assert_match '"json"' "${module_content}" \ + "MODULE.bazel should contain json gem" + assert_match '"psych"' "${module_content}" \ + "MODULE.bazel should contain psych gem" + + # Verify gemfile was NOT changed + assert_match 'gemfile = "//:Gemfile"' "${module_content}" \ + "MODULE.bazel should preserve existing gemfile" + +} + +# Test: Buildozer updates only target bundle by name +test_buildozer_name_filtering() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Create workspace with multiple bundle_fetch calls + cat >WORKSPACE.bazel <<'EOF' +# Empty workspace for testing +EOF + + cat >MODULE.bazel <<'EOF' +module(name = "test_workspace") + +ruby = use_extension("@rules_ruby//ruby:extensions.bzl", "ruby") + +ruby.bundle_fetch( + name = "bundle", + gemfile = "//:Gemfile", + gemfile_lock = "//:Gemfile.lock", +) + +ruby.bundle_fetch( + name = "bundle_alt", + gemfile = "//alt:Gemfile", + gemfile_lock = "//alt:Gemfile.lock", +) + +use_repo(ruby, "bundle", "bundle_alt") +EOF + + # Mock the stdgems response + export STDGEMS_URL="file://${mock_response}" + + # Run the script for "bundle_alt" + "${generate_excluded_gems}" --ruby-version 3.4.8 --name bundle_alt \ + --module-bazel MODULE.bazel + + # Verify MODULE.bazel was updated + local module_content + module_content=$(cat MODULE.bazel) + + # Count how many excluded_gems assignments there are + local excluded_gems_count + excluded_gems_count=$(grep -c "excluded_gems = \[" MODULE.bazel || true) + + assert_equal "1" "${excluded_gems_count}" \ + 'Should have exactly one excluded_gems list (only in bundle_alt)' + + # Verify the first bundle_fetch (bundle) was NOT updated + # Check that there's still a bundle_fetch without excluded_gems before + # bundle_alt + if ! grep -B5 'name = "bundle_alt"' MODULE.bazel \ + | grep -q 'name = "bundle"'; then + fail "First bundle_fetch should still exist" + fi + +} + +# Run all tests +test_buildozer_updates +test_buildozer_name_filtering + diff --git a/tools/generate_excluded_gems/generate_excluded_gems_test.sh b/tools/generate_excluded_gems/generate_excluded_gems_test.sh new file mode 100755 index 00000000..2beb5ae7 --- /dev/null +++ b/tools/generate_excluded_gems/generate_excluded_gems_test.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env bash + +# Tests for generate_excluded_gems.sh + +# --- begin runfiles.bash initialization v3 --- +# Copy-pasted from the Bazel Bash runfiles library v3. +set -uo pipefail +set +e +f=bazel_tools/tools/bash/runfiles/runfiles.bash +# shellcheck disable=SC1090 +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null \ + || source "$0.runfiles/$f" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || { + echo >&2 "ERROR: ${BASH_SOURCE[0]} cannot find $f" + exit 1 + } +f= +set -e +# --- end runfiles.bash initialization v3 --- + +# MARK - Locate Deps + +assertions_sh_location=cgrindel_bazel_starlib/shlib/lib/assertions.sh +assertions_sh="$(rlocation "${assertions_sh_location}")" \ + || (echo >&2 "Failed to locate ${assertions_sh_location}" && exit 1) +# shellcheck disable=SC1090 +source "${assertions_sh}" + +generate_excluded_gems_location=rules_ruby/tools/generate_excluded_gems/generate_excluded_gems.sh +generate_excluded_gems="$(rlocation "${generate_excluded_gems_location}")" + +mock_response_location=rules_ruby/tools/generate_excluded_gems/testdata/default_gems.json +mock_response="$(rlocation "${mock_response_location}")" + +expected_output_location=rules_ruby/tools/generate_excluded_gems/testdata/expected_excluded_gems_output.txt +expected_output="$(rlocation "${expected_output_location}")" + +# MARK - Cleanup + +# Collect temp directories for cleanup +temp_dirs=() +cleanup_temp_dirs() { + for dir in "${temp_dirs[@]:-}"; do + rm -rf "${dir}" + done +} +trap cleanup_temp_dirs EXIT + +# MARK - Tests + +# Test 1: Basic dry-run with .ruby-version +test_basic_dry_run() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Setup .ruby-version + echo "3.4.8" >.ruby-version + + # Mock the stdgems response + export STDGEMS_URL="file://${mock_response}" + + # Run the script + local output + output=$("${generate_excluded_gems}" --dry-run) + + # Verify output matches expected + local expected + expected=$(cat "${expected_output}") + + assert_equal "${expected}" "${output}" \ + "Output should match expected excluded gems" + +} + +# Test 2: Explicit Ruby version +test_explicit_ruby_version() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Mock the stdgems response + export STDGEMS_URL="file://${mock_response}" + + # Run the script with explicit version + local output + output=$("${generate_excluded_gems}" --ruby-version 3.4.8 --dry-run) + + # Verify output contains expected gems + assert_match "psych" "${output}" "Output should contain psych" + assert_match "json" "${output}" "Output should contain json" + + # Should NOT contain non-native gems + if echo "${output}" | grep -q "csv"; then + fail "Output should not contain csv (non-native gem)" + fi + +} + +# Test 3: Missing .ruby-version without --ruby-version +test_missing_ruby_version() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Don't create .ruby-version file + + # Mock the stdgems response + export STDGEMS_URL="file://${mock_response}" + + # Run the script and expect failure + if "${generate_excluded_gems}" --dry-run 2>/dev/null; then + fail "Should have failed when .ruby-version is missing" + fi + +} + +# Test 4: Ruby version not in stdgems data +test_unsupported_ruby_version() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Mock the stdgems response + export STDGEMS_URL="file://${mock_response}" + + # Run the script with unsupported version and expect failure + generate_excluded_gems_cmd=( + "${generate_excluded_gems}" --ruby-version 1.0.0 --dry-run + ) + if "${generate_excluded_gems_cmd[@]}" 2>/dev/null; then + fail "Should have failed for unsupported Ruby version" + fi + +} + +# Test 5: Ruby 3.3 version +test_ruby_33_version() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Mock the stdgems response + export STDGEMS_URL="file://${mock_response}" + + # Run the script with 3.3.x version + local output + output=$("${generate_excluded_gems}" --ruby-version 3.3.0 --dry-run) + + # Verify output contains expected gems (same ones exist for 3.3) + assert_match "psych" "${output}" "Output should contain psych" + assert_match "json" "${output}" "Output should contain json" + +} + +# Run all tests +test_basic_dry_run +test_explicit_ruby_version +test_missing_ruby_version +test_unsupported_ruby_version +test_ruby_33_version + diff --git a/tools/generate_excluded_gems/testdata/default_gems.json b/tools/generate_excluded_gems/testdata/default_gems.json new file mode 100644 index 00000000..2cdcea03 --- /dev/null +++ b/tools/generate_excluded_gems/testdata/default_gems.json @@ -0,0 +1,69 @@ +{ + "version": "5.24.0", + "gems": [ + { + "gem": "date", + "native": true, + "versions": { + "3.4": "3.4.1", + "3.3": "3.3.4" + } + }, + { + "gem": "digest", + "native": true, + "versions": { + "3.4": "3.2.0", + "3.3": "3.1.1" + } + }, + { + "gem": "etc", + "native": true, + "versions": { + "3.4": "1.4.6", + "3.3": "1.4.3" + } + }, + { + "gem": "json", + "native": true, + "versions": { + "3.4": "2.9.1", + "3.3": "2.7.2" + } + }, + { + "gem": "psych", + "native": true, + "versions": { + "3.4": "5.2.2", + "3.3": "5.1.2" + } + }, + { + "gem": "stringio", + "native": true, + "versions": { + "3.4": "3.1.2", + "3.3": "3.1.0" + } + }, + { + "gem": "csv", + "native": false, + "versions": { + "3.4": "3.3.2", + "3.3": "3.2.8" + } + }, + { + "gem": "erb", + "native": false, + "versions": { + "3.4": "6.0.1", + "3.3": "4.0.4" + } + } + ] +} diff --git a/tools/generate_excluded_gems/testdata/expected_excluded_gems_output.txt b/tools/generate_excluded_gems/testdata/expected_excluded_gems_output.txt new file mode 100644 index 00000000..b6c09b20 --- /dev/null +++ b/tools/generate_excluded_gems/testdata/expected_excluded_gems_output.txt @@ -0,0 +1,8 @@ +excluded_gems = [ + "date", + "digest", + "etc", + "json", + "psych", + "stringio", +], diff --git a/tools/generate_rv_checksums/BUILD.bazel b/tools/generate_rv_checksums/BUILD.bazel new file mode 100644 index 00000000..e2490d67 --- /dev/null +++ b/tools/generate_rv_checksums/BUILD.bazel @@ -0,0 +1,50 @@ +load("@rules_shell//shell:sh_binary.bzl", "sh_binary") +load("@rules_shell//shell:sh_test.bzl", "sh_test") + +sh_binary( + name = "generate_rv_checksums", + srcs = ["generate_rv_checksums.sh"], + data = [ + "@buildifier_prebuilt//buildozer", + ], + visibility = ["//visibility:public"], + deps = [ + "@cgrindel_bazel_starlib//shlib/lib:fail", + "@rules_shell//shell/runfiles", + ], +) + +sh_test( + name = "generate_rv_checksums_test", + srcs = ["generate_rv_checksums_test.sh"], + data = [ + "testdata/20251225", + "testdata/expected_checksums_output.txt", + "testdata/rv_ruby_release_response.json", + ":generate_rv_checksums", + ], + deps = [ + "@cgrindel_bazel_starlib//shlib/lib:assertions", + "@rules_shell//shell/runfiles", + ], +) + +sh_test( + name = "generate_rv_checksums_integration_test", + srcs = ["generate_rv_checksums_integration_test.sh"], + data = [ + "testdata/20251225", + "testdata/rv_ruby_release_response.json", + ":generate_rv_checksums", + ], + deps = [ + "@cgrindel_bazel_starlib//shlib/lib:assertions", + "@rules_shell//shell/runfiles", + ], +) + +filegroup( + name = "all_files", + srcs = glob(["**/*"]), + visibility = ["//:__subpackages__"], +) diff --git a/tools/generate_rv_checksums/generate_rv_checksums.sh b/tools/generate_rv_checksums/generate_rv_checksums.sh new file mode 100755 index 00000000..b13311a6 --- /dev/null +++ b/tools/generate_rv_checksums/generate_rv_checksums.sh @@ -0,0 +1,221 @@ +#!/usr/bin/env bash + +# Generates rv_checksums for ruby.toolchain() and updates MODULE.bazel. + +# --- begin runfiles.bash initialization v3 --- +# Copy-pasted from the Bazel Bash runfiles library v3. +set -uo pipefail +set +e +f=bazel_tools/tools/bash/runfiles/runfiles.bash +# shellcheck disable=SC1090 +source "${RUNFILES_DIR:-/dev/null}/${f}" 2>/dev/null \ + || source "$(grep -sm1 "^${f} " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null \ + || source "$0.runfiles/${f}" 2>/dev/null \ + || source "$(grep -sm1 "^${f} " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || source "$(grep -sm1 "^${f} " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || { + echo >&2 "ERROR: ${BASH_SOURCE[0]} cannot find ${f}" + exit 1 + } +f= +set -e +# --- end runfiles.bash initialization v3 --- + +# MARK - Dependencies + +fail_sh_location=cgrindel_bazel_starlib/shlib/lib/fail.sh +fail_sh="$(rlocation "${fail_sh_location}")" \ + || (echo >&2 "Failed to locate ${fail_sh_location}" && exit 1) +# shellcheck disable=SC1090 +source "${fail_sh}" + +# Locate buildozer via runfiles +buildozer_location=buildifier_prebuilt/buildozer/buildozer +buildozer="$(rlocation "${buildozer_location}")" \ + || (echo >&2 "Failed to locate ${buildozer_location}" && exit 1) + +# MARK - Default Values + +# Default values +dry_run=false +name="ruby" +module_bazel="${BUILD_WORKSPACE_DIRECTORY:-.}/MODULE.bazel" +ruby_version="" +rv_version="" + +# Map rv-ruby platform names to rules_ruby platform keys +declare -A PLATFORM_MAP=( + ["arm64_linux"]="linux-arm64" + ["x86_64_linux"]="linux-x86_64" + ["arm64_sonoma"]="macos-arm64" + ["ventura"]="macos-x86_64" +) + +# MARK - Functions + +# Read .ruby-version file +read_ruby_version() { + local version_file="${BUILD_WORKSPACE_DIRECTORY}/.ruby-version" + if [[ ! -f ${version_file} ]]; then + fail "Error: .ruby-version not found and --ruby-version not specified" + fi + tr -d '[:space:]' <"${version_file}" +} + +# MARK - Argument Handling + +# Parse arguments +args=() +while (("$#")); do + case "${1}" in + --dry-run) + dry_run=true + shift + ;; + --ruby-version) + ruby_version="${2}" + shift 2 + ;; + --name) + name="${2}" + shift 2 + ;; + --module-bazel) + module_bazel="${2}" + shift 2 + ;; + -*) + fail "Error: Unknown option: ${1}" + ;; + *) + args+=("${1}") + shift + ;; + esac +done + +# Check for required positional argument +if [[ ${#args[@]} -eq 0 ]]; then + fail <<-EOT +Error: rv_version is required +Usage: ${0} [OPTIONS] + +Options: + --ruby-version VERSION + --name NAME + --module-bazel PATH + --dry-run +EOT +fi + +rv_version="${args[0]}" + +# Get Ruby version +if [[ -z ${ruby_version} ]]; then + ruby_version="$(read_ruby_version)" +fi + +# MARK - Retrieve rv-ruby release info + +# Fetch release data from GitHub API +api_url="${RV_RUBY_API_URL:-https://api.github.com/repos/spinel-coop/rv-ruby/releases/tags}/${rv_version}" +response=$(curl -sL --max-time 30 "${api_url}") + +# Check if release was found +if echo "${response}" | jq -e '.message == "Not Found"' >/dev/null 2>&1; then + fail "Error: rv-ruby release ${rv_version} not found" +fi + +# MARK - Extract checksums + +# Extract checksums for each platform +declare -A checksums +found_ruby_version=false + +for rv_platform in "${!PLATFORM_MAP[@]}"; do + platform_key="${PLATFORM_MAP[${rv_platform}]}" + + # Find asset for this Ruby version and platform + asset_name="ruby-${ruby_version}.${rv_platform}.tar.gz" + digest=$(echo "${response}" | jq -r --arg name "${asset_name}" \ + '.assets[] | select(.name == $name) | .digest // ""') + + if [[ -n ${digest} ]]; then + found_ruby_version=true + # Strip "sha256:" prefix if present + checksum="${digest#sha256:}" + checksums["${platform_key}"]="${checksum}" + fi +done + +# Check if we found any assets for this Ruby version +if [[ ${found_ruby_version} != "true" ]]; then + fail <<-EOT +Error: Ruby version ${ruby_version} not found in rv-ruby release ${rv_version} +EOT +fi + +# Check if we have all expected platforms +expected_platforms=("linux-arm64" "linux-x86_64" "macos-arm64" "macos-x86_64") +missing_platforms=() +for platform in "${expected_platforms[@]}"; do + if [[ -z ${checksums[${platform}]:-} ]]; then + missing_platforms+=("${platform}") + fi +done + +if [[ ${#missing_platforms[@]} -gt 0 ]]; then + warn "Warning: Missing platforms in release: ${missing_platforms[*]}" +fi + +# MARK - Update MODULE.bazel + +# Generate output for dry-run or display +output="rv_version = \"${rv_version}\",\n" +output+="rv_checksums = {\n" +for platform in "${expected_platforms[@]}"; do + if [[ -n ${checksums[${platform}]:-} ]]; then + output+=" \"${platform}\": \"${checksums[${platform}]}\",\n" + fi +done +output+="}," + +if [[ ${dry_run} == "true" ]]; then + # Dry-run: just output the version and checksums + echo -e "${output}" + exit 0 +fi + +# Construct dict string for buildozer +dict_str="" +for platform in "${expected_platforms[@]}"; do + if [[ -n ${checksums[${platform}]:-} ]]; then + dict_str+=" ${platform}:${checksums[${platform}]}" + fi +done + +# Update MODULE.bazel using buildozer +# Set both rv_version and rv_checksums +buildozer_cmd=( + "${buildozer}" + -types ruby.toolchain + "set rv_version \"${rv_version}\"" + "remove rv_checksums" + "dict_set rv_checksums ${dict_str}" + "${module_bazel}:${name}" +) +if ! "${buildozer_cmd[@]}" 2>/dev/null; then + fail <<-EOT +Failed to update ${module_bazel} + +Buildozer command failed. This could mean: +- The file doesn't exist at ${module_bazel} +- No ruby.toolchain() call was found with name="${name}" +- The file has syntax errors + +You can use --dry-run to see what would be updated: +$(echo -e "${output}") +EOT +fi + +echo "Successfully updated rv_version and rv_checksums in ${module_bazel}" diff --git a/tools/generate_rv_checksums/generate_rv_checksums_integration_test.sh b/tools/generate_rv_checksums/generate_rv_checksums_integration_test.sh new file mode 100755 index 00000000..69a743ea --- /dev/null +++ b/tools/generate_rv_checksums/generate_rv_checksums_integration_test.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash + +# Integration tests for generate_rv_checksums.sh that verify buildozer updates +# work + +# --- begin runfiles.bash initialization v3 --- +# Copy-pasted from the Bazel Bash runfiles library v3. +set -uo pipefail +set +e +f=bazel_tools/tools/bash/runfiles/runfiles.bash +# shellcheck disable=SC1090 +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null \ + || source "$0.runfiles/$f" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || { + echo >&2 "ERROR: ${BASH_SOURCE[0]} cannot find $f" + exit 1 + } +f= +set -e +# --- end runfiles.bash initialization v3 --- + +# MARK - Locate Deps + +assertions_sh_location=cgrindel_bazel_starlib/shlib/lib/assertions.sh +assertions_sh="$(rlocation "${assertions_sh_location}")" \ + || (echo >&2 "Failed to locate ${assertions_sh_location}" && exit 1) +# shellcheck disable=SC1090 +source "${assertions_sh}" + +generate_rv_checksums_location=rules_ruby/tools/generate_rv_checksums/generate_rv_checksums.sh +generate_rv_checksums="$(rlocation "${generate_rv_checksums_location}")" + +mock_response_location=rules_ruby/tools/generate_rv_checksums/testdata/rv_ruby_release_response.json +mock_response="$(rlocation "${mock_response_location}")" + +# MARK - Cleanup + +# Collect temp directories for cleanup +temp_dirs=() +cleanup_temp_dirs() { + for dir in "${temp_dirs[@]:-}"; do + rm -rf "${dir}" + done +} +trap cleanup_temp_dirs EXIT + +# MARK - Tests + +# Test: Buildozer updates MODULE.bazel correctly +test_buildozer_updates() { + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Create a minimal Bazel workspace + cat >WORKSPACE.bazel <<'EOF' +# Empty workspace for testing +EOF + + cat >MODULE.bazel <<'EOF' +module(name = "test_workspace") + +ruby = use_extension("@rules_ruby//ruby:extensions.bzl", "ruby") + +ruby.toolchain( + name = "ruby", + ruby_version = "3.3.0", +) + +use_repo(ruby, "ruby_toolchains") +EOF + + # Mock the API response + export RV_RUBY_API_URL="file://${mock_response%/*}" + + # Run the script WITHOUT --dry-run + "${generate_rv_checksums}" 20251225 --ruby-version 3.4.8 \ + --module-bazel MODULE.bazel + + # Verify MODULE.bazel was updated + local module_content + module_content=$(cat MODULE.bazel) + + # Check rv_version was set + assert_match 'rv_version = "20251225"' "${module_content}" \ + "MODULE.bazel should contain rv_version" + + # Check rv_checksums was set with all platforms + assert_match "rv_checksums = \{" "${module_content}" \ + "MODULE.bazel should contain rv_checksums" + assert_match \ + '"linux-arm64": "0c08c35a99f10817643d548f98012268c5433ae25a737ab4d6751336108a941d"' \ + "${module_content}" \ + "MODULE.bazel should contain linux-arm64 checksum" + assert_match \ + '"linux-x86_64": "f36cef10365d370e0867f0c3ac36e457a26ab04f3cfbbd7edb227a18e6e9b3c3"' \ + "${module_content}" \ + "MODULE.bazel should contain linux-x86_64 checksum" + assert_match \ + '"macos-arm64": "cd9d7a1428076bfcc6c2ca3c0eb69b8e671e9b48afb4c351fa4a84927841ffef"' \ + "${module_content}" \ + "MODULE.bazel should contain macos-arm64 checksum" + assert_match \ + '"macos-x86_64": "e9da39082d1dd8502d322c850924d929bc45b7a1e35da593a5606c00673218d4"' \ + "${module_content}" \ + "MODULE.bazel should contain macos-x86_64 checksum" + + # Verify ruby_version was NOT changed (we didn't update it) + assert_match 'ruby_version = "3.3.0"' "${module_content}" \ + "MODULE.bazel should preserve existing ruby_version" +} + +# Test: Buildozer updates only target toolchain by name +test_buildozer_name_filtering() { + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Create workspace with multiple toolchains + cat >WORKSPACE.bazel <<'EOF' +# Empty workspace for testing +EOF + + cat >MODULE.bazel <<'EOF' +module(name = "test_workspace") + +ruby = use_extension("@rules_ruby//ruby:extensions.bzl", "ruby") + +ruby.toolchain( + name = "ruby", + ruby_version = "3.3.0", +) + +ruby.toolchain( + name = "ruby_alt", + ruby_version = "3.2.0", +) + +use_repo(ruby, "ruby_toolchains") +EOF + + # Mock the API response + export RV_RUBY_API_URL="file://${mock_response%/*}" + + # Run the script for "ruby_alt" toolchain + "${generate_rv_checksums}" 20251225 --ruby-version 3.4.8 --name ruby_alt \ + --module-bazel MODULE.bazel + + # Verify MODULE.bazel was updated + local module_content + module_content=$(cat MODULE.bazel) + + # Count how many rv_version assignments there are + local rv_version_count + rv_version_count=$(grep -c 'rv_version = "20251225"' MODULE.bazel || true) + + assert_equal "1" "${rv_version_count}" \ + 'Should have exactly one rv_version = "20251225" (only in ruby_alt)' + + # Verify the first toolchain (ruby) was NOT updated + # Check that there's still a toolchain without rv_version before ruby_alt + if ! grep -B5 'name = "ruby_alt"' MODULE.bazel | grep -q 'name = "ruby"'; then + fail "First toolchain should still exist" + fi +} + +# Run all tests +test_buildozer_updates +test_buildozer_name_filtering diff --git a/tools/generate_rv_checksums/generate_rv_checksums_test.sh b/tools/generate_rv_checksums/generate_rv_checksums_test.sh new file mode 100755 index 00000000..fe206c68 --- /dev/null +++ b/tools/generate_rv_checksums/generate_rv_checksums_test.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +# Tests for generate_rv_checksums.sh + +# --- begin runfiles.bash initialization v3 --- +# Copy-pasted from the Bazel Bash runfiles library v3. +set -uo pipefail +set +e +f=bazel_tools/tools/bash/runfiles/runfiles.bash +# shellcheck disable=SC1090 +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null \ + || source "$0.runfiles/$f" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null \ + || { + echo >&2 "ERROR: ${BASH_SOURCE[0]} cannot find $f" + exit 1 + } +f= +set -e +# --- end runfiles.bash initialization v3 --- + +# MARK - Locate Deps + +assertions_sh_location=cgrindel_bazel_starlib/shlib/lib/assertions.sh +assertions_sh="$(rlocation "${assertions_sh_location}")" \ + || (echo >&2 "Failed to locate ${assertions_sh_location}" && exit 1) +# shellcheck disable=SC1090 +source "${assertions_sh}" + +generate_rv_checksums_location=rules_ruby/tools/generate_rv_checksums/generate_rv_checksums.sh +generate_rv_checksums="$(rlocation "${generate_rv_checksums_location}")" + +mock_response_location=rules_ruby/tools/generate_rv_checksums/testdata/rv_ruby_release_response.json +mock_response="$(rlocation "${mock_response_location}")" + +expected_output_location=rules_ruby/tools/generate_rv_checksums/testdata/expected_checksums_output.txt +expected_output="$(rlocation "${expected_output_location}")" + +# MARK - Cleanup + +# Collect temp directories for cleanup +temp_dirs=() +cleanup_temp_dirs() { + for dir in "${temp_dirs[@]:-}"; do + rm -rf "${dir}" + done +} +trap cleanup_temp_dirs EXIT + +# MARK - Tests + +# Test 1: Basic dry-run with .ruby-version +test_basic_dry_run() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Setup .ruby-version + echo "3.4.8" >.ruby-version + + # Mock the API response + export RV_RUBY_API_URL="file://${mock_response%/*}" + + # Run the script + local output + output=$("${generate_rv_checksums}" 20251225 --dry-run) + + # Verify output matches expected + local expected + expected=$(cat "${expected_output}") + + assert_equal "${expected}" "${output}" "Output should match expected checksums" + +} + +# Test 2: Explicit Ruby version +test_explicit_ruby_version() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Mock the API response + export RV_RUBY_API_URL="file://${mock_response%/*}" + + # Run the script with explicit version + local output + output=$("${generate_rv_checksums}" 20251225 --ruby-version 3.4.8 --dry-run) + + # Verify output contains expected checksums + assert_match "linux-arm64" "${output}" "Output should contain linux-arm64" + assert_match "0c08c35a99f10817643d548f98012268c5433ae25a737ab4d6751336108a941d" "${output}" \ + "Output should contain correct checksum" + +} + +# Test 3: Missing .ruby-version without --ruby-version +test_missing_ruby_version() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Don't create .ruby-version file + + # Mock the API response + export RV_RUBY_API_URL="file://${mock_response%/*}" + + # Run the script and expect failure + if "${generate_rv_checksums}" 20251225 --dry-run 2>/dev/null; then + fail "Should have failed when .ruby-version is missing" + fi + +} + +# Test 4: Invalid rv_version +test_invalid_rv_version() { + + local temp_dir + temp_dir="$(mktemp -d)" + temp_dirs+=("${temp_dir}") + + cd "${temp_dir}" + export BUILD_WORKSPACE_DIRECTORY="${temp_dir}" + + # Use a non-existent API URL to simulate 404 + export RV_RUBY_API_URL="file:///nonexistent" + + # Run the script and expect failure + if "${generate_rv_checksums}" 99999999 --ruby-version 3.4.8 --dry-run 2>/dev/null; then + fail "Should have failed for invalid rv_version" + fi + +} + +# Run all tests +test_basic_dry_run +test_explicit_ruby_version +test_missing_ruby_version +test_invalid_rv_version + diff --git a/tools/generate_rv_checksums/testdata/20251225 b/tools/generate_rv_checksums/testdata/20251225 new file mode 120000 index 00000000..ba22c852 --- /dev/null +++ b/tools/generate_rv_checksums/testdata/20251225 @@ -0,0 +1 @@ +rv_ruby_release_response.json \ No newline at end of file diff --git a/tools/generate_rv_checksums/testdata/expected_checksums_output.txt b/tools/generate_rv_checksums/testdata/expected_checksums_output.txt new file mode 100644 index 00000000..d52745ba --- /dev/null +++ b/tools/generate_rv_checksums/testdata/expected_checksums_output.txt @@ -0,0 +1,7 @@ +rv_version = "20251225", +rv_checksums = { + "linux-arm64": "0c08c35a99f10817643d548f98012268c5433ae25a737ab4d6751336108a941d", + "linux-x86_64": "f36cef10365d370e0867f0c3ac36e457a26ab04f3cfbbd7edb227a18e6e9b3c3", + "macos-arm64": "cd9d7a1428076bfcc6c2ca3c0eb69b8e671e9b48afb4c351fa4a84927841ffef", + "macos-x86_64": "e9da39082d1dd8502d322c850924d929bc45b7a1e35da593a5606c00673218d4", +}, diff --git a/tools/generate_rv_checksums/testdata/rv_ruby_release_response.json b/tools/generate_rv_checksums/testdata/rv_ruby_release_response.json new file mode 100644 index 00000000..6d9eb5ad --- /dev/null +++ b/tools/generate_rv_checksums/testdata/rv_ruby_release_response.json @@ -0,0 +1,22 @@ +{ + "tag_name": "20251225", + "name": "20251225", + "assets": [ + { + "name": "ruby-3.4.8.arm64_linux.tar.gz", + "digest": "sha256:0c08c35a99f10817643d548f98012268c5433ae25a737ab4d6751336108a941d" + }, + { + "name": "ruby-3.4.8.x86_64_linux.tar.gz", + "digest": "sha256:f36cef10365d370e0867f0c3ac36e457a26ab04f3cfbbd7edb227a18e6e9b3c3" + }, + { + "name": "ruby-3.4.8.arm64_sonoma.tar.gz", + "digest": "sha256:cd9d7a1428076bfcc6c2ca3c0eb69b8e671e9b48afb4c351fa4a84927841ffef" + }, + { + "name": "ruby-3.4.8.ventura.tar.gz", + "digest": "sha256:e9da39082d1dd8502d322c850924d929bc45b7a1e35da593a5606c00673218d4" + } + ] +}