chore: add docker image (#398)

diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml
new file mode 100644
index 0000000..a4a527a
--- /dev/null
+++ b/.github/workflows/image.yml
@@ -0,0 +1,46 @@
+name: image
+
+on:
+  pull_request:
+  push:
+    branches:
+      - main
+  release:
+    types:
+      - published
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout project
+        uses: actions/checkout@v3
+
+      - name: Install Earthly
+        uses: earthly/actions-setup@v1
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Build image
+        run: earthly +image --tag ${{ github.sha }}
+        env:
+          EARTHLY_PUSH: "${{ github.event_name == 'push' }}"
+
+  publish:
+    runs-on: ubuntu-latest
+    if: github.event_name == 'release'
+    needs:
+      - build
+    steps:
+      - name: Authenticate to Quay.io
+        uses: docker/login-action@v2
+        with:
+          registry: quay.io
+          username: ${{ secrets.QUAY_USERNAME }}
+          password: ${{ secrets.QUAY_ROBOT_TOKEN }}
+
+      - name: Promote image
+        uses: akhilerm/tag-push-action@v2.1.0
+        with:
+          src: quay.io/vexxhost/atmosphere:${{ github.sha }}
+          dst: quay.io/vexxhost/atmosphere:${{ github.event.release.tag_name }}
diff --git a/Earthfile b/Earthfile
index 751d9f2..230720e 100644
--- a/Earthfile
+++ b/Earthfile
@@ -1,30 +1,59 @@
-VERSION 0.7
+VERSION --use-copy-link 0.7
 FROM python:3.10
 
 poetry:
   RUN pip3 install poetry
 
-deps:
+build.wheels:
   FROM +poetry
   COPY pyproject.toml poetry.lock ./
-  RUN poetry export -f requirements.txt --without-hashes > requirements.txt
+  ARG --required only
+  RUN poetry export --only=${only} -f requirements.txt --without-hashes > requirements.txt
   RUN pip wheel -r requirements.txt --wheel-dir=/wheels
   SAVE ARTIFACT requirements.txt
   SAVE ARTIFACT /wheels
+  SAVE IMAGE --cache-hint
 
-build:
-  FROM +deps
+build.venv:
+  ARG --required only
+  FROM +build.wheels --only ${only}
   RUN python3 -m venv /venv
   ENV PATH=/venv/bin:$PATH
   RUN pip install -r requirements.txt
+  SAVE IMAGE --cache-hint
+
+build.venv.dev:
+  FROM +build.venv --only main,dev
   SAVE ARTIFACT /venv
 
-docker:
-  COPY +build/venv /venv
+build.venv.runtime:
+  FROM +build.venv --only main
+  SAVE ARTIFACT /venv
+
+build.collections:
+  FROM +build.venv.runtime
+  COPY charts /src/charts
+  COPY meta /src/meta
+  COPY playbooks /src/playbooks
+  COPY plugins /src/plugins
+  COPY roles /src/roles
+  COPY galaxy.yml /src/galaxy.yml
+  RUN ansible-galaxy collection install --collections-path /usr/share/ansible/collections /src
+  SAVE ARTIFACT /usr/share/ansible/collections
+  SAVE IMAGE --cache-hint
+
+image:
+  FROM python:3.10-slim
+  COPY +build.venv.runtime/venv /venv
+  COPY +build.collections/ /usr/share/ansible
+  ENV ANSIBLE_PIPELINING=True
   ENV PATH=/venv/bin:$PATH
+  ENTRYPOINT ["bash"]
+  ARG tag=latest
+  SAVE IMAGE --push quay.io/vexxhost/atmosphere:${tag}
 
 pin-images:
-  FROM +docker
+  FROM +build.venv.dev
   COPY roles/defaults/defaults/main.yml /defaults.yml
   COPY build/pin-images.py /usr/local/bin/pin-images
   RUN /usr/local/bin/pin-images /defaults.yml /pinned.yml
diff --git a/pyproject.toml b/pyproject.toml
index 3b265f7..369bb5f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,18 +16,18 @@
 docker-image-py = "^0.1.12"
 rjsonnet = "^0.5.2"
 netaddr = "^0.8.0"
-ruyaml = "^0.91.0"
-oslo-log = "^5.2.0"
 
 [tool.poetry.group.dev.dependencies]
 flake8 = "^5.0.4"
 flake8-isort = "^4.2.0"
 molecule = {version = "^4.0.4", extras = ["docker"]}
+oslo-log = "^5.2.0"
 pytest = "^7.1.3"
 pytest-cov = "^3.0.0"
-pytest-mock = "^3.8.2"
 pytest-forked = "^1.4.0"
+pytest-mock = "^3.8.2"
 pytest-xdist = "^3.1.0"
+ruyaml = "^0.91.0"
 
 [tool.poetry.group.docs.dependencies]
 mkdocs-material = "^8.5.7"