major: added initial version

This commit is contained in:
2024-10-25 13:40:27 +02:00
parent ad22ed1ee2
commit 085c34a4c7
11 changed files with 351 additions and 18 deletions

121
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,121 @@
name: Lint, Test, Tag, Build and Deploy DEV
on:
push:
branches:
- dev
- master
paths-ignore:
- "**/.github/**"
- "**/.gitignore"
- "**/.vscode/**"
- "**/README.md"
- "**/CHANGELOG.md"
- "**/docs/**"
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: psf/black@stable
with:
options: "-l 100 --exclude '/.venv/|alembic/|/__init__.py'"
- uses: creyD/autoflake_action@master
with:
no_commit: True
options: --in-place --remove-all-unused-imports -r --exclude **/__init__.py,**/db/models.py,
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Adjusted files for isort & autopep
test:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.12
cache: 'pip' # caching pip dependencies
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: pytest
tag:
needs: test
runs-on: ubuntu-latest
permissions:
contents: write # for the tags
outputs:
version: ${{ steps.git_version.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-tags: true
ref: ${{ github.ref }}
fetch-depth: 0
- name: setup git
run: |
git config --local user.email "15138480+creyD@users.noreply.github.com"
git config --local user.name "creyD"
- name: Git Version
uses: PaulHatch/semantic-version@v5.4.0
id: git_version
with:
tag_prefix: ""
major_pattern: "breaking:"
minor_pattern: "feat:"
enable_prerelease_mode: false
version_format: "${major}.${minor}.${patch}-rc${increment}"
- name: Create Tag
run: git tag ${{ steps.git_version.outputs.version }}
- name: Push tag
run: git push origin ${{ steps.git_version.outputs.version }}
build_and_push:
runs-on: ubuntu-latest
permissions: write-all
needs: tag
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-${{ github.ref_name }}
tags: latest
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
build-args: |
VERSION=${{ needs.tag.outputs.version }}-${{ github.ref_name }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

28
.gitignore vendored
View File

@@ -55,16 +55,6 @@ cover/
*.mo *.mo
*.pot *.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff: # Scrapy stuff:
.scrapy .scrapy
@@ -77,6 +67,7 @@ target/
# Jupyter Notebook # Jupyter Notebook
.ipynb_checkpoints .ipynb_checkpoints
.virtual_documents
# IPython # IPython
profile_default/ profile_default/
@@ -106,10 +97,8 @@ ipython_config.py
#pdm.lock #pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control. # in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control # https://pdm.fming.dev/#use-with-ide
.pdm.toml .pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/ __pypackages__/
@@ -155,8 +144,11 @@ dmypy.json
cython_debug/ cython_debug/
# PyCharm # PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can .idea/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear # MacOS
# option (not recommended) you can uncomment the following to ignore the entire idea folder. .DS_Store
#.idea/
*.log
files/
DOCKER/

35
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,35 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": ["app.main:app", "--reload", "--port", "8000"],
"jinja": true,
"justMyCode": true,
"env": {
"PYDEVD_DISABLE_FILE_VALIDATION": "1"
}
},
{
"name": "Migrate",
"type": "debugpy",
"request": "launch",
"module": "alembic",
"args": ["upgrade", "head"],
"jinja": true,
"justMyCode": true
},
{
"name": "Make Migrations",
"type": "debugpy",
"request": "launch",
"module": "alembic",
"args": ["revision", "--autogenerate"],
"jinja": true,
"justMyCode": true
}
]
}

42
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,42 @@
{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python3",
"python.globalModuleInstallation": false,
"python.terminal.activateEnvironment": true,
"python.analysis.typeCheckingMode": "basic",
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "ms-python.black-formatter"
},
"black-formatter.args": ["--line-length", "100"],
"isort.args": ["--profile", "black"],
// Editor General
"files.insertFinalNewline": true,
"editor.fontSize": 15,
"editor.formatOnSave": true,
"editor.rulers": [100],
"editor.minimap.enabled": true,
"files.exclude": {
"**/.git": true,
"**/.pytest_cache": true,
"**/.venv": true,
"**/.svn": true,
"**/.hg": true,
"**/db.sqlite3": true,
"**/.DS_Store": true,
"**/*.pyc": true,
"**/__pycache__/": true
},
"search.exclude": {
"**/.git": true,
"**/.venv": true,
"**/tmp": true,
"htmlcov/*": true,
"docs/*": true,
".venv/*": true
},
"python.testing.pytestArgs": ["app"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}

