From fd7961a6b74b1eff00045bf358cc415b634707cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Corn=C3=A9=20Lukken?= <lukken@astron.nl>
Date: Fri, 21 Apr 2023 08:39:52 +0000
Subject: [PATCH] Use docker base image across CI steps

---
 .gitlab-ci.yml                                |  8 +++-
 docker/ci-runner/Dockerfile                   |  4 ++
 {{cookiecutter.project_slug}}/.gitlab-ci.yml  | 42 +++++++++++++------
 .../.prepare.gitlab-ci.yml                    | 23 ++++++++++
 .../docker/ci-runner/Dockerfile               |  4 ++
 {{cookiecutter.project_slug}}/setup.cfg       |  1 +
 6 files changed, 67 insertions(+), 15 deletions(-)
 create mode 100644 docker/ci-runner/Dockerfile
 create mode 100644 {{cookiecutter.project_slug}}/.prepare.gitlab-ci.yml
 create mode 100644 {{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 875087f..8835594 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,9 +1,13 @@
 include:
 - local: "{{cookiecutter.project_slug}}/.gitlab-ci.yml"
 
+trigger_prepare:
+  stage: prepare
+  trigger:
+    strategy: depend
+    include: "{{cookiecutter.project_slug}}/.prepare.gitlab-ci.yml"
+
 default:
   before_script:
     - python --version # For debugging
-    - python -m pip install --upgrade pip
-    - pip install --upgrade cookiecutter tox twine
     - cookiecutter --no-input --overwrite-if-exists -output-dir .
diff --git a/docker/ci-runner/Dockerfile b/docker/ci-runner/Dockerfile
new file mode 100644
index 0000000..937fe86
--- /dev/null
+++ b/docker/ci-runner/Dockerfile
@@ -0,0 +1,4 @@
+FROM python:3.11
+
+RUN python -m pip install --upgrade pip
+RUN pip install --upgrade cookiecutter tox twine
diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml
index b17457d..af7749c 100644
--- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml
+++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml
@@ -1,15 +1,14 @@
 default:
-  image: python:3.10  # use latest for building/linting
+  image: $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG
   before_script:
     - python --version # For debugging
-    - python -m pip install --upgrade pip
-    - pip install --upgrade tox twine
   cache:
     paths:
       - .cache/pip
       # Do not cache .tox, to recreate virtualenvs for every step
 
 stages:
+  - prepare
   - lint
   # check if this needs to be a separate step
   # - build_extensions
@@ -22,6 +21,14 @@ stages:
 variables:
   PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
 
+
+# Prepare image to run ci on
+trigger_prepare:
+  stage: prepare
+  trigger:
+    strategy: depend
+    include: .prepare.gitlab-ci.yml
+
 run_black:
   stage: lint
   script:
@@ -45,9 +52,27 @@ run_pylint:
 #   script:
 #     - echo "build fortran/c/cpp extension source code"
 
+# Basic setup for all Python versions for which we don't have a base image
+.run_unit_test_version_base:
+  before_script:
+    - python --version # For debugging
+    - python -m pip install --upgrade pip
+    - pip install --upgrade tox twine
+
+# Run all unit tests for Python versions except the base image
+run_unit_tests:
+  extends: .run_unit_test_version_base
+  stage: test
+  image: python:3.${PY_VERSION}
+  script:
+    - tox -e py3${PY_VERSION}
+  parallel:
+    matrix: # use the matrix for testing
+      - PY_VERSION: [7, 8, 9, 10]
+
+# Run code coverage on the base image thus also performing unit tests
 run_unit_tests_coverage:
   stage: test
-  image: python:3.7
   script:
    - tox -e coverage
   artifacts:
@@ -58,15 +83,6 @@ run_unit_tests_coverage:
     paths:
       - htmlcov/*
 
-run_unit_tests:
-  stage: test
-  image: python:3.${PY_VERSION}
-  script:
-    - tox -e py3${PY_VERSION}
-  parallel:
-    matrix: # use the matrix for testing
-      - PY_VERSION: [7, 8, 9, 10]
-
 package_files:
   stage: package
   artifacts:
diff --git a/{{cookiecutter.project_slug}}/.prepare.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.prepare.gitlab-ci.yml
new file mode 100644
index 0000000..e531b6d
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/.prepare.gitlab-ci.yml
@@ -0,0 +1,23 @@
+stages:
+  - build
+
+build_ci_runner_image:
+  stage: build
+  image: docker:stable
+  services:
+    - docker:dind
+  script:
+    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+    - |
+      if docker pull $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG; then
+        docker build --cache-from $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG --tag $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG docker/ci-runner
+      else
+        docker pull $CI_REGISTRY_IMAGE/ci-build-runner:latest || true
+        docker build --cache-from $CI_REGISTRY_IMAGE/ci-build-runner:latest --tag $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG docker/ci-runner
+      fi
+    - docker push $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG  # push the image
+    - |
+      if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
+        docker image tag $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG $CI_REGISTRY_IMAGE/ci-build-runner:latest
+        docker push $CI_REGISTRY_IMAGE/ci-build-runner:latest
+      fi
diff --git a/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile b/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile
new file mode 100644
index 0000000..01d5ee1
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile
@@ -0,0 +1,4 @@
+FROM python:3.11
+
+RUN python -m pip install --upgrade pip
+RUN pip install --upgrade tox twine
diff --git a/{{cookiecutter.project_slug}}/setup.cfg b/{{cookiecutter.project_slug}}/setup.cfg
index fcc9167..1741808 100644
--- a/{{cookiecutter.project_slug}}/setup.cfg
+++ b/{{cookiecutter.project_slug}}/setup.cfg
@@ -19,6 +19,7 @@ classifiers =
     Programming Language :: Python :: 3.8
     Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3.10
+    Programming Language :: Python :: 3.11
     Topic :: Internet :: WWW/HTTP
     Topic :: Internet :: WWW/HTTP :: Dynamic Content
     Topic :: Scientific/Engineering
-- 
GitLab