Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .boringcache.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
workspace = "ruby/ruby"
48 changes: 38 additions & 10 deletions .github/workflows/build-ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ on:
- "versions.yml"
- ".github/workflows/build-ruby.yml"
- "scripts/**"
pull_request:
paths:
- "versions.yml"
- ".github/workflows/build-ruby.yml"
- "scripts/**"
schedule:
# Build weekly on Sundays at 02:00 UTC
- cron: "0 2 * * 0"
Expand All @@ -27,12 +22,33 @@ on:
default: "ubuntu-22.04,ubuntu-24.04,ubuntu-25.04,debian-bookworm,alpine,arch,macos,windows"
type: string

permissions:
contents: read

env:
BORINGCACHE_API_TOKEN: ${{ secrets.BORINGCACHE_API_TOKEN }}
BORINGCACHE_RESTORE_TOKEN: ${{ secrets.BORINGCACHE_RESTORE_TOKEN }}
BORINGCACHE_SAVE_TOKEN: ${{ secrets.BORINGCACHE_SAVE_TOKEN }}
BORINGCACHE_DEFAULT_WORKSPACE: ${{ secrets.BORINGCACHE_DEFAULT_WORKSPACE || 'ruby/ruby' }}

jobs:
token-check:
name: BoringCache Token Check
runs-on: ubuntu-latest
steps:
- name: Require save and restore tokens
run: |
if [ -z "${BORINGCACHE_RESTORE_TOKEN}" ]; then
echo "BORINGCACHE_RESTORE_TOKEN is required for cache lookup/restore." >&2
exit 1
fi

if [ -z "${BORINGCACHE_SAVE_TOKEN}" ]; then
echo "BORINGCACHE_SAVE_TOKEN is required for cache saves." >&2
exit 1
fi

prepare:
needs: token-check
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.setup.outputs.matrix }}
Expand Down Expand Up @@ -181,7 +197,8 @@ jobs:
docker run --rm \
-v "$PWD:/workspace" \
-w /workspace \
-e BORINGCACHE_API_TOKEN="${BORINGCACHE_API_TOKEN}" \
-e BORINGCACHE_RESTORE_TOKEN="${BORINGCACHE_RESTORE_TOKEN}" \
-e BORINGCACHE_SAVE_TOKEN="${BORINGCACHE_SAVE_TOKEN}" \
-e BORINGCACHE_DEFAULT_WORKSPACE="${BORINGCACHE_DEFAULT_WORKSPACE}" \
${{ matrix.container }} \
/bin/sh -c '
Expand Down Expand Up @@ -317,8 +334,6 @@ jobs:
echo "Using native ${{ matrix.arch }} runner for ${{ matrix.platform }}"
make ci-build RUBY_VERSION=${{ matrix.version }} PLATFORM=${{ matrix.platform }} ARCH=${{ matrix.arch }} VARIANTS="${{ matrix.variant_group }}"
'
env:
BORINGCACHE_API_TOKEN: ${{ secrets.BORINGCACHE_API_TOKEN }}

- name: Check if cache exists (Unix)
id: cache-check
Expand Down Expand Up @@ -460,5 +475,18 @@ jobs:
tail -n +11 | \
while read entry; do
echo "Deleting old entry: $entry"
boringcache delete "$WORKSPACE" "$entry" || true
boringcache delete --no-platform --no-git "$WORKSPACE" "$entry" || true
done

build-status:
name: Build Status
needs: [token-check, prepare, build-ruby, cleanup]
runs-on: ubuntu-latest
if: always()
steps:
- name: Verify required jobs
run: |
test "${{ needs.token-check.result }}" = "success"
test "${{ needs.prepare.result }}" = "success"
test "${{ needs.build-ruby.result }}" = "success"
test "${{ needs.cleanup.result }}" = "success"
131 changes: 31 additions & 100 deletions .github/workflows/check-ruby-versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,117 +5,48 @@ on:
workflow_dispatch:

permissions:
contents: write
pull-requests: write
contents: read

concurrency:
group: ruby-version-check
cancel-in-progress: false

jobs:
check-versions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Check for new Ruby releases
run: |
pip install pyyaml requests

python3 << 'EOF'
import yaml, requests, re
from datetime import date

# Fetch ruby-build definitions
resp = requests.get("https://api.github.com/repos/rbenv/ruby-build/contents/share/ruby-build")
definitions = [f["name"] for f in resp.json() if re.match(r"^\d+\.\d+\.\d+$", f["name"])]

with open("versions.yml") as f:
config = yaml.safe_load(f)

current = {v["version"] for v in config["versions"]}

# Check each tracked minor series for newer patches
tracked_series = set()
for v in config["versions"]:
parts = v["version"].split(".")
tracked_series.add(f"{parts[0]}.{parts[1]}")

updates = []
for series in tracked_series:
available = sorted(
[d for d in definitions if d.startswith(series + ".")],
key=lambda x: [int(p) for p in x.split(".")],
reverse=True
)
if available and available[0] not in current:
updates.append(available[0])

if updates:
print(f"New versions available: {', '.join(updates)}")
with open("new_versions.txt", "w") as f:
f.write("\n".join(updates))
else:
print("All versions up to date")
EOF

