all:
  children:
    cephs:
      hosts:
        instance: null
    computes:
      hosts:
        instance: null
    controllers:
      hosts:
        instance: null
    zuul_unreachable:
      hosts: {}
  hosts:
    instance:
      ansible_connection: ssh
      ansible_host: 162.253.55.161
      ansible_port: 22
      ansible_python_interpreter: auto
      ansible_user: zuul
      atmosphere_image_mirrors:
      - mirror: atmosphere-ci-registry:5443
        source: atmosphere-ci-registry:5443
      atmosphere_image_prefix: harbor.atmosphere.dev/
      atmosphere_release_manifest: atmosphere-ci-registry:5443/vexxhost/atmosphere-release:ci
      atmosphere_release_manifest_ca_file: /usr/local/share/ca-certificates/atmosphere-ci-registry.crt
      atmosphere_release_manifest_digest: sha256:51453c5226453ec76db8be984c0360ae234206e4a65400e908efa26636b466da
      ceph_conf_overrides:
      - option: mon allow pool size one
        section: global
        value: true
      - option: osd crush chooseleaf type
        section: global
        value: 0
      - option: auth allow insecure global id reclaim
        section: mon
        value: false
      ceph_csi_rbd_helm_values:
        provisioner:
          replicaCount: 1
      ceph_fsid: 4837cbf8-4f90-4300-b3f6-726c9b9f89b4
      ceph_osd_devices:
      - /dev/ceph-{{ inventory_hostname_short }}-osd0/data
      - /dev/ceph-{{ inventory_hostname_short }}-osd1/data
      - /dev/ceph-{{ inventory_hostname_short }}-osd2/data
      cilium_helm_values:
        operator:
          replicas: 1
      cluster_issuer_type: self-signed
      csi_driver: local-path-provisioner
      ingress_nginx_helm_values:
        controller:
          config:
            worker-processes: 2
      keystone_helm_values:
        pod:
          replicas:
            api: 1
      kube_vip_address: 172.17.0.100
      kube_vip_interface: '{{ ansible_facts[''default_ipv4''].interface }}'
      kubernetes_hostname: '{{ ansible_facts[''default_ipv4''].address }}'
      molecule_install_collection_siblings: true
      molecule_scenario: keycloak
      nodepool:
        az: yul2
        cloud: public
        external_id: 4fa809f5-343d-460d-810e-63f0a23c4579
        host_id: a89317a4251e4b893721d69756ba374963c1949052f4d77e70dbd5e4
        interface_ip: 162.253.55.161
        label: ubuntu-jammy
        node_properties: {}
        private_ipv4: 162.253.55.161
        private_ipv6: null
        provider: yul1
        public_ipv4: 162.253.55.161
        public_ipv6: 2604:e100:1:0:f816:3eff:fe91:8d93
        region: ca-ymq-1
        slot: null
      percona_xtradb_cluster_spec:
        allowUnsafeConfigurations: true
        haproxy:
          size: 1
        pxc:
          size: 1
      zuul_node:
        az: yul2
        cloud: public
        external_id: 4fa809f5-343d-460d-810e-63f0a23c4579
        host_id: a89317a4251e4b893721d69756ba374963c1949052f4d77e70dbd5e4
        interface_ip: 162.253.55.161
        label: ubuntu-jammy
        node_properties: {}
        private_ipv4: 162.253.55.161
        private_ipv6: null
        provider: yul1
        public_ipv4: 162.253.55.161
        public_ipv6: 2604:e100:1:0:f816:3eff:fe91:8d93
        region: ca-ymq-1
        slot: null
        uuid: null
  vars:
    atmosphere_image_mirrors:
    - mirror: atmosphere-ci-registry:5443
      source: atmosphere-ci-registry:5443
    atmosphere_image_prefix: harbor.atmosphere.dev/
    atmosphere_release_manifest: atmosphere-ci-registry:5443/vexxhost/atmosphere-release:ci
    atmosphere_release_manifest_ca_file: /usr/local/share/ca-certificates/atmosphere-ci-registry.crt
    atmosphere_release_manifest_digest: sha256:51453c5226453ec76db8be984c0360ae234206e4a65400e908efa26636b466da
    ceph_conf_overrides:
    - option: mon allow pool size one
      section: global
      value: true
    - option: osd crush chooseleaf type
      section: global
      value: 0
    - option: auth allow insecure global id reclaim
      section: mon
      value: false
    ceph_csi_rbd_helm_values:
      provisioner:
        replicaCount: 1
    ceph_fsid: 4837cbf8-4f90-4300-b3f6-726c9b9f89b4
    ceph_osd_devices:
    - /dev/ceph-{{ inventory_hostname_short }}-osd0/data
    - /dev/ceph-{{ inventory_hostname_short }}-osd1/data
    - /dev/ceph-{{ inventory_hostname_short }}-osd2/data
    cilium_helm_values:
      operator:
        replicas: 1
    cluster_issuer_type: self-signed
    csi_driver: local-path-provisioner
    ingress_nginx_helm_values:
      controller:
        config:
          worker-processes: 2
    keystone_helm_values:
      pod:
        replicas:
          api: 1
    kube_vip_address: 172.17.0.100
    kube_vip_interface: '{{ ansible_facts[''default_ipv4''].interface }}'
    kubernetes_hostname: '{{ ansible_facts[''default_ipv4''].address }}'
    molecule_install_collection_siblings: true
    molecule_scenario: keycloak
    percona_xtradb_cluster_spec:
      allowUnsafeConfigurations: true
      haproxy:
        size: 1
      pxc:
        size: 1
    zuul:
      _inheritance_path:
      - '<Job base explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/zuul-config/zuul.d/jobs.yaml@main#1>'
      - '<Job molecule explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/zuul-jobs/zuul.d/ansible-jobs.yaml@main#1>'
      - '<Job atmosphere-molecule explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/atmosphere-zuul-jobs/zuul.d/jobs.yaml@main#19>'
      - '<Job atmosphere-molecule-keycloak explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/atmosphere-zuul-jobs/zuul.d/jobs.yaml@main#303>'
      - '<Job atmosphere-molecule-keycloak explicit: None implied: None source: vexxhost/atmosphere-zuul-jobs/zuul.d/project-templates.yaml@main#1>'
      ansible_version: '9'
      artifacts:
      - branch: main
        change: '4420'
        job: atmosphere-release-manifest
        metadata:
          type: zuul_manifest
        name: Zuul Manifest
        patchset: 32ae9479091acf66cc00cd680ed6e0307341626d
        project: vexxhost/atmosphere
        url: https://logs.oss.vexxhost.dev/a87/oss/a87f85c366344c8d9154ec6df44fc6c9/zuul-manifest.json
      - branch: main
        change: '4420'
        job: atmosphere-release-manifest
        metadata:
          digest: sha256:51453c5226453ec76db8be984c0360ae234206e4a65400e908efa26636b466da
        name: Release manifest
        patchset: 32ae9479091acf66cc00cd680ed6e0307341626d
        project: vexxhost/atmosphere
        url: https://logs.oss.vexxhost.dev/a87/oss/a87f85c366344c8d9154ec6df44fc6c9/release-manifest.yaml
      - branch: main
        change: '4420'
        job: atmosphere-release-manifest
        name: atmosphere-release
        patchset: 32ae9479091acf66cc00cd680ed6e0307341626d
        project: vexxhost/atmosphere
        url: https://logs.oss.vexxhost.dev/a87/oss/a87f85c366344c8d9154ec6df44fc6c9/atmosphere-release
      attempts: 1
      branch: main
      build: 88153ed6dc634da0ab502e7273feee8e
      build_refs:
      - branch: main
        change: '4420'
        change_message: "feat: publish release manifests and deploy from them\n\nToday
          a release resolves image tags at release time, after CI has run: 88 of the
          232 entries in `roles/defaults/vars/main.yml` are plain tags, and `cmd/pinimages`
          resolves them into the Galaxy collection, silently skipping any it cannot
          resolve (it logs 94 errors on `main` today). The digests a release ships
          are only recorded inside the Galaxy tarball, and mirroring for air-gapped
          installs goes through the `image_manifest` role, whose registry rewrite
          keeps only the last path component, so `rook/ceph` and `ceph/ceph` both
          become `<registry>/ceph`.\n\nThis PR makes a digest-pinned **release manifest**
          the source of truth for which images a commit or release deploys.\n\n**Publishing**\n-
          On every push to `main` and `stable/*`, `atmosphere-release publish` publishes
          the manifest as an OCI artifact to `ghcr.io/vexxhost/atmosphere-release`,
          tagged with the full commit SHA and the branch (`main`, `stable-2025.2`,
          ...) as computed by `docker/metadata-action`. It looks up the merged pull
          request through the GitHub API and asks Zuul for the newest buildset in
          which every job passed for that PR's head commit (`gate`, then `check`).
          If that buildset's manifest was generated from the same image inputs as
          this commit, it is published unchanged, so what ships is exactly what CI
          deployed. Otherwise (direct push, no passing buildset, logs expired, or
          another merge changed the image list) the manifest the `build` job just
          generated is published with `com.vexxhost.atmosphere.tested: \"false\"`.\n-
          Manifests record `inputs`, a digest of the rendered image list before tags
          are resolved, and pushes are reproducible (fixed creation time), so the
          same manifest always has the same artifact digest wherever it is pushed.\n-
          `release.yaml` uses `docker/metadata-action` for the release tag and promotes
          the released commit's manifest to it (no tag is resolved again). GoReleaser
          (`.goreleaser.yaml`) attaches `atmosphere-release` archives for linux/darwin
          amd64/arm64, their checksums and the release manifest to the existing GitHub
          release without touching its notes, and the Galaxy collection is pinned
          from that same manifest.\n\n**Deploying**\n- `atmosphere_release_manifest`
          (OCI reference or file path) deploys exactly a manifest's images. OCI references
          are read through `atmosphere_image_mirrors` and `atmosphere_image_prefix`
          like every image, so Harbor-prefix and mirror users also fetch the manifest
          through their cache. `atmosphere_release_manifest_ca_file` adds a private
          CA to trust; credentials come from the Docker configuration. Kubernetes
          control plane images fall back to the tag when another `atmosphere_kubernetes_version`
          is deployed.\n- `atmosphere_image_mirrors` redirects images per registry,
          namespace or repository (most specific rule wins); `atmosphere_image_prefix`
          still applies to everything no rule matches, so Harbor proxy projects with
          custom names now work too.\n- Resolution happens in the rendered references,
          so it works with any Kubernetes and container runtime. `atmosphere_image_overrides`
          are applied last, unchanged.\n\n**`atmosphere-release` command** (`cmd/atmosphere-release`,
          replaces `cmd/pinimages`)\n- `generate [--write-vars] [--resolve-prefix]`,
          `push`, `promote`, `publish`, `digest`, `show`, `diff`, `check`. `push`
          and `promote` take `repository:tag` references, so they consume `docker/metadata-action`
          output directly; `--resolve-prefix` resolves tags through a pull-through
          cache (falling back to the source registry) to avoid upstream rate limits.\n-
          `mirror` to a registry, or `--to-dir` / `--from-dir` for disconnected sites,
          in `nested`, `flat` (`quay.io--ceph--ceph`) or `single-repo` layouts. Images
          are copied with every platform so digests never change, each digest is verified,
          kubeadm's images stay under one prefix with their original names, the manifest
          artifact itself is copied with its digest intact, and the `atmosphere_release_manifest`
          / `atmosphere_image_mirrors` vars are written out.\n\nThe `image_manifest`
          role keeps working but now warns that it is deprecated.\n\n**Docs:** a new
          deployment guide, `doc/source/deploy/images.rst` (\"Container images and
          mirrors\"), covers release manifests, the image prefix, mirror rules, overrides,
          mirroring (including disconnected sites) and `check`/`diff`. The `defaults`
          role README lists the image variables, and the release note covers the new
          features, the unchanged upgrade path, the `image_manifest` deprecation and
          the `pinimages` replacement.\n\n### Compatibility\n\nDeployments that set
          none of the new variables get byte-identical `atmosphere_images`: I rendered
          the `defaults` role through `ansible-playbook` before and after this change
          with no prefix, with a Harbor prefix, and with prefix + overrides, on `main`
          (231 images) and on `stable/zed` (216 images); all outputs are identical.
          `write-vars` produces the same vars file as `pinimages` except that Ceph
          is now pinned too (`pinimages` could not resolve its `{{ ceph_version }}`
          template).\n\n### Testing\n\n- Go: `internal/releasemanifest` tests cover
          generation, `write-vars`, push/fetch/promote, every mirror layout with multi-arch
          images in in-memory registries, directory export/import, digest-mismatch
          detection, registries with ports and retry on rate limits.\n- Go: `publish`
          against a fake Zuul API (tested manifest used unchanged with the same artifact
          digest, gate searched before check, failed buildsets skipped, fallback when
          inputs changed, nothing passed or Zuul is unreachable), and reproducible
          pushes.\n- Python: the `resolve_images` filter and the Go resolver run the
          same cases from `internal/releasemanifest/testdata/resolve.yaml`; the lookup
          is tested against a local HTTP registry with token auth and blob redirects
          (credentials must not reach storage) and tampered content, and against an
          HTTPS registry with a private CA and basic authentication (fails without
          the CA or credentials), including fetching the manifest through the prefix
          and mirror rules. A new `python-test` CI job runs these (the existing `tests/unit`
          suite is not run in CI and has a stale magnum expectation, so the job only
          runs the new tests).\n- End to end against a local CNCF Distribution registry
          with real images from `registry.k8s.io`, `ghcr.io` and Docker Hub: `generate`,
          `push`, `promote`, `show`, nested mirror, export \u2192 flat import, `check`,
          `diff`, plus deploying the mirrored manifest through the lookup.\n- `generate`
          against the real registries resolves all 231 images on `main` and `stable/2025.2`.\n\n###
          Backports\n\n`go.mod` and `go.sum` are untouched; every dependency is already
          present at the same version on all stable branches.\n\n- `stable/2025.2`
          cherry-picks cleanly and passes everything above.\n- `stable/2025.1` back
          to `stable/zed` conflict in one hunk of `roles/defaults/vars/main.yml`:
          the conflict context includes `main`'s `_atmosphere_storage` block, which
          those branches don't have. Resolve it by keeping the new `atmosphere_images`
          block and dropping the `_atmosphere_storage` lines. I did this on `stable/zed`
          and the Go tests, Python tests and the old/new rendering comparison all
          pass.\n\n### CI deploys the manifest\n\nWith vexxhost/atmosphere-zuul-jobs#7,
          an `atmosphere-release-manifest` job generates this change's release manifest
          once per buildset. Every molecule job (AIO, CSI, Keycloak) depends on it,
          pushes that exact manifest to a TLS registry with password authentication
          on its node, checks the pushed digest matches, and deploys by registry reference
          with `atmosphere_release_manifest`, `atmosphere_release_manifest_ca_file`
          and a mirror rule, the same path customers use for published releases. That
          buildset's manifest is what `publish` publishes after merge.\n\n### Before
          merging / follow-ups\n\n- After the first push publishes `ghcr.io/vexxhost/atmosphere-release`,
          make the package public and set retention (keep release tags; commit tags
          can be pruned).\n- Docker Hub's anonymous rate limit can still fail `generate`
          after its retries; previously this silently produced unpinned images.\n-
          `harbor.atmosphere.dev` has no proxy project for `packages.vexxhost.com`
          (used by `prometheus_smartctl_exporter`).\n- The docs build with `-W` already
          fails on `main` because of existing warnings in `admin/monitoring.rst` and
          `arch/images.rst`; the new page adds none.\n\nDepends-On: https://github.com/vexxhost/atmosphere-zuul-jobs/pull/7\n"
        change_url: https://github.com/vexxhost/atmosphere/pull/4420
        commit_id: 32ae9479091acf66cc00cd680ed6e0307341626d
        patchset: 32ae9479091acf66cc00cd680ed6e0307341626d
        project:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/atmosphere
          name: vexxhost/atmosphere
          short_name: atmosphere
          src_dir: src/github.com/vexxhost/atmosphere
        src_dir: src/github.com/vexxhost/atmosphere
        topic: null
      buildset: 48c8ccf4dd2445b0b970240f8a709a45
      buildset_refs:
      - branch: main
        change: '4420'
        change_message: "feat: publish release manifests and deploy from them\n\nToday
          a release resolves image tags at release time, after CI has run: 88 of the
          232 entries in `roles/defaults/vars/main.yml` are plain tags, and `cmd/pinimages`
          resolves them into the Galaxy collection, silently skipping any it cannot
          resolve (it logs 94 errors on `main` today). The digests a release ships
          are only recorded inside the Galaxy tarball, and mirroring for air-gapped
          installs goes through the `image_manifest` role, whose registry rewrite
          keeps only the last path component, so `rook/ceph` and `ceph/ceph` both
          become `<registry>/ceph`.\n\nThis PR makes a digest-pinned **release manifest**
          the source of truth for which images a commit or release deploys.\n\n**Publishing**\n-
          On every push to `main` and `stable/*`, `atmosphere-release publish` publishes
          the manifest as an OCI artifact to `ghcr.io/vexxhost/atmosphere-release`,
          tagged with the full commit SHA and the branch (`main`, `stable-2025.2`,
          ...) as computed by `docker/metadata-action`. It looks up the merged pull
          request through the GitHub API and asks Zuul for the newest buildset in
          which every job passed for that PR's head commit (`gate`, then `check`).
          If that buildset's manifest was generated from the same image inputs as
          this commit, it is published unchanged, so what ships is exactly what CI
          deployed. Otherwise (direct push, no passing buildset, logs expired, or
          another merge changed the image list) the manifest the `build` job just
          generated is published with `com.vexxhost.atmosphere.tested: \"false\"`.\n-
          Manifests record `inputs`, a digest of the rendered image list before tags
          are resolved, and pushes are reproducible (fixed creation time), so the
          same manifest always has the same artifact digest wherever it is pushed.\n-
          `release.yaml` uses `docker/metadata-action` for the release tag and promotes
          the released commit's manifest to it (no tag is resolved again). GoReleaser
          (`.goreleaser.yaml`) attaches `atmosphere-release` archives for linux/darwin
          amd64/arm64, their checksums and the release manifest to the existing GitHub
          release without touching its notes, and the Galaxy collection is pinned
          from that same manifest.\n\n**Deploying**\n- `atmosphere_release_manifest`
          (OCI reference or file path) deploys exactly a manifest's images. OCI references
          are read through `atmosphere_image_mirrors` and `atmosphere_image_prefix`
          like every image, so Harbor-prefix and mirror users also fetch the manifest
          through their cache. `atmosphere_release_manifest_ca_file` adds a private
          CA to trust; credentials come from the Docker configuration. Kubernetes
          control plane images fall back to the tag when another `atmosphere_kubernetes_version`
          is deployed.\n- `atmosphere_image_mirrors` redirects images per registry,
          namespace or repository (most specific rule wins); `atmosphere_image_prefix`
          still applies to everything no rule matches, so Harbor proxy projects with
          custom names now work too.\n- Resolution happens in the rendered references,
          so it works with any Kubernetes and container runtime. `atmosphere_image_overrides`
          are applied last, unchanged.\n\n**`atmosphere-release` command** (`cmd/atmosphere-release`,
          replaces `cmd/pinimages`)\n- `generate [--write-vars] [--resolve-prefix]`,
          `push`, `promote`, `publish`, `digest`, `show`, `diff`, `check`. `push`
          and `promote` take `repository:tag` references, so they consume `docker/metadata-action`
          output directly; `--resolve-prefix` resolves tags through a pull-through
          cache (falling back to the source registry) to avoid upstream rate limits.\n-
          `mirror` to a registry, or `--to-dir` / `--from-dir` for disconnected sites,
          in `nested`, `flat` (`quay.io--ceph--ceph`) or `single-repo` layouts. Images
          are copied with every platform so digests never change, each digest is verified,
          kubeadm's images stay under one prefix with their original names, the manifest
          artifact itself is copied with its digest intact, and the `atmosphere_release_manifest`
          / `atmosphere_image_mirrors` vars are written out.\n\nThe `image_manifest`
          role keeps working but now warns that it is deprecated.\n\n**Docs:** a new
          deployment guide, `doc/source/deploy/images.rst` (\"Container images and
          mirrors\"), covers release manifests, the image prefix, mirror rules, overrides,
          mirroring (including disconnected sites) and `check`/`diff`. The `defaults`
          role README lists the image variables, and the release note covers the new
          features, the unchanged upgrade path, the `image_manifest` deprecation and
          the `pinimages` replacement.\n\n### Compatibility\n\nDeployments that set
          none of the new variables get byte-identical `atmosphere_images`: I rendered
          the `defaults` role through `ansible-playbook` before and after this change
          with no prefix, with a Harbor prefix, and with prefix + overrides, on `main`
          (231 images) and on `stable/zed` (216 images); all outputs are identical.
          `write-vars` produces the same vars file as `pinimages` except that Ceph
          is now pinned too (`pinimages` could not resolve its `{{ ceph_version }}`
          template).\n\n### Testing\n\n- Go: `internal/releasemanifest` tests cover
          generation, `write-vars`, push/fetch/promote, every mirror layout with multi-arch
          images in in-memory registries, directory export/import, digest-mismatch
          detection, registries with ports and retry on rate limits.\n- Go: `publish`
          against a fake Zuul API (tested manifest used unchanged with the same artifact
          digest, gate searched before check, failed buildsets skipped, fallback when
          inputs changed, nothing passed or Zuul is unreachable), and reproducible
          pushes.\n- Python: the `resolve_images` filter and the Go resolver run the
          same cases from `internal/releasemanifest/testdata/resolve.yaml`; the lookup
          is tested against a local HTTP registry with token auth and blob redirects
          (credentials must not reach storage) and tampered content, and against an
          HTTPS registry with a private CA and basic authentication (fails without
          the CA or credentials), including fetching the manifest through the prefix
          and mirror rules. A new `python-test` CI job runs these (the existing `tests/unit`
          suite is not run in CI and has a stale magnum expectation, so the job only
          runs the new tests).\n- End to end against a local CNCF Distribution registry
          with real images from `registry.k8s.io`, `ghcr.io` and Docker Hub: `generate`,
          `push`, `promote`, `show`, nested mirror, export \u2192 flat import, `check`,
          `diff`, plus deploying the mirrored manifest through the lookup.\n- `generate`
          against the real registries resolves all 231 images on `main` and `stable/2025.2`.\n\n###
          Backports\n\n`go.mod` and `go.sum` are untouched; every dependency is already
          present at the same version on all stable branches.\n\n- `stable/2025.2`
          cherry-picks cleanly and passes everything above.\n- `stable/2025.1` back
          to `stable/zed` conflict in one hunk of `roles/defaults/vars/main.yml`:
          the conflict context includes `main`'s `_atmosphere_storage` block, which
          those branches don't have. Resolve it by keeping the new `atmosphere_images`
          block and dropping the `_atmosphere_storage` lines. I did this on `stable/zed`
          and the Go tests, Python tests and the old/new rendering comparison all
          pass.\n\n### CI deploys the manifest\n\nWith vexxhost/atmosphere-zuul-jobs#7,
          an `atmosphere-release-manifest` job generates this change's release manifest
          once per buildset. Every molecule job (AIO, CSI, Keycloak) depends on it,
          pushes that exact manifest to a TLS registry with password authentication
          on its node, checks the pushed digest matches, and deploys by registry reference
          with `atmosphere_release_manifest`, `atmosphere_release_manifest_ca_file`
          and a mirror rule, the same path customers use for published releases. That
          buildset's manifest is what `publish` publishes after merge.\n\n### Before
          merging / follow-ups\n\n- After the first push publishes `ghcr.io/vexxhost/atmosphere-release`,
          make the package public and set retention (keep release tags; commit tags
          can be pruned).\n- Docker Hub's anonymous rate limit can still fail `generate`
          after its retries; previously this silently produced unpinned images.\n-
          `harbor.atmosphere.dev` has no proxy project for `packages.vexxhost.com`
          (used by `prometheus_smartctl_exporter`).\n- The docs build with `-W` already
          fails on `main` because of existing warnings in `admin/monitoring.rst` and
          `arch/images.rst`; the new page adds none.\n\nDepends-On: https://github.com/vexxhost/atmosphere-zuul-jobs/pull/7\n"
        change_url: https://github.com/vexxhost/atmosphere/pull/4420
        commit_id: 32ae9479091acf66cc00cd680ed6e0307341626d
        patchset: 32ae9479091acf66cc00cd680ed6e0307341626d
        project:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/atmosphere
          name: vexxhost/atmosphere
          short_name: atmosphere
          src_dir: src/github.com/vexxhost/atmosphere
        src_dir: src/github.com/vexxhost/atmosphere
        topic: null
      change: '4420'
      change_message: "feat: publish release manifests and deploy from them\n\nToday
        a release resolves image tags at release time, after CI has run: 88 of the
        232 entries in `roles/defaults/vars/main.yml` are plain tags, and `cmd/pinimages`
        resolves them into the Galaxy collection, silently skipping any it cannot
        resolve (it logs 94 errors on `main` today). The digests a release ships are
        only recorded inside the Galaxy tarball, and mirroring for air-gapped installs
        goes through the `image_manifest` role, whose registry rewrite keeps only
        the last path component, so `rook/ceph` and `ceph/ceph` both become `<registry>/ceph`.\n\nThis
        PR makes a digest-pinned **release manifest** the source of truth for which
        images a commit or release deploys.\n\n**Publishing**\n- On every push to
        `main` and `stable/*`, `atmosphere-release publish` publishes the manifest
        as an OCI artifact to `ghcr.io/vexxhost/atmosphere-release`, tagged with the
        full commit SHA and the branch (`main`, `stable-2025.2`, ...) as computed
        by `docker/metadata-action`. It looks up the merged pull request through the
        GitHub API and asks Zuul for the newest buildset in which every job passed
        for that PR's head commit (`gate`, then `check`). If that buildset's manifest
        was generated from the same image inputs as this commit, it is published unchanged,
        so what ships is exactly what CI deployed. Otherwise (direct push, no passing
        buildset, logs expired, or another merge changed the image list) the manifest
        the `build` job just generated is published with `com.vexxhost.atmosphere.tested:
        \"false\"`.\n- Manifests record `inputs`, a digest of the rendered image list
        before tags are resolved, and pushes are reproducible (fixed creation time),
        so the same manifest always has the same artifact digest wherever it is pushed.\n-
        `release.yaml` uses `docker/metadata-action` for the release tag and promotes
        the released commit's manifest to it (no tag is resolved again). GoReleaser
        (`.goreleaser.yaml`) attaches `atmosphere-release` archives for linux/darwin
        amd64/arm64, their checksums and the release manifest to the existing GitHub
        release without touching its notes, and the Galaxy collection is pinned from
        that same manifest.\n\n**Deploying**\n- `atmosphere_release_manifest` (OCI
        reference or file path) deploys exactly a manifest's images. OCI references
        are read through `atmosphere_image_mirrors` and `atmosphere_image_prefix`
        like every image, so Harbor-prefix and mirror users also fetch the manifest
        through their cache. `atmosphere_release_manifest_ca_file` adds a private
        CA to trust; credentials come from the Docker configuration. Kubernetes control
        plane images fall back to the tag when another `atmosphere_kubernetes_version`
        is deployed.\n- `atmosphere_image_mirrors` redirects images per registry,
        namespace or repository (most specific rule wins); `atmosphere_image_prefix`
        still applies to everything no rule matches, so Harbor proxy projects with
        custom names now work too.\n- Resolution happens in the rendered references,
        so it works with any Kubernetes and container runtime. `atmosphere_image_overrides`
        are applied last, unchanged.\n\n**`atmosphere-release` command** (`cmd/atmosphere-release`,
        replaces `cmd/pinimages`)\n- `generate [--write-vars] [--resolve-prefix]`,
        `push`, `promote`, `publish`, `digest`, `show`, `diff`, `check`. `push` and
        `promote` take `repository:tag` references, so they consume `docker/metadata-action`
        output directly; `--resolve-prefix` resolves tags through a pull-through cache
        (falling back to the source registry) to avoid upstream rate limits.\n- `mirror`
        to a registry, or `--to-dir` / `--from-dir` for disconnected sites, in `nested`,
        `flat` (`quay.io--ceph--ceph`) or `single-repo` layouts. Images are copied
        with every platform so digests never change, each digest is verified, kubeadm's
        images stay under one prefix with their original names, the manifest artifact
        itself is copied with its digest intact, and the `atmosphere_release_manifest`
        / `atmosphere_image_mirrors` vars are written out.\n\nThe `image_manifest`
        role keeps working but now warns that it is deprecated.\n\n**Docs:** a new
        deployment guide, `doc/source/deploy/images.rst` (\"Container images and mirrors\"),
        covers release manifests, the image prefix, mirror rules, overrides, mirroring
        (including disconnected sites) and `check`/`diff`. The `defaults` role README
        lists the image variables, and the release note covers the new features, the
        unchanged upgrade path, the `image_manifest` deprecation and the `pinimages`
        replacement.\n\n### Compatibility\n\nDeployments that set none of the new
        variables get byte-identical `atmosphere_images`: I rendered the `defaults`
        role through `ansible-playbook` before and after this change with no prefix,
        with a Harbor prefix, and with prefix + overrides, on `main` (231 images)
        and on `stable/zed` (216 images); all outputs are identical. `write-vars`
        produces the same vars file as `pinimages` except that Ceph is now pinned
        too (`pinimages` could not resolve its `{{ ceph_version }}` template).\n\n###
        Testing\n\n- Go: `internal/releasemanifest` tests cover generation, `write-vars`,
        push/fetch/promote, every mirror layout with multi-arch images in in-memory
        registries, directory export/import, digest-mismatch detection, registries
        with ports and retry on rate limits.\n- Go: `publish` against a fake Zuul
        API (tested manifest used unchanged with the same artifact digest, gate searched
        before check, failed buildsets skipped, fallback when inputs changed, nothing
        passed or Zuul is unreachable), and reproducible pushes.\n- Python: the `resolve_images`
        filter and the Go resolver run the same cases from `internal/releasemanifest/testdata/resolve.yaml`;
        the lookup is tested against a local HTTP registry with token auth and blob
        redirects (credentials must not reach storage) and tampered content, and against
        an HTTPS registry with a private CA and basic authentication (fails without
        the CA or credentials), including fetching the manifest through the prefix
        and mirror rules. A new `python-test` CI job runs these (the existing `tests/unit`
        suite is not run in CI and has a stale magnum expectation, so the job only
        runs the new tests).\n- End to end against a local CNCF Distribution registry
        with real images from `registry.k8s.io`, `ghcr.io` and Docker Hub: `generate`,
        `push`, `promote`, `show`, nested mirror, export \u2192 flat import, `check`,
        `diff`, plus deploying the mirrored manifest through the lookup.\n- `generate`
        against the real registries resolves all 231 images on `main` and `stable/2025.2`.\n\n###
        Backports\n\n`go.mod` and `go.sum` are untouched; every dependency is already
        present at the same version on all stable branches.\n\n- `stable/2025.2` cherry-picks
        cleanly and passes everything above.\n- `stable/2025.1` back to `stable/zed`
        conflict in one hunk of `roles/defaults/vars/main.yml`: the conflict context
        includes `main`'s `_atmosphere_storage` block, which those branches don't
        have. Resolve it by keeping the new `atmosphere_images` block and dropping
        the `_atmosphere_storage` lines. I did this on `stable/zed` and the Go tests,
        Python tests and the old/new rendering comparison all pass.\n\n### CI deploys
        the manifest\n\nWith vexxhost/atmosphere-zuul-jobs#7, an `atmosphere-release-manifest`
        job generates this change's release manifest once per buildset. Every molecule
        job (AIO, CSI, Keycloak) depends on it, pushes that exact manifest to a TLS
        registry with password authentication on its node, checks the pushed digest
        matches, and deploys by registry reference with `atmosphere_release_manifest`,
        `atmosphere_release_manifest_ca_file` and a mirror rule, the same path customers
        use for published releases. That buildset's manifest is what `publish` publishes
        after merge.\n\n### Before merging / follow-ups\n\n- After the first push
        publishes `ghcr.io/vexxhost/atmosphere-release`, make the package public and
        set retention (keep release tags; commit tags can be pruned).\n- Docker Hub's
        anonymous rate limit can still fail `generate` after its retries; previously
        this silently produced unpinned images.\n- `harbor.atmosphere.dev` has no
        proxy project for `packages.vexxhost.com` (used by `prometheus_smartctl_exporter`).\n-
        The docs build with `-W` already fails on `main` because of existing warnings
        in `admin/monitoring.rst` and `arch/images.rst`; the new page adds none.\n\nDepends-On:
        https://github.com/vexxhost/atmosphere-zuul-jobs/pull/7\n"
      change_url: https://github.com/vexxhost/atmosphere/pull/4420
      child_jobs: []
      commit_id: 32ae9479091acf66cc00cd680ed6e0307341626d
      event_id: 510d96f0-c9fd-11f1-8dae-ad4ac9d9d558
      executor:
        hostname: 2d72f0692154
        inventory_file: /var/lib/zuul/builds/88153ed6dc634da0ab502e7273feee8e/ansible/inventory.yaml
        log_root: /var/lib/zuul/builds/88153ed6dc634da0ab502e7273feee8e/work/logs
        result_data_file: /var/lib/zuul/builds/88153ed6dc634da0ab502e7273feee8e/work/results.json
        src_root: /var/lib/zuul/builds/88153ed6dc634da0ab502e7273feee8e/work/src
        work_root: /var/lib/zuul/builds/88153ed6dc634da0ab502e7273feee8e/work
      include_vars: []
      items:
      - branch: main
        change: '3'
        change_message: 'feat(pre-commit): create hook environments with uv


          The `pre-commit` job now runs pre-commit with the [pre-commit-uv](https://github.com/tox-dev/pre-commit-uv)
          plugin, so hook environments are created with `uv`.


          The main reason is the latest `ansible-lint` hook: it sets `language_version:
          python3.14`, and our Ubuntu 24.04 nodes only have Python 3.12, so repositories
          have to override the hook''s Python version. With pre-commit-uv, `uv` downloads
          the Python a hook asks for when the node doesn''t have it. `setup-uv` installs
          only the `uv` binary with no configuration, so `uv`''s default (download
          managed Pythons when needed) applies. Cold hook installs are also faster:
          about 20s instead of 29s for ansible-lint.


          Tested locally with `UV_NO_CONFIG=1` (uv defaults, as on the nodes) and
          a cold pre-commit cache:


          - vexxhost/atmosphere-zuul-jobs#7 without the Python 3.12 override: passes,
          ansible-lint v26.10.1 runs in a Python 3.14 environment downloaded by uv.

          - vexxhost/ansible-collection-ceph (ansible-lint v25.6.1) passes unchanged.


          This repository has no checks of its own; vexxhost/atmosphere-zuul-jobs#7
          tests this change through `Depends-On`.

          '
        change_url: https://github.com/vexxhost/zuul-jobs/pull/3
        commit_id: 9848de64840bf296f0bd8039c6a425174ceaee91
        patchset: 9848de64840bf296f0bd8039c6a425174ceaee91
        project:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/zuul-jobs
          name: vexxhost/zuul-jobs
          short_name: zuul-jobs
          src_dir: src/github.com/vexxhost/zuul-jobs
        topic: null
      - branch: main
        change: '7'
        change_message: 'feat(molecule): deploy from one release manifest per buildset


          The molecule jobs deploy whatever tags `roles/defaults/vars/main.yml` points
          at when each job runs, while releases ship digest-pinned images. With vexxhost/atmosphere#4420,
          Atmosphere publishes a digest-pinned release manifest per commit; this makes
          CI generate that manifest once per buildset and deploy exactly it, through
          the same registry path customers use.


          - **`atmosphere-release-manifest` job** (`atmosphere-build-manifest` role):
          builds `atmosphere-release` from the change under test, generates the manifest
          (resolving tags through `harbor.atmosphere.dev/` to avoid upstream rate
          limits; the manifest records the original references) and returns the manifest
          and the binary as Zuul artifacts, with the digest of the artifact that pushing
          the manifest produces.

          - **Molecule jobs** depend on it. Their pre-run uses the new `atmosphere-use-manifest`
          role: it starts a CNCF Distribution registry on the node (pinned, checksum-verified)
          with TLS from a throwaway CA and password authentication (user created with
          `community.general.htpasswd`), pushes the manifest, and fails unless the
          pushed artifact has the digest the build job reported. Atmosphere then reads
          it by reference (`atmosphere_release_manifest`, `atmosphere_release_manifest_ca_file`,
          and a mirror rule that keeps `atmosphere_image_prefix` away from that registry).

          - Every job therefore deploys the same manifest, and after merge `atmosphere-release
          publish` finds this buildset''s manifest through the Zuul API and publishes
          it unchanged.


          **Linting:** every change to this repository now runs the existing `pre-commit`
          job with the latest `ansible-lint` (v26.10.1, with ansible-core 2.21; passes
          the `production` profile) and whitespace checks from `pre-commit-hooks`
          v6.0.0, the same setup as the Ansible collection repositories. The hook
          asks for Python 3.14, which the nodes don''t have; vexxhost/zuul-jobs#3
          makes the `pre-commit` job use pre-commit-uv, so `uv` provides it and no
          override is needed. `.ansible-lint` mocks the roles and `zuul_return` that
          only exist in Zuul and skips `role-name`, since Zuul roles use hyphens;
          `requirements.yml` lets ansible-lint install `community.general` to validate
          module arguments. This replaces the `noop` job.


          This calls `cmd/atmosphere-release` on every branch these jobs run against,
          so it must merge **after** vexxhost/atmosphere#4420 and its backports.


          Tested through `Depends-On` from vexxhost/atmosphere#4420 and by the role
          tests in #8.


          Depends-On: https://github.com/vexxhost/zuul-jobs/pull/3

          '
        change_url: https://github.com/vexxhost/atmosphere-zuul-jobs/pull/7
        commit_id: fe46543b39a569a2360919be71612a4f28966bd6
        patchset: fe46543b39a569a2360919be71612a4f28966bd6
        project:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/atmosphere-zuul-jobs
          name: vexxhost/atmosphere-zuul-jobs
          short_name: atmosphere-zuul-jobs
          src_dir: src/github.com/vexxhost/atmosphere-zuul-jobs
        topic: null
      - branch: main
        change: '4420'
        change_message: "feat: publish release manifests and deploy from them\n\nToday
          a release resolves image tags at release time, after CI has run: 88 of the
          232 entries in `roles/defaults/vars/main.yml` are plain tags, and `cmd/pinimages`
          resolves them into the Galaxy collection, silently skipping any it cannot
          resolve (it logs 94 errors on `main` today). The digests a release ships
          are only recorded inside the Galaxy tarball, and mirroring for air-gapped
          installs goes through the `image_manifest` role, whose registry rewrite
          keeps only the last path component, so `rook/ceph` and `ceph/ceph` both
          become `<registry>/ceph`.\n\nThis PR makes a digest-pinned **release manifest**
          the source of truth for which images a commit or release deploys.\n\n**Publishing**\n-
          On every push to `main` and `stable/*`, `atmosphere-release publish` publishes
          the manifest as an OCI artifact to `ghcr.io/vexxhost/atmosphere-release`,
          tagged with the full commit SHA and the branch (`main`, `stable-2025.2`,
          ...) as computed by `docker/metadata-action`. It looks up the merged pull
          request through the GitHub API and asks Zuul for the newest buildset in
          which every job passed for that PR's head commit (`gate`, then `check`).
          If that buildset's manifest was generated from the same image inputs as
          this commit, it is published unchanged, so what ships is exactly what CI
          deployed. Otherwise (direct push, no passing buildset, logs expired, or
          another merge changed the image list) the manifest the `build` job just
          generated is published with `com.vexxhost.atmosphere.tested: \"false\"`.\n-
          Manifests record `inputs`, a digest of the rendered image list before tags
          are resolved, and pushes are reproducible (fixed creation time), so the
          same manifest always has the same artifact digest wherever it is pushed.\n-
          `release.yaml` uses `docker/metadata-action` for the release tag and promotes
          the released commit's manifest to it (no tag is resolved again). GoReleaser
          (`.goreleaser.yaml`) attaches `atmosphere-release` archives for linux/darwin
          amd64/arm64, their checksums and the release manifest to the existing GitHub
          release without touching its notes, and the Galaxy collection is pinned
          from that same manifest.\n\n**Deploying**\n- `atmosphere_release_manifest`
          (OCI reference or file path) deploys exactly a manifest's images. OCI references
          are read through `atmosphere_image_mirrors` and `atmosphere_image_prefix`
          like every image, so Harbor-prefix and mirror users also fetch the manifest
          through their cache. `atmosphere_release_manifest_ca_file` adds a private
          CA to trust; credentials come from the Docker configuration. Kubernetes
          control plane images fall back to the tag when another `atmosphere_kubernetes_version`
          is deployed.\n- `atmosphere_image_mirrors` redirects images per registry,
          namespace or repository (most specific rule wins); `atmosphere_image_prefix`
          still applies to everything no rule matches, so Harbor proxy projects with
          custom names now work too.\n- Resolution happens in the rendered references,
          so it works with any Kubernetes and container runtime. `atmosphere_image_overrides`
          are applied last, unchanged.\n\n**`atmosphere-release` command** (`cmd/atmosphere-release`,
          replaces `cmd/pinimages`)\n- `generate [--write-vars] [--resolve-prefix]`,
          `push`, `promote`, `publish`, `digest`, `show`, `diff`, `check`. `push`
          and `promote` take `repository:tag` references, so they consume `docker/metadata-action`
          output directly; `--resolve-prefix` resolves tags through a pull-through
          cache (falling back to the source registry) to avoid upstream rate limits.\n-
          `mirror` to a registry, or `--to-dir` / `--from-dir` for disconnected sites,
          in `nested`, `flat` (`quay.io--ceph--ceph`) or `single-repo` layouts. Images
          are copied with every platform so digests never change, each digest is verified,
          kubeadm's images stay under one prefix with their original names, the manifest
          artifact itself is copied with its digest intact, and the `atmosphere_release_manifest`
          / `atmosphere_image_mirrors` vars are written out.\n\nThe `image_manifest`
          role keeps working but now warns that it is deprecated.\n\n**Docs:** a new
          deployment guide, `doc/source/deploy/images.rst` (\"Container images and
          mirrors\"), covers release manifests, the image prefix, mirror rules, overrides,
          mirroring (including disconnected sites) and `check`/`diff`. The `defaults`
          role README lists the image variables, and the release note covers the new
          features, the unchanged upgrade path, the `image_manifest` deprecation and
          the `pinimages` replacement.\n\n### Compatibility\n\nDeployments that set
          none of the new variables get byte-identical `atmosphere_images`: I rendered
          the `defaults` role through `ansible-playbook` before and after this change
          with no prefix, with a Harbor prefix, and with prefix + overrides, on `main`
          (231 images) and on `stable/zed` (216 images); all outputs are identical.
          `write-vars` produces the same vars file as `pinimages` except that Ceph
          is now pinned too (`pinimages` could not resolve its `{{ ceph_version }}`
          template).\n\n### Testing\n\n- Go: `internal/releasemanifest` tests cover
          generation, `write-vars`, push/fetch/promote, every mirror layout with multi-arch
          images in in-memory registries, directory export/import, digest-mismatch
          detection, registries with ports and retry on rate limits.\n- Go: `publish`
          against a fake Zuul API (tested manifest used unchanged with the same artifact
          digest, gate searched before check, failed buildsets skipped, fallback when
          inputs changed, nothing passed or Zuul is unreachable), and reproducible
          pushes.\n- Python: the `resolve_images` filter and the Go resolver run the
          same cases from `internal/releasemanifest/testdata/resolve.yaml`; the lookup
          is tested against a local HTTP registry with token auth and blob redirects
          (credentials must not reach storage) and tampered content, and against an
          HTTPS registry with a private CA and basic authentication (fails without
          the CA or credentials), including fetching the manifest through the prefix
          and mirror rules. A new `python-test` CI job runs these (the existing `tests/unit`
          suite is not run in CI and has a stale magnum expectation, so the job only
          runs the new tests).\n- End to end against a local CNCF Distribution registry
          with real images from `registry.k8s.io`, `ghcr.io` and Docker Hub: `generate`,
          `push`, `promote`, `show`, nested mirror, export \u2192 flat import, `check`,
          `diff`, plus deploying the mirrored manifest through the lookup.\n- `generate`
          against the real registries resolves all 231 images on `main` and `stable/2025.2`.\n\n###
          Backports\n\n`go.mod` and `go.sum` are untouched; every dependency is already
          present at the same version on all stable branches.\n\n- `stable/2025.2`
          cherry-picks cleanly and passes everything above.\n- `stable/2025.1` back
          to `stable/zed` conflict in one hunk of `roles/defaults/vars/main.yml`:
          the conflict context includes `main`'s `_atmosphere_storage` block, which
          those branches don't have. Resolve it by keeping the new `atmosphere_images`
          block and dropping the `_atmosphere_storage` lines. I did this on `stable/zed`
          and the Go tests, Python tests and the old/new rendering comparison all
          pass.\n\n### CI deploys the manifest\n\nWith vexxhost/atmosphere-zuul-jobs#7,
          an `atmosphere-release-manifest` job generates this change's release manifest
          once per buildset. Every molecule job (AIO, CSI, Keycloak) depends on it,
          pushes that exact manifest to a TLS registry with password authentication
          on its node, checks the pushed digest matches, and deploys by registry reference
          with `atmosphere_release_manifest`, `atmosphere_release_manifest_ca_file`
          and a mirror rule, the same path customers use for published releases. That
          buildset's manifest is what `publish` publishes after merge.\n\n### Before
          merging / follow-ups\n\n- After the first push publishes `ghcr.io/vexxhost/atmosphere-release`,
          make the package public and set retention (keep release tags; commit tags
          can be pruned).\n- Docker Hub's anonymous rate limit can still fail `generate`
          after its retries; previously this silently produced unpinned images.\n-
          `harbor.atmosphere.dev` has no proxy project for `packages.vexxhost.com`
          (used by `prometheus_smartctl_exporter`).\n- The docs build with `-W` already
          fails on `main` because of existing warnings in `admin/monitoring.rst` and
          `arch/images.rst`; the new page adds none.\n\nDepends-On: https://github.com/vexxhost/atmosphere-zuul-jobs/pull/7\n"
        change_url: https://github.com/vexxhost/atmosphere/pull/4420
        commit_id: 32ae9479091acf66cc00cd680ed6e0307341626d
        patchset: 32ae9479091acf66cc00cd680ed6e0307341626d
        project:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/atmosphere
          name: vexxhost/atmosphere
          short_name: atmosphere
          src_dir: src/github.com/vexxhost/atmosphere
        topic: null
      job: atmosphere-molecule-keycloak
      jobtags: []
      max_attempts: 3
      message: ZmVhdDogcHVibGlzaCByZWxlYXNlIG1hbmlmZXN0cyBhbmQgZGVwbG95IGZyb20gdGhlbQoKVG9kYXkgYSByZWxlYXNlIHJlc29sdmVzIGltYWdlIHRhZ3MgYXQgcmVsZWFzZSB0aW1lLCBhZnRlciBDSSBoYXMgcnVuOiA4OCBvZiB0aGUgMjMyIGVudHJpZXMgaW4gYHJvbGVzL2RlZmF1bHRzL3ZhcnMvbWFpbi55bWxgIGFyZSBwbGFpbiB0YWdzLCBhbmQgYGNtZC9waW5pbWFnZXNgIHJlc29sdmVzIHRoZW0gaW50byB0aGUgR2FsYXh5IGNvbGxlY3Rpb24sIHNpbGVudGx5IHNraXBwaW5nIGFueSBpdCBjYW5ub3QgcmVzb2x2ZSAoaXQgbG9ncyA5NCBlcnJvcnMgb24gYG1haW5gIHRvZGF5KS4gVGhlIGRpZ2VzdHMgYSByZWxlYXNlIHNoaXBzIGFyZSBvbmx5IHJlY29yZGVkIGluc2lkZSB0aGUgR2FsYXh5IHRhcmJhbGwsIGFuZCBtaXJyb3JpbmcgZm9yIGFpci1nYXBwZWQgaW5zdGFsbHMgZ29lcyB0aHJvdWdoIHRoZSBgaW1hZ2VfbWFuaWZlc3RgIHJvbGUsIHdob3NlIHJlZ2lzdHJ5IHJld3JpdGUga2VlcHMgb25seSB0aGUgbGFzdCBwYXRoIGNvbXBvbmVudCwgc28gYHJvb2svY2VwaGAgYW5kIGBjZXBoL2NlcGhgIGJvdGggYmVjb21lIGA8cmVnaXN0cnk+L2NlcGhgLgoKVGhpcyBQUiBtYWtlcyBhIGRpZ2VzdC1waW5uZWQgKipyZWxlYXNlIG1hbmlmZXN0KiogdGhlIHNvdXJjZSBvZiB0cnV0aCBmb3Igd2hpY2ggaW1hZ2VzIGEgY29tbWl0IG9yIHJlbGVhc2UgZGVwbG95cy4KCioqUHVibGlzaGluZyoqCi0gT24gZXZlcnkgcHVzaCB0byBgbWFpbmAgYW5kIGBzdGFibGUvKmAsIGBhdG1vc3BoZXJlLXJlbGVhc2UgcHVibGlzaGAgcHVibGlzaGVzIHRoZSBtYW5pZmVzdCBhcyBhbiBPQ0kgYXJ0aWZhY3QgdG8gYGdoY3IuaW8vdmV4eGhvc3QvYXRtb3NwaGVyZS1yZWxlYXNlYCwgdGFnZ2VkIHdpdGggdGhlIGZ1bGwgY29tbWl0IFNIQSBhbmQgdGhlIGJyYW5jaCAoYG1haW5gLCBgc3RhYmxlLTIwMjUuMmAsIC4uLikgYXMgY29tcHV0ZWQgYnkgYGRvY2tlci9tZXRhZGF0YS1hY3Rpb25gLiBJdCBsb29rcyB1cCB0aGUgbWVyZ2VkIHB1bGwgcmVxdWVzdCB0aHJvdWdoIHRoZSBHaXRIdWIgQVBJIGFuZCBhc2tzIFp1dWwgZm9yIHRoZSBuZXdlc3QgYnVpbGRzZXQgaW4gd2hpY2ggZXZlcnkgam9iIHBhc3NlZCBmb3IgdGhhdCBQUidzIGhlYWQgY29tbWl0IChgZ2F0ZWAsIHRoZW4gYGNoZWNrYCkuIElmIHRoYXQgYnVpbGRzZXQncyBtYW5pZmVzdCB3YXMgZ2VuZXJhdGVkIGZyb20gdGhlIHNhbWUgaW1hZ2UgaW5wdXRzIGFzIHRoaXMgY29tbWl0LCBpdCBpcyBwdWJsaXNoZWQgdW5jaGFuZ2VkLCBzbyB3aGF0IHNoaXBzIGlzIGV4YWN0bHkgd2hhdCBDSSBkZXBsb3llZC4gT3RoZXJ3aXNlIChkaXJlY3QgcHVzaCwgbm8gcGFzc2luZyBidWlsZHNldCwgbG9ncyBleHBpcmVkLCBvciBhbm90aGVyIG1lcmdlIGNoYW5nZWQgdGhlIGltYWdlIGxpc3QpIHRoZSBtYW5pZmVzdCB0aGUgYGJ1aWxkYCBqb2IganVzdCBnZW5lcmF0ZWQgaXMgcHVibGlzaGVkIHdpdGggYGNvbS52ZXh4aG9zdC5hdG1vc3BoZXJlLnRlc3RlZDogImZhbHNlImAuCi0gTWFuaWZlc3RzIHJlY29yZCBgaW5wdXRzYCwgYSBkaWdlc3Qgb2YgdGhlIHJlbmRlcmVkIGltYWdlIGxpc3QgYmVmb3JlIHRhZ3MgYXJlIHJlc29sdmVkLCBhbmQgcHVzaGVzIGFyZSByZXByb2R1Y2libGUgKGZpeGVkIGNyZWF0aW9uIHRpbWUpLCBzbyB0aGUgc2FtZSBtYW5pZmVzdCBhbHdheXMgaGFzIHRoZSBzYW1lIGFydGlmYWN0IGRpZ2VzdCB3aGVyZXZlciBpdCBpcyBwdXNoZWQuCi0gYHJlbGVhc2UueWFtbGAgdXNlcyBgZG9ja2VyL21ldGFkYXRhLWFjdGlvbmAgZm9yIHRoZSByZWxlYXNlIHRhZyBhbmQgcHJvbW90ZXMgdGhlIHJlbGVhc2VkIGNvbW1pdCdzIG1hbmlmZXN0IHRvIGl0IChubyB0YWcgaXMgcmVzb2x2ZWQgYWdhaW4pLiBHb1JlbGVhc2VyIChgLmdvcmVsZWFzZXIueWFtbGApIGF0dGFjaGVzIGBhdG1vc3BoZXJlLXJlbGVhc2VgIGFyY2hpdmVzIGZvciBsaW51eC9kYXJ3aW4gYW1kNjQvYXJtNjQsIHRoZWlyIGNoZWNrc3VtcyBhbmQgdGhlIHJlbGVhc2UgbWFuaWZlc3QgdG8gdGhlIGV4aXN0aW5nIEdpdEh1YiByZWxlYXNlIHdpdGhvdXQgdG91Y2hpbmcgaXRzIG5vdGVzLCBhbmQgdGhlIEdhbGF4eSBjb2xsZWN0aW9uIGlzIHBpbm5lZCBmcm9tIHRoYXQgc2FtZSBtYW5pZmVzdC4KCioqRGVwbG95aW5nKioKLSBgYXRtb3NwaGVyZV9yZWxlYXNlX21hbmlmZXN0YCAoT0NJIHJlZmVyZW5jZSBvciBmaWxlIHBhdGgpIGRlcGxveXMgZXhhY3RseSBhIG1hbmlmZXN0J3MgaW1hZ2VzLiBPQ0kgcmVmZXJlbmNlcyBhcmUgcmVhZCB0aHJvdWdoIGBhdG1vc3BoZXJlX2ltYWdlX21pcnJvcnNgIGFuZCBgYXRtb3NwaGVyZV9pbWFnZV9wcmVmaXhgIGxpa2UgZXZlcnkgaW1hZ2UsIHNvIEhhcmJvci1wcmVmaXggYW5kIG1pcnJvciB1c2VycyBhbHNvIGZldGNoIHRoZSBtYW5pZmVzdCB0aHJvdWdoIHRoZWlyIGNhY2hlLiBgYXRtb3NwaGVyZV9yZWxlYXNlX21hbmlmZXN0X2NhX2ZpbGVgIGFkZHMgYSBwcml2YXRlIENBIHRvIHRydXN0OyBjcmVkZW50aWFscyBjb21lIGZyb20gdGhlIERvY2tlciBjb25maWd1cmF0aW9uLiBLdWJlcm5ldGVzIGNvbnRyb2wgcGxhbmUgaW1hZ2VzIGZhbGwgYmFjayB0byB0aGUgdGFnIHdoZW4gYW5vdGhlciBgYXRtb3NwaGVyZV9rdWJlcm5ldGVzX3ZlcnNpb25gIGlzIGRlcGxveWVkLgotIGBhdG1vc3BoZXJlX2ltYWdlX21pcnJvcnNgIHJlZGlyZWN0cyBpbWFnZXMgcGVyIHJlZ2lzdHJ5LCBuYW1lc3BhY2Ugb3IgcmVwb3NpdG9yeSAobW9zdCBzcGVjaWZpYyBydWxlIHdpbnMpOyBgYXRtb3NwaGVyZV9pbWFnZV9wcmVmaXhgIHN0aWxsIGFwcGxpZXMgdG8gZXZlcnl0aGluZyBubyBydWxlIG1hdGNoZXMsIHNvIEhhcmJvciBwcm94eSBwcm9qZWN0cyB3aXRoIGN1c3RvbSBuYW1lcyBub3cgd29yayB0b28uCi0gUmVzb2x1dGlvbiBoYXBwZW5zIGluIHRoZSByZW5kZXJlZCByZWZlcmVuY2VzLCBzbyBpdCB3b3JrcyB3aXRoIGFueSBLdWJlcm5ldGVzIGFuZCBjb250YWluZXIgcnVudGltZS4gYGF0bW9zcGhlcmVfaW1hZ2Vfb3ZlcnJpZGVzYCBhcmUgYXBwbGllZCBsYXN0LCB1bmNoYW5nZWQuCgoqKmBhdG1vc3BoZXJlLXJlbGVhc2VgIGNvbW1hbmQqKiAoYGNtZC9hdG1vc3BoZXJlLXJlbGVhc2VgLCByZXBsYWNlcyBgY21kL3BpbmltYWdlc2ApCi0gYGdlbmVyYXRlIFstLXdyaXRlLXZhcnNdIFstLXJlc29sdmUtcHJlZml4XWAsIGBwdXNoYCwgYHByb21vdGVgLCBgcHVibGlzaGAsIGBkaWdlc3RgLCBgc2hvd2AsIGBkaWZmYCwgYGNoZWNrYC4gYHB1c2hgIGFuZCBgcHJvbW90ZWAgdGFrZSBgcmVwb3NpdG9yeTp0YWdgIHJlZmVyZW5jZXMsIHNvIHRoZXkgY29uc3VtZSBgZG9ja2VyL21ldGFkYXRhLWFjdGlvbmAgb3V0cHV0IGRpcmVjdGx5OyBgLS1yZXNvbHZlLXByZWZpeGAgcmVzb2x2ZXMgdGFncyB0aHJvdWdoIGEgcHVsbC10aHJvdWdoIGNhY2hlIChmYWxsaW5nIGJhY2sgdG8gdGhlIHNvdXJjZSByZWdpc3RyeSkgdG8gYXZvaWQgdXBzdHJlYW0gcmF0ZSBsaW1pdHMuCi0gYG1pcnJvcmAgdG8gYSByZWdpc3RyeSwgb3IgYC0tdG8tZGlyYCAvIGAtLWZyb20tZGlyYCBmb3IgZGlzY29ubmVjdGVkIHNpdGVzLCBpbiBgbmVzdGVkYCwgYGZsYXRgIChgcXVheS5pby0tY2VwaC0tY2VwaGApIG9yIGBzaW5nbGUtcmVwb2AgbGF5b3V0cy4gSW1hZ2VzIGFyZSBjb3BpZWQgd2l0aCBldmVyeSBwbGF0Zm9ybSBzbyBkaWdlc3RzIG5ldmVyIGNoYW5nZSwgZWFjaCBkaWdlc3QgaXMgdmVyaWZpZWQsIGt1YmVhZG0ncyBpbWFnZXMgc3RheSB1bmRlciBvbmUgcHJlZml4IHdpdGggdGhlaXIgb3JpZ2luYWwgbmFtZXMsIHRoZSBtYW5pZmVzdCBhcnRpZmFjdCBpdHNlbGYgaXMgY29waWVkIHdpdGggaXRzIGRpZ2VzdCBpbnRhY3QsIGFuZCB0aGUgYGF0bW9zcGhlcmVfcmVsZWFzZV9tYW5pZmVzdGAgLyBgYXRtb3NwaGVyZV9pbWFnZV9taXJyb3JzYCB2YXJzIGFyZSB3cml0dGVuIG91dC4KClRoZSBgaW1hZ2VfbWFuaWZlc3RgIHJvbGUga2VlcHMgd29ya2luZyBidXQgbm93IHdhcm5zIHRoYXQgaXQgaXMgZGVwcmVjYXRlZC4KCioqRG9jczoqKiBhIG5ldyBkZXBsb3ltZW50IGd1aWRlLCBgZG9jL3NvdXJjZS9kZXBsb3kvaW1hZ2VzLnJzdGAgKCJDb250YWluZXIgaW1hZ2VzIGFuZCBtaXJyb3JzIiksIGNvdmVycyByZWxlYXNlIG1hbmlmZXN0cywgdGhlIGltYWdlIHByZWZpeCwgbWlycm9yIHJ1bGVzLCBvdmVycmlkZXMsIG1pcnJvcmluZyAoaW5jbHVkaW5nIGRpc2Nvbm5lY3RlZCBzaXRlcykgYW5kIGBjaGVja2AvYGRpZmZgLiBUaGUgYGRlZmF1bHRzYCByb2xlIFJFQURNRSBsaXN0cyB0aGUgaW1hZ2UgdmFyaWFibGVzLCBhbmQgdGhlIHJlbGVhc2Ugbm90ZSBjb3ZlcnMgdGhlIG5ldyBmZWF0dXJlcywgdGhlIHVuY2hhbmdlZCB1cGdyYWRlIHBhdGgsIHRoZSBgaW1hZ2VfbWFuaWZlc3RgIGRlcHJlY2F0aW9uIGFuZCB0aGUgYHBpbmltYWdlc2AgcmVwbGFjZW1lbnQuCgojIyMgQ29tcGF0aWJpbGl0eQoKRGVwbG95bWVudHMgdGhhdCBzZXQgbm9uZSBvZiB0aGUgbmV3IHZhcmlhYmxlcyBnZXQgYnl0ZS1pZGVudGljYWwgYGF0bW9zcGhlcmVfaW1hZ2VzYDogSSByZW5kZXJlZCB0aGUgYGRlZmF1bHRzYCByb2xlIHRocm91Z2ggYGFuc2libGUtcGxheWJvb2tgIGJlZm9yZSBhbmQgYWZ0ZXIgdGhpcyBjaGFuZ2Ugd2l0aCBubyBwcmVmaXgsIHdpdGggYSBIYXJib3IgcHJlZml4LCBhbmQgd2l0aCBwcmVmaXggKyBvdmVycmlkZXMsIG9uIGBtYWluYCAoMjMxIGltYWdlcykgYW5kIG9uIGBzdGFibGUvemVkYCAoMjE2IGltYWdlcyk7IGFsbCBvdXRwdXRzIGFyZSBpZGVudGljYWwuIGB3cml0ZS12YXJzYCBwcm9kdWNlcyB0aGUgc2FtZSB2YXJzIGZpbGUgYXMgYHBpbmltYWdlc2AgZXhjZXB0IHRoYXQgQ2VwaCBpcyBub3cgcGlubmVkIHRvbyAoYHBpbmltYWdlc2AgY291bGQgbm90IHJlc29sdmUgaXRzIGB7eyBjZXBoX3ZlcnNpb24gfX1gIHRlbXBsYXRlKS4KCiMjIyBUZXN0aW5nCgotIEdvOiBgaW50ZXJuYWwvcmVsZWFzZW1hbmlmZXN0YCB0ZXN0cyBjb3ZlciBnZW5lcmF0aW9uLCBgd3JpdGUtdmFyc2AsIHB1c2gvZmV0Y2gvcHJvbW90ZSwgZXZlcnkgbWlycm9yIGxheW91dCB3aXRoIG11bHRpLWFyY2ggaW1hZ2VzIGluIGluLW1lbW9yeSByZWdpc3RyaWVzLCBkaXJlY3RvcnkgZXhwb3J0L2ltcG9ydCwgZGlnZXN0LW1pc21hdGNoIGRldGVjdGlvbiwgcmVnaXN0cmllcyB3aXRoIHBvcnRzIGFuZCByZXRyeSBvbiByYXRlIGxpbWl0cy4KLSBHbzogYHB1Ymxpc2hgIGFnYWluc3QgYSBmYWtlIFp1dWwgQVBJICh0ZXN0ZWQgbWFuaWZlc3QgdXNlZCB1bmNoYW5nZWQgd2l0aCB0aGUgc2FtZSBhcnRpZmFjdCBkaWdlc3QsIGdhdGUgc2VhcmNoZWQgYmVmb3JlIGNoZWNrLCBmYWlsZWQgYnVpbGRzZXRzIHNraXBwZWQsIGZhbGxiYWNrIHdoZW4gaW5wdXRzIGNoYW5nZWQsIG5vdGhpbmcgcGFzc2VkIG9yIFp1dWwgaXMgdW5yZWFjaGFibGUpLCBhbmQgcmVwcm9kdWNpYmxlIHB1c2hlcy4KLSBQeXRob246IHRoZSBgcmVzb2x2ZV9pbWFnZXNgIGZpbHRlciBhbmQgdGhlIEdvIHJlc29sdmVyIHJ1biB0aGUgc2FtZSBjYXNlcyBmcm9tIGBpbnRlcm5hbC9yZWxlYXNlbWFuaWZlc3QvdGVzdGRhdGEvcmVzb2x2ZS55YW1sYDsgdGhlIGxvb2t1cCBpcyB0ZXN0ZWQgYWdhaW5zdCBhIGxvY2FsIEhUVFAgcmVnaXN0cnkgd2l0aCB0b2tlbiBhdXRoIGFuZCBibG9iIHJlZGlyZWN0cyAoY3JlZGVudGlhbHMgbXVzdCBub3QgcmVhY2ggc3RvcmFnZSkgYW5kIHRhbXBlcmVkIGNvbnRlbnQsIGFuZCBhZ2FpbnN0IGFuIEhUVFBTIHJlZ2lzdHJ5IHdpdGggYSBwcml2YXRlIENBIGFuZCBiYXNpYyBhdXRoZW50aWNhdGlvbiAoZmFpbHMgd2l0aG91dCB0aGUgQ0Egb3IgY3JlZGVudGlhbHMpLCBpbmNsdWRpbmcgZmV0Y2hpbmcgdGhlIG1hbmlmZXN0IHRocm91Z2ggdGhlIHByZWZpeCBhbmQgbWlycm9yIHJ1bGVzLiBBIG5ldyBgcHl0aG9uLXRlc3RgIENJIGpvYiBydW5zIHRoZXNlICh0aGUgZXhpc3RpbmcgYHRlc3RzL3VuaXRgIHN1aXRlIGlzIG5vdCBydW4gaW4gQ0kgYW5kIGhhcyBhIHN0YWxlIG1hZ251bSBleHBlY3RhdGlvbiwgc28gdGhlIGpvYiBvbmx5IHJ1bnMgdGhlIG5ldyB0ZXN0cykuCi0gRW5kIHRvIGVuZCBhZ2FpbnN0IGEgbG9jYWwgQ05DRiBEaXN0cmlidXRpb24gcmVnaXN0cnkgd2l0aCByZWFsIGltYWdlcyBmcm9tIGByZWdpc3RyeS5rOHMuaW9gLCBgZ2hjci5pb2AgYW5kIERvY2tlciBIdWI6IGBnZW5lcmF0ZWAsIGBwdXNoYCwgYHByb21vdGVgLCBgc2hvd2AsIG5lc3RlZCBtaXJyb3IsIGV4cG9ydCDihpIgZmxhdCBpbXBvcnQsIGBjaGVja2AsIGBkaWZmYCwgcGx1cyBkZXBsb3lpbmcgdGhlIG1pcnJvcmVkIG1hbmlmZXN0IHRocm91Z2ggdGhlIGxvb2t1cC4KLSBgZ2VuZXJhdGVgIGFnYWluc3QgdGhlIHJlYWwgcmVnaXN0cmllcyByZXNvbHZlcyBhbGwgMjMxIGltYWdlcyBvbiBgbWFpbmAgYW5kIGBzdGFibGUvMjAyNS4yYC4KCiMjIyBCYWNrcG9ydHMKCmBnby5tb2RgIGFuZCBgZ28uc3VtYCBhcmUgdW50b3VjaGVkOyBldmVyeSBkZXBlbmRlbmN5IGlzIGFscmVhZHkgcHJlc2VudCBhdCB0aGUgc2FtZSB2ZXJzaW9uIG9uIGFsbCBzdGFibGUgYnJhbmNoZXMuCgotIGBzdGFibGUvMjAyNS4yYCBjaGVycnktcGlja3MgY2xlYW5seSBhbmQgcGFzc2VzIGV2ZXJ5dGhpbmcgYWJvdmUuCi0gYHN0YWJsZS8yMDI1LjFgIGJhY2sgdG8gYHN0YWJsZS96ZWRgIGNvbmZsaWN0IGluIG9uZSBodW5rIG9mIGByb2xlcy9kZWZhdWx0cy92YXJzL21haW4ueW1sYDogdGhlIGNvbmZsaWN0IGNvbnRleHQgaW5jbHVkZXMgYG1haW5gJ3MgYF9hdG1vc3BoZXJlX3N0b3JhZ2VgIGJsb2NrLCB3aGljaCB0aG9zZSBicmFuY2hlcyBkb24ndCBoYXZlLiBSZXNvbHZlIGl0IGJ5IGtlZXBpbmcgdGhlIG5ldyBgYXRtb3NwaGVyZV9pbWFnZXNgIGJsb2NrIGFuZCBkcm9wcGluZyB0aGUgYF9hdG1vc3BoZXJlX3N0b3JhZ2VgIGxpbmVzLiBJIGRpZCB0aGlzIG9uIGBzdGFibGUvemVkYCBhbmQgdGhlIEdvIHRlc3RzLCBQeXRob24gdGVzdHMgYW5kIHRoZSBvbGQvbmV3IHJlbmRlcmluZyBjb21wYXJpc29uIGFsbCBwYXNzLgoKIyMjIENJIGRlcGxveXMgdGhlIG1hbmlmZXN0CgpXaXRoIHZleHhob3N0L2F0bW9zcGhlcmUtenV1bC1qb2JzIzcsIGFuIGBhdG1vc3BoZXJlLXJlbGVhc2UtbWFuaWZlc3RgIGpvYiBnZW5lcmF0ZXMgdGhpcyBjaGFuZ2UncyByZWxlYXNlIG1hbmlmZXN0IG9uY2UgcGVyIGJ1aWxkc2V0LiBFdmVyeSBtb2xlY3VsZSBqb2IgKEFJTywgQ1NJLCBLZXljbG9haykgZGVwZW5kcyBvbiBpdCwgcHVzaGVzIHRoYXQgZXhhY3QgbWFuaWZlc3QgdG8gYSBUTFMgcmVnaXN0cnkgd2l0aCBwYXNzd29yZCBhdXRoZW50aWNhdGlvbiBvbiBpdHMgbm9kZSwgY2hlY2tzIHRoZSBwdXNoZWQgZGlnZXN0IG1hdGNoZXMsIGFuZCBkZXBsb3lzIGJ5IHJlZ2lzdHJ5IHJlZmVyZW5jZSB3aXRoIGBhdG1vc3BoZXJlX3JlbGVhc2VfbWFuaWZlc3RgLCBgYXRtb3NwaGVyZV9yZWxlYXNlX21hbmlmZXN0X2NhX2ZpbGVgIGFuZCBhIG1pcnJvciBydWxlLCB0aGUgc2FtZSBwYXRoIGN1c3RvbWVycyB1c2UgZm9yIHB1Ymxpc2hlZCByZWxlYXNlcy4gVGhhdCBidWlsZHNldCdzIG1hbmlmZXN0IGlzIHdoYXQgYHB1Ymxpc2hgIHB1Ymxpc2hlcyBhZnRlciBtZXJnZS4KCiMjIyBCZWZvcmUgbWVyZ2luZyAvIGZvbGxvdy11cHMKCi0gQWZ0ZXIgdGhlIGZpcnN0IHB1c2ggcHVibGlzaGVzIGBnaGNyLmlvL3ZleHhob3N0L2F0bW9zcGhlcmUtcmVsZWFzZWAsIG1ha2UgdGhlIHBhY2thZ2UgcHVibGljIGFuZCBzZXQgcmV0ZW50aW9uIChrZWVwIHJlbGVhc2UgdGFnczsgY29tbWl0IHRhZ3MgY2FuIGJlIHBydW5lZCkuCi0gRG9ja2VyIEh1YidzIGFub255bW91cyByYXRlIGxpbWl0IGNhbiBzdGlsbCBmYWlsIGBnZW5lcmF0ZWAgYWZ0ZXIgaXRzIHJldHJpZXM7IHByZXZpb3VzbHkgdGhpcyBzaWxlbnRseSBwcm9kdWNlZCB1bnBpbm5lZCBpbWFnZXMuCi0gYGhhcmJvci5hdG1vc3BoZXJlLmRldmAgaGFzIG5vIHByb3h5IHByb2plY3QgZm9yIGBwYWNrYWdlcy52ZXh4aG9zdC5jb21gICh1c2VkIGJ5IGBwcm9tZXRoZXVzX3NtYXJ0Y3RsX2V4cG9ydGVyYCkuCi0gVGhlIGRvY3MgYnVpbGQgd2l0aCBgLVdgIGFscmVhZHkgZmFpbHMgb24gYG1haW5gIGJlY2F1c2Ugb2YgZXhpc3Rpbmcgd2FybmluZ3MgaW4gYGFkbWluL21vbml0b3JpbmcucnN0YCBhbmQgYGFyY2gvaW1hZ2VzLnJzdGA7IHRoZSBuZXcgcGFnZSBhZGRzIG5vbmUuCgpEZXBlbmRzLU9uOiBodHRwczovL2dpdGh1Yi5jb20vdmV4eGhvc3QvYXRtb3NwaGVyZS16dXVsLWpvYnMvcHVsbC83Cg==
      patchset: 32ae9479091acf66cc00cd680ed6e0307341626d
      pipeline: check
      playbook_context:
        playbook_projects:
          trusted/project_0/github.com/vexxhost/zuul-config:
            canonical_name: github.com/vexxhost/zuul-config
            checkout: main
            commit: 298983cd1253e6833abdb49d87d912527e0e6597
          trusted/project_1/opendev.org/zuul/zuul-jobs:
            canonical_name: opendev.org/zuul/zuul-jobs
            checkout: master
            commit: be52461cd48abccbf95b13a58b2551ac673d8ad7
          trusted/project_2/github.com/vexxhost/zuul-jobs:
            canonical_name: github.com/vexxhost/zuul-jobs
            checkout: main
            commit: c286bf94224b5660b8e49e9a058289e61062a9a0
          untrusted/project_0/github.com/vexxhost/zuul-jobs:
            canonical_name: github.com/vexxhost/zuul-jobs
            checkout: main
            commit: 9848de64840bf296f0bd8039c6a425174ceaee91
          untrusted/project_1/github.com/vexxhost/zuul-config:
            canonical_name: github.com/vexxhost/zuul-config
            checkout: main
            commit: 298983cd1253e6833abdb49d87d912527e0e6597
          untrusted/project_2/opendev.org/zuul/zuul-jobs:
            canonical_name: opendev.org/zuul/zuul-jobs
            checkout: master
            commit: be52461cd48abccbf95b13a58b2551ac673d8ad7
          untrusted/project_3/github.com/vexxhost/atmosphere-zuul-jobs:
            canonical_name: github.com/vexxhost/atmosphere-zuul-jobs
            checkout: main
            commit: fe46543b39a569a2360919be71612a4f28966bd6
          untrusted/project_4/opendev.org/openstack/openstack-helm:
            canonical_name: opendev.org/openstack/openstack-helm
            checkout: master
            commit: 1b2f27965ad71deed6d89c9aab8f421c2f4d6fc4
        playbooks:
        - path: untrusted/project_0/github.com/vexxhost/zuul-jobs/playbooks/molecule/run.yaml
          roles:
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/playbook_0/role_1/zuul-jobs
            link_target: untrusted/project_2/opendev.org/zuul/zuul-jobs
            role_path: ansible/playbook_0/role_1/zuul-jobs/roles
          - checkout: main
            checkout_description: playbook branch
            link_name: ansible/playbook_0/role_2/zuul-jobs
            link_target: untrusted/project_0/github.com/vexxhost/zuul-jobs
            role_path: ansible/playbook_0/role_2/zuul-jobs/roles
        post_playbooks:
        - path: untrusted/project_3/github.com/vexxhost/atmosphere-zuul-jobs/playbooks/molecule/post.yml
          roles:
          - checkout: main
            checkout_description: playbook branch
            link_name: ansible/post_playbook_0/role_0/atmosphere-zuul-jobs
            link_target: untrusted/project_3/github.com/vexxhost/atmosphere-zuul-jobs
            role_path: ansible/post_playbook_0/role_0/atmosphere-zuul-jobs/roles
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/post_playbook_0/role_1/openstack-helm
            link_target: untrusted/project_4/opendev.org/openstack/openstack-helm
            role_path: ansible/post_playbook_0/role_1/openstack-helm/roles
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/post_playbook_0/role_3/zuul-jobs
            link_target: untrusted/project_2/opendev.org/zuul/zuul-jobs
            role_path: ansible/post_playbook_0/role_3/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/post_playbook_0/role_4/zuul-jobs
            link_target: untrusted/project_0/github.com/vexxhost/zuul-jobs
            role_path: ansible/post_playbook_0/role_4/zuul-jobs/roles
        - path: trusted/project_0/github.com/vexxhost/zuul-config/playbooks/base/post.yaml
          roles:
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/post_playbook_1/role_1/zuul-jobs
            link_target: trusted/project_1/opendev.org/zuul/zuul-jobs
            role_path: ansible/post_playbook_1/role_1/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/post_playbook_1/role_2/zuul-jobs
            link_target: trusted/project_2/github.com/vexxhost/zuul-jobs
            role_path: ansible/post_playbook_1/role_2/zuul-jobs/roles
        - path: trusted/project_0/github.com/vexxhost/zuul-config/playbooks/base/post-logs.yaml
          roles:
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/post_playbook_2/role_1/zuul-jobs
            link_target: trusted/project_1/opendev.org/zuul/zuul-jobs
            role_path: ansible/post_playbook_2/role_1/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/post_playbook_2/role_2/zuul-jobs
            link_target: trusted/project_2/github.com/vexxhost/zuul-jobs
            role_path: ansible/post_playbook_2/role_2/zuul-jobs/roles
        pre_playbooks:
        - path: trusted/project_0/github.com/vexxhost/zuul-config/playbooks/base/pre.yaml
          roles:
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/pre_playbook_0/role_1/zuul-jobs
            link_target: trusted/project_1/opendev.org/zuul/zuul-jobs
            role_path: ansible/pre_playbook_0/role_1/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/pre_playbook_0/role_2/zuul-jobs
            link_target: trusted/project_2/github.com/vexxhost/zuul-jobs
            role_path: ansible/pre_playbook_0/role_2/zuul-jobs/roles
        - path: untrusted/project_0/github.com/vexxhost/zuul-jobs/playbooks/molecule/pre.yaml
          roles:
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/pre_playbook_1/role_1/zuul-jobs
            link_target: untrusted/project_2/opendev.org/zuul/zuul-jobs
            role_path: ansible/pre_playbook_1/role_1/zuul-jobs/roles
          - checkout: main
            checkout_description: playbook branch
            link_name: ansible/pre_playbook_1/role_2/zuul-jobs
            link_target: untrusted/project_0/github.com/vexxhost/zuul-jobs
            role_path: ansible/pre_playbook_1/role_2/zuul-jobs/roles
        - path: untrusted/project_3/github.com/vexxhost/atmosphere-zuul-jobs/playbooks/molecule/pre.yml
          roles:
          - checkout: main
            checkout_description: playbook branch
            link_name: ansible/pre_playbook_2/role_0/atmosphere-zuul-jobs
            link_target: untrusted/project_3/github.com/vexxhost/atmosphere-zuul-jobs
            role_path: ansible/pre_playbook_2/role_0/atmosphere-zuul-jobs/roles
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/pre_playbook_2/role_1/openstack-helm
            link_target: untrusted/project_4/opendev.org/openstack/openstack-helm
            role_path: ansible/pre_playbook_2/role_1/openstack-helm/roles
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/pre_playbook_2/role_3/zuul-jobs
            link_target: untrusted/project_2/opendev.org/zuul/zuul-jobs
            role_path: ansible/pre_playbook_2/role_3/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/pre_playbook_2/role_4/zuul-jobs
            link_target: untrusted/project_0/github.com/vexxhost/zuul-jobs
            role_path: ansible/pre_playbook_2/role_4/zuul-jobs/roles
      post_review: false
      post_timeout: null
      pre_timeout: null
      project:
        canonical_hostname: github.com
        canonical_name: github.com/vexxhost/atmosphere
        name: vexxhost/atmosphere
        short_name: atmosphere
        src_dir: src/github.com/vexxhost/atmosphere
      projects:
        github.com/ansible-collections/ansible.netcommon:
          canonical_hostname: github.com
          canonical_name: github.com/ansible-collections/ansible.netcommon
          checkout: main
          checkout_description: zuul branch
          commit: a929db70577e5630c32b36690fe58362f8c17443
          name: ansible-collections/ansible.netcommon
          required: true
          short_name: ansible.netcommon
          src_dir: src/github.com/ansible-collections/ansible.netcommon
        github.com/ansible-collections/ansible.posix:
          canonical_hostname: github.com
          canonical_name: github.com/ansible-collections/ansible.posix
          checkout: main
          checkout_description: zuul branch
          commit: d5699a3fcfca765b32f94b68a592139dc58d0301
          name: ansible-collections/ansible.posix
          required: true
          short_name: ansible.posix
          src_dir: src/github.com/ansible-collections/ansible.posix
        github.com/ansible-collections/ansible.utils:
          canonical_hostname: github.com
          canonical_name: github.com/ansible-collections/ansible.utils
          checkout: main
          checkout_description: zuul branch
          commit: 1f3e1bdfe2ba8904d397222dd6f90911d6f2402b
          name: ansible-collections/ansible.utils
          required: true
          short_name: ansible.utils
          src_dir: src/github.com/ansible-collections/ansible.utils
        github.com/ansible-collections/community.crypto:
          canonical_hostname: github.com
          canonical_name: github.com/ansible-collections/community.crypto
          checkout: main
          checkout_description: zuul branch
          commit: 051f8716048743eb7b9f9a875d851b97731bbf90
          name: ansible-collections/community.crypto
          required: true
          short_name: community.crypto
          src_dir: src/github.com/ansible-collections/community.crypto
        github.com/ansible-collections/community.general:
          canonical_hostname: github.com
          canonical_name: github.com/ansible-collections/community.general
          checkout: main
          checkout_description: zuul branch
          commit: e7f0aa1b4a0c73c9d2a8c222d2d824049965aa51
          name: ansible-collections/community.general
          required: true
          short_name: community.general
          src_dir: src/github.com/ansible-collections/community.general
        github.com/ansible-collections/community.mysql:
          canonical_hostname: github.com
          canonical_name: github.com/ansible-collections/community.mysql
          checkout: main
          checkout_description: zuul branch
          commit: 4cf7a3176fbd07be43459f1366e8066381df6d97
          name: ansible-collections/community.mysql
          required: true
          short_name: community.mysql
          src_dir: src/github.com/ansible-collections/community.mysql
        github.com/ansible-collections/kubernetes.core:
          canonical_hostname: github.com
          canonical_name: github.com/ansible-collections/kubernetes.core
          checkout: main
          checkout_description: zuul branch
          commit: ebb0d7f087413013eb27e275c7d638e6838e95c0
          name: ansible-collections/kubernetes.core
          required: true
          short_name: kubernetes.core
          src_dir: src/github.com/ansible-collections/kubernetes.core
        github.com/vexxhost/ansible-collection-ceph:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/ansible-collection-ceph
          checkout: main
          checkout_description: zuul branch
          commit: 44a694a80e3179c28d75180baa1a56426f987257
          name: vexxhost/ansible-collection-ceph
          required: true
          short_name: ansible-collection-ceph
          src_dir: src/github.com/vexxhost/ansible-collection-ceph
        github.com/vexxhost/ansible-collection-containers:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/ansible-collection-containers
          checkout: main
          checkout_description: zuul branch
          commit: d22923ebd40c0fd5a345318c1db90d125fb5140f
          name: vexxhost/ansible-collection-containers
          required: true
          short_name: ansible-collection-containers
          src_dir: src/github.com/vexxhost/ansible-collection-containers
        github.com/vexxhost/ansible-collection-kubernetes:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/ansible-collection-kubernetes
          checkout: main
          checkout_description: zuul branch
          commit: bb3fd0fde58a5b158605523e708645eb2a763886
          name: vexxhost/ansible-collection-kubernetes
          required: true
          short_name: ansible-collection-kubernetes
          src_dir: src/github.com/vexxhost/ansible-collection-kubernetes
        github.com/vexxhost/atmosphere:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/atmosphere
          checkout: main
          checkout_description: zuul branch
          commit: 32ae9479091acf66cc00cd680ed6e0307341626d
          name: vexxhost/atmosphere
          required: false
          short_name: atmosphere
          src_dir: src/github.com/vexxhost/atmosphere
        github.com/vexxhost/atmosphere-zuul-jobs:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/atmosphere-zuul-jobs
          checkout: main
          checkout_description: zuul branch
          commit: fe46543b39a569a2360919be71612a4f28966bd6
          name: vexxhost/atmosphere-zuul-jobs
          required: false
          short_name: atmosphere-zuul-jobs
          src_dir: src/github.com/vexxhost/atmosphere-zuul-jobs
        github.com/vexxhost/atmosphere.common:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/atmosphere.common
          checkout: main
          checkout_description: zuul branch
          commit: 0317937da7abc1979d331af8b8f7b640b80d7e75
          name: vexxhost/atmosphere.common
          required: true
          short_name: atmosphere.common
          src_dir: src/github.com/vexxhost/atmosphere.common
        github.com/vexxhost/zuul-jobs:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/zuul-jobs
          checkout: main
          checkout_description: zuul branch
          commit: 9848de64840bf296f0bd8039c6a425174ceaee91
          name: vexxhost/zuul-jobs
          required: false
          short_name: zuul-jobs
          src_dir: src/github.com/vexxhost/zuul-jobs
        opendev.org/openstack/ansible-collections-openstack:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/ansible-collections-openstack
          checkout: master
          checkout_description: project default branch
          commit: da4a211b2d1dd147dead0e3db63a373b43b3c09e
          name: openstack/ansible-collections-openstack
          required: true
          short_name: ansible-collections-openstack
          src_dir: src/opendev.org/openstack/ansible-collections-openstack
      ref: refs/pull/4420/head
      resources: {}
      tenant: oss
      timeout: 1800
      topic: null
      voting: true
