diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..84be464 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,139 @@ +# Everything below this point is a copy of .gitignore. + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# macOS +.DS_Store diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 44a14de..429e9fb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,7 +1,18 @@ name: CI 'on': - push: {} + push: + branches-ignore: + # These should always correspond to pull requests, so ignore them for + # the push trigger and let them be triggered by the pull_request + # trigger, avoiding running the workflow twice. This is a minor + # optimization so there's no need to ensure this is comprehensive. + - 'dependabot/**' + - 'renovate/**' + - 'tickets/**' + - 'u/**' + tags: + - '*' pull_request: {} jobs: @@ -14,7 +25,73 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.9' + python-version: 3.9 - - name: Run pre-commit - uses: pre-commit/action@v2.0.3 + - name: Install tox + run: pip install tox + + - name: Cache tox environments + id: cache-tox + uses: actions/cache@v2 + with: + path: .tox + # requirements/*.txt and pyproject.toml have versioning info + # that would impact the tox environment. + key: tox-${{ matrix.python }}-${{ hashFiles('requirements/*.txt') }}-${{ hashFiles('pyproject.toml') }} + restore-keys: | + tox-${{ matrix.python }}-${{ hashFiles('requirements/*.txt') }}- + + - name: Run tox + run: tox -e py,coverage-report,typing + + build: + runs-on: ubuntu-latest + needs: [test] + + # Only do Docker builds of tagged releases and pull requests from ticket + # branches. This will still trigger on pull requests from untrusted + # repositories whose branch names match our tickets/* branch convention, + # but in this case the build will fail with an error since the secret + # won't be set. + if: > + startsWith(github.ref, 'refs/tags/') + || startsWith(github.head_ref, 'tickets/') + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Define the Docker tag + id: vars + run: echo ::set-output name=tag::$(scripts/docker-tag.sh "$GITHUB_HEAD_REF") + + - name: Print the tag + id: print + run: echo ${{ steps.vars.outputs.tag }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: token + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Docker image + id: docker_build + uses: docker/build-push-action@v2 + with: + context: ./ # use currently-checked out files + file: ./Dockerfile + push: ${{ github.repository == 'lsst-sitcom/doc-portal-prototype' }} + tags: | + ghcr.io/lsst-sitcom/protorubinportal:${{ steps.vars.outputs.tag }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index b6e4761..7af665b 100644 --- a/.gitignore +++ b/.gitignore @@ -82,7 +82,9 @@ profile_default/ ipython_config.py # pyenv -.python-version +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -127,3 +129,9 @@ dmypy.json # Pyre type checker .pyre/ + +# pytype static type analyzer +.pytype/ + +# macOS +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2770046 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,68 @@ +# This Dockerfile has four stages: +# +# base-image +# Updates the base Python image with security patches and common system +# packages. This image becomes the base of all other images. +# dependencies-image +# Installs third-party dependencies (requirements/main.txt) into a virtual +# environment. This virtual environment is ideal for copying across build +# stages. +# install-image +# Installs the app into the virtual environment. +# runtime-image +# - Copies the virtual environment into place. +# - Runs a non-root user. +# - Sets up the entrypoint and port. + +FROM python:3.9.8-slim-bullseye as base-image + +# Update system packages +COPY scripts/install-base-packages.sh . +RUN ./install-base-packages.sh && rm ./install-base-packages.sh + +FROM base-image AS dependencies-image + +# Install system packages only needed for building dependencies. +COPY scripts/install-dependency-packages.sh . +RUN ./install-dependency-packages.sh + +# Create a Python virtual environment +ENV VIRTUAL_ENV=/opt/venv +RUN python -m venv $VIRTUAL_ENV +# Make sure we use the virtualenv +ENV PATH="$VIRTUAL_ENV/bin:$PATH" +# Put the latest pip and setuptools in the virtualenv +RUN pip install --upgrade --no-cache-dir pip setuptools wheel + +# Install the app's Python runtime dependencies +COPY requirements/main.txt ./requirements.txt +RUN pip install --quiet --no-cache-dir -r requirements.txt + +FROM dependencies-image AS install-image + +# Use the virtualenv +ENV PATH="/opt/venv/bin:$PATH" + +COPY . /workdir +WORKDIR /workdir +RUN pip install --no-cache-dir . + +FROM base-image AS runtime-image + +# Create a non-root user +RUN useradd --create-home appuser + +# Copy the virtualenv +COPY --from=install-image /opt/venv /opt/venv + +# Make sure we use the virtualenv +ENV PATH="/opt/venv/bin:$PATH" + +# Switch to the non-root user. +USER appuser + +# Expose the port. +EXPOSE 8080 + +# Run the application. +CMD ["uvicorn", "protorubinportal.main:app", "--host", "0.0.0.0", "--port", "8080"] diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile index 4655e7e..bf63d14 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,20 @@ +.PHONY: update-deps +update-deps: + pip install --upgrade pip-tools "pip<22" setuptools + pip-compile --upgrade --build-isolation --generate-hashes --output-file requirements/main.txt requirements/main.in + pip-compile --upgrade --build-isolation --generate-hashes --output-file requirements/dev.txt requirements/dev.in + .PHONY: init init: - pip install -U pre-commit + pip install --editable . + pip install --upgrade -r requirements/main.txt -r requirements/dev.txt + rm -rf .tox + pip install --upgrade tox pre-commit install -.PHONY: test -test: - pre-commit run --all-files +.PHONY: update +update: update-deps init + +.PHONY: run +run: + tox -e run diff --git a/manifests/base/configmap.yaml b/manifests/base/configmap.yaml new file mode 100644 index 0000000..bb0c3df --- /dev/null +++ b/manifests/base/configmap.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: 'protorubinportal' + labels: + app.kubernetes.io/name: 'protorubinportal' +data: + # These configurations are injected as environment variables into the + # app container. + SAFIR_NAME: 'protorubinportal' + SAFIR_PROFILE: 'production' + SAFIR_LOGGER: 'protorubinportal' + SAFIR_LOG_LEVEL: 'INFO' diff --git a/manifests/base/deployment.yaml b/manifests/base/deployment.yaml new file mode 100644 index 0000000..a8eb57c --- /dev/null +++ b/manifests/base/deployment.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: 'protorubinportal' + labels: + app.kubernetes.io/name: 'protorubinportal' +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: 'protorubinportal' + template: + metadata: + labels: + app.kubernetes.io/name: 'protorubinportal' + spec: + automountServiceAccountToken: false + containers: + - name: app + imagePullPolicy: 'Always' + # Use images field in a Kustomization to set/update image tag + image: 'ghcr.io/lsst-sitcom/protorubinportal' + ports: + - containerPort: 8080 + name: 'app' + envFrom: + - configMapRef: + name: 'protorubinportal' + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - 'all' + readOnlyRootFilesystem: true + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 diff --git a/manifests/base/kustomization.yaml b/manifests/base/kustomization.yaml new file mode 100644 index 0000000..7880ac4 --- /dev/null +++ b/manifests/base/kustomization.yaml @@ -0,0 +1,11 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +images: + - name: 'ghcr.io/lsst-sitcom/protorubinportal' + newTag: 0.0.0 + +resources: + - configmap.yaml + - deployment.yaml + - service.yaml diff --git a/manifests/base/service.yaml b/manifests/base/service.yaml new file mode 100644 index 0000000..ec80d13 --- /dev/null +++ b/manifests/base/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: 'protorubinportal' + labels: + app.kubernetes.io/name: 'protorubinportal' +spec: + ports: + - name: 'protorubinportal-http' + protocol: 'TCP' + port: 8080 + targetPort: 'app' + selector: + app.kubernetes.io/name: 'protorubinportal' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..27bc8b6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,55 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel", + "setuptools_scm[toml]>=3.4" +] +build-backend = 'setuptools.build_meta' + +[tool.setuptools_scm] + +[tool.coverage.run] +parallel = true +branch = true +source = ["protorubinportal"] + +[tool.coverage.paths] +source = ["src", ".tox/*/site-packages"] + +[tool.coverage.report] +show_missing = true +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "if self.debug:", + "if settings.DEBUG", + "raise AssertionError", + "raise NotImplementedError", + "if 0:", + "if __name__ == .__main__.:", + "if TYPE_CHECKING:" +] + +[tool.black] +line-length = 79 +target-version = ['py38'] +exclude = ''' +/( + \.eggs + | \.git + | \.mypy_cache + | \.tox + | \.venv + | _build + | build + | dist +)/ +''' +# Use single-quoted strings so TOML treats the string like a Python r-string +# Multi-line strings are implicitly treated by black as regular expressions + +[tool.isort] +include_trailing_comma = true +multi_line_output = 3 +known_first_party = ["protorubinportal", "tests"] +skip = ["docs/conf.py"] diff --git a/requirements/dev.in b/requirements/dev.in new file mode 100644 index 0000000..a27da32 --- /dev/null +++ b/requirements/dev.in @@ -0,0 +1,17 @@ +# Editable development dependencies +# Add direct development, test, and documentation dependencies here, as well +# as implicit dev dependencies with constrained versions. +# +# After editing, update requirements/dev.txt by running: +# make update-deps + +-c main.txt + +asgi-lifespan +coverage[toml] +httpx +mypy +pre-commit +pytest +pytest-asyncio +pytest-cov diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 0000000..d2483e3 --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,269 @@ +# +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: +# +# pip-compile --generate-hashes --output-file=requirements/dev.txt requirements/dev.in +# +anyio==3.5.0 \ + --hash=sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6 \ + --hash=sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e + # via + # -c requirements/main.txt + # httpcore +asgi-lifespan==1.0.1 \ + --hash=sha256:9a33e7da2073c4764bc79bd6136501d6c42f60e3d2168ba71235e84122eadb7f \ + --hash=sha256:9ea969dc5eb5cf08e52c08dce6f61afcadd28112e72d81c972b1d8eb8691ab53 + # via -r requirements/dev.in +attrs==21.4.0 \ + --hash=sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4 \ + --hash=sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd + # via pytest +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 + # via + # -c requirements/main.txt + # httpcore + # httpx +cfgv==3.3.1 \ + --hash=sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426 \ + --hash=sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736 + # via pre-commit +charset-normalizer==2.0.11 \ + --hash=sha256:2842d8f5e82a1f6aa437380934d5e1cd4fcf2003b06fed6940769c164a480a45 \ + --hash=sha256:98398a9d69ee80548c762ba991a4728bfc3836768ed226b3945908d1a688371c + # via + # -c requirements/main.txt + # httpx +coverage[toml]==6.3.1 \ + --hash=sha256:1245ab82e8554fa88c4b2ab1e098ae051faac5af829efdcf2ce6b34dccd5567c \ + --hash=sha256:1bc6d709939ff262fd1432f03f080c5042dc6508b6e0d3d20e61dd045456a1a0 \ + --hash=sha256:25e73d4c81efa8ea3785274a2f7f3bfbbeccb6fcba2a0bdd3be9223371c37554 \ + --hash=sha256:276b13cc085474e482566c477c25ed66a097b44c6e77132f3304ac0b039f83eb \ + --hash=sha256:2aed4761809640f02e44e16b8b32c1a5dee5e80ea30a0ff0912158bde9c501f2 \ + --hash=sha256:2dd70a167843b4b4b2630c0c56f1b586fe965b4f8ac5da05b6690344fd065c6b \ + --hash=sha256:352c68e233409c31048a3725c446a9e48bbff36e39db92774d4f2380d630d8f8 \ + --hash=sha256:3f2b05757c92ad96b33dbf8e8ec8d4ccb9af6ae3c9e9bd141c7cc44d20c6bcba \ + --hash=sha256:448d7bde7ceb6c69e08474c2ddbc5b4cd13c9e4aa4a717467f716b5fc938a734 \ + --hash=sha256:463e52616ea687fd323888e86bf25e864a3cc6335a043fad6bbb037dbf49bbe2 \ + --hash=sha256:482fb42eea6164894ff82abbcf33d526362de5d1a7ed25af7ecbdddd28fc124f \ + --hash=sha256:56c4a409381ddd7bbff134e9756077860d4e8a583d310a6f38a2315b9ce301d0 \ + --hash=sha256:56d296cbc8254a7dffdd7bcc2eb70be5a233aae7c01856d2d936f5ac4e8ac1f1 \ + --hash=sha256:5e15d424b8153756b7c903bde6d4610be0c3daca3986173c18dd5c1a1625e4cd \ + --hash=sha256:618eeba986cea7f621d8607ee378ecc8c2504b98b3fdc4952b30fe3578304687 \ + --hash=sha256:61d47a897c1e91f33f177c21de897267b38fbb45f2cd8e22a710bcef1df09ac1 \ + --hash=sha256:621f6ea7260ea2ffdaec64fe5cb521669984f567b66f62f81445221d4754df4c \ + --hash=sha256:6a5cdc3adb4f8bb8d8f5e64c2e9e282bc12980ef055ec6da59db562ee9bdfefa \ + --hash=sha256:6c3f6158b02ac403868eea390930ae64e9a9a2a5bbfafefbb920d29258d9f2f8 \ + --hash=sha256:704f89b87c4f4737da2860695a18c852b78ec7279b24eedacab10b29067d3a38 \ + --hash=sha256:72128176fea72012063200b7b395ed8a57849282b207321124d7ff14e26988e8 \ + --hash=sha256:78fbb2be068a13a5d99dce9e1e7d168db880870f7bc73f876152130575bd6167 \ + --hash=sha256:7bff3a98f63b47464480de1b5bdd80c8fade0ba2832c9381253c9b74c4153c27 \ + --hash=sha256:84f2436d6742c01136dd940ee158bfc7cf5ced3da7e4c949662b8703b5cd8145 \ + --hash=sha256:9976fb0a5709988778ac9bc44f3d50fccd989987876dfd7716dee28beed0a9fa \ + --hash=sha256:9ad0a117b8dc2061ce9461ea4c1b4799e55edceb236522c5b8f958ce9ed8fa9a \ + --hash=sha256:9e3dd806f34de38d4c01416344e98eab2437ac450b3ae39c62a0ede2f8b5e4ed \ + --hash=sha256:9eb494070aa060ceba6e4bbf44c1bc5fa97bfb883a0d9b0c9049415f9e944793 \ + --hash=sha256:9fde6b90889522c220dd56a670102ceef24955d994ff7af2cb786b4ba8fe11e4 \ + --hash=sha256:9fff3ff052922cb99f9e52f63f985d4f7a54f6b94287463bc66b7cdf3eb41217 \ + --hash=sha256:a06c358f4aed05fa1099c39decc8022261bb07dfadc127c08cfbd1391b09689e \ + --hash=sha256:a4f923b9ab265136e57cc14794a15b9dcea07a9c578609cd5dbbfff28a0d15e6 \ + --hash=sha256:c5b81fb37db76ebea79aa963b76d96ff854e7662921ce742293463635a87a78d \ + --hash=sha256:d5ed164af5c9078596cfc40b078c3b337911190d3faeac830c3f1274f26b8320 \ + --hash=sha256:d651fde74a4d3122e5562705824507e2f5b2d3d57557f1916c4b27635f8fbe3f \ + --hash=sha256:de73fca6fb403dd72d4da517cfc49fcf791f74eee697d3219f6be29adf5af6ce \ + --hash=sha256:e647a0be741edbb529a72644e999acb09f2ad60465f80757da183528941ff975 \ + --hash=sha256:e92c7a5f7d62edff50f60a045dc9542bf939758c95b2fcd686175dd10ce0ed10 \ + --hash=sha256:eeffd96882d8c06d31b65dddcf51db7c612547babc1c4c5db6a011abe9798525 \ + --hash=sha256:f5a4551dfd09c3bd12fca8144d47fe7745275adf3229b7223c2f9e29a975ebda \ + --hash=sha256:fac0bcc5b7e8169bffa87f0dcc24435446d329cbc2b5486d155c2e0f3b493ae1 + # via + # -r requirements/dev.in + # pytest-cov +distlib==0.3.4 \ + --hash=sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b \ + --hash=sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579 + # via virtualenv +filelock==3.4.2 \ + --hash=sha256:38b4f4c989f9d06d44524df1b24bd19e167d851f19b50bf3e3559952dddc5b80 \ + --hash=sha256:cf0fc6a2f8d26bd900f19bf33915ca70ba4dd8c56903eeb14e1e7a2fd7590146 + # via virtualenv +h11==0.12.0 \ + --hash=sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6 \ + --hash=sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042 + # via + # -c requirements/main.txt + # httpcore +httpcore==0.14.7 \ + --hash=sha256:47d772f754359e56dd9d892d9593b6f9870a37aeb8ba51e9a88b09b3d68cfade \ + --hash=sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1 + # via + # -c requirements/main.txt + # httpx +httpx==0.22.0 \ + --hash=sha256:d8e778f76d9bbd46af49e7f062467e3157a5a3d2ae4876a4bbfd8a51ed9c9cb4 \ + --hash=sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6 + # via + # -c requirements/main.txt + # -r requirements/dev.in +identify==2.4.8 \ + --hash=sha256:97e839c1779f07011b84c92af183e1883d9745d532d83412cca1ca76d3808c1c \ + --hash=sha256:a55bdd671b6063eb837af938c250ec00bba6e610454265133b0d2db7ae718d0f + # via pre-commit +idna==3.3 \ + --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ + --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d + # via + # -c requirements/main.txt + # anyio + # rfc3986 +iniconfig==1.1.1 \ + --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ + --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 + # via pytest +mypy==0.931 \ + --hash=sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce \ + --hash=sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d \ + --hash=sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069 \ + --hash=sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c \ + --hash=sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d \ + --hash=sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714 \ + --hash=sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a \ + --hash=sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d \ + --hash=sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05 \ + --hash=sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266 \ + --hash=sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697 \ + --hash=sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc \ + --hash=sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799 \ + --hash=sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd \ + --hash=sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00 \ + --hash=sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7 \ + --hash=sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a \ + --hash=sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0 \ + --hash=sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0 \ + --hash=sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166 + # via -r requirements/dev.in +mypy-extensions==0.4.3 \ + --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ + --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 + # via mypy +nodeenv==1.6.0 \ + --hash=sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b \ + --hash=sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7 + # via pre-commit +packaging==21.3 \ + --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ + --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 + # via pytest +platformdirs==2.4.1 \ + --hash=sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca \ + --hash=sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda + # via virtualenv +pluggy==1.0.0 \ + --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ + --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 + # via pytest +pre-commit==2.17.0 \ + --hash=sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616 \ + --hash=sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a + # via -r requirements/dev.in +py==1.11.0 \ + --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \ + --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378 + # via pytest +pyparsing==3.0.7 \ + --hash=sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea \ + --hash=sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484 + # via packaging +pytest==7.0.0 \ + --hash=sha256:42901e6bd4bd4a0e533358a86e848427a49005a3256f657c5c8f8dd35ef137a9 \ + --hash=sha256:dad48ffda394e5ad9aa3b7d7ddf339ed502e5e365b1350e0af65f4a602344b11 + # via + # -r requirements/dev.in + # pytest-asyncio + # pytest-cov +pytest-asyncio==0.18.0 \ + --hash=sha256:5c510e5d3ad0f97bab0ae0223363d2aa6329bbbafb0981d96dbed6a804a99349 \ + --hash=sha256:5e33f5010402309ff4e8cdec04e76b057ae73e0c132f12c6aa2fa6ec8cabfbf1 + # via -r requirements/dev.in +pytest-cov==3.0.0 \ + --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ + --hash=sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470 + # via -r requirements/dev.in +pyyaml==6.0 \ + --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \ + --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \ + --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \ + --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \ + --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \ + --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \ + --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \ + --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \ + --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \ + --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \ + --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \ + --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ + --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ + --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ + --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ + --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ + --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ + --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ + --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ + --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ + --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ + --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ + --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ + --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ + --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ + --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ + --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ + --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ + --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ + --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ + --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ + --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ + --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 + # via + # -c requirements/main.txt + # pre-commit +rfc3986[idna2008]==1.5.0 \ + --hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \ + --hash=sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97 + # via + # -c requirements/main.txt + # httpx +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via virtualenv +sniffio==1.2.0 \ + --hash=sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663 \ + --hash=sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de + # via + # -c requirements/main.txt + # anyio + # asgi-lifespan + # httpcore + # httpx +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via pre-commit +tomli==2.0.0 \ + --hash=sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224 \ + --hash=sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1 + # via + # coverage + # mypy + # pytest +typing-extensions==4.0.1 \ + --hash=sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e \ + --hash=sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b + # via + # -c requirements/main.txt + # mypy +virtualenv==20.13.1 \ + --hash=sha256:45e1d053cad4cd453181ae877c4ffc053546ae99e7dd049b9ff1d9be7491abf7 \ + --hash=sha256:e0621bcbf4160e4e1030f05065c8834b4e93f4fcc223255db2a823440aca9c14 + # via pre-commit diff --git a/requirements/main.in b/requirements/main.in new file mode 100644 index 0000000..e3fb747 --- /dev/null +++ b/requirements/main.in @@ -0,0 +1,15 @@ +# Editable runtime dependencies (equivalent to install_requires) +# Add direct runtime dependencies here, as well as implicit dependencies +# with constrained versions. +# +# After editing, update requirements/main.txt by running: +# make update-deps + +# These dependencies are for fastapi including some optional features. +fastapi +starlette +uvicorn[standard] +jinja2 + +# Other dependencies. +safir diff --git a/requirements/main.txt b/requirements/main.txt new file mode 100644 index 0000000..131137d --- /dev/null +++ b/requirements/main.txt @@ -0,0 +1,341 @@ +# +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: +# +# pip-compile --generate-hashes --output-file=requirements/main.txt requirements/main.in +# +anyio==3.5.0 \ + --hash=sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6 \ + --hash=sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e + # via + # httpcore + # starlette +asgiref==3.5.0 \ + --hash=sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0 \ + --hash=sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9 + # via uvicorn +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 + # via + # httpcore + # httpx +charset-normalizer==2.0.11 \ + --hash=sha256:2842d8f5e82a1f6aa437380934d5e1cd4fcf2003b06fed6940769c164a480a45 \ + --hash=sha256:98398a9d69ee80548c762ba991a4728bfc3836768ed226b3945908d1a688371c + # via httpx +click==8.0.3 \ + --hash=sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3 \ + --hash=sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b + # via uvicorn +fastapi==0.73.0 \ + --hash=sha256:dcfee92a7f9a72b5d4b7ca364bd2b009f8fc10d95ed5769be20e94f39f7e5a15 \ + --hash=sha256:f0a618aff5f6942862f2d3f20f39b1c037e33314d1b8207fd1c3a2cca76dfd8c + # via + # -r requirements/main.in + # safir +h11==0.12.0 \ + --hash=sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6 \ + --hash=sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042 + # via + # httpcore + # uvicorn +httpcore==0.14.7 \ + --hash=sha256:47d772f754359e56dd9d892d9593b6f9870a37aeb8ba51e9a88b09b3d68cfade \ + --hash=sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1 + # via httpx +httptools==0.3.0 \ + --hash=sha256:04114db99605c9b56ea22a8ec4d7b1485b908128ed4f4a8f6438489c428da794 \ + --hash=sha256:074afd8afdeec0fa6786cd4a1676e0c0be23dc9a017a86647efa6b695168104f \ + --hash=sha256:113816f9af7dcfc4aa71ebb5354d77365f666ecf96ac7ff2aa1d24b6bca44165 \ + --hash=sha256:1a8f26327023fa1a947d36e60a0582149e182fbbc949c8a65ec8665754dbbe69 \ + --hash=sha256:2119fa619a4c53311f594f25c0205d619350fcb32140ec5057f861952e9b2b4f \ + --hash=sha256:21e948034f70e47c8abfa2d5e6f1a5661f87a2cddc7bcc70f61579cc87897c70 \ + --hash=sha256:32a10a5903b5bc0eb647d01cd1e95bec3bb614a9bf53f0af1e01360b2debdf81 \ + --hash=sha256:3787c1f46e9722ef7f07ea5c76b0103037483d1b12e34a02c53ceca5afa4e09a \ + --hash=sha256:3f82eb106e1474c63dba36a176067e65b48385f4cecddf3616411aa5d1fbdfec \ + --hash=sha256:3f9b4856d46ba1f0c850f4e84b264a9a8b4460acb20e865ec00978ad9fbaa4cf \ + --hash=sha256:4137137de8976511a392e27bfdcf231bd926ac13d375e0414e927b08217d779e \ + --hash=sha256:4687dfc116a9f1eb22a7d797f0dc6f6e17190d406ca4e729634b38aa98044b17 \ + --hash=sha256:47dba2345aaa01b87e4981e8756af441349340708d5b60712c98c55a4d28f4af \ + --hash=sha256:5a836bd85ae1fb4304f674808488dae403e136d274aa5bafd0e6ee456f11c371 \ + --hash=sha256:6e676bc3bb911b11f3d7e2144b9a53600bf6b9b21e0e4437aa308e1eef094d97 \ + --hash=sha256:72ee0e3fb9c6437ab3ae34e9abee67fcee6876f4f58504e3f613dd5882aafdb7 \ + --hash=sha256:79717080dc3f8b1eeb7f820b9b81528acbc04be6041f323fdd97550da2062575 \ + --hash=sha256:8ac842df4fc3952efa7820b277961ea55e068bbc54cb59a0820400de7ae358d8 \ + --hash=sha256:9f475b642c48b1b78584bdd12a5143e2c512485664331eade9c29ef769a17598 \ + --hash=sha256:b8ac7dee63af4346e02b1e6d32202e3b5b3706a9928bec6da6d7a5b066217422 \ + --hash=sha256:c0ac2e0ce6733c55858932e7d37fcc7b67ba6bb23e9648593c55f663de031b93 \ + --hash=sha256:c14576b737d9e6e4f2a86af04918dbe9b62f57ce8102a8695c9a382dbe405c7f \ + --hash=sha256:cdc3975db86c29817e6d13df14e037c931fc893a710fb71097777a4147090068 \ + --hash=sha256:eda95634027200f4b2a6d499e7c2e7fa9b8ee57e045dfda26958ea0af27c070b + # via uvicorn +httpx==0.22.0 \ + --hash=sha256:d8e778f76d9bbd46af49e7f062467e3157a5a3d2ae4876a4bbfd8a51ed9c9cb4 \ + --hash=sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6 + # via safir +idna==3.3 \ + --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ + --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d + # via + # anyio + # rfc3986 +jinja2==3.0.3 \ + --hash=sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8 \ + --hash=sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7 + # via -r requirements/main.in +markupsafe==2.0.1 \ + --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \ + --hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \ + --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \ + --hash=sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194 \ + --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \ + --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ + --hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \ + --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \ + --hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \ + --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \ + --hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \ + --hash=sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a \ + --hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \ + --hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \ + --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \ + --hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \ + --hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \ + --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \ + --hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \ + --hash=sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047 \ + --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \ + --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \ + --hash=sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b \ + --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \ + --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \ + --hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \ + --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \ + --hash=sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1 \ + --hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \ + --hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \ + --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \ + --hash=sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee \ + --hash=sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f \ + --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \ + --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \ + --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \ + --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \ + --hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \ + --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \ + --hash=sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86 \ + --hash=sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6 \ + --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \ + --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \ + --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \ + --hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \ + --hash=sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e \ + --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \ + --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \ + --hash=sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f \ + --hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \ + --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \ + --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \ + --hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \ + --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \ + --hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \ + --hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \ + --hash=sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a \ + --hash=sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207 \ + --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \ + --hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \ + --hash=sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd \ + --hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \ + --hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \ + --hash=sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9 \ + --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \ + --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \ + --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \ + --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \ + --hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872 + # via jinja2 +pydantic==1.9.0 \ + --hash=sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3 \ + --hash=sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398 \ + --hash=sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1 \ + --hash=sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65 \ + --hash=sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4 \ + --hash=sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16 \ + --hash=sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2 \ + --hash=sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c \ + --hash=sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6 \ + --hash=sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce \ + --hash=sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9 \ + --hash=sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3 \ + --hash=sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034 \ + --hash=sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c \ + --hash=sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a \ + --hash=sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77 \ + --hash=sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b \ + --hash=sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6 \ + --hash=sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f \ + --hash=sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721 \ + --hash=sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37 \ + --hash=sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032 \ + --hash=sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d \ + --hash=sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed \ + --hash=sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6 \ + --hash=sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054 \ + --hash=sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25 \ + --hash=sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46 \ + --hash=sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5 \ + --hash=sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c \ + --hash=sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070 \ + --hash=sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1 \ + --hash=sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7 \ + --hash=sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d \ + --hash=sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145 + # via + # fastapi + # safir +python-dotenv==0.19.2 \ + --hash=sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3 \ + --hash=sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f + # via uvicorn +pyyaml==6.0 \ + --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \ + --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \ + --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \ + --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \ + --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \ + --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \ + --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \ + --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \ + --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \ + --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \ + --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \ + --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ + --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ + --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ + --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ + --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ + --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ + --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ + --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ + --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ + --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ + --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ + --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ + --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ + --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ + --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ + --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ + --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ + --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ + --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ + --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ + --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ + --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 + # via uvicorn +rfc3986[idna2008]==1.5.0 \ + --hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \ + --hash=sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97 + # via httpx +safir==2.4.2 \ + --hash=sha256:03e4f686d979aec6be220b9803edf470bd96dbda95ecf6c928ac9ceea018dee9 \ + --hash=sha256:27f243e0087385e08084748dc71310e66767a0b347ed568e9ee62956c034d474 + # via -r requirements/main.in +sniffio==1.2.0 \ + --hash=sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663 \ + --hash=sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de + # via + # anyio + # httpcore + # httpx +starlette==0.17.1 \ + --hash=sha256:26a18cbda5e6b651c964c12c88b36d9898481cd428ed6e063f5f29c418f73050 \ + --hash=sha256:57eab3cc975a28af62f6faec94d355a410634940f10b30d68d31cb5ec1b44ae8 + # via + # -r requirements/main.in + # fastapi + # safir +structlog==21.5.0 \ + --hash=sha256:68c4c29c003714fe86834f347cb107452847ba52414390a7ee583472bde00fc9 \ + --hash=sha256:fd7922e195262b337da85c2a91c84be94ccab1f8fd1957bd6986f6904e3761c8 + # via safir +typing-extensions==4.0.1 \ + --hash=sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e \ + --hash=sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b + # via pydantic +uvicorn[standard]==0.17.4 \ + --hash=sha256:25850bbc86195a71a6477b3e4b3b7b4c861fb687fb96912972ce5324472b1011 \ + --hash=sha256:e85872d84fb651cccc4c5d2a71cf7ead055b8fb4d8f1e78e36092282c0cf2aec + # via -r requirements/main.in +uvloop==0.16.0 \ + --hash=sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450 \ + --hash=sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897 \ + --hash=sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861 \ + --hash=sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c \ + --hash=sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805 \ + --hash=sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d \ + --hash=sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464 \ + --hash=sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f \ + --hash=sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9 \ + --hash=sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab \ + --hash=sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f \ + --hash=sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638 \ + --hash=sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64 \ + --hash=sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee \ + --hash=sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382 \ + --hash=sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228 + # via uvicorn +watchgod==0.7 \ + --hash=sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29 \ + --hash=sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7 + # via uvicorn +websockets==10.1 \ + --hash=sha256:002071169d2e44ce8eb9e5ebac9fbce142ba4b5146eef1cfb16b177a27662657 \ + --hash=sha256:05e7f098c76b0a4743716590bb8f9706de19f1ef5148d61d0cf76495ec3edb9c \ + --hash=sha256:08a42856158307e231b199671c4fce52df5786dd3d703f36b5d8ac76b206c485 \ + --hash=sha256:0d93b7cadc761347d98da12ec1930b5c71b2096f1ceed213973e3cda23fead9c \ + --hash=sha256:10edd9d7d3581cfb9ff544ac09fc98cab7ee8f26778a5a8b2d5fd4b0684c5ba5 \ + --hash=sha256:14e9cf68a08d1a5d42109549201aefba473b1d925d233ae19035c876dd845da9 \ + --hash=sha256:181d2b25de5a437b36aefedaf006ecb6fa3aa1328ec0236cdde15f32f9d3ff6d \ + --hash=sha256:189ed478395967d6a98bb293abf04e8815349e17456a0a15511f1088b6cb26e4 \ + --hash=sha256:1d858fb31e5ac992a2cdf17e874c95f8a5b1e917e1fb6b45ad85da30734b223f \ + --hash=sha256:1dafe98698ece09b8ccba81b910643ff37198e43521d977be76caf37709cf62b \ + --hash=sha256:3477146d1f87ead8df0f27e8960249f5248dceb7c2741e8bbec9aa5338d0c053 \ + --hash=sha256:38db6e2163b021642d0a43200ee2dec8f4980bdbda96db54fde72b283b54cbfc \ + --hash=sha256:3a02ab91d84d9056a9ee833c254895421a6333d7ae7fff94b5c68e4fa8095519 \ + --hash=sha256:3bbf080f3892ba1dc8838786ec02899516a9d227abe14a80ef6fd17d4fb57127 \ + --hash=sha256:3ef6f73854cded34e78390dbdf40dfdcf0b89b55c0e282468ef92646fce8d13a \ + --hash=sha256:468f0031fdbf4d643f89403a66383247eb82803430b14fa27ce2d44d2662ca37 \ + --hash=sha256:483edee5abed738a0b6a908025be47f33634c2ad8e737edd03ffa895bd600909 \ + --hash=sha256:531d8eb013a9bc6b3ad101588182aa9b6dd994b190c56df07f0d84a02b85d530 \ + --hash=sha256:5560558b0dace8312c46aa8915da977db02738ac8ecffbc61acfbfe103e10155 \ + --hash=sha256:5bb6256de5a4fb1d42b3747b4e2268706c92965d75d0425be97186615bf2f24f \ + --hash=sha256:667c41351a6d8a34b53857ceb8343a45c85d438ee4fd835c279591db8aeb85be \ + --hash=sha256:6b014875fae19577a392372075e937ebfebf53fd57f613df07b35ab210f31534 \ + --hash=sha256:6fdec1a0b3e5630c58e3d8704d2011c678929fce90b40908c97dfc47de8dca72 \ + --hash=sha256:7bdd3d26315db0a9cf8a0af30ca95e0aa342eda9c1377b722e71ccd86bc5d1dd \ + --hash=sha256:7c9407719f42cb77049975410490c58a705da6af541adb64716573e550e5c9db \ + --hash=sha256:7d6673b2753f9c5377868a53445d0c321ef41ff3c8e3b6d57868e72054bfce5f \ + --hash=sha256:816ae7dac2c6522cfa620947ead0ca95ac654916eebf515c94d7c28de5601a6e \ + --hash=sha256:882c0b8bdff3bf1bd7f024ce17c6b8006042ec4cceba95cf15df57e57efa471c \ + --hash=sha256:8877861e3dee38c8d302eee0d5dbefa6663de3b46dc6a888f70cd7e82562d1f7 \ + --hash=sha256:888a5fa2a677e0c2b944f9826c756475980f1b276b6302e606f5c4ff5635be9e \ + --hash=sha256:89e985d40d407545d5f5e2e58e1fdf19a22bd2d8cd54d20a882e29f97e930a0a \ + --hash=sha256:97b4b68a2ddaf5c4707ae79c110bfd874c5be3c6ac49261160fb243fa45d8bbb \ + --hash=sha256:98de71f86bdb29430fd7ba9997f47a6b10866800e3ea577598a786a785701bb0 \ + --hash=sha256:9f304a22ece735a3da8a51309bc2c010e23961a8f675fae46fdf62541ed62123 \ + --hash=sha256:9fd62c6dc83d5d35fb6a84ff82ec69df8f4657fff05f9cd6c7d9bec0dd57f0f6 \ + --hash=sha256:a249139abc62ef333e9e85064c27fefb113b16ffc5686cefc315bdaef3eefbc8 \ + --hash=sha256:b66e6d514f12c28d7a2d80bb2a48ef223342e99c449782d9831b0d29a9e88a17 \ + --hash=sha256:b68b6caecb9a0c6db537aa79750d1b592a841e4f1a380c6196091e65b2ad35f9 \ + --hash=sha256:baa83174390c0ff4fc1304fbe24393843ac7a08fdd59295759c4b439e06b1536 \ + --hash=sha256:bb01ea7b5f52e7125bdc3c5807aeaa2d08a0553979cf2d96a8b7803ea33e15e7 \ + --hash=sha256:cfae282c2aa7f0c4be45df65c248481f3509f8c40ca8b15ed96c35668ae0ff69 \ + --hash=sha256:d0d81b46a5c87d443e40ce2272436da8e6092aa91f5fbeb60d1be9f11eff5b4c \ + --hash=sha256:d9b245db5a7e64c95816e27d72830e51411c4609c05673d1ae81eb5d23b0be54 \ + --hash=sha256:ddab2dc69ee5ae27c74dbfe9d7bb6fee260826c136dca257faa1a41d1db61a89 \ + --hash=sha256:e1b60fd297adb9fc78375778a5220da7f07bf54d2a33ac781319650413fc6a60 \ + --hash=sha256:e259be0863770cb91b1a6ccf6907f1ac2f07eff0b7f01c249ed751865a70cb0d \ + --hash=sha256:e3872ae57acd4306ecf937d36177854e218e999af410a05c17168cd99676c512 \ + --hash=sha256:e4819c6fb4f336fd5388372cb556b1f3a165f3f68e66913d1a2fc1de55dc6f58 + # via uvicorn diff --git a/scripts/docker-tag.sh b/scripts/docker-tag.sh new file mode 100755 index 0000000..080a9c9 --- /dev/null +++ b/scripts/docker-tag.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Determine the tag for Docker images based on GitHub Actions environment +# variables. + +set -eo pipefail + +if [ -n "$GITHUB_HEAD_REF" ]; then + # For pull requests + echo ${GITHUB_HEAD_REF} | sed -E 's,/,-,g' +else + # For push events + echo ${GITHUB_REF} | sed -E 's,refs/(heads|tags)/,,' | sed -E 's,/,-,g' +fi diff --git a/scripts/install-base-packages.sh b/scripts/install-base-packages.sh new file mode 100755 index 0000000..620781c --- /dev/null +++ b/scripts/install-base-packages.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This script updates packages in the base Docker image that's used by both the +# build and runtime images, and gives us a place to install additional +# system-level packages with apt-get. +# +# Based on the blog post: +# https://pythonspeed.com/articles/system-packages-docker/ + +# Bash "strict mode", to help catch problems and bugs in the shell +# script. Every bash script you write should include this. See +# http://redsymbol.net/articles/unofficial-bash-strict-mode/ for +# details. +set -euo pipefail + +# Display each command as it's run. +set -x + +# Tell apt-get we're never going to be able to give manual +# feedback: +export DEBIAN_FRONTEND=noninteractive + +# Update the package listing, so we know what packages exist: +apt-get update + +# Install security updates: +apt-get -y upgrade + +# Example of installing a new package, without unnecessary packages: +apt-get -y install --no-install-recommends git + +# Delete cached files we don't need anymore: +apt-get clean +rm -rf /var/lib/apt/lists/* diff --git a/scripts/install-dependency-packages.sh b/scripts/install-dependency-packages.sh new file mode 100755 index 0000000..f63ef75 --- /dev/null +++ b/scripts/install-dependency-packages.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This script installs additional packages used by the dependency image but +# not needed by the runtime image, such as additional packages required to +# build Python dependencies. +# +# Since the base image wipes all the apt caches to clean up the image that +# will be reused by the runtime image, we unfortunately have to do another +# apt-get update here, which wastes some time and network. + +# Bash "strict mode", to help catch problems and bugs in the shell +# script. Every bash script you write should include this. See +# http://redsymbol.net/articles/unofficial-bash-strict-mode/ for +# details. +set -euo pipefail + +# Display each command as it's run. +set -x + +# Tell apt-get we're never going to be able to give manual +# feedback: +export DEBIAN_FRONTEND=noninteractive + +# Update the package listing, so we know what packages exist: +apt-get update + +# Install build-essential because sometimes Python dependencies need to build +# C modules, particularly when upgrading to newer Python versions. libffi-dev +# is sometimes needed to build cffi (a cryptography dependency). +apt-get -y install --no-install-recommends build-essential libffi-dev + +# Delete cached files we don't need anymore: +apt-get clean +rm -rf /var/lib/apt/lists/* diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..6389a21 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,58 @@ +[metadata] +name = protorubinportal +description = Prototype app for the Rubin documentation portal.. +author = Association of Universities for Research in Astronomy, Inc. (AURA) +author_email = sqre-admin@lists.lsst.org +long_description = file: README.rst, CHANGELOG.rst, LICENSE +long_description_content_type = text/x-rst +license = MIT +url = https://github.com/lsst-sqre/protorubinportal +project_urls = + Change log = https://github.com/lsst-sqre/protorubinportal/master/blob/CHANGELOG.rst + Source code = https://github.com/lsst-sqre/protorubinportal + Issue tracker = https://github.com/lsst-sqre/protorubinportal/issues +classifiers = + Development Status :: 4 - Beta + License :: OSI Approved :: MIT License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Natural Language :: English + Operating System :: POSIX +keywords = + lsst + +[options] +zip_safe = False +include_package_data = True +package_dir = + = src +packages=find: +python_requires = >=3.8 +setup_requires = + setuptools_scm +# Use requirements/main.in for runtime dependencies instead of install_requires + +[options.packages.find] +where = src + +[options.entry_points] +console_scripts = + protorubinportal = protorubinportal.cli:main + +[flake8] +max-line-length = 79 +# E203: whitespace before :, flake8 disagrees with PEP-8 +# W503: line break after binary operator, flake8 disagrees with PEP-8 +ignore = E203, W503 + +[mypy] +disallow_untyped_defs = True +disallow_incomplete_defs = True +ignore_missing_imports = True +show_error_codes = True +strict_equality = True +warn_redundant_casts = True +warn_unreachable = True +warn_unused_ignores = True diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d5d43d7 --- /dev/null +++ b/setup.py @@ -0,0 +1,3 @@ +from setuptools import setup + +setup(use_scm_version=True) diff --git a/src/protorubinportal/__init__.py b/src/protorubinportal/__init__.py new file mode 100644 index 0000000..2f899ff --- /dev/null +++ b/src/protorubinportal/__init__.py @@ -0,0 +1,14 @@ +"""The protorubinportal service.""" + +__all__ = ["__version__"] + +from importlib.metadata import PackageNotFoundError, version + +__version__: str +"""The application version string (PEP 440 / SemVer compatible).""" + +try: + __version__ = version(__name__) +except PackageNotFoundError: + # package is not installed + __version__ = "0.0.0" diff --git a/src/protorubinportal/config.py b/src/protorubinportal/config.py new file mode 100644 index 0000000..3221ea1 --- /dev/null +++ b/src/protorubinportal/config.py @@ -0,0 +1,44 @@ +"""Configuration definition.""" + +from __future__ import annotations + +from enum import Enum + +from pydantic import BaseSettings, Field + +__all__ = ["Config", "Profile", "LogLevel"] + + +class Profile(str, Enum): + + production = "production" + + development = "development" + + +class LogLevel(str, Enum): + + DEBUG = "DEBUG" + + INFO = "INFO" + + WARNING = "WARNING" + + ERROR = "ERROR" + + CRITICAL = "CRITICAL" + + +class Config(BaseSettings): + + name: str = Field("protorubinportal", env="SAFIR_NAME") + + profile: Profile = Field(Profile.production, env="SAFIR_PROFILE") + + log_level: LogLevel = Field(LogLevel.INFO, env="SAFIR_LOG_LEVEL") + + logger_name: str = Field("protorubinportal", env="SAFIR_LOGGER") + + +config = Config() +"""Configuration for noteburst.""" diff --git a/src/protorubinportal/handlers/__init__.py b/src/protorubinportal/handlers/__init__.py new file mode 100644 index 0000000..b89a48f --- /dev/null +++ b/src/protorubinportal/handlers/__init__.py @@ -0,0 +1,4 @@ +__all__ = ["router"] + +from .homepage import * # noqa: F401 F403 +from .router import router diff --git a/src/protorubinportal/handlers/homepage.py b/src/protorubinportal/handlers/homepage.py new file mode 100644 index 0000000..9ef01d1 --- /dev/null +++ b/src/protorubinportal/handlers/homepage.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from pathlib import Path + +from fastapi import Depends +from safir.dependencies.logger import logger_dependency +from starlette.requests import Request +from structlog.stdlib import BoundLogger +from starlette.templating import Jinja2Templates, _TemplateResponse + +from .router import router + +templates = Jinja2Templates(directory=Path(__file__).parent / 'templates') + + +@router.get( + "/", + description="The app homepage", + summary="Homepage", +) +async def get_index( + request: Request, + logger: BoundLogger = Depends(logger_dependency), +) -> _TemplateResponse: + """The portal's homepage.""" + return templates.TemplateResponse('homepage.jinja', {'request': request}) diff --git a/src/protorubinportal/handlers/router.py b/src/protorubinportal/handlers/router.py new file mode 100644 index 0000000..095ee45 --- /dev/null +++ b/src/protorubinportal/handlers/router.py @@ -0,0 +1,6 @@ +from fastapi import APIRouter + +__all__ = ["router"] + +router = APIRouter() +"""FastAPI router for all external handlers.""" diff --git a/src/protorubinportal/handlers/templates/base.jinja b/src/protorubinportal/handlers/templates/base.jinja new file mode 100644 index 0000000..48b8c9a --- /dev/null +++ b/src/protorubinportal/handlers/templates/base.jinja @@ -0,0 +1,13 @@ + + + + + Hello world + + + + + + {% block body %}{% endblock %} + + diff --git a/src/protorubinportal/handlers/templates/homepage.jinja b/src/protorubinportal/handlers/templates/homepage.jinja new file mode 100644 index 0000000..ae0bad9 --- /dev/null +++ b/src/protorubinportal/handlers/templates/homepage.jinja @@ -0,0 +1,5 @@ +{% extends "base.jinja" %} + +{% block body %} +

