diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index aa5cc40..18046e6 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -5,6 +5,8 @@ on: branches: [main] pull_request: branches: [main] + schedule: + - cron: "0 6 * * 1" jobs: build-and-scan: @@ -17,125 +19,169 @@ jobs: persist-credentials: false - name: Build prod image - run: docker build -t lumeex:ci . + run: | + docker build -t lumeex:ci . 2>&1 | tee build.log + if grep -q "Building wheel for" build.log; then + echo "::warning::A dependency was built from source — check Python/Alpine compatibility" + fi - name: Smoke test run: | docker run --rm --entrypoint python lumeex:ci -c " - import ast - ast.parse(open('build.py').read()) - ast.parse(open('gallery.py').read()) - print('syntax ok') + import ast, glob + ok = True + for f in glob.glob('/app/*.py'): + with open(f) as fh: + source = fh.read() + try: + ast.parse(source, filename=f) + except SyntaxError as e: + print(f'::error::Syntax error in {f}: {e}') + ok = False + if not ok: + exit(1) + print('OK: syntax is valid') " - - name: Build test image - run: docker build --target test -t lumeex:test . - - - name: Unit tests - run: docker run --rm lumeex:test pytest -v + - name: Run unit tests + run: | + docker build --target test -t lumeex:test . + docker run --rm lumeex:test pytest -v - name: Check deprecation warnings - run: docker run --rm --entrypoint python lumeex:ci -W error::DeprecationWarning -c "import src.py.webui.webui" + run: | + docker run --rm --entrypoint python lumeex:ci -W error::DeprecationWarning -c "import src.py.webui.webui" 2>&1 | tee deprecation.log || true + if grep -qi "deprecat" deprecation.log; then + echo "::warning::Deprecation warning detected, check logs" + fi - - name: Trivy critical (blocking) + - name: Scan with Trivy (critical - blocking) run: | docker run --rm \ - -e DOCKER_HOST=tcp://dockerhost:2375 --add-host=dockerhost:host-gateway \ - -v /var/run/docker.sock:/var/run/docker.sock \ - aquasec/trivy image --exit-code 1 --severity CRITICAL lumeex:ci + -e DOCKER_HOST=tcp://dockerhost:2375 \ + --add-host=dockerhost:host-gateway \ + aquasec/trivy:0.74.0 image --exit-code 1 --severity CRITICAL lumeex:ci - - name: Trivy high (informative) + - name: Scan with Trivy (high - informative) run: | docker run --rm \ - -e DOCKER_HOST=tcp://dockerhost:2375 --add-host=dockerhost:host-gateway \ - -v /var/run/docker.sock:/var/run/docker.sock \ - aquasec/trivy image --exit-code 0 --severity HIGH lumeex:ci + -e DOCKER_HOST=tcp://dockerhost:2375 \ + --add-host=dockerhost:host-gateway \ + aquasec/trivy:0.74.0 image --exit-code 0 --severity HIGH lumeex:ci - name: Publish tagged image if: github.event_name == 'push' && github.ref == 'refs/heads/main' - env: - REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} - CI_PUSH_TOKEN: ${{ secrets.CI_PUSH_TOKEN }} - GITEA_URL: https://git.djeex.fr - IMAGE: git.djeex.fr/djeex/lumeex run: | - set -e - - # Only republish when a container-relevant file actually changed BEFORE="${{ github.event.before }}" - if [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then - BEFORE="HEAD~1" + if [ -n "$BEFORE" ] && [ "$BEFORE" != "0000000000000000000000000000000000000000" ] && git cat-file -e "$BEFORE" 2>/dev/null; then + BASE_REF="$BEFORE" + else + BASE_REF="HEAD~1" fi - CHANGED=$(git diff --name-only "$BEFORE" "${{ github.sha }}" -- \ - Dockerfile requirements.txt VERSION build.py gallery.py \ - src/ config/ docker/.sh/entrypoint.sh || true) + CHANGED=$(git diff --name-only "$BASE_REF" "${{ github.sha }}") + echo "Changed files:" + echo "$CHANGED" - if [ -z "$CHANGED" ]; then - echo "No container-relevant changes, skipping publish." + if ! echo "$CHANGED" | grep -qE '^(Dockerfile|requirements\.txt|VERSION|build\.py|gallery\.py)$|^(src|config)/|^docker/\.sh/entrypoint\.sh$'; then + echo "No container-relevant file changed, skipping publish." exit 0 fi - VERSION_BUMPED=false - if ! echo "$CHANGED" | grep -qx "VERSION"; then - CURRENT_VERSION=$(cat VERSION) - MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1) - MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2) - PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3) - NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))" + if echo "$CHANGED" | grep -qE '^VERSION$'; then + echo "VERSION was manually edited in this push, using it as-is." + else + echo "VERSION untouched but container files changed, auto-bumping the build number (Z)." + OLD_VERSION=$(tr -d '[:space:]' < VERSION) + IFS='.' read -r MAJOR MINOR PATCH <<< "$OLD_VERSION" + NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" echo "$NEW_VERSION" > VERSION - git config user.name "gitea-ci" + git config user.name "lumeex-ci" git config user.email "ci@git.djeex.fr" - git remote set-url origin "https://gitea-ci:${CI_PUSH_TOKEN}@git.djeex.fr/Djeex/lumeex.git" git add VERSION git commit -m "chore: bump version to $NEW_VERSION [skip ci]" - git push origin HEAD:main - git config --unset-all "http.https://git.djeex.fr/Djeex/lumeex.git/.extraheader" || true - VERSION_BUMPED=true + + # Belt and suspenders: actions/checkout can leave its own ephemeral + # credential injected as an extraheader, which would silently override + # the URL-embedded token below. persist-credentials:false on checkout + # should already prevent this, but strip it here too just in case. + git config --unset-all http.https://git.djeex.fr/.extraheader || true + + git push "https://Djeex:${{ secrets.CI_PUSH_TOKEN }}@git.djeex.fr/Djeex/lumeex.git" HEAD:main fi - FULL_VERSION=$(cat VERSION) - MAJOR_MINOR=$(echo "$FULL_VERSION" | cut -d. -f1,2) + VERSION=$(tr -d '[:space:]' < VERSION) + IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION" + MINOR_TAG="${MAJOR}.${MINOR}" - echo "$REGISTRY_TOKEN" | docker login git.djeex.fr -u Djeex --password-stdin + IMAGE=git.djeex.fr/djeex/lumeex + echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.djeex.fr -u Djeex --password-stdin + # Retag the already-built, already-scanned image — never rebuild for publish, + # so what ships is byte-for-byte what Trivy just scanned. docker tag lumeex:ci "$IMAGE:latest" - docker tag lumeex:ci "$IMAGE:$MAJOR_MINOR" - docker tag lumeex:ci "$IMAGE:$FULL_VERSION" + docker tag lumeex:ci "$IMAGE:$MINOR_TAG" + docker tag lumeex:ci "$IMAGE:$VERSION" docker push "$IMAGE:latest" - docker push "$IMAGE:$MAJOR_MINOR" - docker push "$IMAGE:$FULL_VERSION" + docker push "$IMAGE:$MINOR_TAG" + docker push "$IMAGE:$VERSION" + + TRIGGER_MSG=$(git log -1 --format=%s "${{ github.sha }}") + PR_NUM=$(echo "$TRIGGER_MSG" | grep -oE '#[0-9]+' | head -1 | tr -d '#' || true) - # Categorize the changelog from the merged PR's labels, if any - PR_NUMBER=$(git log -1 --format=%B "${{ github.sha }}" | grep -oE '#[0-9]+' | head -1 | tr -d '#' || true) CATEGORY="🔧 Maintenance" - PR_LINK="" - if [ -n "$PR_NUMBER" ]; then - PR_LINK="https://git.djeex.fr/Djeex/lumeex/pulls/$PR_NUMBER" - LABELS=$(curl -s -H "Authorization: token $CI_PUSH_TOKEN" \ - "$GITEA_URL/api/v1/repos/Djeex/lumeex/issues/$PR_NUMBER/labels" | jq -r '.[].name' || true) - if echo "$LABELS" | grep -qx "bug"; then + CHANGE_TITLE="$TRIGGER_MSG" + + if [ -n "$PR_NUM" ]; then + PR_JSON=$(curl -s -H "Authorization: token ${{ secrets.CI_PUSH_TOKEN }}" \ + "https://git.djeex.fr/api/v1/repos/Djeex/lumeex/pulls/$PR_NUM") + PR_TITLE=$(echo "$PR_JSON" | jq -r '.title // empty' 2>/dev/null || true) + LABELS=$(echo "$PR_JSON" | jq -r '.labels[]?.name' 2>/dev/null || true) + + if [ -n "$PR_TITLE" ]; then + CHANGE_TITLE="$PR_TITLE" + fi + + if echo "$LABELS" | grep -qx 'bug'; then CATEGORY="⚠️ Hotfix" - elif echo "$LABELS" | grep -qx "major"; then + elif echo "$LABELS" | grep -qx 'major'; then CATEGORY="💥 Breaking change" - elif echo "$LABELS" | grep -qx "minor"; then + elif echo "$LABELS" | grep -qx 'minor'; then CATEGORY="✨ Update" fi fi - COMMIT_LINK="https://git.djeex.fr/Djeex/lumeex/commit/${{ github.sha }}" - BODY=$(printf '**%s**\n\nCommit: %s' "$CATEGORY" "$COMMIT_LINK") - if [ -n "$PR_LINK" ]; then - BODY=$(printf '%s\nPR: %s' "$BODY" "$PR_LINK") + CHANGED_LIST=$(echo "$CHANGED" | sed 's/^/- /') + + REPO_URL="https://git.djeex.fr/Djeex/lumeex" + SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) + SOURCE_LINE="[${SHORT_SHA}](${REPO_URL}/commit/${{ github.sha }})" + if [ -n "$PR_NUM" ]; then + SOURCE_LINE="[#${PR_NUM}](${REPO_URL}/pulls/${PR_NUM}) · ${SOURCE_LINE}" fi - jq -n \ - --arg tag "$FULL_VERSION" \ - --arg name "$FULL_VERSION" \ + BODY=$(cat <