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
177 changes: 177 additions & 0 deletions .github/workflows/bump-major-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
name: Bump main to next major preview

on:
workflow_dispatch:
inputs:
next_major:
description: 'Next major version (e.g., 10) for the new preview line on main'
required: true
type: string

permissions:
contents: write
pull-requests: write

concurrency:
group: main-version-mutator
cancel-in-progress: false

jobs:
bump:
runs-on: ubuntu-latest
env:
NEXT_MAJOR_INPUT: ${{ inputs.next_major }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0

- name: Configure git
run: |
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"

- name: Validate inputs
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'

$next = $env:NEXT_MAJOR_INPUT
if ($next -notmatch '^(0|[1-9]\d*)$') {
throw "next_major must be a positive integer with no leading zeros (got: '$next')"
}
$nextInt = [int]$next
$next = [string]$nextInt

git fetch --tags --force origin
if ($LASTEXITCODE -ne 0) { throw "git fetch --tags failed (exit $LASTEXITCODE); cannot validate sequential-major rule." }

$mainHeadSha = (git rev-parse origin/main).Trim()
if ($LASTEXITCODE -ne 0 -or -not $mainHeadSha) { throw "git rev-parse origin/main failed (exit $LASTEXITCODE)." }

$versionJson = git show "${mainHeadSha}:version.json" | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) { throw "Failed to read version.json from origin/main." }
$ver = $versionJson.version
if ($ver -notmatch '^(0|[1-9]\d*)\.(0|[1-9]\d*)-preview') {
throw "main's version is '$ver' but should be '<major>.<minor>-preview.{height}'"
}
$currentMajor = [int]$matches[1]
if ($nextInt -le $currentMajor) {
throw "next_major ($nextInt) must be greater than current major ($currentMajor)"
}

$latestStable = git tag --list --sort=-v:refname |
Where-Object { $_ -match '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$' } |
Select-Object -First 1

if ($latestStable) {
if ($latestStable -notmatch '^(\d+)\.') {
throw "Could not parse latest stable tag: '$latestStable'"
}
$latestStableMajor = [int]$matches[1]
$expectedMajor = $latestStableMajor + 1
if ($nextInt -ne $expectedMajor) {
throw "next_major ($nextInt) must be exactly one greater than the latest stable major ($latestStableMajor; tag '$latestStable'). Expected: $expectedMajor. Major versions must be incremented sequentially."
}
Write-Host "OK: next_major ($nextInt) is exactly one greater than latest stable major ($latestStableMajor)."
} else {
$expectedMajor = $currentMajor + 1
if ($nextInt -ne $expectedMajor) {
throw "No stable tags found; falling back to comparing against main's current major ($currentMajor). next_major ($nextInt) must be exactly $expectedMajor."
}
Write-Host "No stable tags found; next_major ($nextInt) matches currentMajor+1 ($expectedMajor)."
}

$newVersion = "$next.0-preview.{height}"
$runId = $env:GITHUB_RUN_ID
$runAttempt = $env:GITHUB_RUN_ATTEMPT
Add-Content -Path $env:GITHUB_ENV -Value "NEW_VERSION=$newVersion"
Add-Content -Path $env:GITHUB_ENV -Value "NEXT_MAJOR=$next"
Add-Content -Path $env:GITHUB_ENV -Value "BUMP_BRANCH=bot/bump-major-$next-$runId-$runAttempt"
Add-Content -Path $env:GITHUB_ENV -Value "MAIN_HEAD_SHA=$mainHeadSha"

- name: Create bump branch (from validated main SHA)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
git checkout "$env:MAIN_HEAD_SHA"
if ($LASTEXITCODE -ne 0) { throw "git checkout failed (exit $LASTEXITCODE)." }
git checkout -b "$env:BUMP_BRANCH"
if ($LASTEXITCODE -ne 0) { throw "git checkout -b failed (exit $LASTEXITCODE)." }

