Files
Klotzkette--claude-fuer-deu…/.github/workflows/release-plugin-zips.yml
T
2026-06-08 12:20:30 +02:00

158 lines
6.3 KiB
YAML

name: Release Plugin ZIPs
# Baut bei jedem Tag oder bei manueller Ausloesung pro Plugin eine ZIP-Datei
# und haengt sie an den GitHub Release. Damit ergeben sich stabile Download-URLs:
# https://github.com/Klotzkette/claude-fuer-deutsches-recht/releases/latest/download/<plugin>.zip
# Direkt einzeln in Claude Code (Customize Plugins) installierbar.
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Release-Tag (z. B. v1.1.0). Leer = nur ZIPs bauen, keinen Release anlegen.'
required: false
default: ''
permissions:
contents: write
jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Validator
run: node scripts/validate-plugin-structure.mjs
- name: Gesamt-PDFs der Testakten validieren
run: python3 scripts/validate-testakten-gesamt-pdf.py
- name: Liste aller Plugins aus marketplace.json lesen
id: plugins
run: |
python3 - <<'PY' >> "$GITHUB_OUTPUT"
import json, pathlib
m = json.loads(pathlib.Path('.claude-plugin/marketplace.json').read_text())
names = [p['name'] for p in m['plugins']]
print('names=' + ' '.join(names))
PY
- name: ZIPs bauen
run: |
mkdir -p dist
for plugin in ${{ steps.plugins.outputs.names }}; do
echo "Baue $plugin.zip"
(cd "$plugin" && zip -rq "../dist/${plugin}.zip" . -x '*.DS_Store' '__pycache__/*' '*.pyc' 'CLAUDE.md')
ls -la "dist/${plugin}.zip"
done
# Manifest fuer Marketplace-Komfort
cp .claude-plugin/marketplace.json dist/marketplace.json
- name: Release-ZIPs validieren
run: python3 scripts/validate-release-zips.py dist .claude-plugin/marketplace.json
- name: Beispielakten separat verpacken (kein Teil der Plugins, testakte-Prefix)
run: |
# Beispielakten werden mit testakte-Prefix gepackt, damit sie sich im
# Namespace klar von Plugin-ZIPs unterscheiden und niemals versehentlich
# über 'Install from .zip' als Plugin geladen werden können.
python3 scripts/build-testakten-release-zips.py dist
ls -la dist/testakte-*.zip | head
- name: Sammel-ZIPs bauen
run: |
# alle-plugins-megazip.zip enthaelt die installierbaren Einzel-ZIPs
# plus marketplace.json, nicht die Rohordner.
mkdir -p dist/mega-plugins
cp dist/marketplace.json dist/mega-plugins/marketplace.json
for plugin in ${{ steps.plugins.outputs.names }}; do
cp "dist/${plugin}.zip" "dist/mega-plugins/${plugin}.zip"
done
(cd dist/mega-plugins && zip -rq "../alle-plugins-megazip.zip" . -x '*.DS_Store')
rm -rf dist/mega-plugins
# alle-testakten.zip wurde im vorherigen Schritt mit demselben
# Arbeitsakten-Filter gebaut wie die Einzel-Testakten-ZIPs.
test -s dist/alle-testakten.zip
# alles-komplettpaket.zip enthaelt die Einzel-ZIPs, Sammel-ZIPs,
# Marketplace und zentrale Uebersichten. Ein Download fuer alles,
# ohne die installierbaren Plugin-ZIPs mit Rohordnern zu vermischen.
mkdir -p dist/komplettpaket/plugins dist/komplettpaket/testakten dist/komplettpaket/uebersichten
cp dist/marketplace.json dist/komplettpaket/marketplace.json
cp dist/alle-plugins-megazip.zip dist/komplettpaket/alle-plugins-megazip.zip
cp dist/alle-testakten.zip dist/komplettpaket/alle-testakten.zip
for plugin in ${{ steps.plugins.outputs.names }}; do
cp "dist/${plugin}.zip" "dist/komplettpaket/plugins/${plugin}.zip"
done
if [ -d testakten ]; then
for z in dist/testakte-*.zip; do
[ -e "$z" ] || continue
cp "$z" "dist/komplettpaket/testakten/$(basename "$z")"
done
fi
cp README.md SKILLS.md ASSET_INDEX.md CHANGELOG.md dist/komplettpaket/uebersichten/
# skills-index/ enthaelt die pro-Plugin-Detailseiten, auf die SKILLS.md
# mit relativen Links zeigt. Ohne diesen Ordner waeren die Links im
# ZIP-Download ins Leere gerichtet.
if [ -d skills-index ]; then
cp -R skills-index dist/komplettpaket/uebersichten/skills-index
fi
(cd dist/komplettpaket && zip -rq "../alles-komplettpaket.zip" . -x '*.DS_Store')
rm -rf dist/komplettpaket
test -s dist/alle-plugins-megazip.zip
test -s dist/alle-testakten.zip
test -s dist/alles-komplettpaket.zip
ls -lh dist/alle-plugins-megazip.zip dist/alle-testakten.zip dist/alles-komplettpaket.zip
- name: Tag bestimmen
id: tagref
run: |
if [ "${{ github.event_name }}" = "push" ]; then
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
else
echo "tag=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT"
fi
- name: Release veroeffentlichen (gedrosselter ZIP-Upload)
if: steps.tagref.outputs.tag != ''
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.tagref.outputs.tag }}
run: |
set -euo pipefail
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
echo "Release $RELEASE_TAG existiert bereits; fehlende/aktualisierte Assets werden ueberschrieben."
else
gh release create "$RELEASE_TAG" --title "$RELEASE_TAG" --generate-notes
fi
# GitHub kann bei sehr vielen Release-Assets ein Secondary Rate Limit
# ausloesen. Deshalb laden wir bewusst einzeln und mit kleinen Pausen.
count=0
while IFS= read -r asset; do
echo "Lade $(basename "$asset") hoch"
gh release upload "$RELEASE_TAG" "$asset" --clobber
count=$((count + 1))
if [ $((count % 20)) -eq 0 ]; then
sleep 20
else
sleep 2
fi
done < <(find dist -maxdepth 1 -type f \( -name '*.zip' -o -name 'marketplace.json' \) | sort)
- name: Artefakte hochladen (immer)
uses: actions/upload-artifact@v7
with:
name: plugin-zips
path: dist/*.zip
retention-days: 30