all repos — min @ 3f3149f2f2b715355d01c3d73a0732edb7806cf3

A small but practical concatenative programming language.

Implemented install and remove.
h3rald h3rald@h3rald.com
Wed, 29 Nov 2023 14:46:53 +0100
commit

3f3149f2f2b715355d01c3d73a0732edb7806cf3

parent

c117e1e523c2c549128c8ed21e4124433787ee8e

2 files changed, 170 insertions(+), 44 deletions(-)

jump to
M min.nimmin.nim

@@ -122,6 +122,7 @@ minpkg/core/meta

var REPL = false var MODULEPATH = "" + var GLOBAL = false proc resolveFile(file: string): string = if (file.endsWith(".min") or file.endsWith(".mn")) and fileExists(file):

@@ -149,6 +150,7 @@ Options:

-a, --asset-path Specify a directory containing the asset files to include in the compiled executable (if -c is set) -d, --dev Enable "development mode" (runtime checks) + -g, --global Execute the specified command (install or remove) globally. -h, --help Print this help -i, --interactive Start $exe shell (with advanced prompt, default if no file specidied)" -j, --interactive-simple Start $exe shell (without advanced prompt)

@@ -182,6 +184,8 @@ of "module-path", "m":

MODULEPATH = val of "asset-path", "a": ASSETPATH = val + of "global", "g": + GLOBAL = true of "prelude", "p": customPrelude = val of "dev", "d":

@@ -226,34 +230,66 @@ if fn == "":

if file == "compile": op = "compile" if args.len < 2: - logging.error "[compile] No file was specified." + logging.error "No file was specified." quit(8) fn = resolveFile(args[1]) if fn == "": - logging.error "[compile] File '$#' does not exist." % [args[1]] + logging.error "File '$#' does not exist." % [args[1]] quit(9) elif file == "eval": if args.len < 2: - logging.error "[eval] No string to evaluate was specified." + logging.error "No string to evaluate was specified." quit(9) minStr args[1] quit(0) elif file == "help": if args.len < 2: - logging.error "[help] No symbol to lookup was specified." + logging.error "No symbol to lookup was specified." quit(9) minStr("\"$#\" help" % [args[1]]) quit(0) elif file == "init": - MMM.setup() - MMM.init() - quit(0) + try: + MMM.setup() + MMM.init() + quit(0) + except CatchableError: + error getCurrentExceptionMsg() + quit(10) elif file == "install": - logging.error "[install] Not implemented." - quit(100) + if args.len < 2: + logging.error "Module name not specified." + quit(10) + if args.len < 3: + logging.error "Module version not specified." + debug getCurrentException().getStackTrace() + quit(11) + let name = args[1] + let version = args[2] + try: + MMM.setup() + MMM.install(name, version, GLOBAL) + quit(0) + except CatchableError: + error getCurrentExceptionMsg() + debug getCurrentException().getStackTrace() + quit(10) elif file == "remove": - logging.error "[remove] Not implemented." - quit(100) + if args.len < 2: + logging.error "Module name not specified." + quit(10) + let name = args[1] + var version = "" + if args.len > 2: + version = args[2] + try: + MMM.setup() + MMM.remove(name, version, GLOBAL) + quit(0) + except CatchableError: + error getCurrentExceptionMsg() + debug getCurrentException().getStackTrace() + quit(10) elif file == "update": logging.error "[update] Not implemented." quit(100)
M minpkg/core/mmm.nimminpkg/core/mmm.nim

@@ -3,6 +3,7 @@ std/[json,

os, httpclient, strutils, + sequtils, logging ] import

@@ -16,9 +17,13 @@ registry: string

modules = %[] globalDir: string localDir: string + MMMError = ref object of CatchableError -proc setup*(MMM: var MinModuleManager) = +proc raiseError(msg: string) = + raise MMMError(msg: msg) + +proc setup*(MMM: var MinModuleManager, check = true) = MMM.registry = MMMREGISTRY MMM.globalDir = HOME / "mmm" MMM.localDir = getCurrentDir() / "mmm"

@@ -34,52 +39,137 @@ let data = parseFile(mmmJson)

