Skip to content

Migration: Props.Version and Props.Assets are concrete types

version.Version had one implementation, version.Info, and because Props.Version was the interface every reader guarded it with a nil check (17 in the framework alone) and the update command carried a hand-written double. Props.Version is now version.Info by value.

What changes for a tool

Nothing, if the tool sets the field the way the skeleton does:

v := version.NewInfo(versionString, commit, date)
p := &props.Props{Version: v, ...}

A tool that passed its own type implementing the old interface must construct a version.Info instead (version.NewInfo or a struct literal). A tool that compared p.Version == nil uses p.Version.IsZero(); an unset Info reports GetVersion() == "" and IsDevelopment() == true.

One behaviour change

A Props built without a Version used to run the self-update check (a nil interface was not "development"). A zero Info is a development build, so an unstamped binary skips the check, which is what a -dev or +dirty build already did. Stamp the version through ldflags, as the generated main does, to get release behaviour.

Props.Assets is *props.Assets

props.Assets was a twelve-method interface with one implementation, and SetLogger sat outside it so the root command reached it by type assertion. The concrete type is now exported under the same name: NewAssets returns *props.Assets, For and Merge return and take *props.Assets, and Props.Assets is the pointer. Code that only calls props.NewAssets(...) and passes the result on compiles unchanged. Code that named the interface in a signature writes *props.Assets; a second implementation of the old interface has no home, and none was known.