Skip to content

Replace dtolnay/rust-toolchain with manual rustup#2340

Merged
Eliah Kagan (EliahKagan) merged 1 commit intoGitoxideLabs:mainfrom
EliahKagan:rustup
Jan 4, 2026
Merged

Replace dtolnay/rust-toolchain with manual rustup#2340
Eliah Kagan (EliahKagan) merged 1 commit intoGitoxideLabs:mainfrom
EliahKagan:rustup

Conversation

@EliahKagan
Copy link
Copy Markdown
Member

This builds on #2337 by replacing all uses of dtolnay/rust-toolchain with rustup commands, as described in the "Plan" section there. See the commit message here (dc980b2) for considerable details including background, rationale, and why an alternative action was not as good of a choice.

I first attempted to do this change (within my fork) via a Copilot PR. An error occurred, but the generated prompt in EliahKagan#141 (if one expands the details) is potentially a useful alternative summary. The diff here is the same as what Copilot would've done.

Analogous to that release test of changes to release.yml in #2337, a release test achieved by cherry-picking the commit used there onto this branch was done in this release test. (That was before rebasing this, but the rebase was just to bring this ahead of the merge commit--it didn't change the contents of release.yml or anything else.) Based on that, this seems not to break release.yml (so it should work again once some production-suitable fix/workaround for #2338 is devised).

However, this PR remains a draft until I check that the expected security scanning alerts, for both CodeQL and Zizmor, have appeared in the Security tab due to #2337. (Most of them should then go away as a result of this PR.)

Previously, we used manual `rustup` commands in some CI jobs, but
the `dtolnay/rust-toolchain` action in most. That action handles
various conditions nicely, and is also very convenient. One of the
conditions it handles, on some operating systems, is even to
install `rustup` itself, if absent.

However, we don't seem to need any of its additional functionality
now or in the immediately foreseeable future. Furthermore, as
tracked at dtolnay/rust-toolchain#160, it doesn't have any specific
version tags for its own versions, nor any tags at all for Rust
versions (instead, Rust versions are specified either in `with`
keys, or by specifying a *branch* of the action named as the
version).

This makes it so that it can't be pinned to a version in such a way
that Dependabot (or similar tools like Renovatebot) can update it.
That means if we want recent toolchains, we would either have to
manually (or through some ad-hoc automated method) update it
regularly, or continue to specify `@master` and `@stable` as we had
been doing. The latter is incompatible with the Dependabot
`cooldown` period set up recently to provide partial defense
against supply chain attacks.

`dtolnay/rust-toolchain` is overall high quality, and replacing it
with a less used action would not necessarily confer a net gain in
security. At least one other action is also actively maintained and
may be mostly suitable, `actions-rust-lang/setup-rust-toolchain`,
though it is less widely used.

However, it wouldn't be easy to use that action correctly here,
because on macOS it installs a newer version of `bash` via `brew`,
placing it ahead of the very old `bash` 3 that ships with macOS.
(Per actions-rust-lang/setup-rust-toolchain#10, it does this
because its own script steps rely on some newer `bash` features.)

It's important that the shell scripts in this repository, such as
the fixture scripts run via `gix-testtools`, be run in `bash` 3 on
macOS. Otherwise, we might break compatibility with most macOS
systems, where often no newer `bash` has been installed. Thus,
`actions-rust-lang/setup-rust-toolchain` won't work here as a
drop-in replacement.

Fortunately, because all the jobs using `dtolnay/rust-toolchain`
run in environments that have a working `rustup` command, which is
able to install the desired versions, it's enough to replace each
use of that action with a small number of `rustup` commands (and
adjust how data used to parameterize it is passed around, in a few
cases). This makes that change.

Because `dtolnay/rust-toolchain` is now no longer used, this change
should resolve all non-suppressed Zizmor alerts, as well as most
CodeQL alerts.
Copy link
Copy Markdown
Member Author

@EliahKagan Eliah Kagan (EliahKagan) left a comment

Choose a reason for hiding this comment

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

The expected code scanning alerts, from both CodeQL and Zizmor, were created and are visible in the Security tab, as intended in #2337. That's the main thing I wanted to check before merging this (which is expected to close most of the alerts, since it fixes the condition it describes).

The failure here that I mentioned in #2339 (review) looks like it's an occasional network error and not due to any changes here.

Therefore, I think this is ready to merge once its tests pass.

@EliahKagan Eliah Kagan (EliahKagan) marked this pull request as ready for review January 4, 2026 11:54
@EliahKagan Eliah Kagan (EliahKagan) changed the title Replace dtolnay/rust-toolchain with manual rustup commands Replace dtolnay/rust-toolchain with manual rustup Jan 4, 2026
@EliahKagan Eliah Kagan (EliahKagan) merged commit 12a35c8 into GitoxideLabs:main Jan 4, 2026
30 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces all uses of the dtolnay/rust-toolchain GitHub Action with manual rustup commands to address security concerns. The changes maintain functional equivalence while eliminating dependencies on third-party actions for Rust toolchain installation.

  • Converts all toolchain installations to use rustup update, rustup default, and rustup target add commands
  • Moves environment variables to appropriate scopes (job-level or step-level) to support the manual installation approach
  • Adds explicit shell: bash specifications where needed for cross-platform compatibility

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
.github/workflows/release.yml Replaces two instances of dtolnay/rust-toolchain with manual rustup commands; moves environment variables from step-level to job-level in the installation job for proper scoping
.github/workflows/ci.yml Replaces six instances of dtolnay/rust-toolchain with manual rustup commands; adds shell: bash where needed for Windows/multi-OS jobs; converts component installation from action parameters to rustup component add

