├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── README.md ├── assets ├── evm-install.gif └── switch-versions.gif ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | assets 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.4 (November 20, 2016) 2 | 3 | [Add Windows Support](https://github.com/mattludwigs/elm-version-manager/pull/4) by @ryanplant-au 4 | 5 | # 1.0.0 (November 18. 2016) 6 | 7 | Initial Release 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First off, I would love for contributors! This package works closely we the system, and since everyone's system is different it hard to account for possible systems. More people who try out evm and report back any issue they run into, the better this package can be. 4 | 5 | Open source does not succeed because of its maintainers, but through the people who contribute back. However, a maintainer, or maintainers, can kill a project, so I will do my part in trying to keep up with this package. 6 | 7 | ## Getting set up: 8 | 9 | ``` 10 | $ git clone git@github.com:mattludwigs/elm-version-manager.git 11 | $ cd elm-version-manager 12 | $ npm install 13 | ``` 14 | 15 | Then you should be able to `./index.js -h` and other commands. 16 | 17 | 18 | ## Error Messages 19 | 20 | The biggest thing I am going to look for in any PR that tries to handle an error message is this that is clear and tries to provide a helpful fix when there is one. Elm as a language is regarded as having some of the best error messages, and I want this tool to reflect that as well. 21 | 22 | 23 | ## No ES6 24 | 25 | I am not using any ES6 or Babel for two reasons. First, having a build for this type of package just to compile to another version of ES did not seem needed and just would add complexity for no gain. Second, since we are not compiling to ES5, this package should be written ES5 and try to support the smallest node version possible from Node 3 and up. I want to say currently it should work with Node 4+, but I would love to get this down to Node 3. 26 | 27 | 28 | ## Avoid Needless Abstractions 29 | 30 | I want this code to be simple and nice to work in. We are not building a massive node project, so a lot of the abstractions we might normally use are not really needed. So, before adding an abstraction try to think through if it really need. Clarity over brevity should be the standard for this package. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elm Version Manager (EVM) 2 | 3 | **Note:** I will be rewriting the package summer this year (2017). My goal is to keep the CLI commands the same but the internals will work a little different. This will address all the issues filed. 4 | 5 | A package that helps manage the local version Elm. 6 | 7 | Now that Elm is being used in production and is having more releases, many people will run into supporting older Elm projects, while at the same time wanting to use the newest version for a new project, at least until they are able to upgrade the older ones. Also, if you have a production project and an open source Elm package that are using an older version, you will need to manually do the path switching to different elm platform executables, or have a script to do it for you, if you want to upgrade your Elm package while supporting the production project. Neither is quite shareable or easy, so this cli is trying to make switching elm versions locally a breeze. 8 | 9 | 10 | ### To install 11 | 12 | ``` 13 | $ npm install -g elm-version-manager 14 | ``` 15 | 16 | ### Example Installing 17 | 18 | ![alt text](https://github.com/mattludwigs/elm-version-manager/raw/master/assets/evm-install.gif "evm install example") 19 | 20 | ### Example Switching 21 | 22 | ![alt text](https://github.com/mattludwigs/elm-version-manager/raw/master/assets/switch-versions.gif "evm use example") 23 | 24 | 25 | ## System Requirements 26 | 27 | Node 4.0+ 28 | 29 | ## What about elmenv? 30 | 31 | [elmenv](https://github.com/sonnym/elmenv) should be just fine. However, it looks like from the issues, that new versions of Elm are not automatically included and the maintainer has to go and add those, which can be a blocker for people wanting to switch versions as they come out. With evm everything should stay synced up with the latest versions, so as new releases are cut but the Elm core team you can install them via evm. 32 | 33 | ## Install a version of Elm 34 | 35 | ``` 36 | $ evm install 0.18.0 37 | ``` 38 | 39 | ## Use a version of Elm 40 | 41 | ``` 42 | $ evm use 0.18.0 43 | ``` 44 | 45 | ## List versions of Elm installed 46 | 47 | ``` 48 | $ evm ls 49 | 50 | - 0.18.0 51 | - 0.17.1 52 | ``` 53 | 54 | ``` 55 | $ evm list 56 | 57 | - 0.18.0 58 | - 0.17.1 59 | ``` 60 | 61 | ## List Remote 62 | 63 | List out the remote versions 64 | 65 | ``` 66 | $ evm list-remote 67 | 68 | - 0.15.1 69 | - 0.16.0 70 | - 0.16 71 | - 0.17.0 72 | - 0.17.1 73 | - 0.18.0 74 | - master 75 | ``` 76 | 77 | ``` 78 | $ evm lsrm 79 | 80 | - 0.15.1 81 | - 0.16.0 82 | - 0.16 83 | - 0.17.0 84 | - 0.17.1 85 | - 0.18.0 86 | - master 87 | ``` 88 | 89 | ## Remove a version of Elm 90 | 91 | ``` 92 | $ evm remove 0.18.0 93 | ``` 94 | 95 | ## Remove All 96 | 97 | This is mainly help in development 98 | 99 | ``` 100 | $ evm remove-all 101 | ``` 102 | 103 | ## Help 104 | 105 | ``` 106 | $ evm -h 107 | ``` 108 | 109 | ``` 110 | $ evm --help 111 | ``` 112 | 113 | ## Version 114 | 115 | ``` 116 | $ evm -v 117 | ``` 118 | 119 | ``` 120 | $ evm --version 121 | ``` 122 | 123 | 124 | ## Contributing 125 | 126 | [Contributing](CONTRIBUTING.md) 127 | -------------------------------------------------------------------------------- /assets/evm-install.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattludwigs/elm-version-manager/a2f2ab6e57c337c0722b185d0be4306d263e6566/assets/evm-install.gif -------------------------------------------------------------------------------- /assets/switch-versions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattludwigs/elm-version-manager/a2f2ab6e57c337c0722b185d0be4306d263e6566/assets/switch-versions.gif -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var Promise = require("promise"); 6 | var zlib = require("zlib"); 7 | var tar = require("tar"); 8 | var request = require("request"); 9 | var fs = require("fs-extra"); 10 | var os = require("os"); 11 | var program = require("commander"); 12 | var chalk = require("chalk"); 13 | 14 | var URL_BASE = "https://dl.bintray.com/elmlang/elm-platform/"; 15 | var EVM_DIR = os.homedir() + "/evm"; 16 | 17 | var packageJson = require("./package.json"); 18 | var arch = process.arch; 19 | var operatingSystem = process.platform; 20 | var filename = operatingSystem + "-" + arch + ".tar.gz"; 21 | var versionPattern = /([0-9]\D.[0-9].?([0-9]?))|(master)/g 22 | 23 | if (process.platform === 'win32') { 24 | var usrBin = os.homedir() + "/Appdata/Local/Elm Version Manager"; 25 | var elmBins = [ 26 | "elm.exe", 27 | "elm-make.exe", 28 | "elm-repl.exe", 29 | "elm-package.exe", 30 | "elm-reactor.exe" 31 | ]; 32 | } else { 33 | var usrBin = "/usr/local/bin"; 34 | var elmBins = [ 35 | "elm", 36 | "elm-make", 37 | "elm-repl", 38 | "elm-package", 39 | "elm-reactor" 40 | ]; 41 | } 42 | 43 | 44 | var hasEvmDir = function() { 45 | return new Promise(function(resolve, reject) { 46 | fs.readdir(homeDir, function(error, paths) { 47 | var hasDir = paths.some(function(p) { 48 | return p === "evm"; 49 | }); 50 | 51 | if (hasDir) { 52 | reject("Has evm dir"); 53 | } else { 54 | resolve(); 55 | } 56 | }); 57 | }); 58 | } 59 | 60 | var createEvmDir = function() { 61 | console.log("INFO creating evm folder"); 62 | 63 | return new Promise(function(resolve, reject) { 64 | var homeDir = os.homedir(); 65 | fs.mkdir(EVM_DIR, function(err) { 66 | if (err) { 67 | reject(err); 68 | return; 69 | } 70 | resolve(); 71 | }); 72 | }); 73 | } 74 | 75 | 76 | var isVersionInstall = function(version) { 77 | return fs.readdirSync(EVM_DIR) 78 | .some(function(file) { 79 | return file === version; 80 | }); 81 | } 82 | 83 | 84 | var listEvmDir = function() { 85 | fs.readdir(EVM_DIR, function(err, files) { 86 | if (files === undefined || files.length === 0) { 87 | console.log(chalk.yellow("😱 Oh no! It looks like you don't have any versions of Elm installed. try running:\n\nevm install 0.18.0\n\nThis will install the newest version of Elm for you. Then just run:\n\nevm use 0.18.0\n\nThen you are ready to get rolling! 🚀")); 88 | return; 89 | } 90 | files.forEach(function(f) { 91 | console.log(chalk.yellow("- " + f)); 92 | }); 93 | }); 94 | } 95 | 96 | 97 | var use = function(version) { 98 | if (isVersionInstall(version)) { 99 | console.log(chalk.yellow("Using: " + version + "\n")); 100 | 101 | elmBins.forEach(function(bin) { 102 | fs.unlink(usrBin + '/' + bin, function(err) { 103 | if (err && err.code !== "ENOENT") { 104 | console.log(chalk.red(err.message)); 105 | return; 106 | } 107 | 108 | if (process.platform === 'win32') { 109 | fs.copySync(EVM_DIR + "/" + version + "/" + bin, usrBin + "/" + bin); 110 | } else { 111 | fs.symlinkSync(EVM_DIR + "/" + version + "/" + bin, usrBin + '/' + bin); 112 | } 113 | 114 | console.log(chalk.green("🚀 " + bin + " good to go!")); 115 | }); 116 | }); 117 | } else { 118 | console.log(chalk.red(version + " appears to not be installed, to install please run:\n\n" + "evm install " + version + "\n")); 119 | } 120 | } 121 | 122 | 123 | var install = function(version) { 124 | var url = URL_BASE + version + "/" + filename; 125 | var desPath = EVM_DIR + "/" + version; 126 | 127 | console.log(chalk.yellow("Downloading Elm binaries from " + url)); 128 | 129 | request.get(url, function(err, response) { 130 | if (err) { 131 | console.log(chalk.red("Error communicating with URL: " + url + " " + error)); 132 | } 133 | 134 | if (response.statusCode == 404) { 135 | console.log(chalk.red("Unfortunately, there are currently no Elm Platform binaries available for your operating system and architecture.\n\nIf you would like to build Elm from source, there are instructions at https://github.com/elm-lang/elm-platform#build-from-source\n")); 136 | return; 137 | } 138 | }) 139 | .pipe(zlib.createGunzip()) 140 | .pipe(tar.Extract({path: desPath, strip: 1}) 141 | .on("end", function() { 142 | console.log(chalk.yellow("\nDownloaded " + version + " 🍻\n")); 143 | 144 | elmBins.forEach(function(exec) { 145 | fs.stat(desPath + "/" + exec, function(err, stat) { 146 | if (err) { 147 | console.log(chalk.red("Error " + err.message)); 148 | return; 149 | } else if (!stat.isFile()) { 150 | console.log(chalk.red("Error file: " + exec + "is not a file")); 151 | return; 152 | } else { 153 | console.log(chalk.green("☁️ 🚀 ☁️ installed " + exec + " for version: " + version)); 154 | } 155 | }); 156 | }); 157 | }) 158 | ); 159 | } 160 | 161 | 162 | var remove = function(version) { 163 | console.log(chalk.yellow("Removing Elm version: " + version + "\n")); 164 | fs.remove(EVM_DIR + "/" + version, function(err) { 165 | if (err) { 166 | console.log(chalk.red(err.message)); 167 | return; 168 | } 169 | console.log(chalk.yellow("Ok " + version + " is removed")); 170 | }); 171 | } 172 | 173 | 174 | var removeAll = function() { 175 | console.log(chalk.yellow("Removing all Elm versions\n")); 176 | try { 177 | var versions = fs.readdirSync(EVM_DIR); 178 | versions.forEach(remove); 179 | } finally { 180 | console.log(chalk.yellow("Ok")); 181 | } 182 | } 183 | 184 | 185 | var listRemote = function() { 186 | console.log(chalk.yellow("Getting information from server ⛵\n")); 187 | request.get(URL_BASE, function(err, response) { 188 | if (err) { 189 | console.log(chalk.red("Error communicating with URL: " + url + " " + error)); 190 | }; 191 | 192 | if (response.statusCode == 404) { 193 | console.log(chalk.red("Unfortunately, the package binaries cannot be found")); 194 | return; 195 | } 196 | 197 | response.body.split(" ") 198 | .filter(function(s) { 199 | return s.indexOf("href=") > -1; 200 | }) 201 | .forEach(function(version) { 202 | var v = version.match(versionPattern)[0].replace("/", ""); 203 | console.log(chalk.yellow("- " + v)); 204 | }); 205 | }); 206 | } 207 | 208 | 209 | var help = function() { 210 | if (arguments[0]) { 211 | console.log(chalk.yellow("\n Unknown command: `" + arguments[0] + "` 👈\n")); 212 | } 213 | 214 | program.help(); 215 | } 216 | 217 | 218 | program 219 | .version(packageJson.version) 220 | .command("install ") 221 | .description("installs version") 222 | .alias("i") 223 | .action(function(version) { 224 | hasEvmDir() 225 | .then(function() { 226 | createEvmDir() 227 | .then(function() { 228 | install(version); 229 | }) 230 | }, function(err) { 231 | install(version); 232 | }); 233 | }); 234 | 235 | program 236 | .command("list") 237 | .alias("ls") 238 | .description("lists out versions installed") 239 | .action(listEvmDir); 240 | 241 | 242 | program 243 | .command("remove ") 244 | .description("removes a version that is installed") 245 | .alias("rm") 246 | .action(remove); 247 | 248 | 249 | program 250 | .command("use ") 251 | .description("Sets up system to use a certain version of elm") 252 | .action(use); 253 | 254 | program 255 | .command("list-remote") 256 | .alias("lsrm") 257 | .description("Lists the possible versions to download from") 258 | .action(listRemote); 259 | 260 | 261 | program 262 | .command("remove-all") 263 | .alias("rma") 264 | .description("removes all of your Elm versions") 265 | .action(removeAll) 266 | 267 | 268 | program 269 | .command("help", "Display help information", {isDefault: true}) 270 | .action(help); 271 | 272 | 273 | program.parse(process.argv); 274 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elm-version-manager", 3 | "version": "1.0.4", 4 | "description": "CLI for managing Elm versions on your local system", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "preferGlobal": true, 8 | "scripts": { 9 | "clean": "rm -r ~/evm", 10 | "list:evm": "ls ~/evm", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "bin" : { 14 | "evm": "index.js" 15 | }, 16 | "author": { 17 | "name": "Matt Ludwigs", 18 | "url": "https://github.com/mattludwigs" 19 | }, 20 | "engines": { 21 | "node": ">=4.0.0" 22 | }, 23 | "dependencies": { 24 | "chalk": "^1.1.3", 25 | "commander": "^2.9.0", 26 | "fs-extra": "^1.0.0", 27 | "mkdirp": "^0.5.1", 28 | "promise": "^7.1.1", 29 | "request": "^2.78.0", 30 | "tar": "^2.2.1" 31 | }, 32 | "keywords": [ 33 | "elm", 34 | "version management", 35 | "elm-lang" 36 | ] 37 | } 38 | --------------------------------------------------------------------------------