返回 CodeWhale
prepare-release.test.sh
根目录 / scripts / release / prepare-release.test.sh
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5 tmp_dir="$(mktemp -d)"
6 trap 'rm -rf "${tmp_dir}"' EXIT
7
8 make_fixture() {
9 local root="$1"
10 mkdir -p \
11 "${root}/bin" \
12 "${root}/crates/example" \
13 "${root}/crates/tui" \
14 "${root}/docs" \
15 "${root}/npm/codewhale" \
16 "${root}/scripts/remote-smoke" \
17 "${root}/scripts/release" \
18 "${root}/web/lib" \
19 "${root}/web/scripts"
20
21 cp "${repo_root}/scripts/release/prepare-release.sh" \
22 "${root}/scripts/release/prepare-release.sh"
23
24 cat >"${root}/Cargo.toml" <<'EOF'
25 [workspace]
26 members = ["crates/example"]
27
28 [workspace.package]
29 version = "0.8.68"
30 EOF
31
32 cat >"${root}/crates/example/Cargo.toml" <<'EOF'
33 [package]
34 name = "codewhale-example"
35 version.workspace = true
36
37 [dependencies]
38 codewhale-core = { path = "../core", version = "0.8.68" }
39 EOF
40
41 cat >"${root}/npm/codewhale/package.json" <<'EOF'
42 {
43 "name": "codewhale",
44 "version": "0.8.68",
45 "codewhaleBinaryVersion": "0.8.68"
46 }
47 EOF
48
49 cat >"${root}/package-lock.json" <<'EOF'
50 {
51 "name": "fixture",
52 "lockfileVersion": 3,
53 "packages": {
54 "": { "name": "fixture" },
55 "npm/codewhale": {
56 "version": "0.8.68",
57 "license": "MIT"
58 }
59 }
60 }
61 EOF
62
63 cat >"${root}/Cargo.lock" <<'EOF'
64 # fixture lock
65 EOF
66
67 cat >"${root}/CHANGELOG.md" <<'EOF'
68 ## [Unreleased]
69
70 ## [0.9.0] - 2026-07-15
71
72 ### Changed
73
74 - Test release.
75 EOF
76
77 cat >"${root}/docs/INSTALL.md" <<'EOF'
78 The npm wrapper installs the matching published binaries.
79
80 codewhale --version # prints the published version that was installed
81 EOF
82
83 cat >"${root}/docs/public-surface-facts.json" <<'EOF'
84 {
85 "sourceCandidate": { "version": "0.8.68" },
86 "latestPublishedRelease": {
87 "tag": "v0.8.67",
88 "version": "0.8.67",
89 "url": "https://example.invalid/v0.8.67"
90 },
91 "screenshot": { "sourceVersion": "0.8.67" }
92 }
93 EOF
94
95 cat >"${root}/scripts/remote-smoke/setup-vm.sh" <<'EOF'
96 #!/usr/bin/env bash
97 RELEASE_TAG="${RELEASE_TAG:-v0.8.68}"
98 EOF
99
100 printf 'fixture packaged changelog\n' >"${root}/crates/tui/CHANGELOG.md"
101 printf 'fixture generated facts\n' >"${root}/web/lib/facts.generated.ts"
102
103 for readme in README.md README.zh-CN.md README.ja-JP.md README.vi.md README.ko-KR.md; do
104 printf 'Install Codewhale from the package manager.\n' >"${root}/${readme}"
105 done
106
107 cat >"${root}/bin/cargo" <<'EOF'
108 #!/usr/bin/env bash
109 set -euo pipefail
110 : >"${PREPARE_RELEASE_TEST_MARKERS}/cargo"
111 EOF
112
113 cat >"${root}/scripts/sync-changelog.sh" <<'EOF'
114 #!/usr/bin/env bash
115 set -euo pipefail
116 : >"${PREPARE_RELEASE_TEST_MARKERS}/sync-changelog"
117 EOF
118
119 cat >"${root}/scripts/release/check-versions.sh" <<'EOF'
120 #!/usr/bin/env bash
121 set -euo pipefail
122 : >"${PREPARE_RELEASE_TEST_MARKERS}/check-versions"
123 EOF
124
125 cat >"${root}/scripts/release/check-ohos-deps.sh" <<'EOF'
126 #!/usr/bin/env bash
127 set -euo pipefail
128 : >"${PREPARE_RELEASE_TEST_MARKERS}/check-ohos-deps"
129 EOF
130
131 cat >"${root}/web/scripts/derive-facts.mjs" <<'EOF'
132 import { writeFileSync } from "node:fs";
133
134 writeFileSync(`${process.env.PREPARE_RELEASE_TEST_MARKERS}/derive-facts`, "");
135 EOF
136
137 chmod +x \
138 "${root}/bin/cargo" \
139 "${root}/scripts/release/prepare-release.sh" \
140 "${root}/scripts/release/check-ohos-deps.sh" \
141 "${root}/scripts/release/check-versions.sh" \
142 "${root}/scripts/sync-changelog.sh"
143 }
144
145 success_root="${tmp_dir}/success"
146 success_markers="${tmp_dir}/success-markers"
147 make_fixture "${success_root}"
148 mkdir -p "${success_markers}"
149 printf 'Install from a release tag: --tag v0.8.68\n' >"${success_root}/README.md"
150
151 PREPARE_RELEASE_TEST_MARKERS="${success_markers}" \
152 PATH="${success_root}/bin:${PATH}" \
153 "${success_root}/scripts/release/prepare-release.sh" 0.9.0 >/dev/null
154
155 grep -Fq 'version = "0.9.0"' "${success_root}/Cargo.toml"
156 grep -Fq 'version = "0.9.0"' "${success_root}/crates/example/Cargo.toml"
157 grep -Fq '"version": "0.9.0"' "${success_root}/npm/codewhale/package.json"
158 grep -Fq '"codewhaleBinaryVersion": "0.9.0"' \
159 "${success_root}/npm/codewhale/package.json"
160 grep -Fq '"version": "0.9.0"' "${success_root}/package-lock.json"
161 grep -Fq 'RELEASE_TAG="${RELEASE_TAG:-v0.9.0}"' \
162 "${success_root}/scripts/remote-smoke/setup-vm.sh"
163 grep -Fq '"sourceCandidate": { "version": "0.9.0" }' \
164 "${success_root}/docs/public-surface-facts.json"
165 grep -Fq '"tag": "v0.8.67"' \
166 "${success_root}/docs/public-surface-facts.json"
167 grep -Fq '"sourceVersion": "0.8.67"' \
168 "${success_root}/docs/public-surface-facts.json"
169 grep -Fq 'prints the published version that was installed' \
170 "${success_root}/docs/INSTALL.md"
171 grep -Fq -- '--tag v0.9.0' "${success_root}/README.md"
172 if grep -R -E -- '--tag v[0-9]+\.[0-9]+\.[0-9]+' \
173 "${success_root}/README.zh-CN.md" \
174 "${success_root}/README.ja-JP.md" \
175 "${success_root}/README.vi.md" \
176 "${success_root}/README.ko-KR.md"; then
177 echo "tag-free localized README unexpectedly gained a release tag" >&2
178 exit 1
179 fi
180 for marker in cargo sync-changelog derive-facts check-versions check-ohos-deps; do
181 [[ -f "${success_markers}/${marker}" ]] || {
182 echo "prepare-release did not reach ${marker}" >&2
183 exit 1
184 }
185 done
186
187 same_root="${tmp_dir}/same"
188 same_markers="${tmp_dir}/same-markers"
189 same_log="${tmp_dir}/same.log"
190 make_fixture "${same_root}"
191 mkdir -p "${same_markers}"
192 cat >"${same_root}/CHANGELOG.md" <<'EOF'
193 ## [Unreleased]
194
195 ## [0.8.68] - 2026-07-18
196
197 ### Changed
198
199 - Test already-prepared release.
200 EOF
201
202 PREPARE_RELEASE_TEST_MARKERS="${same_markers}" \
203 PATH="${same_root}/bin:${PATH}" \
204 "${same_root}/scripts/release/prepare-release.sh" 0.8.68 \
205 >"${same_log}"
206
207 grep -Fq \
208 'Workspace is already at 0.8.68; refreshing generated release state and rerunning gates.' \
209 "${same_log}"
210 for marker in sync-changelog derive-facts check-versions check-ohos-deps; do
211 [[ -f "${same_markers}/${marker}" ]] || {
212 echo "same-version prepare-release did not reach ${marker}" >&2
213 exit 1
214 }
215 done
216 if [[ -f "${same_markers}/cargo" ]]; then
217 echo "same-version prepare-release unexpectedly mutated Cargo.lock" >&2
218 exit 1
219 fi
220 grep -Fq 'version = "0.8.68"' "${same_root}/Cargo.toml"
221 grep -Fq 'version = "0.8.68"' "${same_root}/crates/example/Cargo.toml"
222 grep -Fq '"version": "0.8.68"' "${same_root}/npm/codewhale/package.json"
223
224 same_stale_root="${tmp_dir}/same-stale"
225 same_stale_markers="${tmp_dir}/same-stale-markers"
226 same_stale_log="${tmp_dir}/same-stale.log"
227 make_fixture "${same_stale_root}"
228 mkdir -p "${same_stale_markers}"
229 python3 - "${same_stale_root}/docs/public-surface-facts.json" <<'PY'
230 import pathlib, sys
231
232 path = pathlib.Path(sys.argv[1])
233 path.write_text(path.read_text().replace(
234 '"sourceCandidate": { "version": "0.8.68" }',
235 '"sourceCandidate": { "version": "0.8.67" }',
236 ))
237 PY
238
239 if PREPARE_RELEASE_TEST_MARKERS="${same_stale_markers}" \
240 PATH="${same_stale_root}/bin:${PATH}" \
241 "${same_stale_root}/scripts/release/prepare-release.sh" 0.8.68 \
242 >"${same_stale_log}" 2>&1; then
243 echo "same-version stale source candidate unexpectedly passed" >&2
244 exit 1
245 fi
246 grep -Fq \
247 "sourceCandidate.version is '0.8.67'; expected '0.8.68'" \
248 "${same_stale_log}"
249 if find "${same_stale_markers}" -type f -print -quit | grep -q .; then
250 echo "same-version stale source candidate reached downstream release steps" >&2
251 exit 1
252 fi
253
254 legacy_root="${tmp_dir}/legacy"
255 legacy_markers="${tmp_dir}/legacy-markers"
256 make_fixture "${legacy_root}"
257 mkdir -p "${legacy_markers}"
258 cat >"${legacy_root}/docs/INSTALL.md" <<'EOF'
259 The npm wrapper is published at v0.8.68.
260 codewhale --version # 0.8.68
261 EOF
262
263 PREPARE_RELEASE_TEST_MARKERS="${legacy_markers}" \
264 PATH="${legacy_root}/bin:${PATH}" \
265 "${legacy_root}/scripts/release/prepare-release.sh" 0.9.0 >/dev/null
266
267 grep -Fq 'wrapper is published at v0.9.0' "${legacy_root}/docs/INSTALL.md"
268 grep -Fq 'codewhale --version # 0.9.0' "${legacy_root}/docs/INSTALL.md"
269
270 stale_root="${tmp_dir}/stale"
271 stale_markers="${tmp_dir}/stale-markers"
272 stale_log="${tmp_dir}/stale.log"
273 make_fixture "${stale_root}"
274 mkdir -p "${stale_markers}"
275 printf 'Stale install example: --tag v0.8.67\n' >"${stale_root}/README.ja-JP.md"
276
277 if PREPARE_RELEASE_TEST_MARKERS="${stale_markers}" \
278 PATH="${stale_root}/bin:${PATH}" \
279 "${stale_root}/scripts/release/prepare-release.sh" 0.9.0 \
280 >"${stale_log}" 2>&1; then
281 echo "stale README release tag unexpectedly passed" >&2
282 exit 1
283 fi
284
285 grep -Fq \
286 'README.ja-JP.md has release tag version(s) 0.8.67; expected 0.8.68' \
287 "${stale_log}"
288 grep -Fq 'version = "0.8.68"' "${stale_root}/Cargo.toml"
289 grep -Fq 'version = "0.8.68"' "${stale_root}/crates/example/Cargo.toml"
290 grep -Fq '"version": "0.8.68"' "${stale_root}/npm/codewhale/package.json"
291 if find "${stale_markers}" -type f -print -quit | grep -q .; then
292 echo "stale README validation mutated downstream release state" >&2
293 exit 1
294 fi
295
296 # A downstream command failure must restore every release-bearing file, not
297 # strand the fixture after the text mutation phase.
298 failure_root="${tmp_dir}/failure"
299 failure_snapshot="${tmp_dir}/failure-before"
300 failure_markers="${tmp_dir}/failure-markers"
301 failure_log="${tmp_dir}/failure.log"
302 make_fixture "${failure_root}"
303 mkdir -p "${failure_markers}"
304 cp -R "${failure_root}" "${failure_snapshot}"
305 cat >"${failure_root}/bin/cargo" <<'EOF'
306 #!/usr/bin/env bash
307 set -euo pipefail
308 exit 42
309 EOF
310 chmod +x "${failure_root}/bin/cargo"
311 # Snapshot the intentional failing cargo shim too; only release preparation's
312 # effects should be absent after rollback.
313 cp "${failure_root}/bin/cargo" "${failure_snapshot}/bin/cargo"
314
315 if PREPARE_RELEASE_TEST_MARKERS="${failure_markers}" \
316 PATH="${failure_root}/bin:${PATH}" \
317 "${failure_root}/scripts/release/prepare-release.sh" 0.9.0 \
318 >"${failure_log}" 2>&1; then
319 echo "failing cargo refresh unexpectedly passed" >&2
320 exit 1
321 fi
322
323 grep -Fq "restored the checkout's pre-run release files" "${failure_log}"
324 diff -ru "${failure_snapshot}" "${failure_root}"
325
326 echo "prepare-release tests passed"
327
327 lines BASH