- name: Bump version.json
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$path = 'version.json'
$content = [System.IO.File]::ReadAllText($path)
$content = [regex]::Replace($content, '"version":\s*"[^"]+"', "`"version`": `"$env:NEW_VERSION`"")
$content = [regex]::Replace($content, '(?m)^\s*"versionHeightOffset"\s*:\s*-?\d+\s*,?\s*(//[^\r\n]*)?\s*(\r?\n|$)', '')
$content = [regex]::Replace($content, ',(\s*[}\]])', '$1')
[System.IO.File]::WriteAllText($path, $content)
$obj = $content | ConvertFrom-Json
if ($obj.PSObject.Properties.Name -contains 'versionHeightOffset') {
throw "Failed to strip versionHeightOffset from version.json. Manual edit required."
}

- name: Commit and push
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
git add version.json
if ($LASTEXITCODE -ne 0) { throw "git add failed (exit $LASTEXITCODE)." }
git commit -m "Bump main to $env:NEW_VERSION (next major preview line)"
if ($LASTEXITCODE -ne 0) { throw "git commit failed (exit $LASTEXITCODE)." }
git push --force-with-lease -u origin "$env:BUMP_BRANCH"
if ($LASTEXITCODE -ne 0) { throw "git push failed (exit $LASTEXITCODE)." }

- name: Verify main hasn't moved since validation
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
git fetch origin main --quiet
if ($LASTEXITCODE -ne 0) { throw "git fetch failed (exit $LASTEXITCODE)." }
$currentMainSha = (git rev-parse origin/main).Trim()
if ($currentMainSha -ne $env:MAIN_HEAD_SHA) {
throw "main moved during bump-major-preview (was $env:MAIN_HEAD_SHA, now $currentMainSha). The bump branch was pushed but no PR was created. Delete the bump branch and re-run."
}
Write-Host "OK: main is still at $env:MAIN_HEAD_SHA."

- name: Open PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$nextMajor = $env:NEXT_MAJOR
$body = @"
Bumps ``main`` from its current preview to ``$env:NEW_VERSION`` ahead of upcoming breaking changes for ``${nextMajor}.0``.

Merge this **before** any PR labeled ``breaking-change`` so the prereleases are correctly versioned as ``${nextMajor}.0.0-preview.N``.
"@
try {
$url = (gh pr create `
--base main `
--head "$env:BUMP_BRANCH" `
--title "Bump main to $env:NEW_VERSION (next major preview)" `
--body $body | Select-Object -Last 1).Trim()
} catch {
throw "gh pr create failed: $($_.Exception.Message)"
}
if ($LASTEXITCODE -ne 0 -or -not $url -or $url -notmatch '^https?://') {
throw "gh pr create did not return a valid URL (exit $LASTEXITCODE; got: '$url')"
}
Add-Content -Path $env:GITHUB_ENV -Value "PR_URL=$url"

- name: Summary
shell: pwsh
run: |
$summary = @"
## Major preview bump

- **PR**: $env:PR_URL
- Merge this before landing the first breaking-change PR.
"@
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $summary
9 changes: 4 additions & 5 deletions .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ name: Build

on:
pull_request:
branches: [ main ]
branches: [ main, 'release/*.x' ]

env:
configuration: Release
productNamespacePrefix: "DynamicData"

jobs:
build:
Expand Down Expand Up @@ -40,7 +39,7 @@ jobs:

- name: NBGV
id: nbgv
uses: dotnet/nbgv@master
uses: dotnet/nbgv@v0.5.1
with:
setAllVars: true

Expand All @@ -61,7 +60,7 @@ jobs:
working-directory: src

- name: Create NuGet Artifacts
uses: actions/upload-artifact@master
uses: actions/upload-artifact@v7
with:
name: nuget
path: '**/*.nupkg'
path: 'src/**/bin/Release/*.nupkg'
Loading
Loading