commit ddc78dd5baeb892586cbe31a9b53194bad3f1fb9 Author: Nicolò Rossi Date: Sun May 31 11:52:44 2026 +0200 intial release diff --git a/.gitea/workflows/maven-compile.yml b/.gitea/workflows/maven-compile.yml new file mode 100644 index 0000000..66fdc01 --- /dev/null +++ b/.gitea/workflows/maven-compile.yml @@ -0,0 +1,49 @@ +name: Maven Compile (reusable) + +on: + workflow_call: + inputs: + maven-profiles: + description: "Profili Maven da attivare (es. bitwork,ci)" + required: false + type: string + default: "" + +jobs: + compile: + 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 }} + steps: + - uses: actions/checkout@v6 + + - name: Checkout CI config + run: | + git clone --depth=1 "https://biters.gitea:${{ secrets.REPO_ACCESS_TOKEN }}@git.biters.it/Biters/ci-workflows.git" .ci-config + + - name: Verifica configurazione + run: | + missing=0 + for v in MAVEN_REPO_URL MAVEN_REPO_RELEASES_URL MAVEN_REPO_SNAPSHOTS_URL MAVEN_USERNAME MAVEN_PASSWORD; 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; } + + - name: Compile + run: | + MVN_PROFILES="" + [ -n "${{ inputs.maven-profiles }}" ] && MVN_PROFILES="-P${{ inputs.maven-profiles }}" + mvn -B -U $MVN_PROFILES -s .ci-config/settings.xml -DskipTests clean compile diff --git a/.gitea/workflows/maven-release.yml b/.gitea/workflows/maven-release.yml new file mode 100644 index 0000000..e808677 --- /dev/null +++ b/.gitea/workflows/maven-release.yml @@ -0,0 +1,336 @@ +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 }} + DOCKER_REPO_NEXUS_URL: ${{ vars.DOCKER_REPO_NEXUS_URL }} + DOCKER_HUB_REPO_URL: ${{ vars.DOCKER_HUB_REPO_URL }} + 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 \ + DOCKER_REPO_NEXUS_URL DOCKER_HUB_REPO_URL 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 + + if [ -n "$MISSING" ]; then + echo "" + echo "VARIABILI MANCANTI:" + 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}. Push bloccato." + exit 1 + fi + + echo "Tutte le variabili sono correttamente censite." + + - 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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85e7c1d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea/ diff --git a/Dockerfile.ci b/Dockerfile.ci new file mode 100644 index 0000000..ca4bb3d --- /dev/null +++ b/Dockerfile.ci @@ -0,0 +1,10 @@ +FROM maven:3.9-eclipse-temurin-21 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl ca-certificates git jq \ + && curl -sL "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64" \ + -o /usr/local/bin/yq && chmod +x /usr/local/bin/yq \ + && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get install -y nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* diff --git a/settings.xml b/settings.xml new file mode 100644 index 0000000..50a7f37 --- /dev/null +++ b/settings.xml @@ -0,0 +1,80 @@ + + + + + nexus.public + ${env.MAVEN_USERNAME} + ${env.MAVEN_PASSWORD} + + + archiva.internal + ${env.MAVEN_USERNAME} + ${env.MAVEN_PASSWORD} + + + Nexus.snapshots + ${env.MAVEN_USERNAME} + ${env.MAVEN_PASSWORD} + + + + + + nexus.public + ${env.MAVEN_REPO_URL} + * + + + archiva.internal + ${env.MAVEN_REPO_RELEASES_URL} + * + + + nexus.snapshots + ${env.MAVEN_REPO_SNAPSHOTS_URL} + nexus.snapshots + + + + + + biters + + true + + + + nexus.public + Nexus Maven Public Repository + ${env.MAVEN_REPO_URL} + true + true + + + archiva.internal + Nexus Managed Internal Repository + ${env.MAVEN_REPO_RELEASES_URL} + true + false + + + nexus.snapshots + Nexus Managed Snapshots Repository + ${env.MAVEN_REPO_SNAPSHOTS_URL} + false + true + + + + + nexus.public + ${env.MAVEN_REPO_URL} + true + true + + + + +