Skip to content
Closed
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
260 changes: 235 additions & 25 deletions .github/skills/update-supported-os/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,73 @@ Audit and update `supported-os.json` files in this repository. These files decla
- An OS version reaches end-of-life and should be moved to unsupported
- Periodic audit to ensure the support matrix is current

## Prerequisites
## Prerequisites — pre-flight checks

The `dotnet-release` tool must be installed. Packages are published to [GitHub Packages](https://github.com/richlander/dotnet-release/packages).
Before starting any work, verify all required tools are available. Install anything missing automatically.

### 1. GitHub CLI

```bash
gh --version
```

If not installed, stop and ask the user to install it — the rest of the workflow depends on it.

### 2. Node.js (for markdownlint)

```bash
node --version && npx --version
```

If not available, install via the system package manager:

```bash
# macOS
brew install node

# Linux (Debian/Ubuntu)
sudo apt-get install -y nodejs npm
```

If installation fails, note this to the user and continue — markdownlint will be skipped but CI will catch issues.

### 3. dotnet-release tool

```bash
dotnet-release --version
```

If not installed, install it. GitHub Packages requires `read:packages` scope on the `gh` CLI token.

**Check if the scope is already granted:**

```bash
gh api /user/packages?package_type=nuget --jq '.[0].name' 2>&1
```

If this returns a 403, add the scope:

```bash
# GitHub Packages requires authentication — use a GitHub token (PAT or GITHUB_TOKEN)
gh auth refresh -s read:packages
```

> The authorization prompt lists **all** granted scopes (existing + new), not just the new one — this is expected and not a sign of over-permissioning.

Then configure the NuGet source and install:

```bash
dotnet nuget add source https://nuget.pkg.github.com/richlander/index.json \
--name github-richlander \
--username "$(gh api user --jq '.login')" \
--password "$(gh auth token)" \
--store-password-in-clear-text

dotnet tool install -g Dotnet.Release.Tools \
--add-source https://nuget.pkg.github.com/richlander/index.json \
--version "0.*"

# Verify
dotnet-release --help
```

> **Note:** GitHub Packages requires authentication even for public repositories. If you get a 401 error, configure credentials for the source:
>
> ```bash
> dotnet nuget add source https://nuget.pkg.github.com/richlander/index.json \
> --name github-richlander \
> --username USERNAME \
> --password "$GITHUB_TOKEN" \
> --store-password-in-clear-text
> ```
>
> In GitHub Actions, `GITHUB_TOKEN` is available automatically. For local use, create a [personal access token](https://github.com/settings/tokens) with `read:packages` scope.
In GitHub Actions, `GITHUB_TOKEN` is available automatically with packages access.

## Inputs

Expand Down Expand Up @@ -81,25 +123,29 @@ The report uses GitHub callout blocks to categorize issues:

| Callout | Meaning | Action |
| --------- | --------- | -------- |
| `> [!WARNING]` | EOL but still listed as supported | Move to `unsupported-versions` |
| `> [!WARNING]` | EOL but still listed as supported | Move to `unsupported-versions` (see ESU exception below) |
| `> [!IMPORTANT]` | Active release not listed | Consider adding to `supported-versions` |
| `> [!TIP]` | Active but listed as unsupported | Verify this is intentional (no action usually needed) |
| `> [!CAUTION]` | Approaching EOL within 3 months | Informational — no immediate action |

> **ESU exception:** Windows versions with [Extended Security Updates](https://learn.microsoft.com/windows-server/get-started/extended-security-updates-overview) remain supported even after their mainstream EOL date. Check the `notes` field in `supported-os.json` — if a version has an ESU note, keep it in `supported-versions` and ignore the WARNING.

See [references/verify-output-example.md](references/verify-output-example.md) for example output.

If all versions return exit code 0, the matrix is current. **Stop here.**

### 2. Determine scope of changes

Review the verify report and decide which issues to act on:
Act on the verify report automatically — the user will review the resulting PR:

- **WARNING items** (EOL but supported) — always fix these
- **IMPORTANT items** (missing active releases) — add unless there's a known reason to exclude
- **TIP items** (active but unsupported) — usually intentional, skip unless the user says otherwise
- **WARNING items** (EOL but supported) — always fix: move to `unsupported-versions`. Exception: Windows versions with ESU notes in the JSON stay in `supported-versions`.
- **IMPORTANT items** (missing active releases) — always fix: add to `supported-versions`
- **TIP items** (active but unsupported) — skip, these are intentionally excluded
- **CAUTION items** (approaching EOL) — informational only, no JSON changes needed

Present findings to the user with recommendations before making changes.
Do not prompt the user to confirm individual changes. Apply all WARNING and IMPORTANT fixes, then create a PR for review.

After applying changes, summarize the CAUTION items to the user so they have visibility into what's coming in the next 3 months. Format as a brief list noting which versions are approaching EOL and their dates.

### 3. Apply changes to supported-os.json

Expand Down Expand Up @@ -131,7 +177,9 @@ Before committing, verify the generated markdown passes linting:
npx markdownlint --config .github/linters/.markdown-lint.yml release-notes/<version>/supported-os.md
```

CI runs markdownlint via super-linter. If linting fails, fix the generator or Markout library — do not patch the markdown by hand.
If `npx` is not available (Node.js was not installed during pre-flight), skip this step and note to the user that markdownlint was skipped. CI runs markdownlint via super-linter and will catch any issues.

If linting fails, fix the generator or Markout library — do not patch the markdown by hand.

### 6. Cross-reference with os-packages.json

Expand All @@ -149,7 +197,169 @@ Check if any newly added distro versions need entries in `os-packages.json`. If

2. Spot-check the generated markdown renders correctly.

### 8. Create PR
### 8. Cross-repo impact

OS version changes here also affect build and test infrastructure in other repos. After creating the PR in dotnet/core, search for Dockerfiles and Helix YAML files that reference the added or removed OS versions and file issues or PRs in the affected repos.

**Primary repos:**

- [dotnet/dotnet-buildtools-prereqs-docker](https://github.com/dotnet/dotnet-buildtools-prereqs-docker) — container images used for building and testing. New OS versions need new Dockerfiles; EOL versions should be removed.
- [dotnet/runtime](https://github.com/dotnet/runtime) — Helix queue YAML files and pipeline definitions reference OS-specific container images.

**Branches to check in dotnet/runtime:**

Check `main` **and** all active `release/` branches. The set of active release branches corresponds to supported .NET versions (check `releases.md` or `release-notes/releases-index.json` in this repo for the current list).

```bash
# If dotnet/runtime is cloned locally, search across branches:
cd ~/git/runtime
git fetch origin

# Check main
git grep -l "alpine-3.20\|Alpine.320" origin/main -- eng/pipelines/

# Check each active release branch
for branch in origin/release/10.0 origin/release/9.0 origin/release/8.0; do
echo "=== $branch ==="
git grep "alpine-3.20\|Alpine.320" "$branch" -- eng/pipelines/ || echo " (none found)"
done
```

If the repo is not cloned locally, use the GitHub API:

```bash
# Check a specific branch via GitHub API
gh api repos/dotnet/runtime/contents/eng/pipelines/helix-platforms.yml?ref=release/10.0 \
--jq '.content' | base64 -d | grep -i "alpine.*3.20"
```

> **Why release branches matter:** Per the [OS onboarding guide](https://github.com/dotnet/runtime/blob/main/docs/project/os-onboarding.md), EOL OS references in release branches should be remediated to avoid compliance issues. Alpine is especially prone to needing backports due to its short support window.

**What to search for:**

For **new OS versions**, check if container images and Helix queues exist:

```bash
# In dotnet-buildtools-prereqs-docker — look for Dockerfiles
gh search code "FROM alpine:3.23" --repo dotnet/dotnet-buildtools-prereqs-docker
gh search code "FROM debian:13" --repo dotnet/dotnet-buildtools-prereqs-docker

# In dotnet/runtime — look for Helix queue references (main + release branches)
grep -rn "alpine" eng/pipelines/ # if cloned locally
```

For **removed/EOL OS versions**, check if stale references remain:

```bash
# Search across all branches locally
git grep "alpine-3.20\|Alpine.320" origin/main origin/release/10.0 origin/release/9.0 -- eng/pipelines/
```

**Key files in dotnet/runtime:**

- `eng/pipelines/helix-platforms.yml`
- `eng/pipelines/coreclr/templates/helix-queues-setup.yml`
- `eng/pipelines/installer/helix-queues-setup.yml`
- `eng/pipelines/libraries/helix-queues-setup.yml`
- `eng/pipelines/common/templates/pipeline-with-resources.yml`

**Org-wide search for deprecated OS references:**

After auditing the primary repos, search the entire `dotnet` org for YAML files that still reference deprecated OS versions. Use `gh search code` with sleeps between queries to stay within the 10 req/min rate limit.

Search using three pattern types derived from dotnet/runtime conventions:

1. **Container image tags** — `prereqs:{distro}-{version}` (e.g. `prereqs:debian-11`, `prereqs:ubuntu-20.04`, `prereqs:opensuse-15.5`)
2. **Helix queue names** — `{Distro}.{Version}` with dots, no dashes (e.g. `Debian.11`, `Ubuntu.2004`, `openSUSE.15.5`, `Alpine.321`)
3. **Codenames** — Debian/Ubuntu codenames (e.g. `bullseye`, `focal`, `jammy`)

```bash
# Example: search for Debian 11 references across the org
# Sleep 7s between searches to respect rate limit (10 req/min)
gh search code --owner dotnet --language yaml '"prereqs:debian-11"' --limit 30
sleep 7
gh search code --owner dotnet --language yaml '"Debian.11"' --limit 30
sleep 7
gh search code --owner dotnet --language yaml bullseye --limit 30
sleep 7
```

Skip `dotnet/dotnet` (VMR) — it mirrors source repos so findings there should be addressed in the source repo instead.

After collecting results, verify each finding is still present on the default branch:

```bash
gh api repos/dotnet/{repo}/contents/{path} --jq '.content' | base64 -d | grep -i '{pattern}'
```

**File tracking issues on affected repos:**

File a tracking issue on each affected repo (not just runtime and prereqs). Each issue should:

1. Link back to the parent tracking issues: `dotnet/core` OS tracking issue, `dotnet/runtime` pipeline issue, and `dotnet/dotnet-buildtools-prereqs-docker` container image issue.
2. Include a checklist of specific files to update.
3. Include commit-pinned links to the affected lines (get the HEAD SHA with `gh api repos/dotnet/{repo}/commits/{branch} --jq '.sha'`).
4. End with attribution — note that the issue was generated by GitHub Copilot CLI (include version) via the [`update-supported-os`](https://github.com/dotnet/core/blob/main/.github/skills/update-supported-os/SKILL.md) skill.

Example issue body for a downstream repo:

```markdown
An org-wide audit of the dotnet org against the [.NET supported OS matrix](...) found a reference to {OS version}, which {reached EOL on date / reaches EOL on date}.

Part of [.NET OS Support Tracking](https://github.com/dotnet/core/issues/NNNN). See also: [dotnet/runtime#NNNN](...), [dotnet/dotnet-buildtools-prereqs-docker#NNNN](...).

## Tracking

- [ ] Update `{pattern}` in `{file}`

## Details

https://github.com/dotnet/{repo}/blob/{sha}/{path}
```

**Actions for primary repos:**

File tracking issues on both `dotnet/runtime` and `dotnet/dotnet-buildtools-prereqs-docker` with checklists covering all findings.

**For `dotnet/runtime`** — file a single issue covering `main` and all active release branches:

1. **Start with the high-level view** — a tracking checklist grouped by urgency (EOL now, approaching EOL, cleanup), with each item noting the branch, old → new version, and whether it's a helix (testing) or build image reference.
2. **Include a details section** with commit-pinned links to the specific lines — use bare URLs on their own line so GitHub renders the code inline.
3. **Distinguish helix vs build images** — helix images are for testing, build images are in `pipeline-with-resources.yml` and `platform-matrix.yml`.
4. **End with attribution** — note that the issue was generated by GitHub Copilot CLI (include version) via the [`update-supported-os`](https://github.com/dotnet/core/blob/main/.github/skills/update-supported-os/SKILL.md) skill.

**For `dotnet/dotnet-buildtools-prereqs-docker`** — file a separate issue covering container image cleanup and provisioning:

1. **EOL images to remove** — Dockerfiles for OS versions that are EOL.
2. **Approaching EOL** — images that will need removal soon.
3. **Stale images** — older versions no longer referenced in runtime pipelines. For Alpine, only the latest release + edge should be kept.
4. **New images to provision** — upcoming OS versions that should be provisioned ahead of need. For example, when Fedora N is current, Fedora N+1 images should be created proactively so they're ready before N reaches EOL.

Example checklist format:

```markdown
### EOL — fix now

- [ ] `main` — openSUSE 15.5 → 16.0 (helix)
- [ ] `release/8.0` — Ubuntu 20.04 → 22.04 (build image)

### Approaching EOL — fix within 3 months

- [ ] `main` — Debian 12 (EOL 2026-06-10) → 13 (helix)
```

Reference the [OS onboarding guide](https://github.com/dotnet/runtime/blob/main/docs/project/os-onboarding.md) in the issue body for context.

**Container image guidelines for `dotnet/dotnet-buildtools-prereqs-docker`:**

- **Alpine:** Keep only the latest stable release + edge. Remove older versions once runtime pipelines have moved off them.
- **`-WithNode` variant retired:** The old `-WithNode` Alpine image flavor (e.g. `prereqs:alpine-3.19-WithNode`) has been retired. The standard helix images (e.g. `prereqs:alpine-3.23-helix-amd64`) are the replacement. See [dotnet/dotnet-buildtools-prereqs-docker#990](https://github.com/dotnet/dotnet-buildtools-prereqs-docker/issues/990) for context and [PR #1554](https://github.com/dotnet/dotnet-buildtools-prereqs-docker/pull/1554) which removed the last `WithNode` directories. When proposing replacements for repos still using `-WithNode`, recommend the standard helix image.
- **Fedora:** Provision images for the next Fedora release (N+1) proactively, before the current version (N) reaches EOL. Fedora has a ~13-month lifecycle, so plan ahead.
- **Debian/Ubuntu LTS:** These have long support windows but should still be cleaned up once EOL. When proposing Ubuntu replacements, push forward to the latest LTS (e.g. 26.04), not back to an older one (e.g. 22.04 or 24.04).
- **Windows VM images:** Search for `ImageOverride` in YAML files to find VM pool references. Old images like `1es-windows-2019`, `windows.vs2019.amd64`, and `vmImage: windows-2019` should be updated to current equivalents (e.g. `1es-windows-2022`, `windows.vs2026preview.scout.amd64`).
- Cross-reference `src/<distro>/` directories against what runtime pipelines actually reference to find orphaned images.

### 9. Create PR

1. Create a branch:

Expand Down
2 changes: 1 addition & 1 deletion os-lifecycle-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The following documents describe supported operating systems:

## Preview .NET versions

N/A
* [.NET 11 supported OS versions](release-notes/11.0/supported-os.md)

## Out-of-support .NET versions

Expand Down
Loading
Loading