Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
309826ccb6 | ||
|
|
b6d49882ea | ||
|
|
6f85fd3e4b | ||
|
|
7334bd587a |
@@ -0,0 +1,114 @@
|
|||||||
|
name: Reusable Deploy via rsync
|
||||||
|
|
||||||
|
# Wiederverwendbarer Deploy-Workflow fuer IDF-Projekte.
|
||||||
|
# Repository-Pfad: ideenfabrik/idf-ci/.gitea/workflows/deploy-rsync.yml
|
||||||
|
#
|
||||||
|
# Aufruf aus einem Caller-Repo:
|
||||||
|
#
|
||||||
|
# jobs:
|
||||||
|
# deploy:
|
||||||
|
# uses: ideenfabrik/idf-ci/.gitea/workflows/deploy-rsync.yml@v1
|
||||||
|
# with:
|
||||||
|
# ssh_host: ${{ vars.SSH_HOST }}
|
||||||
|
# ssh_user: ${{ vars.SSH_USER }}
|
||||||
|
# deploy_path: ${{ vars.DEPLOY_PATH }}
|
||||||
|
# ref: ${{ github.event.inputs.ref || github.ref }}
|
||||||
|
# extra_excludes: |
|
||||||
|
# kiosk-config.php
|
||||||
|
# kiosk-config.example.php
|
||||||
|
# secrets: inherit
|
||||||
|
#
|
||||||
|
# Caller-Repo muss hinterlegen:
|
||||||
|
# Secret SSH_PRIVATE_KEY private Key fuer den Deploy-User
|
||||||
|
# Variable SSH_HOST Host/IP des Live-Servers
|
||||||
|
# Variable SSH_USER SSH-Login-User
|
||||||
|
# Variable DEPLOY_PATH Zielpfad auf dem Server
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
ssh_host:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: 'Ziel-Host fuer rsync ueber SSH'
|
||||||
|
ssh_user:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: 'SSH-Login-User'
|
||||||
|
deploy_path:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: 'Zielpfad auf dem Server'
|
||||||
|
ref:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 'Branch/Tag zum Deployen (default: github.ref)'
|
||||||
|
extra_excludes:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 'Zusaetzliche rsync --exclude Patterns, eine pro Zeile'
|
||||||
|
secrets:
|
||||||
|
SSH_PRIVATE_KEY:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.ref || github.ref }}
|
||||||
|
|
||||||
|
- name: Verify rsync + ssh available
|
||||||
|
run: |
|
||||||
|
command -v rsync >/dev/null || { echo "::error::rsync ist auf dem Runner nicht installiert"; exit 1; }
|
||||||
|
command -v ssh >/dev/null || { echo "::error::ssh ist auf dem Runner nicht installiert"; exit 1; }
|
||||||
|
rsync --version | head -1
|
||||||
|
ssh -V
|
||||||
|
|
||||||
|
- name: Setup SSH key
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
KEY_FILE="$HOME/.ssh/id_ed25519_deploy_${{ github.run_id }}"
|
||||||
|
printf '%s\n' "${{ secrets.SSH_PRIVATE_KEY }}" > "$KEY_FILE"
|
||||||
|
chmod 600 "$KEY_FILE"
|
||||||
|
echo "DEPLOY_KEY_FILE=$KEY_FILE" >> "$GITHUB_ENV"
|
||||||
|
ssh-keyscan -H "${{ inputs.ssh_host }}" >> ~/.ssh/known_hosts 2>/dev/null
|
||||||
|
|
||||||
|
- name: Deploy via rsync
|
||||||
|
env:
|
||||||
|
EXTRA_EXCLUDES: ${{ inputs.extra_excludes }}
|
||||||
|
run: |
|
||||||
|
# Standard-Excludes -- gilt fuer alle Deploys
|
||||||
|
BASE_EXCLUDES=(
|
||||||
|
--exclude='.git/'
|
||||||
|
--exclude='.gitea/'
|
||||||
|
--exclude='.gitignore'
|
||||||
|
--exclude='CHANGELOG.md'
|
||||||
|
--exclude='README.md'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Caller-spezifische Excludes (multiline -> array)
|
||||||
|
EXTRA=()
|
||||||
|
while IFS= read -r line; do
|
||||||
|
line="${line#"${line%%[![:space:]]*}"}"
|
||||||
|
line="${line%"${line##*[![:space:]]}"}"
|
||||||
|
[ -n "$line" ] && EXTRA+=( "--exclude=$line" )
|
||||||
|
done <<<"$EXTRA_EXCLUDES"
|
||||||
|
|
||||||
|
rsync -avz "${BASE_EXCLUDES[@]}" "${EXTRA[@]}" \
|
||||||
|
-e "ssh -i $DEPLOY_KEY_FILE -o StrictHostKeyChecking=yes" \
|
||||||
|
./ \
|
||||||
|
"${{ inputs.ssh_user }}@${{ inputs.ssh_host }}:${{ inputs.deploy_path }}/"
|
||||||
|
|
||||||
|
- name: Cleanup deploy key
|
||||||
|
if: always()
|
||||||
|
run: rm -f "$DEPLOY_KEY_FILE"
|
||||||
|
|
||||||
|
- name: Done
|
||||||
|
run: |
|
||||||
|
echo "Deployed ${{ github.ref_name }} to ${{ inputs.deploy_path }} on ${{ inputs.ssh_host }}"
|
||||||
@@ -21,7 +21,12 @@ name: Release Plugin
|
|||||||
# "== Changelog ==" in readme.txt wird beim Build aus CHANGELOG.md
|
# "== Changelog ==" in readme.txt wird beim Build aus CHANGELOG.md
|
||||||
# generiert und ist nur im fertigen ZIP vollständig.
|
# generiert und ist nur im fertigen ZIP vollständig.
|
||||||
#
|
#
|
||||||
# Trigger im aufrufenden Repo: push eines Tags der Form v<MAJOR>.<MINOR>.<PATCH>.
|
# Trigger im aufrufenden Repo:
|
||||||
|
# - push auf main → Auto-Modus: Header-Version wird ausgelesen, Tag v<X.Y.Z>
|
||||||
|
# wird erzeugt (sofern noch nicht vorhanden), Release wird gebaut.
|
||||||
|
# Existiert der Tag bereits, beendet der Workflow still (kein Release).
|
||||||
|
# - push eines Tags v<MAJOR>.<MINOR>.<PATCH> → klassischer Modus: Tag wird
|
||||||
|
# direkt für den Release verwendet.
|
||||||
# Ausführung auf dem IDF-Runner (self-hosted, Host-Modus).
|
# Ausführung auf dem IDF-Runner (self-hosted, Host-Modus).
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -50,14 +55,12 @@ jobs:
|
|||||||
- name: Checkout Tag
|
- name: Checkout Tag
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Variablen ableiten
|
- name: Variablen ableiten (Auto-Modus oder Tag-Modus)
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
SLUG="${{ inputs.slug }}"
|
SLUG="${{ inputs.slug }}"
|
||||||
TAG="${GITHUB_REF_NAME}"
|
|
||||||
VERSION="${TAG#v}"
|
|
||||||
MAIN_FILE="${{ inputs.main_file }}"
|
MAIN_FILE="${{ inputs.main_file }}"
|
||||||
[[ -z "$MAIN_FILE" ]] && MAIN_FILE="${SLUG}.php"
|
[[ -z "$MAIN_FILE" ]] && MAIN_FILE="${SLUG}.php"
|
||||||
CONST="${{ inputs.version_constant }}"
|
CONST="${{ inputs.version_constant }}"
|
||||||
@@ -66,6 +69,37 @@ jobs:
|
|||||||
base_upper="$(echo "$base" | tr '[:lower:]-' '[:upper:]_')"
|
base_upper="$(echo "$base" | tr '[:lower:]-' '[:upper:]_')"
|
||||||
CONST="IDF_${base_upper}_VERSION"
|
CONST="IDF_${base_upper}_VERSION"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
||||||
|
# Klassischer Modus — Tag-Push
|
||||||
|
MODE="tag"
|
||||||
|
TAG="${GITHUB_REF_NAME}"
|
||||||
|
VERSION="${TAG#v}"
|
||||||
|
else
|
||||||
|
# Auto-Modus — Push auf Branch (z.B. main)
|
||||||
|
MODE="auto"
|
||||||
|
if [[ ! -f "$MAIN_FILE" ]]; then
|
||||||
|
echo "::error::Plugin-Haupt-PHP nicht gefunden: $MAIN_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
HEADER_VERSION="$(grep -iE '^[[:space:]]*\*?[[:space:]]*Version:[[:space:]]*' "$MAIN_FILE" | head -n1 | sed -E 's/^[^0-9]*([0-9]+\.[0-9]+\.[0-9]+).*$/\1/')"
|
||||||
|
if [[ -z "$HEADER_VERSION" ]]; then
|
||||||
|
echo "::error::Header-Version in $MAIN_FILE nicht lesbar"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION="$HEADER_VERSION"
|
||||||
|
TAG="v$VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Skip-Check (nur Auto-Modus): existiert der Tag schon, kein neues Release
|
||||||
|
SKIP=false
|
||||||
|
if [[ "$MODE" == "auto" ]]; then
|
||||||
|
if git ls-remote --tags origin "refs/tags/${TAG}" 2>/dev/null | grep -q "refs/tags/${TAG}"; then
|
||||||
|
echo "Tag $TAG existiert bereits — Skip (kein neues Release)."
|
||||||
|
SKIP=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
ZIPNAME="${SLUG}_v${VERSION}.zip"
|
ZIPNAME="${SLUG}_v${VERSION}.zip"
|
||||||
{
|
{
|
||||||
echo "slug=$SLUG"
|
echo "slug=$SLUG"
|
||||||
@@ -74,10 +108,13 @@ jobs:
|
|||||||
echo "main_file=$MAIN_FILE"
|
echo "main_file=$MAIN_FILE"
|
||||||
echo "const=$CONST"
|
echo "const=$CONST"
|
||||||
echo "zipname=$ZIPNAME"
|
echo "zipname=$ZIPNAME"
|
||||||
|
echo "mode=$MODE"
|
||||||
|
echo "skip=$SKIP"
|
||||||
} >> "$GITHUB_OUTPUT"
|
} >> "$GITHUB_OUTPUT"
|
||||||
echo "Ermittelt: slug=$SLUG tag=$TAG version=$VERSION main_file=$MAIN_FILE const=$CONST"
|
echo "Ermittelt: mode=$MODE tag=$TAG version=$VERSION main_file=$MAIN_FILE const=$CONST skip=$SKIP"
|
||||||
|
|
||||||
- name: Pre-Flight — Tag-Format
|
- name: Pre-Flight — Tag-Format
|
||||||
|
if: steps.vars.outputs.skip != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -89,6 +126,7 @@ jobs:
|
|||||||
echo "Tag-Format OK: $TAG"
|
echo "Tag-Format OK: $TAG"
|
||||||
|
|
||||||
- name: Pre-Flight — Pflichtdateien
|
- name: Pre-Flight — Pflichtdateien
|
||||||
|
if: steps.vars.outputs.skip != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -102,6 +140,7 @@ jobs:
|
|||||||
echo "Pflichtdateien OK"
|
echo "Pflichtdateien OK"
|
||||||
|
|
||||||
- name: Pre-Flight — Plugin-Header-Version
|
- name: Pre-Flight — Plugin-Header-Version
|
||||||
|
if: steps.vars.outputs.skip != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -123,6 +162,7 @@ jobs:
|
|||||||
echo "Header-Version OK: $HEADER_VERSION"
|
echo "Header-Version OK: $HEADER_VERSION"
|
||||||
|
|
||||||
- name: Pre-Flight — Versions-Konstante
|
- name: Pre-Flight — Versions-Konstante
|
||||||
|
if: steps.vars.outputs.skip != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -141,6 +181,7 @@ jobs:
|
|||||||
echo "Konstante OK: $CONST=$CONST_VERSION"
|
echo "Konstante OK: $CONST=$CONST_VERSION"
|
||||||
|
|
||||||
- name: Pre-Flight — readme.txt Stable tag
|
- name: Pre-Flight — readme.txt Stable tag
|
||||||
|
if: steps.vars.outputs.skip != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -157,6 +198,7 @@ jobs:
|
|||||||
echo "Stable tag OK: $STABLE_TAG"
|
echo "Stable tag OK: $STABLE_TAG"
|
||||||
|
|
||||||
- name: Changelog-Block aus CHANGELOG.md extrahieren
|
- name: Changelog-Block aus CHANGELOG.md extrahieren
|
||||||
|
if: steps.vars.outputs.skip != 'true'
|
||||||
id: changelog
|
id: changelog
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
@@ -190,6 +232,7 @@ jobs:
|
|||||||
echo "Changelog-Block extrahiert (${#BLOCK} Zeichen)."
|
echo "Changelog-Block extrahiert (${#BLOCK} Zeichen)."
|
||||||
|
|
||||||
- name: ZIP bauen (readme.txt Changelog-Section aus CHANGELOG.md generieren)
|
- name: ZIP bauen (readme.txt Changelog-Section aus CHANGELOG.md generieren)
|
||||||
|
if: steps.vars.outputs.skip != 'true'
|
||||||
id: zip
|
id: zip
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
@@ -303,6 +346,7 @@ jobs:
|
|||||||
echo "zippath=/tmp/$ZIPNAME" >> "$GITHUB_OUTPUT"
|
echo "zippath=/tmp/$ZIPNAME" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Gitea-Release anlegen und Asset anhängen
|
- name: Gitea-Release anlegen und Asset anhängen
|
||||||
|
if: steps.vars.outputs.skip != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN || github.token }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN || github.token }}
|
||||||
@@ -314,11 +358,16 @@ jobs:
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
SERVER="${GITHUB_SERVER_URL:-https://git.ihre-ideenfabrik.de}"
|
SERVER="${GITHUB_SERVER_URL:-https://git.ihre-ideenfabrik.de}"
|
||||||
REPO="${GITHUB_REPOSITORY}"
|
REPO="${GITHUB_REPOSITORY}"
|
||||||
|
# target_commitish setzt den Commit, aus dem der Tag erzeugt wird,
|
||||||
|
# falls er noch nicht existiert (Auto-Modus). Bei Tag-Push ist es egal,
|
||||||
|
# weil der Tag schon den Commit kennt — Gitea ignoriert das Feld dann.
|
||||||
|
TARGET_SHA="${GITHUB_SHA}"
|
||||||
PAYLOAD="$(jq -n \
|
PAYLOAD="$(jq -n \
|
||||||
--arg tag "$TAG" \
|
--arg tag "$TAG" \
|
||||||
--arg name "$TAG" \
|
--arg name "$TAG" \
|
||||||
--arg body "$BODY" \
|
--arg body "$BODY" \
|
||||||
'{tag_name:$tag, name:$name, body:$body, draft:false, prerelease:false}')"
|
--arg target "$TARGET_SHA" \
|
||||||
|
'{tag_name:$tag, name:$name, body:$body, target_commitish:$target, draft:false, prerelease:false}')"
|
||||||
RESP="$(curl -sSf \
|
RESP="$(curl -sSf \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v1.2.0
|
||||||
|
|
||||||
|
### Hinzugefügt
|
||||||
|
- Auto-Modus für `release-plugin.yml`: Push auf main triggert ein Release, sobald die Header-Version eines Plugins erhöht wurde. Tag wird automatisch erzeugt, kein manueller `git tag` mehr nötig.
|
||||||
|
- Wenn der Tag zur aktuellen Header-Version bereits existiert, beendet der Workflow still (kein neues Release, keine Mail).
|
||||||
|
- Caller-Repos können den Workflow weiterhin im klassischen Modus (Tag-Push) nutzen — beide Trigger werden unterstützt.
|
||||||
|
|
||||||
|
### Geändert
|
||||||
|
- Release-API-Call nutzt jetzt `target_commitish`, damit Tags im Auto-Modus aus dem aktuellen Commit erzeugt werden können.
|
||||||
|
|
||||||
|
## v1.1.0
|
||||||
|
|
||||||
|
### Geändert
|
||||||
|
- Frühere Iteration des Workflows (vor Auto-Modus). Details siehe Git-Historie.
|
||||||
|
|
||||||
|
## v1.0.2
|
||||||
|
|
||||||
|
### Behoben
|
||||||
|
- Diverse Pre-Flight-Korrekturen.
|
||||||
|
|
||||||
|
## v1.0.1
|
||||||
|
|
||||||
|
### Behoben
|
||||||
|
- Initial-Hotfix nach v1.0.0.
|
||||||
|
|
||||||
|
## v1.0.0
|
||||||
|
|
||||||
|
### Hinzugefügt
|
||||||
|
- Erste produktive Version von `release-plugin.yml` (Tag-Push-Modus).
|
||||||
@@ -25,7 +25,8 @@ In jedem Plugin-Repo (`ideenfabrik/idf-<slug>`) liegt ein schlanker Caller-Workf
|
|||||||
name: Release
|
name: Release
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ['v*.*.*']
|
branches: [main] # Auto-Modus: Release sobald Header-Version hochgezogen wurde
|
||||||
|
tags: ['v*.*.*'] # Klassischer Modus: manuelle Tags sind weiterhin möglich
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
@@ -35,7 +36,11 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
```
|
```
|
||||||
|
|
||||||
Für die Anwendung reicht `git tag v1.2.3 && git push --tags`. Alles andere macht der Reusable Workflow. Details und Fehlerdiagnose im [Caller-Template](docs/plugin-caller-template.md).
|
**Auto-Modus (Standard, seit v1.2.0):** Plugin-Header-Version, PHP-Konstante, `Stable tag:` und CHANGELOG-Block hochziehen, committen, nach `main` pushen. Der Workflow erzeugt Tag `v<X.Y.Z>` selbst und legt das Release an. Wenn die Version nicht erhöht wurde, beendet der Workflow still (kein Release, keine Mail).
|
||||||
|
|
||||||
|
**Klassischer Modus:** Falls du lieber explizit taggst — `git tag v1.2.3 && git push --tags` triggert denselben Workflow im Tag-Modus. Beide Pfade können koexistieren.
|
||||||
|
|
||||||
|
Details und Fehlerdiagnose im [Caller-Template](docs/plugin-caller-template.md).
|
||||||
|
|
||||||
## Pflichtdateien im Plugin-Repo
|
## Pflichtdateien im Plugin-Repo
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user