Merge fix/c3-client-versions: CLI-46 client versions bumped past the published train (0.2.1 / Java 0.2.2), pypi 0.2.0 broken-connect() note, check-client-versions.ps1 drift guard in CI
This commit is contained in:
@@ -23,7 +23,12 @@ jobs:
|
|||||||
# tests, the codegen/descriptor freshness guards, and the clients that build on Linux.
|
# tests, the codegen/descriptor freshness guards, and the clients that build on Linux.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
# fetch-depth: 0 (full history + tags): the CLI-46 client-version-freshness check below
|
||||||
|
# diffs each client's source against its last publish tag (e.g. clients/go/vX.Y.Z), which
|
||||||
|
# a shallow, tagless checkout cannot resolve.
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v4
|
||||||
with:
|
with:
|
||||||
@@ -77,6 +82,15 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: ./scripts/check-codegen.ps1
|
run: ./scripts/check-codegen.ps1
|
||||||
|
|
||||||
|
# CLI-46: catches a client whose source changed since its last publish
|
||||||
|
# tag while the version constant still reads that tag's already-shipped
|
||||||
|
# number — the drift the pack-clients.ps1/tag-go-module.ps1 collision
|
||||||
|
# guards only catch at actual publish time. Needs tag history, hence
|
||||||
|
# `fetch-depth: 0` on this job's checkout above.
|
||||||
|
- name: Client version freshness
|
||||||
|
shell: pwsh
|
||||||
|
run: ./scripts/check-client-versions.ps1
|
||||||
|
|
||||||
- name: Gateway fake-worker tests
|
- name: Gateway fake-worker tests
|
||||||
run: dotnet test src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj -c Release --no-build
|
run: dotnet test src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj -c Release --no-build
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ This plan is executed as **seven parallel batches**, each by one subagent workin
|
|||||||
|
|
||||||
| Batch | Branch | Findings | Model | Why this model | Status |
|
| Batch | Branch | Findings | Model | Why this model | Status |
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| A | `fix/c3-client-versions` | CLI-46 | sonnet | Mechanical five-client bump + README note + one script guard; recipe is fully specified | Not started |
|
| A | `fix/c3-client-versions` | CLI-46 | sonnet | Mechanical five-client bump + README note + one script guard; recipe is fully specified | Done |
|
||||||
| B | `fix/c3-glauth-doc` | TST-31 | sonnet | Reconcile a table against one TOML source of truth, re-derive one default, change an assert to a log | Done |
|
| B | `fix/c3-glauth-doc` | TST-31 | sonnet | Reconcile a table against one TOML source of truth, re-derive one default, change an assert to a log | Done |
|
||||||
| C | `fix/c3-tracker-hygiene` | GWC-31 + housekeeping (close GWC-16/IPC-08/IPC-31, refresh TST-06/TST-17, register WRK-31) | sonnet | Cross-file comment/ID sweep needs care about what each ID *means*, but no logic changes | Not started |
|
| C | `fix/c3-tracker-hygiene` | GWC-31 + housekeeping (close GWC-16/IPC-08/IPC-31, refresh TST-06/TST-17, register WRK-31) | sonnet | Cross-file comment/ID sweep needs care about what each ID *means*, but no logic changes | Not started |
|
||||||
| D | `fix/c3-dashboard-acl` | SEC-40, SEC-41, GWC-38 | opus | Authorization semantics, fail-closed decisions, claims re-resolution; each needs a regression test | Not started |
|
| D | `fix/c3-dashboard-acl` | SEC-40, SEC-41, GWC-38 | opus | Authorization semantics, fail-closed decisions, claims re-resolution; each needs a regression test | Not started |
|
||||||
@@ -66,7 +66,6 @@ This plan is executed as **seven parallel batches**, each by one subagent workin
|
|||||||
- WRK-32: one `pumpStep()` before capturing the `CurrentVersion` baseline in `MxAccessCommandExecutor.cs`; rewrite the executor doc comment (`:996-1003`) to state the recency-based contract; add the same to `docs/MxAccessWorkerInstanceDesign.md` write-completion section and the reply-field comment in `mxaccess_gateway.proto` (**comment-only proto edit → regenerate all generated artifacts in the same commit**, per the repo rule; if regen tooling is unavailable on macOS, skip the proto comment and say so).
|
- WRK-32: one `pumpStep()` before capturing the `CurrentVersion` baseline in `MxAccessCommandExecutor.cs`; rewrite the executor doc comment (`:996-1003`) to state the recency-based contract; add the same to `docs/MxAccessWorkerInstanceDesign.md` write-completion section and the reply-field comment in `mxaccess_gateway.proto` (**comment-only proto edit → regenerate all generated artifacts in the same commit**, per the repo rule; if regen tooling is unavailable on macOS, skip the proto comment and say so).
|
||||||
- WRK-34 if trivial: `CreateFault(StaHung, "Watchdog", …)` with the correlation id moved into the message.
|
- WRK-34 if trivial: `CreateFault(StaHung, "Watchdog", …)` with the correlation id moved into the message.
|
||||||
- Cannot build on macOS: keep diff minimal, net48-safe (no init-only/positional records). Verified on windev afterwards.
|
- Cannot build on macOS: keep diff minimal, net48-safe (no init-only/positional records). Verified on windev afterwards.
|
||||||
- **Done-pending-windev** (`93edb1b` WRK-33, `20a3eef` WRK-32, `3679e59` WRK-34) on `fix/c3-worker-residuals`. WRK-32 applied `CaptureCompletionBaseline` (pump-then-snapshot) uniformly to all four write call sites (Write/Write2/WriteSecured/WriteSecured2), not just the two the finding cited as evidence, since the correlation gap is identical at all four. **Deferred**: the `mxaccess_gateway.proto` reply-field comment was not edited — a comment-only proto change still requires regenerating every generated artifact (C#, Go, Python, Java, Rust, protoset) per the repo rule, and this macOS box cannot verify all five client generator pins for a Low-severity doc-only change; the contract is documented in `docs/MxAccessWorkerInstanceDesign.md` instead. `NonWindows.slnx` builds clean (0 warnings/errors); worker/Worker.Tests unverified until windev runs `MxAccessCommandExecutorTests`/`WorkerPipeSessionTests`.
|
|
||||||
|
|
||||||
### G — GWC-32, GWC-34, GWC-37 (opus)
|
### G — GWC-32, GWC-34, GWC-37 (opus)
|
||||||
- GWC-32: `DisposeAsync` transitions `_state` to `Closed` under `_syncRoot` before completing channels (or `if (_disposed || IsTerminalState()) return;` in `StageWorkerEvent`); update the XML doc to name all three completion paths. Test: dispose a Ready client mid-dispatch → no `ProtocolViolation` fault/metric.
|
- GWC-32: `DisposeAsync` transitions `_state` to `Closed` under `_syncRoot` before completing channels (or `if (_disposed || IsTerminalState()) return;` in `StageWorkerEvent`); update the XML doc to name all three completion paths. Test: dispose a Ready client mid-dispatch → no `ProtocolViolation` fault/metric.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<PackageId>ZB.MOM.WW.MxGateway.Client</PackageId>
|
<PackageId>ZB.MOM.WW.MxGateway.Client</PackageId>
|
||||||
<Version>0.2.0</Version>
|
<Version>0.2.1</Version>
|
||||||
<Description>.NET 10 gRPC client for the MxAccessGateway service. Provides typed wrappers, retry, and a lazy-browse walker over the Galaxy Repository hierarchy.</Description>
|
<Description>.NET 10 gRPC client for the MxAccessGateway service. Provides typed wrappers, retry, and a lazy-browse walker over the Galaxy Repository hierarchy.</Description>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<!-- Only the shipped library generates XML docs (matching src/Contracts). The Cli and
|
<!-- Only the shipped library generates XML docs (matching src/Contracts). The Cli and
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package mxgateway
|
|||||||
const (
|
const (
|
||||||
// ClientVersion is the released semantic version of this Go client module.
|
// ClientVersion is the released semantic version of this Go client module.
|
||||||
// Keep it in sync with the module tag applied by scripts/tag-go-module.ps1.
|
// Keep it in sync with the module tag applied by scripts/tag-go-module.ps1.
|
||||||
ClientVersion = "0.2.0"
|
ClientVersion = "0.2.1"
|
||||||
|
|
||||||
// GatewayProtocolVersion matches GatewayContractInfo.GatewayProtocolVersion
|
// GatewayProtocolVersion matches GatewayContractInfo.GatewayProtocolVersion
|
||||||
// in the shared .NET contracts.
|
// in the shared .NET contracts.
|
||||||
|
|||||||
@@ -483,7 +483,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.zb.mom.ww.mxgateway:zb-mom-ww-mxgateway-client:0.2.1'
|
implementation 'com.zb.mom.ww.mxgateway:zb-mom-ww-mxgateway-client:0.2.2'
|
||||||
}
|
}
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,14 @@ subprojects {
|
|||||||
// 0.2.0 was already published to the Gitea Maven feed on 2026-06-26,
|
// 0.2.0 was already published to the Gitea Maven feed on 2026-06-26,
|
||||||
// before the CLI-37/38/40/41 conformance fixes changed the client's
|
// before the CLI-37/38/40/41 conformance fixes changed the client's
|
||||||
// observable behavior (status.category-based validation, hresult < 0,
|
// observable behavior (status.category-based validation, hresult < 0,
|
||||||
// exact-secret redaction, typed malformed-reply errors). Bump to 0.2.1
|
// exact-secret redaction, typed malformed-reply errors). Bumped to 0.2.1
|
||||||
// so the published coordinate matches the conformant behavior the other
|
// so the published coordinate matched the conformant behavior the other
|
||||||
// four clients ship at 0.2.0 for the first time. See CLI-39 and the
|
// four clients shipped at 0.2.0 for the first time. See CLI-39 and the
|
||||||
// "Versioning" section of docs/ClientPackaging.md.
|
// "Versioning" section of docs/ClientPackaging.md.
|
||||||
version = '0.2.1'
|
// 0.2.1 was itself published (Maven, 2026-08-07); this is the next
|
||||||
|
// release-train bump (CLI-46), one ahead of the other four clients'
|
||||||
|
// 0.2.1 because the Java coordinate has always trailed by one number.
|
||||||
|
version = '0.2.2'
|
||||||
|
|
||||||
pluginManager.withPlugin('java') {
|
pluginManager.withPlugin('java') {
|
||||||
java {
|
java {
|
||||||
|
|||||||
+2
-2
@@ -60,7 +60,7 @@ final class MxGatewayCliTests {
|
|||||||
|
|
||||||
assertEquals(0, run.exitCode());
|
assertEquals(0, run.exitCode());
|
||||||
assertEquals("", run.errors());
|
assertEquals("", run.errors());
|
||||||
assertTrue(run.output().contains("mxgateway-java 0.2.1"));
|
assertTrue(run.output().contains("mxgateway-java 0.2.2"));
|
||||||
assertTrue(run.output().contains("gatewayProtocolVersion=3"));
|
assertTrue(run.output().contains("gatewayProtocolVersion=3"));
|
||||||
assertTrue(run.output().contains("workerProtocolVersion=1"));
|
assertTrue(run.output().contains("workerProtocolVersion=1"));
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ final class MxGatewayCliTests {
|
|||||||
CliRun run = execute(new FakeClientFactory(), "version", "--json");
|
CliRun run = execute(new FakeClientFactory(), "version", "--json");
|
||||||
|
|
||||||
assertEquals(0, run.exitCode());
|
assertEquals(0, run.exitCode());
|
||||||
assertTrue(run.output().contains("\"clientVersion\":\"0.2.1\""));
|
assertTrue(run.output().contains("\"clientVersion\":\"0.2.2\""));
|
||||||
assertTrue(run.output().contains("\"gatewayProtocolVersion\":3"));
|
assertTrue(run.output().contains("\"gatewayProtocolVersion\":3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ package com.zb.mom.ww.mxgateway.client;
|
|||||||
public final class MxGatewayClientVersion {
|
public final class MxGatewayClientVersion {
|
||||||
private static final int GATEWAY_PROTOCOL_VERSION = 3;
|
private static final int GATEWAY_PROTOCOL_VERSION = 3;
|
||||||
private static final int WORKER_PROTOCOL_VERSION = 1;
|
private static final int WORKER_PROTOCOL_VERSION = 1;
|
||||||
private static final String CLIENT_VERSION = "0.2.1";
|
private static final String CLIENT_VERSION = "0.2.2";
|
||||||
|
|
||||||
private MxGatewayClientVersion() {
|
private MxGatewayClientVersion() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -484,6 +484,15 @@ mxgw-py smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGAT
|
|||||||
|
|
||||||
## Installing from the Gitea PyPI Feed
|
## Installing from the Gitea PyPI Feed
|
||||||
|
|
||||||
|
**The published `0.2.0` artifact cannot connect.** Its `connect()` built the
|
||||||
|
`grpc.aio` channel inside `asyncio.to_thread`, and a `grpc.aio` channel binds
|
||||||
|
to the event loop current on the *constructing* thread — so every
|
||||||
|
non-stub connection raised `RuntimeError: There is no current event loop in
|
||||||
|
thread`. Fixed in-tree by commit `a8f86b5` (`resolve_channel_security` now
|
||||||
|
runs off-loop, `create_channel` runs on-loop). The fix is superseded by
|
||||||
|
`0.2.1`; do not install `0.2.0` from the feed below — use `0.2.1` or later,
|
||||||
|
or install from source at a commit at or after `a8f86b5`.
|
||||||
|
|
||||||
The client publishes to the internal Gitea PyPI feed:
|
The client publishes to the internal Gitea PyPI feed:
|
||||||
|
|
||||||
````bash
|
````bash
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "zb-mom-ww-mxaccess-gateway-client"
|
name = "zb-mom-ww-mxaccess-gateway-client"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
description = "Async Python client for MXAccess Gateway."
|
description = "Async Python client for MXAccess Gateway."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
"""Package version information."""
|
"""Package version information."""
|
||||||
|
|
||||||
__version__ = "0.2.0"
|
__version__ = "0.2.1"
|
||||||
|
|||||||
Generated
+2
-2
@@ -590,7 +590,7 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mxgw-cli"
|
name = "mxgw-cli"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -1490,7 +1490,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zb-mom-ww-mxgateway-client"
|
name = "zb-mom-ww-mxgateway-client"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "zb-mom-ww-mxgateway-client"
|
name = "zb-mom-ww-mxgateway-client"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Joseph Doherty"]
|
authors = ["Joseph Doherty"]
|
||||||
description = "Async Rust client for the MxAccessGateway gRPC service, including a lazy-browse walker over the Galaxy Repository hierarchy."
|
description = "Async Rust client for the MxAccessGateway gRPC service, including a lazy-browse walker over the Galaxy Repository hierarchy."
|
||||||
@@ -25,7 +25,7 @@ resolver = "2"
|
|||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
authors = ["Joseph Doherty"]
|
authors = ["Joseph Doherty"]
|
||||||
license = "Proprietary"
|
license = "Proprietary"
|
||||||
repository = "https://gitea.dohertylan.com/dohertj2/mxaccessgw"
|
repository = "https://gitea.dohertylan.com/dohertj2/mxaccessgw"
|
||||||
|
|||||||
@@ -72,6 +72,23 @@ carries the equivalent guard for the Go module: it refuses to create a
|
|||||||
`ClientVersion` already equals `X.Y.Z` (CLI-21/CLI-39), so a forgotten
|
`ClientVersion` already equals `X.Y.Z` (CLI-21/CLI-39), so a forgotten
|
||||||
version bump fails the tag instead of shipping a mismatched module.
|
version bump fails the tag instead of shipping a mismatched module.
|
||||||
|
|
||||||
|
Both of those guards only fire when someone actually runs a publish.
|
||||||
|
CLI-46 found the same hazard recurring silently *between* publishes: a
|
||||||
|
real bug fix (the pypi 0.2.0 `connect()` fix above, commit `a8f86b5`) plus
|
||||||
|
later wire-surface changes shipped into the tree while every version
|
||||||
|
constant still read the already-published number, and nothing recorded
|
||||||
|
that the registry artifact and the in-repo source had diverged.
|
||||||
|
`scripts/check-client-versions.ps1` is the PR-time half of that guard —
|
||||||
|
wired into CI (`.gitea/workflows/ci.yml`, the `portable` job, next to the
|
||||||
|
codegen freshness checks): for each client it finds the latest publish tag
|
||||||
|
(`clients/go/vX.Y.Z` for Go — the only client with a git tag at all; .NET,
|
||||||
|
Python, Rust, and Java publish straight to a package registry via
|
||||||
|
`pack-clients.ps1 -Publish` with no corresponding git tag, so the check
|
||||||
|
reports "no tags found" and passes for those four today) and fails if
|
||||||
|
`clients/<lang>` differs from that tag while the version constant still
|
||||||
|
equals the tag's version. A client with no publish tag is not a
|
||||||
|
violation — there is no "since the last publish" window to check yet.
|
||||||
|
|
||||||
As of 2026-08-07 (CLI-39) all five clients — plus `ZB.MOM.WW.MxGateway.Contracts`,
|
As of 2026-08-07 (CLI-39) all five clients — plus `ZB.MOM.WW.MxGateway.Contracts`,
|
||||||
which releases in lockstep with the .NET Client — moved to **0.2.0**,
|
which releases in lockstep with the .NET Client — moved to **0.2.0**,
|
||||||
converging on one number after four of the five had drifted onto the
|
converging on one number after four of the five had drifted onto the
|
||||||
@@ -111,6 +128,26 @@ confirmed present through the Gitea package API after the push, and
|
|||||||
`go list -m` resolves the Go tag. These are the numbers a future release
|
`go list -m` resolves the Go tag. These are the numbers a future release
|
||||||
bumps off.
|
bumps off.
|
||||||
|
|
||||||
|
**The published pypi `0.2.0` artifact has a broken `connect()` (CLI-46).**
|
||||||
|
Its `connect()` built the `grpc.aio` channel inside `asyncio.to_thread`, and
|
||||||
|
a `grpc.aio` channel binds to the event loop current on the *constructing*
|
||||||
|
thread, so every non-stub connection raised `RuntimeError: There is no
|
||||||
|
current event loop in thread` — TST-24's first real-wire run caught it.
|
||||||
|
Fixed in-tree by commit `a8f86b5` (2026-08-10): `resolve_channel_security`
|
||||||
|
now runs off-loop, `create_channel` runs on-loop. That fix, plus later
|
||||||
|
wire-surface additions (`d9ea8a8`, `c748361`), shipped after the 2026-08-07
|
||||||
|
publish while the version constants still read the already-published
|
||||||
|
0.2.0/0.2.1 numbers above — see `clients/python/README.md` for the
|
||||||
|
user-facing warning. **0.2.0 is superseded by 0.2.1; do not install it.**
|
||||||
|
|
||||||
|
As of this remediation (CLI-46), the in-source version constants for the
|
||||||
|
.NET, Go, Python, and Rust clients (plus Contracts) read **0.2.1**, and the
|
||||||
|
Java client reads **0.2.2**. These are not yet published — the numbers
|
||||||
|
above (0.2.0 four clients + Contracts, 0.2.1 Java) remain the live registry
|
||||||
|
state until the next `pack-clients.ps1` / `tag-go-module.ps1` run. Once that
|
||||||
|
publish lands, update this section with the new coordinates and commit(s),
|
||||||
|
the same way the 2026-08-07 record above was written after the fact.
|
||||||
|
|
||||||
## .NET
|
## .NET
|
||||||
|
|
||||||
The .NET client uses .NET 10 and references
|
The .NET client uses .NET 10 and references
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Fails a PR that changes a client's source without bumping its version
|
||||||
|
past the last thing actually published (CLI-46).
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
CLI-39 (2026-07-12 cycle) added publish-time guards that refuse to
|
||||||
|
re-publish an already-published name+version
|
||||||
|
(scripts/pack-clients.ps1's Assert-GiteaPackageNotPublished,
|
||||||
|
scripts/tag-go-module.ps1's ClientVersion-vs-tag check). Those guards
|
||||||
|
only fire when someone actually runs a publish. CLI-46 (2026-08-19
|
||||||
|
cycle) found the same hazard recurring silently in between publishes:
|
||||||
|
library source (including a real bug fix, commit a8f86b5) shipped
|
||||||
|
while the version constant still equalled the already-published
|
||||||
|
number, so nothing in the tree recorded that the published artifact
|
||||||
|
and the in-repo source had diverged. This script is the PR-time half
|
||||||
|
of that guard: catch the drift as soon as it happens, not at the next
|
||||||
|
publish.
|
||||||
|
|
||||||
|
For each client this repo ships, the script:
|
||||||
|
1. Finds the latest "publish tag" for that client, per the tag
|
||||||
|
naming convention its own publish tooling actually uses.
|
||||||
|
2. If no such tag exists, says so and passes (nothing to compare
|
||||||
|
against yet — this is expected for every client except Go; see
|
||||||
|
below).
|
||||||
|
3. If a tag exists, reads the version recorded in-source at that tag
|
||||||
|
and the version recorded in-source now. If `clients/<lang>` has
|
||||||
|
changed since the tag AND the version constant is unchanged, that
|
||||||
|
is exactly the CLI-46 pattern: source moved, the published
|
||||||
|
coordinate did not, and nothing says so. Fail.
|
||||||
|
|
||||||
|
Tag conventions, as implemented today (verified against
|
||||||
|
scripts/pack-clients.ps1, scripts/tag-go-module.ps1, and `git tag -l`):
|
||||||
|
- Go is the only client with a git tag at all: `tag-go-module.ps1`
|
||||||
|
creates `clients/go/vX.Y.Z` (CLI-21/CLI-39 already keep this tag's
|
||||||
|
version in lockstep with clients/go/mxgateway/version.go's
|
||||||
|
ClientVersion at *tag time*; this script is what keeps it in
|
||||||
|
lockstep *between* tags).
|
||||||
|
- .NET, Python, Rust, and Java publish straight to a package
|
||||||
|
registry via `pack-clients.ps1 -Publish` (NuGet/PyPI/cargo/Maven
|
||||||
|
API pushes) with no corresponding git tag of any kind — there is
|
||||||
|
nothing in this repo's history to diff against for those four.
|
||||||
|
The script looks for a same-shaped tag anyway
|
||||||
|
(`clients/<lang>/v*`) so it stays correct if that ever changes,
|
||||||
|
but today it will report "no tags found" and pass for all four.
|
||||||
|
|
||||||
|
A client with no tag is not a violation of anything — it means no
|
||||||
|
release train has ever tagged that client, so there is no "since the
|
||||||
|
last publish" window to check. The check exists to catch drift once a
|
||||||
|
baseline exists, not to demand one.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
pwsh scripts/check-client-versions.ps1
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
param()
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
|
||||||
|
Push-Location $repoRoot
|
||||||
|
try {
|
||||||
|
# One entry per official client. `TagGlob` is the tag naming pattern that
|
||||||
|
# client's own publish tooling uses (see doc comment above); `VersionFile`
|
||||||
|
# + `VersionPattern` locate the single version-of-record constant
|
||||||
|
# docs/ClientPackaging.md's "Versioning" section names as authoritative
|
||||||
|
# for that client; `SourceDir` is the diff scope (matches the literal
|
||||||
|
# `clients/<lang>` scope described in the CLI-46 remediation task).
|
||||||
|
$clients = @(
|
||||||
|
[ordered]@{
|
||||||
|
Name = 'dotnet'
|
||||||
|
SourceDir = 'clients/dotnet'
|
||||||
|
TagGlob = 'clients/dotnet/v*'
|
||||||
|
VersionFile = 'clients/dotnet/ZB.MOM.WW.MxGateway.Client/ZB.MOM.WW.MxGateway.Client.csproj'
|
||||||
|
VersionPattern = '<Version>([^<]+)</Version>'
|
||||||
|
},
|
||||||
|
[ordered]@{
|
||||||
|
Name = 'go'
|
||||||
|
SourceDir = 'clients/go'
|
||||||
|
TagGlob = 'clients/go/v*'
|
||||||
|
VersionFile = 'clients/go/mxgateway/version.go'
|
||||||
|
VersionPattern = 'ClientVersion\s*=\s*"([^"]+)"'
|
||||||
|
},
|
||||||
|
[ordered]@{
|
||||||
|
Name = 'python'
|
||||||
|
SourceDir = 'clients/python'
|
||||||
|
TagGlob = 'clients/python/v*'
|
||||||
|
VersionFile = 'clients/python/src/zb_mom_ww_mxgateway/version.py'
|
||||||
|
VersionPattern = '__version__\s*=\s*"([^"]+)"'
|
||||||
|
},
|
||||||
|
[ordered]@{
|
||||||
|
Name = 'rust'
|
||||||
|
SourceDir = 'clients/rust'
|
||||||
|
TagGlob = 'clients/rust/v*'
|
||||||
|
VersionFile = 'clients/rust/Cargo.toml'
|
||||||
|
VersionPattern = 'version\s*=\s*"([^"]+)"'
|
||||||
|
},
|
||||||
|
[ordered]@{
|
||||||
|
Name = 'java'
|
||||||
|
SourceDir = 'clients/java'
|
||||||
|
TagGlob = 'clients/java/v*'
|
||||||
|
VersionFile = 'clients/java/build.gradle'
|
||||||
|
VersionPattern = "version\s*=\s*'([^']+)'"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Get-LatestTag {
|
||||||
|
param([string]$Glob)
|
||||||
|
|
||||||
|
# --sort=-v:refname orders by version-aware descending semver, so the
|
||||||
|
# first line is the newest tag matching the glob. Empty result means
|
||||||
|
# no tag of this shape has ever been created for this client.
|
||||||
|
$tags = git tag -l --sort='-v:refname' $Glob
|
||||||
|
if (-not $tags) {
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
return @($tags -split "`n")[0].Trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-VersionFromFile {
|
||||||
|
param([string]$Path, [string]$Pattern)
|
||||||
|
|
||||||
|
if (-not (Test-Path $Path)) {
|
||||||
|
throw "Version file not found: $Path"
|
||||||
|
}
|
||||||
|
$content = Get-Content -Path $Path -Raw
|
||||||
|
$match = [regex]::Match($content, $Pattern)
|
||||||
|
if (-not $match.Success) {
|
||||||
|
throw "Could not find a version matching /$Pattern/ in $Path"
|
||||||
|
}
|
||||||
|
return $match.Groups[1].Value
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-VersionFromFileAtRevision {
|
||||||
|
param([string]$Revision, [string]$Path, [string]$Pattern)
|
||||||
|
|
||||||
|
$content = git show "${Revision}:${Path}" 2>$null
|
||||||
|
if ($LASTEXITCODE -ne 0 -or -not $content) {
|
||||||
|
# The version file did not exist (or was at a different path) at
|
||||||
|
# that tag. Treat as "unknown" rather than failing the whole
|
||||||
|
# check — the drift comparison below only needs "does the
|
||||||
|
# current version still equal the tag's version", and an
|
||||||
|
# unresolvable historical version can never equal the current
|
||||||
|
# one, so this degrades to "assume it changed" (safe: it will
|
||||||
|
# not mask a real drift).
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
$match = [regex]::Match(($content -join "`n"), $Pattern)
|
||||||
|
if (-not $match.Success) {
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
return $match.Groups[1].Value
|
||||||
|
}
|
||||||
|
|
||||||
|
$failures = New-Object System.Collections.Generic.List[string]
|
||||||
|
|
||||||
|
foreach ($client in $clients) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Host "== $($client.Name) ==" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$tag = Get-LatestTag -Glob $client.TagGlob
|
||||||
|
if (-not $tag) {
|
||||||
|
Write-Host " No publish tags matching '$($client.TagGlob)' — nothing to compare against yet. Pass." -ForegroundColor Yellow
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host " Latest publish tag: $tag"
|
||||||
|
|
||||||
|
$currentVersion = Get-VersionFromFile -Path $client.VersionFile -Pattern $client.VersionPattern
|
||||||
|
Write-Host " Current version constant ($($client.VersionFile)): $currentVersion"
|
||||||
|
|
||||||
|
# Any diff at all in the client's source tree since its last publish
|
||||||
|
# tag — matches the CLI-46 remediation task's literal check.
|
||||||
|
git diff --quiet $tag -- $client.SourceDir
|
||||||
|
$sourceChanged = ($LASTEXITCODE -ne 0)
|
||||||
|
|
||||||
|
if (-not $sourceChanged) {
|
||||||
|
Write-Host " No changes under $($client.SourceDir) since $tag. Pass." -ForegroundColor Green
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$tagVersion = Get-VersionFromFileAtRevision -Revision $tag -Path $client.VersionFile -Pattern $client.VersionPattern
|
||||||
|
Write-Host " Source under $($client.SourceDir) changed since $tag (tag's version constant: $(if ($tagVersion) { $tagVersion } else { '<unresolvable>' }))."
|
||||||
|
|
||||||
|
if ($currentVersion -eq $tagVersion) {
|
||||||
|
$failures.Add("$($client.Name): source changed since publish tag '$tag' but $($client.VersionFile) still reads '$currentVersion' (the version at '$tag'). Bump the version before another publish (CLI-46).")
|
||||||
|
Write-Host " FAIL: version constant unchanged since the publish tag." -ForegroundColor Red
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Host " Version constant already bumped ('$tagVersion' -> '$currentVersion') since $tag. Pass." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
if ($failures.Count -gt 0) {
|
||||||
|
Write-Host 'Client version freshness check FAILED:' -ForegroundColor Red
|
||||||
|
foreach ($failure in $failures) {
|
||||||
|
Write-Host " - $failure" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host 'Client version freshness check passed.' -ForegroundColor Green
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<PackageId>ZB.MOM.WW.MxGateway.Contracts</PackageId>
|
<PackageId>ZB.MOM.WW.MxGateway.Contracts</PackageId>
|
||||||
<Version>0.2.0</Version>
|
<Version>0.2.1</Version>
|
||||||
<Authors>Joseph Doherty</Authors>
|
<Authors>Joseph Doherty</Authors>
|
||||||
<Company>ZB MOM WW</Company>
|
<Company>ZB MOM WW</Company>
|
||||||
<Copyright>Copyright (c) ZB MOM WW. All rights reserved.</Copyright>
|
<Copyright>Copyright (c) ZB MOM WW. All rights reserved.</Copyright>
|
||||||
|
|||||||
Reference in New Issue
Block a user