Skip to content

Commit 7793b7f

Browse files
lbussellCopilotCopilot
authored
Add agent skills for release process (#7168)
Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a44c5a1 commit 7793b7f

5 files changed

Lines changed: 237 additions & 0 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
name: backport-changes
3+
description: Analyze and backport changes from nightly to the release branch in dotnet/dotnet-docker.
4+
user-invocable: true
5+
disable-model-invocation: true
6+
---
7+
8+
# Backporting Changes
9+
10+
## Workflow
11+
12+
1. **Get candidates for backport**: Run `pwsh scripts/Get-BackportPRs.ps1` to find PRs to backport.
13+
2. **Analyze PRs** - classify each PR using the backport guidelines below and present the analysis table to the user
14+
3. **Cherry-pick** - confirm the plan with the user, then run `git cherry-pick <commits>` (in the order they were merged).
15+
- If any templates or manifests changed, regenerate Dockerfiles and READMEs. Confirm that the diff looks correct.
16+
4. **Resolve conflicts** - follow the conflict resolution table below; stop and consult the user for anything not covered
17+
18+
## Backport guidelines
19+
20+
- Backport:
21+
- New images - if ready for the main branch
22+
- Removal of EOL images - end-of-life image cleanup
23+
- Dockerfile/template changes - structural changes to how images are built
24+
- Image component updates - MinGit, PowerShell, and other tools
25+
- Infrastructure and tooling changes - build scripts, CI/CD updates
26+
- Automated `eng/common` updates - standard engineering infrastructure
27+
- Do not backport:
28+
- Version-only updates for daily/preview builds (no Dockerfile changes)
29+
- Changes already on the release branch
30+
- Experimental or incomplete features
31+
- Requires extra consideration:
32+
- Daily builds of .NET or appliance images - only backport if they include Dockerfile changes beyond simple version updates.
33+
34+
## Output
35+
36+
After analyzing PRs, present results in a table:
37+
38+
```markdown
39+
| PR | Title | Backport | Reason | Commit |
40+
| --- | --- | --- | --- | --- |
41+
| #1234 | Update PowerShell to X.Y.Z | ✅ Yes | Component update | 123abc... |
42+
| #1235 | Daily build .NET X.Y.Z-preview.Y | ❌ No | Version-only update, no Dockerfile changes | 123abc... |
43+
| #1236 | Add Ubuntu X.Y images | ✅ Yes | New images ready for release | 123abc... |
44+
```
45+
46+
## Conflict resolution
47+
48+
| Conflicting files | Resolution |
49+
| --- | --- |
50+
| `src/*` | Regenerate Dockerfiles. |
51+
| `READMEs` | Regenerate READMEs. |
52+
| `manifest.json` | Take changes from nightly, ensure `latest` tags are on the correct (non-preview) versions, then regenerate Dockerfiles and READMEs. |
53+
| `manifest.versions.json` | Keep the latest/most up-to-date versions. |
54+
| Other files | Stop and consult the user. |
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env pwsh
2+
3+
# Lists pull requests with the 'needs-backport' label in dotnet/dotnet-docker.
4+
# Separates PRs merged before the most recent Patch Tuesday (likely stale labels).
5+
6+
function Get-PatchTuesday ([int]$Offset = 0) {
7+
$target = [datetime]::Today.AddMonths($Offset)
8+
$firstOfMonth = [datetime]::new($target.Year, $target.Month, 1)
9+
$firstTuesday = $firstOfMonth
10+
while ($firstTuesday.DayOfWeek -ne [DayOfWeek]::Tuesday) {
11+
$firstTuesday = $firstTuesday.AddDays(1)
12+
}
13+
return $firstTuesday.AddDays(7)
14+
}
15+
16+
function Write-PRTable ($prs) {
17+
Write-Host "| # | Title | Author | State | Created | Merged |"
18+
Write-Host "|---|-------|--------|-------|---------|--------|"
19+
foreach ($pr in $prs) {
20+
$created = ([datetime]$pr.createdAt).ToString("yyyy-MM-dd")
21+
$merged = if ($pr.mergedAt) { ([datetime]$pr.mergedAt).ToString("yyyy-MM-dd") } else { "" }
22+
$state = $pr.state
23+
$author = $pr.author.login
24+
Write-Host "| #$($pr.number) | $($pr.title) | $author | $state | $created | $merged |"
25+
}
26+
}
27+
28+
$prs = gh pr list --repo dotnet/dotnet-docker --label needs-backport --state all --json number,title,state,createdAt,mergedAt,author --limit 100 | ConvertFrom-Json
29+
$prs = $prs | Sort-Object createdAt
30+
31+
if ($prs.Count -eq 0) {
32+
Write-Host "No PRs found with the 'needs-backport' label."
33+
return
34+
}
35+
36+
$patchTuesday = Get-PatchTuesday -1
37+
$current = @()
38+
$stale = @()
39+
40+
foreach ($pr in $prs) {
41+
if ($pr.mergedAt -and ([datetime]$pr.mergedAt) -lt $patchTuesday) {
42+
$stale += $pr
43+
} else {
44+
$current += $pr
45+
}
46+
}
47+
48+
Write-Host "## Needs Backport"
49+
Write-Host ""
50+
Write-Host "The following pull requests are candidates to be backported from the nightly branch to the release branch."
51+
Write-Host ""
52+
if ($current.Count -gt 0) {
53+
Write-PRTable $current
54+
} else {
55+
Write-Host "_None._"
56+
}
57+
58+
if ($stale.Count -gt 0) {
59+
Write-Host ""
60+
Write-Host "## Possibly Stale (merged before $($patchTuesday.ToString("yyyy-MM-dd")))"
61+
Write-Host ""
62+
Write-Host 'The following pull requests have the `needs-backport` label, but were merged before the most recent release.'
63+
Write-Host 'They may already be backported but did not have their label removed.'
64+
Write-Host ""
65+
Write-PRTable $stale
66+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: create-public-release-pr
3+
description: Create a public PR to the dotnet-docker release branch with new .NET versions, and a follow-up PR from the release branch into main. Use when preparing the public release PR for a .NET containers servicing release.
4+
user-invocable: true
5+
disable-model-invocation: true
6+
---
7+
8+
# Create Public Release PR
9+
10+
## Workflow
11+
12+
1. **Create a working branch** - create a new branch based off of the public release branch (e.g. `release-$ReleaseName`).
13+
- Run `pwsh ../shared/Get-ReleaseBranches.ps1` to find the latest release branch.
14+
- The most recently created branch corresponds to the current release.
15+
2. **Collect stage container names** - read `stage-containers.txt` from the internal release branch get the stage container names for each release.
16+
3. **Update Dockerfiles to new .NET versions** - for each .NET version to be released, run the update-dependencies tool from the root of the dotnet-docker repo:
17+
```bash
18+
dotnet run --project ./eng/update-dependencies/update-dependencies.csproj -- from-staging-pipeline $StageContainerName --azdo-organization "https://dev.azure.com/dnceng" --azdo-project internal --source-branch "main"
19+
```
20+
- Commit changes separately for each .NET version with this commit message format: `Update .NET X.0 to X.0.Y Runtime / X.0.ZZZ SDK`.
21+
For previews: `Update .NET X.0 to X.0 Preview N`.
22+
4. **Confirm with the user** — Let the user review the changes.
23+
5. **Submit the PR** — Push the branch to `origin` and submit a PR to `main`.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
name: merge-main-to-nightly
3+
description: Create a PR to merge main into the nightly branch after a .NET containers servicing release. Use when updating the nightly branch post-release or merging main to nightly.
4+
user-invocable: true
5+
disable-model-invocation: true
6+
---
7+
8+
# Merge main branch to nightly branch
9+
10+
## Workflow
11+
12+
1. **Determine the release name**: Run `pwsh ../shared/Get-ReleaseBranches.ps1` to find the latest release branch.
13+
- The most recently created branch corresponds to the current release.
14+
2. **Create a working branch** - create a new branch based off of the `nightly` branch, called `main-to-nightly-$releaseName`.
15+
- `git fetch upstream nightly && git checkout -b main-to-nightly-$releaseName upstream/nightly`
16+
3. **Merge main into the branch** - merge the `main` branch into your new branch, fixing any merge conflicts as necessary.
17+
- `git fetch upstream main && git merge upstream/main`
18+
- If there are merge conflicts, refer to the conflict resolution table below.
19+
4. **Confirm with the user** - Let the user review the changes and the diff.
20+
5. **Submit the PR** - Push the branch to `origin` and create a PR targeting the `nightly` branch.
21+
22+
## Conflict resolution
23+
24+
| Conflicting files | Resolution |
25+
|---|---|
26+
| `src/*` | Regenerate Dockerfiles. |
27+
| `READMEs` | Regenerate READMEs. |
28+
| `manifest.json` | Take changes from nightly, ensure preview/nightly tags remain, then regenerate Dockerfiles and READMEs. |
29+
| `manifest.versions.json` | Keep nightly's versions (which are typically newer/preview). Incorporate any structural changes from main. |
30+
| Other files | Stop and consult the user. |
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env pwsh
2+
3+
# Finds the latest public and internal release branches in dotnet/dotnet-docker
4+
# by fetching from GitHub (public) and Azure DevOps (internal) and listing by
5+
# branch creation date.
6+
7+
function Get-RemoteName {
8+
param(
9+
[Parameter(Mandatory = $true)]
10+
[string] $UrlPattern
11+
)
12+
13+
$matchingRemotes = @(git remote | Where-Object {
14+
$remoteName = $_
15+
$remoteUrl = git remote get-url $remoteName 2>$null
16+
$remoteUrl -match $UrlPattern
17+
})
18+
19+
if ($matchingRemotes.Count -eq 0) {
20+
throw "Unable to find a remote with a URL matching '$UrlPattern'."
21+
}
22+
23+
if ($matchingRemotes.Count -gt 1) {
24+
throw "Found multiple remotes with URLs matching '$UrlPattern': $($matchingRemotes -join ', ')."
25+
}
26+
27+
return $matchingRemotes[0]
28+
}
29+
30+
# Show basic documentation
31+
Write-Host "# Release branches"
32+
Write-Host "Release branches follow the Windows release naming scheme."
33+
Write-Host "Example: 2026-04B refers to the second week (B) of April 2026."
34+
35+
$gitHubRemote = Get-RemoteName -UrlPattern 'github\.com[:/]dotnet/'
36+
$dncengRemote = Get-RemoteName -UrlPattern 'dev\.azure\.com/dnceng/'
37+
$upstreamRemoteUrl = git remote get-url $gitHubRemote
38+
$dncengRemoteUrl = git remote get-url $dncengRemote
39+
40+
Write-Host ""
41+
Write-Host "## Repo configuration"
42+
Write-Host "This repo is configured with the following remotes:"
43+
Write-Host ""
44+
Write-Host "Source | Remote Name | Remote URL"
45+
Write-Host "--- | --- | ---"
46+
Write-Host "Public | $gitHubRemote | $upstreamRemoteUrl"
47+
Write-Host "Internal | $dncengRemote | $dncengRemoteUrl"
48+
49+
git fetch $gitHubRemote 2>&1 | Out-Null
50+
git fetch $dncengRemote 2>&1 | Out-Null
51+
52+
$numberOfBranches = 5
53+
54+
Write-Host ""
55+
Write-Host "## ${numberOfBranches} most recent public release branches"
56+
git branch -r --list "$gitHubRemote/release/*" --sort=-creatordate `
57+
| Select-Object -First $numberOfBranches `
58+
| ForEach-Object { Write-Host "- $($_.Trim())" }
59+
60+
Write-Host ""
61+
Write-Host "## ${numberOfBranches} most recent internal release branches"
62+
git branch -r --list "$dncengRemote/internal/release/*" --sort=-creatordate `
63+
| Select-Object -First $numberOfBranches `
64+
| ForEach-Object { Write-Host "- $($_.Trim())" }

0 commit comments

Comments
 (0)