@EliahKagan
Copy link
Copy Markdown
Member Author

Adds explicit shell: bash specifications where needed for cross-platform compatibility

These are actually just to be able to run a script step of multiple commands that stops and reports failure on the first failing command. Maybe this reason for using step-level shell: bash isn't obvious and should be made clearer in some way.

@Byron
Copy link
Copy Markdown
Member

Adds explicit shell: bash specifications where needed for cross-platform compatibility

These are actually just to be able to run a script step of multiple commands that stops and reports failure on the first failing command. Maybe this reason for using step-level shell: bash isn't obvious and should be made clearer in some way.

If Copilot doesn't know that even, then I think it's very non-obvious. Even reading this I can't imagine why it has this behaviour except that it's something they explicitly put in to make using scripts easier, while having to keep the default behaviour backwards-compatible to not fail fast.

@EliahKagan
Copy link
Copy Markdown
Member Author

Eliah Kagan (EliahKagan) commented Jan 5, 2026

If Copilot doesn't know that even, then I think it's very non-obvious.

I can put comments on every step-level shell: bash, much as we do for shell: bash when it is set at the workflow or job level. I think that should help both humans and LLMs.

I think we should also somehow make clearer (at least to humans reading the workflow file) why we're using different shells in the first place in some jobs, even when setting shell: bash at the job level would simplify commands, and even though we're using job-level shell: bash in some other jobs. There are good reasons to run cargo nextest from different shells depending on the operating system--bash on a Windows GHA runner is Git Bash, whose environment subtly differs, and we intend the test suite to work properly even when run from outside a Git Bash environment. But I don't think this is currently clear enough.

Even reading this I can't imagine why it has this behaviour except that it's something they explicitly put in to make using scripts easier, while having to keep the default behaviour backwards-compatible to not fail fast.

I realized I forgot to reply to something earlier about this. I've done so now; see #1363 (comment). In short, PowerShell doesn't quite have this feature, and also it makes different judgments from POSIX-compatble shells about what constitutes an error.

But it almost has it, such that the default behavior on CI for PowerShell should maybe be different from what it is currently. The situation is:

  1. set -e itself is subject to huge mismatches between its actual effect and its intuitive effects, and many shells intentionally avoid replicating that design. This includes not only shells like PowerShell that are meant to be very different from Unix-style shells, but also shells like fish that are meant to be intuitive for users of other Unix-style shells.
  2. PowerShell does have an $ErrorActionPreference variable. Setting $ErrorActionPreference = 'stop' fails fast on errors. When an external command fails, however, that's not considered an error in PowerShell. This is set in PowerShell script steps in GHA.
  3. PowerShell doesn't even automatically return the exit status of the last command to have run. However, this can be done reliably by appending a line to the script. GHA runners append if ((Test-Path -LiteralPath variable:\LASTEXITCODE)) { exit $LASTEXITCODE } when making the PowerShell script file for a script step.
  4. Some versions of PowerShell support $PSNativeCommandErrorActionPreference. If this is set to $true then something approximating set -e is achieved. I'm not sure how good the approximation is. I think this is still conisdered an experimental feautre, but I'm not sure. This would be usable in pwsh but not in powershell. But GitHub-hosted Windows runners have both. pwsh is the default on Windows when present.
  5. The GitHub Actions documentation documents points (2) and (3). But it characterizes the effect of the former as, "Fail-fast behavior when possible." It seems to me that this is misleading, since $PSNativeCommandErrorActionPreference is not set even when doing so is possible. (But it could be that I'm overestimating the uplift that confers in practice for achieving fail-fast behavior across separate commands in a PowerShell script.)

The different bash and pwsh/powershell behavior on CI led to the bug in #1556 that was fixed in #1559, which was not caused by any change in #1363 but would perhaps have been found in review had I not forgotten to reply to #1363 (comment) there. (I was reminded recently of the bug fixed in #1559, because the more severe bug I introduced in #2337 and fixed in #2342 resembles it, though this recent bug did not relate to fail-fast behavior in scripts.)

@Byron
Copy link
Copy Markdown
Member

Thanks a lot for sharing, I learned a lot!

Eliah Kagan (EliahKagan) added a commit to EliahKagan/gitoxide that referenced this pull request Jan 12, 2026
- Add missing comments on step-level `shell: bash` that were
  introduced without them in GitoxideLabs#2340.

- Add a conceptual comment where step-level `shell: bash` is
  intentionally omitted for the main nextest step of the
  `test-fast` jobs.

These changes are as discussed in GitoxideLabs#2340 comments, in particular:
GitoxideLabs#2340 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants