|
| 1 | +# TEST WORKFLOW - DO NOT MERGE. |
| 2 | +# |
| 3 | +# Validates that `secrets.GH_PAT_TO_ACCESS_GITHUB_API` is a safe drop-in |
| 4 | +# replacement for `secrets.GHA_COMMIT_TO_MASTER_PAT` in release.yaml (PR #13). |
| 5 | +# |
| 6 | +# In release.yaml the `ad-m/github-push-action` step pushes the helm-docs |
| 7 | +# README commit DIRECTLY to `main`. `main` is guarded by the "Protect Main" |
| 8 | +# ruleset, so that direct push only succeeds when the token's identity is a |
| 9 | +# member of a bypass team (`deployment-team` / `deployment-team-actions`). |
| 10 | +# |
| 11 | +# This workflow, in order: |
| 12 | +# 1. asserts the secret is present (value stays masked), |
| 13 | +# 2. reveals which identity the PAT authenticates as (`gh api user`), |
| 14 | +# 3. confirms that identity has push permission on the repo, |
| 15 | +# 4. gives a verdict on main-ruleset bypass (compares the identity against |
| 16 | +# the known bypass roster, plus a best-effort live team-membership check), |
| 17 | +# 5. proves the token can actually push, using the SAME action release.yaml |
| 18 | +# uses (`ad-m/github-push-action`), against a throwaway branch, |
| 19 | +# 6. deletes that throwaway branch. |
| 20 | +# |
| 21 | +# `main` is never written to. Delete this file / close the PR when done. |
| 22 | + |
| 23 | +name: "TEST - Validate GH_PAT_TO_ACCESS_GITHUB_API (do not merge)" |
| 24 | + |
| 25 | +on: |
| 26 | + pull_request: |
| 27 | + workflow_dispatch: |
| 28 | + |
| 29 | +# The default GITHUB_TOKEN is not used for the push; the PAT is. Keep it minimal. |
| 30 | +permissions: |
| 31 | + contents: read |
| 32 | + |
| 33 | +concurrency: |
| 34 | + group: validate-pat-${{ github.ref }} |
| 35 | + cancel-in-progress: true |
| 36 | + |
| 37 | +jobs: |
| 38 | + validate-pat: |
| 39 | + runs-on: ubuntu-latest |
| 40 | + timeout-minutes: 5 |
| 41 | + env: |
| 42 | + # Bypass actors for the "Protect Main" ruleset on comet-ml/s3proxy-chart, |
| 43 | + # captured when this test was written. The push to `main` in release.yaml |
| 44 | + # succeeds only if the PAT owner is one of these logins. |
| 45 | + BYPASS_ROSTER: "CRThaze darenjacobs thalesac jms200 CometActions" |
| 46 | + TEST_BRANCH: "pat-validate/${{ github.run_id }}" |
| 47 | + steps: |
| 48 | + - name: Checkout |
| 49 | + uses: actions/checkout@v4 |
| 50 | + with: |
| 51 | + fetch-depth: 0 |
| 52 | + persist-credentials: false |
| 53 | + |
| 54 | + - name: 1. Assert secret is present (value masked) |
| 55 | + env: |
| 56 | + PAT: ${{ secrets.GH_PAT_TO_ACCESS_GITHUB_API }} |
| 57 | + run: | |
| 58 | + if [ -z "$PAT" ]; then |
| 59 | + echo "::error::secrets.GH_PAT_TO_ACCESS_GITHUB_API is empty or not exposed to this run" |
| 60 | + exit 1 |
| 61 | + fi |
| 62 | + echo "Secret present: ${#PAT} characters (value masked by Actions)." |
| 63 | +
|
| 64 | + - name: 2. Resolve PAT owner identity |
| 65 | + id: whoami |
| 66 | + env: |
| 67 | + GH_TOKEN: ${{ secrets.GH_PAT_TO_ACCESS_GITHUB_API }} |
| 68 | + run: | |
| 69 | + if ! login=$(gh api user --jq '.login' 2>/tmp/err); then |
| 70 | + echo "::error::PAT failed to authenticate against the GitHub API" |
| 71 | + cat /tmp/err |
| 72 | + exit 1 |
| 73 | + fi |
| 74 | + echo "PAT authenticates as: $login" |
| 75 | + echo "login=$login" >> "$GITHUB_OUTPUT" |
| 76 | +
|
| 77 | + - name: 3. Confirm push permission on this repo |
| 78 | + env: |
| 79 | + GH_TOKEN: ${{ secrets.GH_PAT_TO_ACCESS_GITHUB_API }} |
| 80 | + LOGIN: ${{ steps.whoami.outputs.login }} |
| 81 | + run: | |
| 82 | + perm=$(gh api "repos/${{ github.repository }}/collaborators/${LOGIN}/permission" --jq '.permission') |
| 83 | + echo "${LOGIN} has '${perm}' permission on ${{ github.repository }}" |
| 84 | + case "$perm" in |
| 85 | + admin|maintain|write) echo "OK: push-capable." ;; |
| 86 | + *) echo "::error::'${LOGIN}' cannot push (permission='${perm}')"; exit 1 ;; |
| 87 | + esac |
| 88 | +
|
| 89 | + - name: 4. Verdict - can this identity bypass the 'Protect Main' ruleset? |
| 90 | + env: |
| 91 | + GH_TOKEN: ${{ secrets.GH_PAT_TO_ACCESS_GITHUB_API }} |
| 92 | + LOGIN: ${{ steps.whoami.outputs.login }} |
| 93 | + run: | |
| 94 | + in_roster=false |
| 95 | + for u in $BYPASS_ROSTER; do |
| 96 | + [ "$u" = "$LOGIN" ] && in_roster=true |
| 97 | + done |
| 98 | +
|
| 99 | + # Best-effort live check (needs read:org on the PAT; non-fatal if denied). |
| 100 | + live="unknown" |
| 101 | + for team in deployment-team deployment-team-actions; do |
| 102 | + state=$(gh api "orgs/${{ github.repository_owner }}/teams/${team}/memberships/${LOGIN}" --jq '.state' 2>/dev/null || echo "") |
| 103 | + if [ "$state" = "active" ]; then live="member of ${team}"; break; fi |
| 104 | + done |
| 105 | +
|
| 106 | + { |
| 107 | + echo "### GH_PAT_TO_ACCESS_GITHUB_API validation" |
| 108 | + echo "" |
| 109 | + echo "| Field | Value |" |
| 110 | + echo "| --- | --- |" |
| 111 | + echo "| Authenticates as | \`${LOGIN}\` |" |
| 112 | + echo "| In known bypass roster | ${in_roster} |" |
| 113 | + echo "| Live team check | ${live} |" |
| 114 | + } >> "$GITHUB_STEP_SUMMARY" |
| 115 | +
|
| 116 | + if [ "$in_roster" = "true" ] || [ "$live" != "unknown" ]; then |
| 117 | + echo "::notice::PASS - '${LOGIN}' can bypass 'Protect Main'; the direct push to main in release.yaml will work." |
| 118 | + else |
| 119 | + echo "::warning::REVIEW - '${LOGIN}' is not in the known bypass roster and live check was inconclusive. Add it to deployment-team(-actions) or the push to main will be rejected." |
| 120 | + fi |
| 121 | +
|
| 122 | + - name: 5. Prove push works (same action as release.yaml -> throwaway branch) |
| 123 | + run: | |
| 124 | + git config user.name "PAT Validation (test)" |
| 125 | + git config user.email "github-actions@comet.com" |
| 126 | + git commit --allow-empty -m "test: validate GH_PAT_TO_ACCESS_GITHUB_API push (throwaway, safe to delete)" |
| 127 | +
|
| 128 | + - name: 5b. Push to throwaway branch with the new PAT |
| 129 | + uses: ad-m/github-push-action@master |
| 130 | + with: |
| 131 | + github_token: ${{ secrets.GH_PAT_TO_ACCESS_GITHUB_API }} |
| 132 | + branch: ${{ env.TEST_BRANCH }} |
| 133 | + force: false |
| 134 | + |
| 135 | + - name: 6. Delete throwaway branch (cleanup) |
| 136 | + if: always() |
| 137 | + env: |
| 138 | + GH_TOKEN: ${{ secrets.GH_PAT_TO_ACCESS_GITHUB_API }} |
| 139 | + run: | |
| 140 | + if gh api -X DELETE "repos/${{ github.repository }}/git/refs/heads/${TEST_BRANCH}" 2>/dev/null; then |
| 141 | + echo "Deleted throwaway branch ${TEST_BRANCH}." |
| 142 | + else |
| 143 | + echo "Throwaway branch ${TEST_BRANCH} not present (nothing to delete)." |
| 144 | + fi |
0 commit comments