-
Notifications
You must be signed in to change notification settings - Fork 392
fix(CI): split documentation workflow to avoid pull_request_target checkout block #12625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| name: Documentation deploy | ||
| on: | ||
| workflow_run: | ||
| workflows: [Documentation] | ||
| types: [completed] | ||
| jobs: | ||
| deploy: | ||
| name: Deploy | ||
| runs-on: ubuntu-latest | ||
| if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion != 'cancelled' | ||
| env: | ||
| SURGE_LOGIN: ${{ secrets.SURGE_LOGIN }} | ||
| SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} | ||
| GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN }} | ||
| steps: | ||
| - name: Check out project | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up project | ||
| uses: ./.github/actions/setup-project | ||
| with: | ||
| skip-build: true | ||
|
|
||
| - name: Download PR number | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: pr-number | ||
| run-id: ${{ github.event.workflow_run.id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Set PR number | ||
| run: echo "GH_PR_NUM=$(cat pr-number.txt)" >> $GITHUB_ENV | ||
|
Comment on lines
+24
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
# Verify that the completed run exposes its associated pull request in trusted API metadata.
# Expect: `.pull_requests` contains the source pull request number.
gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}" \
--jq '{event, conclusion, pull_requests}'Repository: patternfly/patternfly-react Length of output: 215 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
sed -n '1,220p' .github/workflows/documentation-deploy.yml
printf '%s\n' '--- source workflow references ---'
rg -n -C 5 'pr-number|workflow_run|download-artifact|upload-artifact|SURGE_TOKEN|GH_PR_TOKEN|GITHUB_ENV|BASH_ENV' .github/workflows .github 2>/dev/null || true
printf '%s\n' '--- repository files related to documentation workflow ---'
git ls-files '.github/workflows/*' | sortRepository: patternfly/patternfly-react Length of output: 23203 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
paths = [
Path(".github/workflows/documentation-deploy.yml"),
Path(".github/workflows/documentation.yml"),
]
for path in paths:
if not path.exists():
print(f"{path}: absent")
continue
text = path.read_text()
print(f"--- {path} ---")
for i, line in enumerate(text.splitlines(), 1):
if re.search(r"workflow_run|pr-number|upload-artifact|download-artifact|always\(\)|pull_request|pull-requests|GITHUB_ENV|BASH_ENV|SURGE_TOKEN|GH_PR_TOKEN", line, re.I):
start = max(1, i - 4)
end = min(len(text.splitlines()), i + 5)
lines = text.splitlines()
for j in range(start, end + 1):
print(f"{j:4}: {lines[j-1]}")
print()
PYRepository: patternfly/patternfly-react Length of output: 12487 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
sed -n '1,220p' .github/workflows/documentation-deploy.yml
printf '%s\n' '--- related references ---'
rg -n -C 5 'pr-number|workflow_run|download-artifact|upload-artifact|SURGE_TOKEN|GH_PR_TOKEN|GITHUB_ENV|BASH_ENV' .github/workflows .github 2>/dev/null || trueRepository: patternfly/patternfly-react Length of output: 22867 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- privileged action and script ---'
sed -n '1,180p' .github/actions/setup-project/action.yml
sed -n '1,180p' .github/upload-preview.mjs
printf '%s\n' '--- environment-file and BASH_ENV simulation ---'
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
artifact="$tmpdir/pr-number.txt"
envfile="$tmpdir/GITHUB_ENV"
# This is an attacker-controlled artifact payload. The first line is valid shell
# and the second line becomes a separate GITHUB_ENV assignment.
printf 'true\nBASH_ENV=%s\n' "$artifact" > "$artifact"
printf 'GH_PR_NUM=%s\n' "$(cat "$artifact")" > "$envfile"
printf '%s\n' 'Generated GITHUB_ENV contents:'
cat -n "$envfile"
set -a
while IFS= read -r line; do
case "$line" in
*=*) export "${line?}" ;;
esac
done < "$envfile"
set +a
printf 'GH_PR_NUM=%q\n' "$GH_PR_NUM"
printf 'BASH_ENV=%q\n' "$BASH_ENV"
# A later GitHub Actions `run` step uses a non-interactive Bash shell.
BASH_ENV="$artifact" bash -c 'printf "BASH_ENV executed; GH_PR_NUM=%q\n" "$GH_PR_NUM"'Repository: patternfly/patternfly-react Length of output: 4593 🌐 Web query:
💡 Result: When using the workflow_run event, the github.event.workflow_run.pull_requests payload may be empty, particularly for pull requests originating from forked repositories [1][2]. Consequently, relying solely on this array to obtain the pull request number is unreliable [3][4]. To robustly retrieve the pull request number, you should implement a fallback strategy [3]: 1. First, check github.event.workflow_run.pull_requests.number if it exists [3][4]. 2. If that is empty or unavailable, use the github.event.workflow_run.head_sha to query the GitHub REST API for associated pull requests [3][4]. You can do this by calling the GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls endpoint [4]. Regarding actions/download-artifact: To download an artifact from the workflow that triggered your current run (the "upstream" workflow), you must explicitly specify the run-id in the action configuration [5][6]. Use the id from the trigger event payload: - uses: actions/download-artifact@v4 with: run-id: ${{ github.event.workflow_run.id }} github-token: ${{ secrets.GITHUB_TOKEN }} Note that providing the github-token is required when downloading artifacts from a different workflow run [5][6]. Depending on your repository settings, you may need a Personal Access Token (PAT) with appropriate permissions instead of the default GITHUB_TOKEN if cross-repository or specific permission boundaries are hit [6][3]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
artifact="$tmpdir/pr-number.txt"
envfile="$tmpdir/GITHUB_ENV"
printf 'true\nBASH_ENV=%s\n' "$artifact" > "$artifact"
printf 'GH_PR_NUM=%s\n' "$(cat "$artifact")" > "$envfile"
cat -n "$envfile"
while IFS= read -r line; do
case "$line" in
*=*) export "$line" ;;
esac
done < "$envfile"
printf 'GH_PR_NUM=%q\n' "$GH_PR_NUM"
printf 'BASH_ENV=%q\n' "$BASH_ENV"
BASH_ENV="$artifact" bash -c 'printf "BASH_ENV executed; GH_PR_NUM=%q\n" "$GH_PR_NUM"'Repository: patternfly/patternfly-react Length of output: 329 Remove the A pull request can change 🧰 Tools🪛 zizmor (1.29.0)[error] 32-32: dangerous use of environment file (github-env): write to GITHUB_ENV may allow code execution (github-env) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| - name: Download documentation | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: documentation | ||
| path: packages/react-docs/public | ||
| run-id: ${{ github.event.workflow_run.id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Download a11y coverage | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: a11y-coverage | ||
| path: packages/react-docs/coverage | ||
| run-id: ${{ github.event.workflow_run.id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Upload documentation | ||
| run: node .github/upload-preview.mjs packages/react-docs/public | ||
|
|
||
| - name: Upload accessibility results | ||
| if: always() | ||
| run: node .github/upload-preview.mjs packages/react-docs/coverage | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| name: Documentation | ||
| on: | ||
| pull_request_target: | ||
| pull_request: | ||
| issue_comment: | ||
| types: [created] | ||
| workflow_call: | ||
|
|
@@ -19,6 +19,7 @@ on: | |
| required: true | ||
| jobs: | ||
| check-permissions: | ||
| if: github.event_name == 'issue_comment' | ||
| uses: patternfly/.github/.github/workflows/check-team-membership.yml@fdb52a63a2220ec8a3b6c2d43f312cda708ffa06 | ||
| secrets: inherit | ||
|
|
||
|
|
@@ -29,37 +30,61 @@ jobs: | |
| if: >- | ||
| always() && | ||
| !cancelled() && | ||
| (inputs.is-release || needs.check-permissions.outputs.allowed == 'true') | ||
| (inputs.is-release || github.event_name != 'issue_comment' || needs.check-permissions.outputs.allowed == 'true') | ||
| env: | ||
| SURGE_LOGIN: ${{ secrets.SURGE_LOGIN }} | ||
| SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }} | ||
| GH_PR_TOKEN: ${{ secrets.GH_PR_TOKEN }} | ||
| GH_PR_NUM: ${{ needs.check-permissions.outputs.pr-number }} | ||
| steps: | ||
| - name: Check out project from PR branch | ||
| if: github.event_name == 'pull_request_target' || github.event_name == 'issue_comment' | ||
| if: github.event_name == 'issue_comment' | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| # Checkout the merge commit so that we can access the PR's changes. | ||
| # This is nessesary because `pull_request_target` checks out the base branch (e.g. `main`) by default. | ||
| ref: refs/pull/${{ env.GH_PR_NUM }}/head | ||
|
|
||
| - name: Check out project | ||
| if: inputs.is-release || github.event_name == 'workflow_call' | ||
| if: github.event_name != 'issue_comment' | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up and build project | ||
| uses: ./.github/actions/setup-project | ||
|
|
||
| - name: Build documentation | ||
| run: yarn build:docs | ||
|
|
||
| - name: Upload documentation | ||
| if: always() | ||
| - name: Upload documentation preview | ||
| if: always() && !cancelled() && github.event_name != 'pull_request' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need both the always() and !cancelled() calls? I am not an expert on this, but it sounds like maybe we don't: https://docs.github.com/en/actions/reference/workflows-and-actions/expressions#always |
||
| run: node .github/upload-preview.mjs packages/react-docs/public | ||
|
|
||
| - name: Run accessibility tests | ||
| run: yarn serve:docs & yarn test:a11y | ||
|
|
||
| - name: Upload accessibility results | ||
| if: always() | ||
| if: always() && !cancelled() && github.event_name != 'pull_request' | ||
| run: node .github/upload-preview.mjs packages/react-docs/coverage | ||
|
|
||
| - name: Upload docs artifact | ||
| if: always() && !cancelled() && github.event_name == 'pull_request' | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: documentation | ||
| path: packages/react-docs/public | ||
|
|
||
| - name: Upload a11y artifact | ||
| if: always() && !cancelled() && github.event_name == 'pull_request' | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: a11y-coverage | ||
| path: packages/react-docs/coverage | ||
|
|
||
| - name: Save PR number | ||
| if: always() && !cancelled() && github.event_name == 'pull_request' | ||
| run: echo "${{ github.event.pull_request.number }}" > pr-number.txt | ||
|
|
||
| - name: Upload PR number | ||
| if: always() && !cancelled() && github.event_name == 'pull_request' | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: pr-number | ||
| path: pr-number.txt | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Deploy only successful Documentation runs.
Line 10 also accepts failed and timed-out source runs. The source workflow uploads artifacts with
always(), so this workflow can deploy incomplete documentation or accessibility output.Require a successful conclusion before deployment.
Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents