diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 000000000..44de8d36a
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,54 @@
+// For format details, see https://containers.dev/implementors/json_reference/
+{
+ "name": "Python 3 Developer Container",
+ "build": {
+ "dockerfile": "../Dockerfile",
+ "target": "build",
+ // Only upgrade pip, we will install the project below
+ "args": {
+ "PIP_OPTIONS": "--upgrade pip"
+ }
+ },
+ "remoteEnv": {
+ "DISPLAY": "${localEnv:DISPLAY}"
+ },
+ // Add the URLs of features you want added when the container is built.
+ "features": {
+ "ghcr.io/devcontainers/features/common-utils:1": {
+ "username": "none",
+ "upgradePackages": false
+ }
+ },
+ // Set *default* container specific settings.json values on container create.
+ "settings": {
+ "python.defaultInterpreterPath": "/venv/bin/python"
+ },
+ "customizations": {
+ "vscode": {
+ // Add the IDs of extensions you want installed when the container is created.
+ "extensions": [
+ "ms-python.python",
+ "tamasfe.even-better-toml",
+ "redhat.vscode-yaml",
+ "ryanluker.vscode-coverage-gutters"
+ ]
+ }
+ },
+ // Make sure the files we are mapping into the container exist on the host
+ "initializeCommand": "bash -c 'for i in $HOME/.inputrc; do [ -f $i ] || touch $i; done'",
+ "runArgs": [
+ "--net=host",
+ "--security-opt=label=type:container_runtime_t"
+ ],
+ "mounts": [
+ "source=${localEnv:HOME}/.ssh,target=/root/.ssh,type=bind",
+ "source=${localEnv:HOME}/.inputrc,target=/root/.inputrc,type=bind",
+ // map in home directory - not strictly necessary but useful
+ "source=${localEnv:HOME},target=${localEnv:HOME},type=bind,consistency=cached"
+ ],
+ // make the workspace folder the same inside and outside of the container
+ "workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
+ "workspaceFolder": "${localWorkspaceFolder}",
+ // After the container is created, install the python project in editable form
+ "postCreateCommand": "pip install -e '.[dev]'"
+}
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 41a139337..000000000
--- a/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-*/_version_git.py export-subst
diff --git a/.github/actions/install_requirements/action.yml b/.github/actions/install_requirements/action.yml
new file mode 100644
index 000000000..20d7a3adf
--- /dev/null
+++ b/.github/actions/install_requirements/action.yml
@@ -0,0 +1,57 @@
+name: Install requirements
+description: Run pip install with requirements and upload resulting requirements
+inputs:
+ requirements_file:
+ description: Name of requirements file to use and upload
+ required: true
+ install_options:
+ description: Parameters to pass to pip install
+ required: true
+ python_version:
+ description: Python version to install
+ default: "3.x"
+
+runs:
+ using: composite
+
+ steps:
+ - name: Setup python
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ inputs.python_version }}
+
+ - name: Pip install
+ run: |
+ touch ${{ inputs.requirements_file }}
+ # -c uses requirements.txt as constraints, see 'Validate requirements file'
+ pip install -c ${{ inputs.requirements_file }} ${{ inputs.install_options }}
+ shell: bash
+
+ - name: Create lockfile
+ run: |
+ mkdir -p lockfiles
+ pip freeze --exclude-editable > lockfiles/${{ inputs.requirements_file }}
+ # delete the self referencing line and make sure it isn't blank
+ sed -i'' -e '/file:/d' lockfiles/${{ inputs.requirements_file }}
+ shell: bash
+
+ - name: Upload lockfiles
+ uses: actions/upload-artifact@v3
+ with:
+ name: lockfiles
+ path: lockfiles
+
+ # This eliminates the class of problems where the requirements being given no
+ # longer match what the packages themselves dictate. E.g. In the rare instance
+ # where I install some-package which used to depend on vulnerable-dependency
+ # but now uses good-dependency (despite being nominally the same version)
+ # pip will install both if given a requirements file with -r
+ - name: If requirements file exists, check it matches pip installed packages
+ run: |
+ if [ -s ${{ inputs.requirements_file }} ]; then
+ if ! diff -u ${{ inputs.requirements_file }} lockfiles/${{ inputs.requirements_file }}; then
+ echo "Error: ${{ inputs.requirements_file }} need the above changes to be exhaustive"
+ exit 1
+ fi
+ fi
+ shell: bash
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..fb7c6ee67
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,16 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/pages/index.html b/.github/pages/index.html
index d0f588233..cc33127d4 100644
--- a/.github/pages/index.html
+++ b/.github/pages/index.html
@@ -6,4 +6,4 @@
-