appdir: a data folder beside magpie in the home folder, Downloads, Desktop or Documents is its portable data only when it holds magpie's files and the installed folder doesn't (or with .portable), so magpie.exe run from Downloads no longer writes into a user's own Downloads\data (tangle778 on X)
This commit is contained in:
1 parent
07e246e515
commit
cda11d8467
2 files changed
+106
-29
No files matched your search
+46
-28
@@ -20,10 +20,14 @@
|
|||||||
// keychain, the folders the OS reads for autostart and link handling, and
|
// keychain, the folders the OS reads for autostart and link handling, and
|
||||||
// temporary folders.
|
// temporary folders.
|
||||||
//
|
//
|
||||||
// A data folder in a folder every app shares (/Applications on a Mac) is
|
// A data folder in a folder every app or download shares (/Applications on
|
||||||
// not taken for magpie's own while magpie's installed folder holds its
|
// a Mac; the home folder, Downloads, Desktop and Documents everywhere) is
|
||||||
// files (#989): a "data" folder there is as likely another app's, and
|
// taken for magpie's own only when it already holds magpie's files and the
|
||||||
// taking it showed a user who had never asked for portable an empty magpie.
|
// installed folder doesn't, or with a ".portable" marker: a "data" folder
|
||||||
|
// there is as likely another app's or the user's own. Taking one showed a
|
||||||
|
// user who had never asked for portable an empty magpie (#989), and wrote
|
||||||
|
// magpie's files into a Downloads\data folder a Windows user's archiver
|
||||||
|
// empties, so a provider just added was gone (tangle778 on X).
|
||||||
//
|
//
|
||||||
// The decision is made once, from the executable as it was when magpie
|
// The decision is made once, from the executable as it was when magpie
|
||||||
// started: an update moving the running copy aside (into .magpie-update on
|
// started: an update moving the running copy aside (into .magpie-update on
|
||||||
@@ -65,7 +69,7 @@ func Portable() string {
|
|||||||
portable = Resolve(exe)
|
portable = Resolve(exe)
|
||||||
if portable == "" {
|
if portable == "" {
|
||||||
if data := Passed(exe); data != "" {
|
if data := Passed(exe); data != "" {
|
||||||
log.Printf("appdir: %s is beside magpie but not taken for its portable data: it is in a folder every app shares, and %s has magpie's files (#989)", data, installed())
|
log.Printf("appdir: %s is beside magpie but not taken for its portable data: it is in a shared folder and %s has magpie's files too; a .portable file beside magpie makes it portable", data, installed())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -93,61 +97,75 @@ func Resolve(exe string) string {
|
|||||||
if fi, err := os.Stat(filepath.Join(base, MarkerName)); err == nil && !fi.IsDir() {
|
if fi, err := os.Stat(filepath.Join(base, MarkerName)); err == nil && !fi.IsDir() {
|
||||||
return absolute(data)
|
return absolute(data)
|
||||||
}
|
}
|
||||||
if fi, err := os.Stat(data); err == nil && fi.IsDir() && !shared(base) {
|
if fi, err := os.Stat(data); err == nil && fi.IsDir() && !shared(base, data) {
|
||||||
return absolute(data)
|
return absolute(data)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passed is the data folder beside a magpie run from exe that Resolve
|
// Passed is the data folder beside a magpie run from exe that holds
|
||||||
// doesn't take for its own because it is in a folder every app shares
|
// magpie's files but that Resolve doesn't take for its own, because it is
|
||||||
// while the installed folder has magpie's files, or "".
|
// in a shared folder while the installed folder has magpie's files too, or
|
||||||
|
// "". A data folder holding none of them is someone else's and isn't named.
|
||||||
func Passed(exe string) string {
|
func Passed(exe string) string {
|
||||||
if real, err := filepath.EvalSymlinks(exe); err == nil {
|
if real, err := filepath.EvalSymlinks(exe); err == nil {
|
||||||
exe = real
|
exe = real
|
||||||
}
|
}
|
||||||
base := Beside(exe)
|
base := Beside(exe)
|
||||||
data := filepath.Join(base, DataName)
|
data := filepath.Join(base, DataName)
|
||||||
if fi, err := os.Stat(data); err == nil && fi.IsDir() && Resolve(exe) == "" {
|
if fi, err := os.Stat(data); err == nil && fi.IsDir() && hasFiles(data) && Resolve(exe) == "" {
|
||||||
return absolute(data)
|
return absolute(data)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// shared says whether a data folder in dir can't be taken for magpie's:
|
// shared says whether the data folder in dir can't be taken for magpie's:
|
||||||
// dir is a folder every app shares (/Applications or ~/Applications on a
|
// dir is a folder every app or download shares, where a folder named data
|
||||||
// Mac), where a folder named data is as likely another app's, and the
|
// is as likely another app's or the user's own, and either data holds none
|
||||||
// installed folder holds magpie's files. One who keeps magpie portable
|
// of magpie's files or the installed folder holds them too. One who keeps
|
||||||
// there never ran it installed, and keeps it portable; a ".portable"
|
// magpie portable there has its files in data and never ran it installed,
|
||||||
// marker beside it makes it portable whatever is installed.
|
// and keeps it portable; a ".portable" marker beside it makes it portable
|
||||||
func shared(dir string) bool {
|
// whatever is there.
|
||||||
|
func shared(dir, data string) bool {
|
||||||
if !sharedFolder(dir) {
|
if !sharedFolder(dir) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
in := installed()
|
if !hasFiles(data) {
|
||||||
if in == "" {
|
return true
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
in := installed()
|
||||||
|
return in != "" && hasFiles(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasFiles says whether dir holds magpie's own files.
|
||||||
|
func hasFiles(dir string) bool {
|
||||||
for _, f := range []string{"providers.json", "logins.json", "settings.json"} {
|
for _, f := range []string{"providers.json", "logins.json", "settings.json"} {
|
||||||
if fi, err := os.Stat(filepath.Join(in, f)); err == nil && !fi.IsDir() {
|
if fi, err := os.Stat(filepath.Join(dir, f)); err == nil && !fi.IsDir() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// sharedFolder says whether dir is a folder every app shares; tests change
|
// sharedFolder says whether dir is a folder every app or download shares:
|
||||||
// it.
|
// /Applications and ~/Applications on a Mac, and the home folder, its
|
||||||
|
// Downloads, Desktop and Documents everywhere. Tests change it.
|
||||||
var sharedFolder = func(dir string) bool {
|
var sharedFolder = func(dir string) bool {
|
||||||
if runtime.GOOS != "darwin" {
|
var dirs []string
|
||||||
return false
|
if runtime.GOOS == "darwin" {
|
||||||
|
dirs = append(dirs, "/Applications")
|
||||||
}
|
}
|
||||||
dirs := []string{"/Applications"}
|
|
||||||
if h, err := Home(); err == nil {
|
if h, err := Home(); err == nil {
|
||||||
dirs = append(dirs, filepath.Join(h, "Applications"))
|
dirs = append(dirs, h)
|
||||||
|
for _, d := range []string{"Downloads", "Desktop", "Documents"} {
|
||||||
|
dirs = append(dirs, filepath.Join(h, d))
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
dirs = append(dirs, filepath.Join(h, "Applications"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, d := range dirs {
|
for _, d := range dirs {
|
||||||
if strings.EqualFold(filepath.Clean(dir), d) {
|
if strings.EqualFold(filepath.Clean(dir), filepath.Clean(d)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,6 +182,12 @@ func TestResolveSharedFolder(t *testing.T) {
|
|||||||
mkdir(t, filepath.Join(data, "nodes"))
|
mkdir(t, filepath.Join(data, "nodes"))
|
||||||
touch(t, filepath.Join(data, "install-id"))
|
touch(t, filepath.Join(data, "install-id"))
|
||||||
|
|
||||||
|
// another app's data folder, nothing installed: not taken
|
||||||
|
if got := Resolve(exe); got != "" {
|
||||||
|
t.Errorf("no magpie files in data: %q, want installed", got)
|
||||||
|
}
|
||||||
|
touch(t, filepath.Join(data, "settings.json"))
|
||||||
|
|
||||||
// nothing installed: one who keeps magpie portable there
|
// nothing installed: one who keeps magpie portable there
|
||||||
if got := Resolve(exe); got != data {
|
if got := Resolve(exe); got != data {
|
||||||
t.Errorf("nothing installed: %q, want %q", got, data)
|
t.Errorf("nothing installed: %q, want %q", got, data)
|
||||||
@@ -216,6 +222,59 @@ func TestResolveSharedFolder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tangle778 on X: magpie.exe run from Downloads beside a data folder the
|
||||||
|
// user's archiver empties wrote its providers there and lost them. A data
|
||||||
|
// folder in Downloads holding none of magpie's files isn't its portable
|
||||||
|
// data; one that does is, and .portable always makes it portable.
|
||||||
|
func TestResolveDownloads(t *testing.T) {
|
||||||
|
t.Setenv("APPIMAGE", "")
|
||||||
|
r := root(t)
|
||||||
|
h := filepath.Join(r, "home")
|
||||||
|
t.Setenv("HOME", h)
|
||||||
|
t.Setenv("USERPROFILE", h)
|
||||||
|
t.Setenv("XDG_CONFIG_HOME", "")
|
||||||
|
dl := filepath.Join(h, "Downloads")
|
||||||
|
exe := touch(t, filepath.Join(dl, "magpie.exe"))
|
||||||
|
data := filepath.Join(dl, "data")
|
||||||
|
touch(t, filepath.Join(data, "report.json"))
|
||||||
|
|
||||||
|
if got := Resolve(exe); got != "" {
|
||||||
|
t.Errorf("user's data folder: %q, want installed", got)
|
||||||
|
}
|
||||||
|
if got := Passed(exe); got != "" {
|
||||||
|
t.Errorf("user's data folder: Passed %q, want none", got)
|
||||||
|
}
|
||||||
|
touch(t, filepath.Join(data, "providers.json"))
|
||||||
|
if got := Resolve(exe); got != data {
|
||||||
|
t.Errorf("portable run before: %q, want %q", got, data)
|
||||||
|
}
|
||||||
|
touch(t, filepath.Join(h, ".config", "magpie", "logins.json"))
|
||||||
|
if got := Resolve(exe); got != "" {
|
||||||
|
t.Errorf("installed has files: %q, want installed", got)
|
||||||
|
}
|
||||||
|
if got := Passed(exe); got != data {
|
||||||
|
t.Errorf("Passed %q, want %q", got, data)
|
||||||
|
}
|
||||||
|
touch(t, filepath.Join(dl, ".portable"))
|
||||||
|
if got := Resolve(exe); got != data {
|
||||||
|
t.Errorf(".portable: %q, want %q", got, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedFolders(t *testing.T) {
|
||||||
|
h, _ := Home()
|
||||||
|
for _, d := range []string{h, filepath.Join(h, "Downloads"), filepath.Join(h, "Downloads") + string(filepath.Separator), filepath.Join(h, "Desktop"), filepath.Join(h, "Documents")} {
|
||||||
|
if !sharedFolder(d) {
|
||||||
|
t.Errorf("%s not shared", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, d := range []string{filepath.Join(h, "Downloads", "magpie"), filepath.Join(h, "tools")} {
|
||||||
|
if sharedFolder(d) {
|
||||||
|
t.Errorf("%s shared", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSharedFolderIsApplications(t *testing.T) {
|
func TestSharedFolderIsApplications(t *testing.T) {
|
||||||
if runtime.GOOS != "darwin" {
|
if runtime.GOOS != "darwin" {
|
||||||
if sharedFolder("/Applications") {
|
if sharedFolder("/Applications") {
|
||||||
@@ -229,7 +288,7 @@ func TestSharedFolderIsApplications(t *testing.T) {
|
|||||||
t.Errorf("%s not shared", d)
|
t.Errorf("%s not shared", d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, d := range []string{"/Volumes/USB", filepath.Join(h, "Downloads"), "/Applications/Utilities"} {
|
for _, d := range []string{"/Volumes/USB", "/Applications/Utilities"} {
|
||||||
if sharedFolder(d) {
|
if sharedFolder(d) {
|
||||||
t.Errorf("%s shared", d)
|
t.Errorf("%s shared", d)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user