Files
ci-workflows/.gitea/workflows/maven-release.yml
T

343 lines
15 KiB
YAML

name: Maven Release (reusable)
on:
workflow_call:
inputs:
compose-repos:
description: "Repo docker-compose da aggiornare, separati da virgola (es. Biters/bitwork,Biters/altro). Vuoto = skip."
required: false
type: string
default: ""
maven-profiles:
description: "Profili Maven da attivare (es. bitwork,ci,git)"
required: false
type: string
default: ""
docker-registry:
description: "Registry Docker per il pattern sed nei docker-compose"
required: false
type: string
default: "artifacts.biters.it:8443"
build-docker:
description: "Costruisce e pubblica l'immagine Docker (jib). false per librerie jar-only."
required: false
type: boolean
default: true
jobs:
release:
runs-on: ubuntu-latest
container:
image: artifacts.biters.it:8443/biters/gitea-ci-maven:21
credentials:
username: ${{ secrets.MAVEN_USERNAME }}
password: ${{ secrets.MAVEN_PASSWORD }}
volumes:
- /opt/ci-cache/m2-repository:/root/.m2/repository
env:
RUNNER_TOOL_CACHE: /toolcache
MAVEN_REPO_URL: ${{ vars.MAVEN_REPO_URL }}
MAVEN_REPO_RELEASES_URL: ${{ vars.MAVEN_REPO_RELEASES_URL }}
MAVEN_REPO_SNAPSHOTS_URL: ${{ vars.MAVEN_REPO_SNAPSHOTS_URL }}
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
GITEA_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
MVN_PROFILES: ${{ inputs.maven-profiles }}
steps:
# ── Bootstrap ──────────────────────────────────────────────
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Checkout CI config
run: |
git clone --depth=1 "https://biters.gitea:${GITEA_TOKEN}@git.biters.it/Biters/ci-workflows.git" .ci-config
- name: Configura git
run: |
git config user.name "CI Release Bot"
git config user.email "ci@biters.it"
git remote set-url origin "https://biters.gitea:${GITEA_TOKEN}@git.biters.it/${{ github.repository }}.git"
- name: Verifica configurazione
run: |
missing=0
for v in MAVEN_REPO_URL MAVEN_REPO_RELEASES_URL MAVEN_REPO_SNAPSHOTS_URL \
MAVEN_USERNAME MAVEN_PASSWORD GITEA_TOKEN; do
eval "val=\${$v}"
if [ -z "$val" ]; then echo "MANCANTE: $v"; missing=1; else echo "OK: $v"; fi
done
[ "$missing" -eq 0 ] || { echo "Imposta le Variables/Secrets mancanti a livello org."; exit 1; }
# ── Versione ───────────────────────────────────────────────
- name: Promuovi a release (rimuovi -SNAPSHOT)
id: version
run: |
PROF=""
[ -n "$MVN_PROFILES" ] && PROF="-P$MVN_PROFILES"
mvn -B -s .ci-config/settings.xml $PROF versions:set \
-DremoveSnapshot=true -DgenerateBackupPoms=false
RELEASE_VERSION=$(mvn -q -s .ci-config/settings.xml $PROF help:evaluate \
-Dexpression=project.version -DforceStdout)
echo "RELEASE_VERSION=$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
ARTIFACT_ID=$(mvn -q -s .ci-config/settings.xml $PROF help:evaluate \
-Dexpression=project.artifactId -DforceStdout)
echo "ARTIFACT_ID=$ARTIFACT_ID" >> "$GITHUB_OUTPUT"
GROUP_ID=$(mvn -q -s .ci-config/settings.xml $PROF help:evaluate \
-Dexpression=project.groupId -DforceStdout)
echo "GROUP_ID=$GROUP_ID" >> "$GITHUB_OUTPUT"
PROJECT_NAME=$(mvn -q -s .ci-config/settings.xml $PROF help:evaluate \
-Dexpression=project.artifactId -DforceStdout)
echo "PROJECT_NAME=$PROJECT_NAME" >> "$GITHUB_OUTPUT"
MAJOR=$(echo "$RELEASE_VERSION" | cut -d. -f1)
MINOR=$(echo "$RELEASE_VERSION" | cut -d. -f2)
PATCH=$(echo "$RELEASE_VERSION" | cut -d. -f3)
NEXT_VERSION="$MAJOR.$MINOR.$((PATCH + 1))-SNAPSHOT"
echo "NEXT_VERSION=$NEXT_VERSION" >> "$GITHUB_OUTPUT"
echo "── Riepilogo ──"
echo " release : $RELEASE_VERSION"
echo " artifactId: $ARTIFACT_ID"
echo " groupId : $GROUP_ID"
echo " next : $NEXT_VERSION"
# ── Check Nexus ────────────────────────────────────────────
- name: Verifica che la versione non esista già su Nexus
run: |
VERSION="${{ steps.version.outputs.RELEASE_VERSION }}"
ARTIFACT_ID="${{ steps.version.outputs.ARTIFACT_ID }}"
GROUP_PATH=$(echo "${{ steps.version.outputs.GROUP_ID }}" | tr '.' '/')
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
-u "$MAVEN_USERNAME:$MAVEN_PASSWORD" \
"${MAVEN_REPO_RELEASES_URL}/${GROUP_PATH}/${ARTIFACT_ID}/${VERSION}/${ARTIFACT_ID}-${VERSION}.pom")
if [ "$HTTP_CODE" = "200" ]; then
echo "ERRORE: la versione $VERSION esiste già su Nexus!"
exit 1
fi
echo "OK: versione $VERSION non presente su Nexus (HTTP $HTTP_CODE)"
# ── Build & Deploy ─────────────────────────────────────────
- name: Build + Deploy artifact (+ Docker)
run: |
PROF=""
[ -n "$MVN_PROFILES" ] && PROF="-P$MVN_PROFILES"
if [ "${{ inputs.build-docker }}" = "true" ]; then
mvn -B -U $PROF -s .ci-config/settings.xml -DskipTests \
-Djib.to.auth.username="$MAVEN_USERNAME" \
-Djib.to.auth.password="$MAVEN_PASSWORD" \
-Djib.from.auth.username="$MAVEN_USERNAME" \
-Djib.from.auth.password="$MAVEN_PASSWORD" \
clean deploy
else
mvn -B -U $PROF -s .ci-config/settings.xml -DskipTests clean deploy
fi
# ── Tag & Bump ─────────────────────────────────────────────
- name: Commit release e tag
run: |
VERSION="${{ steps.version.outputs.RELEASE_VERSION }}"
git add pom.xml
git commit -m "release: ${VERSION} [skip ci]"
git tag -a "${VERSION}" -m "Release ${VERSION}"
- name: Avanza a prossima versione SNAPSHOT
run: |
PROF=""
[ -n "$MVN_PROFILES" ] && PROF="-P$MVN_PROFILES"
NEXT="${{ steps.version.outputs.NEXT_VERSION }}"
mvn -B -s .ci-config/settings.xml $PROF versions:set \
-DnewVersion="$NEXT" -DgenerateBackupPoms=false
git add pom.xml
git commit -m "chore: avanza versione a ${NEXT} [skip ci]"
- name: Push release + tag + versione successiva
run: |
VERSION="${{ steps.version.outputs.RELEASE_VERSION }}"
git push origin HEAD "${VERSION}"
# ── Gitea Release ──────────────────────────────────────────
- name: Crea release su Gitea
run: |
VERSION="${{ steps.version.outputs.RELEASE_VERSION }}"
ARTIFACT_ID="${{ steps.version.outputs.ARTIFACT_ID }}"
GROUP_PATH=$(echo "${{ steps.version.outputs.GROUP_ID }}" | tr '.' '/')
JAR_URL="${MAVEN_REPO_RELEASES_URL}/${GROUP_PATH}/${ARTIFACT_ID}/${VERSION}/${ARTIFACT_ID}-${VERSION}.jar"
if [ "${{ inputs.build-docker }}" = "true" ]; then
DOCKER_IMAGE="${{ inputs.docker-registry }}/biters/${ARTIFACT_ID}:${VERSION}"
BODY="## Artifacts
- **JAR**: [${ARTIFACT_ID}-${VERSION}.jar](${JAR_URL})
- **Docker**: \`${DOCKER_IMAGE}\`"
else
BODY="## Artifacts
- **JAR**: [${ARTIFACT_ID}-${VERSION}.jar](${JAR_URL})"
fi
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"https://git.biters.it/api/v1/repos/${{ github.repository }}/releases" \
-d "$(jq -n \
--arg tag "$VERSION" \
--arg name "v${VERSION}" \
--arg body "$BODY" \
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')"
# ── Rebase develop ─────────────────────────────────────────
- name: Rebase develop su master
run: |
git checkout -- .
git clean -fd
git fetch origin develop
git checkout develop
git rebase origin/${{ github.ref_name }}
git push origin develop --force-with-lease
# ── Aggiorna docker-compose (opzionale, multi-repo) ────────
- name: Aggiorna docker-compose nei repo compose
if: ${{ inputs.compose-repos != '' }}
run: |
VERSION="${{ steps.version.outputs.RELEASE_VERSION }}"
PROJECT_NAME="${{ steps.version.outputs.PROJECT_NAME }}"
REGISTRY="${{ inputs.docker-registry }}"
ESCAPED_REGISTRY=$(echo "$REGISTRY" | sed 's/\./\\./g')
APP_YML="${GITHUB_WORKSPACE}/src/main/resources/application.yml"
# ── Estrai variabili da application.yml (una volta sola) ──
APP_VARS_REQUIRED=""
APP_VARS_WITH_DEFAULT=""
APP_VARS_ALL=""
if [ -f "$APP_YML" ]; then
ALL_PLACEHOLDERS=$(grep -oP '\$\{[^}]+\}' "$APP_YML" | sort -u)
for ph in $ALL_PLACEHOLDERS; do
var=$(echo "$ph" | sed 's/\${//;s/[:}].*//')
echo "$var" | grep -q '\.' && continue
if echo "$ph" | grep -q ':'; then
APP_VARS_WITH_DEFAULT=$(printf '%s\n%s' "$APP_VARS_WITH_DEFAULT" "$var")
else
APP_VARS_REQUIRED=$(printf '%s\n%s' "$APP_VARS_REQUIRED" "$var")
fi
done
APP_VARS_REQUIRED=$(echo "$APP_VARS_REQUIRED" | grep -v '^$' | sort -u)
APP_VARS_WITH_DEFAULT=$(echo "$APP_VARS_WITH_DEFAULT" | grep -v '^$' | sort -u)
APP_VARS_ALL=$(printf '%s\n%s' "$APP_VARS_REQUIRED" "$APP_VARS_WITH_DEFAULT" | grep -v '^$' | sort -u)
echo "Variabili application.yml: $(echo "$APP_VARS_REQUIRED" | wc -w) obbligatorie, $(echo "$APP_VARS_WITH_DEFAULT" | wc -w) con default"
fi
# ── Cicla su ogni repo compose ──
echo "${{ inputs.compose-repos }}" | tr ',' '\n' | while IFS= read -r COMPOSE_REPO; do
COMPOSE_REPO=$(echo "$COMPOSE_REPO" | xargs)
[ -z "$COMPOSE_REPO" ] && continue
REPO_DIR="/tmp/compose-$(echo "$COMPOSE_REPO" | tr '/' '-')"
echo ""
echo "════════════════════════════════════════"
echo " Repo: ${COMPOSE_REPO}"
echo "════════════════════════════════════════"
# ── Clone e aggiorna versione immagine ──
git clone "https://biters.gitea:${GITEA_TOKEN}@git.biters.it/${COMPOSE_REPO}.git" "$REPO_DIR"
cd "$REPO_DIR"
git config user.name "CI Release Bot"
git config user.email "ci@biters.it"
find . -name 'docker-compose*.yml' -exec \
sed -i "s|\(${ESCAPED_REGISTRY}/biters/${PROJECT_NAME}:\)[^ \"']*|\1${VERSION}|g" {} +
# ── Verifica variabili environment ──
if [ -n "$APP_VARS_ALL" ]; then
MISSING=""
for compose_file in docker-compose-*.yml; do
[ -f "$compose_file" ] || continue
ENV_NAME=${compose_file#docker-compose-}
ENV_NAME=${ENV_NAME%.yml}
ENV_FILE=".env_${ENV_NAME}"
echo " ── ${compose_file} ──"
COMMON_VARS=$(yq '.services["common-env-hc"].environment // {} | keys | .[]' \
"$compose_file" 2>/dev/null || true)
SVC_VARS=$(yq ".services | to_entries[] |
select(.value.image // \"\" | test(\"${PROJECT_NAME}\")) |
.value.environment // {} | keys | .[]" \
"$compose_file" 2>/dev/null || true)
COMPOSE_VARS=$(printf '%s\n%s' "$COMMON_VARS" "$SVC_VARS" | grep -v '^$' | sort -u)
for var in $APP_VARS_ALL; do
if ! echo "$COMPOSE_VARS" | grep -qx "$var"; then
severity="REQUIRED"
echo "$APP_VARS_WITH_DEFAULT" | grep -qx "$var" && severity="HAS_DEFAULT"
MISSING="${MISSING}| \`${var}\` | \`${compose_file}\` | ${severity} |\n"
fi
done
if [ -f "$ENV_FILE" ]; then
ENV_REFS=$(grep -oP '\$\{\K\w+(?=\})' "$compose_file" | sort -u)
for ref in $ENV_REFS; do
if ! grep -q "^${ref}=" "$ENV_FILE"; then
MISSING="${MISSING}| \`${ref}\` | \`${ENV_FILE}\` | REQUIRED |\n"
fi
done
fi
done
MISSING_REQUIRED=$(printf '%b' "$MISSING" | grep "REQUIRED" || true)
MISSING_DEFAULTS=$(printf '%b' "$MISSING" | grep "HAS_DEFAULT" || true)
if [ -n "$MISSING_DEFAULTS" ] && [ -z "$MISSING_REQUIRED" ]; then
echo " WARNING: variabili con default mancanti (non bloccante)"
elif [ -n "$MISSING_REQUIRED" ]; then
echo " WARNING: variabili obbligatorie mancanti"
ISSUE_BODY="## Variabili mancanti — ${PROJECT_NAME} v${VERSION}
La pipeline di release ha rilevato variabili non censite:
| Variabile | File | Severità |
|-----------|------|----------|
$(printf '%b' "$MISSING")
**REQUIRED** = nessun default, il servizio non parte senza.
**HAS_DEFAULT** = ha un default in application.yml, verificare se adeguato.
### Azione richiesta
Aggiungere le variabili mancanti nei rispettivi file e committare su ${COMPOSE_REPO}."
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"https://git.biters.it/api/v1/repos/${COMPOSE_REPO}/issues" \
-d "$(jq -n \
--arg title "[CI] Variabili mancanti: ${PROJECT_NAME} v${VERSION}" \
--arg body "$ISSUE_BODY" \
'{title: $title, body: $body}')"
echo " Issue creata su ${COMPOSE_REPO}."
else
echo " Tutte le variabili censite."
fi
fi
# ── Push se ci sono modifiche ──
if git diff --quiet; then
echo " Nessuna modifica necessaria"
else
git add -A
git commit -m "chore(${PROJECT_NAME}): aggiorna immagine a ${VERSION}"
git push origin HEAD
echo " Docker-compose aggiornati con versione ${VERSION}"
fi
cd "$GITHUB_WORKSPACE"
done