typespec1-11 Exercise 2: Freezing Published Versions =========================================================== The new function and step added to ci.sh (the diff against the original script): diff --git a/ci.sh b/ci.sh index 84a9a7a..f879f8b 100644 --- a/ci.sh +++ b/ci.sh @@ -27,12 +27,25 @@ breaking_vs_base() { return $status } +frozen_vs_base() { + # Stricter policy: a generated file that already exists on the base branch is a released version and must not change at all. + status=0 + for f in $(git ls-tree --name-only "$BASE" api/openapi/ 2>/dev/null); do + if ! git diff --quiet "$BASE" -- "$f"; then + echo "$f changed, but it is a released version" + status=1 + fi + done + return $status +} + cd api || exit 1 step "tsp format --check" npx tsp format "**/*.tsp" --check step "tsp compile --warn-as-error" npx tsp compile . --warn-as-error cd .. step "generated OpenAPI is committed" sh -c 'git diff --exit-code -- api/openapi >/dev/null && test -z "$(git ls-files --others --exclude-standard api/openapi)"' step "no breaking change vs $BASE" breaking_vs_base +step "released versions are frozen vs $BASE" frozen_vs_base rm -rf "$tmp" [ "$fail" -eq 0 ] && echo "ALL CHECKS PASSED" || echo "CHECKS FAILED" Run on the Exercise 1 change (subtitle added to version 2), with the new ci.sh: PASS tsp format --check PASS tsp compile --warn-as-error PASS generated OpenAPI is committed PASS no breaking change vs main FAIL released versions are frozen vs main api/openapi/openapi.2.0.yaml changed, but it is a released version api/openapi/openapi.3.0.yaml changed, but it is a released version CHECKS FAILED It FAILS (exit 1) on the new step: openapi.2.0.yaml and openapi.3.0.yaml changed. Run on a branch that adds a version 4 with an optional 'edition' (the rest of ci.sh unchanged): PASS tsp format --check PASS tsp compile --warn-as-error PASS generated OpenAPI is committed PASS no breaking change vs main PASS released versions are frozen vs main ALL CHECKS PASSED The pull request's summary was: api/main.tsp | 4 ++ api/openapi/openapi.4.0.yaml | 128 +++++++++++++++++++++++++++++++++++++++++++ ci.sh | 13 +++++ 3 files changed, 145 insertions(+) It PASSES: only a new file, openapi.4.0.yaml, was added; every existing generated file is byte-identical. That holds because the project sets omit-unreachable-types (see the chapter): without it, the Versions enum would have gained a member in every old file and the new step would have failed. CHOICES AND LIMITS The rule compares the generated file with the base branch's file using git diff, so it is strict to the byte; it will fail on a harmless reformatting of the emitter's output after an upgrade. That is probably right for released contracts, and annoying on the day you upgrade the emitter (the upgrade would need its own pull request and an explicit exception). I did not test what happens when a file was deleted. WHY THIS WORKS AS AN ANSWER --------------------------- One extra step turns a policy ('published means frozen') into something that fails a pull request, and the two runs show both outcomes.