66 - main
77 paths :
88 - ' docs/**'
9+ - ' packages/**'
910 - ' package.json'
11+ - ' pnpm-lock.yaml'
12+ - ' pnpm-workspace.yaml'
1013 - ' .github/workflows/docs.yml'
11- workflow_dispatch :
14+ - ' scripts/docs/promotion-resolver.cjs '
1215
13- # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
14- permissions :
15- contents : read
16- pages : write
17- id-token : write
16+ permissions : {}
1817
19- # Allow one concurrent deployment
2018concurrency :
2119 group : pages
2220 cancel-in-progress : true
2321
24- # Environment variables for caching
2522env :
2623 NODE_VERSION : ' 22.20.0'
2724 PNPM_VERSION : ' 10.19.0'
2825
2926jobs :
30- build :
31- name : Qualify and build authoring documentation
27+ resolve-source :
28+ name : Resolve promoted authoring source
3229 runs-on : ubuntu-latest
30+ permissions :
31+ contents : read
32+ pull-requests : read
3333 outputs :
34- ready : ${{ steps.qualify.outputs.ready }}
35- source-sha : ${{ steps.qualify.outputs.source-sha }}
34+ source-sha : ${{ steps.receipt.outputs.source-sha }}
35+ source-ci-run-id : ${{ steps.receipt.outputs.source-ci-run-id }}
36+ source-ci-run-url : ${{ steps.receipt.outputs.source-ci-run-url }}
3637 steps :
37- - name : Check out authoring source
38+ - name : Check out promotion resolver
3839 uses : actions/checkout@v4
3940 with :
40- repository : theGeekist/wpkernel-1
41- ref : main
4241 fetch-depth : 1
4342 persist-credentials : false
4443
45- - name : Qualify authoring revision
46- id : qualify
44+ - name : Verify promotion receipt and authoring CI
45+ id : receipt
4746 shell : bash
47+ env :
48+ GITHUB_TOKEN : ${{ github.token }}
4849 run : |
4950 set -euo pipefail
50- source_sha="$(git rev-parse HEAD)"
51- author_name="$(git show -s --format=%an HEAD)"
52- author_email="$(git show -s --format=%ae HEAD)"
53- committer_name="$(git show -s --format=%cn HEAD)"
54- committer_email="$(git show -s --format=%ce HEAD)"
55-
56- test "${author_name}" = 'Pipe Work'
57- test "${author_email}" = '780157+pipewrk@users.noreply.github.com'
58- test "${committer_name}" = 'Pipe Work'
59- test "${committer_email}" = '780157+pipewrk@users.noreply.github.com'
60-
61- printf 'source-sha=%s\n' "${source_sha}" >> "${GITHUB_OUTPUT}"
62- api_url="https://api.github.com/repos/theGeekist/wpkernel-1/actions/workflows/ci.yml/runs?branch=main&event=push&head_sha=${source_sha}&per_page=100"
63- for attempt in {1..30}; do
64- ci_state="$(
65- curl --fail --silent --show-error --location \
51+
52+ public_get() {
53+ local url=$1
54+ local destination=$2
55+ local headers="${destination}.headers"
56+ local status
57+
58+ if ! status="$(
59+ curl --silent --show-error --location \
60+ --connect-timeout 10 \
61+ --max-time 45 \
62+ --retry 3 \
63+ --retry-all-errors \
64+ --retry-delay 2 \
6665 --header 'Accept: application/vnd.github+json' \
66+ --header 'Cache-Control: no-cache' \
6767 --header 'X-GitHub-Api-Version: 2022-11-28' \
68- "${api_url}" |
69- jq --arg sha "${source_sha}" '
70- [.workflow_runs[] | select(
71- .head_sha == $sha and
72- .head_branch == "main" and
73- .event == "push"
74- )] as $runs |
75- if any($runs[]; .status == "completed" and .conclusion == "success") then
76- "success"
77- elif any($runs[]; .status == "completed") then
78- "failure"
79- else
80- "pending"
81- end'
82- )"
68+ --dump-header "${headers}" \
69+ --output "${destination}" \
70+ --write-out '%{http_code}' \
71+ "${url}"
72+ )"; then
73+ echo "Unauthenticated GitHub API request failed before an HTTP response: ${url}" >&2
74+ return 1
75+ fi
76+ node scripts/docs/promotion-resolver.cjs check-public-response \
77+ --url "${url}" \
78+ --status "${status}" \
79+ --headers-file "${headers}" \
80+ --body-file "${destination}"
81+ }
82+
83+ pulls_files=()
84+ for page in 1 2 3; do
85+ pulls_page_file="${RUNNER_TEMP}/associated-pulls-${page}.json"
86+ curl --fail --silent --show-error --location \
87+ --connect-timeout 10 \
88+ --max-time 45 \
89+ --header "Authorization: Bearer ${GITHUB_TOKEN}" \
90+ --header 'Accept: application/vnd.github+json' \
91+ --header 'X-GitHub-Api-Version: 2022-11-28' \
92+ --output "${pulls_page_file}" \
93+ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/commits/${GITHUB_SHA}/pulls?per_page=100&page=${page}"
94+ pulls_files+=("${pulls_page_file}")
95+ page_state="$(node scripts/docs/promotion-resolver.cjs associated-pull-page-state \
96+ --page-file "${pulls_page_file}" \
97+ --page-number "${page}")"
98+ if [[ "${page_state}" == 'complete' ]]; then
99+ break
100+ fi
101+ done
102+ pulls_args=()
103+ for pulls_file in "${pulls_files[@]}"; do
104+ pulls_args+=(--pulls-file "${pulls_file}")
105+ done
106+ source_sha="$(node scripts/docs/promotion-resolver.cjs find-promotion \
107+ --upstream-sha "${GITHUB_SHA}" \
108+ "${pulls_args[@]}")"
109+ upstream_commit_file="${RUNNER_TEMP}/upstream-commit.json"
110+ curl --fail --silent --show-error --location \
111+ --connect-timeout 10 \
112+ --max-time 45 \
113+ --header "Authorization: Bearer ${GITHUB_TOKEN}" \
114+ --header 'Accept: application/vnd.github+json' \
115+ --header 'X-GitHub-Api-Version: 2022-11-28' \
116+ --output "${upstream_commit_file}" \
117+ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/git/commits/${GITHUB_SHA}"
118+ source_commit_file="${RUNNER_TEMP}/authoring-commit.json"
119+ public_get \
120+ "${GITHUB_API_URL}/repos/theGeekist/wpkernel-1/git/commits/${source_sha}" \
121+ "${source_commit_file}"
122+ IFS=$'\t' read -r source_sha source_tree < <(
123+ node scripts/docs/promotion-resolver.cjs resolve-promotion \
124+ --upstream-sha "${GITHUB_SHA}" \
125+ --upstream-commit-file "${upstream_commit_file}" \
126+ --source-commit-file "${source_commit_file}" \
127+ "${pulls_args[@]}"
128+ )
129+
130+ ci_api_url="${GITHUB_API_URL}/repos/theGeekist/wpkernel-1/actions/workflows/ci.yml/runs?branch=main&event=push&head_sha=${source_sha}&per_page=100"
131+ # Ten polls with 710 seconds of scheduled wait. Request
132+ # timeouts and workflow overhead are additional.
133+ poll_wait_delays_seconds=(15 20 30 45 60 90 120 150 180)
134+ poll_attempts=$((${#poll_wait_delays_seconds[@]} + 1))
135+ for (( attempt = 1; attempt <= poll_attempts; attempt += 1 )); do
136+ ci_file="${RUNNER_TEMP}/authoring-ci-${attempt}.json"
137+ public_get "${ci_api_url}&poll=${attempt}" "${ci_file}"
138+ ci_result="$(node scripts/docs/promotion-resolver.cjs select-ci \
139+ --source-sha "${source_sha}" \
140+ --response-file "${ci_file}")"
141+ IFS=$'\t' read -r ci_state ci_run_id ci_run_url <<< "${ci_result}"
83142
84143 case "${ci_state}" in
85144 success)
86- printf 'ready=true\n' >> "${GITHUB_OUTPUT}"
145+ printf 'source-sha=%s\n' "${source_sha}" >> "${GITHUB_OUTPUT}"
146+ printf 'source-ci-run-id=%s\n' "${ci_run_id}" >> "${GITHUB_OUTPUT}"
147+ printf 'source-ci-run-url=%s\n' "${ci_run_url}" >> "${GITHUB_OUTPUT}"
148+ {
149+ echo '### Documentation promotion receipt'
150+ echo
151+ echo "- Upstream revision: \`${GITHUB_SHA}\`"
152+ echo "- Authoring revision: \`${source_sha}\`"
153+ echo "- Tree: \`${source_tree}\`"
154+ echo "- Authoring CI: ${ci_run_url}"
155+ } >> "${GITHUB_STEP_SUMMARY}"
87156 exit 0
88157 ;;
89158 failure)
90- echo "Authoring CI did not pass for ${source_sha}; deployment deferred."
91- printf 'ready=false\n' >> "${GITHUB_OUTPUT}"
92- exit 0
159+ echo "Authoring CI run ${ci_run_url} failed for ${source_sha}." >&2
160+ exit 1
93161 ;;
94162 esac
95163
96- sleep 10
164+ if (( attempt < poll_attempts )); then
165+ sleep "${poll_wait_delays_seconds[attempt - 1]}"
166+ fi
97167 done
98168
99- echo "Authoring CI did not complete for ${source_sha}; deployment deferred."
100- printf 'ready=false\n' >> "${GITHUB_OUTPUT}"
169+ echo "Authoring CI did not complete for ${source_sha}." >&2
170+ exit 1
171+
172+ build :
173+ name : Build promoted authoring documentation
174+ needs : resolve-source
175+ runs-on : ubuntu-latest
176+ permissions :
177+ contents : read
178+ steps :
179+ - name : Check out promoted authoring source
180+ uses : actions/checkout@v4
181+ with :
182+ repository : theGeekist/wpkernel-1
183+ ref : ${{ needs.resolve-source.outputs.source-sha }}
184+ fetch-depth : 1
185+ persist-credentials : false
186+
187+ - name : Verify checked-out source
188+ shell : bash
189+ env :
190+ SOURCE_SHA : ${{ needs.resolve-source.outputs.source-sha }}
191+ run : test "$(git rev-parse HEAD)" = "${SOURCE_SHA}"
101192
102193 - name : Setup pnpm
103- if : steps.qualify.outputs.ready == 'true'
104194 uses : pnpm/action-setup@v4
105195 with :
106196 version : ${{ env.PNPM_VERSION }}
107197
108198 - name : Setup Node.js
109- if : steps.qualify.outputs.ready == 'true'
110199 uses : actions/setup-node@v4
111200 with :
112201 node-version : ${{ env.NODE_VERSION }}
113202 cache : ' pnpm'
114203
115204 - name : Cache node_modules
116- if : steps.qualify.outputs.ready == 'true'
117205 id : cache-node-modules
118206 uses : actions/cache@v4
119207 with :
@@ -126,31 +214,62 @@ jobs:
126214 node-modules-${{ runner.os }}-
127215
128216 - name : Install dependencies
129- if : steps.qualify.outputs.ready == 'true' && steps. cache-node-modules.outputs.cache-hit != 'true'
217+ if : steps.cache-node-modules.outputs.cache-hit != 'true'
130218 run : pnpm install --frozen-lockfile
131219
132- - name : Setup Pages
133- if : steps.qualify.outputs.ready == 'true'
134- uses : actions/configure-pages@v5
135-
136220 - name : Build documentation
137- if : steps.qualify.outputs.ready == 'true'
138221 run : pnpm docs:build
139222
140- - name : Upload artifact
141- if : steps.qualify.outputs.ready == 'true'
142- uses : actions/upload-pages-artifact@v3
223+ - name : Upload built documentation
224+ uses : actions/upload-artifact@v4
143225 with :
226+ name : authoring-docs-site
144227 path : docs/.vitepress/dist
228+ if-no-files-found : error
229+ retention-days : 1
145230
146231 deploy :
232+ name : Deploy promoted documentation
233+ needs :
234+ - resolve-source
235+ - build
236+ runs-on : ubuntu-latest
237+ permissions :
238+ actions : read
239+ pages : write
240+ id-token : write
147241 environment :
148242 name : github-pages
149243 url : ${{ steps.deployment.outputs.page_url }}
150- needs : build
151- if : needs.build.outputs.ready == 'true'
152- runs-on : ubuntu-latest
153244 steps :
245+ - name : Configure Pages
246+ uses : actions/configure-pages@v5
247+
248+ - name : Download built documentation
249+ uses : actions/download-artifact@v4
250+ with :
251+ name : authoring-docs-site
252+ path : ${{ runner.temp }}/authoring-docs-site
253+
254+ - name : Upload Pages artifact
255+ uses : actions/upload-pages-artifact@v3
256+ with :
257+ path : ${{ runner.temp }}/authoring-docs-site
258+
259+ - name : Record deployment receipt
260+ shell : bash
261+ env :
262+ SOURCE_CI_RUN_ID : ${{ needs.resolve-source.outputs.source-ci-run-id }}
263+ SOURCE_CI_RUN_URL : ${{ needs.resolve-source.outputs.source-ci-run-url }}
264+ SOURCE_SHA : ${{ needs.resolve-source.outputs.source-sha }}
265+ run : |
266+ {
267+ echo '### Documentation deployment receipt'
268+ echo
269+ echo "- Authoring revision: \`${SOURCE_SHA}\`"
270+ echo "- Authoring CI run: [${SOURCE_CI_RUN_ID}](${SOURCE_CI_RUN_URL})"
271+ } >> "${GITHUB_STEP_SUMMARY}"
272+
154273 - name : Deploy to GitHub Pages
155274 id : deployment
156275 uses : actions/deploy-pages@v4
0 commit comments