From d9ea21d50e0acd5da4b33056c8ccdc06dc5255d5 Mon Sep 17 00:00:00 2001
From: Sebastiaan Huber <mail@sphuber.net>
Date: Sun, 3 Sep 2023 22:11:49 +0200
Subject: [PATCH] Devops: Add continuous-deployment workflow

---
 .github/workflows/cd.yml                  | 103 ++++++++++++++++++++++
 .github/workflows/validate_release_tag.py |  36 ++++++++
 2 files changed, 139 insertions(+)
 create mode 100644 .github/workflows/cd.yml
 create mode 100644 .github/workflows/validate_release_tag.py

diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 0000000..046bfda
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,103 @@
+name: cd
+
+on:
+    push:
+        tags:
+        -   'v[0-9]+.[0-9]+.[0-9]+*'
+
+jobs:
+
+    validate-release-tag:
+
+        if: github.repository == 'aiidateam/aiida-pseudo'
+        runs-on: ubuntu-latest
+
+        steps:
+        -   name: Checkout source
+            uses: actions/checkout@v2
+
+        -   name: Install Python 3.9
+            uses: actions/setup-python@v4
+            with:
+                python-version: '3.9'
+
+        -   name: Validate the tag version against the package version
+            run: python .github/workflows/validate_release_tag.py $GITHUB_REF
+
+    pre-commit:
+
+        needs: [validate-release-tag]
+        runs-on: ubuntu-latest
+
+        steps:
+        -   uses: actions/checkout@v2
+
+        -   name: Install Python
+            uses: actions/setup-python@v4
+            with:
+                python-version: '3.9'
+                cache: 'pip'
+                cache-dependency-path: pyproject.toml
+
+        -   name: Install Python package and dependencies
+            run: pip install -e .[pre-commit,tests]
+
+        -   name: Run pre-commit
+            run: pre-commit run --all-files || ( git status --short ; git diff ; exit 1 )
+
+    tests:
+
+        needs: [validate-release-tag]
+        runs-on: ubuntu-latest
+
+        strategy:
+            matrix:
+                python-version: ['3.7', '3.8', '3.9']
+
+        services:
+            postgres:
+                image: postgres:12
+            rabbitmq:
+                image: rabbitmq:latest
+                ports:
+                -   5672:5672
+
+        steps:
+        -   uses: actions/checkout@v2
+
+        -   name: Install Python ${{ matrix.python-version }}
+            uses: actions/setup-python@v4
+            with:
+                python-version: ${{ matrix.python-version }}
+                cache: 'pip'
+                cache-dependency-path: pyproject.toml
+
+        -   name: Install Python package and dependencies
+            run: pip install -e .[tests]
+
+        -   name: Run pytest
+            run: pytest -sv tests
+
+    publish:
+
+        name: Publish to PyPI
+        needs: [pre-commit, tests]
+        runs-on: ubuntu-latest
+
+        steps:
+        -   name: Checkout source
+            uses: actions/checkout@v2
+
+        -   name: Install Python 3.9
+            uses: actions/setup-python@v4
+            with:
+                python-version: '3.9'
+
+        -   name: Install flit
+            run: pip install flit~=3.4
+
+        -   name: Build and publish
+            run: flit publish
+            env:
+                FLIT_USERNAME: __token__
+                FLIT_PASSWORD: ${{ secrets.PYPI_KEY }}
diff --git a/.github/workflows/validate_release_tag.py b/.github/workflows/validate_release_tag.py
new file mode 100644
index 0000000..a3f69f6
--- /dev/null
+++ b/.github/workflows/validate_release_tag.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+"""Validate that the version in the tag label matches the version of the package."""
+import argparse
+import ast
+from pathlib import Path
+
+
+def get_version_from_module(content: str) -> str:
+    """Get the ``__version__`` attribute from a module.
+
+    .. note:: This has been adapted from :mod:`setuptools.config`.
+    """
+    try:
+        module = ast.parse(content)
+    except SyntaxError as exception:
+        raise IOError('Unable to parse module.') from exception
+
+    try:
+        return next(
+            ast.literal_eval(statement.value) for statement in module.body if isinstance(statement, ast.Assign)
+            for target in statement.targets if isinstance(target, ast.Name) and target.id == '__version__'
+        )
+    except StopIteration as exception:
+        raise IOError('Unable to find the `__version__` attribute in the module.') from exception
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument('GITHUB_REF', help='The GITHUB_REF environmental variable')
+    args = parser.parse_args()
+    TAG_PREFIX = 'refs/tags/v'
+    assert args.GITHUB_REF.startswith(TAG_PREFIX), f'GITHUB_REF should start with "{TAG_PREFIX}": {args.GITHUB_REF}'
+    tag_version = args.GITHUB_REF.removeprefix(TAG_PREFIX)
+    package_version = get_version_from_module(Path('aiida_pseudo/__init__.py').read_text(encoding='utf-8'))
+    error_message = f'The tag version `{tag_version}` is different from the package version `{package_version}`'
+    assert tag_version == package_version, error_message