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

#1239 - Clone workspace #1425

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/common/redux/workspaces/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ export const deleteWorkspace = function(name) {
};
};

export const CLONE_WORKSPACE = `${prefix}/CLONE_WORKSPACE`;
export const cloneWorkspace = function(name, cloneName) {
return function(dispatch) {
dispatch({ type: CLONE_WORKSPACE, name, cloneName });

ipcRenderer.send(CLONE_WORKSPACE, name, cloneName);
};
};

export const SET_CURRENT_WORKSPACE = `${prefix}/SET_CURRENT_WORKSPACE`;
export const setCurrentWorkspace = function(workspace, contractCache) {
return { type: SET_CURRENT_WORKSPACE, workspace, contractCache };
Expand Down
15 changes: 15 additions & 0 deletions src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
GET_CONTRACT_DETAILS,
OPEN_NEW_WORKSPACE_CONFIG,
PROJECT_UPDATED,
CLONE_WORKSPACE,
} from "../common/redux/workspaces/actions";

import {
Expand Down Expand Up @@ -481,6 +482,20 @@ app.on('ready', () => {
);
});

ipcMain.on(CLONE_WORKSPACE, async (event, name, cloneName) => {
const sourceWorkspace = workspaceManager.get(name);
if (sourceWorkspace) {
sourceWorkspace.clone(cloneName);

workspaceManager.bootstrap();

mainWindow.webContents.send(
SET_WORKSPACES,
workspaceManager.getNonDefaultNames(),
);
}
});

ipcMain.on(DELETE_WORKSPACE, async (event, name) => {
const tempWorkspace = workspaceManager.get(name);
if (tempWorkspace) {
Expand Down
35 changes: 35 additions & 0 deletions src/main/types/workspaces/Workspace.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 UUID from "uuid";

import WorkspaceSettings from "../settings/WorkspaceSettings";
import ContractCache from "../contracts/ContractCache";
Expand Down Expand Up @@ -117,6 +118,40 @@ class Workspace {
// a solution here
}
}

clone(cloneName) {
const sanitizedName = Workspace.getSanitizedName(cloneName);
const configDirectory = path.join(this.workspaceDirectory, "..", "..");
const cloneDirectory = path.join(
configDirectory,
"workspaces",
sanitizedName,
);

try {
// copy workspace directory, make sure not to overwrite any existing data
fse.copySync(this.workspaceDirectory, cloneDirectory, {
overwrite: false,
errorOnExist: true,
});
} catch (e) {
// Failed to copy directory. Most likely target already exists
// TODO: Handle this error more gracefully/provide user feedback
return;
}

// update settings of cloned workspace to match new location
const db_path = path.join(cloneDirectory, "chaindata");
let settings = new WorkspaceSettings(cloneDirectory, db_path);
settings.bootstrap();
// set new db_path
settings.set("server.db_path", db_path);
// generate new uuid
settings.set("uuid", UUID.v4());
// set new name
settings.set("name", cloneName);
return new Workspace(cloneName, configDirectory);
}
}

export default Workspace;
1 change: 1 addition & 0 deletions src/renderer/icons/clone-regular.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 24 additions & 1 deletion src/renderer/screens/startup/HomeScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
openDefaultWorkspace,
openNewWorkspaceConfig,
deleteWorkspace,
cloneWorkspace,
} from "../../../common/redux/workspaces/actions";
import UpdateNotification from "../auto-update/UpdateNotification";
import ErrorModal from "../../components/modal/ErrorModal";
Expand All @@ -22,6 +23,7 @@ import Logo from "../../icons/logo.svg";
import ChainIcon from "../../icons/chain.svg";
import MenuIcon from "../../icons/list.svg";
import TrashIcon from "../../icons/trash-icon.svg";
import CloneIcon from "../../icons/clone-regular.svg";

