Skip to content

Commit c635af2

Browse files
mvvmmkodster28
andauthored
Only build once in PR workflows: Step 1 (#28975)
Note: We're adding back the compiles job, to meet the repo requirements of having a compiles job. So currently, this still builds twice. We will change the repo rules after merging this PR to only require build and validate. Then we will add another PR to remove the compiles job. Previously, opening a PR triggered two separate full site builds: one in ci.yml (for checks) and one in publish-preview.yml (for preview deployment). This PR consolidates them into a single build. --------- Co-authored-by: Kody Jackson <kody@cloudflare.com>
1 parent 1e790c7 commit c635af2

6 files changed

Lines changed: 262 additions & 105 deletions

File tree

.github/workflows/ci.yml

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,211 @@ jobs:
8888
env:
8989
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
9090
run: npx tsx bin/post-pr-ci-failure-comment/index.ts
91+
92+
build:
93+
name: Build
94+
runs-on: ubuntu-latest
95+
outputs:
96+
link_check_failed: ${{ steps.check_link_result.outputs.failed }}
97+
permissions:
98+
contents: read
99+
pull-requests: write
100+
steps:
101+
- uses: actions/checkout@v4
102+
with:
103+
fetch-depth: 1
104+
105+
- uses: actions/setup-node@v4
106+
with:
107+
node-version: 22.x
108+
cache: npm
109+
110+
- run: |
111+
FILES=$(
112+
find src/content \
113+
-type f \
114+
-not -name '*.mdx' \
115+
-not -name '*.md' \
116+
-not -name '*.json' \
117+
-not -name '*.yml' \
118+
-not -name '*.yaml' \
119+
-not -name '*.txt' \
120+
-not -wholename 'src/content/collections/*'
121+
)
122+
123+
if [ -n "$FILES" ]; then
124+
echo "Found files with invalid file extensions:\n\n$FILES"
125+
exit 1
126+
fi
127+
128+
- run: npm ci
129+
130+
- uses: actions/cache/restore@v4
131+
with:
132+
path: |
133+
node_modules/.astro/assets
134+
key: static
135+
136+
- run: npx tsx bin/post-codeowners-comment/index.ts
137+
continue-on-error: true
138+
env:
139+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
140+
141+
- run: npm run check
142+
143+
# The starlight-links-validator plugin runs in astro:build:done, which fires
144+
# AFTER all pages have been written to dist/. If link validation fails, the
145+
# build exits non-zero but dist/ is complete. We use continue-on-error so the
146+
# job succeeds (allowing deploy + validate to run), then check the outcome below.
147+
- run: npm run build
148+
id: build_step
149+
name: Build
150+
continue-on-error: true
151+
env:
152+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
153+
RUN_LINK_CHECK: true
154+
155+
# Distinguish between "link check failed" (dist/ exists) and "real build failure" (no dist/).
156+
# If the build produced no output, we must fail the job immediately.
157+
- name: Check build result
158+
id: check_link_result
159+
run: |
160+
if [ ! -d dist ] || [ -z "$(ls -A dist)" ]; then
161+
echo "Build failed before producing output. Check the Build job logs for the error."
162+
exit 1
163+
fi
164+
if [ "${{ steps.build_step.outcome }}" = "failure" ]; then
165+
echo "failed=true" >> "$GITHUB_OUTPUT"
166+
echo "::warning::Build succeeded but link validation failed. Preview will still be deployed."
167+
else
168+
echo "failed=false" >> "$GITHUB_OUTPUT"
169+
fi
170+
171+
- uses: actions/cache/save@v4
172+
with:
173+
path: |
174+
node_modules/.astro/assets
175+
key: static
176+
177+
- uses: actions/upload-artifact@v4
178+
with:
179+
name: dist
180+
path: dist
181+
182+
validate:
183+
name: Validate
184+
needs: build
185+
runs-on: ubuntu-latest
186+
permissions:
187+
contents: read
188+
pull-requests: write
189+
steps:
190+
- uses: actions/checkout@v4
191+
with:
192+
fetch-depth: 1
193+
194+
- uses: actions/setup-node@v4
195+
with:
196+
node-version: 22.x
197+
cache: npm
198+
199+
- run: npm ci
200+
201+
- uses: actions/download-artifact@v4
202+
with:
203+
name: dist
204+
path: dist
205+
206+
- uses: reviewdog/action-eslint@v1
207+
with:
208+
github_token: ${{ secrets.GITHUB_TOKEN }}
209+
reporter: github-pr-review
210+
fail_level: error
211+
filter_mode: nofilter
212+
213+
- run: npm run format:core:check
214+
215+
- name: Validate redirects
216+
run: npx tsm bin/validate-redirects.ts
217+
218+
- name: Tests
219+
run: npm run test
220+
221+
- name: Link validation
222+
if: needs.build.outputs.link_check_failed == 'true'
223+
run: |
224+
echo "::error::starlight-links-validator found broken internal links during the build. See the Build job logs for details."
225+
exit 1
226+
227+
notify:
228+
name: Notify
229+
needs: [build, validate]
230+
if: always()
231+
runs-on: ubuntu-latest
232+
permissions:
233+
contents: read
234+
pull-requests: write
235+
steps:
236+
- uses: actions/checkout@v4
237+
with:
238+
fetch-depth: 1
239+
240+
- uses: actions/setup-node@v4
241+
with:
242+
node-version: 22.x
243+
cache: npm
244+
245+
- run: npm ci
246+
247+
- name: Post PR CI failure comment
248+
continue-on-error: true
249+
env:
250+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
251+
run: npx tsx bin/post-pr-ci-failure-comment/index.ts
252+
253+
publish-preview:
254+
name: Deploy Preview
255+
needs: build
256+
if: github.repository == 'cloudflare/cloudflare-docs'
257+
runs-on: ubuntu-latest
258+
permissions:
259+
contents: read
260+
pull-requests: write
261+
steps:
262+
- uses: actions/checkout@v4
263+
with:
264+
fetch-depth: 1
265+
266+
- uses: actions/setup-node@v4
267+
with:
268+
node-version: 22.x
269+
cache: npm
270+
271+
- run: npm ci
272+
273+
- uses: actions/download-artifact@v4
274+
with:
275+
name: dist
276+
path: dist
277+
278+
- name: Deploy to Cloudflare Workers
279+
id: deploy
280+
env:
281+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
282+
run: |
283+
SHORT_SHA="${{ github.event.pull_request.head.sha }}"
284+
SHORT_SHA="${SHORT_SHA:0:8}"
285+
BRANCH="${{ github.event.pull_request.head.ref }}"
286+
BRANCH_SLUG=$(echo "$BRANCH" | iconv -c -t ascii//TRANSLIT | sed -E 's/[~^]+//g' | sed -E 's/[^a-zA-Z0-9]+/-/g' | sed -E 's/^-+|-+$//g' | tr A-Z a-z)
287+
288+
echo "branch_slug=$BRANCH_SLUG" >> "$GITHUB_OUTPUT"
289+
290+
npx wrangler deploy --dispatch-namespace preview-deployments --name $SHORT_SHA
291+
npx wrangler deploy --dispatch-namespace preview-deployments --name $BRANCH_SLUG
292+
293+
- name: Post preview URL on PR
294+
env:
295+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
296+
BRANCH_SLUG: ${{ steps.deploy.outputs.branch_slug }}
297+
run: npx tsx bin/post-preview-url-comment/index.ts
298+
continue-on-error: true

.github/workflows/publish-preview.yml

Lines changed: 0 additions & 55 deletions
This file was deleted.

bin/post-pr-ci-failure-comment/index.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,29 @@ async function run(): Promise<void> {
2424
run_id: runId,
2525
});
2626

27-
const job = run.jobs.findLast((job) => job.name === "Compiles");
27+
const ciJobs = run.jobs.filter(
28+
(job) => job.name === "Build" || job.name === "Validate",
29+
);
2830

29-
if (!job) {
30-
core.setFailed(`Could not find a job called 'Compiles'`);
31+
if (ciJobs.length === 0) {
32+
core.setFailed(`Could not find Build or Validate jobs`);
3133
process.exit();
3234
}
3335

34-
const failedStep = job.steps?.find((step) => step.conclusion === "failure");
36+
const failedJob = ciJobs.find((job) =>
37+
job.steps?.some((step) => step.conclusion === "failure"),
38+
);
3539

36-
if (failedStep) {
37-
core.info(`Found failed step ${failedStep.name}`);
40+
if (failedJob) {
41+
const failedStep = failedJob.steps?.find(
42+
(step) => step.conclusion === "failure",
43+
);
44+
core.info(
45+
`Found failed step ${failedStep?.name} in job ${failedJob.name}`,
46+
);
3847
}
3948

40-
const conclusion = failedStep ? "failure" : "success";
49+
const conclusion = failedJob ? "failure" : "success";
4150

4251
const { data: comments } = await octokit.rest.issues.listComments({
4352
owner,
@@ -58,7 +67,8 @@ async function run(): Promise<void> {
5867
core.info(`No existing comment found`);
5968
}
6069

61-
const url = `https://github.com/${owner}/${repo}/actions/runs/${runId}/job/${job.id}`;
70+
const targetJob = failedJob ?? ciJobs[0];
71+
const url = `https://github.com/${owner}/${repo}/actions/runs/${runId}/job/${targetJob.id}`;
6272
const comment = `**CI run failed:** [build logs](${url})`;
6373

6474
if (conclusion === "failure") {

bin/post-preview-url-comment/index.node.test.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, test } from "vitest";
22
import { DOCS_BASE_URL, PREVIEW_URL_REGEX } from "./constants";
3-
import { filenameToPath, branchToSubdomain } from "./util";
3+
import { filenameToPath } from "./util";
44

55
describe("PREVIEW_URL_REGEX", () => {
66
test("no changed files", () => {
@@ -22,20 +22,6 @@ describe("PREVIEW_URL_REGEX", () => {
2222
});
2323
});
2424

25-
describe("branchToSubdomain", () => {
26-
test("slash", () => {
27-
expect(branchToSubdomain("kian/pcx-15803")).toEqual("kian-pcx-15803");
28-
});
29-
30-
test("normal", () => {
31-
expect(branchToSubdomain("pcx-15803")).toEqual("pcx-15803");
32-
});
33-
34-
test("capitalisation", () => {
35-
expect(branchToSubdomain("PCX-15803")).toEqual("pcx-15803");
36-
});
37-
});
38-
3925
describe("filenameToPath", () => {
4026
test("index", () => {
4127
expect(filenameToPath("src/content/docs/workers/index.mdx")).toEqual(

0 commit comments

Comments
 (0)