feat(ninja): publish --bnp uploads a prebuilt wasm artifact (WO-WZ-011)

`ninja plugin publish --bnp <file>` uploads a .bnp built by `ninja plugin
build` instead of a source archive. The orchestrator verifies the .bnp
layout/ABI/name/version server-side and records its abi_version. Without
--bnp, publish keeps shipping a source archive for backwards compatibility
until the CMS install path is wasm-native.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Alex Dunmow 2026-07-03 20:07:57 +08:00
parent ad6d87bf06
commit f708a0269d

View File

@ -679,9 +679,10 @@ func newPluginPublishCmd() *cobra.Command {
var channel string var channel string
var strict bool var strict bool
var privateFlag bool var privateFlag bool
var bnpFile string
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "publish", Use: "publish",
Short: "Build a source archive and publish a new version to the registry", Short: "Publish a new version to the registry (source archive, or a prebuilt .bnp via --bnp)",
RunE: func(c *cobra.Command, _ []string) error { RunE: func(c *cobra.Command, _ []string) error {
host, _ := c.Flags().GetString("host") host, _ := c.Flags().GetString("host")
cr, err := creds.Load() cr, err := creds.Load()
@ -751,9 +752,27 @@ func newPluginPublishCmd() *cobra.Command {
return err return err
} }
archiveBytes, err := archive.BuildSourceArchive(".") // WO-WZ-011: --bnp <file> uploads a prebuilt wasm artifact (from
if err != nil { // `ninja plugin build`) instead of a source archive. The orchestrator
return fmt.Errorf("build archive: %w", err) // verifies the .bnp layout/ABI/name/version server-side and records
// its abi_version. Without --bnp, publish keeps shipping a source
// archive (compiled in-container by the CMS reconciler) for
// backwards compatibility until the CMS install path is wasm-native.
var (
archiveBytes []byte
artifactKind = "source archive"
)
if bnpFile != "" {
archiveBytes, err = os.ReadFile(bnpFile)
if err != nil {
return fmt.Errorf("read --bnp file: %w", err)
}
artifactKind = ".bnp artifact"
} else {
archiveBytes, err = archive.BuildSourceArchive(".")
if err != nil {
return fmt.Errorf("build archive: %w", err)
}
} }
ctx := context.Background() ctx := context.Background()
@ -784,7 +803,7 @@ func newPluginPublishCmd() *cobra.Command {
if err != nil { if err != nil {
return fmt.Errorf("publish: %w", err) return fmt.Errorf("publish: %w", err)
} }
fmt.Printf("Published %s (%d bytes)\n", mod.Coords(), len(archiveBytes)) fmt.Printf("Published %s (%s, %d bytes)\n", mod.Coords(), artifactKind, len(archiveBytes))
for _, w := range pubResp.Msg.Warnings { for _, w := range pubResp.Msg.Warnings {
fmt.Printf(" warning: %s\n", w) fmt.Printf(" warning: %s\n", w)
} }
@ -794,6 +813,7 @@ func newPluginPublishCmd() *cobra.Command {
cmd.Flags().StringVar(&channel, "channel", "latest", "Channel to point at this version") cmd.Flags().StringVar(&channel, "channel", "latest", "Channel to point at this version")
cmd.Flags().BoolVar(&strict, "strict", false, "Fail if the working tree is dirty (default: ship dirty trees via stash-create)") cmd.Flags().BoolVar(&strict, "strict", false, "Fail if the working tree is dirty (default: ship dirty trees via stash-create)")
cmd.Flags().BoolVar(&privateFlag, "private", false, "Publish as a private plugin under your active account's @private namespace") cmd.Flags().BoolVar(&privateFlag, "private", false, "Publish as a private plugin under your active account's @private namespace")
cmd.Flags().StringVar(&bnpFile, "bnp", "", "Upload a prebuilt .bnp wasm artifact (from `ninja plugin build`) instead of a source archive")
return cmd return cmd
} }