Adding the sha based version pinnig

This commit is contained in:
Ketan Patel committed 2026-06-03 21:21:06 -04:00
1 parent cf3e12e9d2
commit 34d8b5a299
11 files changed
+2266 -55

No files matched your search

+11 -6
View File
@@ -18,14 +18,13 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: '3.12' python-version: '3.14'
cache: pip cache: pip
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install -r requirements.txt pip install --require-hashes -r requirements.dev.txt
pip install coverage
- name: Ruff lint - name: Ruff lint
run: ruff check . run: ruff check .
@@ -43,6 +42,13 @@ jobs:
contents: read contents: read
packages: write packages: write
id-token: write id-token: write
strategy:
matrix:
include:
- image_tag: ghcr.io/k2patel/apcupsd-client:latest
melange_config: melange.yaml
- image_tag: ghcr.io/k2patel/apcupsd-client-dev:latest
melange_config: melange.dev.yaml
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -55,7 +61,7 @@ jobs:
- name: Build package with melange - name: Build package with melange
run: | run: |
melange build melange.yaml \ melange build ${{ matrix.melange_config }} \
--signing-key melange.rsa \ --signing-key melange.rsa \
--arch x86_64,aarch64 --arch x86_64,aarch64
@@ -71,6 +77,5 @@ jobs:
with: with:
config: apko.yaml config: apko.yaml
archs: x86_64,aarch64 archs: x86_64,aarch64
tag: ghcr.io/k2patel/apcupsd-client:latest tag: ${{ matrix.image_tag }}
keyring-append: melange.rsa.pub keyring-append: melange.rsa.pub
+10 -6
View File
@@ -1,7 +1,10 @@
# syntax=docker/dockerfile:1.6 # syntax=docker/dockerfile:1.6
# NOTE: The primary build method is now melange + apko (see melange.yaml / apko.yaml). # NOTE: The primary build method is now melange + apko (see melange.yaml / apko.yaml).
# This Dockerfile is kept for local dev and Docker Compose backward compatibility. # This Dockerfile is kept for local dev and Docker Compose backward compatibility.
FROM python:3.12.7-slim AS builder ARG PYTHON_IMAGE=python:3.14.5-slim@sha256:c845af9399020c7e562969a13689e929074a10fd057acd1b1fad06a2fb068e97
ARG REQUIREMENTS_FILE=requirements.txt
FROM ${PYTHON_IMAGE} AS builder
ARG REQUIREMENTS_FILE
ENV PYTHONDONTWRITEBYTECODE=1 \ ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \ PYTHONUNBUFFERED=1 \
@@ -13,11 +16,12 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential gcc \ && apt-get install -y --no-install-recommends build-essential gcc \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY requirements.txt ./ COPY ${REQUIREMENTS_FILE} requirements.txt
RUN pip install --upgrade pip && pip wheel --wheel-dir /wheels -r requirements.txt RUN pip install --upgrade pip && pip wheel --require-hashes --wheel-dir /wheels -r requirements.txt
FROM python:3.12.7-slim AS runtime FROM ${PYTHON_IMAGE} AS runtime
ARG REQUIREMENTS_FILE
ENV PYTHONDONTWRITEBYTECODE=1 \ ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \ PYTHONUNBUFFERED=1 \
@@ -33,8 +37,8 @@ RUN apt-get update \
&& useradd --system --uid 10001 --gid 10001 --home /app --shell /usr/sbin/nologin appuser && useradd --system --uid 10001 --gid 10001 --home /app --shell /usr/sbin/nologin appuser
COPY --from=builder /wheels /wheels COPY --from=builder /wheels /wheels
COPY requirements.txt ./ COPY ${REQUIREMENTS_FILE} requirements.txt
RUN pip install --no-index --find-links=/wheels -r requirements.txt \ RUN pip install --no-index --find-links=/wheels --require-hashes -r requirements.txt \
&& rm -rf /wheels && rm -rf /wheels
COPY app ./app COPY app ./app
+6 -3
View File
@@ -23,7 +23,7 @@ A production-ready FastAPI + Redis dashboard for monitoring multiple APC UPS dev
- SSRF host validation (rejects loopback/link-local; private IPs gated by `ALLOW_PRIVATE_IPS`) - SSRF host validation (rejects loopback/link-local; private IPs gated by `ALLOW_PRIVATE_IPS`)
- SMTP password **only** from env — never persisted to Redis - SMTP password **only** from env — never persisted to Redis
- Subprocess timeout on `apcaccess` (10s), rate-limiting on auth/config, security headers + CSP - Subprocess timeout on `apcaccess` (10s), rate-limiting on auth/config, security headers + CSP
- Non-root container (UID 10001), pinned `python:3.12.7-slim` multi-stage build - Non-root container (UID 10001), digest-pinned `python:3.14.5-slim` multi-stage build
- `/healthz`, `/readyz`, `/metrics` (Prometheus), JSON structured logs with request-ID correlation - `/healthz`, `/readyz`, `/metrics` (Prometheus), JSON structured logs with request-ID correlation
- GitHub Actions pipeline runs ruff + pytest before building/publishing the image - GitHub Actions pipeline runs ruff + pytest before building/publishing the image
@@ -82,9 +82,12 @@ Your remote APC UPS hosts must run `apcupsd` with the Network Information Server
## Development ## Development
Production Docker builds install `requirements.txt`; local development and CI use
`requirements.dev.txt`.
```bash ```bash
python3.12 -m venv .venv && . .venv/bin/activate python3.14 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt pip install -r requirements.dev.txt
# Run tests (uses fakeredis) # Run tests (uses fakeredis)
pytest tests/ pytest tests/
+70
View File
@@ -0,0 +1,70 @@
package:
name: apcupsd-client-build
version: 1.0.0
epoch: 0
description: Build stage for apcupsd-client dev image (not installed directly)
copyright:
- license: MIT
environment:
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.21/main
- https://dl-cdn.alpinelinux.org/alpine/v3.21/community
packages:
- alpine-baselayout
- busybox
- python3
- py3-pip
- py3-virtualenv
pipeline:
- name: Create virtualenv and install dependencies
runs: |
set -ex
mkdir -p "${{targets.destdir}}/app"
python3 -m virtualenv "${{targets.destdir}}/app/.venv"
"${{targets.destdir}}/app/.venv/bin/pip" install \
--no-cache-dir \
--only-binary :all: \
--require-hashes \
-r requirements.dev.txt
- name: Copy application source
runs: |
set -ex
cp -r app/ "${{targets.destdir}}/app/app/"
- name: Fix virtualenv shebangs
runs: |
set -ex
find "${{targets.destdir}}/app/.venv/bin" -type f -exec \
sed -i "s|${{targets.destdir}}||g" {} +
- name: Strip build artifacts from virtualenv
runs: |
set -ex
rm -rf "${{targets.destdir}}/app/.venv/bin/pip"*
rm -rf "${{targets.destdir}}/app/.venv/bin/wheel"*
rm -rf "${{targets.destdir}}/app/.venv/lib/python*/site-packages/pip"
rm -rf "${{targets.destdir}}/app/.venv/lib/python*/site-packages/pip-*"
rm -rf "${{targets.destdir}}/app/.venv/lib/python*/site-packages/setuptools"
rm -rf "${{targets.destdir}}/app/.venv/lib/python*/site-packages/setuptools-*"
rm -rf "${{targets.destdir}}/app/.venv/lib/python*/site-packages/wheel"
rm -rf "${{targets.destdir}}/app/.venv/lib/python*/site-packages/wheel-*"
find "${{targets.destdir}}/app/.venv" -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
find "${{targets.destdir}}/app/.venv" -name "*.pyc" -delete 2>/dev/null || true
subpackages:
- name: apcupsd-client
description: FastAPI + Redis UPS monitoring dashboard with dev dependencies
options:
no-depends: true
dependencies:
runtime:
- python3
pipeline:
- runs: |
set -ex
mkdir -p "${{targets.subpkgdir}}"
mv "${{targets.destdir}}/app" "${{targets.subpkgdir}}/app"
+2 -1
View File
@@ -27,7 +27,8 @@ pipeline:
"${{targets.destdir}}/app/.venv/bin/pip" install \ "${{targets.destdir}}/app/.venv/bin/pip" install \
--no-cache-dir \ --no-cache-dir \
--only-binary :all: \ --only-binary :all: \
-r requirements.prod.txt --require-hashes \
-r requirements.txt
- name: Copy application source - name: Copy application source
runs: | runs: |
+2 -2
View File
@@ -2,7 +2,7 @@
name = "apcupsd-client" name = "apcupsd-client"
version = "0.2.0" version = "0.2.0"
description = "FastAPI + Redis dashboard for monitoring multiple APC UPS devices via apcupsd NIS" description = "FastAPI + Redis dashboard for monitoring multiple APC UPS devices via apcupsd NIS"
requires-python = ">=3.12" requires-python = ">=3.14"
readme = "README.md" readme = "README.md"
license = { text = "MIT" } license = { text = "MIT" }
@@ -17,7 +17,7 @@ filterwarnings = [
[tool.ruff] [tool.ruff]
line-length = 100 line-length = 100
target-version = "py312" target-version = "py314"
extend-exclude = [".venv", "app/static"] extend-exclude = [".venv", "app/static"]
[tool.ruff.lint] [tool.ruff.lint]
+9
View File
@@ -0,0 +1,9 @@
-r requirements.in
# Dev / test dependencies
coverage
pytest
pytest-asyncio
httpx
fakeredis
ruff
+1161
View File
File diff suppressed because it is too large. Load diff
+15
View File
@@ -0,0 +1,15 @@
fastapi
uvicorn[standard]
pyyaml
redis
pydantic
pydantic-settings
jinja2
python-multipart
orjson
passlib[argon2]
itsdangerous
slowapi
python-json-logger
tenacity
prometheus-client
-15
View File
@@ -1,15 +0,0 @@
fastapi==0.115.0
uvicorn[standard]==0.30.6
pyyaml==6.0.2
redis==5.0.7
pydantic==2.9.2
pydantic-settings==2.5.2
jinja2==3.1.4
python-multipart==0.0.9
orjson==3.10.7
passlib[argon2]==1.7.4
itsdangerous==2.2.0
slowapi==0.1.9
python-json-logger==2.0.7
tenacity==9.0.0
prometheus-client==0.21.0
+980 -22
View File
File diff suppressed because it is too large. Load diff