From fb795936965c9a7d877194ef1072e320b82279bd Mon Sep 17 00:00:00 2001
From: Guy Sartorelli <guy.sartorelli@silverstripe.com>
Date: Tue, 30 Jul 2024 12:11:08 +1200
Subject: [PATCH] ENH Dispatch patch tag workflow instead of running directly

---
 .github/workflows/ci.yml | 63 +++++++++++++++++++++++-----------------
 README.md                | 19 ++++++++++++
 2 files changed, 56 insertions(+), 26 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 69bdf14..9c0a499 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -957,6 +957,7 @@ jobs:
     name: Check governance
     runs-on: ubuntu-latest
     needs: 'genmatrix'
+    if: ${{ github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
     outputs:
       can_tag: ${{ steps.check-governance.outputs.can_tag }}
     env:
@@ -1034,33 +1035,43 @@ jobs:
           echo "can_tag output is $CAN_TAG"
           echo "can_tag=$CAN_TAG" >> $GITHUB_OUTPUT
 
-  gaugerelease:
-    name: Check unreleased changes
+  dispatchrelease:
+    name: Dispatch tag release
     runs-on: ubuntu-latest
     needs: [tests, checkgovernance]
-    if: ${{ needs.checkgovernance.outputs.can_tag == '1' && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }}
-    outputs:
-      do_release: ${{ steps.gauge-release.outputs.do_release }}
-      next_tag: ${{ steps.gauge-release.outputs.next_tag }}
+    if: ${{ needs.checkgovernance.outputs.can_tag == '1' }}
+    env:
+      GITHUB_REPOSITORY: ${{ github.repository }}
+      BRANCH: ${{ github.ref_name }}
     steps:
-      - name: Gauge release
-        id: gauge-release
-        uses: silverstripe/gha-gauge-release@v1
-        with:
-          latest_local_sha: ${{ needs.tests.outputs.latest_local_sha }}
 
-  patchrelease:
-    name: Patch release
-    runs-on: ubuntu-latest
-    needs: gaugerelease
-    if: ${{ needs.gaugerelease.outputs.do_release == '1' }}
-    permissions:
-      contents: write
-    steps:
-      - name: Patch release
-        uses: silverstripe/gha-tag-release@v1
-        with:
-          tag: ${{ needs.gaugerelease.outputs.next_tag }}
-          delete_existing: false
-          release: true
-          release_auto_notes: true
+      - name: Dispatch release
+        shell: bash
+        id: dispatch-release
+        run: |
+          if ! [[ -f .github/workflows/tag-patch-release.yml ]]; then
+            echo "tag-patch-release.yml not present. Skipping."
+            exit 0
+          fi
+          # https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event
+          RESP_CODE=$(curl -w %{http_code} -s -L -o __response.json \
+            -X POST \
+            -H "Accept: application/vnd.github+json" \
+            -H "Authorization: Bearer ${{ github.token }}"\
+            -H "X-GitHub-Api-Version: 2022-11-28" \
+            https://api.github.com/repos/$GITHUB_REPOSITORY/actions/workflows/tag-patch-release.yml/dispatches \
+            -d "{\"ref\":\"$BRANCH\",\"inputs\":{\"latest_local_sha\":\"${{ needs.tests.outputs.latest_local_sha }}\"}}"
+          )
+          if [[ $RESP_CODE != "204" ]]; then
+            echo "Failed to dispatch workflow - HTTP response code was $RESP_CODE"
+            cat __response.json
+            exit 1
+          fi
+
+      - name: Delete temporary files
+        shell: bash
+        if: always()
+        run: |
+          if [[ -f __response.json ]]; then
+            rm __response.json
+          fi
diff --git a/README.md b/README.md
index 0efc07b..8363ad7 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,15 @@ Note: Unlike other silverstripe/gha-* repositories, this one is a [reusable work
 
 Create the following file in your module, and substitute the tagged version for the most recent tag prefixed with a `v` e.g. `@v1`
 
+> [!WARNING]
+> Note that the `actions: write` permission won't be used in third-party repositories, but still needs to be defined. This permission is required because in commercially supported repositories and repositories in the "silverstripe" GitHub organisation we dispatch a separate workflow which tags patch releases.
+>
+> Unfortunately the workflow dispatch has to be done this way because regular actions such as "push" won't trigger workflows if they were triggered by a GitHub action.
+>
+> To ensure you protect yourself from malicious actors, we recommend you set the "Fork pull request workflows from outside collaborators" setting in `https://github.com/<org>/<repo>/settings/actions` to one of
+> - Require approval for first-time contributors
+> - Require approval for all outside collaborators
+
 **.github/workflows/ci.yml**
 ```yml
 name: CI
@@ -21,9 +30,14 @@ on:
   pull_request:
   workflow_dispatch:
 
+permissions: {}
+
 jobs:
   ci:
     name: CI
+    permissions:
+      pull-requests: read
+      contents: read
     uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
 ```
 
@@ -35,9 +49,14 @@ on:
   schedule:
   - cron: '0 0 * * 1'
 
+permissions: {}
+
 jobs:
   ci:
     name: CI
+    permissions:
+      pull-requests: read
+      contents: read
     # Only run the cron on the account hosting this repository, not on the accounts of forks
     # Change '<account_name>' to match the name of the account hosting this repository
     if: (github.event_name == 'schedule' && github.repository_owner == '<account_name>') || (github.event_name != 'schedule')