1010
1111name : " Deploy docs preview"
1212
13+ permissions :
14+ actions : read
15+ contents : read
16+ pull-requests : write
17+
1318on :
1419 workflow_run :
1520 workflows : ["Verify docs PR"]
1621 types :
1722 - completed
1823
1924jobs :
20- publish-docs :
21- # Uncomment this if statement to deploy only when the PR builds cleanly
22- # if: github.event.workflow_run.conclusion == 'success'
25+ # [Optional] Restrict automatic dpeloyment to PRs from the upstream repo
26+ # For fork PRs, requires manual approval via the "preview" environment.
27+ # For PRs from the main repository this job is skipped and deploy-docs runs immediately.
28+ # Setup: create a "preview" environment in Settings → Environments with required reviewers.
29+ # approve-fork:
30+ # if: github.event.workflow_run.head_repository.full_name != github.repository
31+ # environment: preview
32+ # runs-on: ubuntu-latest
33+ # steps:
34+ # - name: Fork PR approved for deployment
35+ # run: echo "Approved"
36+
37+ deploy-docs :
38+ # needs: [approve-fork]
39+ # Run when approve-fork succeeded (fork, approved) or was skipped (non-fork).
40+ # Uncomment the conclusion check to deploy only when the PR builds cleanly:
41+ # && github.event.workflow_run.conclusion == 'success'
42+ # if: |
43+ # always() &&
44+ # (needs.approve-fork.result == 'success' || needs.approve-fork.result == 'skipped')
2345
2446 runs-on : ubuntu-latest
2547
2648 steps :
2749 - name : " Download built documentation"
28- uses : actions/github-script@v7
50+ uses : actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
2951 env :
3052 RUN_ID : ${{ github.event.workflow_run.id }}
31- WORKSPACE : ${{ github.workspace }}
53+ ARTIFACT_DIR : ${{ runner.temp }}/artifacts
3254 with :
3355 script : |
3456 var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
4769 archive_format: 'zip',
4870 });
4971 var fs = require('fs');
50- fs.writeFileSync('${{ env.WORKSPACE }}/docs.zip', Buffer.from(downloadDocs.data));
72+ fs.mkdirSync(process.env.ARTIFACT_DIR + '/docs', { recursive: true });
73+ fs.writeFileSync(process.env.ARTIFACT_DIR + '/docs/docs.zip', Buffer.from(downloadDocs.data));
5174
5275 var matchArtifactChangelog = artifacts.data.artifacts.filter((artifact) => {
5376 return artifact.name == "changelog"
@@ -58,27 +81,68 @@ jobs:
5881 artifact_id: matchArtifactChangelog.id,
5982 archive_format: 'zip',
6083 });
61- fs.writeFileSync('${{ env.WORKSPACE }}/changelog.zip', Buffer.from(downloadChangelog.data));
84+ fs.mkdirSync(process.env.ARTIFACT_DIR + '/changelog', { recursive: true });
85+ fs.writeFileSync(process.env.ARTIFACT_DIR + '/changelog/changelog.zip', Buffer.from(downloadChangelog.data));
86+
87+ - id : suspicious-path-check
88+ name : Suspicious paths check
89+ env :
90+ ARTIFACT_DIR : ${{ runner.temp }}/artifacts/docs
91+ run : |
92+ cd "$ARTIFACT_DIR"
93+ if unzip -l docs.zip | grep -q "\.\./"; then
94+ exit 1
95+ fi
96+
97+ - id : hidden-files-check
98+ name : Hidden files check
99+ env :
100+ ARTIFACT_DIR : ${{ runner.temp }}/artifacts/docs
101+ run : |
102+ cd "$ARTIFACT_DIR"
103+ HIDDEN_FILES=$(find . -type f -name ".*" | wc -l)
104+ if [ "$HIDDEN_FILES" -ne 0 ]; then
105+ echo "Security Alert: Unexpected hidden files detected!"
106+ exit 1
107+ fi
62108
63109 - id : unzip-docs
64- run : unzip docs.zip
110+ name : Unzip docs artifact
111+ env :
112+ ARTIFACT_DIR : ${{ runner.temp }}/artifacts/docs
113+ run : |
114+ cd "$ARTIFACT_DIR"
115+ unzip docs.zip
65116
66- - id : get-top-dir
117+ - id : find-changelog
118+ name : Get changelog
67119 run : |
68- root=$(ls -d */index.html | sed -r 's/(.*)\/index\.html/\1/')
69- echo "top-dir=$root" >> $GITHUB_OUTPUT
120+ if [ -f "${{ runner.temp }}/artifacts/changelog/changelog.zip" ]; then
121+ echo "has-changelog=true" >> $GITHUB_OUTPUT
122+ else
123+ echo "has-changelog=false" >> $GITHUB_OUTPUT
124+ fi
70125
71126 - id : unzip-changelog
72- if : ${{ hashFiles('changelog.zip') != '' }}
73- run : unzip changelog.zip
127+ name : Unzip changelog artifact
128+ if : ${{ steps.find-changelog.outputs.has-changelog == 'true' }}
129+ env :
130+ ARTIFACT_DIR : ${{ runner.temp }}/artifacts/changelog
131+ run : |
132+ cd "$ARTIFACT_DIR"
133+ unzip changelog.zip
74134
75135 - id : get-deploy-id
136+ name : Get deploy ID
137+ env :
138+ ARTIFACT_DIR : ${{ runner.temp }}/artifacts/docs
76139 run : |
77- deployid=$(<deployid)
140+ deployid=$(<"$ARTIFACT_DIR/ deployid" )
78141 case "$deployid" in ''|*[!0-9]*) echo "Provided PR number is not an integer"; exit 1 ;; esac
79142 echo "deploy-id=$deployid" >> "$GITHUB_OUTPUT"
80143
81144 - id : get-deploy-url
145+ name : Get deploy URL
82146 env :
83147 ORG : ${{ github.event.repository.owner.login }}
84148 REPO : ${{ github.event.repository.name }}
@@ -87,38 +151,53 @@ jobs:
87151 deployurl=$ORG-$REPO-$DEPLOYID.surge.sh
88152 echo "deploy-url=$deployurl" >> $GITHUB_OUTPUT
89153
90- - uses : actions/setup-node@v4
154+ - uses : actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
91155 with :
92156 node-version : lts/*
157+
158+ - id : prepare-deploy-dir
159+ name : Prepare deploy directory
160+ shell : bash
161+ env :
162+ DOCS_SRC_DIR : ${{ runner.temp }}/artifacts/docs
163+ DOCS_DEPLOY_DIR : ${{ runner.temp }}/deploy-docs
164+ run : |
165+ mkdir -p "$DOCS_DEPLOY_DIR"
166+ # Copy only the built docs into a clean directory for deployment
167+ cp -R "$DOCS_SRC_DIR"/. "$DOCS_DEPLOY_DIR"/
93168
94- - name : Deploy docs to surge
169+ - id : surge-deploy
170+ name : Deploy docs to surge
95171 shell : bash
96172 env :
97173 DEPLOY_URL : ${{ steps.get-deploy-url.outputs.deploy-url }}
98174 SURGE_TOKEN : " ${{ secrets.DOCS_SURGE_TOKEN }}"
99- SITE_DIR : ${{ steps.get-top-dir.outputs.top-dir }}
175+ DEPLOY_DIR : ${{ runner.temp }}/deploy-docs
100176 run : |
101177 npm install -g surge
102- surge ./$SITE_DIR $DEPLOY_URL --token "$SURGE_TOKEN"
178+ cd "$DEPLOY_DIR"
179+ surge . "$DEPLOY_URL" --token "$SURGE_TOKEN"
103180
104181 # If the PR artifacts include a changelog file, add it to the PR as a comment
105182 # The changelog contains links to new and changed files in the deployed docs
106- - name : Comment on PR (changelog)
107- if : ${{ hashFiles('changelog') != '' }}
108- uses : marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0
183+ - id : comment-changelog
184+ name : Prepare changelog comment
185+ if : ${{ steps.find-changelog.outputs.has-changelog == 'true' }}
186+ uses : marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3
109187 with :
110188 number : ${{ steps.get-deploy-id.outputs.deploy-id }}
111189 recreate : true
112190 header : docs-pr-changes
113- path : changelog
191+ path : ${{ runner.temp }}/artifacts/changelog/ changelog
114192 GITHUB_TOKEN : ${{ secrets.DOCS_PR_COMMENT_TOKEN }}
115193
116194 # If there's no changelog, add a generic comment to the PR
117- - name : Comment on PR (no changelog)
118- if : ${{ hashFiles('changelog') == '' }}
195+ - id : comment-no-changelog
196+ name : Comment on PR (no changelog)
197+ if : ${{ steps.find-changelog.outputs.has-changelog == 'false' }}
119198 env :
120199 DEPLOY_URL : ${{ steps.get-deploy-url.outputs.deploy-url }}
121- uses : marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0
200+ uses : marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3
122201 with :
123202 number : ${{ steps.get-deploy-id.outputs.deploy-id }}
124203 header : docs-pr-changes
0 commit comments