From d8529289cffae8ed6a191ac5a7ac9f061eb66343 Mon Sep 17 00:00:00 2001 From: zhangwenjian Date: Sat, 5 Sep 2026 10:01:23 +0800 Subject: [PATCH] =?UTF-8?q?fix=F0=9F=90=9B:=20fold=20the=20host's=20GetFil?= =?UTF-8?q?ename=20into=20the=20contract's?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The host kept its own copy of the version-naming rule, byte-identical to the one in contract/migration: slice the leading 13 characters, no check. Two copies of a convention that applications also have to follow is two things to keep in step, and the copies had already stopped matching - core now rejects a name that carries no timestamp, and this one still accepted "add_orders.go" and registered a migration under that string as its version, which nothing would ever match and nothing would report. Delegate instead, so there is one implementation of the rule and an app's migration and a host migration derive their version the same way. The test pins the reject case, not just the happy path: a re-divergence that only sliced would still pass the happy path. Claude-Session: https://claude.ai/code/session_01HPTAw8b8tAdFNFn8rKdPYx --- cmd/migrate/migration/init.go | 8 +++++--- cmd/migrate/migration/init_test.go | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/cmd/migrate/migration/init.go b/cmd/migrate/migration/init.go index 08e61160..669bec65 100644 --- a/cmd/migrate/migration/init.go +++ b/cmd/migrate/migration/init.go @@ -3,7 +3,6 @@ package migration import ( "fmt" "log" - "path/filepath" "sort" "strings" "sync" @@ -363,7 +362,10 @@ func (e *Migration) run(appCode string) { // from the empty app code, which selects the framework's own migrations. const allApps = "\x00all" +// GetFilename derives a migration's version from its file name. The rule +// lives in contract/migration, because an application registering through +// that package names its files by the same convention and must land on the +// same version string; a second copy here is a second thing to keep in step. func GetFilename(s string) string { - s = filepath.Base(s) - return s[:13] + return contractmigration.GetFilename(s) } diff --git a/cmd/migrate/migration/init_test.go b/cmd/migrate/migration/init_test.go index c4c8db6f..13d8c6a6 100644 --- a/cmd/migrate/migration/init_test.go +++ b/cmd/migrate/migration/init_test.go @@ -561,3 +561,22 @@ func TestMergedEntriesHostRegistrationWinsOnKeyCollision(t *testing.T) { t.Error("contract registration ran; host registration should have won the collision") } } + +// GetFilename must stay the same rule the contract package applies, since an +// application registering through contract/migration names its files by that +// convention and has to land on the same version string. Pinning the reject +// case is what catches a re-divergence: a local copy that only sliced would +// return "add_orders.go" here and register a migration under a key that never +// matches anything. +func TestGetFilenameDelegatesToTheContractRule(t *testing.T) { + if got := GetFilename("version/1786700001000_demo_menu.go"); got != "1786700001000" { + t.Fatalf("GetFilename = %q, want %q", got, "1786700001000") + } + + defer func() { + if recover() == nil { + t.Fatal("a file name carrying no version did not panic") + } + }() + GetFilename("version/add_orders.go") +}