Skip to content

feat(packaging): add amd-repo-setup package for ROCm repository setup - #7269

Merged
lsudarsh-amd merged 12 commits into
mainfrom
shared/amdrocm-repo-package
Oct 22, 2026
Merged

lsudarsh-amd merged 12 commits into
mainfrom
shared/amdrocm-repo-package

Conversation

@lsudarsh-amd

@lsudarsh-amd lsudarsh-amd commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Installing ROCm today needs a manual repository setup. The user has to fetch and dearmor the AMD signing key, write a sources or repo file by hand, and look up the correct URL for their distribution and stream.

This change adds amd-repo-setup, a repo configuration package that replaces that multi-step manual setup. Once it is installed, ROCm can be installed and updated with the native package manager.

This is the second of five pull requests:

Contributes to #6986.

Technical Details

build_repo_setup_package.py renders the package from the Jinja templates under template/repo/. It builds one package per OS profile (ubuntu2404, rhel8, rhel10, sles16) and stream (stable, rc, nightly).

Repository URLs are derived per OS profile. The URL is --repo-base-url plus the profile, for example https://stable.repo.amd.com/rocm/core/packages/ubuntu2404/; rpm adds x86_64/. Nightly also adds the folder for one build (--repo-sub-folder).

The package is amd-repo-setup, the same name as the amd-repo-setup/ folder it will be published to (PR 4). The files that carry the name follow it: the builder, its test and the rpm spec template (amd-repo-setup.spec.j2, picked by package name). The repository the package configures keeps its amdrocm names, the same ones the documented manual setup uses: repo id amdrocm-stable, key amdrocm.gpg.

Package names include the distro and a build number: amd-repo-setup_10.0.0-1~ubuntu2404_all.deb, amd-repo-setup-10.0.0-1.stable.el8.noarch.rpm. The build number is --version-suffix (default 1). The docs have a table of examples.

The package version is --rocm-version, as given, on every stream. CI passes the version TheRock computes for the release type, the same one the ROCm packages in that repository carry (10.1.0, 10.1.0~pre3, 10.2.0~20261013). So the package sorts the way they do, below stable at the same ROCm version, and no two streams share a package name. rpm also carries the stream in the release field (1.stable.el8).

The signing key is fetched at build time. For signed streams it is retrieved over https and size-bounded. A missing key URL fails at once; only timeouts and rate limiting are retried. The file must contain only the pinned AMD key, and only that key is embedded. It is never stored in the source tree. For offline builds, --gpg-key-file embeds a local key as provided. The nightly repository is unsigned upstream, so nightly packages ship no key and disable signature checking.

The package conflicts with amdgpu-install, which configures a different ROCm repository and raises it above the default priority. Its repository files use different names from ours, so nothing else stops both being installed, and its repository then wins resolution silently. On apt the conflict is settled by removing amdgpu-install, which leaves its configuration files behind. The deb postinst detects that state and tells the user to run apt purge. dnf and zypper refuse the pair outright and remove their %config files on erase, so no equivalent is needed there.

The rpm %post prints a refresh message, like the deb postinst.

Nothing builds the repo packages yet. The script writes finished packages to --dest-dir and is run once per profile and stream. The workflow that calls it is in #7280; until then it is invoked by hand, as the documentation describes. Each format needs its own tooling, dpkg-buildpackage for deb and rpmbuild for rpm, so the CI job builds each profile in a container that has it.

build_tools/.gitignore is deleted. Its only entry was an unanchored repo pattern, which would hide the entire new template/repo/ directory from git. The tool it guarded was removed in #128.

Also in this PR: the rc stream

This also builds the rc stream, which goes beyond the review feedback.

RFC0012 requires stable and rc at v1, while nightly, weekly and dev come at v2. The tool used to build stable and nightly, so it shipped a v2 stream but was missing a v1 one. rc was left out earlier because its repositories were empty. They aren't anymore: rc.repo.amd.com now serves more packages than stable, is signed on all four OS profiles, and uses the same signing key.

A few details:

  • The installed file is amdrocm-stablerc, not amdrocm-rc. stablerc is the name RFC0012 gives the rc tier, so the file uses it while the subdomain stays rc. That way a later split into per-tier packages won't need to rename a file on users' machines.
  • Versioning. Like every stream, rc uses the version it's given. CI passes the prerelease version of the ROCm packages (10.1.0~pre3 on deb, 10.1.0~rc3 on rpm), which sorts below the GA.
  • Suites: is stable for every stream. That's what the servers publish (dists/stable/, Suite: stable); dists/stablerc/ returns 404.

CI wiring for rc is in #7280, not here.

Notes for reviewers

  • The list-profiles subcommand has no caller yet; get-repo-setup-params in feat(packaging): add amd-repo-setup build parameters, upload and install test #7276 calls the list_profiles() function directly. Unit tests cover both.
  • The test file sits in build_tools/tests/ rather than packaging/linux/tests/, where the other tests for that module's directory live. build_tools/tests/ is already in testpaths, so these tests run in CI from the first push, including on the Windows leg.
  • The root-ownership directives in rules.j2 and the spec match the other ROCm packages. Building as a non-root user already produces root:root without them.
  • No apt pin or priority= is shipped; the package sits at each manager's default. Matching amdgpu-install's pin would raise this whole CDN above the distribution's own repositories. The conflict declaration, and the purge the postinst points at, address the overlap instead. Happy to add a pin if you would prefer one.
  • weekly and dev aren't built: weekly has no packages yet, and dev isn't for end users.
  • Switching streams on Debian/Ubuntu isn't handled in the package yet: the old .sources stays behind, so the docs say to purge first. A follow-up will handle it in the package before it's published.

