The copr.yaml manifest
On this page
A manifest is the declarative form of a Copr project. It is a coprctl/v1
Project document that names the target project, describes the desired
state, and is reconciled with coprctl apply -f copr.yaml. coprctl diff
reports field-level drift and coprctl export writes a manifest from a live
project.
apiVersion: coprctl/v1
kind: Project
metadata:
owner: you
name: mypkg
profile: production
spec:
description: My package
instructions: |
dnf copr enable you/mypkg
dnf install mypkg
homepage: https://example.com/mypkg
contact: https://github.com/you/mypkg/issues
settings:
enableNet: true
autoPrune: true
bootstrap: on
unlistedOnHomepage: true
chroots:
enabled:
- fedora-42-x86_64
- epel-9-x86_64
packages:
- name: mypkg
source:
type: scm
cloneUrl: https://github.com/you/mypkg
committish: main
spec: mypkg.spec
maxBuilds: 10
timeout: 36000
chrootDenylist:
- 'fedora-rawhide-*'
permissions:
builders: [builder1]
admins: [admin1]
Project settings
The spec.settings map carries project-level flags. Every field maps to a
Copr project setting with the same meaning; see the Copr project settings in
the web UI or the upstream schema for the details of each one.
Fields that are applied both on create and on edit:
| Field | Setting | Wire constraint |
|---|---|---|
autoPrune |
auto-delete old builds | bool |
bootstrap |
bootstrap mode | default, off, on, or image |
isolation |
build isolation | default, nspawn, or simple |
moduleHotfixes |
repository holds module hotfixes | bool |
appstream |
generate AppStream metadata | bool |
packitForgeProjectsAllowed |
forge projects allowed to build via Packit | list of forge strings |
followFedoraBranching |
auto-follow Fedora branching | bool |
repoPriority |
repository priority | integer >= 1 |
unlistedOnHomepage |
hide the project from the home page | bool |
multilib |
enable multilib support | bool |
fedoraReview |
run fedora-review on packages | bool |
runtimeDependencies |
external repositories enabled with this project | list of base URLs |
deleteAfterDays |
delete the project after N days | integer -1..720 |
Create-only fields, sent only at project creation (the edit API has no field
for them, so re-apply cannot change them on an existing project). validate
warns when they are set:
| Field | Setting | Wire constraint |
|---|---|---|
persistent |
immune to deletion | bool |
storage |
backing storage, admin only | backend or pulp |
additionalRepos is not yet modeled by the client and warns on validate.
persistent and deleteAfterDays are mutually exclusive, matching the
upstream form rule; validate rejects a manifest that sets both.
Declared-only apply
apply sends a project setting only when the manifest declares it. For the
fields in the tables above, declaring means setting a non-zero value: a bool
that is true, a non-empty string, a non-empty list, or a non-nil integer.
Everything else is left to the live value or the server default. This is the
behavior the upstream edit API backs: it applies only the fields present in
the request body. develMode and enableNet are the exception: apply always
sends them, so omitting them writes the false value.
The consequence is the declared-vs-zero family, a single limitation that shows up in several fields:
- A manifest cannot express an explicit
falsefor a bool. DeclareautoPrune: trueto force it on; leaving it out keeps whatever the project has. - An empty
chrootDenylistcannot clear a live denylist. Omit the field instead of writing an empty list. repoPrioritycannot declare0, which is below the upstream minimum of 1. Omit it to keep the live priority.- An empty
packitForgeProjectsAllowedcannot clear live entries. Omit the field instead of writing an empty list.
export writes only non-zero values, so an exported manifest round-trips:
apply it and nothing it omitted is touched.
What diff and export verify
Project settings fall into three treatment classes:
- Readable and reconciled, diffed only when declared:
autoPrune,bootstrap,isolation,moduleHotfixes,appstream,packitForgeProjectsAllowed,followFedoraBranching,repoPriority,unlistedOnHomepage.diffcompares these only when the manifest declares them, andexportemits them from live state. - Create-only, echoed by GET but unreconcilable:
persistent,storage.diffnever compares them andexportnever emits them: the edit API has no field, so apply could never converge drift. - Write-only, set on apply and never verifiable by diff or export:
multilib,fedoraReview,deleteAfterDays,runtimeDependencies, and the package settings below. The API does not echo them back, so there is no live value to compare.
Because diff only compares declared fields, a manifest that declares nothing
stays clean against the live defaults (auto_prune true, bootstrap
"default", isolation "default", follow_fedora_branching true). The
workflow for a clean diff is declare-or-export: either declare a field
non-zero, or leave it out so export omits it too. Running apply after
export is always clean.
Package settings
Each entry in spec.packages may carry the per-package settings:
| Field | Setting | Wire constraint |
|---|---|---|
maxBuilds |
keep only the newest N builds | integer 0..100 |
timeout |
max build time in seconds | integer >= 0 |
chrootDenylist |
chroot patterns that never build this package | list of patterns |
These are write-only through the API: GET does not echo them, so diff and
export cannot verify them. They are sent on apply when declared. apply
falls back to editing an existing package when the add conflicts, so the
settings reach packages that already exist; the upstream edit merges the
stored source, which stays safe.
chrootDenylist patterns are joined with commas on the wire, matching the
upstream cleanup filter. The local validate check accepts the upstream
pattern set, but the server re-validates each pattern against the active
chroots and rejects patterns that match none or all of them.
Reconcile workflow
coprctl validate -f copr.yaml # schema and constraint checks, no network
coprctl diff -f copr.yaml # field-level drift against the live project
coprctl apply -f copr.yaml # create or update to match the manifest
coprctl apply -f copr.yaml --dry-run
coprctl export you/mypkg -o copr.yaml # adopt an existing project
apply is additive and safe to re-run: it creates a missing project or
package, edits existing state to match the declared fields, and never removes
anything unless --prune is given (which restricts chroots to the manifest's
list). --dry-run reports the drift without changing anything.