updatedLocal = data["updated"].getInt MMM.modules = data["modules"] except CatchableError: - logging.debug getCurrentExceptionMsg() - error "Invalid local registry data ($#)" % [mmmJson] - let client = newHttpClient() - # Check remote data - var updatedRemote = 0 - try: - logging.debug "Checking remote registry..." - updatedRemote = client.getContent(MMMREGISTRY & "/mmm.timestamp").parseInt - logging.debug "Remote registry timestamp retrieved." - except CatchableError: - logging.debug getCurrentExceptionMsg() - logging.warn "Unable to connect to remote registry ($#)" % [MMMREGISTRY] - if updatedRemote > updatedLocal: - logging.notice "Updating local module registry..." + debug getCurrentExceptionMsg() + raiseError "Invalid local registry data ($#)" % [mmmJson] + if check: + let client = newHttpClient() + # Check remote data + var updatedRemote = 0 try: - client.downloadFile(MMMREGISTRY & "/mmm.json", mmmJson) + debug "Checking remote registry" + updatedRemote = client.getContent(MMMREGISTRY & "/mmm.timestamp").parseInt + debug "Remote registry timestamp retrieved." except CatchableError: - logging.debug getCurrentExceptionMsg() - logging.warn "Unable to download remote registry data ($#)" % [MMMREGISTRY & "/mmm.json"] - try: - let data = parseFile(mmmJson) - MMM.modules = data["modules"] - except CatchableError: - logging.debug getCurrentExceptionMsg() - error "Invalid local registry data ($#)" % [mmmJson] - else: - logging.debug "Local registry up-to-date: $#" % [$updatedLocal] + debug getCurrentExceptionMsg() + warn "Unable to connect to remote registry ($#)" % [MMMREGISTRY] + if updatedRemote > updatedLocal: + notice "Updating local module registry" + try: + client.downloadFile(MMMREGISTRY & "/mmm.json", mmmJson) + except CatchableError: + warn "Unable to download remote registry data ($#)" % [MMMREGISTRY & "/mmm.json"] + debug getCurrentExceptionMsg() + try: + let data = parseFile(mmmJson) + MMM.modules = data["modules"] + except CatchableError: + debug getCurrentExceptionMsg() + raiseError "Invalid local registry data ($#)" % [mmmJson] + else: + debug "Local registry up-to-date: $#" % [$updatedLocal] proc init*(MMM: var MinModuleManager) = let pwd = getCurrentDir() if dirExists(pwd / "mmm.json"): - error "The current directory already contains a managed module (mmm.json already exists)" - logging.debug "Creating mmm.json file" + raiseError "The current directory already contains a managed module (mmm.json already exists)" + debug "Creating mmm.json file" let json = """ { "name": "$#", "method": "git", - "url": "", - "author": "", - "description": "", - "license": "", + "url": "TBD", + "author": "TBD", + "description": "TBD", + "license": "MIT", "deps": {} } """ % [pwd.lastPathPart] writeFile(pwd / "mmm.json", json) if not dirExists(pwd / "mmm"): - logging.debug "Creating mmm directory" + debug "Creating mmm directory" createDir(pwd / "mmm") +proc remove*(MMM: var MinModuleManager, name, version: string, global = false) = + var dir: string + var versionLabel = version + if version == "": + versionLabel = "<all-versions>" + if global: + dir = MMM.globalDir / name + else: + dir = MMM.localDir / name + else: + if global: + dir = MMM.globalDir / name / version + else: + dir = MMM.localDir / name / version + if not dir.dirExists(): + raiseError "Module '$#' (version: $#) is not installed." % [name, versionLabel] + notice "Removing module $#@$#..." % [name, versionLabel] + try: + dir.removeDir() + if version != "" and dir.parentDir().walkDir().toSeq().len == 0: + # Remove parent directory if no versions are installed + dir.parentDir().removeDir() + except CatchableError: + debug getCurrentExceptionMsg() + raiseError "Unable to remove module $#@$#" % [name, versionLabel] + notice "Removal complete." + +proc install*(MMM: var MinModuleManager, name, version: string, global = false) = + var dir: string + if global: + dir = MMM.globalDir / name / version + else: + dir = MMM.localDir / name / version + if dir.dirExists(): + raiseError "Module '$#' (version: $#) is already installed." % [name, version] + dir.createDir() + let results = MMM.modules.filterIt(it.hasKey("name") and it["name"] == %name) + if results.len == 0: + raiseError "Unknown module '$#'." % [name] + let data = results[0] + if not data.hasKey("method"): + raiseError "Installation method not specified for module '$#'" % [name] + let meth = data["method"].getStr + if meth != "git": + raiseError "Unable to install module '$#': Installation method '$#' is not supported" % [name, meth] + if not data.hasKey("url"): + raiseError "URL not specified for module '$#'" % [name] + let url = data["url"].getStr + let cmd = "git clone $# -b $# --depth 1 \"$#\"" % [url, version, dir.replace("\\", "/")] + debug cmd + notice "Installing module $#@$#..." % [name, version] + if not data.hasKey("deps"): + raiseError "Dependencies not specified for module '$#'" % [name] + var result = execShellCmd(cmd) + if (result == 0): + # Go to directory and install dependencies + let originalDir = getCurrentDir() + dir.setCurrentDir() + MMM.setup(false) + if data["deps"].pairs.toSeq().len > 0: + notice "Installing dependencies..." + for depName, depVersion in data["deps"].pairs: + try: + MMM.install depName, depVersion.getStr, global + except CatchableError: + warn getCurrentExceptionMsg() + originalDir.setCurrentDir() + result = 1 + break + if result != 0: + # Rollback + warn "Installation failed - Rolling back..." + try: + MMM.setup(false) + MMM.remove(name, version, global) + notice "Rollback completed." + except: + debug getCurrentExceptionMsg() + warn "Rollback failed." + finally: + raiseError "Installation failed." + + +