Test Plan

All testing ran in containers.

  • Built the package for all four OS profiles and all three streams with the versions CI computes, and inspected each payload with dpkg-deb -c or rpm -qp.
  • Checked with real dpkg and rpm that each stream's package sorts the same way as the ROCm packages in its repository.
  • Installed each package on ubuntu2404, rhel8, rhel10 and sles16, refreshed metadata against the live stable, rc and nightly repositories, and resolved the amdrocm package.
  • Tested the amdgpu-install conflict in both directions on both formats, using the real amdgpu-install 31.40 and 31.50 packages, then repeated it on each format with the declaration removed as a control.
  • Installed the deb against all four amdgpu-install states: not installed, installed, removed but not purged, and purged.
  • Tested the key check with real gpg: the real key alone, and with a throwaway extra key. Built with a key URL that returns 404 and with one that serves something other than a key.
  • Tested switching streams in every direction on apt, dnf and zypper.
  • Ran the documented install, verification and build commands.
  • Ran the unit tests with the module's path handling bound to the Windows flavour.
  • Ran pre-commit, shellcheck, and the full unit test suite.

Test Result

Check Result
New unit tests 183 pass. Tests that need dpkg, rpm, gpg or the de_DE locale skip where those are missing.
Full suite No new failures. The delta is exactly the new test file.
Package payload Correct on all four profiles. Every file lands at its documented path, owned by root:root.
Version ordering Each stream's package sorts the same way as the ROCm packages in its repository. At the same ROCm version, stable is above rc and nightly on both formats.
Signing key The real key is accepted. The real key plus another key, in either order, is rejected. A key URL that returns 404 fails on the first attempt, and a response that isn't a key fails with gpg's own message.
Live repository Metadata refreshes for all three streams on all four profiles, and amdrocm resolves. A real signed ROCm package installs on each profile using the shipped key.
Documented commands All ran as written. zypper needs --allow-unsigned-rpm for a locally built file, which the documentation states.
Conflict enforcement dpkg, dnf and zypper refuse the pair in both directions. apt resolves it by removing the installed package.
Conflict controls With the declaration removed, both packages install together and two then own competing repository files.
postinst Warns only in the removed-but-not-purged state, including after apt removes amdgpu-install to resolve the conflict, and never fails an install.
Stream switching dnf switches directly. apt needs a purge first and zypper needs --oldpackage, as documented.
Windows path handling Binding the Windows flavour makes the keyring test fail, so a regression there would be caught.
Template naming The spec template is picked by package name. A template that doesn't match the package name fails 11 unit tests and the rpm build.
pre-commit, shellcheck Clean.

Submission Checklist

@therock-pr-bot

therock-pr-bot Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

✅ All Policy Checks Passed

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

🎉 All policy checks passed!

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

Adds a configuration package pointing apt, dnf, or zypper at a public
ROCm repository, so ROCm installs and updates through the native package
manager instead of a manual multi-step setup.

One package is generated per OS profile and release line. The version
carries the line, so packages from different lines are never confusable,
and URL derivation is isolated in one function because the published
layout is expected to change. For signed lines the signing key is
fetched and fingerprint-checked at build time; it is never stored in the
source tree.

It conflicts with amdgpu-install, whose competing ROCm repository uses
different filenames and sits above the default priority, so nothing
else would stop it silently winning. On apt that conflict is settled by
removal, which leaves the configuration behind — hence the deb postinst
pointing at "apt purge".

build_tools/.gitignore is removed: its unanchored "repo" pattern would
hide the new template/repo/ tree from git, and the tool it guarded is
long gone.
ROCm 10 ships from <stream>.repo.amd.com. The legacy packages-multi-arch
release line is frozen at ROCm 7.14, so a package built against it
points users three releases back.

Streams are modelled by layout shape, not name: flat (stable) serves one
repository per distro, per-build (nightly) adds a YYYYMMDD-runid
segment. Signing follows from the shape rather than a parallel list, so
the two cannot drift apart.

  --release-type prerelease  ->  --stream stable
  amdrocm.sources            ->  amdrocm-<stream>.sources

The stream in the filename stops one stream's package silently replacing
another's config. The key is passed whole via --gpg-key-url; it sits
outside the packages base and cannot be derived from it. The deb822 file
gains X-Repo-Id and Enabled, matching the published install
instructions.

rc is absent: it resolves but serves no content, so a package pointing
there would install and then find nothing.

Verified in containers across ubuntu2404/rhel8/rhel10/sles16 on both
streams: build, install, clean refresh, signature check, amdrocm
10.0.0-4.
@lsudarsh-amd
lsudarsh-amd force-pushed the shared/amdrocm-repo-package branch from 71cbd7b to de7e7b4 Compare August 28, 2026 16:23
@therock-pr-bot

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

@dbieleck

Copy link
Copy Markdown

Folks, let's move this along this week. Thanks.

Comment thread docs/packaging/rocm_repo_setup.md Outdated
Ubuntu:

```bash
sudo apt install ./repo-package-out/amdrocm-repo_*_all.deb

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.

Once the repo packages are merged might have to update these steps in the RELEASES.md file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I agree. amdrocm-repo has no public download URL yet, so those steps would not be actionable until the publish side lands. I have left RELEASES.md alone here and will update it as a follow up once the package is reachable.

Comment thread build_tools/.gitignore
@@ -1 +0,0 @@
repo

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.

Is this a oversight change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is deliberate. Scott asked for the file to be removed rather than edited in #6901, since the repo tool it guarded has been unused since #128. Its bare repo pattern would also have matched the new template/repo/ directory and hidden those files.

embedded in the package; it is never stored in the source tree.

```
python build_repo_package.py \

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.

