Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .bandit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
exclude_dirs:
- tests
- .venv
- .git
- __pycache__
- .pytest_cache
19 changes: 18 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ __pycache__/
.pytest_cache/
.vscode/

# pytest
.pytest_cache/
.cache/

# coverage
.coverage
.coverage.*
htmlcov/
coverage.xml
*.cover
*.py,cover

# test logs
pytest.xml
test-results/
*.log

# Git
.git
.gitignore
Expand All @@ -35,4 +52,4 @@ env/

# OS-specific
*.DS_Store
Thumbs.db
Thumbs.db
113 changes: 113 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: CI

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install Poetry
run: |
pip install poetry
poetry config virtualenvs.create false

- name: Install dependencies
run: poetry install --with dev

- name: Lint with ruff
run: poetry run ruff check .

- name: Type check with mypy
run: poetry run mypy app/

- name: Run tests with pytest
run: |
poetry run pytest \
--cov=app \
--cov-report=xml \
--cov-report=html \
--junitxml=pytest.xml \
-v

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
fail_ci_if_error: false

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.python-version }}
path: |
pytest.xml
coverage.xml
htmlcov/

security:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4

- name: Run security scan
run: |
pip install bandit safety

echo "=== Checking .bandit.yml ==="
cat .bandit.yml

echo "=== Running Bandit (txt output for logs) ==="
bandit -c .bandit.yml -r . -f txt || true

echo "=== Creating JSON report ==="
bandit -c .bandit.yml -r . -f json -o bandit-report.json || true

echo "=== Running Safety check ==="
safety check --json > safety-report.json || true

- name: Upload security reports
if: always()
uses: actions/upload-artifact@v4
with:
name: security-reports
path: |
bandit-report.json
safety-report.json
retention-days: 7

build:
needs: [test, security]
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

steps:
- uses: actions/checkout@v4

- name: Build package
run: |
pip install poetry
poetry build

- name: Upload package artifact
uses: actions/upload-artifact@v4
with:
name: python-package
path: dist/
19 changes: 18 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ credentials/
*.p12
*.pem

# pytest
.pytest_cache/
.cache/

# coverage
.coverage
.coverage.*
htmlcov/
coverage.xml
*.cover
*.py,cover

# test logs
pytest.xml
test-results/
*.log

# Python
__pycache__/
.mypy_cache
Expand Down Expand Up @@ -59,4 +76,4 @@ Thumbs.db

# Logs
*.log
logs/
logs/
61 changes: 61 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
image: python:3.11

stages:
- test
- security
- build

variables:
POETRY_VERSION: "1.8.0"
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"

cache:
paths:
- .cache/pip
- .venv/

before_script:
- python --version
- pip install poetry==$POETRY_VERSION
- poetry config virtualenvs.create false
- poetry install --with dev

test:
stage: test
script:
- poetry run ruff check .
- poetry run mypy app/
- poetry run pytest --cov=app --cov-report=xml --junitxml=pytest.xml -v
artifacts:
reports:
junit: pytest.xml
coverage_report:
coverage_format: cobertura
path: coverage.xml
paths:
- htmlcov/
expire_in: 1 week
coverage: '/TOTAL.*\s+(\d+%)$/'

security:
stage: security
script:
- pip install bandit safety
- bandit -r . -c .bandit.yml -f json -o bandit-report.json
- safety check --json > safety-report.json
artifacts:
paths:
- bandit-report.json
- safety-report.json
expire_in: 1 week

build:
stage: build
only:
- main
script:
- poetry build
artifacts:
paths:
- dist/
expire_in: 1 week
26 changes: 26 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.13
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.19.1
hooks:
- id: mypy
args: [--ignore-missing-imports]

- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks:
- id: detect-secrets
6 changes: 6 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from importlib.metadata import metadata

pkg_metadata = metadata("deribit-tracker").json
__version__ = str(pkg_metadata.get("version", "Unversioned"))
__description__ = str(pkg_metadata.get("summary", "No description"))
__title__ = str(pkg_metadata.get("name", "Unnamed")).replace("-", " ").title()
32 changes: 32 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app import (
__description__,
__title__,
__version__,
)

app = FastAPI(
title=__title__,
version=__version__,
description=__description__,
)

app.add_middleware(
CORSMiddleware,
allow_origins=["http://127.0.0.1:8000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)


@app.get("/")
async def root():
return {"message": "root"}


@app.get("/health")
async def health_check():
return {"status": "healthy"}
Loading