Repository navigation
175 lines (156 loc) · 7.62 KB
/
Copy pathci-web.yml
File metadata and controls
175 lines (156 loc) · 7.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
name: Web CI (lint, test, build)
# Lint, test and build the SPA on a GitHub-hosted Linux runner.
#
# This job exists because nothing was running any of these three commands.
# The checks on this repo were licenses, PR hygiene, gitleaks and actionlint,
# so a pull request could be green with a main that did not compile -- which
# is exactly what happened in #98 and was only found when the 1670 FleetOS
# host could not be rebuilt (#99).
#
# All three are run, not just one of them, because each catches what the
# others miss: `vitest run` does not typecheck, and `tsc -b` runs only inside
# `npm run build`, so a type error in a test file passes the test suite and
# fails the production build.
# Pull requests only, and not while the PR is a draft. There is no run on push
# to main: the squash merge lands the tree the PR run already tested.
#
# Skip when a change touches only Markdown or issue templates. A mixed change
# still runs, since paths-ignore only skips when every changed file matches.
on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- "**.md"
- ".github/ISSUE_TEMPLATE/**"
permissions:
contents: read
# One run per PR per workflow: a new push cancels the older PR run.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
web-validation:
name: Web validation (lint, test, build)
if: github.event.pull_request.draft == false
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
# 22 LTS. The repo declares no `engines` range, so this pin is the
# only statement of what version the project is expected to build
# on; if that ever needs to change, change it here on purpose.
node-version: 22
cache: npm
- name: Install dependencies
# `npm ci` rather than `npm install`: it installs exactly the lockfile
# and fails if package.json and the lockfile disagree, so CI cannot
# quietly pass against a dependency tree no one else has.
run: npm ci
- name: Install buf
# The generated-stubs check below regenerates packages/api-client with
# buf. The release binary is checked against the SHA-256 its release
# publishes (sha256.txt); bump both together.
env:
BUF_VERSION: 1.73.0
BUF_SHA256: 8f2986298ad08f0cc1bf999b9797b7c383adf32d7edf0f73d6f1e1a701baeac1
run: |
set -euo pipefail
bin="$RUNNER_TEMP/bin"
mkdir -p "$bin"
curl -sSfL -o "$bin/buf" \
"https://github.com/bufbuild/buf/releases/download/v${BUF_VERSION}/buf-Linux-x86_64"
echo "${BUF_SHA256} $bin/buf" | sha256sum -c -
chmod +x "$bin/buf"
echo "$bin" >> "$GITHUB_PATH"
- name: Lint
run: npm run lint
- name: Release bundle check's own tests
# scripts/ is plain Node, outside the npm workspaces the Test step
# below covers, so its node:test suite gets its own step.
run: node --test scripts/*.test.mjs
- name: Test
run: npm run test
- name: Build
# tsc -b then vite build. This is the step that #99 was about.
run: npm run build
- name: Release bundle check catches dev-only builds
# A positive control for the check below, so it can't be vacuously
# passing: building with VITE_FLEET_MODE=mock or DEV_UI_ISSUE_COPY=true
# must make it fail. `vite build` directly (skipping tsc -b, already
# run by Build above) into a throwaway outDir keeps this cheap.
#
# check_catches requires exit code 1 exactly (development-only code
# found), not merely non-zero: the script also exits 2 for an invalid
# dist directory (missing, empty, no index.html, no built JS), which
# a wrong or empty --outDir here would trigger without the build ever
# being scanned -- "non-zero" alone would wave that through as if the
# control had passed.
run: |
set -euo pipefail
check_catches() {
status=0
node scripts/check-release-bundle.mjs "$1" || status=$?
if [ "$status" -ne 1 ]; then
echo "expected scripts/check-release-bundle.mjs to exit 1 (development-only code found) for $2, got exit $status" >&2
exit 1
fi
}
VITE_FLEET_MODE=mock npm exec -w @cryptos-pki/console -- vite build --outDir dist-check-mock
check_catches apps/console/dist-check-mock "a VITE_FLEET_MODE=mock build"
rm -rf apps/console/dist-check-mock
DEV_UI_ISSUE_COPY=true npm exec -w @cryptos-pki/console -- vite build --outDir dist-check-devui
check_catches apps/console/dist-check-devui "a DEV_UI_ISSUE_COPY=true build"
rm -rf apps/console/dist-check-devui
- name: Release bundle has no development-only code
# The existing Build step above builds without DEV_UI_ISSUE_COPY or
# VITE_FLEET_MODE, so its output is the release build.
run: node scripts/check-release-bundle.mjs apps/console/dist
- name: Icons match their sources
run: npm -w @cryptos-pki/ui run generate:icons && git diff --exit-code packages/ui/src/icons
- name: No embedded design-tool metadata
# A plain `grep -r ... packages apps/console/public` fails outright
# (exit 2, not 1) while apps/console/public doesn't exist yet, which
# `!` would silently turn into a false pass. `find` with a missing
# starting path is no better under this shell's `-eo pipefail`: find
# itself exits 1 when one of its starting points doesn't exist, which
# aborts the step before the hits check ever runs -- so the directory
# list is built from only the paths that exist first. gen-icons.mjs
# and icons.test.tsx are the only files allowed to say "c2pa" --
# stripping and checking for it is the whole point of both -- so
# they're excluded by name; any other hit is a real embedded manifest.
# GNU find also propagates a non-zero exit from `-exec ... +` itself,
# and grep exits 1 on the normal, clean case where nothing matches --
# `|| true` keeps that from aborting the step under `-e`; the actual
# verdict is the $hits check below.
run: |
dirs=()
for d in packages apps/console/public; do
[ -d "$d" ] && dirs+=("$d")
done
hits="$(find "${dirs[@]}" -type f \
! -path '*/node_modules/*' ! -name gen-icons.mjs ! -name icons.test.tsx \
-exec grep -lI -i c2pa {} + || true)"
if [ -n "$hits" ]; then
echo "$hits" >&2
exit 1
fi
- name: Generated stubs match the pinned protos
# git status rather than git diff, so a newly generated file counts as
# stale too instead of passing as untracked.
run: |
npm run generate
stale="$(git status --porcelain packages/api-client/src/gen)"
if [ -n "$stale" ]; then
echo "packages/api-client/src/gen is out of date. Run 'npm run generate' and commit the result." >&2
echo "$stale" >&2
exit 1
fi
- name: Check tree is clean
# A formatter or a generated file that rewrites something in place
# would otherwise pass here and surprise the next person.
run: git diff --exit-code