Skip to content

Configure gem and crate release automation - #1036

Open
alexcrocha wants to merge 3 commits into
mainfrom
ar-release-workflow
Open

Configure gem and crate release automation#1036
alexcrocha wants to merge 3 commits into
mainfrom
ar-release-workflow

Conversation

@alexcrocha

@alexcrocha alexcrocha commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

This PR does 2 things:

  1. Centralizes gem and crate versions by making the Cargo workspace the source of truth. This allows us to automate releases to both RubyGems and crates.io.
  2. Extends the existing release workflow for trusted publishing on crates.io.

Before this PR merges, we will need to complete the setup of trusted publishing on crates.io before our next release. The steps needed for both rubydex and rubydex-sys are:

  • Open Settings → Trusted Publishing
  • Add a GitHub Actions publisher with:
    • Owner: Shopify
    • Repository: rubydex
    • Workflow: release.yml
    • Environment: release

@alexcrocha alexcrocha self-assigned this Sep 3, 2026
@alexcrocha
alexcrocha marked this pull request as ready for review September 4, 2026 06:30
@alexcrocha
alexcrocha requested a review from a team as a code owner September 4, 2026 06:30
@alexcrocha alexcrocha added the chore Something that should not be included in release notes label Sep 4, 2026

@vinistock vinistock left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we already configured trusted publishing in crates.io?

Comment thread rust/Cargo.toml Outdated
Comment on lines +12 to +13
[workspace.dependencies]
rubydex = { version = "=0.4.1", path = "rubydex" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this entry? I'm not sure I understand what it does.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a dependency of rubydex-sys that I moved here as a convenience so there was only one file to update when bumping the version. As discussed offline, I moved it back to rubydex-sys/Cargo.toml

Comment thread CONTRIBUTING.md Outdated
rubydex = { version = "=X.Y.Z", path = "rubydex" }
```

The Ruby gem dynamically reads its version from this manifest, so do not edit `lib/rubydex/version.rb`. For pre-release versions, Cargo requires a SemVer prerelease identifier such as `X.Y.Z-beta.N`, which `lib/rubydex/version.rb` translates to `X.Y.Z.betaN` for RubyGems.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a lot of this sentence is an implementation detail and not really instructions for releasing.

Let's tailor this section specifically for the instructions:

  • Bump the version here. This controls the versions of all crates and the Ruby gem
  • If you want to ship a beta release, use the format 1.2.3-beta.1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 updated as suggested

Comment thread CONTRIBUTING.md Outdated
Comment on lines +106 to +110
in `.github/workflows/release.yml`. That workflow verifies that the tag matches
the declared version, cross-compiles the precompiled gems, runs install
verification, publishes to RubyGems, publishes the workspace crates to
crates.io, and creates the GitHub release. You can trigger a dry run with
workflow dispatch; only tag pushes publish a release.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar here. Are these internal details or relevant instructions for someone trying to release?

I'd honestly not add anything here. If someone tries to push a tag that doesn't match the version, they will notice the mistake.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a mention that it publishes to crates.io as well, and took the opportunity to simplified here

Comment thread .github/workflows/release.yml Outdated
Comment on lines +26 to +40
- name: "Verify exact workspace dependency versions"
run: |
metadata="$(cargo metadata --no-deps --format-version 1 --manifest-path rust/Cargo.toml)"
ruby -rjson -e '
metadata = JSON.parse($stdin.read)
packages = metadata.fetch("packages")
rubydex = packages.find { |package| package.fetch("name") == "rubydex" }
rubydex_sys = packages.find { |package| package.fetch("name") == "rubydex-sys" }
requirement = rubydex_sys.fetch("dependencies").find { |dependency| dependency.fetch("name") == "rubydex" }.fetch("req")
expected = "=#{rubydex.fetch("version")}"

if requirement != expected
abort "rubydex-sys requires rubydex #{requirement.inspect}; expected #{expected.inspect}"
end
' <<<"$metadata"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this check protecting us from?

@alexcrocha alexcrocha Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a defensive check to make sure the dependency version was pinned to the crate version.
It's overkill; removed.

@vinistock vinistock left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few questions, but the general automation looks good. Let's get it configured on the crates io side and ship 🚀

Comment on lines +24 to +25
with:
ruby-version: "3.2.9"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to run on Ruby 3.2.9? If not, we can just remove this and it will pick up the version specified in .ruby-version.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me 👍

with:
ruby-version: "3.2.9"
- name: "Verify the tag matches the declared version"
if: startsWith(github.ref, 'refs/tags/')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we match the other similar checks?

Suggested change
if: startsWith(github.ref, 'refs/tags/')
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')

fi
compile:
timeout-minutes: 20
needs: verify_version

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we're adding a needs requirement to an optional step (since verify_version doesn't run if you trigger the workflow manually for debugging instead of releasing).

Will this work properly if verify_version is skipped by the conditional? Or will the entire workflow get stuck?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fair question. The condition is on the step, not on the job. So the verify_version job will run and complete regardless of the conditional step being true or not.

But now that you point it out, the previous steps will always run and that's wasteful.

I will look into an alternative approach for this

permissions:
id-token: write
contents: read
timeout-minutes: 30

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

30 minutes seems like a lot for just publishing the crates. Maybe 5 is fine?


[dependencies]
rubydex = { version = "0.2.6", path = "../rubydex" }
rubydex = { version = "=0.4.1", path = "../rubydex" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you confirm that if we bump the version in rust/Cargo.toml and forget to bump it here, it fails compilation?

@alexcrocha alexcrocha Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it fails immediately with

error: failed to select a version for the requirement `rubydex = "=0.4.1"`
candidate versions found which didn't match: 0.4.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Something that should not be included in release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants