Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
fix: move workspaces to Ganache/ui/workspaces (#5151)
Browse files Browse the repository at this point in the history
* Change workspaces directory to ui/workspaces in anticipation of all legacy data being migrated to here. Symlink workspaces from 'old' workspaces directory. Use fs.readdirSync along with 'withFileTypes' to avoid additional 'stat' call.

* If the new workspaces directory doesn't exist - create it. Handle deleting linked workspaces.

* Ensure that symlinking of legacy workspaces works in windows

* fix miss-spelling in comment

* Silently ignore invalid flavors, and invalid workspaces when attempting to symlink workspaces
  • Loading branch information
jeffsmale90 authored Jan 12, 2023
1 parent c5bfe6a commit 55865cb
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 25 deletions.
4 changes: 2 additions & 2 deletions src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ if (process.platform === "win32") {
return spawn("cmd.exe", ["/c", "mkdir", path.join(USERDATA_PATH, "extras")])
})
.then(()=> {
return spawn("cmd.exe", ["/c", "mkdir", path.join(USERDATA_PATH, "workspaces")])
return spawn("cmd.exe", ["/c", "mkdir", path.join(USERDATA_PATH, "ui/workspaces")])
})
.then(()=> {
return spawn("cmd.exe", ["/c", "mkdir", path.join(USERDATA_PATH, "default")])
Expand All @@ -120,7 +120,7 @@ if (process.platform === "win32") {
}
});
} else {
migrationPromise = Promise.resolve();
migrationPromise = migration.migrate(USERDATA_PATH);

// https://github.com/sindresorhus/fix-path
// GUI apps on macOS don't inherit the $PATH defined in your dotfiles (.bashrc/.bash_profile/.zshrc/etc)
Expand Down
70 changes: 60 additions & 10 deletions src/main/init/migration.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,57 @@
import { join } from "path";
import { copy, pathExists as exists, readdir } from "fs-extra";
import { copy, pathExists as exists } from "fs-extra";
import { exec } from "child_process";
import * as pkg from "../../../package.json";
import { readdir, symlink, mkdir, readFile } from "fs/promises";

let migrate, uninstallOld;

/*
When we introduced Ganache 7 for ethereum chains, we moved the workspaces to
Ganache/ui/workspaces. Previous versions of Ganache-UI would crash loading
these workspaces, so we link the legacy workspaces to the new workspaces
directory. This means that the old workspaces are available to both new and
old versions of Ganache-UI, but new workspaces are only available to new
versions.
The intention is to migrate all Ganache-UI data to the /Ganache/ui directory,
giving the user the option to move (and migrate the chaindata of) legacy
workspaces.
See: https://github.com/trufflesuite/ganache-ui/pull/5151
*/
const linkLegacyWorkspaces = async (configRoot) => {
const legacyWorkspacesDirectory = join(configRoot, "workspaces");
const newWorkspacesDirectory = join(configRoot, "ui/workspaces");

if (!await exists(newWorkspacesDirectory)) {
await mkdir(newWorkspacesDirectory, { recursive: true })
}

if (await exists(legacyWorkspacesDirectory)) {
const legacyWorkspaces = await readdir(legacyWorkspacesDirectory, { withFileTypes: true });
const linkingWorkspaces = legacyWorkspaces.map(async legacyWorkspace => {
try {
const fullPath = join(legacyWorkspacesDirectory, legacyWorkspace.name);

const settings = await readFile(join(fullPath, "Settings"));
const { flavor } = JSON.parse(settings);
if (flavor === "ethereum" || flavor === "filecoin") {
// silently ignore any workspaces that aren't of a supported flavor
const linkPath = join(newWorkspacesDirectory, legacyWorkspace.name);
if (legacyWorkspace.isDirectory() && !await exists(linkPath)) {
return symlink(fullPath, linkPath, "junction");
}
}
} catch {
// silently ignore any workspaces that fail to link
}
});

return Promise.all(linkingWorkspaces);
}
};


if (process.platform == "win32") {
const APP_DATA = process.env.APPDATA;
const COPY_SETTINGS = {
Expand Down Expand Up @@ -40,7 +88,7 @@ if (process.platform == "win32") {
await Promise.all(promises);
}

const getOldGanachePath = ()=>{
const getOldGanachePath = () => {
return join(APP_DATA, "/../Local/Packages/Ganache_zh355ej5cj694/LocalCache/Roaming/Ganache");
}

Expand All @@ -56,13 +104,14 @@ if (process.platform == "win32") {
* workspace folder.
*/
migrate = async (newGanache) => {
if (!APP_DATA) return;
const oldGanache = getOldGanachePath();

if (!(await ganacheExists())) return;
if (APP_DATA && await ganacheExists()) {
const oldGanache = getOldGanachePath();

const newGanacheVirtualized = join(APP_DATA, `/../Local/Packages/${pkg.build.appx.identityName}_5dg5pnz03psnj/LocalCache/Roaming/Ganache`);
await Promise.all([moveWorkspaces(oldGanache, newGanache), moveGlobalSettings(oldGanache, newGanache), moveWorkspaces(newGanacheVirtualized, newGanache), moveGlobalSettings(newGanacheVirtualized, newGanache)]);
}

const newGanacheVirtualized = join(APP_DATA, `/../Local/Packages/${pkg.build.appx.identityName}_5dg5pnz03psnj/LocalCache/Roaming/Ganache`);
return Promise.all([moveWorkspaces(oldGanache, newGanache), moveGlobalSettings(oldGanache, newGanache), moveWorkspaces(newGanacheVirtualized, newGanache), moveGlobalSettings(newGanacheVirtualized, newGanache)]);
return linkLegacyWorkspaces(newGanache);
};

uninstallOld = async () => {
Expand All @@ -79,10 +128,11 @@ if (process.platform == "win32") {
}
} else {
const noop = () => Promise.resolve();
migrate = uninstallOld = noop;
migrate = linkLegacyWorkspaces;
uninstallOld = noop;
}

export default {
migrate,
uninstallOld
};
}
16 changes: 13 additions & 3 deletions src/main/types/workspaces/Workspace.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import path from "path";
import fse from "fs-extra";

import WorkspaceSettings from "../settings/WorkspaceSettings";
import ContractCache from "../../../integrations/ethereum/main/types/contracts/ContractCache";

Expand Down Expand Up @@ -54,7 +53,7 @@ class Workspace {
return path.join(configDirectory, `default_${flavor}`);
}
} else {
return path.join(configDirectory, "workspaces", sanitizedName);
return path.join(configDirectory, "ui/workspaces", sanitizedName);
}
}

Expand Down Expand Up @@ -121,15 +120,26 @@ class Workspace {
}

delete() {
let workspaceDirectory;
if (fse.lstatSync(this.workspaceDirectory).isSymbolicLink()) {
workspaceDirectory = fse.readlinkSync(this.workspaceDirectory);
fse.unlinkSync(this.workspaceDirectory);
} else {
workspaceDirectory = this.workspaceDirectory;
}

try {
fse.removeSync(this.workspaceDirectory);
fse.removeSync(workspaceDirectory);
} catch (e) {
// TODO: couldn't delete the directory; probably don't have
// permissions or some file is open somewhere. we probably
// want to handle this better (i.e. return false, then send
// a message to renderer process, display toast saying there
// were issues, etc.). Don't really have time right now for
// a solution here
// todo: if unlinking is successful, but removing the
// directory is not, the link will be recreated during the
// migration process next time the app is started.
}
}

Expand Down
24 changes: 14 additions & 10 deletions src/main/types/workspaces/WorkspaceManager.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from "path";
import fse from "fs-extra";
import { readdirSync } from "fs";
import Workspace from "./Workspace";
import WorkspaceSettings from "../settings/WorkspaceSettings";

Expand All @@ -9,30 +10,33 @@ class WorkspaceManager {
this.workspaces = [];
}

enumerateWorkspaces() {
const workspacesDirectory = path.join(this.directory, "workspaces");
enumerateWorkspaces() {
const workspacesDirectory = path.join(this.directory, "ui/workspaces");

if (fse.existsSync(workspacesDirectory)) {
this.workspaces = fse.readdirSync(workspacesDirectory).flatMap((file) => {
this.workspaces = readdirSync(workspacesDirectory, { withFileTypes: true }).flatMap((file) => {
// if an osx user navigates to the workspaces directory osx will put a
// .DS_Store folder there, ignore and delete these. If the file isn't
// a directory, also delete it.

if (
file === ".DS_Store" ||
!fse.lstatSync(path.join(workspacesDirectory, file)).isDirectory()
file.name === ".DS_Store" ||
!file.isDirectory()
&& !file.isSymbolicLink()
) {
try {
// remove files and folders that aren't allow in the workspaces
// directory
fse.removeSync(path.join(workspacesDirectory, file));
fse.removeSync(path.join(workspacesDirectory, file.name));
} catch {
// ignore
}
return [];
}

let settings = new WorkspaceSettings(
path.join(workspacesDirectory, file),
path.join(workspacesDirectory, file, "chaindata")
path.join(workspacesDirectory, file.name),
path.join(workspacesDirectory, file.name, "chaindata")
);

const isQuickstart = settings.get("isDefault");
Expand All @@ -46,12 +50,12 @@ class WorkspaceManager {

const name = settings.get("name");
const sanitizedName = Workspace.getSanitizedName(name);
if (sanitizedName !== file) {
if (sanitizedName !== file.name) {
// apparently the Settings file has a name that is not equal to the directory,
// we need to move the directory
try {
fse.moveSync(
path.join(workspacesDirectory, file),
path.join(workspacesDirectory, file.name),
path.join(workspacesDirectory, sanitizedName)
);
} catch (e) {
Expand Down

0 comments on commit 55865cb

Please sign in to comment.