Skip to content
Open
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,9 @@ We strive to deliver high-quality code and as such, we've put in place a number
We pin the version of the tools we use in CI to ensure hermetic builds as much as possible. We routinely update
the versions of everything to stay up to date using three scripts:

- `scripts/update_rust_toolchain.ps1` which updates the version of the Rust toolchain in the `constants.env` file.
- `scripts/update_rust_toolchain.ps1` which requires an internal `ox-sdk` checkout and updates the public
Rust toolchain versions after the internal release toolchain has been selected.
Comment thread
sandersaares marked this conversation as resolved.
Example: `.\scripts\update_rust_toolchain.ps1 -InternalToolchainFile ..\ox-sdk\.pipelines\variables\publish.yml`.
- `scripts/update_tool_versions.ps1` which updates the version of tools in the `constants.env` file.
- `scripts/update_action_versions.ps1` which updates the version of GitHub actions in the various files in `.github/workflows`.

Expand Down
5 changes: 3 additions & 2 deletions constants.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@

# used for testing, ensures the MSRV promise is kept; must match Cargo.toml [workspace.package].rust-version
RUST_MSRV=1.93.1
# used for static analysis & mutation testing
RUST_LATEST=1.96.1
# used for static analysis & mutation testing; must match rust-toolchain.toml; its major/minor must match:
# https://o365exchange.visualstudio.com/O365%20Core/_git/ox-sdk?path=/.pipelines/variables/publish.yml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

DANGER: remove references to internal repo

RUST_LATEST=1.95.0
# used for coverage and extended analysis; update on a regular basis
RUST_NIGHTLY=nightly-2026-05-30
# used for external type exposure checks; update alongside updates to cargo-check-external-types
Expand Down
7 changes: 7 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

[toolchain]
# Must match RUST_LATEST in constants.env. Its major/minor must match MSRUSTUP_TOOLCHAIN in:
# https://o365exchange.visualstudio.com/O365%20Core/_git/ox-sdk?path=/.pipelines/variables/publish.yml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

DANGER: Remove this reference to internal repos!

channel = "1.95.0"
153 changes: 132 additions & 21 deletions scripts/update_rust_toolchain.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,55 @@

<#
.SYNOPSIS
Updates Rust toolchain versions in constants.env.
Updates Rust toolchain versions in constants.env and rust-toolchain.toml.

.DESCRIPTION
This script automatically updates the Rust toolchain configuration to the latest versions:
- Queries the latest stable Rust version and updates RUST_LATEST in constants.env
- Reads the internal stable Rust minor, then updates RUST_LATEST and rust-toolchain.toml to its latest upstream patch
- Calculates yesterday's nightly build date and updates RUST_NIGHTLY in constants.env
- Fetches the latest cargo-check-external-types release to determine the tested nightly version for RUST_NIGHTLY_EXTERNAL_TYPES

.PARAMETER ConstantsFile
Path to the constants.env file. Defaults to ../constants.env relative to script location.

.PARAMETER ToolchainFile
Path to the rust-toolchain.toml file. Defaults to ../rust-toolchain.toml relative to script location.

.PARAMETER InternalToolchainFile
Path to ox-sdk's .pipelines/variables/publish.yml file. Update its MSRUSTUP_TOOLCHAIN values first;
this script then aligns the public toolchain files to the latest upstream patch in that Rust minor.

.PARAMETER DryRun
If specified, shows what would be updated without actually modifying the files.

.EXAMPLE
.\update_rust_toolchain.ps1
.\update_rust_toolchain.ps1 -InternalToolchainFile ..\ox-sdk\.pipelines\variables\publish.yml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ox-sdk might not be the name of the folder under a different worktree

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Well, it's just an example. Or do you see the problem also in some non-example locations?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should we better use some fake name in example?


.EXAMPLE
.\update_rust_toolchain.ps1 -DryRun
.\update_rust_toolchain.ps1 -InternalToolchainFile ..\ox-sdk\.pipelines\variables\publish.yml -DryRun
#>

param(
[string]$ConstantsFile = (Join-Path $PSScriptRoot ".." "constants.env"),
[string]$ToolchainFile = (Join-Path $PSScriptRoot ".." "rust-toolchain.toml"),
[Parameter(Mandatory = $true)]
[string]$InternalToolchainFile,
[switch]$DryRun
)

