343 lines
15 KiB
YAML
343 lines
15 KiB
YAML
name: Maven Release (reusable)
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
compose-repo:
|
|
description: "Repo docker-compose da aggiornare (es. Biters/bitwork). 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"
|
|
|
|
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 + Push immagine Docker
|
|
run: |
|
|
PROF=""
|
|
[ -n "$MVN_PROFILES" ] && PROF="-P$MVN_PROFILES"
|
|
|
|
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
|
|
|
|
# ── 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"
|
|
DOCKER_IMAGE="${{ inputs.docker-registry }}/biters/${ARTIFACT_ID}:${VERSION}"
|
|
|
|
BODY="## Artifacts
|
|
|
|
- **JAR**: [${ARTIFACT_ID}-${VERSION}.jar](${JAR_URL})
|
|
- **Docker**: \`${DOCKER_IMAGE}\`"
|
|
|
|
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 fetch origin develop
|
|
git checkout develop
|
|
git rebase origin/${{ github.ref_name }}
|
|
git push origin develop --force-with-lease
|
|
|
|
# ── Aggiorna docker-compose (opzionale) ────────────────────
|
|
- name: Clona repo compose e aggiorna versione immagine
|
|
if: ${{ inputs.compose-repo != '' }}
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.RELEASE_VERSION }}"
|
|
PROJECT_NAME="${{ steps.version.outputs.PROJECT_NAME }}"
|
|
REGISTRY="${{ inputs.docker-registry }}"
|
|
COMPOSE_REPO="${{ inputs.compose-repo }}"
|
|
|
|
git clone "https://biters.gitea:${GITEA_TOKEN}@git.biters.it/${COMPOSE_REPO}.git" /tmp/compose-repo
|
|
cd /tmp/compose-repo
|
|
|
|
git config user.name "CI Release Bot"
|
|
git config user.email "ci@biters.it"
|
|
|
|
ESCAPED_REGISTRY=$(echo "$REGISTRY" | sed 's/\./\\./g')
|
|
find . -name 'docker-compose*.yml' -exec \
|
|
sed -i "s|\(${ESCAPED_REGISTRY}/biters/${PROJECT_NAME}:\)[^ \"']*|\1${VERSION}|g" {} +
|
|
|
|
- name: Verifica variabili environment
|
|
if: ${{ inputs.compose-repo != '' }}
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.RELEASE_VERSION }}"
|
|
PROJECT_NAME="${{ steps.version.outputs.PROJECT_NAME }}"
|
|
COMPOSE_REPO="${{ inputs.compose-repo }}"
|
|
APP_YML="${GITHUB_WORKSPACE}/src/main/resources/application.yml"
|
|
|
|
if [ ! -f "$APP_YML" ]; then
|
|
echo "application.yml non trovato, skip verifica variabili."
|
|
exit 0
|
|
fi
|
|
|
|
ALL_PLACEHOLDERS=$(grep -oP '\$\{[^}]+\}' "$APP_YML" | sort -u)
|
|
|
|
APP_VARS_REQUIRED=""
|
|
APP_VARS_WITH_DEFAULT=""
|
|
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 " Obbligatorie: $(echo "$APP_VARS_REQUIRED" | wc -w)"
|
|
echo " Con default: $(echo "$APP_VARS_WITH_DEFAULT" | wc -w)"
|
|
|
|
MISSING=""
|
|
cd /tmp/compose-repo
|
|
|
|
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 ""
|
|
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
|
|
|
|
# Separa REQUIRED da HAS_DEFAULT
|
|
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 ""
|
|
echo "WARNING: variabili con default mancanti nei compose (non bloccante):"
|
|
printf '%b' "$MISSING"
|
|
echo ""
|
|
echo "Queste variabili hanno un valore di default in application.yml — verificare se adeguato."
|
|
elif [ -n "$MISSING_REQUIRED" ]; then
|
|
echo ""
|
|
echo "WARNING: variabili obbligatorie mancanti nei compose:"
|
|
printf '%b' "$MISSING"
|
|
|
|
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 ""
|
|
echo "Issue creata su ${COMPOSE_REPO}."
|
|
else
|
|
echo "Tutte le variabili sono correttamente censite."
|
|
fi
|
|
|
|
- name: Push docker-compose aggiornato
|
|
if: ${{ inputs.compose-repo != '' }}
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.RELEASE_VERSION }}"
|
|
PROJECT_NAME="${{ steps.version.outputs.PROJECT_NAME }}"
|
|
cd /tmp/compose-repo
|
|
|
|
if git diff --quiet; then
|
|
echo "Nessuna modifica necessaria ai docker-compose"
|
|
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
|