9b2abef4e1
Converges all five clients on one version after four had drifted onto the already-published 0.1.2/0.1.1 while their APIs kept changing underneath it: - Rust Cargo.toml [package] + [workspace.package] -> 0.2.0 (CLIENT_VERSION already derives from CARGO_PKG_VERSION, no separate edit). - Python pyproject.toml + version.py -> 0.2.0; new test asserts __version__ matches pyproject.toml (closes the CLI-26 residual drift mode). - Go mxgateway/version.go ClientVersion -> 0.2.0. - .NET ZB.MOM.WW.MxGateway.Client.csproj <Version> -> 0.2.0. - Java -> 0.2.1, not 0.2.0: the live Gitea Maven feed already had 0.2.0 published (2026-06-26), before the CLI-37/38/40/41 conformance fixes changed the client's observable behavior, so reusing 0.2.0 would label two different APIs identically. Recorded as an exception in docs/ClientPackaging.md's new Versioning section. Publish-pipeline guards: - scripts/tag-go-module.ps1 implements the CLI-21 guard: after semver validation it refuses to tag unless clients/go/mxgateway/version.go's ClientVersion already matches the requested tag version. - scripts/pack-clients.ps1 gains a Gitea package-registry collision guard wired into every per-language -Publish step; it aborts if the target name+version already exists rather than force-overwriting. Verified live against the real Gitea registry (credentials already present in this environment) — correctly refuses on every known-published artifact and passes on every unpublished target. Docs updated in the same commit: docs/ClientPackaging.md (new Versioning section), and the five client READMEs' stale 0.1.1/0.1.2 example versions. No .proto changes. No publish performed.
322 lines
12 KiB
Markdown
322 lines
12 KiB
Markdown
# Client Packaging
|
|
|
|
This document defines the clean-checkout commands for building, packaging, and
|
|
running the official MXAccess Gateway clients. Use the tool paths and versions
|
|
in [Toolchain Links](./ToolchainLinks.md) when a command is missing from
|
|
`PATH`.
|
|
|
|
## Shared Inputs
|
|
|
|
All clients generate bindings from the shared protobuf files under
|
|
`src/ZB.MOM.WW.MxGateway.Contracts/Protos`. Regenerate the published client descriptor
|
|
after changing either `.proto` file or `clients/proto/proto-inputs.json`:
|
|
|
|
```powershell
|
|
scripts/publish-client-proto-inputs.ps1
|
|
scripts/publish-client-proto-inputs.ps1 -Check
|
|
```
|
|
|
|
Generated protobuf and gRPC files are generator output. Do not edit them by
|
|
hand.
|
|
|
|
## Environment
|
|
|
|
The examples use these common variables:
|
|
|
|
```powershell
|
|
$env:MXGATEWAY_ENDPOINT = 'localhost:5000'
|
|
$env:MXGATEWAY_API_KEY = '<gateway-api-key>'
|
|
$env:MXGATEWAY_TEST_ITEM = 'TestObject.TestInt'
|
|
```
|
|
|
|
Use plaintext only for a local gateway. Use TLS when the gateway crosses a
|
|
machine boundary or uses a production certificate.
|
|
|
|
## Versioning
|
|
|
|
Every client's version lives in its own manifest: `clients/rust/Cargo.toml`
|
|
(`[package]` and `[workspace.package]`, both must match — `crates/mxgw-cli`
|
|
inherits via `version.workspace = true`), `clients/python/pyproject.toml`
|
|
(`[project].version`) and `clients/python/src/zb_mom_ww_mxgateway/version.py`
|
|
(`__version__`, must match `pyproject.toml`), `clients/go/mxgateway/version.go`
|
|
(`ClientVersion`), `clients/dotnet/ZB.MOM.WW.MxGateway.Client/ZB.MOM.WW.MxGateway.Client.csproj`
|
|
(`<Version>`), and `clients/java/build.gradle` (`subprojects { version = ... }`,
|
|
mirrored by the hand-maintained `MxGatewayClientVersion.CLIENT_VERSION`
|
|
constant — the two have drifted before and there is no build-time link
|
|
between them, so bump both together).
|
|
|
|
**Bump the version before every publish, never after.** A Gitea package feed
|
|
rejects re-uploading an existing name+version, and `scripts/pack-clients.ps1`
|
|
enforces this before it ever attempts a push: each per-language `-Publish`
|
|
step queries the Gitea package API
|
|
(`GET /api/v1/packages/dohertj2/{type}/{name}/{version}`) for the version
|
|
about to be published and aborts with a clear error if it already exists —
|
|
the script never force-overwrites a published artifact. `scripts/tag-go-module.ps1`
|
|
carries the equivalent guard for the Go module: it refuses to create a
|
|
`clients/go/vX.Y.Z` tag unless `clients/go/mxgateway/version.go`'s
|
|
`ClientVersion` already equals `X.Y.Z` (CLI-21/CLI-39), so a forgotten
|
|
version bump fails the tag instead of shipping a mismatched module.
|
|
|
|
As of 2026-08-07 (CLI-39) all five clients moved to **0.2.0**, converging on
|
|
one number after four of the five had drifted onto the *already-published*
|
|
0.1.2/0.1.1 while their public APIs kept changing underneath it (see
|
|
`archreview/2026-07-12/remediation/50-clients.md` CLI-39). Verified against
|
|
the live Gitea package API at that time: `nuget` had `ZB.MOM.WW.MxGateway.Client`
|
|
and `.Contracts` published through 0.1.2; `pypi` (`zb-mom-ww-mxaccess-gateway-client`)
|
|
and `cargo` (`zb-mom-ww-mxgateway-client`) had only reached 0.1.1 despite their
|
|
source pinning 0.1.2; **`maven`
|
|
(`com.zb.mom.ww.mxgateway:zb-mom-ww-mxgateway-client`) had already published
|
|
0.2.0 on 2026-06-26** — before the CLI-37/38/40/41 conformance fixes changed
|
|
the client's observable behavior (`category`-based status validation,
|
|
`hresult < 0`, exact-secret redaction, typed malformed-reply errors). Reusing
|
|
0.2.0 for the conformant Java build would have labeled two different APIs
|
|
with the same coordinate, so **Java is the one exception: it shipped as
|
|
0.2.1**, not 0.2.0. Operators publishing a future release must re-check the
|
|
target version against the live registry before assuming any of these
|
|
numbers are still unclaimed — the guards above do this automatically at
|
|
publish time, but a version bump in the source is still a manual step per
|
|
client.
|
|
|
|
## .NET
|
|
|
|
The .NET client uses .NET 10 and references
|
|
`src/ZB.MOM.WW.MxGateway.Contracts/ZB.MOM.WW.MxGateway.Contracts.csproj` for generated C# contract
|
|
types. `clients/dotnet/generated` remains reserved for client-local generator
|
|
output if the client later decouples from the contracts project.
|
|
|
|
Regenerate the generated C# contract types:
|
|
|
|
```powershell
|
|
dotnet build src/ZB.MOM.WW.MxGateway.Contracts/ZB.MOM.WW.MxGateway.Contracts.csproj
|
|
```
|
|
|
|
Build and test from the repository root:
|
|
|
|
```powershell
|
|
dotnet build clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx
|
|
dotnet test clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx --no-build
|
|
```
|
|
|
|
Create local package artifacts:
|
|
|
|
```powershell
|
|
$dotnetPackageOutput = Join-Path (Get-Location) 'artifacts/clients/dotnet'
|
|
dotnet pack clients/dotnet/ZB.MOM.WW.MxGateway.Client/ZB.MOM.WW.MxGateway.Client.csproj -c Release -p:PackageOutputPath="$dotnetPackageOutput"
|
|
dotnet publish clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/ZB.MOM.WW.MxGateway.Client.Cli.csproj -c Release -o artifacts/clients/dotnet/mxgw-dotnet
|
|
```
|
|
|
|
Run the CLI from source:
|
|
|
|
```powershell
|
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- version --json
|
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- smoke --endpoint "http://$env:MXGATEWAY_ENDPOINT" --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
|
dotnet run --project clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli -- smoke --endpoint "https://mxgateway.example.local:5001" --tls --ca-file C:\certs\mxgateway-ca.pem --server-name mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
|
```
|
|
|
|
## Go
|
|
|
|
The Go client is the module
|
|
`gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go`.
|
|
Generated Go files live under `clients/go/internal/generated`.
|
|
|
|
Regenerate the Go bindings:
|
|
|
|
```powershell
|
|
Push-Location clients/go
|
|
./generate-proto.ps1
|
|
Pop-Location
|
|
```
|
|
|
|
Build and test from `clients/go`:
|
|
|
|
```powershell
|
|
Push-Location clients/go
|
|
go test ./...
|
|
go build ./...
|
|
go vet ./...
|
|
Pop-Location
|
|
```
|
|
|
|
Create a local CLI executable:
|
|
|
|
```powershell
|
|
Push-Location clients/go
|
|
New-Item -ItemType Directory -Force ../../artifacts/clients/go | Out-Null
|
|
go build -o ../../artifacts/clients/go/mxgw-go.exe ./cmd/mxgw-go
|
|
Pop-Location
|
|
```
|
|
|
|
Run the CLI from source:
|
|
|
|
```powershell
|
|
Push-Location clients/go
|
|
go run ./cmd/mxgw-go version -json
|
|
go run ./cmd/mxgw-go smoke -endpoint $env:MXGATEWAY_ENDPOINT -plaintext -api-key-env MXGATEWAY_API_KEY -item $env:MXGATEWAY_TEST_ITEM -json
|
|
go run ./cmd/mxgw-go smoke -endpoint mxgateway.example.local:5001 -ca-cert C:\certs\mxgateway-ca.pem -server-name-override mxgateway.example.local -api-key-env MXGATEWAY_API_KEY -item $env:MXGATEWAY_TEST_ITEM -json
|
|
Pop-Location
|
|
```
|
|
|
|
## Rust
|
|
|
|
The Rust workspace builds the `mxgateway-client` library crate and the `mxgw`
|
|
CLI crate. `build.rs` generates `tonic` and `prost` modules into Cargo build
|
|
output on each build that needs updated protobuf output.
|
|
|
|
`build.rs` resolves its `.proto` inputs repo-path-first, then vendored: it
|
|
prefers the canonical protos under `src/ZB.MOM.WW.MxGateway.Contracts/Protos`
|
|
so an in-repo edit is live immediately, and falls back to the copies vendored
|
|
into `clients/rust/protos/` only when the canonical directory is absent — the
|
|
case for a published crate unpacked outside this repo. The vendored copies
|
|
are declared in `Cargo.toml`'s `include` list, so `cargo package`/`cargo
|
|
publish` ship them inside the `.crate`, making the crate buildable standalone
|
|
with no access to the rest of the mxaccessgw repo. Any Contracts proto change
|
|
must refresh `clients/rust/protos/` in the same commit; `scripts/check-codegen.ps1`
|
|
Check 3 byte-compares the vendored copies against the canonical protos and
|
|
fails on drift. Because the vendored protos make a standalone build possible,
|
|
`cargo package`/`cargo publish` run **with** verification (no `--no-verify`) —
|
|
a `cargo package` that cannot build from the vendored tree alone would mean
|
|
the vendored copies are stale, and verification is what catches that before
|
|
publish.
|
|
|
|
Regenerate and compile Rust bindings:
|
|
|
|
```powershell
|
|
Push-Location clients/rust
|
|
cargo check --workspace
|
|
Pop-Location
|
|
```
|
|
|
|
Build and test from `clients/rust`:
|
|
|
|
```powershell
|
|
Push-Location clients/rust
|
|
cargo fmt --all --check
|
|
cargo test --workspace
|
|
cargo check --workspace
|
|
Pop-Location
|
|
```
|
|
|
|
Create local release artifacts:
|
|
|
|
```powershell
|
|
Push-Location clients/rust
|
|
cargo build --workspace --release
|
|
cargo install --path crates/mxgw-cli --locked --force
|
|
Pop-Location
|
|
```
|
|
|
|
Run the CLI from source:
|
|
|
|
```powershell
|
|
Push-Location clients/rust
|
|
cargo run -p mxgw-cli -- version --json
|
|
cargo run -p mxgw-cli -- smoke --endpoint "http://127.0.0.1:5000" --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
|
cargo run -p mxgw-cli -- smoke --endpoint "https://mxgateway.example.local:5001" --tls --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
|
Pop-Location
|
|
```
|
|
|
|
## Python
|
|
|
|
The Python package is `zb-mom-ww-mxaccess-gateway-client`. Generated modules
|
|
live under `clients/python/src/zb_mom_ww_mxgateway/generated`.
|
|
|
|
Regenerate the Python bindings:
|
|
|
|
```powershell
|
|
Push-Location clients/python
|
|
./generate-proto.ps1
|
|
Pop-Location
|
|
```
|
|
|
|
Install, test, and build a wheel from `clients/python`:
|
|
|
|
```powershell
|
|
Push-Location clients/python
|
|
python -m pip install -e ".[dev]"
|
|
python -m pytest
|
|
python -m pip wheel . --no-deps --wheel-dir "$env:TEMP\mxgateway-python-wheel"
|
|
Pop-Location
|
|
```
|
|
|
|
Run the CLI from the editable install or with `python -m`:
|
|
|
|
```powershell
|
|
Push-Location clients/python
|
|
mxgw-py version --json
|
|
mxgw-py smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
|
mxgw-py smoke --endpoint mxgateway.example.local:5001 --tls --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
|
python -m zb_mom_ww_mxgateway_cli version --json
|
|
Pop-Location
|
|
```
|
|
|
|
## Java
|
|
|
|
The Java workspace uses Gradle, Java 17, `zb-mom-ww-mxgateway-client`, and
|
|
`zb-mom-ww-mxgateway-cli`. The Gradle protobuf plugin writes generated Java protobuf and
|
|
gRPC sources under `clients/java/src/main/generated`.
|
|
|
|
Regenerate Java bindings:
|
|
|
|
```powershell
|
|
Push-Location clients/java
|
|
gradle :zb-mom-ww-mxgateway-client:generateProto
|
|
Pop-Location
|
|
```
|
|
|
|
Build and test from `clients/java`:
|
|
|
|
```powershell
|
|
Push-Location clients/java
|
|
gradle test
|
|
Pop-Location
|
|
```
|
|
|
|
Create local library and CLI artifacts:
|
|
|
|
```powershell
|
|
Push-Location clients/java
|
|
gradle :zb-mom-ww-mxgateway-client:jar :zb-mom-ww-mxgateway-cli:installDist
|
|
Pop-Location
|
|
```
|
|
|
|
Run the CLI through Gradle:
|
|
|
|
```powershell
|
|
Push-Location clients/java
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="version --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
|
|
gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint mxgateway.example.local:5001 --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
|
|
Pop-Location
|
|
```
|
|
|
|
## Integration Tests
|
|
|
|
Client integration checks are opt-in because they need a live gateway and a
|
|
gateway host that can create MXAccess worker sessions. Set the common
|
|
environment before running a client smoke:
|
|
|
|
```powershell
|
|
$env:MXGATEWAY_INTEGRATION = '1'
|
|
$env:MXGATEWAY_ENDPOINT = 'localhost:5000'
|
|
$env:MXGATEWAY_API_KEY = '<gateway-api-key>'
|
|
$env:MXGATEWAY_TEST_ITEM = 'TestObject.TestInt'
|
|
$env:MXGATEWAY_TEST_CONTEXT = ''
|
|
$env:MXGATEWAY_TEST_WRITE_VALUE = '123'
|
|
```
|
|
|
|
Run the bounded `smoke` command for each client against the same item. The
|
|
smoke commands open a session, register a client name, add one item, advise it,
|
|
and close the session. The .NET and Python smoke commands also read a bounded
|
|
event stream; the Go, Rust, and Java smoke commands exercise the command path
|
|
and can be paired with their `stream-events` commands after a session is open.
|
|
|
|
Client-side cancellation or timeout stops waiting for the gateway response. It
|
|
does not abort an MXAccess COM call that is already executing on the worker STA.
|
|
|
|
## Related Documentation
|
|
|
|
- [Client Proto Generation](./ClientProtoGeneration.md)
|
|
- [Client Libraries Detailed Design](./ClientLibrariesDesign.md)
|
|
- [Client Behavior Fixtures](./ClientBehaviorFixtures.md)
|
|
- [Toolchain Links](./ToolchainLinks.md)
|