Add depot base jobs
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..ef07c5f
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,5 @@
+[gerrit]
+host=review.vexxhost.dev
+port=29418
+project=zuul-jobs.git
+defaultbranch=main
diff --git a/.zuul.yaml b/.zuul.yaml
new file mode 100644
index 0000000..e30e3ff
--- /dev/null
+++ b/.zuul.yaml
@@ -0,0 +1,7 @@
+- project:
+    check:
+      jobs:
+        - noop
+    gate:
+      jobs:
+        - noop
diff --git a/playbooks/depot-bake/pre.yml b/playbooks/depot-bake/pre.yml
new file mode 100644
index 0000000..b9148e5
--- /dev/null
+++ b/playbooks/depot-bake/pre.yml
@@ -0,0 +1,3 @@
+- hosts: all
+  roles:
+    - ensure-depot
diff --git a/playbooks/depot-bake/run.yml b/playbooks/depot-bake/run.yml
new file mode 100644
index 0000000..96c99cc
--- /dev/null
+++ b/playbooks/depot-bake/run.yml
@@ -0,0 +1,32 @@
+- hosts: all
+  tasks:
+    - name: Ensure folder for Docker configuration
+      ansible.builtin.file:
+        path: "{{ ansible_env.HOME }}/.docker"
+        state: directory
+
+    - name: Build images using Bake
+      block:
+        - name: Login to Docker registry
+          no_log: true
+          ansible.builtin.copy:
+            content: "{{ depot_bake_docker_config }}"
+            dest: "{{ ansible_env.HOME }}/.docker/config.json"
+            mode: 0600
+          vars:
+            depot_bake_docker_config: |
+              {
+                "auths": {
+                  "{{ depot_bake_registry.host }}{% if depot_bake_registry.port != 443 %}:{{ depot_bake_registry.port }}{% endif %}": {
+                    "auth": "{{ (depot_bake_registry.username + ':' + depot_bake_registry.password) | b64encode }}"
+                  }
+                }
+              }
+
+        - name: Run "depot bake"
+          ansible.builtin.include_role:
+            name: depot-bake
+
+      always:
+        - name: Delete Docker configuration
+          ansible.builtin.command: "shred {{ ansible_env.HOME }}/.docker/config.json"
diff --git a/playbooks/promote-depot-bake/run.yaml b/playbooks/promote-depot-bake/run.yaml
new file mode 100644
index 0000000..c426d08
--- /dev/null
+++ b/playbooks/promote-depot-bake/run.yaml
@@ -0,0 +1,3 @@
+- hosts: localhost
+  roles:
+    - promote-depot-bake
diff --git a/roles/depot-bake/defaults/main.yaml b/roles/depot-bake/defaults/main.yaml
new file mode 100644
index 0000000..4ab600e
--- /dev/null
+++ b/roles/depot-bake/defaults/main.yaml
@@ -0,0 +1 @@
+depot_bake_environment: {}
diff --git a/roles/depot-bake/tasks/main.yaml b/roles/depot-bake/tasks/main.yaml
new file mode 100644
index 0000000..3c1d043
--- /dev/null
+++ b/roles/depot-bake/tasks/main.yaml
@@ -0,0 +1,59 @@
+- name: Build images
+  block:
+    - name: Create metadata file
+      ansible.builtin.tempfile:
+        state: file
+        prefix: metadata
+        suffix: .json
+      register: depot_bake_metadata_file
+
+    - name: Run "depot bake"
+      ansible.builtin.command: >-
+        {{ ensure_depot_path | default('/usr/local/bin') }}/depot bake --push --metadata-file={{ depot_bake_metadata_file.path }}
+      args:
+        chdir: "{{ zuul.project.src_dir }}"
+      environment: "{{ _depot_bake_environment | combine(depot_bake_environment) }}"
+
+    - name: Slurp the metadata file
+      ansible.builtin.slurp:
+        src: "{{ depot_bake_metadata_file.path }}"
+      register: depot_bake_metadata
+
+    - name: Unmarshal the metadata file
+      ansible.builtin.set_fact:
+        depot_bake_metadata: "{{ depot_bake_metadata.content | b64decode | from_json }}"
+
+    - name: Build empty list of artifacts
+      ansible.builtin.set_fact:
+        depot_bake_artifacts: []
+
+    - name: Append each artifact to the list
+      ansible.builtin.set_fact:
+        depot_bake_artifacts: "{{ depot_bake_artifacts + [depot_bake_artifact] }}"
+      loop: "{{ depot_bake_metadata | dict2items }}"
+      loop_control:
+        label: "{{ item.key }}"
+      when:
+        - item.key != "depot.build"
+        - item.value != {}
+        - item.value["image.name"] is defined
+      vars:
+        depot_bake_artifact:
+          name: "{{ item.value['image.name'] }}"
+          url: "docker://{{ item.value['image.name'] }}"
+          metadata:
+            type: container_image
+            repository: "{{ item.value['image.name'] | split(':') | first }}"
+            tag: "{{ item.value['image.name'] | split(':') | last }}"
+
+    - name: Return artifacts for each image
+      zuul_return:
+        data:
+          zuul:
+            artifacts: "{{ depot_bake_artifacts }}"
+
+  always:
+    - name: Cleanup metadata file
+      ansible.builtin.file:
+        path: "{{ depot_bake_metadata_file.path }}"
+        state: absent
diff --git a/roles/depot-bake/vars/main.yaml b/roles/depot-bake/vars/main.yaml
new file mode 100644
index 0000000..4243f0a
--- /dev/null
+++ b/roles/depot-bake/vars/main.yaml
@@ -0,0 +1,3 @@
+_depot_bake_environment:
+  DEPOT_PROJECT_ID: "{{ depot_bake_project_id }}"
+  DEPOT_TOKEN: "{{ depot_bake_credentials.token }}"
diff --git a/roles/ensure-depot/defaults/main.yaml b/roles/ensure-depot/defaults/main.yaml
new file mode 100644
index 0000000..94a2c52
--- /dev/null
+++ b/roles/ensure-depot/defaults/main.yaml
@@ -0,0 +1 @@
+ensure_depot_path: /usr/local/bin
diff --git a/roles/ensure-depot/tasks/main.yaml b/roles/ensure-depot/tasks/main.yaml
new file mode 100644
index 0000000..5be63e7
--- /dev/null
+++ b/roles/ensure-depot/tasks/main.yaml
@@ -0,0 +1,27 @@
+- name: Download and extract Depot
+  block:
+    - name: Create a temporary file
+      ansible.builtin.tempfile:
+        prefix: depot
+        suffix: .tar.gz
+        state: file
+      register: depot_tar
+
+    - name: Download Depot release
+      ansible.builtin.get_url:
+        url: "https://dl.depot.dev/cli/download/{{ ansible_system | lower }}/{{ ansible_architecture | lower }}/latest"
+        dest: "{{ depot_tar.path }}"
+        force: true
+
+    - name: Extract into {{ ensure_depot_path }}
+      become: true
+      ansible.builtin.unarchive:
+        src: "{{ depot_tar.path }}"
+        dest: "{{ ensure_depot_path }}"
+        remote_src: yes
+        extra_opts: --strip-components=1
+  always:
+    - name: Remove the temporary file
+      ansible.builtin.file:
+        path: "{{ depot_tar.path }}"
+        state: absent
diff --git a/roles/promote-depot-bake/defaults/main.yaml b/roles/promote-depot-bake/defaults/main.yaml
new file mode 100644
index 0000000..5b58d43
--- /dev/null
+++ b/roles/promote-depot-bake/defaults/main.yaml
@@ -0,0 +1,2 @@
+promote_depot_bake_pipeline: gate
+promote_depot_bake_query: "change={{ zuul.change }}&patchset={{ zuul.patchset }}&pipeline={{ promote_depot_bake_pipeline }}&job_name={{ promote_depot_bake_job }}"
diff --git a/roles/promote-depot-bake/tasks/main.yaml b/roles/promote-depot-bake/tasks/main.yaml
new file mode 100644
index 0000000..d9f86d9
--- /dev/null
+++ b/roles/promote-depot-bake/tasks/main.yaml
@@ -0,0 +1,30 @@
+- name: Query Zuul API for image information
+  uri:
+    url: "{{ promote_depot_bake_api }}/builds?{{ promote_depot_bake_query }}"
+  register: promote_depot_bake_build
+
+- name: Parse build response
+  set_fact:
+    promote_depot_bake_build: "{{ promote_depot_bake_build.json[0] }}"
+
+- name: Promote Images
+  block:
+    - name: Log in to registry
+      no_log: true
+      ansible.builtin.command: >-
+        skopeo login {{ promote_depot_bake_registry.host }}{% if promote_depot_bake_registry.port != 443 %}:{{ promote_depot_bake_registry.port }}{% endif %} -u {{ promote_depot_bake_registry.username }} -p {{ promote_depot_bake_registry.password }}
+      register: result
+      until: result.rc == 0
+      retries: 3
+      delay: 30
+
+    - name: Copy image
+      ansible.builtin.command: >-
+        skopeo --insecure-policy copy --all {{ zj_zuul_artifact.url }} docker://{{ zj_zuul_artifact.metadata.repository | regex_replace('/ci/', '/library/', 1) }}:{{ zuul.branch | replace('stable/', '') }}
+      loop: '{{ promote_depot_bake_build | json_query("artifacts[?metadata.type==''container_image'']") }}'
+      loop_control:
+        loop_var: zj_zuul_artifact
+
+  always:
+    - name: Log out of registry
+      command: skopeo logout {{ promote_depot_bake_registry.host }}{% if promote_depot_bake_registry.port != 443 %}:{{ promote_depot_bake_registry.port }}{% endif %}
diff --git a/zuul.d/depot-jobs.yaml b/zuul.d/depot-jobs.yaml
new file mode 100644
index 0000000..a623664
--- /dev/null
+++ b/zuul.d/depot-jobs.yaml
@@ -0,0 +1,14 @@
+- job:
+    name: depot-bake-base
+    abstract: true
+    pre-run:
+      - playbooks/depot-bake/pre.yml
+    run:
+      - playbooks/depot-bake/run.yml
+
+- job:
+    name: promote-depot-bake-base
+    abstract: true
+    run: playbooks/promote-depot-bake/run.yaml
+    nodeset:
+      nodes: []