class HomeScreen extends Component {
constructor(props) {
Expand All @@ -33,8 +35,21 @@ class HomeScreen extends Component {
this.props.dispatch(openWorkspace(workspaceName));
}

handleCloneWorkspace(e) {
const workspaceName = e.currentTarget.parentElement.querySelector("span").innerText;
e.stopPropagation();
e.preventDefault();

document.activeElement.blur();

// Future improvement: Create modal dialog asking for new name
// For now, just go with hardcoded extension
const cloneName = workspaceName + "-clone";
this.props.dispatch(cloneWorkspace(workspaceName, cloneName));
}

handleDeleteWorkspace(e) {
const workspaceName = e.currentTarget.previousSibling.innerText;
const workspaceName = e.currentTarget.parentElement.querySelector("span").innerText;
e.stopPropagation();
e.preventDefault();

Expand Down Expand Up @@ -79,8 +94,16 @@ class HomeScreen extends Component {
<li key={workspaceName}>
<button onClick={this.selectWorkspace.bind(this)}>
<span>{workspaceName}</span>
<div
className="CloneWorkspace"
title="Clone workspace"
onClick={this.handleCloneWorkspace.bind(this)}
>
<CloneIcon/>
</div>
<div
className="DeleteWorkspace"
title="Remove workspace"
onClick={this.handleDeleteWorkspace.bind(this)}
>
<TrashIcon />
Expand Down
15 changes: 12 additions & 3 deletions src/renderer/screens/startup/HomeScreen.scss
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,16 @@
&:last-child {
margin-bottom: 0;
}


.CloneWorkspace {
opacity: 0;
width: 1.3rem;
vertical-align: middle;
transition: all .3s;
position: absolute;
right: 3rem;
}

.DeleteWorkspace {
opacity: 0;
width: 1.3rem;
Expand All @@ -146,8 +155,8 @@
&:hover, &:focus, &:active {
color: #fbf1ec;
background: #5f474e;
.DeleteWorkspace {

.DeleteWorkspace, .CloneWorkspace {
opacity: 1;

&:hover, &:focus {
Expand Down
98 changes: 98 additions & 0 deletions test/mocha/workspaces/clone-workspace.subtest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import Workspace from "../../../src/main/types/workspaces/Workspace";
import temp from "temp";
import assert from "assert";
import fs from "fs";
import path from "path";
import ganacheLib from "ganache-core";
import Web3 from "web3";

describe("Clone Workspace", () => {
let configDirectory = "/";
let workspace;
let clonedWorkspace;
const cloneName = "cloned workspace";

before("create folder where workspace will live", async () => {
temp.track();
configDirectory = temp.mkdirSync("ganache-temp-workspaces");
fs.mkdirSync(path.join(configDirectory, "workspaces"));
});

before("created and bootstrapped new workspace", async () => {
workspace = new Workspace("Temp Workspace", configDirectory);
workspace.bootstrap();
});

it("did not clone workspace into existing directory", async () => {
clonedWorkspace = workspace.clone(workspace.name);
assert.strictEqual(
clonedWorkspace,
undefined,
"Workspace was cloned into existing directory",
);
});

it("cloned workspace without error", async () => {
clonedWorkspace = workspace.clone(cloneName);
assert(
fs.existsSync(clonedWorkspace.workspaceDirectory),
"Cloned workspace directory wasn't created",
);
const settingsFile = path.join(
clonedWorkspace.workspaceDirectory,
"Settings",
);
assert(
fs.existsSync(settingsFile),
"Cloned Workspace Settings file wasn't created",
);
});

it("applied correct settings to cloned workspace", async () => {
assert.notEqual(
clonedWorkspace.settings.get("uuid"),
workspace.settings.get("uuid"),
"The uuid of the cloned workspace should be different from the original",
);
assert.equal(
clonedWorkspace.name,
cloneName,
"Cloned workspace should have the correct name set",
);
assert.equal(
clonedWorkspace.settings.get("name"),
cloneName,
"Cloned workspace settings should have correct name set",
);
assert.notEqual(
workspace.settings.get("server.db_path"),
clonedWorkspace.settings.get("server.db_path"),
"Cloned workspace db_path is the same like original workspace",
);
assert.equal(
clonedWorkspace.settings.get("server.db_path"),
path.join(clonedWorkspace.workspaceDirectory, "chaindata"),
"Cloned workspace db_path is not correct",
);
assert.equal(
clonedWorkspace.settings.get("server.mnemonic"),
workspace.settings.get("server.mnemonic"),
"Mnemeonic of cloned workspace is different from original",
);
});

it("started and stopped cloned workspace with no errors", done => {
var web3 = new Web3();
web3.setProvider(ganacheLib.provider(clonedWorkspace.settings.getAll()));

web3.eth.getAccounts(function(err, result) {
if (err) return done(err);
assert(
result.length,
10,
"The number of accounts created should be 10 (the default)",
);
done();
});
});
});
1 change: 1 addition & 0 deletions test/mocha/workspaces/workspaces.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
describe("Workspaces", function() {
require("./new-workspace.subtest");
require("./clone-workspace.subtest");
require("./workspace-manager.subtest");
});