With these parameters, looks like the package created will be amdrocm-repo_10.0.0_all.deb Which is not as per the packaging name standard.
amdrocm-_-<build_no>_amd64.deb

Also the package name will be same for ubuntu2404, ubuntu2604, debian10 or any other debian os flavors.

One possibility is to name the packages something like this.

10.0.0-1~noble1      # Ubuntu 24.04 (Noble)
10.0.0-1~jammy1       # Ubuntu 22.04 (Jammy)
10.0.0-1~bookworm1    # Debian 12

Another option would be to have one deb package which has all the config for all deb flavors and install only the correct one checking the OS version

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this. Deb now builds as:

amdrocm-repo_10.0.0-1~ubuntu2404_all.deb
amdrocm-repo_20260716.12345-1~ubuntu2404_all.deb    (nightly)

For the distro token I used the OS-profile name rather than a codename, to keep things consistent with the directory names and to avoid maintaining a codename mapping.

I applied the same fix to rpm, where %{?dist} is empty on SUSE, so sles15 and sles16 would have collided once a second SUSE profile was added. RHEL package names are unchanged.

I avoided taking the second approach you suggested regarding a single deb choosing config at install time, since it needs OS detection in a maintainer script and the repository URL is already per distro.

--stream stable \
--repo-base-url https://stable.repo.amd.com/rocm/core/packages \
--rocm-version 10.0.0 \
--dest-dir ./output

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.

Looks like the parameter handling is bit complicated. Can you give more samples and the expected output(package name and version) for better review?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The module docstring and docs/packaging/rocm_repo_setup.md have been updated to both carry the naming rule and a worked examples table. All rows use --rocm-version 10.0.0 and the default --version-suffix. The nightly rows add --repo-sub-folder 20260716-12345.

--os-profile --stream Resulting file
ubuntu2404 stable amdrocm-repo_10.0.0-1~ubuntu2404_all.deb
ubuntu2404 rc amdrocm-repo_10.0.0~pre-1~ubuntu2404_all.deb
ubuntu2404 nightly amdrocm-repo_20260716.12345-1~ubuntu2404_all.deb
rhel8 stable amdrocm-repo-10.0.0-1.stable.el8.noarch.rpm
rhel8 rc amdrocm-repo-10.0.0-1.rc.el8.noarch.rpm
rhel8 nightly amdrocm-repo-20260716-12345.1.nightly.el8.noarch.rpm
rhel10 stable amdrocm-repo-10.0.0-1.stable.el10.noarch.rpm
sles16 stable amdrocm-repo-10.0.0-1.stable.sles16.noarch.rpm

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.

Here the version ordering is
nightly > stable > rc.

Which is not the same scheme used in other ROCm packages. Why the nightly version scheme needs to be different from rc and release?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is fixed as of c7500eb. Nightly now follows the ROCm version scheme (e.g. 10.2.0~20261013).


%changelog
* {{ changelog_date }} {{ maintainer }} - {{ version }}-{{ release }}
- ROCm {{ rocm_version }} repository configuration.

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.

rpm is not having a post install script like debian. Which is a difference in package behavior

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

rpm now has a post install script. It prints the same message the deb postinst does, with the refresh command for each family: sudo dnf makecache or sudo zypper refresh. I confirmed the message reaches the user on both dnf install and zypper install.

amdrocm-repo produced one filename for every debian-family profile and
carried no build number:

  amdrocm-repo_10.0.0_all.deb -> amdrocm-repo_10.0.0-1~ubuntu2404_all.deb

The token is the os-profile name, not a Debian codename, which would not
sort: bullseye ranks above bookworm. rpm takes it from an explicit
dist_tag rather than %{?dist}, which reads the build host and is empty on
SUSE; RHEL names are unchanged.

rc is now built, reversing an earlier commit that skipped it for serving
no content: RFC0012 requires stable and rc at v1. Also adds a
message-only %post mirroring the deb postinst.
The key check only looked at the first key in the downloaded file, so a
file with an extra key appended still passed and both keys ended up
trusted. Now the file must contain just the pinned key, and only that
key is embedded. The tests use real gpg instead of mocks.

The docs said installing another stream's deb over the current one
switches repos. It doesn't: the old .sources stays behind. They now say
to purge first on Ubuntu, and to use --oldpackage on SLES.

Also swaps a link to an internal repo for the public install docs,
fixes the --repo-base-url help, and corrects a few wrong comments.
@lsudarsh-amd

Copy link
Copy Markdown
Contributor Author

@nunnikri Thanks for the initial review. I've made some changes since then:

  • Package names include the distro and a build number (--version-suffix, default 1): amdrocm-repo_10.0.0-1~ubuntu2404_all.deb, amdrocm-repo-10.0.0-1.stable.el8.noarch.rpm. Examples are in the docs.
  • rpm %post now prints a refresh message, like the deb postinst.
  • Signing key: the check only looked at the first key in the file. Now the file must contain only the pinned key, and only that key is embedded.
  • Docs: purge before switching streams on Ubuntu, use --oldpackage on SLES, and use apt purge to remove. Plus a few text fixes.
  • rc stream: can now be built.

@arvindcheru arvindcheru left a comment

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.

