Working with RPM

CodeScoring.Save implements an RPM repository in the createrepo format with the /rpm/<project>/<repository>/ prefix. It is compatible with standard dnf and yum clients on RHEL, Rocky Linux, AlmaLinux, Fedora, CentOS, and derived distributions.

Proxy Repository

curl -X POST https://save.example.com/api/v1/repos \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "project": "infra",
    "name": "rocky-proxy",
    "format": "rpm",
    "repository_type": "proxy",
    "remote_url": "https://dl.rockylinux.org/pub/rocky/9/BaseOS/x86_64/os",
    "cache_ttl": 3600
  }'
remote_url is the baseurl

In remote_url, specify the same URL you would use as baseurl in a .repo file: the directory that contains repodata/repomd.xml.

Proxy serves upstream repodata verbatim

A proxy repository does not generate its own indices. The repomd.xml file, the hash-named repodata files, and repomd.xml.asc are proxied from upstream byte for byte, so signatures and checksums remain valid. Packages are cached as immutable artifacts; metadata is revalidated after cache_ttl expires. If upstream is unavailable, Save serves the last cached copy of the metadata. Uploads to a proxy repository are rejected: it is read-only.

Hosted Repository

curl -X POST https://save.example.com/api/v1/repos \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "project": "infra",
    "name": "rpm-hosted",
    "format": "rpm",
    "repository_type": "hosted"
  }'

When a hosted repository is created, Save immediately publishes an empty repodata/repomd.xml, so dnf makecache works before the first package is uploaded. The indices (primary, filelists, other) are regenerated automatically after every package upload or deletion.

URL Scheme

https://save.example.com/cs-save/rpm/<project>/<repository>/repodata/repomd.xml    # index of indices
https://save.example.com/cs-save/rpm/<project>/<repository>/Packages/<letter>/...  # packages
https://save.example.com/cs-save/rpm/<project>/<repository>/repository.key         # public GPG key

Client Configuration

dnf / yum

Create /etc/yum.repos.d/codescoring.repo:

[codescoring]
name=CodeScoring Save
baseurl=https://save.example.com/cs-save/rpm/<project>/rpm-hosted
enabled=1
username=<username>
password=<password>
gpgcheck=0
repo_gpgcheck=0
dnf makecache --repo=codescoring
dnf install <package>

dnf / yum with Metadata Signature Verification

If metadata signing is enabled on the server (METADATA_SIGNING_ENABLED), Save publishes the detached signature repodata/repomd.xml.asc, and the public key is available at <repo>/repository.key. Enable repo_gpgcheck=1:

[codescoring]
name=CodeScoring Save
baseurl=https://save.example.com/cs-save/rpm/<project>/rpm-hosted
enabled=1
username=<username>
password=<password>
gpgcheck=0
repo_gpgcheck=1
gpgkey=https://save.example.com/cs-save/rpm/<project>/rpm-hosted/repository.key
gpgcheck vs repo_gpgcheck

repo_gpgcheck=1 enables verification of the repository metadata signature (repomd.xml.asc). This is the counterpart of apt's signed-by, and this is the signature Save creates. gpgcheck=1 verifies per-package signatures, which Save never creates or modifies. Enable gpgcheck=1 only if the uploaded packages are signed at build time.

Robot accounts in CI

For CI/CD, use a robot account: username = sa$<robot-name>, password = <api-key> in the .repo file. The file structure remains the same; only values change. For details, see Authentication.

Publishing Packages (hosted)

Uploads are performed with a PUT request to the canonical Packages/<first-letter-of-name>/ path:

curl -u "<username>:<password>" \
  -T mypackage-1.0.0-1.x86_64.rpm \
  https://save.example.com/cs-save/rpm/<project>/rpm-hosted/Packages/m/mypackage-1.0.0-1.x86_64.rpm

The file name must follow the <name>-<version>-<release>.<arch>.rpm scheme. Save validates the package headers and normalizes the path to the canonical form. The resulting path is returned in the response.

An alternative is a multipart POST to the repository root:

curl -u "<username>:<password>" \
  -F "file=@mypackage-1.0.0-1.x86_64.rpm" \
  https://save.example.com/cs-save/rpm/<project>/rpm-hosted

Forcing repodata Regeneration

curl -u "<username>:<password>" \
  -X POST https://save.example.com/cs-save/rpm/<project>/rpm-hosted/rebuild-index

The rebuild also removes stale hash-named repodata files left over from previous publications.

Repository URL Migration

Use case: migrating an RPM repository from Nexus / Artifactory to CodeScoring.Save.

Sourcebaseurl before migrationbaseurl after migration
Nexushttps://nexus.host.ru/repository/yum-hostedhttps://save.example.com/cs-save/rpm/<project>/rpm-hosted
Artifactoryhttps://jfrog.host.ru/artifactory/rpm-localhttps://save.example.com/cs-save/rpm/<project>/rpm-hosted
Official mirrorhttps://dl.rockylinux.org/pub/rocky/9/BaseOS/x86_64/oshttps://save.example.com/cs-save/rpm/<project>/rocky-proxy

Authentication parameters (username / password) in the .repo file remain unchanged. For a proxy repository, Save serves the upstream repomd.xml.asc verbatim, so existing distribution gpgkey keys keep working with repo_gpgcheck=1.

Troubleshooting

Checking repomd.xml

curl -u "<username>:<password>" \
  https://save.example.com/cs-save/rpm/<project>/rpm-hosted/repodata/repomd.xml

The response is XML with data type="primary", filelists, and other sections and hash-named href references.

Checking That a Package Is in the primary Index

# the primary index href is taken from repomd.xml
curl -s -u "<username>:<password>" \
  https://save.example.com/cs-save/rpm/<project>/rpm-hosted/repodata/<hash>-primary.xml.gz \
  | gunzip | grep '<location'

If a package is uploaded but missing from the index, wait a few seconds because indexing is asynchronous, or run rebuild-index.

Checking the Public Key

curl -u "<username>:<password>" \
  https://save.example.com/cs-save/rpm/<project>/rpm-hosted/repository.key
# Expected: -----BEGIN PGP PUBLIC KEY BLOCK-----
# A 404 means metadata signing is not enabled on the server

Service Status

curl https://save.example.com/health

Repository Audit

curl -u "<username>:<password>" \
  "https://save.example.com/api/v1/admin/audit?resource_type=repository&q=rpm-hosted&limit=50"
Was this page helpful?