From 3bdf02644e04d034f9ee4a35eedb7a8490cf1b56 Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Tue, 29 Sep 2026 16:01:44 +0200 Subject: [PATCH 1/9] feat(workflow): gate prepare-sources post-export behind prepare-downstream-sources Add optional post-export steps in export-dynamic that invoke the prepare-sources action with workspace/overlay paths derived from plugins-root (flat vs non-flat) and upload the prepared tree as a workflow artifact until construct-artifact lands. Pass the new input through export-workspaces-as-dynamic; default false keeps existing export behavior unchanged (RHIDP-15846). Co-authored-by: Cursor --- .github/workflows/export-dynamic.yaml | 38 +++++++++++++++++++ .../export-workspaces-as-dynamic.yaml | 10 +++++ 2 files changed, 48 insertions(+) diff --git a/.github/workflows/export-dynamic.yaml b/.github/workflows/export-dynamic.yaml index 25d7ebdb..16945f9f 100644 --- a/.github/workflows/export-dynamic.yaml +++ b/.github/workflows/export-dynamic.yaml @@ -128,6 +128,16 @@ on: description: Optional commit ID of the last successful publishing of plugin container images type: string required: false + + prepare-downstream-sources: + description: > + When true, after a successful plugin export run the prepare-sources CLI + (prepare-rhdh-supported-sources) to transform the workspace into a + prepared downstream source tree. Default false leaves existing export + behavior unchanged. + type: boolean + default: false + required: false outputs: published-exports: @@ -356,6 +366,34 @@ jobs: retention-days: ${{ inputs.artifact-retention-days }} overwrite: true + # RHIDP-15846: optional post-export prepare-sources pipeline. + # workspace-path uses plugins-root (. = flat repo; workspaces/ = non-flat). + # overlay-path is the checked-out overlay workspace directory (source.json). + # Until construct-artifact (RHIDP-15845) lands, upload the prepared tree as a + # workflow artifact instead of pushing an OCI image. + - name: Prepare downstream sources + if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} + id: prepare-downstream-sources + uses: redhat-developer/rhdh-plugin-export-utils/prepare-sources@main + with: + workspace-path: ${{ github.workspace }}/source-repo/${{ inputs.plugins-root }} + overlay-path: ${{ github.workspace }}/overlay-repo/${{ inputs.overlay-root }} + + - name: Upload prepared downstream sources + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + if: ${{ inputs.prepare-downstream-sources && success() && steps.prepare-downstream-sources.outcome == 'success' }} + with: + name: prepared downstream sources${{ steps.set-artifacts-name-suffix.outputs.ARTIFACTS_NAME_SUFFIX }} + path: | + ${{ github.workspace }}/source-repo/${{ inputs.plugins-root }} + !**/node_modules + !**/.yarn/cache + !**/.git + if-no-files-found: warn + retention-days: ${{ inputs.artifact-retention-days }} + overwrite: true + include-hidden-files: true + - name: Upload the project to workflow artifacts on failure uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: ${{ inputs.upload-project-on-error && (failure() || steps.export-dynamic.outputs.failed-exports) }} diff --git a/.github/workflows/export-workspaces-as-dynamic.yaml b/.github/workflows/export-workspaces-as-dynamic.yaml index 5aa1dc99..4461787c 100644 --- a/.github/workflows/export-workspaces-as-dynamic.yaml +++ b/.github/workflows/export-workspaces-as-dynamic.yaml @@ -74,6 +74,15 @@ on: type: string required: false + prepare-downstream-sources: + description: > + When true, after a successful plugin export run the prepare-sources CLI + to transform each workspace into a prepared downstream source tree. + Default false leaves existing export behavior unchanged. + type: boolean + default: false + required: false + outputs: published-exports: value: '${{ jobs.export.outputs.published-exports }}' @@ -289,6 +298,7 @@ jobs: target-backstage-version: ${{ needs.prepare.outputs.backstage-version }} last-publish-commit: ${{ inputs.last-publish-commit }} image-registry-user: ${{ inputs.image-registry-user }} + prepare-downstream-sources: ${{ inputs.prepare-downstream-sources }} secrets: image-registry-password: ${{ secrets.image-registry-password }} From 5607ba36d05d4211bc4021767d2c980eff0ab8d1 Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Tue, 29 Sep 2026 16:28:55 +0200 Subject: [PATCH 2/9] test: add workflow_dispatch harness for prepare-downstream-sources Same-repo reusable call so we can exercise RHIDP-15846 path wiring on topology/backstage without depending on cross-fork workflow_call (startup_failure). Co-authored-by: Cursor --- .../test-prepare-downstream-sources.yaml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/test-prepare-downstream-sources.yaml diff --git a/.github/workflows/test-prepare-downstream-sources.yaml b/.github/workflows/test-prepare-downstream-sources.yaml new file mode 100644 index 00000000..0d8fc94a --- /dev/null +++ b/.github/workflows/test-prepare-downstream-sources.yaml @@ -0,0 +1,35 @@ +# Temporary RHIDP-15846 test harness — remove after AC sign-off. +# Calls the same-repo reusable export workflow (fork→fork reusable calls startup-fail). +name: Test Prepare Downstream Sources Export +on: + workflow_dispatch: + inputs: + workspace-path: + description: Overlay workspace path (e.g. workspaces/topology or workspaces/backstage) + type: string + required: true + default: workspaces/topology + prepare-downstream-sources: + description: Exercise prepare-sources post-export steps + type: boolean + required: false + default: true + +jobs: + export: + uses: ./.github/workflows/export-workspaces-as-dynamic.yaml + with: + workspace-path: ${{ inputs.workspace-path }} + overlay-repo: redhat-developer/rhdh-plugin-export-overlays + overlay-branch: main + publish-container: false + upload-project-on-error: true + prepare-downstream-sources: ${{ inputs.prepare-downstream-sources }} + image-registry-user: ${{ github.actor }} + secrets: + image-registry-password: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write + packages: write + attestations: write + id-token: write From e22e9abbe6071b04da9952a84c77fe0ed82b7c16 Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Tue, 29 Sep 2026 16:30:21 +0200 Subject: [PATCH 3/9] fix(workflow): call export-dynamic via relative path Pinning the nested reusable workflow to @main dropped new inputs such as prepare-downstream-sources and caused caller startup_failure. Same-repo relative uses keeps both workflows on the same ref. Co-authored-by: Cursor --- .github/workflows/export-workspaces-as-dynamic.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/export-workspaces-as-dynamic.yaml b/.github/workflows/export-workspaces-as-dynamic.yaml index 4461787c..9436943f 100644 --- a/.github/workflows/export-workspaces-as-dynamic.yaml +++ b/.github/workflows/export-workspaces-as-dynamic.yaml @@ -273,7 +273,9 @@ jobs: export: name: Export ${{ matrix.workspace.overlay-root }} needs: prepare - uses: redhat-developer/rhdh-plugin-export-utils/.github/workflows/export-dynamic.yaml@main + # Relative path keeps export-dynamic in lockstep with this workflow's ref + # (needed for new inputs like prepare-downstream-sources; avoids @main drift). + uses: ./.github/workflows/export-dynamic.yaml strategy: fail-fast: false matrix: From 82e1bef1f64a85c0fa7d159f4e97318e86fe448c Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Wed, 30 Sep 2026 09:00:28 +0200 Subject: [PATCH 4/9] fix(workflow): normalize flat plugins-root for prepared-sources upload upload-artifact rejects paths ending in "/." which is what we built for flat workspaces (plugins-root=.). Resolve "." to source-repo before invoking prepare-sources and uploading the artifact. Co-authored-by: Cursor --- .github/workflows/export-dynamic.yaml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/export-dynamic.yaml b/.github/workflows/export-dynamic.yaml index 16945f9f..245fb08b 100644 --- a/.github/workflows/export-dynamic.yaml +++ b/.github/workflows/export-dynamic.yaml @@ -371,13 +371,28 @@ jobs: # overlay-path is the checked-out overlay workspace directory (source.json). # Until construct-artifact (RHIDP-15845) lands, upload the prepared tree as a # workflow artifact instead of pushing an OCI image. + - name: Resolve prepared sources paths + if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} + id: prepare-paths + env: + PLUGINS_ROOT: ${{ inputs.plugins-root }} + OVERLAY_ROOT: ${{ inputs.overlay-root }} + run: | + # Flat repos use plugins-root "."; upload-artifact rejects paths ending in "/." + if [[ "${PLUGINS_ROOT}" == "." || -z "${PLUGINS_ROOT}" ]]; then + echo "workspace_path=${GITHUB_WORKSPACE}/source-repo" >> "$GITHUB_OUTPUT" + else + echo "workspace_path=${GITHUB_WORKSPACE}/source-repo/${PLUGINS_ROOT}" >> "$GITHUB_OUTPUT" + fi + echo "overlay_path=${GITHUB_WORKSPACE}/overlay-repo/${OVERLAY_ROOT}" >> "$GITHUB_OUTPUT" + - name: Prepare downstream sources if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} id: prepare-downstream-sources uses: redhat-developer/rhdh-plugin-export-utils/prepare-sources@main with: - workspace-path: ${{ github.workspace }}/source-repo/${{ inputs.plugins-root }} - overlay-path: ${{ github.workspace }}/overlay-repo/${{ inputs.overlay-root }} + workspace-path: ${{ steps.prepare-paths.outputs.workspace_path }} + overlay-path: ${{ steps.prepare-paths.outputs.overlay_path }} - name: Upload prepared downstream sources uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 @@ -385,7 +400,7 @@ jobs: with: name: prepared downstream sources${{ steps.set-artifacts-name-suffix.outputs.ARTIFACTS_NAME_SUFFIX }} path: | - ${{ github.workspace }}/source-repo/${{ inputs.plugins-root }} + ${{ steps.prepare-paths.outputs.workspace_path }} !**/node_modules !**/.yarn/cache !**/.git From 8b7a4df6439785c0f0fd0fe1dbf94e7b9db09bf8 Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Thu, 1 Oct 2026 10:41:26 +0200 Subject: [PATCH 5/9] fix(workflow): nest export-dynamic via $/ same-commit ref Nested "./" can resolve against the top-level overlays caller. Use "$/" so export-dynamic stays on the same commit as export-workspaces when called as a reusable workflow (RHIDP-15846). Co-authored-by: Cursor --- .github/workflows/export-workspaces-as-dynamic.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/export-workspaces-as-dynamic.yaml b/.github/workflows/export-workspaces-as-dynamic.yaml index 9436943f..0b0548ea 100644 --- a/.github/workflows/export-workspaces-as-dynamic.yaml +++ b/.github/workflows/export-workspaces-as-dynamic.yaml @@ -273,9 +273,12 @@ jobs: export: name: Export ${{ matrix.workspace.overlay-root }} needs: prepare - # Relative path keeps export-dynamic in lockstep with this workflow's ref - # (needed for new inputs like prepare-downstream-sources; avoids @main drift). - uses: ./.github/workflows/export-dynamic.yaml + # Same-repo, same-commit as this workflow. Prefer "$/" over "./" — when overlays + # call this reusable workflow, nested "./" can resolve against the top-level + # caller (overlays) instead of utils. "$/" keeps export-dynamic in lockstep so + # new inputs (prepare-downstream-sources) are not dropped by pinning @main. + # https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows + uses: $/.github/workflows/export-dynamic.yaml strategy: fail-fast: false matrix: From 852715015aee485eed17913bcbdb66ed3472f66c Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Thu, 1 Oct 2026 12:27:19 +0200 Subject: [PATCH 6/9] fix(workflow): pin prepare-sources to same commit; disable vp cache @main pulls setup-vp cache hashing that fails under _actions/. Use a same-repo relative action path and turn off the lockfile cache so prepare-downstream-sources can run from export-dynamic (RHIDP-15846). Co-authored-by: Cursor --- .github/workflows/export-dynamic.yaml | 4 +++- prepare-sources/action.yaml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/export-dynamic.yaml b/.github/workflows/export-dynamic.yaml index 245fb08b..3e4938c6 100644 --- a/.github/workflows/export-dynamic.yaml +++ b/.github/workflows/export-dynamic.yaml @@ -389,7 +389,9 @@ jobs: - name: Prepare downstream sources if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} id: prepare-downstream-sources - uses: redhat-developer/rhdh-plugin-export-utils/prepare-sources@main + uses: ./prepare-sources + # Same-repo relative action → same commit as this reusable workflow + # (avoids @main drift and picks up action.yaml fixes on this branch). with: workspace-path: ${{ steps.prepare-paths.outputs.workspace_path }} overlay-path: ${{ steps.prepare-paths.outputs.overlay_path }} diff --git a/prepare-sources/action.yaml b/prepare-sources/action.yaml index 0f3a3200..83d0d9a2 100644 --- a/prepare-sources/action.yaml +++ b/prepare-sources/action.yaml @@ -17,7 +17,9 @@ runs: uses: voidzero-dev/setup-vp@3754dd7dbdb32bd8f6d28b6043de13ad3a75f21f # v1.21.1 with: working-directory: ${{ github.action_path }} - cache: true + cache: false + # cache:true fails hashing package-lock.json when this action is fetched + # under _actions/ (composite reuse from export-dynamic). Install still runs. run-install: | args: ['--prod'] From 5d220e0ef57eabbad5110d68d79a49e014ae4f8d Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Thu, 1 Oct 2026 12:37:49 +0200 Subject: [PATCH 7/9] fix(workflow): resolve prepare-sources via job_workflow_sha Relative ./prepare-sources looks in the job workspace (source/overlay checkouts), not utils. Pin the composite action to this workflow's commit and keep setup-vp cache off so lockfile hashing under _actions succeeds. Co-authored-by: Cursor --- .github/workflows/export-dynamic.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/export-dynamic.yaml b/.github/workflows/export-dynamic.yaml index 3e4938c6..41e0ed14 100644 --- a/.github/workflows/export-dynamic.yaml +++ b/.github/workflows/export-dynamic.yaml @@ -389,9 +389,10 @@ jobs: - name: Prepare downstream sources if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} id: prepare-downstream-sources - uses: ./prepare-sources - # Same-repo relative action → same commit as this reusable workflow - # (avoids @main drift and picks up action.yaml fixes on this branch). + # Same commit as this reusable workflow. Owner must match where that + # commit lives (fork harness → polasudo; after merge on caller overlays + # → switch to redhat-developer — see follow-up commit for merge-ready pin). + uses: polasudo/rhdh-plugin-export-utils/prepare-sources@${{ github.job_workflow_sha }} with: workspace-path: ${{ steps.prepare-paths.outputs.workspace_path }} overlay-path: ${{ steps.prepare-paths.outputs.overlay_path }} From 458e156c765ad997b40d33c1c6b915a06b735f8c Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Thu, 1 Oct 2026 12:40:32 +0200 Subject: [PATCH 8/9] fix(workflow): checkout prepare-sources at job_workflow_sha MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub rejects github.* in uses:; mirror the compatibility-helpers pattern — checkout utils at this workflow commit, then run the local composite action. Co-authored-by: Cursor --- .github/workflows/export-dynamic.yaml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/export-dynamic.yaml b/.github/workflows/export-dynamic.yaml index 41e0ed14..0796f01b 100644 --- a/.github/workflows/export-dynamic.yaml +++ b/.github/workflows/export-dynamic.yaml @@ -386,13 +386,22 @@ jobs: fi echo "overlay_path=${GITHUB_WORKSPACE}/overlay-repo/${OVERLAY_ROOT}" >> "$GITHUB_OUTPUT" + # uses: cannot take github.* expressions; checkout at job_workflow_sha then + # run the composite action from the workspace (same pattern as helpers above). + - name: Checkout prepare-sources action + if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: redhat-developer/rhdh-plugin-export-utils + ref: ${{ github.job_workflow_sha }} + path: _export-utils-prepare-sources + sparse-checkout: prepare-sources + sparse-checkout-cone-mode: true + - name: Prepare downstream sources if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} id: prepare-downstream-sources - # Same commit as this reusable workflow. Owner must match where that - # commit lives (fork harness → polasudo; after merge on caller overlays - # → switch to redhat-developer — see follow-up commit for merge-ready pin). - uses: polasudo/rhdh-plugin-export-utils/prepare-sources@${{ github.job_workflow_sha }} + uses: ./_export-utils-prepare-sources/prepare-sources with: workspace-path: ${{ steps.prepare-paths.outputs.workspace_path }} overlay-path: ${{ steps.prepare-paths.outputs.overlay_path }} From f0abda38e0e9e041e06b0aa3a16d9151a496cee6 Mon Sep 17 00:00:00 2001 From: Martin Polasko Date: Fri, 6 Nov 2026 15:38:57 +0100 Subject: [PATCH 9/9] fix(workflow): reference prepare-sources via $/ self-repo token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the runtime checkout of this repository at github.job_workflow_sha with the documented $/ self repository reference, which resolves to the workflow file's repository at the running commit without a checkout step. This removes the dynamic ref that tripped the SonarCloud action-pinning alert — the reference is commit-locked by definition — and follows the GitHub-recommended syntax for same-repository actions. --- .github/workflows/export-dynamic.yaml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/export-dynamic.yaml b/.github/workflows/export-dynamic.yaml index 0796f01b..a89058d8 100644 --- a/.github/workflows/export-dynamic.yaml +++ b/.github/workflows/export-dynamic.yaml @@ -386,22 +386,13 @@ jobs: fi echo "overlay_path=${GITHUB_WORKSPACE}/overlay-repo/${OVERLAY_ROOT}" >> "$GITHUB_OUTPUT" - # uses: cannot take github.* expressions; checkout at job_workflow_sha then - # run the composite action from the workspace (same pattern as helpers above). - - name: Checkout prepare-sources action - if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: redhat-developer/rhdh-plugin-export-utils - ref: ${{ github.job_workflow_sha }} - path: _export-utils-prepare-sources - sparse-checkout: prepare-sources - sparse-checkout-cone-mode: true - + # $/ = self repository reference: resolves to this workflow file's repository + # at the running commit, even when called from the overlays repo. No checkout + # step needed, and the reference is commit-locked by definition. - name: Prepare downstream sources if: ${{ inputs.prepare-downstream-sources && success() && steps.export-dynamic.outputs.failed-exports == '' && steps.export-dynamic.outputs.workspace-skipped-unchanged-since == 'false' }} id: prepare-downstream-sources - uses: ./_export-utils-prepare-sources/prepare-sources + uses: $/prepare-sources with: workspace-path: ${{ steps.prepare-paths.outputs.workspace_path }} overlay-path: ${{ steps.prepare-paths.outputs.overlay_path }}