LGTM, Thank you.

  • Verified the install Test FULL (ub24/rhel/sles), uninstall, no-gpg install_test on shared/amdrocm-repo-ci @ a0bfec8
  • Uninstall: Must exclude/handle amdrocm-repo before CI enablement.
  • (non-blocking): refresh PR body to streams stable/rc/nightly (instead of prerelease/release);

Please hold merge until existing/pending review threads are re-acked.

Nightly took its version from the build folder, so it sorted above
stable. On deb, rc added its own ~pre on top of the ~preN CI already
passes.

Now every stream uses --rocm-version as is. CI passes the same version
the ROCm packages get, so the repo package sorts the same way they do.
The tests use those versions and check the order with dpkg and rpm.
@lsudarsh-amd

Copy link
Copy Markdown
Contributor Author

@nunnikri As discussed, package contents for all 12 packages (3 streams x 4 distros), built with the CI job's steps in CI's container images and the versions CI computes. Each one installed and resolved amdrocm from its live repository. The packages themselves are in the attached amdrocm-repo-packages.zip if you want a closer look.

deb (ubuntu2404) rpm (rhel8)
stable amdrocm-repo_10.1.0-1~ubuntu2404_all.deb amdrocm-repo-10.1.0-1.stable.el8.noarch.rpm
rc amdrocm-repo_10.1.0~pre3-1~ubuntu2404_all.deb amdrocm-repo-10.1.0~rc3-1.rc.el8.noarch.rpm
nightly amdrocm-repo_10.2.0~20261013-1~ubuntu2404_all.deb amdrocm-repo-10.2.0~20261013-1.nightly.el8.noarch.rpm

rhel10 and sles16 match rhel8 apart from the .el10 / .sles16 suffix, and on sles16 the repo file is in /etc/zypp/repos.d/.

Installed files, all root:root 0644:

stable rc nightly
deb /etc/apt/sources.list.d/amdrocm-stable.sources
/usr/share/keyrings/amdrocm.gpg
/etc/apt/sources.list.d/amdrocm-stablerc.sources
/usr/share/keyrings/amdrocm.gpg
/etc/apt/sources.list.d/amdrocm-nightly.sources
rpm /etc/yum.repos.d/amdrocm-stable.repo
/etc/pki/rpm-gpg/RPM-GPG-KEY-amdrocm
/etc/yum.repos.d/amdrocm-stablerc.repo
/etc/pki/rpm-gpg/RPM-GPG-KEY-amdrocm
/etc/yum.repos.d/amdrocm-nightly.repo

The repo file is a conffile (deb) or %config (rpm), and the deb also ships its changelog.Debian.gz. Every package declares Conflicts: amdgpu-install. The signed streams embed the key.

Repo Files Contents

/etc/apt/sources.list.d/amdrocm-stable.sources

X-Repo-Id: amdrocm-stable
Types: deb
URIs: https://stable.repo.amd.com/rocm/core/packages/ubuntu2404/
Suites: stable
Components: main
Architectures: amd64
Enabled: yes
Signed-By: /usr/share/keyrings/amdrocm.gpg

/etc/apt/sources.list.d/amdrocm-stablerc.sources

X-Repo-Id: amdrocm-stablerc
Types: deb
URIs: https://rc.repo.amd.com/rocm/core/packages/ubuntu2404/
Suites: stable
Components: main
Architectures: amd64
Enabled: yes
Signed-By: /usr/share/keyrings/amdrocm.gpg

/etc/apt/sources.list.d/amdrocm-nightly.sources

X-Repo-Id: amdrocm-nightly
Types: deb
URIs: https://nightly.repo.amd.com/rocm/core/packages/ubuntu2404/20261013-38325106112/
Suites: stable
Components: main
Architectures: amd64
Enabled: yes
Trusted: yes

/etc/yum.repos.d/amdrocm-stable.repo

[amdrocm-stable]
name=AMD ROCm
baseurl=https://stable.repo.amd.com/rocm/core/packages/rhel8/x86_64/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-amdrocm

/etc/yum.repos.d/amdrocm-stablerc.repo

[amdrocm-stablerc]
name=AMD ROCm
baseurl=https://rc.repo.amd.com/rocm/core/packages/rhel8/x86_64/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-amdrocm

/etc/yum.repos.d/amdrocm-nightly.repo

[amdrocm-nightly]
name=AMD ROCm
baseurl=https://nightly.repo.amd.com/rocm/core/packages/rhel8/20261013-38325106112/x86_64/
enabled=1
gpgcheck=0

amdrocm-repo-packages.zip

@lsudarsh-amd

Copy link
Copy Markdown
Contributor Author

@nunnikri Nightly versioning is updated. Every stream now uses --rocm-version as given, so nightly follows the ROCm scheme (eg. 10.2.0~20261013).

# The package name. The repository identifier used for the repo file and its
# section header is derived per stream by ``repo_id()``. A single repository is
# configured per package.
PACKAGE_NAME = "amdrocm-repo"

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.

I thought we decided to make it amd-repo or amd-repo-setup.
And if we are changing the name, its better to rename the files as well accordingly like amdrocm-repo.spec.j2 -> amd-repo-setup.spec.j2

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As discussed, I've updated the package name to use amd-repo-setup to be consistent with the URL change. Relevant file names and function calls have also been updated.

- Rename the package to amd-repo-setup, the same name as the
  amd-repo-setup/ folder it will be published to.
- Rename the files that carry the name: the rpm spec template, the
  builder (build_repo_setup_package.py) and its test. The builder and
  its tests pick the spec template by package name.
