Skip to content

Sentinel

Sentinel #44

Workflow file for this run

name: Sentinel
on:
workflow_dispatch:
schedule:
- cron: "0 7 * * 6"
- cron: "0 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: Restore application outputs
if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true'
run: |
output_branch="sentinel-outputs"
output_remote_ref="refs/heads/$output_branch"
output_ref="refs/remotes/origin/$output_branch"
git fetch origin "$output_remote_ref:$output_ref"
git restore --source="$output_ref" --worktree -- data snapshots reports
- 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: Bootstrap npm 12
if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true'
run: npm install --global npm@12.0.2
- 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: Gate completo
if: github.event_name != 'schedule' || steps.schedule.outputs.should_run == 'true'
run: npm run check
- 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
output_branch="sentinel-outputs"
output_remote_ref="refs/heads/$output_branch"
output_ref="refs/remotes/origin/$output_branch"
git fetch origin "$output_remote_ref:$output_ref"
output_dir="$(mktemp -d)"
output_index="$output_dir/index"
trap 'unlink "$output_index" 2>/dev/null || true; rmdir "$output_dir" 2>/dev/null || true' EXIT
GIT_INDEX_FILE="$output_index" git read-tree --empty
GIT_INDEX_FILE="$output_index" git add -- data snapshots reports
output_tree="$(GIT_INDEX_FILE="$output_index" git write-tree)"
previous_commit="$(git rev-parse "$output_ref")"
previous_tree="$(git rev-parse "$output_ref^{tree}")"
if [ "$output_tree" = "$previous_tree" ]; then
echo "Nessun output da committare."
exit 0
fi
output_commit="$(printf '%s\n' 'chore: update sentinel outputs' | git commit-tree "$output_tree" -p "$previous_commit")"
git push origin "${output_commit}:refs/heads/${output_branch}"
- 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