Sentinel #30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Sentinel | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: "0 7,8 * * 6" | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: sentinel-scan | |
| cancel-in-progress: false | |
| jobs: | |
| scan: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Check Europe/Rome schedule | |
| id: schedule | |
| if: github.event_name == 'schedule' | |
| run: | | |
| # Non guardare l'ora a runtime: GitHub ritarda i job schedulati anche | |
| # di oltre un'ora, quindi l'ora di Roma è quasi sempre gia' scivolata | |
| # oltre le 09:00. Decidi in base a QUALE cron ha triggerato + fuso DST. | |
| # 09:00 Europe/Rome = 07:00 UTC in estate (+0200), 08:00 UTC in inverno (+0100). | |
| offset=$(TZ=Europe/Rome date +%z) | |
| sched="${{ github.event.schedule }}" | |
| should_run=false | |
| if [ "$offset" = "+0200" ] && [ "$sched" = "0 7 * * 6" ]; then should_run=true; fi | |
| if [ "$offset" = "+0100" ] && [ "$sched" = "0 8 * * 6" ]; then should_run=true; fi | |
| if [ "$should_run" != "true" ]; then | |
| echo "Cron $sched con offset $offset fuori dalla finestra 09:00 Europe/Rome, skip." | |
| fi | |
| echo "should_run=$should_run" >> "$GITHUB_OUTPUT" | |
| - name: Checkout | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| uses: actions/checkout@v7 | |
| - name: Setup Node | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| - name: Install | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: npm ci --ignore-scripts | |
| - name: Enable esbuild | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: npm rebuild esbuild | |
| - name: Test | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: npm test | |
| - name: Build | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: npm run build | |
| - name: Apply GitHub email secret fallback | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| env: | |
| GITHUB_SENTINEL_EMAIL_TO: ${{ secrets.SENTINEL_EMAIL_TO }} | |
| GITHUB_SENTINEL_EMAIL_FROM: ${{ secrets.SENTINEL_EMAIL_FROM }} | |
| GITHUB_SENTINEL_GMAIL_USER: ${{ secrets.SENTINEL_GMAIL_USER }} | |
| GITHUB_SENTINEL_GMAIL_APP_PASSWORD: ${{ secrets.SENTINEL_GMAIL_APP_PASSWORD }} | |
| GITHUB_SENTINEL_ICLOUD_USER: ${{ secrets.SENTINEL_ICLOUD_USER }} | |
| GITHUB_SENTINEL_ICLOUD_APP_PASSWORD: ${{ secrets.SENTINEL_ICLOUD_APP_PASSWORD }} | |
| GITHUB_BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }} | |
| run: | | |
| for name in \ | |
| SENTINEL_EMAIL_TO \ | |
| SENTINEL_EMAIL_FROM \ | |
| SENTINEL_GMAIL_USER \ | |
| SENTINEL_GMAIL_APP_PASSWORD \ | |
| SENTINEL_ICLOUD_USER \ | |
| SENTINEL_ICLOUD_APP_PASSWORD \ | |
| BLOB_READ_WRITE_TOKEN | |
| do | |
| fallback_name="GITHUB_${name}" | |
| if [ -z "${!name:-}" ] && [ -n "${!fallback_name:-}" ]; then | |
| echo "${name}=${!fallback_name}" >> "$GITHUB_ENV" | |
| fi | |
| done | |
| - name: Check email secrets | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: | | |
| missing=0 | |
| for name in \ | |
| SENTINEL_EMAIL_TO \ | |
| SENTINEL_EMAIL_FROM \ | |
| SENTINEL_GMAIL_USER \ | |
| SENTINEL_GMAIL_APP_PASSWORD | |
| do | |
| if [ -z "${!name}" ]; then | |
| echo "::error::Secret mancante: $name" | |
| missing=1 | |
| fi | |
| done | |
| if [ "$missing" -ne 0 ]; then | |
| echo "Configura i secret Gmail prima di avviare la scansione Sentinel." | |
| exit 1 | |
| fi | |
| - name: Scan | |
| id: scan | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: | | |
| set +e | |
| npm run sentinel -- scan | |
| status=$? | |
| echo "exit_code=$status" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| - name: Generate dashboard | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: npm run sentinel -- dashboard | |
| - name: Publish dashboard to Vercel Blob | |
| # La dashboard live legge da Vercel Blob: senza questo step resta ferma | |
| # anche se lo scan gira. Salta con notice se il token non e' disponibile. | |
| if: ${{ github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' }} | |
| run: | | |
| if [ -z "${BLOB_READ_WRITE_TOKEN:-}" ]; then | |
| echo "::warning::BLOB_READ_WRITE_TOKEN assente: dashboard live non aggiornata." | |
| exit 0 | |
| fi | |
| npm run sentinel -- publish-dashboard | |
| - name: Commit application outputs | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| mkdir -p data snapshots reports | |
| git add data snapshots reports | |
| if git diff --cached --quiet; then | |
| echo "Nessun output da committare." | |
| exit 0 | |
| fi | |
| git commit -m "chore: update sentinel outputs" | |
| git push | |
| - name: Fail on scan errors | |
| if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true' | |
| run: | | |
| if [ "${{ steps.scan.outputs.exit_code }}" != "0" ]; then | |
| echo "Scan conclusa con exit code ${{ steps.scan.outputs.exit_code }}." | |
| exit 1 | |
| fi |