- Use repo-setup-package-out as the output directory in the docs.
- The repo files, repo ids and signing key keep their amdrocm names,
  which come from the documented manual setup.
@lsudarsh-amd lsudarsh-amd changed the title feat(packaging): add amdrocm-repo package for ROCm repository setup feat(packaging): add amd-repo-setup package for ROCm repository setup Oct 19, 2026
@lsudarsh-amd
lsudarsh-amd requested a review from nunnikri October 19, 2026 05:54

@nunnikri nunnikri left a comment

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.

LGTM approved.
@raramakr can you also take a look for a second pair of eyes

return key
except (urllib.error.URLError, TimeoutError, ConnectionError) as e:
last_error = e
if attempt < GPG_FETCH_ATTEMPTS:

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.

Adding some comments from my review agent.
urllib.error.HTTPError is a subclass of urllib.error.URLError, so a
definite, permanent failure (e.g. a 403/404 from a wrong --gpg-key-url) is
still caught by this clause and retried GPG_FETCH_ATTEMPTS (3) times with
backoff, exactly like a transient network blip. This is unchanged from the
10-09 review.

Contrast with verify_repo_url
(build_tools/packaging/linux/build_repo_setup_package.py:442-487),
which deliberately distinguishes the two: it catches HTTPError separately
and only retries RETRYABLE_HTTP_STATUS = {408, 429}. That same reasoning
applies to the key fetch but isn't implemented there.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure, this is a good change. I've updated this as of 119adea. The key fetch now only retries 408 and 429, like verify_repo_url.

) from last_error


def _run_gpg(argv: list[str], **kwargs) -> subprocess.CompletedProcess:

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.

_run_gpg (build_tools/packaging/linux/build_repo_setup_package.py:605-621)
and its call sites in _extract_pinned_key and dearmor_key all pass
stdout=subprocess.PIPE, stderr=subprocess.PIPE. _run_gpg only
special-cases FileNotFoundError (missing binary); if gpg exits non-zero for
any other reason, subprocess.run(..., check=True) raises
subprocess.CalledProcessError, whose default str() does not include the
captured stdout/stderr — so the actual gpg error message is silently
dropped from the build log.

Suggested fix: catch subprocess.CalledProcessError in _run_gpg and re-raise with e.stderr included in the message, the same way the FileNotFoundError branch already does.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Implemented this in 119adea. _run_gpg now re-raises with gpg's stderr.

The signing-key fetch now retries only timeouts and rate limiting, like
verify_repo_url. Any other HTTP error, such as a 404 from a wrong
--gpg-key-url, fails on the first attempt.

When gpg fails, the error now includes gpg's own output instead of just
the exit status.

@arvindcheru arvindcheru left a comment

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.

LGTM, Thanks for rename + GPG fail-fast/stderr on 119adea. Note: Please confirm Unit Tests on this tip (Looks to be unrelated (find_latest_artifacts) but please check and confirm)

@lsudarsh-amd

Copy link
Copy Markdown
Contributor Author

LGTM, Thanks for rename + GPG fail-fast/stderr on 119adea. Note: Please confirm Unit Tests on this tip (Looks to be unrelated (find_latest_artifacts) but please check and confirm)

@arvindcheru Confirmed, unit tests pass on 119adea. The earlier failure was find_latest_artifacts_test, which calls the live GitHub API. The re-run passed.

@raramakr

Copy link
Copy Markdown
Contributor

Follow-up (non-blocking): Instead of separate Jinja templates for amd-repo-setup (template/repo/deb/, template/repo/rpm/), please reuse the existing templates in template/ so that all packaging templates are maintained in one place. This needs only small optional additions

