From d336e85a223a50f4661a5115bafc0e7fc98762ea Mon Sep 17 00:00:00 2001 From: Ondrej Kosarko Date: Wed, 22 Jul 2026 17:06:44 +0200 Subject: [PATCH 1/4] CI: redeploy the ok-dspace test environment on pushes to the default branch Sends a repository_dispatch to ufal/dspace-k8s once the images this environment consumes have actually been pushed. That repository pins the tag to git and an in-cluster reconciler applies it, so this job records a version and nothing more - no deployment step and no cluster credential exists here, or anywhere in GitHub. Runs only for pushes to the default branch of the canonical repository, so a pull request builds images but never triggers a deploy. The token is a fine-grained PAT scoped to ufal/dspace-k8s with Contents: write, which is what POST /repos/{owner}/{repo}/dispatches requires. A workflow's own GITHUB_TOKEN cannot act on another repository, so this credential is unavoidable; it is held as an organisation secret shared with this repository. --- .github/workflows/docker.yml | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b5698581201..781c8b74fe3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -62,11 +62,31 @@ jobs: REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }} REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }} - # deploy: - # needs: dspace-angular - # uses: ufal/dspace-angular/.github/workflows/deploy.yml@clarin-v7 - # if: ${{ github.event_name != 'pull_request' }} - # with: - # INSTANCE: '5' - # IMPORT: false - # secrets: inherit + ########################################################################### + # Redeploy the ok-dspace test environment (ufal/dspace-k8s, ns kosarko-ns) + ########################################################################### + # Replaces the old commented-out deploy stub, which targeted the dataquest + # docker-compose instances (dev-5/dev-8) we no longer have access to. + # + # Runs only for pushes to the default branch, after the image this + # environment consumes has actually been pushed. This job only records the + # new version: it sends a repository_dispatch to ufal/dspace-k8s, which pins + # the tag to git, and a reconciler inside the cluster picks it up. No + # deployment step and no cluster credential exists in this repository - or in + # GitHub at all. + deploy-ok-dspace: + if: github.repository == 'ufal/dspace-angular' && github.event_name == 'push' && github.ref_name == 'clarin-v7' + # dspace-angular-dist, NOT dspace-angular: the latter builds the '-dev' + # suffixed image, while the overlay runs the unsuffixed dist image. + needs: [dspace-angular-dist] + runs-on: ubuntu-latest + steps: + - name: Trigger deployment + uses: peter-evans/repository-dispatch@v4 + with: + # Fine-grained PAT, scoped to ufal/dspace-k8s only, Contents: write + # (what POST /repos/{owner}/{repo}/dispatches requires). + token: ${{ secrets.OK_DSPACE_DEPLOY_TOKEN }} + repository: ufal/dspace-k8s + event-type: deploy-ok-dspace + client-payload: '{"component":"frontend","sha":"${{ github.sha }}"}' From 9029e189c048dfef9bee4b24983e42d648fe3248 Mon Sep 17 00:00:00 2001 From: Ondrej Kosarko Date: Wed, 22 Jul 2026 17:27:27 +0200 Subject: [PATCH 2/4] Address review: name what this job actually does The section header said "Redeploy" and the step was called "Trigger deployment", but this job only sends a repository_dispatch - the deploying happens in ufal/dspace-k8s and, ultimately, in the cluster. Renamed both so the workflow logs say what ran. Also names `clarin-v7` explicitly instead of "the default branch", since that is the literal value the guard matches and the two could drift apart. --- .github/workflows/docker.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 781c8b74fe3..c60cd1b0f2b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -63,17 +63,19 @@ jobs: REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }} ########################################################################### - # Redeploy the ok-dspace test environment (ufal/dspace-k8s, ns kosarko-ns) + # Notify ufal/dspace-k8s of a new build, for the ok-dspace test environment ########################################################################### # Replaces the old commented-out deploy stub, which targeted the dataquest # docker-compose instances (dev-5/dev-8) we no longer have access to. # - # Runs only for pushes to the default branch, after the image this - # environment consumes has actually been pushed. This job only records the - # new version: it sends a repository_dispatch to ufal/dspace-k8s, which pins - # the tag to git, and a reconciler inside the cluster picks it up. No - # deployment step and no cluster credential exists in this repository - or in - # GitHub at all. + # Runs only for pushes to `clarin-v7` - currently the default branch, named + # explicitly here because that is exactly what the guard below matches - and + # only after the image this environment consumes has actually been pushed. + # + # This job records a new version; it does not deploy. It sends a + # repository_dispatch to ufal/dspace-k8s, which pins the tag to git, and a + # reconciler inside that cluster applies it. No deployment step and no cluster + # credential exists in this repository, or anywhere in GitHub. deploy-ok-dspace: if: github.repository == 'ufal/dspace-angular' && github.event_name == 'push' && github.ref_name == 'clarin-v7' # dspace-angular-dist, NOT dspace-angular: the latter builds the '-dev' @@ -81,7 +83,7 @@ jobs: needs: [dspace-angular-dist] runs-on: ubuntu-latest steps: - - name: Trigger deployment + - name: Notify ufal/dspace-k8s uses: peter-evans/repository-dispatch@v4 with: # Fine-grained PAT, scoped to ufal/dspace-k8s only, Contents: write From 94eb17a3a885eabd089d068a90fd3c39674fe1ba Mon Sep 17 00:00:00 2001 From: Ondrej Kosarko Date: Wed, 22 Jul 2026 17:55:53 +0200 Subject: [PATCH 3/4] Address review: drop unneeded token permissions from the dispatch job The job authenticates with a fine-grained PAT and never uses GITHUB_TOKEN, but inherited the workflow-level permissions anyway - including `packages: write` in dspace-angular, which it has no use for. `permissions: {}` limits what a compromised third-party action could reach from this job. --- .github/workflows/docker.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c60cd1b0f2b..fa557db94e9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -82,6 +82,10 @@ jobs: # suffixed image, while the overlay runs the unsuffixed dist image. needs: [dspace-angular-dist] runs-on: ubuntu-latest + # This job authenticates with a PAT and never uses GITHUB_TOKEN, so it needs + # none of the workflow-level permissions. Dropping them limits what a + # compromised third-party action could reach from here. + permissions: {} steps: - name: Notify ufal/dspace-k8s uses: peter-evans/repository-dispatch@v4 From e11f62f800c68f1d6005c2b2f1d6658f9057a73e Mon Sep 17 00:00:00 2001 From: Ondrej Kosarko Date: Thu, 23 Jul 2026 10:05:31 +0200 Subject: [PATCH 4/4] Address review: dispatch with gh instead of a third-party action peter-evans/repository-dispatch@v4 ran with the deploy PAT in its environment to make a single POST. ubuntu-latest already ships gh and jq, so the same call is three lines with no third-party code in the credential's blast radius. jq builds the payload from an --arg rather than interpolating the sha into a JSON string, so the value is a JSON string by construction. The request body is identical to what the action sent. The permissions comment no longer justifies itself by third-party actions, since there are none here now; permissions: {} still stands on its own, because the job authenticates with the PAT and never touches GITHUB_TOKEN. --- .github/workflows/docker.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fa557db94e9..b2c274ad967 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -83,16 +83,24 @@ jobs: needs: [dspace-angular-dist] runs-on: ubuntu-latest # This job authenticates with a PAT and never uses GITHUB_TOKEN, so it needs - # none of the workflow-level permissions. Dropping them limits what a - # compromised third-party action could reach from here. + # none of the workflow-level permissions at all. permissions: {} steps: + # One POST, hand-rolled with the `gh` and `jq` that ubuntu-latest already + # ships, rather than a third-party dispatch action: a single API call is + # not worth a supply-chain dependency that runs with the PAT in its + # environment. - name: Notify ufal/dspace-k8s - uses: peter-evans/repository-dispatch@v4 - with: + env: # Fine-grained PAT, scoped to ufal/dspace-k8s only, Contents: write # (what POST /repos/{owner}/{repo}/dispatches requires). - token: ${{ secrets.OK_DSPACE_DEPLOY_TOKEN }} - repository: ufal/dspace-k8s - event-type: deploy-ok-dspace - client-payload: '{"component":"frontend","sha":"${{ github.sha }}"}' + GH_TOKEN: ${{ secrets.OK_DSPACE_DEPLOY_TOKEN }} + SHA: ${{ github.sha }} + run: | + set -euo pipefail + # jq builds the payload from an --arg, so the value is a JSON string + # by construction rather than by careful quoting. + jq -n --arg sha "${SHA}" \ + '{event_type: "deploy-ok-dspace", + client_payload: {component: "frontend", sha: $sha}}' \ + | gh api -X POST repos/ufal/dspace-k8s/dispatches --input -