diff --git a/.github/workflows/catalog-reusable.yml b/.github/workflows/catalog-reusable.yml new file mode 100644 index 0000000..dbd24ff --- /dev/null +++ b/.github/workflows/catalog-reusable.yml @@ -0,0 +1,77 @@ +# Catalog (reusable) — DataCivicLab +# +# Workflow riusabile per generare/aggiornare il source catalog per-repo +# (`registry/catalog.json`) con l'engine SO condiviso. +# +# Differenze chiave da pipeline-reusable: +# - ZERO secrets: niente GCS/WIF — il catalog è solo JSON in git +# - nessun run pregresso: probe + inventario live della fonte; la +# continuità (streak, baseline) arriva dal catalog.json committato +# - trigger indipendente dalla pipeline dati (la fonte upstream cambia +# per conto proprio) +# +# Il repo chiama con i propri input: +# - repo-slug: usato nel titolo PR di fallback +# - catalog-args: argomenti per so-catalog (es. "--workers 4") +# - dry-run: valida la build senza creare PR (usato sui PR che toccano +# pyproject.toml / la config [tool.observatory]) + +name: Catalog (reusable) + +on: + workflow_call: + inputs: + repo-slug: + description: "Slug del repo (es. eurostat, open-mur)" + required: true + type: string + install-extras: + description: "Extras pyproject da installare (deve includere 'observatory')" + default: "dev,observatory" + type: string + catalog-args: + description: "Argomenti extra per so-catalog (es. --workers 4)" + default: "" + type: string + dry-run: + description: "Build senza scrivere/committare (validazione PR)" + default: false + type: boolean + runs-on: + description: "Runner label" + default: "ubuntu-latest" + type: string + +jobs: + catalog: + runs-on: ${{ inputs.runs-on || 'ubuntu-latest' }} + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - uses: dataciviclab/.github/actions/python-setup@main + with: + install-extras: ${{ inputs.install-extras }} + + - name: Build catalog (dry-run) + if: inputs.dry-run + shell: bash + run: | + command -v so-catalog >/dev/null 2>&1 || { + echo "::error::so-catalog non trovato — extra 'observatory' installato?" + exit 1 + } + so-catalog ${{ inputs.catalog-args }} --dry-run + + - name: Build catalog + draft PR + if: ${{ !inputs.dry-run }} + uses: dataciviclab/.github/actions/catalog-update-pr@main + with: + catalog-args: ${{ inputs.catalog-args }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/actions/catalog-update-pr/action.yml b/actions/catalog-update-pr/action.yml new file mode 100644 index 0000000..fa40c77 --- /dev/null +++ b/actions/catalog-update-pr/action.yml @@ -0,0 +1,106 @@ +name: 'Catalog Update PR (org-level)' +description: | + so-catalog → diff → draft PR, come STEP nel job che ha già installato + l'engine SO (extra `observatory` del repo). + + DEVE girare nello stesso job dopo `python-setup` con extras che includono + `observatory` — non reinstalla SO: usa la stessa versione già nel job. + A differenza di registry-update-pr, non serve `out/` da un run precedente: + la continuità (streak radar, baseline) arriva dal `registry/catalog.json` + committato, e il build parte da una probe + inventario live della fonte. + + Il job chiamante deve avere `permissions: contents: write, pull-requests: write`. + Nessun secret GCS richiesto: il catalog è solo JSON in git. + + Usage (dopo python-setup): + - uses: dataciviclab/.github/actions/catalog-update-pr@main + env: + GH_TOKEN: secrets.GITHUB_TOKEN + +inputs: + catalog-args: + description: "Argomenti extra per so-catalog (es. --workers 4)" + required: false + default: "" + registry-dir: + description: "Directory controllata nel diff (git status). Contiene catalog.json." + required: false + default: "registry" + pr-label: + description: "Label per la draft PR" + required: false + default: "post-catalog" + +runs: + using: "composite" + steps: + - name: Build catalog + shell: bash + run: | + command -v so-catalog >/dev/null 2>&1 || { + echo "::error::so-catalog non trovato — catalog-update-pr deve girare " + echo "in un job dove python-setup ha già installato l'extra 'observatory'" + exit 1 + } + so-catalog ${{ inputs.catalog-args }} + + - name: Check catalog diff + id: diff + shell: bash + run: | + if git status --porcelain -- ${{ inputs.registry-dir }} | grep -q .; then + echo "changed=true" >> "$GITHUB_OUTPUT" + else + echo "changed=false" >> "$GITHUB_OUTPUT" + fi + + - name: Extract verdict for PR body + id: verdict + if: steps.diff.outputs.changed == 'true' + shell: bash + run: | + CATALOG="${{ inputs.registry-dir }}/catalog.json" + if [ -f "$CATALOG" ]; then + VERDICT=$(jq -r '.operational_verdict.label // "unknown"' "$CATALOG") + RADAR=$(jq -r '.health.radar_status // "unknown"' "$CATALOG") + ITEMS=$(jq -r '.inventory.total_items // 0' "$CATALOG") + DATASETS=$(jq -r '.datasets_in_use | length' "$CATALOG") + echo "verdict=$VERDICT" >> "$GITHUB_OUTPUT" + echo "summary=radar=$RADAR · items=$ITEMS · datasets_in_use=$DATASETS" >> "$GITHUB_OUTPUT" + else + echo "verdict=unknown" >> "$GITHUB_OUTPUT" + echo "summary=catalog.json non leggibile" >> "$GITHUB_OUTPUT" + fi + + - name: Create draft catalog PR + if: steps.diff.outputs.changed == 'true' + shell: bash + env: + GH_TOKEN: ${{ env.GH_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event_name || 'schedule' }} + VERDICT: ${{ steps.verdict.outputs.verdict }} + SUMMARY: ${{ steps.verdict.outputs.summary }} + run: | + BRANCH="post-catalog/${PR_NUMBER}" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -B "$BRANCH" + git add ${{ inputs.registry-dir }} + git commit -m "chore(post-catalog): aggiorna source catalog ($PR_NUMBER)" || exit 0 + git push --force-with-lease -u origin "$BRANCH" + BODY=$'Source catalog refresh.\n\n- verdict: **'"$VERDICT"$'**\n- '"$SUMMARY"$'\n- generated by `so-catalog` in CI (engine dataciviclab-source-observatory)' + EXISTING_PR=$(gh pr list --head "$BRANCH" --json number --jq '.[0].number // empty') + if [ -n "$EXISTING_PR" ]; then + gh pr edit "$EXISTING_PR" \ + --title "chore(post-catalog): aggiorna source catalog ($PR_NUMBER)" \ + --body "$BODY" \ + --add-label "${{ inputs.pr-label }}" + else + gh pr create \ + --base main \ + --head "$BRANCH" \ + --title "chore(post-catalog): aggiorna source catalog ($PR_NUMBER)" \ + --body "$BODY" \ + --label "${{ inputs.pr-label }}" \ + --draft + fi