This commit is contained in:
96
.drone.yml
Normal file
96
.drone.yml
Normal file
@@ -0,0 +1,96 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-sign-and-release
|
||||
|
||||
steps:
|
||||
# --------------------------------------------------
|
||||
# 1️⃣ Build & Push Image
|
||||
# --------------------------------------------------
|
||||
- name: build-and-push
|
||||
image: docker:26
|
||||
privileged: true
|
||||
environment:
|
||||
REGISTRY_URL:
|
||||
from_secret: REGISTRY_URL
|
||||
DOCKER_USER:
|
||||
from_secret: DOCKER_USER
|
||||
DOCKER_PASS:
|
||||
from_secret: DOCKER_PASS
|
||||
volumes:
|
||||
- name: docker_sock
|
||||
path: /var/run/docker.sock
|
||||
commands:
|
||||
- echo "=== 🏗️ Building and Pushing Image ==="
|
||||
- docker login $REGISTRY_URL -u "$DOCKER_USER" -p "$DOCKER_PASS"
|
||||
- VERSION_TAG="v$DRONE_BUILD_NUMBER"
|
||||
- IMAGE_NAME="public/drone-publish-tool"
|
||||
- IMAGE_FULL="$REGISTRY_URL/$IMAGE_NAME:$VERSION_TAG"
|
||||
- echo "Building image $IMAGE_FULL ..."
|
||||
- docker build -t $IMAGE_FULL .
|
||||
- docker tag $IMAGE_FULL $REGISTRY_URL/$IMAGE_NAME:latest
|
||||
- echo "Pushing images to $REGISTRY_URL ..."
|
||||
- docker push $IMAGE_FULL
|
||||
- docker push $REGISTRY_URL/$IMAGE_NAME:latest
|
||||
- echo "VERSION_TAG=$VERSION_TAG" >> build.env
|
||||
- echo "IMAGE_FULL=$IMAGE_FULL" >> build.env
|
||||
- echo "✅ Build and push complete."
|
||||
|
||||
# --------------------------------------------------
|
||||
# 2️⃣ Sign Image with Cosign (Secret-Key aus Variable)
|
||||
# --------------------------------------------------
|
||||
- name: sign-image
|
||||
image: gcr.io/projectsigstore/cosign:v2.4.0
|
||||
environment:
|
||||
COSIGN_KEY:
|
||||
from_secret: COSIGN_KEY
|
||||
COSIGN_PASSWORD:
|
||||
from_secret: COSIGN_PASSWORD
|
||||
REGISTRY_URL:
|
||||
from_secret: REGISTRY_URL
|
||||
DOCKER_USER:
|
||||
from_secret: DOCKER_USER
|
||||
DOCKER_PASS:
|
||||
from_secret: DOCKER_PASS
|
||||
commands:
|
||||
- echo "=== 🔏 Signing image with Cosign ==="
|
||||
- . build.env
|
||||
- echo "$DOCKER_PASS" | cosign login --username "$DOCKER_USER" --password-stdin "$REGISTRY_URL"
|
||||
# 🔐 Cosign-Key aus Secret in temporäre Datei schreiben
|
||||
- echo "$COSIGN_KEY" > /tmp/cosign.key
|
||||
- chmod 600 /tmp/cosign.key
|
||||
- cosign sign --yes --key /tmp/cosign.key "$IMAGE_FULL"
|
||||
- shred -u /tmp/cosign.key || rm -f /tmp/cosign.key
|
||||
- echo "✅ Image successfully signed."
|
||||
|
||||
# --------------------------------------------------
|
||||
# 3️⃣ Create Gitea Release
|
||||
# --------------------------------------------------
|
||||
- name: create-release
|
||||
image: curlimages/curl:8.10.1
|
||||
environment:
|
||||
GITEA_URL:
|
||||
from_secret: GITEA_URL
|
||||
GITEA_TOKEN:
|
||||
from_secret: GITEA_TOKEN
|
||||
commands:
|
||||
- echo "=== 🏷️ Creating Gitea release ==="
|
||||
- . build.env
|
||||
- RELEASE_NAME="Release $VERSION_TAG"
|
||||
- RELEASE_BODY="Automatisch erstellter Release für Build $DRONE_BUILD_NUMBER\n\nImage:\n\`\`\`\n$IMAGE_FULL\n\`\`\`"
|
||||
- |
|
||||
curl -s -X POST "$GITEA_URL/api/v1/repos/${DRONE_REPO_OWNER}/${DRONE_REPO_NAME}/releases" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"tag_name\": \"$VERSION_TAG\",
|
||||
\"name\": \"$RELEASE_NAME\",
|
||||
\"body\": \"$RELEASE_BODY\",
|
||||
\"draft\": false,
|
||||
\"prerelease\": false
|
||||
}"
|
||||
- echo "✅ Release created in Gitea."
|
||||
|
||||
volumes:
|
||||
- name: docker_sock
|
||||
host:
|
||||
path: /var/run/docker.sock
|
||||
8
.env.example
Normal file
8
.env.example
Normal file
@@ -0,0 +1,8 @@
|
||||
# =======================================================
|
||||
# Drone Publish Tool - Beispielkonfiguration (.env.example)
|
||||
# Bitte kopiere sie zu `.env` und trage deine echten Werte ein.
|
||||
# =======================================================
|
||||
|
||||
VERSION_TAG=v1.0.0
|
||||
|
||||
GITEA_REPO=username/drone-publish-tool
|
||||
44
.gitignore
vendored
Normal file
44
.gitignore
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
# ===============================
|
||||
# Drone Publish Tool - .gitignore
|
||||
# ===============================
|
||||
|
||||
# 🧩 Lokale Umgebungsdateien
|
||||
.env
|
||||
.env.local
|
||||
.env.secret
|
||||
|
||||
# 🔑 Schlüsseldateien
|
||||
*.key
|
||||
*.pub
|
||||
*.pem
|
||||
|
||||
# 🧱 Build-Artefakte / temporäre Dateien
|
||||
*.log
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# 🐋 Docker-Artefakte
|
||||
*.tar
|
||||
*.pid
|
||||
docker-compose.override.yml
|
||||
docker-compose.local.yml
|
||||
|
||||
# 🧰 System / Editor-spezifisch
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.vscode/
|
||||
.idea/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# 📦 Optional: falls du lokale Testdaten oder Push-Caches hast
|
||||
/build/
|
||||
/dist/
|
||||
.cache/
|
||||
output/
|
||||
|
||||
# 🧪 Drone / CI Cache
|
||||
.drone-secrets/
|
||||
.drone-cache/
|
||||
37
Dockerfile
Normal file
37
Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
||||
FROM alpine:3.20
|
||||
|
||||
# --- Metadaten / OCI Labels ---
|
||||
LABEL org.opencontainers.image.title="Drone Publish Tool" \
|
||||
org.opencontainers.image.description="Automatisiertes Drone CI Tool zum Signieren (Cosign) und Erstellen von Gitea-Releases" \
|
||||
org.opencontainers.image.authors="Patrick Buchhorst <patrick@buchhorster.de>" \
|
||||
org.opencontainers.image.vendor="Buchhorster IT" \
|
||||
org.opencontainers.image.source="https://gitea.buchhorster.de/patrick/drone-publish-tool" \
|
||||
org.opencontainers.image.licenses="MIT"
|
||||
|
||||
# Optionaler Build-ARG für Versionstag (falls über Drone gesetzt)
|
||||
ARG VERSION=1.0.0
|
||||
LABEL org.opencontainers.image.version=$VERSION
|
||||
|
||||
# --- Systemabhängigkeiten ---
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
curl \
|
||||
git \
|
||||
jq \
|
||||
tini \
|
||||
wget \
|
||||
ca-certificates \
|
||||
docker-cli && \
|
||||
update-ca-certificates
|
||||
|
||||
# --- Cosign installieren ---
|
||||
RUN wget -qO /usr/local/bin/cosign \
|
||||
https://github.com/sigstore/cosign/releases/download/v2.4.0/cosign-linux-amd64 && \
|
||||
chmod +x /usr/local/bin/cosign
|
||||
|
||||
# --- Entrypoint-Skript ---
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||
|
||||
# --- Saubere Startumgebung ---
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]
|
||||
41
Makefile
Normal file
41
Makefile
Normal file
@@ -0,0 +1,41 @@
|
||||
# ===== Konfiguration =====
|
||||
include .env
|
||||
|
||||
IMAGE_NAME=$(REGISTRY_URL)/public/drone-publish-tool
|
||||
IMAGE_FULL=$(IMAGE_NAME):$(VERSION_TAG)
|
||||
|
||||
# ===== Targets =====
|
||||
|
||||
.PHONY: all build push sign release clean
|
||||
|
||||
all: build push sign release
|
||||
|
||||
build:
|
||||
@echo "=== Building $(IMAGE_FULL) ==="
|
||||
docker build -t $(IMAGE_FULL) .
|
||||
docker tag $(IMAGE_FULL) $(IMAGE_NAME):latest
|
||||
|
||||
push:
|
||||
@echo "=== Pushing images to $(REGISTRY_URL) ==="
|
||||
@docker login $(REGISTRY_URL) -u "$(REGISTRY_USER)" -p "$(REGISTRY_PASS)"
|
||||
docker push $(IMAGE_FULL)
|
||||
docker push $(IMAGE_NAME):latest
|
||||
|
||||
sign:
|
||||
@echo "=== Signing $(IMAGE_FULL) ==="
|
||||
@if [ -z "$(COSIGN_KEY)" ]; then echo "❌ Kein COSIGN_KEY definiert"; exit 1; fi
|
||||
docker run --rm -v $(COSIGN_KEY):/key:ro \
|
||||
-e COSIGN_PASSWORD="$(COSIGN_PASSWORD)" \
|
||||
gcr.io/projectsigstore/cosign:v2.4.0 \
|
||||
sign --yes --key /key $(IMAGE_FULL)
|
||||
|
||||
release:
|
||||
@echo "=== Creating Gitea Release $(VERSION_TAG) ==="
|
||||
curl -s -X POST "$(GITEA_URL)/api/v1/repos/$(GITEA_REPO)/releases" \
|
||||
-H "Authorization: token $(GITEA_TOKEN)" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"$(VERSION_TAG)\",\"name\":\"Release $(VERSION_TAG)\",\"body\":\"Automatischer Release für $(VERSION_TAG)\\n\\nImage: $(IMAGE_FULL)\",\"draft\":false,\"prerelease\":false}"
|
||||
|
||||
clean:
|
||||
@echo "=== Cleaning up local images ==="
|
||||
-docker rmi $(IMAGE_FULL) $(IMAGE_NAME):latest 2>/dev/null || true
|
||||
177
README.md
Normal file
177
README.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# 🚀 Drone Publish Tool
|
||||
|
||||
Ein leichtgewichtiges CI-Tool auf Basis von **Alpine Linux**, das in einer Drone CI-Pipeline automatisch:
|
||||
|
||||
1. Docker-Images mit **[Cosign](https://github.com/sigstore/cosign)** signiert 🔏
|
||||
2. und **Releases in [Gitea](https://gitea.io/)** erstellt 🏷️
|
||||
|
||||
Das Tool vereinfacht den gesamten Veröffentlichungsprozess — von der Signierung bis zum Release —
|
||||
und funktioniert sowohl **innerhalb von Drone** als auch **manuell**.
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Features
|
||||
|
||||
- ✅ Automatisches Signieren von Container-Images via Cosign
|
||||
- ✅ Automatische Release-Erstellung in Gitea
|
||||
- ✅ Vollständig über Umgebungsvariablen steuerbar (ideal für Drone Secrets)
|
||||
- ✅ Kompatibel mit privaten Docker-Registries (z. B. Zot, Harbor, etc.)
|
||||
- ✅ Minimal, sicher, portabel – basiert auf Alpine Linux
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Voraussetzungen
|
||||
|
||||
1. **Drone CI** ist mit Gitea verbunden (z. B. via OAuth oder Token).
|
||||
2. Dein Docker-Image ist bereits gebaut und in der Registry verfügbar.
|
||||
3. Du hast die unten genannten Secrets in Drone hinterlegt.
|
||||
4. Für die Signierung ist ein **Cosign-Schlüssel** vorhanden (siehe unten).
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Erforderliche Secrets
|
||||
|
||||
| Secret Name | Beschreibung | Beispielwert |
|
||||
|--------------|--------------|---------------|
|
||||
| `REGISTRY_URL` | Basis-URL deiner Registry | `registry.example.com` |
|
||||
| `DOCKER_USER` | Benutzername für Registry-Login | `ci-user` |
|
||||
| `DOCKER_PASS` | Passwort oder Token für Registry | `changeme123` |
|
||||
| `COSIGN_KEY` | Inhalt des privaten Cosign-Schlüssels (nicht der Pfad!) | `-----BEGIN ENCRYPTED PRIVATE KEY-----...` |
|
||||
| `COSIGN_PASSWORD` | Passwort für den Cosign-Schlüssel (kann leer sein) | *(leer)* |
|
||||
| `GITEA_URL` | Basis-URL deines Gitea-Servers | `https://gitea.example.com` |
|
||||
| `GITEA_TOKEN` | Personal Access Token für Gitea API | `ghp_abc123tokenxyz` |
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Cosign-Schlüssel erzeugen
|
||||
|
||||
Falls du noch keinen Cosign-Schlüssel hast, kannst du ihn einfach per Docker erzeugen:
|
||||
|
||||
```bash
|
||||
docker run --rm -v $(pwd):/keys \
|
||||
-e COSIGN_PASSWORD="meinpasswort" \
|
||||
gcr.io/projectsigstore/cosign:latest generate-key-pair
|
||||
```
|
||||
|
||||
Danach findest du zwei Dateien:
|
||||
|
||||
- 🗝️ **`cosign.key`** → Privater Schlüssel (als Secret `COSIGN_KEY` in Drone hinterlegen)
|
||||
- 🔓 **`cosign.pub`** → Öffentlicher Schlüssel (optional in Zot für Signaturprüfung hinterlegen)
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Beispiel: Verwendung in Drone CI
|
||||
|
||||
Nach erfolgreichem Build kann das Tool als separater Step eingebunden werden,
|
||||
um das erstellte Image zu signieren und in Gitea automatisch als Release zu veröffentlichen.
|
||||
|
||||
```yaml
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: release
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: docker:26
|
||||
privileged: true
|
||||
environment:
|
||||
REGISTRY_URL:
|
||||
from_secret: REGISTRY_URL
|
||||
DOCKER_USER:
|
||||
from_secret: DOCKER_USER
|
||||
DOCKER_PASS:
|
||||
from_secret: DOCKER_PASS
|
||||
volumes:
|
||||
- name: docker_sock
|
||||
path: /var/run/docker.sock
|
||||
commands:
|
||||
- docker login $REGISTRY_URL -u "$DOCKER_USER" -p "$DOCKER_PASS"
|
||||
- docker build -t $REGISTRY_URL/public/myapp:$DRONE_BUILD_NUMBER .
|
||||
- docker push $REGISTRY_URL/public/myapp:$DRONE_BUILD_NUMBER
|
||||
- echo "IMAGE_FULL=$REGISTRY_URL/public/myapp:$DRONE_BUILD_NUMBER" >> build.env
|
||||
- echo "VERSION_TAG=v$DRONE_BUILD_NUMBER" >> build.env
|
||||
|
||||
- name: publish
|
||||
image: registry.example.com/public/drone-publish-tool:latest
|
||||
environment:
|
||||
COSIGN_KEY:
|
||||
from_secret: COSIGN_KEY
|
||||
COSIGN_PASSWORD:
|
||||
from_secret: COSIGN_PASSWORD
|
||||
GITEA_URL:
|
||||
from_secret: GITEA_URL
|
||||
GITEA_TOKEN:
|
||||
from_secret: GITEA_TOKEN
|
||||
REGISTRY_URL:
|
||||
from_secret: REGISTRY_URL
|
||||
DOCKER_USER:
|
||||
from_secret: DOCKER_USER
|
||||
DOCKER_PASS:
|
||||
from_secret: DOCKER_PASS
|
||||
commands:
|
||||
- . build.env
|
||||
- export GITEA_REPO="$DRONE_REPO_OWNER/$DRONE_REPO_NAME"
|
||||
- /usr/local/bin/entrypoint.sh
|
||||
|
||||
volumes:
|
||||
- name: docker_sock
|
||||
host:
|
||||
path: /var/run/docker.sock
|
||||
```
|
||||
|
||||
💡 **Hinweis:**
|
||||
`IMAGE_FULL` und `VERSION_TAG` werden aus dem Build-Step übernommen,
|
||||
damit das Tool weiß, welches Image signiert und welcher Release erstellt werden soll.
|
||||
|
||||
---
|
||||
|
||||
## 🧰 Beispiel: Manuelle Nutzung
|
||||
|
||||
Du kannst das Tool auch außerhalb von Drone direkt ausführen,
|
||||
z. B. zum Testen oder für manuelle Releases:
|
||||
|
||||
```bash
|
||||
docker run --rm \
|
||||
-e IMAGE_FULL=registry.example.com/public/myapp:v42 \
|
||||
-e VERSION_TAG=v42 \
|
||||
-e COSIGN_KEY="$(cat cosign.key)" \
|
||||
-e COSIGN_PASSWORD="" \
|
||||
-e GITEA_URL=https://gitea.example.com \
|
||||
-e GITEA_TOKEN=ghp_abc123tokenxyz \
|
||||
-e GITEA_REPO=patrick/myapp \
|
||||
registry.example.com/public/drone-publish-tool:latest
|
||||
```
|
||||
|
||||
Damit signierst du das Image `myapp:v42` und erzeugst gleichzeitig ein Release in Gitea.
|
||||
Das funktioniert auch ohne Drone CI – ideal zum Testen oder Troubleshooting.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Image-Metadaten (OCI Labels)
|
||||
|
||||
Das Tool bringt vollständige [OCI Labels](https://github.com/opencontainers/image-spec/blob/main/annotations.md) mit,
|
||||
damit es in Registries wie Zot oder Harbor korrekt beschrieben angezeigt wird:
|
||||
|
||||
| Label | Beschreibung |
|
||||
|--------|---------------|
|
||||
| `org.opencontainers.image.title` | Drone Publish Tool |
|
||||
| `org.opencontainers.image.description` | Automatisiertes Drone CI Tool zum Signieren und Erstellen von Gitea-Releases |
|
||||
| `org.opencontainers.image.version` | 1.0.0 |
|
||||
| `org.opencontainers.image.vendor` | Buchhorster IT |
|
||||
| `org.opencontainers.image.source` | https://gitea.buchhorster.de/patrick/drone-publish-tool |
|
||||
| `org.opencontainers.image.licenses` | MIT |
|
||||
|
||||
---
|
||||
|
||||
## 🧾 Lizenz
|
||||
|
||||
**MIT License**
|
||||
© 2025 Patrick Buchhorst / Buchhorster IT
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Changelog
|
||||
|
||||
| Version | Änderungen |
|
||||
|----------|-------------|
|
||||
| `1.0.0` | Erste Version mit Image-Signing (Cosign) und Gitea-Release-Funktion |
|
||||
57
entrypoint.sh
Normal file
57
entrypoint.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "=== 🚀 Drone Publish Tool ==="
|
||||
echo "Image: $IMAGE_FULL"
|
||||
echo "Version: $VERSION_TAG"
|
||||
|
||||
# --- 1️⃣ Signieren ---
|
||||
if [ -n "$COSIGN_KEY" ]; then
|
||||
echo "🔏 Signing image using Cosign..."
|
||||
|
||||
# Temporäre Datei anlegen
|
||||
COSIGN_KEY_FILE=$(mktemp /tmp/cosign-key-XXXXXX)
|
||||
echo "$COSIGN_KEY" > "$COSIGN_KEY_FILE"
|
||||
chmod 600 "$COSIGN_KEY_FILE"
|
||||
|
||||
# Optionales Passwort weitergeben
|
||||
export COSIGN_PASSWORD="${COSIGN_PASSWORD:-}"
|
||||
|
||||
# Signieren (mit --yes, falls ohne Interaktion)
|
||||
cosign sign --yes --key "$COSIGN_KEY_FILE" "$IMAGE_FULL"
|
||||
|
||||
# Digest extrahieren (zur Info oder für Gitea-Release)
|
||||
SIGN_DIGEST=$(cosign verify --key "$COSIGN_KEY_FILE" "$IMAGE_FULL" 2>/dev/null | grep docker-manifest-digest | head -n1 | awk -F'"' '{print $4}')
|
||||
|
||||
# Schlüssel sicher löschen
|
||||
shred -u "$COSIGN_KEY_FILE" 2>/dev/null || rm -f "$COSIGN_KEY_FILE"
|
||||
|
||||
echo "✅ Image successfully signed."
|
||||
else
|
||||
echo "⚠️ Skipping signing step (no COSIGN_KEY provided)"
|
||||
fi
|
||||
|
||||
# --- 2️⃣ Gitea Release erstellen ---
|
||||
if [ -n "$GITEA_TOKEN" ] && [ -n "$GITEA_REPO" ] && [ -n "$GITEA_URL" ]; then
|
||||
echo "🏷️ Creating Gitea release for $VERSION_TAG..."
|
||||
|
||||
RELEASE_BODY="Automatischer Release für $VERSION_TAG\n\nImage: $IMAGE_FULL"
|
||||
[ -n "$SIGN_DIGEST" ] && RELEASE_BODY="$RELEASE_BODY\n\nSignatur-Digest: $SIGN_DIGEST"
|
||||
|
||||
curl -s -X POST "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"tag_name\": \"$VERSION_TAG\",
|
||||
\"name\": \"Release $VERSION_TAG\",
|
||||
\"body\": \"$RELEASE_BODY\",
|
||||
\"draft\": false,
|
||||
\"prerelease\": false
|
||||
}"
|
||||
|
||||
echo "✅ Gitea release created."
|
||||
else
|
||||
echo "⚠️ Skipping Gitea release creation (missing vars)"
|
||||
fi
|
||||
|
||||
echo "=== ✅ Done ==="
|
||||
Reference in New Issue
Block a user