$ErrorActionPreference = "Stop"
$ToolchainChannelPattern = '(?m)^([ \t]*channel[ \t]*=[ \t]*)"([^"]*)"'

function Get-LatestStableRustVersion {
<#
.SYNOPSIS
Retrieves the latest stable Rust version from the Rust GitHub releases.
Retrieves the latest stable Rust patch for a minor version from the Rust GitHub releases.
#>
$apiUrl = "https://api.github.com/repos/rust-lang/rust/releases"
param(
[string]$MinorVersion
)

$apiUrl = "https://api.github.com/repos/rust-lang/rust/releases?per_page=100"

try {
$headers = @{
Expand All @@ -46,25 +61,20 @@ function Get-LatestStableRustVersion {
}

$response = Invoke-RestMethod -Uri $apiUrl -Headers $headers -ErrorAction Stop
$versionPattern = "^$([regex]::Escape($MinorVersion))\.\d+$"

# Find the latest stable release (not a beta or nightly)
# The internal production feed controls the usable minor; select its latest upstream patch.
$stableRelease = $response | Where-Object {
$_.tag_name -match '^\d+\.\d+\.\d+$' -and -not $_.prerelease
$_.tag_name -match $versionPattern -and -not $_.prerelease
} | Select-Object -First 1

if ($null -eq $stableRelease) {
throw "No stable release found"
throw "No stable release found for Rust $MinorVersion"
}

# Keep the full x.y.z version (e.g., "1.92.0") so the pinned Cargo bugfix patch is preserved
if ($stableRelease.tag_name -match '^\d+\.\d+\.\d+$') {
return @{
Success = $true
Version = $stableRelease.tag_name
}
}
else {
throw "Could not parse version from tag: $($stableRelease.tag_name)"
return @{
Success = $true
Version = $stableRelease.tag_name
}
}
catch {
Expand Down Expand Up @@ -146,6 +156,69 @@ function Update-ConstantsEnv {
return $content
}

function Update-RustToolchain {
param(
[string]$FilePath,
[string]$Version
)

$content = Get-Content $FilePath -Raw
$replacement = '${1}"' + $Version + '"'
Comment thread
sandersaares marked this conversation as resolved.

return $content -replace $ToolchainChannelPattern, $replacement
}

function Get-InternalToolchainMinorVersion {
param(
[string]$FilePath
)

$content = Get-Content $FilePath -Raw
$definition = [regex]::Match(
$content,
'(?ms)^[ \t]*-[ \t]+name:[ \t]*MSRUSTUP_TOOLCHAIN[ \t]*\r?\n(?<body>.*?)(?=^[ \t]*-[ \t]+name:|\z)'
)

if (-not $definition.Success) {
throw "MSRUSTUP_TOOLCHAIN definition not found in '$FilePath'"
}

$values = [regex]::Matches($definition.Groups["body"].Value, '(?m)^[ \t]*value:[ \t]*(\S+)')

if ($values.Count -eq 0) {
throw "MSRUSTUP_TOOLCHAIN has no values in '$FilePath'"
}

$versions = @()

foreach ($value in $values) {
$channel = $value.Groups[1].Value
$channelMatch = [regex]::Match(
$channel,
'^(?<quote>[''"]?)ms-prod-(?<minor>\d+\.\d+)(?:@[a-z0-9_-]+)?\k<quote>$'
)

if (-not $channelMatch.Success) {
throw (
"MSRUSTUP_TOOLCHAIN channel '$channel' in '$FilePath' has an invalid format. " +
"Choose a production short name such as 'ms-prod-1.95' by running " +
"'msrustup toolchain available'; append a required backend such as '@llvm' " +
"only after the short name, for example 'ms-prod-1.95@llvm'."
)
}

$versions += $channelMatch.Groups["minor"].Value
}

$uniqueVersions = @($versions | Sort-Object -Unique)

if ($uniqueVersions.Count -ne 1) {
throw "MSRUSTUP_TOOLCHAIN entries in '$FilePath' select different Rust versions: $($uniqueVersions -join ', ')"
}

return $uniqueVersions[0]
}

# Main script execution
Write-Host "Fetching latest Rust versions..."
Write-Host ""
Expand All @@ -156,18 +229,46 @@ if (-not (Test-Path $ConstantsFile)) {
exit 1
}

if (-not (Test-Path $ToolchainFile)) {
Write-Host "Error: Rust toolchain file not found at '$ToolchainFile'"
exit 1
}

if (-not (Test-Path $InternalToolchainFile)) {
Write-Host "Error: Internal Rust toolchain file not found at '$InternalToolchainFile'"
exit 1
}

# Get current versions
$constantsContent = Get-Content $ConstantsFile

$currentRustLatest = ($constantsContent | Select-String '^RUST_LATEST=(.+)$').Matches.Groups[1].Value
$currentRustNightly = ($constantsContent | Select-String '^RUST_NIGHTLY=(.+)$').Matches.Groups[1].Value
$currentRustNightlyExternal = ($constantsContent | Select-String '^RUST_NIGHTLY_EXTERNAL_TYPES=(.+)$').Matches.Groups[1].Value

$toolchainContent = Get-Content $ToolchainFile -Raw
$toolchainMatch = [regex]::Match($toolchainContent, $ToolchainChannelPattern)

if (-not $toolchainMatch.Success) {
Write-Host "Error: Rust toolchain channel not found in '$ToolchainFile'"
exit 1
}

$currentToolchain = $toolchainMatch.Groups[2].Value

try {
$internalToolchainMinorVersion = Get-InternalToolchainMinorVersion -FilePath $InternalToolchainFile
}
catch {
Write-Host "Error reading internal Rust toolchain version: $($_.Exception.Message)"
exit 1
}

# Fetch new versions
$stableResult = Get-LatestStableRustVersion
$stableResult = Get-LatestStableRustVersion -MinorVersion $internalToolchainMinorVersion

if (-not $stableResult.Success) {
Write-Host "Error fetching latest stable Rust version: $($stableResult.Error)"
Write-Host "Error fetching the latest stable Rust $internalToolchainMinorVersion release: $($stableResult.Error)"
exit 1
}

Expand All @@ -176,13 +277,16 @@ $yesterdayNightly = Get-YesterdayNightlyVersion
$externalTypesResult = Get-ExternalTypesTestedNightly

Write-Host "Current versions:"
Write-Host " Internal Rust minor : $internalToolchainMinorVersion"
Write-Host " RUST_LATEST : $currentRustLatest"
Write-Host " rust-toolchain.toml channel: $currentToolchain"
Write-Host " RUST_NIGHTLY : $currentRustNightly"
Write-Host " RUST_NIGHTLY_EXTERNAL_TYPES: $currentRustNightlyExternal"
Write-Host ""

Write-Host "New versions:"
Write-Host " RUST_LATEST : $newStableVersion"
Write-Host " rust-toolchain.toml channel: $newStableVersion"
Write-Host " RUST_NIGHTLY : $yesterdayNightly"

if ($externalTypesResult.Success) {
Expand Down Expand Up @@ -210,7 +314,9 @@ if ($externalTypesResult.Success -and $currentRustNightlyExternal -ne $externalT
$constantsUpdates["RUST_NIGHTLY_EXTERNAL_TYPES"] = $externalTypesResult.Version
}

if ($constantsUpdates.Count -eq 0) {
$toolchainNeedsUpdate = $currentToolchain -ne $newStableVersion

if ($constantsUpdates.Count -eq 0 -and -not $toolchainNeedsUpdate) {
exit 0
}

Expand All @@ -223,3 +329,8 @@ if ($constantsUpdates.Count -gt 0) {
$newConstantsContent = Update-ConstantsEnv -FilePath $ConstantsFile -Updates $constantsUpdates
Set-Content -Path $ConstantsFile -Value $newConstantsContent -NoNewline
}

if ($toolchainNeedsUpdate) {
$newToolchainContent = Update-RustToolchain -FilePath $ToolchainFile -Version $newStableVersion
Set-Content -Path $ToolchainFile -Value $newToolchainContent -NoNewline
}
Loading