@lsudarsh-amd
lsudarsh-amd merged commit 9d224cc into main Oct 22, 2026
288 of 299 checks passed
@lsudarsh-amd
lsudarsh-amd deleted the shared/amdrocm-repo-package branch October 22, 2026 18:58
@github-project-automation github-project-automation Bot moved this from TODO to Done in TheRock Triage Oct 22, 2026
lsudarsh-amd added a commit that referenced this pull request Oct 22, 2026
…all test (#7276)

## Motivation

The `amd-repo-setup` bootstrap package needs a few pieces before CI can
build and ship it. This pull request adds:
- the build parameters the packaging workflow reads;
- a script that uploads a built package to the run's artifacts bucket;
- an install-harness mode that installs the built package.

The workflow job that runs them comes in the next pull request, and
publishing to repo.amd.com in the one after.

It also resolves two review comments on #6901, which this split
supersedes. The #6901 publish helper imported `boto3` inline and did not
use `WorkflowOutputRoot`. Its replacement here resolves the run's bucket
and prefix through `WorkflowOutputRoot` and writes through
`StorageBackend`. That takes `boto3` out of the script entirely, rather
than hoisting the import to the top of the file. The dependency still
exists behind `S3StorageBackend`, but the script no longer touches it.

This is the third of five pull requests:

- PR 0 (#7004) added `packaging/linux/tests` to `testpaths`, so the
tests the later ones add actually run in CI.
- PR 1 (#7269) adds the builder, its templates, its unit tests and the
user documentation.
- PR 2 (this one) adds the build parameters (`get-repo-setup-params`),
the upload to the run's artifacts bucket and the install-harness package
mode.
- PR 3 (#7280) wires the CI workflow: build, inspect and upload, off by
default.
- PR 4 (#9352) adds the `WorkflowOutputRoot` location for the package
and publishes it to `v5/amd-repo-setup/` during release promotion. Those
changes to shared CI files are kept in their own PR, so they can be
reviewed on their own.

This builds on #7269's branch, because the build parameters and the
harness read the package's names and stream rules from
`build_repo_setup_package`. The diff shows only this PR's files.

Contributes to #6986.

## Technical Details

### What this adds

**Upload script.** `upload_repo_setup_package.py` takes the builder's
`--dest-dir` and uploads the single package it finds there; zero or
several is an error. It takes `--run-id`, `--tier`, `--os-profile` and
`--pkg-type`, and writes to
`{run_id}-linux/amd-repo-setup/{os_profile}/amd-repo-setup-{tier}.{pkg_type}`
in the run's artifacts bucket. After a real upload it writes the
package's https URL to `$GITHUB_OUTPUT` as `repo_setup_package_url`; a
dry run or a local staging run writes none.

The bucket and prefix come from
`WorkflowOutputRoot.from_workflow_run()`. The #6901 version took
`--bucket`, `--prefix` and `--endpoint-url`. Two consequences:

1. **The `release` line raises.** It isn't an artifact release type, so
the bucket lookup rejects it. Release uploads happen outside CI, and
every `therock-release-*` bucket has `iam_role=None`. The upload job
does not run for `release`, so the raise is a backstop.
2. **The script needs CI context.** Its job environment must have
`GITHUB_REPOSITORY`, `RELEASE_TYPE` and the workflow event payload.

The script builds the key from the root's `bucket` and `prefix`.
`WorkflowOutputRoot` gets a method for this folder in PR 4, together
with the publish step that reads it, and the script switches to it
there. The key does not change.

The layout follows the repo-packages folder in
[RFC0012](https://github.com/ROCm/TheRock/blob/main/docs/rfcs/RFC0012-Repo-Structure.md)
(`amdrepos/` there, `amd-repo-setup/` here):
- the tier is part of the file name;
- the name is fixed per tier, so the download URL stays stable;
- the per-distro folder stops one profile's package overwriting
another's.

The folder sits outside `packages/`, so release promotion, which copies
`tarballs/`, `python/` and `packages/<type>/`, does not copy it.

The script's name follows TheRock's convention (upload to the artifacts
bucket, publish to the release buckets) and names the package, to tell
it apart from `upload_package_repo.py` in the same folder, which uploads
the content repository. Nothing on `main` calls this script yet; the
workflow that does lands in #7280.

**Build parameters.** `get_url_repo_params.py get-repo-setup-params`
writes everything the build matrix needs in one `$GITHUB_OUTPUT` call:
- `os_profiles` and `images`, the matrix;
- then `stream`, `tier`, `repo_base_url`, `gpg_key_url` and
`repo_sub_folder`.

The build line and the stream are different vocabularies: `release` maps
to `stable`, `prerelease` to `rc` and `nightly` to `nightly`. This
script holds only that mapping and where each stream is served.
Everything else about a stream comes from `build_repo_setup_package`:
- the key URL is emitted only for a signed stream;
- the dated build folder only for a per-build stream;
- the tier is the stream's repo file name without its `amdrocm-` prefix,
so `rc` gives `stablerc`.

A line with no public stream (`ci`, `dev`) gets those five values empty
and still gets the matrix. rc.repo.amd.com serves all four profiles,
signed with the same key as stable, whose fingerprint matches the
builder's pin.

`build_repo_setup_package` is imported inside the subcommand rather than
at module level. The script's other subcommands run on the runner's
system Python in `test_native_linux_packages_install.yml`, in a job that
installs no Python packages.

**Install harness.** `native_linux_package_install_test.py
--repo-setup-package-dir <dir>` configures the repository by installing
the built package, instead of writing repository files directly. It
then:
- refreshes that repository;
- checks the repo file and signing key are in place;
- installs ROCm from it.

`--repo-config-only` stops before the ROCm install. Nothing in CI uses
this mode yet.

The repo id, repo file name and key paths come from
`build_repo_setup_package` (`repo_id()`, `is_signed()`,
`DEB_KEYRING_PATH`, `RPM_GPG_KEY_PATH`), so only the distro's repo
directories stay in the harness. The package itself is found by the
builder's `PACKAGE_NAME`, so the harness takes either the built file
(`amd-repo-setup_<version>_all.deb`) or the published copy
(`amd-repo-setup-<tier>.deb`). Argument parsing rejects
`--repo-setup-package-dir` unless `--release-type` names a line with a
public stream.

`packaging/linux/tests/requirements.txt` gains `jinja2>=3.0.0`, the pin
used elsewhere, because this mode imports `build_repo_setup_package`.
The harness's other modes still load without it.

### Smaller changes

**Input validation.** These values become parts of the object key, and a
value like `../..` would escape a local staging directory:
- `--run-id` must be numeric;
- `--os-profile` must be a single safe path segment;
- `--tier` must be lowercase letters and digits.

`get-repo-setup-params` checks the run id too. It goes into
`$GITHUB_OUTPUT`, where `gha_set_output` writes multi-line values with a
heredoc whose delimiter it does not check. These inputs come from CI, so
this is defence in depth.

**One fixture.** `RunTestsTestTypeTest._base_args` gains
`repo_setup_package_dir` and `repo_config_only` with their argparse
defaults, both of which `run_tests()` reads. Without them those tests
fail, since #7004 made that directory collectable.

## Test Plan

- **Unit tests** for the upload script, `get-repo-setup-params` and the
harness package mode.
- The upload tests that check what's written drive a real
`LocalStorageBackend` and assert the bytes on disk, instead of mocking
an S3 client. They spell the expected key out, so a layout change has to
be made in two places.
  - Two tests guard against the harness drifting from the package:
- one renders the package's own install manifest for every profile and
stream, and checks it contains every file the harness looks for;
- the other checks that every stream the builder knows is reachable from
exactly one build line.
- Two more check that the harness finds the package by the builder's
package name: a package with another name is ignored, and the published
copy (`amd-repo-setup-<tier>.deb`) is found.
- Three check the `GITHUB_OUTPUT` output: a real upload writes the
package's URL, and a dry run or a local staging run writes none.
- **End to end, in containers:**
- build the package for stable, rc and nightly on ubuntu2404, rhel8,
rhel10 and sles16, then run the harness in package mode against the live
repositories, from a venv built only from
`packaging/linux/tests/requirements.txt`;
- replay #7280's stage job (`get-repo-setup-params`, build, inspect,
upload) on each distro, with MinIO standing in for S3;
  - run release promotion on the result.
- **Full suite.** Compared by test ID against the base branch.
- **Negative controls.** Break each thing a new test guards, confirm the
test fails, restore.
- **Lint.** `pre-commit`.

## Test Result

| Check | Result |
|---|---|
| Unit tests | Pass. 298 tests in the three changed test modules, 131 of
them new. `Unit Tests` passes on `ubuntu-26.04` and `windows-2022`. |
| End to end | **Harness:** 12/12 pass. An earlier revision hardcoded
the repo id `amdrocm` and failed 9/9 on ubuntu2404, rhel10 and sles16
(dnf `Unknown repo: 'amdrocm'`, zypper `Repository 'amdrocm' not found`,
no `amdrocm.sources` on deb). With the new `jinja2` line removed,
package mode fails with `No module named 'jinja2'`. **Stage replay:**
8/8, each writing the package's URL to `GITHUB_OUTPUT`, including the rc
key fetch and a check of the live index. Each uploaded object is
byte-identical to the built package, at the expected key, with nothing
else in its folder. **Promotion:** publishes the content repositories
and nothing from `amd-repo-setup/`. |
| Full suite | Compared by test ID with the base branch: 0 status
changes, 131 tests added, none removed. In CI, 3216 passed and 9 skipped
on Linux, 3138 passed and 87 skipped on Windows. |
| Negative controls | Pass. Each of these fails its tests: a hardcoded
repo id; a missing `prerelease` mapping; a module-level builder import
in either script; a build folder chosen by stream name; a key check that
ignores signing; an upload that takes the first of several packages or
searches recursively; an unvalidated tier; a tier taken from the stream
name; a package lookup that ignores the builder's package name, or that
misses the published file name; an upload URL output that's missing
after a real upload, or written without one; and a changed upload key
(folder or tier). |
| Lint | Pass |

## Submission Checklist

- [x] Look over the contributing guidelines at
https://github.com/ROCm/TheRock/blob/main/CONTRIBUTING.md.

---------

Co-authored-by: Nirmal Unnikrishnan <71024015+nunnikri@users.noreply.github.com>
lsudarsh-amd added a commit that referenced this pull request Oct 22, 2026
## Motivation

Setting up a ROCm package repository currently takes a user several
manual steps. The `amd-repo-setup` package reduces that to a single
install: it ships the repository configuration file and, on the signed
streams, the signing key.

This pull request wires the package into the native package pipeline. On
the `nightly` and `prerelease` lines it builds the package for each
distro, checks its contents, and uploads it to the run's artifacts
bucket. It is off by default (`enable_repo_setup_package: false`), so
nothing runs until a caller turns it on.

This is the fourth of five pull requests:

- PR 0 (#7004) added `packaging/linux/tests` to `testpaths`, so PR 2's
tests under that directory run in CI.
- PR 1 (#7269) adds the builder, its templates, its unit tests and the
user documentation.
- PR 2 (#7276) adds the build parameters (`get-repo-setup-params`), the
upload to the run's artifacts bucket and the install-harness package
mode.
- PR 3 (this one) wires the CI workflow: build, inspect and upload, off
by default.
- PR 4 (#9352) adds the `WorkflowOutputRoot` location for the package
and publishes it to `v5/amd-repo-setup/` during release promotion.

It is stacked on #7276, which is stacked on #7269, so it merges after
both.

Contributes to #6986.

## Technical Details

Everything is in
`.github/workflows/multi_arch_build_native_linux_packages.yml`, apart
from one new script and its unit tests.

### Jobs

| Job | Purpose | Runner |
|---|---|---|
| `resolve_repo_setup_params` | One `get-repo-setup-params` call: the
profile matrix, each profile's container image, and the stream, tier,
repository URL, signing-key URL and nightly sub-folder | The build job's
runner and container |
| `stage_repo_setup_package` | Per profile, in that distro's container:
build the package, inspect it, then upload it to
`<run_id>-linux/amd-repo-setup/<os_profile>/amd-repo-setup-<tier>.<format>`
| `ubuntu-26.04` |
| `notify_quartz_completed` | Reports the jobs' result to Quartz;
`resolve_repo_setup_params` reports the start | `ubuntu-26.04`,
`continue-on-error` |

**When it runs.** It runs only when `enable_repo_setup_package` is
`true`, on `nightly` and `prerelease`. No caller sets it yet.
- `prerelease` configures the `rc` stream.
- `release` is left out: it configures a stream, but has no artifacts
bucket to upload into.
- ASAN builds skip, because their repositories are promoted under
`packages-asan/`, which the package's repository URL does not point at.
- TSAN builds skip, because they are not promoted at all.

**Failure handling.** Neither job uses `continue-on-error`, so once the
switch is on, a failure fails the run.

**Credentials.** AWS credentials are configured only after the package
is built and inspected, so the key fetch, the repository check and the
build run without them.

**Upload URL.** The upload step (`id: upload-repo-setup-package`)
exposes the package's URL, which #7276's script writes to
`GITHUB_OUTPUT` as `repo_setup_package_url`, to later steps in the job.

### Inspect step

"Inspect amd-repo-setup" runs
`build_tools/packaging/linux/inspect_repo_setup_package.py` on the
package the job just built, before the upload. It lists the archive
(`dpkg-deb` or `rpm`, which the build already needs) and checks:
- the repository file for this stream, and on a signed stream the key,
are at the expected paths;
- nothing it must not ship is present, including the key paths that were
renamed away from generic `rocm` names;
- everything is owned by root.

The package type comes from the OS profile. Checking with the job's own
stream also confirms that the package configures that stream. A failure
stops the job before the upload.

The package is built once, in this job, with the real parameters, in the
target distro's image.

### Reads of a published repository

**On `prerelease`.** `stage_repo_setup_package` fetches the rc signing
key; the builder accepts only the pinned key. It also uses
`--verify-repo-url` to check that the repository index is reachable.
Either failing fails the run, which catches a package that installs but
cannot refresh.

**On `nightly`.** Neither check runs. Nightly is unsigned, and the
builder skips the URL check because the run's dated folder is published
by the same run.

No pull request reaches a published repository: the jobs don't run on
the `ci` line.

### Note for reviewers

The nightly package points at the run's dated folder,
`YYYYMMDD-<run_id>`. Two jobs stamp that date:
`resolve_repo_setup_params` when it runs, and release promotion when it
runs.

So two kinds of run produce a nightly package that points at a folder
that doesn't exist:
- a run that crosses UTC midnight between the two;
- a promotion re-run on a later day.

Recent nightlies build their content between 01:37 and 02:10 UTC, well
clear of midnight, so they are not affected. A nightly dispatched by
hand late in the UTC day, or re-run on a later day, is.

The fix touches promotion, which every package type shares, so it will
follow separately. It's one of the things to settle before the switch is
turned on for nightly.

## Test Plan

Verification used the state after the stack merges, entirely in
throwaway containers, with MinIO standing in for S3.

| What it proves | How |
|---|---|
| The new script is correct | Unit tests over path derivation, both
listing parsers, the payload check, finding the built package and the
command line, with fixtures in the format of `dpkg-deb -c` and `rpm -qp`
output |
| Nothing else regressed | The full suite, compared by test ID against
#7276 |
| The setup job works | Its `run:` steps, taken from the workflow and
replayed in its pinned container, for `prerelease`, `nightly` and
`release` × deb and rpm |
| The stage job works | Its `run:` steps, taken from the workflow and
replayed in each distro's container for `prerelease` and `nightly`:
build → inspect → upload |
| Inspect stops a bad package | The built package is tampered with right
after the build, then the job continues |
| The conditions are right | The `if:` expressions evaluated over every
line and build variant, with the switch on and off, and the Quartz job
over every setup and stage result |
| The packages install | The `rc` and `nightly` packages installed per
the user docs on `ubuntu2404`, `rhel8`, `rhel10` and `sles16`, then a
full `amdrocm` dry run |
| CI hygiene | `actionlint`, `pre-commit`, zizmor and the unit tests
that walk the workflow directory |

## Test Result

| What it proves | Result |
|---|---|
| The new script is correct | 74 unit tests passed. `Unit Tests` passes
on `ubuntu-26.04` and `windows-2022`. |
| Nothing else regressed | 0 status changes against #7276, 74 tests
added, none removed |
| The setup job works | 6 of 6, each with all 7 outputs |
| The stage job works | 8 of 8. Each upload is byte-identical to the
built package, at the expected key, with nothing else in its folder. rc
builds against the live repository and key |
| Inspect stops a bad package | A clean repack passes and uploads. A
superseded key path, a non-root owner, a package for the wrong stream,
two packages and no package each fail at Inspect, and nothing is
uploaded (9 of 9) |
| The conditions are right | Switch off: the jobs run on 0 of 63 line ×
variant combinations. Switch on: 4 (`nightly` and `prerelease`, without
sanitizers). The Quartz completion runs only when Quartz is enabled and
the setup job ran |
| The packages install | 8 of 8, plus a full `amdrocm` dry run (255–333
packages resolve) |
| CI hygiene | All pass. zizmor in the security scan reports no
findings; the stage job's container image is picked per distro at
runtime, so it carries an inline `unpinned-images` exemption. |

Not verifiable outside GitHub Actions; worth watching on the first run
with the switch on:
1. The stage job's per-distro containers, and the AWS credentials action
inside them, on GitHub's `ubuntu-26.04` runners.
2. The real upload.
3. The Quartz reports.
4. The setup container on the scale runner.

## Submission Checklist

- [x] Look over the contributing guidelines at
https://github.com/ROCm/TheRock/blob/main/CONTRIBUTING.md.

---------

Co-authored-by: Nirmal Unnikrishnan <71024015+nunnikri@users.noreply.github.com>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

FYI this new (1000 LOC) file has several medium severity security scanner findings: https://github.com/ROCm/TheRock/actions/runs/39438807912/job/118683135969 . The repository is otherwise at zero such findings, so this will block upgrading our policy enforcement (this is a regression from #8272).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

7 participants