major: added initial version
This commit is contained in:
121
.github/workflows/ci.yml
vendored
Normal file
121
.github/workflows/ci.yml
vendored
Normal 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
28
.gitignore
vendored
@@ -55,16 +55,6 @@ cover/
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
@@ -77,6 +67,7 @@ target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
.virtual_documents
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
@@ -106,10 +97,8 @@ ipython_config.py
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
@@ -155,8 +144,11 @@ dmypy.json
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# 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
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
.idea/
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
|
||||
*.log
|
||||
files/
|
||||
DOCKER/
|
||||
|
||||
35
.vscode/launch.json
vendored
Normal file
35
.vscode/launch.json
vendored
Normal 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
42
.vscode/settings.json
vendored
Normal 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
25
Dockerfile
Normal 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
|
||||
@@ -1,2 +1,3 @@
|
||||
# pong
|
||||
|
||||
A really simple FastAPI service to return the request with code and content specified in the parameters.
|
||||
|
||||
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
55
app/main.py
Normal file
55
app/main.py
Normal 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
42
app/test_main.py
Normal 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
5
pytest.ini
Normal file
@@ -0,0 +1,5 @@
|
||||
[pytest]
|
||||
filterwarnings =
|
||||
default:::app.*
|
||||
ignore
|
||||
addopts = -sl
|
||||
15
requirements.txt
Normal file
15
requirements.txt
Normal 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
|
||||
Reference in New Issue
Block a user