From 6ab83c6879e89ee5cea3fe8d180f9cc882222bae Mon Sep 17 00:00:00 2001 From: James Morley Date: Mon, 9 Jan 2023 05:43:59 -0700 Subject: [PATCH] adds readme to repositoy --- README.md | 38 +++++++++++++++++++++++++++++++++++++- main.js | 4 +++- src/book.js | 19 +++++++++---------- src/config.js | 28 ++++++++++++++-------------- src/proc.js | 10 +++++----- src/windows.js | 22 +++++++++++----------- 6 files changed, 79 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 1a170ce..2c0d1ca 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,44 @@ # LibraryNB ## Description +LibraryNB is a cross platform application that provides a convenient way to import and run git repositories of Jupyter Notebooks on your local machine. It handles the process of cloning the repository, creating the Conda environment, and launching Jupyter Lab in a desktop environment on your system. It currently runs on only Windows and Linux. + +## Installation +### Prerequisites + +LibraryNB assumes you have git configured and installed on your computer. It also assumes that you have either Conda or Mamba installed on your system. Mamba is currently recommended. Conda has not been tested. + +### Linux +Start by acquiring the deb or rpm package, depending on your systems package manager, from the release section of this repository. Then run +```bash +sudo apt install ./librarynb_{version}_{arch}.{ext} +``` +in the directory where you downloaded the package. + +### First run +When ran the first time LibraryNB will ask you a few questions to configure itself to your system. Once you have answered the questions and pressed "Save", LibraryNB will exit and will be ready to use. + +## Security +LibraryNB makes no attempt to determine the trustworthiness of Jupyter Notebooks or Conda environments specified in git repositories. You should never use LibraryNB on git repositories you don't trust. ## Current State +This software is very early in its development. It should not be considered secure or stable at this time. Use at your own risk. + +## Development +Please feel free to create an issue for any problem you encounter using this software. Pull requests are welcome. + +### Direction for the future +Current development focuses on bringing LibraryNB to more platforms and increasing the stability of the application. + +### Design Decisions +LibraryNB does not use containers to provide consistent environments to run Jupyter Notebooks. It instead uses just Conda or Mamba to create virtual environments. + +Additionaly LibraryNB does not impose a specific git workflow upon the user. LibraryNB only clones repositories. git should work like normal on the cloned repository. + +### Integration with Jupyter Book + +LibraryNB can read a "_config.yml" files in a repository to provide information about a repository to the user. This includes a title, author, thumbnail, logo, and copyright info. + + -## Installation diff --git a/main.js b/main.js index 2fdb7b5..38d62ca 100644 --- a/main.js +++ b/main.js @@ -3,7 +3,9 @@ const { createLibraryWindow, createJupyterWindow, createSetupWindow } = require( const config = require("./src/config.js").getConfig(); const { registerProtocol } = require("./src/protocol.js"); -if (require('electron-squirrel-startup')) return; +if (require('electron-squirrel-startup')) { + app.quit(); +} app.whenReady().then(() => { if(! config) { diff --git a/src/book.js b/src/book.js index 06b5755..cf5d62c 100644 --- a/src/book.js +++ b/src/book.js @@ -65,19 +65,18 @@ function bookFromConfig(config, projectDir) { function loadBooks() { let books = []; - let globPath = '' - if (os === 'win32') { //Glob only uses posix paths - let prefix = path.normalize(repoDir).replaceAll("\\", "/").substring(2); - globPath = path.posix.join(prefix, '*', '*', '*'); - } else { - globPath = path.join(repoDir, '*', '*', '*'); - } + let globPath = ""; + if (os === "win32") { //Glob only uses posix paths + let prefix = path.normalize(repoDir).replaceAll("\\", "/").substring(2); + globPath = path.posix.join(prefix, "*", "*", "*"); + } else { + globPath = path.join(repoDir, "*", "*", "*"); + } const projectDirs = glob.sync(globPath); for (let i in projectDirs) { let configFile = fs.readFileSync(path.join(projectDirs[i], "_config.yml"), "utf8"); const config = YAML.parse(configFile); books.push(bookFromConfig(config, projectDirs[i])); - fs.closeSync(configFile); } return books; } @@ -222,13 +221,13 @@ async function launchBook(fullPath) { //let output = ""; //for await (const chunk of launcher.stdout) { - //output += chunk; + //output += chunk; //} //let pid = parseInt(output.trim()); //let exitCode = await proc.endProcess(launcher); //if (exitCode) { - //throw `launcher process exited with code ${exitCode}`; + //throw `launcher process exited with code ${exitCode}`; //} let jupyterUrl = await getJupyterUrl(fullPath); diff --git a/src/config.js b/src/config.js index 618edd8..b318174 100644 --- a/src/config.js +++ b/src/config.js @@ -11,7 +11,7 @@ function getConfigPath() { } function validate(config) { - let errors = [] + let errors = []; if (errors.length === 0){ return {success: true}; } else { @@ -20,29 +20,29 @@ function validate(config) { } function writeConfig(config) { - let results = validate(config) + let results = validate(config); if (!results.success) { - return results + return results; } try { - fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2)) + fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2)); } catch (error) { - throw `Unable to write to config file: ${error}` + throw `Unable to write to config file: ${error}`; } return results; } function createConfig(config) { if (!configExists()) { - let configPath = getConfigPath() - let directory = path.dirname(configPath) + let configPath = getConfigPath(); + let directory = path.dirname(configPath); if (! fs.existsSync(directory)){ - if (!fs.mkdirSync(directory, { recursive: true })) { - throw "Config directory does not exist and unable to create it" + if (!fs.mkdirSync(directory, { recursive: true })) { + throw "Config directory does not exist and unable to create it"; } } } - return writeConfig(config) + return writeConfig(config); } function addToWhiteList(url) { @@ -50,13 +50,13 @@ function addToWhiteList(url) { throw "url must be a string"; } let config = getConfig(); - if (! whiteList in config){ + if (!("whiteList" in config)){ config.whiteList = [url]; } else { - config.whiteList.push(url) + config.whiteList.push(url); } - writeConfig(config) - return true + writeConfig(config); + return true; } function configExists() { diff --git a/src/proc.js b/src/proc.js index d82d223..11c34b7 100644 --- a/src/proc.js +++ b/src/proc.js @@ -70,7 +70,7 @@ function endProcess(process){ function getShell() { if (os === "linux"){ return "/bin/bash"; - } else if (os === 'win32') { + } else if (os === "win32") { return true; } else { throw "Coming soon to other operating systems"; @@ -80,7 +80,7 @@ function getShell() { function getLaunchScript() { if (os === "linux") { return linuxSetupScript + linuxLaunchScript; - } else if(os === 'win32'){ + } else if(os === "win32"){ return winCondaSetup + winLaunchScript; } else { throw "Coming soon to other operating systems"; @@ -90,7 +90,7 @@ function getLaunchScript() { function getListScript() { if (os === "linux") { return linuxSetupScript + linuxListScript; - } else if(os === 'win32'){ + } else if(os === "win32"){ return winCondaSetup + winListScript; } else { throw "Coming soon to other operating systems"; @@ -100,7 +100,7 @@ function getListScript() { function getEnvScript() { if (os === "linux") { return linuxSetupScript + linuxEnvScript; - } else if(os === 'win32'){ + } else if(os === "win32"){ return winCondaSetup + winEnvScript; } else { throw "Coming soon to other operating systems"; @@ -110,7 +110,7 @@ function getEnvScript() { function getRemoveScript() { if(os === "linux") { return linuxSetupScript + linuxRemoveScript; - } else if(os === 'win32'){ + } else if(os === "win32"){ return winCondaSetup + winRemoveScript; } else { throw "Coming soon to other operating systems"; diff --git a/src/windows.js b/src/windows.js index a95ba56..355f776 100644 --- a/src/windows.js +++ b/src/windows.js @@ -55,7 +55,7 @@ async function createJupyterWindow (url) { const jupyter = await launchBook(fullPath); jupyterWindow.loadURL(jupyter.url); jupyterWindow.on("close", function(){ - jupyter.server.kill() + jupyter.server.kill(); jupyterWindow = null; }); } @@ -81,6 +81,10 @@ async function createLibraryWindow() { return { action: "deny" }; }); + ipcMain.on("launch-book", (event, url) => { + createJupyterWindow(url); + }); + ipcMain.on("open-repo", (e, url) => { shell.openExternal(url); }); @@ -115,10 +119,6 @@ async function createLibraryWindow() { let books = loadBooks(path.join(repoDir)); win.webContents.send("load-books", books); }); - - ipcMain.on("launch-book", (event, url) => { - createJupyterWindow(url); - }); } function createSetupWindow() { @@ -134,12 +134,12 @@ function createSetupWindow() { }); ipcMain.on("save", (event, config) => { - let results = createConfig(config) - if (results.success){ - win.close(); - } else { - win.webContents.send('display-errors', results.errors); - } + let results = createConfig(config); + if (results.success){ + win.close(); + } else { + win.webContents.send("display-errors", results.errors); + } }); ipcMain.handle("dialog:selectDir", async () => {