25
Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
FROM python:3.12-slim
ARG VERSION=unkown
WORKDIR /app
COPY . .
# Python setup
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV VERSION=${VERSION}
# Install dependencies
RUN pip install --no-cache-dir --upgrade -r requirements.txt
RUN pip install 'uvicorn[standard]'
ENV ENV=DEV
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
# Install curl
RUN apt-get update && apt-get install -y curl && apt-get clean
HEALTHCHECK --interval=30s --timeout=10s --retries=5 \
CMD curl --fail http://localhost:8000/openapi.json || exit 1

View File

@@ -1,2 +1,3 @@
# pong # pong
A really simple FastAPI service to return the request with code and content specified in the parameters. A really simple FastAPI service to return the request with code and content specified in the parameters.

0
app/__init__.py Normal file
View File

55
app/main.py Normal file
View File

@@ -0,0 +1,55 @@
import os
from fastapi import APIRouter
from fastapi import Response
from creyPY.fastapi.app import generate_unique_id
from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
load_dotenv()
ENV = os.getenv("ENV", "local").lower()
VERSION = os.getenv("VERSION", "Alpha")
# App Setup
app = FastAPI(
title="ServerCrow Pong API",
description="A really simple FastAPI service to return the request with code and content specified in the parameters. No logging, no nothing.",
version=VERSION,
docs_url="/",
redoc_url=None,
debug=ENV != "prod",
swagger_ui_parameters={
"docExpansion": "list",
"displayOperationId": True,
"defaultModelsExpandDepth": 5,
"defaultModelExpandDepth": 5,
"filter": True,
"displayRequestDuration": True,
"defaultModelRendering": "model",
"persistAuthorization": True,
},
generate_unique_id_function=generate_unique_id,
)
# CORS Setup
origins = ["http://localhost:5173", "https://pong.servercrow.com"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# App Routers
router = APIRouter(prefix="/pong", tags=["public"])
@router.get("/", operation_id="get_pong")
async def get_status(code: int, response_text: str = "OK") -> Response:
"""Get the ping to your pong. Returns the code that is specified and a response if provided."""
return Response(status_code=code, content=response_text)
app.include_router(router)

42
app/test_main.py Normal file
View File

@@ -0,0 +1,42 @@
from creyPY.fastapi.testing import GenericClient
from .main import app
class TestAPI:
def setup_class(self):
self.c = GenericClient(app)
def test_swagger_gen(self):
re = self.c.get("/openapi.json")
assert re["info"]["title"] == "ServerCrow Pong API"
def test_health_check(self):
self.c.get("/", parse_json=False)
def test_get_pong(self):
re = self.c.get("/pong/?code=200&response_text=OK", parse_json=False)
assert re == b"OK"
def test_get_pong_404(self):
re = self.c.get("/pong/?code=404&response_text=Not Found", parse_json=False, r_code=404)
assert re == b"Not Found"
def test_get_pong_503(self):
re = self.c.get(
"/pong/?code=503&response_text=Service Unavailable", parse_json=False, r_code=503
)
assert re == b"Service Unavailable"
def test_get_pong_500(self):
re = self.c.get(
"/pong/?code=500&response_text=Internal Server Error", parse_json=False, r_code=500
)
assert re == b"Internal Server Error"
def test_get_pong_400(self):
re = self.c.get("/pong/?code=400&response_text=Bad Request", parse_json=False, r_code=400)
assert re == b"Bad Request"
def test_get_pong_401(self):
re = self.c.get("/pong/?code=401&response_text=Unauthorized", parse_json=False, r_code=401)
assert re == b"Unauthorized"

5
pytest.ini Normal file
View File

@@ -0,0 +1,5 @@
[pytest]
filterwarnings =
default:::app.*
ignore
addopts = -sl

15
requirements.txt Normal file
View File

@@ -0,0 +1,15 @@
certifi==2023.11.17 # Testing
iniconfig==2.0.0 # PyTest Testing
packaging==23.2 # PyTest Testing
pluggy==1.3.0 # PyTest Testing
pytest==7.4.3 # PyTest Testing
click==8.1.7 # Uvicorn
httptools==0.6.1 # Uvicorn
pyyaml==6.0.1 # Uvicorn
uvicorn==0.27.0.post1 # Uvicorn
uvloop==0.19.0 # Uvicorn
watchfiles==0.21.0 # Uvicorn
websockets==12.0 # Uvicorn
creyPY==1.3.0 # My own package