Skip to content

Release 2.7.8

Release 2.7.8 #19

Workflow file for this run

name: Release
run-name: Release ${{ inputs.version }}
on:
workflow_dispatch:
inputs:
version:
description: 'Ruby version to release (e.g., 3.4.7)'
required: true
type: string
latest:
description: 'Mark as latest release'
required: false
type: boolean
default: false
# Prevent multiple releases of the same version from running simultaneously
concurrency:
group: release-${{ inputs.version }}
cancel-in-progress: false
env:
HOMEBREW_DEVELOPER: 1
HOMEBREW_NO_AUTO_UPDATE: 1
# Runner images lack a rootless bwrap; newer Homebrew otherwise refuses
# to build from source on Linux (CI runners are throwaway anyway).
HOMEBREW_NO_SANDBOX_LINUX: 1
permissions:
contents: read
id-token: write
attestations: write
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
# Linux runners must satisfy Homebrew 6's glibc baseline (2.39,
# i.e. ubuntu-24.04) from the host: on older hosts every bottle
# pour drags in Homebrew's own glibc + gcc, and the resulting
# ruby links .linuxbrew/glibc, failing the portability check.
- runner: macos-14
yjit: true
- runner: ubuntu-24.04
yjit: true
- runner: ubuntu-24.04
yjit: false
- runner: ubuntu-24.04-arm
yjit: true
- runner: ubuntu-24.04-arm
yjit: false
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # main
# Disabled: Code signing causes issues with native gems compiled locally
# See: https://github.com/jdx/mise/discussions/7268
# - name: Import codesign certs
# if: runner.os == 'macOS'
# uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3
# with:
# p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
# p12-password: ${{ secrets.CERTIFICATES_P12_PASS }}
- uses: ruby/setup-ruby@v1
id: setup-ruby
with:
ruby-version: ${{ inputs.version }}
- name: Install Homebrew
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo "/home/linuxbrew/.linuxbrew/bin" >> $GITHUB_PATH
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
- name: Tap repo
run: bin/setup
- name: Fix Bundler permissions (ARM Linux)
if: runner.os == 'Linux' && runner.arch == 'ARM64'
run: chmod -R go-w /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle || true
- name: Build jdx Ruby
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4
with:
timeout_minutes: 30
max_attempts: 3
retry_wait_seconds: 30
command: |
if [[ -n "${{ steps.setup-ruby.outputs.ruby-prefix }}" ]]; then
export HOMEBREW_BASERUBY="${{ steps.setup-ruby.outputs.ruby-prefix }}/bin/ruby"
fi
YJIT_FLAG=""
if [[ "${{ matrix.yjit }}" == "false" ]]; then
YJIT_FLAG="--without-yjit"
fi
mkdir -p rubies/
cd rubies
brew jdx-package --verbose $YJIT_FLAG jdx-ruby@${{ inputs.version }}
# Disabled: Code signing causes issues with native gems compiled locally
# See: https://github.com/jdx/mise/discussions/7268
# - name: Codesign macOS binaries
# if: runner.os == 'macOS'
# working-directory: rubies
# run: |
# tarball=$(ls *.tar.gz)
# echo "Signing binaries in $tarball"
#
# # Extract
# mkdir -p extracted
# tar -xzf "$tarball" -C extracted
#
# # Sign Mach-O executables and bundles (skip .dSYM, .o, .a files)
# find extracted -type f \( -name "*.bundle" -o -name "*.dylib" -o -perm +111 \) \
# -not -path "*.dSYM/*" \
# -not -name "*.o" \
# -not -name "*.a" | while read -r file; do
# if file "$file" | grep -q "Mach-O"; then
# echo "Signing $file"
# codesign --sign "Developer ID Application: Jeffrey Dickey (4993Y37DX6)" \
# --identifier "dev.jdx.ruby" \
# --options runtime \
# --force \
# "$file"
# fi
# done
#
# # Re-create tarball
# rm "$tarball"
# tar -czf "$tarball" -C extracted .
- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ruby-${{ inputs.version }}-${{ matrix.runner }}-yjit-${{ matrix.yjit }}
path: rubies/
create-release:
needs: build
runs-on: ubuntu-latest
outputs:
revision_tag: ${{ steps.revision.outputs.tag }}
permissions:
contents: write
env:
VERSION: ${{ inputs.version }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # main
- name: Determine build revision
id: revision
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
# Find existing revision tags (e.g., 3.4.5-1, 3.4.5-2, ...)
# Use git ls-remote to get all tags β€” not truncated like gh release list
last_revision=$(git ls-remote --tags origin "refs/tags/${VERSION}-*" | \
sed 's|.*refs/tags/||' | \
{ grep -E "^${VERSION}-[0-9]+$" || true; } | \
sed "s/^${VERSION}-//" | \
sort -n | tail -1)
if [[ -z "$last_revision" ]]; then
revision=1
else
revision=$((last_revision + 1))
fi
echo "tag=${VERSION}-${revision}" >> "$GITHUB_OUTPUT"
echo "Build revision: ${VERSION}-${revision}"
- name: Get Ruby release info
id: ruby-release
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
# Convert version format: 3.4.1 -> v3_4_1 (but Ruby 4.x uses v4.0.0 format)
ruby_tag="v${VERSION//./_}"
# Fetch release info from ruby/ruby repo (try underscore format first, then dot format)
release_info=$(gh release view "$ruby_tag" --repo ruby/ruby --json publishedAt,body 2>/dev/null || \
gh release view "v${VERSION}" --repo ruby/ruby --json publishedAt,body 2>/dev/null || echo "{}")
release_date=$(echo "$release_info" | jq -r '.publishedAt // empty')
if [[ -n "$release_date" ]]; then
# Format as YYYY-MM-DD
formatted_date=$(date -d "$release_date" '+%Y-%m-%d' 2>/dev/null || date -j -f '%Y-%m-%dT%H:%M:%SZ' "$release_date" '+%Y-%m-%d' 2>/dev/null || echo "${release_date:0:10}")
echo "date=$formatted_date" >> "$GITHUB_OUTPUT"
echo "Found Ruby $VERSION release date: $formatted_date"
else
echo "date=" >> "$GITHUB_OUTPUT"
echo "Could not find Ruby release date for $ruby_tag"
fi
# Save changelog body to file (handles multiline content)
changelog=$(echo "$release_info" | jq -r '.body // empty')
if [[ -n "$changelog" ]]; then
echo "$changelog" > /tmp/ruby_changelog.md
echo "has_changelog=true" >> "$GITHUB_OUTPUT"
else
echo "has_changelog=false" >> "$GITHUB_OUTPUT"
fi
- name: Create releases
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN || github.token }}
RUBY_RELEASE_DATE: ${{ steps.ruby-release.outputs.date }}
HAS_CHANGELOG: ${{ steps.ruby-release.outputs.has_changelog }}
REVISION_TAG: ${{ steps.revision.outputs.tag }}
run: |
if [[ "${{ inputs.latest }}" == "true" ]]; then
latest_flag="--latest"
else
latest_flag="--latest=false"
fi
# Mark as prerelease if version contains preview or rc
if echo "${VERSION}" | grep -qiE '(preview|rc)'; then
prerelease_flag="--prerelease"
else
prerelease_flag=""
fi
# Build release notes file
echo "Ruby ${VERSION} portable binaries" > /tmp/release_notes.md
if [[ -n "$RUBY_RELEASE_DATE" ]]; then
echo "" >> /tmp/release_notes.md
echo "**Released by Ruby:** ${RUBY_RELEASE_DATE}" >> /tmp/release_notes.md
fi
if [[ "$HAS_CHANGELOG" == "true" ]]; then
echo "" >> /tmp/release_notes.md
echo "---" >> /tmp/release_notes.md
echo "" >> /tmp/release_notes.md
cat /tmp/ruby_changelog.md >> /tmp/release_notes.md
fi
TARGET="$(git rev-parse --verify HEAD)"
# Delete ALL existing floating releases matching this version (handles orphaned releases)
for release_id in $(gh api repos/${{ github.repository }}/releases --paginate --jq ".[] | select(.name == \"${VERSION}\" or .tag_name == \"${VERSION}\") | .id"); do
echo "Deleting release $release_id"
gh api -X DELETE "repos/${{ github.repository }}/releases/$release_id" || true
done
# Also delete the tag if it exists
gh api -X DELETE "repos/${{ github.repository }}/git/refs/tags/${VERSION}" 2>/dev/null || true
# Create floating release (e.g., 3.4.5) β€” always updated to latest build
gh release create "${VERSION}" \
--draft \
--notes-file /tmp/release_notes.md \
--target "$TARGET" \
--title "${VERSION}" \
$latest_flag \
$prerelease_flag
# Create pinned revision release (e.g., 3.4.5-1) β€” immutable snapshot
echo "" >> /tmp/release_notes.md
echo "**Build revision:** ${REVISION_TAG}" >> /tmp/release_notes.md
gh release create "${REVISION_TAG}" \
--draft \
--notes-file /tmp/release_notes.md \
--target "$TARGET" \
--title "${REVISION_TAG}" \
--latest=false \
$prerelease_flag
upload-assets:
needs: [build, create-release]
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
env:
VERSION: ${{ inputs.version }}
steps:
- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: ruby-${{ inputs.version }}-*
path: rubies
merge-multiple: true
- name: Upload tarballs to GitHub Release
working-directory: rubies
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN || github.token }}
REVISION_TAG: ${{ needs.create-release.outputs.revision_tag }}
run: |
gh release upload "${VERSION}" *.tar.gz --clobber --repo "${{ github.repository }}"
gh release upload "${REVISION_TAG}" *.tar.gz --clobber --repo "${{ github.repository }}"
- name: Attest build provenance
# Attestation is advisory (mise skips verification for custom
# precompiled_url templates); never block the release on it.
continue-on-error: true
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4
with:
subject-path: 'rubies/*.tar.gz'
publish-release:
needs: [create-release, upload-assets]
runs-on: ubuntu-latest
permissions:
contents: write
env:
VERSION: ${{ inputs.version }}
steps:
- name: Publish release
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN || github.token }}
REVISION_TAG: ${{ needs.create-release.outputs.revision_tag }}
run: |
# Mark as prerelease if version contains preview or rc
if echo "${VERSION}" | grep -qiE '(preview|rc)'; then
prerelease_flag="--prerelease"
else
prerelease_flag=""
fi
# Publish floating release
if [[ "${{ inputs.latest }}" == "true" ]]; then
gh release edit "${VERSION}" --draft=false --latest $prerelease_flag --repo "${{ github.repository }}"
else
gh release edit "${VERSION}" --draft=false --latest=false $prerelease_flag --repo "${{ github.repository }}"
fi
# Publish pinned revision release
gh release edit "${REVISION_TAG}" --draft=false --latest=false $prerelease_flag --repo "${{ github.repository }}"