diff --git a/.changes/unreleased/Changed-20261004-020848.yaml b/.changes/unreleased/Changed-20261004-020848.yaml new file mode 100644 index 0000000..59d4d14 --- /dev/null +++ b/.changes/unreleased/Changed-20261004-020848.yaml @@ -0,0 +1,3 @@ +kind: Changed +body: Implement spec 0.3.0. It adds the postcode field and its messages, and results stay the same. +time: 2026-10-04T02:08:48.403352+01:00 diff --git a/AGENTS.md b/AGENTS.md index bbb5e2e..df59905 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,7 +11,7 @@ This repo is part of Gatepost, unofficial open-source developer tools for Nigeri ## Rules that no config file shows -- If the task needs a public symbol that the Interface section of `spec/grammar.md` does not list, add nothing, not even an internal helper. Stop, and say that a spec change must come first (API-1, CS-8). +- If the task needs a public symbol that no Interface section of the spec lists, in `spec/grammar.md`, `spec/client.md` or `spec/field.md`, add nothing, not even an internal helper. Stop, and say that a spec change must come first (API-1, CS-8). - Call only the documented NIPOST gateway endpoints. - Commit only NIPOST's published test codes and synthetic values, such as `FC-01-Z99-ZZ-01`. - Sign off each commit with `git commit -s`. The person who opens the pull request is the only author. Leave out co-author lines for AI tools. diff --git a/CONTEXT.md b/CONTEXT.md index fcc8182..4b82f5f 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -169,6 +169,18 @@ _Avoid_: input, widget, picker To show the user what a postcode points to before the app stores it. _Avoid_: verify, validate (for this step) +**Check status**: +What the WooCommerce plugin learned about the postcode of an order: `valid`, `invalid`, `unchecked` or `error`. The order meta `_gatepost_postcode_check` holds it. +_Avoid_: verification status, validation result + +**Form value**: +The text that a field gives its form when the form is sent: the canonical form of a postcode, the 6 digits of a legacy postcode, or the text without white space at the start and the end. +_Avoid_: submitted value, output + +**Message catalogue**: +A data file that holds the text of a UI in one language, one message for each key. +_Avoid_: strings file, translations, locale file + **Tell**: A habit that makes code look machine-written, such as very long lines. _Avoid_: smell (smells are design problems), anti-pattern diff --git a/README.md b/README.md index f0a6d52..ae0d3ca 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ The example uses `FC-01-Z99-ZZ-01`, a synthetic postcode, and shows the answer o |---|---| | PHP | 8.1 or later | | HTTP, for the client | a PSR-18 client and a PSR-17 request factory | -| Gatepost spec | 0.2.0 | +| Gatepost spec | 0.3.0 | ## Docs diff --git a/docs/api-dump.md b/docs/api-dump.md index ce4859c..6a99b8f 100644 --- a/docs/api-dump.md +++ b/docs/api-dump.md @@ -125,7 +125,7 @@ final class ParseResult final class Postcode { - public const SPEC_VERSION = '0.2.0'; + public const SPEC_VERSION = '0.3.0'; public readonly string $compact; public readonly string $canonical; public readonly string $display; diff --git a/docs/developing.md b/docs/developing.md index 62ababe..38694a4 100644 --- a/docs/developing.md +++ b/docs/developing.md @@ -15,6 +15,8 @@ composer check The BC check tool lists ext-intl as a requirement. Its check runs without that extension, so the install line ignores the requirement. The line also works on a machine that has ext-intl. +`composer api:check` compares the public API with the newest release tag, so run `git fetch --tags` first. It ignores one finding: the value of `Postcode::SPEC_VERSION` changes with each spec version by design, so that change is not a break of the API. + `composer run` lists the other scripts. `composer test` runs the tests without coverage. `composer mutation` runs the mutation tests, and it needs Xdebug. ## Contract scenarios diff --git a/scripts/check-api.sh b/scripts/check-api.sh index 7f0e4af..bf00b23 100755 --- a/scripts/check-api.sh +++ b/scripts/check-api.sh @@ -3,6 +3,10 @@ # SPDX-License-Identifier: Apache-2.0 # Compares the public API with the newest release tag (GIT-7, API-13). Before the first tag, # no released API exists, so the script says so and stops. +# +# The value of Postcode::SPEC_VERSION changes with each spec version by design. That change is +# not a break of the API, so the script drops that one finding. It keeps every other finding, +# and it fails on every other failure of the tool. set -euo pipefail # Git sorts v0.1.0-alpha.0 above v0.1.0 unless the config names the suffix. Without the option, @@ -12,4 +16,32 @@ if [ -z "$tag" ]; then echo "No release tag yet, so there is no released API to compare." exit 0 fi -exec tools/bc-check/vendor/bin/roave-backward-compatibility-check --from="$tag" + +spec_version_change='[BC] CHANGED: Value of constant ' +spec_version_change+='Gatepost\Postcode\Postcode::SPEC_VERSION changed' +status=0 +tool=tools/bc-check/vendor/bin/roave-backward-compatibility-check +output="$("$tool" --from="$tag" 2>&1)" || status=$? + +dropped=0 +breaks=0 +while IFS= read -r line; do + if [[ $line == "$spec_version_change"* ]]; then + dropped=1 + elif [[ $line =~ ^[0-9]+\ backwards-incompatible\ changes\ detected$ ]]; then + : # The tool counts the dropped finding, so the script prints its own count. + else + echo "$line" + if [[ $line == '[BC]'* ]]; then breaks=$((breaks + 1)); fi + fi +done <<<"$output" + +if [ "$breaks" -gt 0 ]; then + echo "$breaks backwards-incompatible changes detected" + exit 3 +fi +# Code 3 is the code of the tool for a break. Another code, or code 3 with no finding to +# explain it, is a failure of the tool itself. +if [ "$status" -ne 0 ] && { [ "$status" -ne 3 ] || [ "$dropped" -eq 0 ]; }; then + exit "$status" +fi diff --git a/spec b/spec index 359db01..db359cd 160000 --- a/spec +++ b/spec @@ -1 +1 @@ -Subproject commit 359db01aa97f2d5b183db431d14dbccb793af9f3 +Subproject commit db359cde28a7c5c549f1a3069f20078f19f05a58 diff --git a/src/Postcode.php b/src/Postcode.php index eb45f64..7e6453d 100644 --- a/src/Postcode.php +++ b/src/Postcode.php @@ -24,7 +24,7 @@ final class Postcode /** * The version of the Gatepost spec that this package implements. */ - public const SPEC_VERSION = '0.2.0'; + public const SPEC_VERSION = '0.3.0'; // In UTF-8, a code point takes at most 4 bytes. Longer input is over the limit without a // count, so a 10 MB string costs no more than a short one. diff --git a/tests/Unit/CheckApiTest.php b/tests/Unit/CheckApiTest.php index e9fc5c6..43b5894 100644 --- a/tests/Unit/CheckApiTest.php +++ b/tests/Unit/CheckApiTest.php @@ -16,7 +16,7 @@ /** * scripts/check-api.sh starts the BC check from the newest release tag. These tests run the * script in a temporary repo with the tags of one case. A stub takes the place of the BC check - * tool and prints the tag that the script gave it. + * tool. It prints the tag that the script gave it, or the output of the case. */ final class CheckApiTest extends TestCase { @@ -83,6 +83,59 @@ public function stopsBeforeTheFirstTag(): void ); } + /** + * @return array + */ + public static function toolOutputs(): array + { + $constant = '[BC] CHANGED: Value of constant Gatepost\\Postcode\\Postcode::SPEC_VERSION ' + . "changed from '0.2.0' to '0.3.0'"; + $method = '[BC] REMOVED: Method Gatepost\\Postcode\\Postcode::check() was removed'; + $summary = static fn(int $count): string + => "{$count} backwards-incompatible changes detected"; + + return [ + 'the constant alone' => [ + "{$constant}\n{$summary(1)}", + 3, + 0, + '', + ], + 'the constant and another break' => [ + "{$constant}\n{$method}\n{$summary(2)}", + 3, + 3, + "{$method}\n{$summary(1)}", + ], + 'another break alone' => [ + "{$method}\n{$summary(1)}", + 3, + 3, + "{$method}\n{$summary(1)}", + ], + 'a failure of the tool' => ['Could not read the tag', 1, 1, 'Could not read the tag'], + 'a break code with no finding' => ['Odd output', 3, 3, 'Odd output'], + ]; + } + + #[Test] + #[DataProvider('toolOutputs')] + public function dropsOnlyTheChangeOfTheSpecVersion( + string $toolOutput, + int $toolCode, + int $exitCode, + string $output, + ): void { + $this->startRepo('v0.1.0'); + $stub = "{$this->repo}/tools/bc-check/vendor/bin/roave-backward-compatibility-check"; + \file_put_contents($stub, "#!/bin/sh\ncat <<'END'\n{$toolOutput}\nEND\nexit {$toolCode}\n"); + + $run = $this->checkApi(); + + self::assertSame($exitCode, $run->exitCode, $run->output); + self::assertSame($output, $run->output); + } + // The repo starts in each test, so that tearDown() removes it even when a git command fails. private function startRepo(string ...$tags): void {