fix(cli): make command registration snapshot-safe
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
88ec9264a9
commit
b8f3c9698a
2 changed files with 36 additions and 6 deletions
|
|
@ -105,15 +105,23 @@ func loadLocaleSources(sources ...LocaleSource) {
|
|||
func RegisteredLocales() []fs.FS {
|
||||
registeredCommandsMu.Lock()
|
||||
defer registeredCommandsMu.Unlock()
|
||||
return registeredLocales
|
||||
if len(registeredLocales) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]fs.FS, len(registeredLocales))
|
||||
copy(out, registeredLocales)
|
||||
return out
|
||||
}
|
||||
|
||||
// RegisteredCommands returns an iterator over the registered command functions.
|
||||
func RegisteredCommands() iter.Seq[CommandRegistration] {
|
||||
return func(yield func(CommandRegistration) bool) {
|
||||
registeredCommandsMu.Lock()
|
||||
defer registeredCommandsMu.Unlock()
|
||||
for _, fn := range registeredCommands {
|
||||
snapshot := make([]CommandRegistration, len(registeredCommands))
|
||||
copy(snapshot, registeredCommands)
|
||||
registeredCommandsMu.Unlock()
|
||||
|
||||
for _, fn := range snapshot {
|
||||
if !yield(fn) {
|
||||
return
|
||||
}
|
||||
|
|
@ -125,10 +133,12 @@ func RegisteredCommands() iter.Seq[CommandRegistration] {
|
|||
// Called by Init() after creating the root command.
|
||||
func attachRegisteredCommands(root *cobra.Command) {
|
||||
registeredCommandsMu.Lock()
|
||||
defer registeredCommandsMu.Unlock()
|
||||
snapshot := make([]CommandRegistration, len(registeredCommands))
|
||||
copy(snapshot, registeredCommands)
|
||||
commandsAttached = true
|
||||
registeredCommandsMu.Unlock()
|
||||
|
||||
for _, fn := range registeredCommands {
|
||||
for _, fn := range snapshot {
|
||||
fn(root)
|
||||
}
|
||||
commandsAttached = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,6 +148,26 @@ func TestRegisterCommands_Bad(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, "late", cmd.Use)
|
||||
})
|
||||
|
||||
t.Run("nested registration during startup does not deadlock", func(t *testing.T) {
|
||||
resetGlobals(t)
|
||||
|
||||
RegisterCommands(func(root *cobra.Command) {
|
||||
root.AddCommand(&cobra.Command{Use: "outer", Short: "Outer"})
|
||||
RegisterCommands(func(root *cobra.Command) {
|
||||
root.AddCommand(&cobra.Command{Use: "inner", Short: "Inner"})
|
||||
})
|
||||
})
|
||||
|
||||
err := Init(Options{AppName: "test"})
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, name := range []string{"outer", "inner"} {
|
||||
cmd, _, err := RootCmd().Find([]string{name})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, name, cmd.Use)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestLocaleLoading_Good verifies locale files become available to the active i18n service.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue