From c94c0305d252ffe0867018b5f4cca50cdd029839 Mon Sep 17 00:00:00 2001 From: Zio Gabber <78922322+Gabrymi93@users.noreply.github.com> Date: Sun, 11 Oct 2026 19:08:50 +0100 Subject: [PATCH 1/2] =?UTF-8?q?feat(catalog):=20catalog-update-pr=20+=20ca?= =?UTF-8?q?talog-reusable=20=E2=80=94=20CI=20per=20source=20catalog=20per-?= =?UTF-8?q?repo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Componenti org per il catalog engine decentralizzato (source-observatory#421): - actions/catalog-update-pr: composite che gira so-catalog nello stesso job (stessa versione engine, come registry-update-pr), fa diff su registry/ e apre draft PR branch post-catalog/* con label e sintesi verdict nel body. Zero secrets GCS: il catalog e' solo JSON in git. - workflows/catalog-reusable.yml: workflow_call con repo-slug, catalog-args e dry-run (per PR-check quando cambia [tool.observatory]). A differenza di pipeline-reusable non ha bisegno di run pregresso ne WIF: probe + inventario live, continuita' streak dal catalog committato. Caller di esempio (eurostat) non incluso — vive nel repo dati. --- actions/catalog-update-pr/action.yml | 106 +++++++++++++++++++++++++++ workflows/catalog-reusable.yml | 77 +++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 actions/catalog-update-pr/action.yml create mode 100644 workflows/catalog-reusable.yml diff --git a/actions/catalog-update-pr/action.yml b/actions/catalog-update-pr/action.yml new file mode 100644 index 0000000..212e440 --- /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 diff --git a/workflows/catalog-reusable.yml b/workflows/catalog-reusable.yml new file mode 100644 index 0000000..dbd24ff --- /dev/null +++ b/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 }} From 0dfc3ee4f61e5488728aa156184812fadbc2b0d7 Mon Sep 17 00:00:00 2001 From: Zio Gabber <78922322+Gabrymi93@users.noreply.github.com> Date: Tue, 13 Oct 2026 12:16:19 +0100 Subject: [PATCH 2/2] fix(catalog-ci): workflow in .github/workflows/ + BODY newlines reali MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review findings su PR #38: - catalog-reusable.yml spostato da workflows/ (root) a .github/workflows/ — path coerente con pipeline-reusable e gli altri; il body dichiarava .github/workflows/ ma il file era al root - BODY con $'...' invece di "...\n..." — i newline erano letterali nel body della PR draft --- {workflows => .github/workflows}/catalog-reusable.yml | 0 actions/catalog-update-pr/action.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename {workflows => .github/workflows}/catalog-reusable.yml (100%) diff --git a/workflows/catalog-reusable.yml b/.github/workflows/catalog-reusable.yml similarity index 100% rename from workflows/catalog-reusable.yml rename to .github/workflows/catalog-reusable.yml diff --git a/actions/catalog-update-pr/action.yml b/actions/catalog-update-pr/action.yml index 212e440..fa40c77 100644 --- a/actions/catalog-update-pr/action.yml +++ b/actions/catalog-update-pr/action.yml @@ -88,7 +88,7 @@ runs: 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)" + 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" \