From 7fe9d3d027da0d18de58720d709c7c5a6c6d9723 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 20 Sep 2026 01:10:03 -0300 Subject: [PATCH 1/4] ci: fail early when workflow update token is missing --- .github/workflows/publish-workflow-catalog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/publish-workflow-catalog.yml b/.github/workflows/publish-workflow-catalog.yml index 71fac77..0210f5d 100644 --- a/.github/workflows/publish-workflow-catalog.yml +++ b/.github/workflows/publish-workflow-catalog.yml @@ -22,6 +22,15 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: + - name: Validate workflow update token + env: + WORKFLOW_UPDATE_TOKEN: ${{ secrets.WORKFLOW_UPDATE_TOKEN }} + run: | + if [ -z "$WORKFLOW_UPDATE_TOKEN" ]; then + echo "::error::WORKFLOW_UPDATE_TOKEN is not configured. It must have access to the target repositories and permission to create/update pull requests." + exit 1 + fi + - name: Checkout workflow source uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: From c4b5920257807ee866f88d076ab16091c59e413a Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 20 Sep 2026 01:10:06 -0300 Subject: [PATCH 2/4] ci: fail early when workflow update token is missing --- .github/workflows/sync-consumers.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/sync-consumers.yml b/.github/workflows/sync-consumers.yml index 9a4a1aa..428b58f 100644 --- a/.github/workflows/sync-consumers.yml +++ b/.github/workflows/sync-consumers.yml @@ -26,6 +26,15 @@ jobs: outputs: matrix: ${{ steps.matrix.outputs.matrix }} steps: + - name: Validate workflow update token + env: + WORKFLOW_UPDATE_TOKEN: ${{ secrets.WORKFLOW_UPDATE_TOKEN }} + run: | + if [ -z "$WORKFLOW_UPDATE_TOKEN" ]; then + echo "::error::WORKFLOW_UPDATE_TOKEN is not configured. It must have access to the target repositories and permission to create/update pull requests." + exit 1 + fi + - name: Checkout workflow source uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: From 6f2cbaa9d4e9976c79da4fbd20b90651128f180b Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 20 Sep 2026 01:10:30 -0300 Subject: [PATCH 3/4] test: preserve last known-good template on patch failure --- tests/test_render_upstream.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_render_upstream.py b/tests/test_render_upstream.py index 6f017b5..5234f05 100644 --- a/tests/test_render_upstream.py +++ b/tests/test_render_upstream.py @@ -127,6 +127,24 @@ def test_sync_reports_failure_and_continues(self) -> None: self.assertEqual(failed["name"], "broken") self.assertIn("failed to apply", failed["error"]) + def test_failed_patch_preserves_previous_generated_template(self) -> None: + with tempfile.TemporaryDirectory() as directory: + root, manifest = self.fixture(directory) + source = root / "upstream/vendor/example.yml" + source.write_text("name: Changed upstream\n", encoding="utf-8") + destination = root / "workflow-templates/example.yml" + destination.parent.mkdir(parents=True) + destination.write_text("name: Last known good\n", encoding="utf-8") + + report = sync(load_templates(manifest), root) + + self.assertFalse(report["ok"]) + self.assertEqual(report["failed"], 1) + self.assertEqual( + destination.read_text(encoding="utf-8"), + "name: Last known good\n", + ) + def test_write_report(self) -> None: with tempfile.TemporaryDirectory() as directory: report_path = Path(directory) / "report.json" From 1c95e0da46051cff5a7aa0b942b2884b055127d1 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Sun, 20 Sep 2026 01:10:33 -0300 Subject: [PATCH 4/4] test: prevent partial catalog updates from invalid source --- tests/test_sync_catalog.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_sync_catalog.py b/tests/test_sync_catalog.py index b2512e1..556b8d0 100644 --- a/tests/test_sync_catalog.py +++ b/tests/test_sync_catalog.py @@ -104,6 +104,26 @@ def test_requires_custom_svg_icon(self) -> None: ) collect_publishable(source) + def test_invalid_source_does_not_partially_modify_catalog(self) -> None: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + source = root / "source" + target = root / "target" + self.write_template(source) + target.mkdir() + existing = target / "reuse.yml" + existing.write_text("name: Published\n", encoding="utf-8") + + (source / "reuse.properties.json").unlink() + + with self.assertRaisesRegex(ValueError, "missing template metadata"): + sync_catalog(source, target) + + self.assertEqual( + existing.read_text(encoding="utf-8"), + "name: Published\n", + ) + def test_check_detects_drift(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory)