From 8edf35fec419e07ffcbe83d70eb50213fd1f6a04 Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Tue, 27 Jan 2026 09:55:39 -0800 Subject: [PATCH 1/2] Support 'bbctl delete -l' --- README.md | 22 ++++++----------- cmd/bbctl/delete.go | 60 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 089dae5..cabcb65 100644 --- a/README.md +++ b/README.md @@ -166,17 +166,11 @@ it from the Beeper servers (e.g. any rooms and ghost users it created). For official bridges, it will also delete the local data directory with the bridge config, database and python virtualenv (if applicable). -Note that deleting a bridge through the Beeper client settings will -*not* delete the bridge database that is stored locally; you must -delete that yourself, or use `bbctl delete` instead. The bridge -databases are stored in `~/.local/share/bbctl/prod` by default. -However, note that if you use any option that causes the bridge -database to be stored in a separate location, such as `-l` which -stores it in the current working directory, then `bbctl delete` will -*not* delete the bridge database, and you will again have to delete it -manually. - -If you later re-add a self-hosted bridge after deleting it but not -deleting the local database, you should expect errors, as the bridge -will have been removed from Matrix rooms that it thinks it is a member -of. +If you ran the bridge using `bbctl run -l`, you should delete it using +`bbctl delete -l` to ensure that the relevant bridge config is +deleted. + +If you later re-add a self-hosted bridge after deleting it from the +Beeper servers but not deleting the local database, you should expect +errors, as the bridge will have been removed from Matrix rooms that it +thinks it is a member of. diff --git a/cmd/bbctl/delete.go b/cmd/bbctl/delete.go index 8abd8b4..d707a50 100644 --- a/cmd/bbctl/delete.go +++ b/cmd/bbctl/delete.go @@ -5,7 +5,9 @@ import ( "fmt" "io/fs" "os" + "path" "path/filepath" + "strings" "github.com/AlecAivazis/survey/v2" "github.com/fatih/color" @@ -23,6 +25,12 @@ var deleteCommand = &cli.Command{ Action: deleteBridge, Before: RequiresAuth, Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "local-dev", + Aliases: []string{"l"}, + Usage: "Delete the bridge database and config from your current working directory. Useful for developing bridges.", + EnvVars: []string{"BEEPER_BRIDGE_LOCAL"}, + }, &cli.BoolFlag{ Name: "force", Aliases: []string{"f"}, @@ -43,6 +51,18 @@ func deleteBridge(ctx *cli.Context) error { } else if bridge == "hungryserv" { return UserError{"You really shouldn't do that"} } + var err error + dataDir := GetEnvConfig(ctx).BridgeDataDir + var bridgeDir string + localDev := ctx.Bool("local-dev") + if localDev { + bridgeDir, err = os.Getwd() + if err != nil { + return fmt.Errorf("failed to get working directory: %w", err) + } + } else { + bridgeDir = filepath.Join(dataDir, bridge) + } homeserver := ctx.String("homeserver") accessToken := GetEnvConfig(ctx).AccessToken if !ctx.Bool("force") { @@ -60,7 +80,7 @@ func deleteBridge(ctx *cli.Context) error { } var confirmation bool - err := survey.AskOne(&survey.Confirm{Message: fmt.Sprintf("Are you sure you want to permanently delete %s?", bridge)}, &confirmation) + err = survey.AskOne(&survey.Confirm{Message: fmt.Sprintf("Are you sure you want to permanently delete %s?", bridge)}, &confirmation) if err != nil { return err } else if !confirmation { @@ -71,8 +91,7 @@ func deleteBridge(ctx *cli.Context) error { return fmt.Errorf("error deleting bridge: %w", err) } fmt.Println("Started deleting bridge") - bridgeDir := filepath.Join(GetEnvConfig(ctx).BridgeDataDir, bridge) - err = os.RemoveAll(bridgeDir) + err = deleteLocalBridgeData(bridgeDir, !localDev) if err != nil && !errors.Is(err, fs.ErrNotExist) { log.Printf("Failed to delete [magenta]%s[reset]: [red]%v[reset]", bridgeDir, err) } else { @@ -80,3 +99,38 @@ func deleteBridge(ctx *cli.Context) error { } return nil } + +func isLocalBridgeFile(name string) bool { + if name == "config.yaml" { + return true + } + if strings.HasSuffix(name, ".db") { + return true + } + if strings.HasSuffix(name, ".db-shm") { + return true + } + if strings.HasSuffix(name, ".db-wal") { + return true + } + return false +} + +func deleteLocalBridgeData(bridgeDir string, deleteWholeDir bool) error { + if deleteWholeDir { + return os.RemoveAll(bridgeDir) + } + items, err := os.ReadDir(bridgeDir) + if err != nil { + return err + } + for _, item := range items { + if isLocalBridgeFile(item.Name()) { + err := os.Remove(path.Join(bridgeDir, item.Name())) + if err != nil { + return err + } + } + } + return nil +} From a5cef7b8d6ab524c74ed9644e7932b7e0812135a Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Tue, 27 Jan 2026 13:59:31 -0800 Subject: [PATCH 2/2] Improve README --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cabcb65..66b77c5 100644 --- a/README.md +++ b/README.md @@ -166,9 +166,11 @@ it from the Beeper servers (e.g. any rooms and ghost users it created). For official bridges, it will also delete the local data directory with the bridge config, database and python virtualenv (if applicable). -If you ran the bridge using `bbctl run -l`, you should delete it using -`bbctl delete -l` to ensure that the relevant bridge config is -deleted. +Note that deleting a bridge through the Beeper client settings will +*not* delete the bridge database that is stored locally; you must +delete that yourself, or use `bbctl delete` instead. (If you created +the bridge database with `bbctl run -l`, then run `bbctl delete -l` +from the same working directory to delete it.) If you later re-add a self-hosted bridge after deleting it from the Beeper servers but not deleting the local database, you should expect