Hello world!

+{% endblock %} diff --git a/src/protorubinportal/main.py b/src/protorubinportal/main.py new file mode 100644 index 0000000..5ec68b5 --- /dev/null +++ b/src/protorubinportal/main.py @@ -0,0 +1,43 @@ +"""The main application factory for the protorubinportal service. + +Notes +----- +Be aware that, following the normal pattern for FastAPI services, the app is +constructed when this module is loaded and is not deferred until a function is +called. +""" + +from fastapi import FastAPI +from safir.dependencies.http_client import http_client_dependency +from safir.logging import configure_logging +from safir.middleware.x_forwarded import XForwardedMiddleware + +from .config import config +from .handlers.router import router + +__all__ = ["app", "config"] + + +configure_logging( + profile=config.profile, + log_level=config.log_level, + name=config.logger_name, +) + +app = FastAPI( + docs_url="/_docs", + redoc_url=None +) +"""The main FastAPI application for protorubinportal.""" + +app.include_router(router) + + +@app.on_event("startup") +async def startup_event() -> None: + app.add_middleware(XForwardedMiddleware) + + +@app.on_event("shutdown") +async def shutdown_event() -> None: + await http_client_dependency.aclose() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..a84f875 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,34 @@ +"""Test fixtures for protorubinportal tests.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest +from asgi_lifespan import LifespanManager +from httpx import AsyncClient + +from protorubinportal import main + +if TYPE_CHECKING: + from typing import AsyncIterator + + from fastapi import FastAPI + + +@pytest.fixture +async def app() -> AsyncIterator[FastAPI]: + """Return a configured test application. + + Wraps the application in a lifespan manager so that startup and shutdown + events are sent during test execution. + """ + async with LifespanManager(main.app): + yield main.app + + +@pytest.fixture +async def client(app: FastAPI) -> AsyncIterator[AsyncClient]: + """Return an ``httpx.AsyncClient`` configured to talk to the test app.""" + async with AsyncClient(app=app, base_url="https://example.com/") as client: + yield client diff --git a/tests/handlers/__init__.py b/tests/handlers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/handlers/homepage_test.py b/tests/handlers/homepage_test.py new file mode 100644 index 0000000..5066982 --- /dev/null +++ b/tests/handlers/homepage_test.py @@ -0,0 +1,17 @@ +"""Tests for the protorubinportal.handlers.external module and routes.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +if TYPE_CHECKING: + from httpx import AsyncClient + + +@pytest.mark.asyncio +async def test_get_index(client: AsyncClient) -> None: + """Test ``GET /``""" + response = await client.get("/") + assert response.status_code == 200 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..f47c750 --- /dev/null +++ b/tox.ini @@ -0,0 +1,36 @@ +[tox] +envlist = py,coverage-report,typing,lint +isolated_build = True + +[testenv] +description = Run pytest against {envname}. +deps = + -r{toxinidir}/requirements/main.txt + -r{toxinidir}/requirements/dev.txt +commands = + pytest --cov=protorubinportal --cov-branch --cov-report= {posargs} + +[testenv:coverage-report] +description = Compile coverage from each test run. +skip_install = true +deps = coverage[toml]>=5.0.2 +depends = + py +commands = coverage report + +[testenv:typing] +description = Run mypy. +commands = + mypy src/protorubinportal tests setup.py + +[testenv:lint] +description = Lint codebase by running pre-commit (Black, isort, Flake8). +skip_install = true +deps = + pre-commit +commands = pre-commit run --all-files + +[testenv:run] +description = Run the development server with auto-reload for code changes. +usedevelop = true +commands = uvicorn protorubinportal.main:app --reload