- name: Update versions.yml
if: hashFiles('new_versions.txt') != ''
id: update
run: |
python3 << 'EOF'
import yaml
from datetime import date

with open("versions.yml") as f:
config = yaml.safe_load(f)

with open("new_versions.txt") as f:
new_versions = f.read().strip().split("\n")

for new_ver in new_versions:
parts = new_ver.split(".")
series = f"{parts[0]}.{parts[1]}"

# Find existing entries for this series
series_entries = [v for v in config["versions"] if v["version"].startswith(series + ".")]
if series_entries:
# Use first entry as template
template = series_entries[0].copy()
template["version"] = new_ver
template["priority"] = "high"

# Demote existing priorities
for entry in series_entries:
if entry["priority"] == "high":
entry["priority"] = "medium"
elif entry["priority"] == "medium":
entry["priority"] = "low"

# Remove oldest if more than 3
if len(series_entries) >= 3:
oldest = series_entries[-1]
config["versions"].remove(oldest)

# Insert new version at the top of its series
idx = config["versions"].index(series_entries[0])
config["versions"].insert(idx, template)

# Remove EOL versions
today = date.today()
config["versions"] = [
v for v in config["versions"]
if date.fromisoformat(v["eol_date"]) > today
]

with open("versions.yml", "w") as f:
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
EOF
python3 scripts/update-ruby-versions.py | tee version-update-summary.txt

if git diff --quiet -- versions.yml; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

- name: Require bot token
if: steps.update.outputs.changed == 'true'
env:
BOT_PUBLIC_GITHUB_TOKEN: ${{ secrets.BOT_PUBLIC_GITHUB_TOKEN }}
run: |
if [ -z "${BOT_PUBLIC_GITHUB_TOKEN}" ]; then
echo "BOT_PUBLIC_GITHUB_TOKEN is required to open the Ruby version update PR." >&2
exit 1
fi

- name: Create PR
if: hashFiles('new_versions.txt') != ''
- name: Create version update PR
if: steps.update.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v8
with:
commit-message: "Update Ruby versions"
title: "chore: bump Ruby patch versions"
body: |
New Ruby versions detected. This PR updates `versions.yml` with the latest patches.

The build workflow will automatically trigger on merge.
token: ${{ secrets.BOT_PUBLIC_GITHUB_TOKEN }}
commit-message: "chore: update Ruby versions"
committer: "boringcache-bot <boringcache-bot@users.noreply.github.com>"
author: "boringcache-bot <boringcache-bot@users.noreply.github.com>"
title: "chore: update Ruby versions"
body-path: version-update-summary.txt
branch: auto/ruby-version-bump
delete-branch: true
68 changes: 68 additions & 0 deletions .github/workflows/validate-ruby.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Ruby Config Validation

on:
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
validate:
name: Ruby Config Validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Validate configuration files
run: |
set -euo pipefail

ruby -e 'require "yaml"; %w[versions.yml variants.yml .github/workflows/build-ruby.yml .github/workflows/check-ruby-versions.yml .github/workflows/validate-ruby.yml].each { |path| YAML.load_file(path); puts "ok #{path}" }'

- name: Validate scripts
run: |
set -euo pipefail

python3 -m py_compile scripts/update-ruby-versions.py
bash -n scripts/build-ruby-variants.sh
bash -n scripts/configure-platform.sh

- name: Ensure cache builds never run on pull requests
run: |
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

workflow = Path(".github/workflows/build-ruby.yml")
in_on_block = False
offenders = []

for number, line in enumerate(workflow.read_text().splitlines(), start=1):
if line.startswith("on:"):
in_on_block = True
continue

if in_on_block and line and not line.startswith((" ", "\t")):
in_on_block = False

if in_on_block and line.lstrip().startswith("pull_request:"):
offenders.append(number)

if offenders:
lines = ", ".join(str(number) for number in offenders)
raise SystemExit(f"{workflow} must not run cache-producing builds on pull_request (line {lines})")
PY

if grep -R "BORINGCACHE_API_TOKEN" .github/workflows/build-ruby.yml scripts; then
echo "Use split BORINGCACHE_RESTORE_TOKEN and BORINGCACHE_SAVE_TOKEN secrets instead." >&2
exit 1
fi

- name: Check version updater idempotence
run: |
set -euo pipefail

python3 scripts/update-ruby-versions.py
git diff --exit-code -- versions.yml
2 changes: 1 addition & 1 deletion scripts/build-ruby-variants.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ build_variant() {
configure_variant "$variant"

# Set up Ruby configure options with variant-specific options
RUBY_CONFIGURE_OPTS="--enable-shared --enable-load-relative --with-static-linked-ext --enable-frozen-string-literal --enable-pthread --enable-debug-env --enable-rubygems $VARIANT_OPTS"
RUBY_CONFIGURE_OPTS="--enable-shared --enable-load-relative --with-static-linked-ext --enable-frozen-string-literal --enable-pthread --enable-debug-env --enable-rubygems --disable-install-doc $VARIANT_OPTS"

# Set platform-specific environment variables for macOS
local BUILD_CPPFLAGS="${CPPFLAGS:-}"
Expand Down
Loading