# Update Distro Packages

> Create and update per-distro package files in release-notes/{version}/distros/ that document .NET runtime dependencies and package availability for each Linux distribution. USE FOR: setting up distros/ for a new .NET version, updating dependency package names when distro versions change, auditing package data. DO NOT USE FOR: supported-os.json changes (use update-supported-os skill), os-packages.json (legacy format).

- Skill: `dotnet/update-distro-packages` (Agent Skill)
- Install (CLI): `npx skillmds@latest add dotnet/update-distro-packages`
- Raw SKILL.md: https://api.skillmd.com/api/skills/dotnet/update-distro-packages/raw
- Safety review: pending (external: skill-scanner PASS, skillspector FAIL)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: .NET (Microsoft) (https://skillmd.com/u/dotnet)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/dotnet/update-distro-packages

---


# Update Distro Packages

Create and maintain per-distro JSON files in `release-notes/{version}/distros/`. Each file declares the native packages .NET depends on for a specific distribution, scoped to a single .NET version.

## Directory structure

```text
release-notes/{version}/distros/
├── dependencies.json    # distro-agnostic dependency list (what .NET needs)
├── index.json           # lists all per-distro file names
├── alpine.json          # per-distro: dependencies with distro-specific names
├── ubuntu.json
├── fedora.json
└── ...
```

## When to use

- A new .NET version needs its `distros/` directory created for the first time
- A distro release is added or removed from the support matrix
- A dependency package name changes (e.g. `libicu74` → `libicu76` on a new Ubuntu)
- Periodic audit to keep dependency data current

## Prerequisites

The `release-notes` tool must be installed for markdown generation and package availability queries. The public `dotnet-release` tool is now for browsing release data and CVEs.

```bash
# Check if already installed
release-notes --version
```

If not installed:

```bash
dotnet tool install -g release-notes \
  --add-source https://nuget.pkg.github.com/richlander/index.json
```

If already installed, update to latest:

```bash
dotnet tool update -g release-notes \
  --add-source https://nuget.pkg.github.com/richlander/index.json
```

## Inputs

The user provides:

- **.NET version** — which version to work on (e.g. "11.0")
- **Task** — what to do: create new distros/ directory, add a distro release, update package names, populate dotnet packages, etc.

Ask the user: **Do you want to update dotnet packages (which .NET packages are available in each distro)?** If so, ask them to provide or set `PKGS_ORG_TOKEN`. This requires a [pkgs.org Gold+ subscription](https://pkgs.org/about/).

## File schemas

### dependencies.json

Distro-agnostic list of packages .NET requires. Updated once per major release — rarely changes.

```json
{
  "channel_version": "11.0",
  "packages": [
    {
      "id": "libc",
      "name": "C Library",
      "required_scenarios": ["all"],
      "references": ["https://..."]
    },
    {
      "id": "openssl",
      "name": "OpenSSL",
      "required_scenarios": ["https", "cryptography"],
      "min_version": "1.1.1",
      "references": ["https://..."]
    }
  ]
}
```

Omit `min_version` and `references` when null/empty.

### index.json

```json
{
  "channel_version": "11.0",
  "distros": [
    "alpine.json",
    "azure_linux.json",
    "ubuntu.json"
  ]
}
```

Alphabetically sorted list of per-distro file names.

### Per-distro files (e.g. ubuntu.json)

Scoped to the .NET version of the parent directory. No `dotnet_versions` field — the version is the directory.

```json
{
  "name": "Ubuntu",
  "install_command": "apt-get install -y {packages}",
  "releases": [
    {
      "name": "Ubuntu 24.04 (Noble Numbat)",
      "release": "24.04",
      "dependencies": [
        { "id": "ca-certificates", "name": "ca-certificates" },
        { "id": "libc", "name": "libc6" },
        { "id": "libicu", "name": "libicu74" },
        { "id": "openssl", "name": "libssl3t64" }
      ]
    }
  ]
}
```

Optional fields on each release (populated by package availability queries):

```json
{
  "dotnet_packages": [
    { "component": "sdk", "name": "dotnet-sdk-11.0" },
    { "component": "runtime", "name": "dotnet-runtime-11.0" }
  ],
  "dotnet_packages_other": {
    "backports": {
      "install_command": "sudo apt-get install -y software-properties-common && sudo add-apt-repository ppa:dotnet/backports && sudo apt-get update",
      "packages": [
        { "component": "sdk", "name": "dotnet-sdk-11.0" }
      ]
    }
  }
}
```

The `install_command` in `dotnet_packages_other` is the command to **register the feed** — it must be run before packages can be installed with the distro's normal install command. This field is required for every alternative feed entry. See [Known alternative feed commands](#known-alternative-feed-commands) for values.

## Process

### Creating distros/ for a new .NET version

#### 1. Identify source data

Use `os-packages.json` from the same version (if it exists) or the previous .NET version as the source for dependency data:

```bash
cat release-notes/{version}/os-packages.json
# or from a previous version:
cat release-notes/{prev-version}/os-packages.json
```

Also reference `release-notes/{version}/supported-os.json` for the list of supported Linux distributions and versions.

#### 2. Create dependencies.json

Extract the `packages` array from `os-packages.json`. Convert keys to snake_case:

- `required-scenarios` → `required_scenarios`
- `min-version` → `min_version`

This file changes very rarely — it lists what .NET needs, not what distros call things.

#### 3. Create per-distro files

For each distribution in `os-packages.json`:

- **File name**: lowercase distro name, spaces → `_` (e.g. `azure_linux.json`, `centos_stream.json`)
- **`install_command`**: From the distro's `install-commands` — use the last command, format as `{command-root} {command-parts}`, normalize `{packageName}` → `{packages}`
- **`releases`**: One entry per distro release, with `dependencies` sorted alphabetically by `id`
- **Do NOT include** `dotnet_packages` or `dotnet_packages_other` — those are populated separately

The scope of distros/ is broader than `supported-os.json`. Include pre-release distro versions and permanent rolling channels where the package information is helpful:

- **Alpine edge** — always include; it tracks the rolling release
- **Debian sid (Unstable)** — always include
- **Pre-release distro versions** — e.g. Fedora beta, Ubuntu interim release before GA

These are informational — their presence does not imply official .NET support.

#### 4. Create index.json

List all per-distro file names alphabetically.

#### 5. Validate

```bash
# Verify all JSON parses
for f in release-notes/{version}/distros/*.json; do
  python3 -c "import json; json.load(open('$f'))" && echo "OK: $f"
done
```

Confirm every Linux distro in `supported-os.json` has a corresponding file.

#### 6. Generate markdown

Regenerate `dotnet-dependencies.md` from the JSON files:

```bash
release-notes generate dotnet-dependencies {version} release-notes
```

This produces `release-notes/{version}/dotnet-dependencies.md` with copy-pasteable install commands for each distro and release. Never hand-edit this file — it is generated from the JSON.

### Updating existing distros/ files

#### Adding a new distro release

1. Check `supported-os.json` for the new release
2. In the distro's JSON file, copy the most recent release entry
3. Update `name`, `release`, and any changed package names (e.g. `libicu74` → `libicu76`)
4. Insert in version order (newest first)

#### Removing a distro release

Delete the release object from the `releases` array. If the entire distro is dropped, delete the file and remove it from `index.json`.

#### Fixing package names

Update the `name` field in the relevant dependency entry. Package names change between distro releases due to shared library versioning (e.g. `libicu70` on Ubuntu 22.04 → `libicu74` on 24.04).

### Populating dotnet packages

This populates `dotnet_packages` and `dotnet_packages_other` in each per-distro file — recording which .NET packages (SDK, runtime, ASP.NET Core) are available in each distro's package feeds.

Requires `PKGS_ORG_TOKEN` to be set.

#### 1. Query package feeds

```bash
export PKGS_ORG_TOKEN=<token>
release-notes query distro-packages --dotnet-version {version} --output /tmp/distro-packages.json
```

This queries pkgs.org and supplemental feeds (Ubuntu backports via Launchpad, Homebrew, NixOS) and writes a JSON file with package availability per distro.

#### 2. Read query results

The output file has this structure:

```json
{
  "channel_version": "10.0",
  "last_verified": "2026-03-25",
  "distributions": [
    {
      "name": "Ubuntu",
      "releases": [
        {
          "name": "Ubuntu 24.04",
          "release": "24.04",
          "feeds": {
            "builtin": [
              { "component_id": "sdk", "package_name": "dotnet-sdk-10.0" },
              { "component_id": "runtime", "package_name": "dotnet-runtime-10.0" },
              { "component_id": "aspnetcore-runtime", "package_name": "aspnetcore-runtime-10.0" }
            ],
            "backports": [
              { "component_id": "sdk", "package_name": "dotnet-sdk-10.0" }
            ]
          }
        }
      ]
    }
  ]
}
```

#### 3. Map into per-distro files

For each distro+release in the query results, match it to the corresponding per-distro file and update:

- **`feeds["builtin"]`** → **`dotnet_packages`** (flat list)
- **Any other feed** (e.g. `feeds["backports"]`) → **`dotnet_packages_other["backports"]`**

Field mapping from query → per-distro file:

| Query field | Per-distro field |
|-------------|-----------------|
| `component_id` | `component` |
| `package_name` | `name` |

Every `dotnet_packages_other` entry **must** include an `install_command` — the command to register that feed before packages can be installed. See [Known alternative feed commands](#known-alternative-feed-commands) for the values to use.

Example — if the query returns this for Ubuntu 24.04:

```json
"feeds": {
  "builtin": [
    { "component_id": "sdk", "package_name": "dotnet-sdk-10.0" }
  ],
  "backports": [
    { "component_id": "sdk", "package_name": "dotnet-sdk-10.0" }
  ]
}
```

Then `ubuntu.json` release 24.04 becomes:

```json
{
  "name": "Ubuntu 24.04 (Noble Numbat)",
  "release": "24.04",
  "dependencies": [ ... ],
  "dotnet_packages": [
    { "component": "sdk", "name": "dotnet-sdk-10.0" }
  ],
  "dotnet_packages_other": {
    "backports": {
      "install_command": "sudo apt-get install -y software-properties-common && sudo add-apt-repository ppa:dotnet/backports && sudo apt-get update",
      "packages": [
        { "component": "sdk", "name": "dotnet-sdk-10.0" }
      ]
    }
  }
}
```

#### 4. Handle distros not in query results

Not all distros appear in the query results (e.g. RHEL packages are in a subscription-only repo). Leave `dotnet_packages` absent for those — do not add an empty list.

#### 5. Query distro package lists directly

pkgs.org may not have data for newly released distro versions yet. When a supported distro release is missing from the query results, check the distro's own package list as a fallback.

**Ubuntu** — query `packages.ubuntu.com` for the release codename:

```bash
curl -s "https://packages.ubuntu.com/{codename}/allpackages?format=txt.gz" \
  | gunzip | grep -i "dotnet.*{major}"
curl -s "https://packages.ubuntu.com/{codename}/allpackages?format=txt.gz" \
  | gunzip | grep -i "aspnetcore.*{major}"
```

For example, to check Ubuntu 26.04 (codename `resolute`) for .NET 10:

```bash
curl -s "https://packages.ubuntu.com/resolute/allpackages?format=txt.gz" \
  | gunzip | grep -iE "(dotnet|aspnetcore).*10"
```

If the packages are found (e.g. `dotnet-sdk-10.0`, `dotnet-runtime-10.0`, `aspnetcore-runtime-10.0` in the `universe` component), add them as `dotnet_packages` for that release.

**Fedora** — query the Bodhi update system and source RPM spec:

```bash
# Check which Fedora releases have dotnet builds
curl -s "https://bodhi.fedoraproject.org/updates/?packages=dotnet{major}.{minor}&rows_per_page=20" \
  | python3 -c "
import sys, json
for u in json.load(sys.stdin).get('updates', []):
    print(f\"{u['release']['name']}: {u['title']} ({u['status']})\")"
```

For example, to check .NET 10.0:

```bash
curl -s "https://bodhi.fedoraproject.org/updates/?packages=dotnet10.0&rows_per_page=20" \
  | python3 -c "
import sys, json
for u in json.load(sys.stdin).get('updates', []):
    print(f\"{u['release']['name']}: {u['title']} ({u['status']})\")"
```

Results show each Fedora release with its build status (e.g. `F44: dotnet10.0-10.0.104-1.fc44 (testing)`). To confirm the subpackage names, check the spec file:

```bash
curl -s "https://src.fedoraproject.org/rpms/dotnet{major}.{minor}/blob/f{release}/f/dotnet{major}.{minor}.spec" \
  | grep -E "^%package|^Name:"
```

Fedora packages follow a consistent naming scheme (`dotnet-sdk-{major}.{minor}`, `dotnet-runtime-{major}.{minor}`, `aspnetcore-runtime-{major}.{minor}`), so this is mainly a confirmation step.

**Other distros** — Alpine packages can be checked at `pkgs.alpinelinux.org`. For most other distros, pkgs.org covers them well. Only fall back to direct queries when pkgs.org is missing data for a release you expect to have packages.

#### 6. Present summary

Show the user a summary of which distros+releases have packages and from which feeds, and flag any gaps (supported distro but no packages found).

### Regenerate markdown

After any JSON changes, regenerate both markdown files:

```bash
release-notes generate dotnet-dependencies {version} release-notes
release-notes generate dotnet-packages {version} release-notes
```

- `dotnet-dependencies.md` — what OS packages .NET requires (from dependency data)
- `dotnet-packages.md` — where to get .NET packages (from `dotnet_packages` / `dotnet_packages_other` data)

> **Important:** Do not hand-edit these markdown files. They are generated from the distros/ JSON files. If the output needs to change, update the generator or template in [dotnet-release](https://github.com/richlander/dotnet-release).

### Commit

```bash
git add release-notes/{version}/distros/ release-notes/{version}/dotnet-dependencies.md release-notes/{version}/dotnet-packages.md
git commit -m "Update {version} distro packages — <summary>"
```

## Known alternative feed commands

When packages come from a non-builtin feed, the `install_command` field tells users how to register that feed before installing packages. Use the exact commands below.

### Ubuntu backports PPA

Feed name: `backports`

```bash
sudo apt-get install -y software-properties-common && sudo add-apt-repository ppa:dotnet/backports && sudo apt-get update
```

This PPA provides .NET packages for older Ubuntu LTS releases that don't carry .NET in the default archive. `add-apt-repository` comes from `software-properties-common`, which is often missing in containers and other minimal environments. After registering, packages are installed with the normal `apt-get install` command.

### Microsoft packages.microsoft.com (PMC)

Feed name: `microsoft`

```bash
wget https://packages.microsoft.com/config/{distro}/{version}/packages-microsoft-prod.deb -O /tmp/packages-microsoft-prod.deb && sudo dpkg -i /tmp/packages-microsoft-prod.deb && rm /tmp/packages-microsoft-prod.deb
```

Replace `{distro}` and `{version}` with the distro name and version (e.g. `ubuntu/22.04`, `debian/12`). Note: Microsoft is phasing out PMC for Ubuntu 24.04+ — prefer the builtin or backports feed when available.

### Other feeds

If the query returns a feed name not listed above, ask the user for the registration command. Do not guess — incorrect feed setup commands are worse than none.

## Key facts

- Files are version-scoped — `release-notes/11.0/distros/ubuntu.json` is about .NET 11.0 on Ubuntu
- Dependencies use an agnostic `id` (e.g. `libicu`) with a distro-specific `name` (e.g. `libicu74`)
- `dependencies.json` is the "what .NET needs" list; per-distro files map those to real package names
- Package names like `libicu` are versioned on Debian/Ubuntu (e.g. `libicu76`) but not on Fedora/RHEL (just `libicu`)
- Alpine uses a different naming scheme for .NET packages: `dotnet{major}-{component}` (e.g. `dotnet9-sdk`)
- Debian/Ubuntu/Fedora use: `dotnet-sdk-{major}.{minor}`, `dotnet-runtime-{major}.{minor}`
- Microsoft is phasing out packages.microsoft.com for Ubuntu 24.04+ and newer Fedora
- `install_command` uses `{packages}` as a placeholder for the package list

## Display name rules

The `name` fields (both top-level distro name and per-release names) must use full display names, **never acronyms**. These names appear in generated markdown and user-facing documentation.

| ❌ Acronym | ✅ Full display name |
|-----------|----------------------|
| RHEL | Red Hat Enterprise Linux |
| SLES | SUSE Linux Enterprise Server |

**Examples:**

- Top-level: `"name": "Red Hat Enterprise Linux"` (not `"RHEL"`)
- Release: `"name": "Red Hat Enterprise Linux 9"` (not `"RHEL 9"`)
- Top-level: `"name": "SUSE Linux Enterprise Server"` (not `"SLES"`)
- Release: `"name": "SUSE Linux Enterprise Server 15.7"` (not `"SLES 15.7"`)

File names (`rhel.json`, `sles.json`) remain short — only the `name` fields inside must use full names. When creating new distro files or adding releases, always verify the display name is the full product name, not an abbreviation.

