Adding the sha based version pinnig
This commit is contained in:
1 parent
cf3e12e9d2
commit
34d8b5a299
11 files changed
+2266
-55
No files matched your search
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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]
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-r requirements.in
|
||||||
|
|
||||||
|
# Dev / test dependencies
|
||||||
|
coverage
|
||||||
|
pytest
|
||||||
|
pytest-asyncio
|
||||||
|
httpx
|
||||||
|
fakeredis
|
||||||
|
ruff
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||||
@@ -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
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user