Skip to content

Commit d4e42ad

Browse files
morgangraphicsCopilotCopilot
authored
Reworked entire service t (#19)
* Reworked entire service to FastAPI, uv, dependency updates, Makefile, Dockerfile, tests, the entire works * newline * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * issues with unused imports, documentation, codacy false positives * Update service/models.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update service/routes/marvel.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update service/routes/dc.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update service/routes/marvel.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update service/routes/dc.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Initial plan (#20) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * [WIP] [WIP] Address feedback on service rework implementation (#21) * Initial plan * Fix POST description in dc.py to use 'characters' instead of 'character' Co-authored-by: morgangraphics <607594+morgangraphics@users.noreply.github.com> Agent-Logs-Url: https://github.com/morgangraphics/simple-superhero-service-python/sessions/adfde50c-baa8-4475-92be-57a754fa9768 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: morgangraphics <607594+morgangraphics@users.noreply.github.com> * Codacy linting issues * Codacy linting updates * Codacy issues --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: morgangraphics <607594+morgangraphics@users.noreply.github.com>
1 parent eafea3f commit d4e42ad

43 files changed

Lines changed: 2108 additions & 658 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.git
2+
.venv
3+
*.pem
4+
htmlcov
5+
coverage
6+
.github
7+
.circleci
8+
img
9+
unit
10+
tests
11+
config/.env
12+
__pycache__
13+
*.pyc
14+
*.egg-info
15+
dist
16+
build
17+
simple-super-hero-service-upgrade.md

.flaskenv

Lines changed: 0 additions & 7 deletions
This file was deleted.

.github/workflows/codacy-analysis.yml

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,35 @@ on:
1313
branches: [ "master", "main" ]
1414
pull_request:
1515
branches: [ "master", "main" ]
16+
workflow_dispatch:
1617

1718
jobs:
1819
codacy-security-scan:
1920
name: Codacy Security Scan
2021
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
security-events: write
25+
env:
26+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
2127
steps:
2228
# Checkout the repository to the GitHub Actions runner
2329
- name: Checkout code
24-
uses: actions/checkout@v2
25-
30+
uses: actions/checkout@v4
31+
with:
32+
submodules: recursive
33+
token: ${{ secrets.GITHUB_TOKEN }}
34+
35+
# Generate temporary self-signed TLS certificates required by the service
36+
- name: Generate temporary TLS certificates
37+
run: |
38+
openssl req -x509 -newkey rsa:4096 -keyout sss-key.pem -out sss-cert.pem \
39+
-sha256 -days 1 -nodes \
40+
-subj "/C=US/ST=CI/L=CI/O=CI/CN=localhost"
41+
2642
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
2743
- name: Run Codacy Analysis CLI
28-
uses: codacy/codacy-analysis-cli-action@1.0.0
44+
uses: codacy/codacy-analysis-cli-action@v4
2945
with:
3046
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
3147
# You can also omit the token and run the tools that support default configurations
@@ -37,8 +53,24 @@ jobs:
3753
# This will handover control about PR rejection to the GitHub side
3854
max-allowed-issues: 2147483647
3955

56+
# Split the multi-run SARIF into individual per-tool files to satisfy the
57+
# requirement that each category upload contains only a single run.
58+
# See: https://github.blog/changelog/2025-07-21-code-scanning-will-stop-combining-multiple-sarif-runs-uploaded-in-the-same-sarif-file/
59+
- name: Split SARIF results into individual tool files
60+
run: |
61+
mkdir -p sarif-runs
62+
count=$(jq '.runs | length' results.sarif)
63+
for i in $(seq 0 $((count - 1))); do
64+
tool=$(jq -r ".runs[$i].tool.driver.name" results.sarif \
65+
| tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g')
66+
jq --argjson i "$i" \
67+
'{"version": .version, "$schema": .["$schema"], "runs": [.runs[$i]]}' \
68+
results.sarif > "sarif-runs/${tool}.sarif"
69+
done
70+
4071
# Upload the SARIF file generated in the previous step
4172
- name: Upload SARIF results file
42-
uses: github/codeql-action/upload-sarif@v1
73+
uses: github/codeql-action/upload-sarif@v4
4374
with:
44-
sarif_file: results.sarif
75+
sarif_file: sarif-runs/
76+
category: codacy

.github/workflows/codeql-analysis.yml

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,65 +7,45 @@ name: "CodeQL"
77

88
on:
99
push:
10-
branches: [master]
10+
branches: [ "master", "main" ]
1111
pull_request:
12-
# The branches below must be a subset of the branches above
13-
branches: [master]
12+
branches: [ "master", "main" ]
1413
schedule:
1514
- cron: '0 20 * * 0'
1615

1716
jobs:
1817
analyze:
1918
name: Analyze
2019
runs-on: ubuntu-latest
20+
permissions:
21+
actions: read
22+
contents: read
23+
security-events: write
2124

2225
strategy:
2326
fail-fast: false
2427
matrix:
25-
# Override automatic language detection by changing the below list
26-
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
27-
language: ['python']
28-
# Learn more...
29-
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
28+
# Supported options: https://aka.ms/codeql-docs/language-support
29+
language: [ 'python' ]
3030

3131
steps:
32-
- name: Checkout repository
33-
uses: actions/checkout@v2
34-
with:
35-
# We must fetch at least the immediate parents so that if this is
36-
# a pull request then we can checkout the head.
37-
fetch-depth: 2
38-
39-
# If this run was triggered by a pull request event, then checkout
40-
# the head of the pull request instead of the merge commit.
41-
- run: git checkout HEAD^2
42-
if: ${{ github.event_name == 'pull_request' }}
43-
44-
# Initializes the CodeQL tools for scanning.
45-
- name: Initialize CodeQL
46-
uses: github/codeql-action/init@v1
47-
with:
48-
languages: ${{ matrix.language }}
49-
# If you wish to specify custom queries, you can do so here or in a config file.
50-
# By default, queries listed here will override any specified in a config file.
51-
# Prefix the list here with "+" to use these queries and those in the config file.
52-
# queries: ./path/to/local/query, your-org/your-repo/queries@main
53-
54-
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
55-
# If this step fails, then you should remove it and run the build manually (see below)
56-
- name: Autobuild
57-
uses: github/codeql-action/autobuild@v1
58-
59-
# ℹ️ Command-line programs to run using the OS shell.
60-
# 📚 https://git.io/JvXDl
61-
62-
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
63-
# and modify them (or add more) to build your code if your project
64-
# uses a compiled language
65-
66-
#- run: |
67-
# make bootstrap
68-
# make release
69-
70-
- name: Perform CodeQL Analysis
71-
uses: github/codeql-action/analyze@v1
32+
- name: Checkout repository
33+
uses: actions/checkout@v4
34+
35+
# Initializes the CodeQL tools for scanning.
36+
- name: Initialize CodeQL
37+
uses: github/codeql-action/init@v3
38+
with:
39+
languages: ${{ matrix.language }}
40+
# If you wish to specify custom queries, you can do so here or in a config file.
41+
# queries: ./path/to/local/query, your-org/your-repo/queries@main
42+
43+
# Python is an interpreted language so Autobuild is a no-op, but keeping
44+
# it here means the workflow works if compiled languages are added later.
45+
- name: Autobuild
46+
uses: github/codeql-action/autobuild@v3
47+
48+
- name: Perform CodeQL Analysis
49+
uses: github/codeql-action/analyze@v3
50+
with:
51+
category: "/language:${{ matrix.language }}"

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
venv/
2+
.venv/
23
.idea/
34

5+
# Environment / secrets
6+
.env
7+
config/.env
8+
49
*.pyc
510
__pycache__/
611

.pylintrc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[MASTER]
2+
# Support both old pylint ([MASTER]) and new pylint ([MAIN]) section names.
3+
py-version = 3.12
4+
5+
[MAIN]
6+
py-version = 3.12
7+
8+
[BASIC]
9+
# h and s are intentional short names that map directly to API query parameters.
10+
# bp_* are idiomatic FastAPI APIRouter instance names, not constants.
11+
good-names = h,s,e,i,j,k,_,bp_dc,bp_hc,bp_marvel
12+
13+
[DESIGN]
14+
# _build_options and route handlers mirror the full set of API query parameters.
15+
max-args = 15
16+
max-positional-arguments = 15
17+
18+
[MESSAGES CONTROL]
19+
# redefined-builtin: 'format' and 'help' shadow builtins intentionally — they
20+
# are public API query-parameter names that cannot be renamed.
21+
# line-too-long: not enforced in this project.
22+
# missing-module-docstring: modules use file-level docstrings only where needed.
23+
# E0611 (no-name-in-module): false positive — Annotated/Literal exist in
24+
# typing since Python 3.8/3.9; old pylint doesn't know this.
25+
# E1136 (unsubscriptable-object): false positive — list[x]/dict[x] are valid
26+
# in Python 3.9+; old pylint doesn't know this.
27+
# C0326 (bad-whitespace): false positive — PEP 8 requires spaces around '='
28+
# for annotated function parameters; old pylint flags these incorrectly.
29+
# C0330 (wrong-hanging-indentation): removed in pylint 2.6+ but triggers as a
30+
# false positive in older Codacy pylint builds.
31+
disable = redefined-builtin,
32+
line-too-long,
33+
missing-module-docstring,
34+
no-name-in-module,
35+
unsubscriptable-object,
36+
bad-whitespace,
37+
wrong-hanging-indentation

.remarkrc.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
plugins:
2+
- remark-lint
3+
- - remark-lint-ordered-list-marker-value
4+
- false

Dockerfile

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# ARG before FROM parameterises the base image tag; override with --build-arg PYTHON=3.13
2+
ARG PYTHON=3.12
3+
4+
FROM python:${PYTHON}-slim
5+
LABEL maintainer="MORGANGRAPHICS,INC"
6+
7+
ARG PORT=8000
8+
9+
# Install curl (HEALTHCHECK) and dumb-init (PID 1 / signal forwarding).
10+
# Clean up apt cache so it is not stored in the layer.
11+
RUN apt-get update -y \
12+
&& apt-get upgrade -y \
13+
&& apt-get install -y --no-install-recommends curl dumb-init \
14+
&& rm -rf /var/lib/apt/lists/*
15+
16+
# Install uv for fast, reproducible dependency installation.
17+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
18+
19+
# The official python image ships with a non-root user we create explicitly.
20+
RUN groupadd --gid 1000 appuser && useradd --uid 1000 --gid appuser --shell /bin/bash --create-home appuser
21+
22+
USER appuser
23+
24+
ENV PORT=${PORT}
25+
# Keep Python from writing .pyc files and buffering stdout/stderr
26+
ENV PYTHONDONTWRITEBYTECODE=1
27+
ENV PYTHONUNBUFFERED=1
28+
# Tell uv to install into the system Python inside the container
29+
ENV UV_SYSTEM_PYTHON=1
30+
# TLS certificate paths — leave empty for plain HTTP, set both for HTTPS
31+
ENV SSL_CERT=""
32+
ENV SSL_KEY=""
33+
34+
WORKDIR /home/appuser/service
35+
36+
# Copy dependency manifests first so the install layer is only invalidated
37+
# when dependencies change, not on every source file change.
38+
COPY --chown=appuser:appuser pyproject.toml uv.lock* ./
39+
40+
RUN uv sync --no-dev --frozen
41+
42+
# NOTE: sssp-cert.pem and sssp-key.pem are excluded via .dockerignore and must
43+
# be mounted at runtime, e.g.:
44+
# docker run -v /path/to/certs:/home/appuser/service ...
45+
COPY --chown=appuser:appuser . .
46+
47+
EXPOSE ${PORT}
48+
49+
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
50+
CMD if [ -n "$SSL_CERT" ] && [ -n "$SSL_KEY" ]; then \
51+
curl -fsk https://localhost:${PORT}/healthcheck; \
52+
else \
53+
curl -fs http://localhost:${PORT}/healthcheck; \
54+
fi || exit 1
55+
56+
# https://github.com/Yelp/dumb-init#usage
57+
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
58+
CMD ["python", "main.py"]

Makefile

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
.DEFAULT_GOAL := help
2+
3+
# ─── Virtual environment ──────────────────────────────────────────────────────
4+
5+
.venv:
6+
uv venv .venv --python 3.12
7+
8+
install: .venv ## Install all dependencies (including dev)
9+
uv sync --extra dev
10+
11+
# ─── Service ──────────────────────────────────────────────────────────────────
12+
13+
start: ## Start the service (foreground)
14+
uv run python main.py
15+
16+
dev: ## Start the service with auto-reload
17+
uv run uvicorn service:create_app --factory --reload --host 127.0.0.1 --port 8000
18+
19+
stop: ## Stop any background uvicorn process
20+
@pkill -f "uvicorn service:create_app" && echo "Service stopped." || echo "No running service found."
21+
22+
# ─── Tests ────────────────────────────────────────────────────────────────────
23+
24+
test: ## Run all unit tests
25+
uv run pytest unit/
26+
27+
test-unit: ## Run unit tests (alias)
28+
uv run pytest unit/
29+
30+
test-file: ## Run file utility tests only
31+
uv run pytest unit/test_file.py -s
32+
33+
test-verbose: ## Run unit tests with verbose output
34+
uv run pytest unit/ -v
35+
36+
# ─── Coverage ─────────────────────────────────────────────────────────────────
37+
38+
coverage: ## Run tests with coverage report (terminal)
39+
uv run coverage erase
40+
uv run coverage run -m pytest unit/
41+
uv run coverage report --show-missing
42+
43+
coverage-html: ## Run tests with coverage report (HTML)
44+
uv run coverage erase
45+
uv run coverage run -m pytest unit/
46+
uv run coverage html
47+
@echo "Report available at htmlcov/index.html"
48+
49+
# ─── Code quality ─────────────────────────────────────────────────────────────
50+
51+
lint: ## Run ruff linter
52+
uv run ruff check .
53+
54+
lint-fix: ## Run ruff linter and auto-fix
55+
uv run ruff check . --fix
56+
57+
format: ## Run black formatter
58+
uv run black .
59+
60+
format-check: ## Check formatting without making changes
61+
uv run black . --check
62+
63+
# ─── SSL certs ────────────────────────────────────────────────────────────────
64+
65+
certs: ## Generate self-signed SSL certs for local development
66+
openssl req -x509 -newkey rsa:4096 -keyout sssp-key.pem -out sssp-cert.pem \
67+
-days 365 -nodes -subj "/CN=localhost"
68+
69+
# ─── Docker ───────────────────────────────────────────────────────────────────
70+
71+
IMAGE ?= simple-superhero-service-python
72+
73+
docker-build: ## Build the Docker image
74+
docker build -t $(IMAGE) .
75+
76+
docker-start: ## Run the container (mounts local certs, exposes port 8000)
77+
docker run --rm -p 8000:8000 \
78+
-v $(PWD)/sssp-cert.pem:/home/appuser/service/sssp-cert.pem:ro \
79+
-v $(PWD)/sssp-key.pem:/home/appuser/service/sssp-key.pem:ro \
80+
-v $(PWD)/config/.env:/home/appuser/service/config/.env:ro \
81+
$(IMAGE)
82+
83+
docker-stop: ## Stop the running container
84+
@docker stop $$(docker ps -q --filter ancestor=$(IMAGE)) 2>/dev/null && echo "Container stopped." || echo "No running container found."
85+
86+
docker-shell: ## Open a shell in a new container
87+
docker run --rm -it $(IMAGE) /bin/bash
88+
89+
# ─── Help ─────────────────────────────────────────────────────────────────────
90+
91+
help: ## Show this help message
92+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
93+
| awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
94+
95+
.PHONY: install start dev stop test test-unit test-file test-verbose \
96+
coverage coverage-html lint lint-fix format format-check certs \
97+
docker-build docker-start docker-stop docker-shell help

0 commit comments

Comments
 (0)