diff --git a/.github/workflows/refresh-versions.yml b/.github/workflows/refresh-versions.yml index 43280636..dd4d9fcd 100644 --- a/.github/workflows/refresh-versions.yml +++ b/.github/workflows/refresh-versions.yml @@ -7,7 +7,7 @@ name: Refresh Splice versions # # Candidates are limited to semver tags newer than the highest already- # catalogued semver whose major has a DevKit adapter (0.5 / 0.6 / 0.7 / -# 0.8) — see internal/localnet/adapters.go. Historical gaps and +# 0.8 / 0.9) — see internal/localnet/adapters.go. Historical gaps and # unsupported majors are left for a deliberate maintainer add. # # The script refuses to overwrite existing entries, so a force-pushed @@ -41,7 +41,7 @@ jobs: # Adapter majors from internal/localnet/adapters.go — cataloguing # a tag whose major has no adapter makes Resolve succeed then # adapterFor fail at up time. - supported_majors="0.5 0.6 0.7 0.8" + supported_majors="0.5 0.6 0.7 0.8 0.9" catalogued=$(python3 -c "import json;print(' '.join(v['tag'] for v in json.load(open('internal/splice/versions.json'))['versions']))") echo "Catalogued: $catalogued" diff --git a/docs/versions.md b/docs/versions.md index 663983d8..8448ab02 100644 --- a/docs/versions.md +++ b/docs/versions.md @@ -116,7 +116,7 @@ A maintainer then: The weekly `.github/workflows/refresh-versions.yml` workflow discovers upstream semver tags that are newer than the highest already-catalogued semver and whose major has a DevKit adapter (`0.5` / `0.6` / `0.7` / -`0.8`), runs the same script with `GITHUB_TOKEN`, and opens a PR on +`0.8` / `0.9`), runs the same script with `GITHUB_TOKEN`, and opens a PR on `chore/refresh-splice-versions` for review. ## Two-layer resolution @@ -131,7 +131,7 @@ opt-in unlocks arbitrary upstream tags for prerelease testing. | 2 — Upstream | `--version --allow-uncurated` for any tag not in the catalogue | `api.github.com/repos/canton-network/splice/git/refs/tags/` | Computed on first extract, recorded for future runs | Requires explicit opt-in. Network on first call. Cached at `~/.canton-devkit/cache/resolved-versions.json`. | Layer 2 trades audit for flexibility: it lets a user spin up a -`0.9.0-alpha.1` LocalNet without waiting for a catalogue PR, but +`0.10.0-alpha.1` LocalNet without waiting for a catalogue PR, but DevKit can't promise the bits were tested against this release. Orchestrators print a one-line "Using uncurated Splice tag" warning on the layer-2 path so the user is never surprised. @@ -155,7 +155,7 @@ Three reasons the catalogue is curated: commit that happens to land in the repo. 3. **Adapter routing.** DevKit ships per-major adapters for each Splice - major version (`0.5`–`0.8` today). A new major version (e.g. `0.9.x`) + major version (`0.5`–`0.9` today). A new major version (e.g. `0.10.x`) needs a corresponding adapter before it can be added — the script leaves `major` blank for non-N.N.N tags so a maintainer notices. diff --git a/internal/localnet/adapters.go b/internal/localnet/adapters.go index cb250ee7..f31d2c91 100644 --- a/internal/localnet/adapters.go +++ b/internal/localnet/adapters.go @@ -8,6 +8,7 @@ import ( v06 "github.com/bitdynamics-ab/canton-devkit/internal/splice/v06" v07 "github.com/bitdynamics-ab/canton-devkit/internal/splice/v07" v08 "github.com/bitdynamics-ab/canton-devkit/internal/splice/v08" + v09 "github.com/bitdynamics-ab/canton-devkit/internal/splice/v09" ) // adapterFor returns the splice.Adapter implementation for a Version. @@ -27,6 +28,8 @@ func adapterFor(v splice.Version) (splice.Adapter, error) { return v07.New(), nil case "0.8": return v08.New(), nil + case "0.9": + return v09.New(), nil default: return nil, fmt.Errorf("no adapter registered for Splice major %q (tag %s) — this is a canton-devkit bug", v.Major, v.Tag) diff --git a/internal/localnet/adapters_test.go b/internal/localnet/adapters_test.go index 10132129..74c23947 100644 --- a/internal/localnet/adapters_test.go +++ b/internal/localnet/adapters_test.go @@ -29,7 +29,7 @@ func TestCoreServicesFor_Unknown(t *testing.T) { } } -// TestCoreServicesFor_VersionsAgree pins the hoist: 0.5.x through 0.8.x +// TestCoreServicesFor_VersionsAgree pins the hoist: 0.5.x through 0.9.x // adapters all delegate to splice.CoreServices, so any drift between // majors is impossible by construction. If a future major needs a // different list, this test should be updated together with that @@ -40,14 +40,18 @@ func TestCoreServicesFor_VersionsAgree(t *testing.T) { if !reflect.DeepEqual(v05, v06) { t.Fatalf("core services drifted between majors: 0.5.18=%v 0.6.4=%v", v05, v06) } - // 0.7.x and 0.8.x reuse the 0.6.x adapter (identical compose interface), + // 0.7.x–0.9.x reuse the 0.6.x adapter (identical compose interface), // so their core-services list must match too. v07 := CoreServicesFor("0.7.5") v08 := CoreServicesFor("0.8.1") + v09 := CoreServicesFor("0.9.0") if !reflect.DeepEqual(v06, v07) { t.Fatalf("core services drifted between majors: 0.6.4=%v 0.7.5=%v", v06, v07) } if !reflect.DeepEqual(v06, v08) { t.Fatalf("core services drifted between majors: 0.6.4=%v 0.8.1=%v", v06, v08) } + if !reflect.DeepEqual(v06, v09) { + t.Fatalf("core services drifted between majors: 0.6.4=%v 0.9.0=%v", v06, v09) + } } diff --git a/internal/splice/resolver_test.go b/internal/splice/resolver_test.go index 1b3e6125..aa7d2894 100644 --- a/internal/splice/resolver_test.go +++ b/internal/splice/resolver_test.go @@ -256,9 +256,9 @@ func TestResolveForOperation_ThreeTiers(t *testing.T) { // Tier 3: an uncurated tag NOT in the cache still yields a Version // carrying the Major inferred from the tag, so the adapter resolves. - v, err = ResolveForOperation("0.8.1-rc.2") - if err != nil || v.Major != "0.8" { - t.Errorf("inferred: got (%+v, %v), want Major 0.8", v, err) + v, err = ResolveForOperation("0.9.0-rc.1") + if err != nil || v.Major != "0.9" { + t.Errorf("inferred: got (%+v, %v), want Major 0.9", v, err) } // A genuinely unparseable tag is the only error case. diff --git a/internal/splice/v09/adapter.go b/internal/splice/v09/adapter.go new file mode 100644 index 00000000..7ec68284 --- /dev/null +++ b/internal/splice/v09/adapter.go @@ -0,0 +1,23 @@ +// Package v09 implements the Adapter contract for Splice LocalNet 0.9.x. +// +// The cluster/compose/localnet/ tree is unchanged from 0.6.x upstream — +// same compose files, env-file set, profile names, service/port map, and +// the env-var contract the adapter wires (IMAGE_TAG, IMAGE_REPO, +// PARTY_HINT, ALPHA_PROTOCOL_VERSION_ENV, …). So 0.9.x embeds the 0.6.x +// adapter and only relabels its major slot. +package v09 + +import ( + "github.com/bitdynamics-ab/canton-devkit/internal/splice" + v06 "github.com/bitdynamics-ab/canton-devkit/internal/splice/v06" +) + +// Adapter is the 0.6.x adapter under the 0.9 major label. +type Adapter struct{ *v06.Adapter } + +// New returns a fresh 0.9.x adapter. Stateless — safe to reuse. +func New() *Adapter { return &Adapter{v06.New()} } + +func (*Adapter) MajorVersion() string { return "0.9" } + +var _ splice.Adapter = (*Adapter)(nil) diff --git a/internal/splice/v09/adapter_test.go b/internal/splice/v09/adapter_test.go new file mode 100644 index 00000000..b6a6f137 --- /dev/null +++ b/internal/splice/v09/adapter_test.go @@ -0,0 +1,19 @@ +package v09 + +import "testing" + +func TestMajorVersion(t *testing.T) { + if got := New().MajorVersion(); got != "0.9" { + t.Fatalf("MajorVersion() = %q, want 0.9", got) + } +} + +func TestDelegatesComposeSurface(t *testing.T) { + a := New() + if len(a.ComposeFiles()) == 0 || len(a.EnvFiles()) == 0 || len(a.Profiles()) == 0 { + t.Fatal("0.9 adapter must expose the same compose surface as 0.6") + } + if !a.SupportsAlphaProtocol() { + t.Fatal("0.9 must keep alpha-protocol support from 0.6") + } +}