Feat: Fügt Konfiguration und automatische Erstellung eines Docker-Containers dieses Projekts hinzu.

This commit is contained in:
2026-08-27 21:05:08 +02:00
parent f6f71f3c20
commit 4af879b2e3
8 changed files with 263 additions and 13 deletions
+7
View File
@@ -0,0 +1,7 @@
target/
!target/release/mirror-package
!target/x86_64-unknown-linux-gnu/release/mirror-package
.git/
.github/
.gitea/
tests/
+31
View File
@@ -181,3 +181,34 @@ jobs:
"${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${filename}" "${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${filename}"
echo "Asset ${filename} erfolgreich hochgeladen." echo "Asset ${filename} erfolgreich hochgeladen."
done done
- name: Build and Publish Docker Container Image (Stable)
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
REPO_OWNER: ${{ gitea.repository_owner || github.repository_owner }}
REPO_NAME: ${{ gitea.repository_name || github.event.repository.name }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
REGISTRY_HOST=$(echo "${GITEA_URL}" | sed -E 's|^https?://||; s|/.*$||')
IMAGE_NAME=$(echo "${REGISTRY_HOST}/${REPO_OWNER}/${REPO_NAME}" | tr '[:upper:]' '[:lower:]')
echo "Authentifiziere an der Container Registry ${REGISTRY_HOST}..."
echo "${TOKEN}" | docker login "${REGISTRY_HOST}" -u "${REPO_OWNER}" --password-stdin
echo "Baue Docker-Container Image (Stable)..."
docker build \
--build-arg TARGET_BIN=target/x86_64-unknown-linux-gnu/release/mirror-package \
-t "${IMAGE_NAME}:latest" \
-t "${IMAGE_NAME}:${VERSION}" \
-t "${IMAGE_NAME}:v${VERSION}" \
-t "${IMAGE_NAME}:${VERSION}.${BUILD_NUMBER}" \
.
echo "Veröffentliche Docker-Images nach ${IMAGE_NAME}..."
docker push "${IMAGE_NAME}:latest"
docker push "${IMAGE_NAME}:${VERSION}"
docker push "${IMAGE_NAME}:v${VERSION}"
docker push "${IMAGE_NAME}:${VERSION}.${BUILD_NUMBER}"
echo "Docker-Images (Stable & latest) erfolgreich veröffentlicht."
+33
View File
@@ -181,3 +181,36 @@ jobs:
"${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${filename}" "${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${filename}"
echo "Asset ${filename} erfolgreich hochgeladen." echo "Asset ${filename} erfolgreich hochgeladen."
done done
- name: Build and Publish Docker Container Image (Testing)
env:
GITEA_URL: ${{ gitea.server_url || github.server_url }}
REPO: ${{ gitea.repository || github.repository }}
REPO_OWNER: ${{ gitea.repository_owner || github.repository_owner }}
REPO_NAME: ${{ gitea.repository_name || github.event.repository.name }}
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.RELEASE_TOKEN || secrets.PUBLISH_TOKEN || secrets.API_TOKEN || secrets.PAT_TOKEN || secrets.CUSTOM_TOKEN || secrets.GITEA_TOKEN || secrets.GITHUB_TOKEN || github.token }}
run: |
VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)"
REGISTRY_HOST=$(echo "${GITEA_URL}" | sed -E 's|^https?://||; s|/.*$||')
IMAGE_NAME=$(echo "${REGISTRY_HOST}/${REPO_OWNER}/${REPO_NAME}" | tr '[:upper:]' '[:lower:]')
echo "Authentifiziere an der Container Registry ${REGISTRY_HOST}..."
echo "${TOKEN}" | docker login "${REGISTRY_HOST}" -u "${REPO_OWNER}" --password-stdin
echo "Baue Docker-Container Image (Testing / Preview)..."
docker build \
--build-arg TARGET_BIN=target/x86_64-unknown-linux-gnu/release/mirror-package \
-t "${IMAGE_NAME}:testing" \
-t "${IMAGE_NAME}:${VERSION}-preview" \
-t "${IMAGE_NAME}:${VERSION}-testing" \
-t "${IMAGE_NAME}:${VERSION}-preview.${BUILD_NUMBER}" \
-t "${IMAGE_NAME}:testing-${BUILD_NUMBER}" \
.
echo "Veröffentliche Docker-Images nach ${IMAGE_NAME}..."
docker push "${IMAGE_NAME}:testing"
docker push "${IMAGE_NAME}:${VERSION}-preview"
docker push "${IMAGE_NAME}:${VERSION}-testing"
docker push "${IMAGE_NAME}:${VERSION}-preview.${BUILD_NUMBER}"
docker push "${IMAGE_NAME}:testing-${BUILD_NUMBER}"
echo "Docker-Images (Testing) erfolgreich veröffentlicht."
+16 -3
View File
@@ -48,6 +48,9 @@ Dieses Dokument dient als technischer Leitfaden und Kontextdokument für KI-Codi
│ ├── config_tests.rs # Tests für Konfigurationsverwaltung & Pfade │ ├── config_tests.rs # Tests für Konfigurationsverwaltung & Pfade
│ ├── gitea_tests.rs # Tests für Gitea Routing & Endpunkte │ ├── gitea_tests.rs # Tests für Gitea Routing & Endpunkte
│ └── github_tests.rs # Tests für Paket-Klassifizierung & Repo-Parsing │ └── github_tests.rs # Tests für Paket-Klassifizierung & Repo-Parsing
├── .dockerignore # Ausschlussregeln für Container-Builds
├── Dockerfile # Minimales & gehärtetes Runtime-Container-Image
├── docker-compose.example.yml # Beispielkonfiguration für Docker Compose
├── Cargo.toml # Projekt-Manifest & Metadaten für deb, rpm und arch ├── Cargo.toml # Projekt-Manifest & Metadaten für deb, rpm und arch
├── LICENSE # Lizenztext ├── LICENSE # Lizenztext
├── README.md # Benutzerdokumentation ├── README.md # Benutzerdokumentation
@@ -77,6 +80,16 @@ Bei Änderungen an Binärnamen, Abhängigkeiten oder Beschreibungen müssen die
- **Generizität**: Die Skripte dürfen keine hardcodierten Anwendungsnamen, spezifischen Abhängigkeiten oder projektspezifischen URLs enthalten. Alle Werte müssen dynamisch aus `Cargo.toml` oder Umgebungsvariablen (`BUILD_NUMBER`, `GITEA_URL`, `REPO`, `TOKEN`) ermittelt werden. - **Generizität**: Die Skripte dürfen keine hardcodierten Anwendungsnamen, spezifischen Abhängigkeiten oder projektspezifischen URLs enthalten. Alle Werte müssen dynamisch aus `Cargo.toml` oder Umgebungsvariablen (`BUILD_NUMBER`, `GITEA_URL`, `REPO`, `TOKEN`) ermittelt werden.
- **Python-Kompatibilität**: Verwende Standard-Python 3 ohne externe PyPI-Abhängigkeiten (nur Standardbibliothek). - **Python-Kompatibilität**: Verwende Standard-Python 3 ohne externe PyPI-Abhängigkeiten (nur Standardbibliothek).
### 3.4 Container-Sicherheit & Persistenz
- **Sicherheitsrichtlinien**:
- Unprivilegierter Benutzer: Der Container läuft unter `appuser` (UID `10001`, GID `10001`).
- Read-Only Rootfs: Kompatibel mit `--read-only` bzw. `read_only: true`.
- Berechtigungen: Alle Linux-Capabilities können gedroppt werden (`--cap-drop=ALL`), `no-new-privileges: true`.
- **Persistente Pfade**:
- `/home/appuser/.config/mirror-package`: Speichert Konfiguration (`config.toml`).
- `/home/appuser/.local/state/mirror-package`: Speichert Logdateien & Status.
- `/tmp`: Temporärer Puffer beim Herunterladen von Paketen (idealerweise via `tmpfs`).
--- ---
## 4. Workflows: Bauen, Testen & Validieren ## 4. Workflows: Bauen, Testen & Validieren
@@ -107,7 +120,7 @@ python3 scripts/package-arch.py --arch x86_64 --pkgrel 1
## 5. CI/CD-Pipeline Details ## 5. CI/CD-Pipeline Details
- **Trigger**: - **Trigger**:
- `push` auf `main`: Baut Binaries für alle 3 Architekturen, baut `.deb`, `.rpm` und `.pkg.tar.zst`, lädt sie in die Gitea Package Registry hoch und erstellt ein Gitea Release `v<VERSION>`. - `push` auf `main`: Baut Binaries für alle 3 Architekturen, baut `.deb`, `.rpm` und `.pkg.tar.zst`, lädt sie in die Gitea Package Registry hoch, erstellt ein Gitea Release `v<VERSION>` und baut/veröffentlicht das Docker-Container-Image mit den Tags `:latest`, `:<VERSION>`, `:v<VERSION>` und `:<VERSION>.<BUILD_NUMBER>`.
- `push` auf `testing`: Baut Binaries & Pakete für den `testing`-Kanal und erstellt ein Pre-Release `v<VERSION>-preview`. - `push` auf `testing`: Baut Binaries & Pakete für den `testing`-Kanal, erstellt ein Pre-Release `v<VERSION>-preview` und baut/veröffentlicht das Docker-Container-Image ausschließlich mit eindeutigen Testing-Tags (`:testing`, `:<VERSION>-preview`, `:<VERSION>-testing`, `:<VERSION>-preview.<BUILD_NUMBER>`, `:testing-<BUILD_NUMBER>`). Der Tag `:latest` ist strikt dem `main`-Workflow vorbehalten.
- **Secrets**: - **Secrets**:
- `PACKAGE_TOKEN` (bzw. Fallback-Token-Namen wie `RELEASE_TOKEN`, `GITEA_TOKEN`) wird für API-Zugriffe auf Gitea Packages und Releases verwendet. - `PACKAGE_TOKEN` (bzw. Fallback-Token-Namen wie `RELEASE_TOKEN`, `GITEA_TOKEN`) wird für API-Zugriffe auf Gitea Packages, Container Registry und Releases verwendet.
+37
View File
@@ -0,0 +1,37 @@
# syntax=docker/dockerfile:1
# Minimal and secure runtime image
FROM debian:bookworm-slim AS runtime
# Install CA certificates and minimal runtime dynamic libraries
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
libssl3 \
zlib1g \
libzstd1 && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/* && \
groupadd -g 10001 appuser && \
useradd -u 10001 -g appuser -d /home/appuser -m -s /usr/sbin/nologin appuser && \
mkdir -p /home/appuser/.config/mirror-package \
/home/appuser/.local/state/mirror-package \
/home/appuser/.local/share/mirror-package \
/etc/mirror-package && \
chown -R appuser:appuser /home/appuser /etc/mirror-package
# Build argument pointing to the prebuilt binary
ARG TARGET_BIN=target/release/mirror-package
COPY ${TARGET_BIN} /usr/local/bin/mirror-package
# Set strict executable permissions
RUN chmod 0755 /usr/local/bin/mirror-package
# Run as unprivileged user
USER 10001:10001
WORKDIR /home/appuser
# Expose volume mount points for configuration and persistent state/logs
VOLUME ["/home/appuser/.config/mirror-package", "/home/appuser/.local/state/mirror-package"]
ENTRYPOINT ["/usr/local/bin/mirror-package"]
CMD ["--help"]
+113 -2
View File
@@ -84,6 +84,114 @@ mirror-package sync --dry-run
--- ---
## Docker & Container Usage
`mirror-package` is packaged and published as an ultra-secure, lightweight container image to the Gitea Container Registry.
### 1. Image Registry & Tagging Strategy
- **Stable Releases (`main` branch)**:
- `<registry>/<owner>/mirror-package:latest`
- `<registry>/<owner>/mirror-package:<version>`
- `<registry>/<owner>/mirror-package:v<version>`
- `<registry>/<owner>/mirror-package:<version>.<build_number>`
- **Testing & Preview Releases (`testing` branch)**:
- `<registry>/<owner>/mirror-package:testing`
- `<registry>/<owner>/mirror-package:<version>-preview`
- `<registry>/<owner>/mirror-package:<version>-testing`
- `<registry>/<owner>/mirror-package:<version>-preview.<build_number>`
- `<registry>/<owner>/mirror-package:testing-<build_number>`
- *(Note: The `:latest` tag is strictly reserved for the stable `main` workflow).*
### 2. Persistent Storage Paths
To persist configurations, tracked repository states, and logs across container restarts, mount the following container paths to host directories or Docker volumes:
| Container Path | Purpose | Recommended Mount Type |
|---|---|---|
| `/home/appuser/.config/mirror-package` | Stores `config.toml` (credentials & repository list) | Host Directory / Volume (rw) |
| `/home/appuser/.local/state/mirror-package` | Persistent application logs & state files | Host Directory / Volume (rw) |
| `/tmp` | Download & stream buffer for package binaries | `tmpfs` (RAM / tempfs, rw, noexec) |
### 3. Security Hardening
The container is built with security as the highest priority:
- **Unprivileged User**: Runs as `appuser` (UID `10001`, GID `10001`), never as `root`.
- **Read-Only Root Filesystem**: Fully operational with `--read-only` / `read_only: true`.
- **No Capabilities**: All Linux capabilities can be safely dropped (`--cap-drop=ALL`).
- **No Privilege Escalation**: Enforces `no-new-privileges:true`.
- **Minimal Image Size**: Based on Debian Bookworm slim, containing only CA certificates and necessary shared libraries (~40 MB).
### 4. Running via Docker CLI
```bash
# Initialize / configure credentials
docker run --rm \
--name mirror-package \
--read-only \
--cap-drop=ALL \
--security-opt=no-new-privileges:true \
--tmpfs /tmp:rw,noexec,nosuid,size=2G \
-v "./config:/home/appuser/.config/mirror-package" \
-v "./state:/home/appuser/.local/state/mirror-package" \
gitea.creative-dragonslayer.de/linuxapps/mirror-package:latest \
config set \
--gitea-url "https://gitea.creative-dragonslayer.de" \
--gitea-token "your_token" \
--registry-owner "Linuxapps"
# Add repositories
docker run --rm \
--read-only \
--cap-drop=ALL \
--security-opt=no-new-privileges:true \
--tmpfs /tmp:rw,noexec,nosuid,size=2G \
-v "./config:/home/appuser/.config/mirror-package" \
-v "./state:/home/appuser/.local/state/mirror-package" \
gitea.creative-dragonslayer.de/linuxapps/mirror-package:latest \
add raspberrypi/rpi-imager
# Run synchronization (can be triggered by a host cronjob or scheduler)
docker run --rm \
--read-only \
--cap-drop=ALL \
--security-opt=no-new-privileges:true \
--tmpfs /tmp:rw,noexec,nosuid,size=2G \
-v "./config:/home/appuser/.config/mirror-package" \
-v "./state:/home/appuser/.local/state/mirror-package" \
gitea.creative-dragonslayer.de/linuxapps/mirror-package:latest \
sync
```
### 5. Docker Compose Example (`docker-compose.yml`)
```yaml
services:
mirror-package:
image: gitea.creative-dragonslayer.de/linuxapps/mirror-package:latest
container_name: mirror-package
user: "10001:10001"
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
volumes:
- ./config:/home/appuser/.config/mirror-package
- ./state:/home/appuser/.local/state/mirror-package
tmpfs:
- /tmp:rw,noexec,nosuid,size=2G
environment:
- GITEA_URL=https://gitea.creative-dragonslayer.de
- GITEA_TOKEN=your_gitea_api_token
- REGISTRY_OWNER=Linuxapps
- GITHUB_TOKEN=your_optional_github_pat
- LOG_LEVEL=info
command: ["sync"]
```
---
## Repository Structure ## Repository Structure
```text ```text
@@ -91,8 +199,8 @@ mirror-package sync --dry-run
│ └── config.toml # Linker & Cargo configuration │ └── config.toml # Linker & Cargo configuration
├── .gitea/ ├── .gitea/
│ └── workflows/ │ └── workflows/
│ ├── main.yaml # CI/CD: Release & Multi-Arch Package Build │ ├── main.yaml # CI/CD: Release, Packages & Container (Stable)
│ └── testing.yaml # CI/CD: Preview & Testing Build │ └── testing.yaml # CI/CD: Preview, Packages & Container (Testing)
├── scripts/ ├── scripts/
│ ├── get-build-number.py # Dynamic build/revision number resolution │ ├── get-build-number.py # Dynamic build/revision number resolution
│ └── package-arch.py # Native Arch Linux package builder │ └── package-arch.py # Native Arch Linux package builder
@@ -108,6 +216,9 @@ mirror-package sync --dry-run
│ ├── config_tests.rs # Tests for configuration management │ ├── config_tests.rs # Tests for configuration management
│ ├── gitea_tests.rs # Tests for Gitea upload routing │ ├── gitea_tests.rs # Tests for Gitea upload routing
│ └── github_tests.rs # Tests for package classification & parsing │ └── github_tests.rs # Tests for package classification & parsing
├── .dockerignore # Container build ignore rules
├── Dockerfile # Secure minimal runtime container
├── docker-compose.example.yml # Example Docker Compose configuration
├── Cargo.toml # Project manifest and packaging metadata ├── Cargo.toml # Project manifest and packaging metadata
├── LICENSE # GPL-3.0-or-later License ├── LICENSE # GPL-3.0-or-later License
├── AGENTS.md # Agent & developer guidelines ├── AGENTS.md # Agent & developer guidelines
+24
View File
@@ -0,0 +1,24 @@
services:
mirror-package:
image: gitea.creative-dragonslayer.de/linuxapps/mirror-package:latest
container_name: mirror-package
# Security: run as unprivileged user, read-only rootfs, drop capabilities
user: "10001:10001"
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
# Persistent storage and temporary stream buffer
volumes:
- ./config:/home/appuser/.config/mirror-package
- ./state:/home/appuser/.local/state/mirror-package
tmpfs:
- /tmp:rw,noexec,nosuid,size=2G
environment:
- GITEA_URL=https://gitea.creative-dragonslayer.de
- GITEA_TOKEN=your_gitea_api_token
- REGISTRY_OWNER=Linuxapps
- GITHUB_TOKEN=your_optional_github_pat
- LOG_LEVEL=info
command: ["sync"]
+2 -8
View File
@@ -56,20 +56,14 @@ pub fn get_target_upload_urls(
if prerelease { if prerelease {
vec![format!("{}/api/packages/{}/rpm/testing/upload", base, owner)] vec![format!("{}/api/packages/{}/rpm/testing/upload", base, owner)]
} else { } else {
vec![ vec![format!("{}/api/packages/{}/rpm/stable/upload", base, owner)]
format!("{}/api/packages/{}/rpm/stable/upload", base, owner),
format!("{}/api/packages/{}/rpm/testing/upload", base, owner),
]
} }
} }
PackageType::Arch => { PackageType::Arch => {
if prerelease { if prerelease {
vec![format!("{}/api/packages/{}/arch/testing", base, owner)] vec![format!("{}/api/packages/{}/arch/testing", base, owner)]
} else { } else {
vec![ vec![format!("{}/api/packages/{}/arch/stable", base, owner)]
format!("{}/api/packages/{}/arch/stable", base, owner),
format!("{}/api/packages/{}/arch/testing", base, owner),
]
} }
} }
} }