mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
fix(cli): ask before macOS pops "Background Items Added" at init (#139)
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestInstallAutostartWarnsBeforeTheFirstMacOSWrite
|
||||
//
|
||||
// Writing ~/Library/LaunchAgents/*.plist is what makes macOS pop "Background
|
||||
// Items Added". Nothing suppresses that notice, so the only thing keeping it
|
||||
// from reading as malware is our own line arriving first — and it must arrive
|
||||
// only when a write is actually about to happen, or it becomes noise every
|
||||
// init prints and nobody reads.
|
||||
func TestInstallAutostartWarnsBeforeTheFirstMacOSWrite(t *testing.T) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
t.Skip("the notice exists only on macOS")
|
||||
}
|
||||
home := t.TempDir()
|
||||
t.Setenv("HOME", home)
|
||||
|
||||
// Non-interactive: no prompt, just the heads-up. (The TTY branch needs a
|
||||
// terminal survey can drive, which a test does not have.)
|
||||
say := func() string {
|
||||
t.Helper()
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
old := os.Stdout
|
||||
os.Stdout = w
|
||||
installAutostart(false)
|
||||
os.Stdout = old
|
||||
w.Close()
|
||||
out, _ := io.ReadAll(r)
|
||||
r.Close()
|
||||
return string(out)
|
||||
}
|
||||
|
||||
first := say()
|
||||
if !strings.Contains(first, "Background Items Added") {
|
||||
t.Errorf("first install said nothing about the macOS notice:\n%s", first)
|
||||
}
|
||||
plist := filepath.Join(home, "Library", "LaunchAgents", "ai.beardrive.daemon.plist")
|
||||
if _, err := os.Stat(plist); err != nil {
|
||||
t.Fatalf("no agent written: %v", err)
|
||||
}
|
||||
|
||||
// Already registered: no second write, so no notice to explain.
|
||||
if second := say(); strings.Contains(second, "Background Items Added") {
|
||||
t.Errorf("re-running warned again about a notice that will not fire:\n%s", second)
|
||||
}
|
||||
}
|
||||
+33
-3
@@ -11,6 +11,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -241,7 +242,7 @@ the folder was renamed or moved.`,
|
||||
installAgentHooks(folder)
|
||||
}
|
||||
if !noAutostart {
|
||||
installAutostart()
|
||||
installAutostart(stdinIsTTY() && !yes)
|
||||
}
|
||||
return startSync(cmd.Context(), folder, proj, foreground, 3*time.Second, 10*time.Second)
|
||||
}
|
||||
@@ -387,7 +388,7 @@ the folder was renamed or moved.`,
|
||||
installAgentHooks(folder)
|
||||
}
|
||||
if !noAutostart {
|
||||
installAutostart()
|
||||
installAutostart(interactive)
|
||||
}
|
||||
if len(scope) > 0 {
|
||||
dirs := make([]string, len(scope))
|
||||
@@ -493,7 +494,36 @@ func installAgentHooks(folder string) {
|
||||
// Linux without systemd) or an unwritable config dir is not a reason to fail
|
||||
// an init that otherwise worked — the folder syncs, it just won't come back by
|
||||
// itself.
|
||||
func installAutostart() {
|
||||
//
|
||||
// On macOS the write itself is what users see: the moment anything lands in
|
||||
// ~/Library/LaunchAgents, Ventura+ pops "Background Items Added", naming a
|
||||
// binary they just installed. Nothing suppresses that notice — SMAppService, a
|
||||
// System Settings login item and a real crontab all trigger it (cron also
|
||||
// wants Full Disk Access on top) — so the fix is to make it expected rather
|
||||
// than to dodge it: say what is about to happen before it happens, and on a
|
||||
// TTY ask first. Linux and Windows show nothing, so they are not asked.
|
||||
func installAutostart(interactive bool) {
|
||||
if runtime.GOOS == "darwin" && !autostart.Installed() {
|
||||
const notice = `macOS will show a "Background Items Added" notice for it`
|
||||
if interactive {
|
||||
ok := true
|
||||
// An interrupt reads as "no": autostart is a convenience, and the
|
||||
// caller is one step from starting the daemon anyway.
|
||||
//
|
||||
// ponytail: a decline is not remembered, so re-running `bdrive init`
|
||||
// interactively asks again. Persist it in settings.json if that ever
|
||||
// nags — agents and scripts have no TTY and never see the prompt.
|
||||
if err := survey.AskOne(&survey.Confirm{
|
||||
Message: "Restart syncing at login? (" + notice + ")",
|
||||
Default: true,
|
||||
}, &ok); err != nil || !ok {
|
||||
fmt.Println(" login: autostart skipped — `bdrive autostart install` enables it later")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fmt.Println(" login: registering sync to restart at login — " + notice)
|
||||
}
|
||||
}
|
||||
res, err := autostart.Install()
|
||||
if err != nil {
|
||||
if !errors.Is(err, autostart.ErrUnsupported) {
|
||||
|
||||
@@ -110,6 +110,15 @@ Linux needs systemd as the init system. Without it — Alpine or another
|
||||
runit/OpenRC distro, WSL1, a slim container — `bdrive autostart` says so rather
|
||||
than writing a unit nothing would read.
|
||||
|
||||
On macOS, the moment that file is written you get a **"Background Items Added"**
|
||||
notification, and `bdrive` appears in System Settings → General → Login Items.
|
||||
That notice is macOS reporting the registration above — every way of starting
|
||||
at login triggers it, including Apple's own `SMAppService` and a plain
|
||||
`crontab` — so `bdrive init` asks first on a terminal and says what is about to
|
||||
happen when it can't ask. Answer no, or run `bdrive autostart uninstall`, and
|
||||
the item goes away; sync then resumes on the next `bdrive resume`, `bdrive
|
||||
init`, or agent turn instead of at login.
|
||||
|
||||
On Windows you may see a console window flicker at logon: bdrive is a console
|
||||
program and `resume` exits in milliseconds. Nothing is wrong.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user