Register a module from a repository the catalogue does not build at the controller's terminal alone, from its done trunk key, naming exactly its repository and path (issue 1851, A11b, ADR 0501 §4)
This commit is contained in:
@@ -73,6 +73,8 @@ func run() error {
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "register":
|
||||
return registerCommand(ctx, args[1:])
|
||||
case "build", "rebuild":
|
||||
// **The hand build goes with the asks** (novox/hq ADR 0501 §5): refused by name, naming the replacement.
|
||||
return removedByName(args[0])
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/novox/mesh-controller/internal/builderkeys"
|
||||
)
|
||||
|
||||
// The terminal's exception becomes a registration, not a build (novox/hq ADR 0501 §4; issue 1851, A11b).
|
||||
//
|
||||
// `register <module> <owner>/<repository> <path>`, at the controller's terminal alone: the operator names the module,
|
||||
// its repository and its path, and the controller registers it from the builder's done key of the fingerprint the
|
||||
// indexer's trunk value of that module names — the registration a build asked at the terminal used to make for a
|
||||
// repository the catalogue does not build (ADR 0432 §3, ADR 0420 §7). No verb reaches it (terminalOnlyCommands), and it
|
||||
// asks no build: a key not done is waited for, and said.
|
||||
func registerCommand(ctx context.Context, args []string) error {
|
||||
if len(args) != 3 {
|
||||
return errors.New("register <module> <owner>/<repository> <path> (the path \".\" for a module at the repository's root)")
|
||||
}
|
||||
if !startedAtTheTerminal() {
|
||||
return terminalRefusal("register is run at the controller's terminal only, never through a verb: it registers " +
|
||||
"a module from a repository the catalogue does not build, and whoever may call a verb includes agents " +
|
||||
"(novox/hq ADR 0266, ADR 0501 §4). Nothing was done")
|
||||
}
|
||||
module, repository, path := args[0], strings.Trim(args[1], "/"), strings.Trim(args[2], "/")
|
||||
if path == sdkdelivery.RootPath {
|
||||
path = ""
|
||||
}
|
||||
open, err := openStores(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer open.Close()
|
||||
keys, closeKeys, err := openBuilderKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeKeys()
|
||||
trunk, err := keys.ModuleAt(ctx, module, "")
|
||||
if errors.Is(err, builderkeys.ErrNoValue) {
|
||||
return fmt.Errorf("the indexer holds no trunk value of %s: a module is registered from the builder's key of a "+
|
||||
"merged trunk, and %s has none yet. Nothing was registered", module, module)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sameModuleSource(module, repository, path, trunk); err != nil {
|
||||
return err
|
||||
}
|
||||
manifest, kept, err := takeInKey(ctx, open.inventory, keys, keyTake{Module: module, Trunk: trunk,
|
||||
Ask: registrationAsk{Terminal: true}})
|
||||
if errors.Is(err, errKeyWaits) {
|
||||
return fmt.Errorf("%s is not registered yet, and nothing was asked: %v. Run register again once its key is done",
|
||||
module, err)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s %s registered from the builder's key %s (%s at %.8s), as asked at the controller's terminal; "+
|
||||
"assigned nowhere\n", manifest.Module, manifest.Version, short(kept.SourceFingerprint), repository, kept.Commit)
|
||||
return nil
|
||||
}
|
||||
|
||||
// sameModuleSource refuses a registration whose repository or path is not the one the indexer's trunk value names: the
|
||||
// operator names exactly what is registered (ADR 0501 §4).
|
||||
func sameModuleSource(module, repository, path string, trunk sdkdelivery.Module) error {
|
||||
if !strings.EqualFold(trunk.Repository, repository) || keyPath(trunk) != path {
|
||||
return fmt.Errorf("the indexer holds %s in %s at %q, not in %s at %q: the registration names exactly what it "+
|
||||
"registers. Nothing was registered", module, trunk.Repository, orRoot(keyPath(trunk)), repository, orRoot(path))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// A11b (ADR 0501 §4): the terminal's registration names exactly the module's repository and path the indexer holds,
|
||||
// and no verb reaches it.
|
||||
func TestTheTerminalsRegistrationNamesItsSourceExactlyAndNoVerbReachesIt(t *testing.T) {
|
||||
trunk := sdkdelivery.Module{Repository: "someone/app", Path: sdkdelivery.RootPath, Commit: "c0ffee"}
|
||||
if err := sameModuleSource("app", "someone/app", "", trunk); err != nil {
|
||||
t.Fatalf("the source the indexer holds is refused: %v", err)
|
||||
}
|
||||
if err := sameModuleSource("app", "someone/other", "", trunk); err == nil || !strings.Contains(err.Error(), "someone/app") {
|
||||
t.Fatalf("another repository is taken: %v", err)
|
||||
}
|
||||
if err := sameModuleSource("app", "someone/app", "modules/app", trunk); err == nil {
|
||||
t.Fatal("another path is taken")
|
||||
}
|
||||
// The generic verb only reads; and register is the terminal's only, whatever reads.
|
||||
if _, err := argvFor("command", map[string]any{"command": "register app someone/app ."}); err == nil {
|
||||
t.Fatal("a verb reaches the terminal's registration")
|
||||
}
|
||||
var refused *heldAtTheTerminal
|
||||
if err := terminalOnly([]string{"register", "app", "someone/app", "."}); !errors.As(err, &refused) {
|
||||
t.Fatalf("register is not the terminal's only: %v", err)
|
||||
}
|
||||
t.Setenv(verbVar, "command")
|
||||
var held *heldAtTheTerminal
|
||||
if err := registerCommand(context.Background(), []string{"app", "someone/app", "."}); !errors.As(err, &held) {
|
||||
t.Fatalf("register through a verb: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1784,6 +1784,9 @@ var terminalOnlyCommands = map[string]string{
|
||||
"broker": "the bus's accounts",
|
||||
"api": "the controller's API keys",
|
||||
"licence": "the licences' secrets",
|
||||
// The terminal's exception of ADR 0501 §4: a module from a repository the catalogue does not build, registered
|
||||
// from its done key.
|
||||
"register": "a module's registration from a repository the catalogue does not build",
|
||||
}
|
||||
|
||||
// givenAtTheDesk is exactly the line the `give` and `secret-ask` verbs compose, and nothing beside it: `secret ask
|
||||
|
||||
Reference in New Issue
Block a user