diff --git a/readme.markdown b/readme.markdown index 8a32227..e8972d7 100644 --- a/readme.markdown +++ b/readme.markdown @@ -297,6 +297,10 @@ nimbleDir = r"C:\Nimble\" [PackageList] name = "CustomPackages" url = "http://mydomain.org/packages.json" + +[PackageList] +name = "Local project packages" +path = r"C:\Projects\Nim\packages.json" ``` You can currently configure the following in this file: @@ -307,12 +311,13 @@ You can currently configure the following in this file: application packages. If ``true`` this will add ``chcp 65001`` to the .cmd stubs generated in ``~/.nimble/bin/``. **Default:** ``true`` -* ``[PackageList]`` + ``name`` + ``url`` - You can use this section to specify +* ``[PackageList]`` + ``name`` + (``url``|``path``) - You can use this section to specify a new custom package list. Multiple package lists can be specified. Nimble defaults to the "Official" package list, you can override it by specifying a ``[PackageList]`` section named "official". Multiple URLs can be specified under each section, Nimble will try each in succession if - downloading from the first fails. + downloading from the first fails. Alternately, ``path`` can specify a + local file path to copy a package list .json file from. * ``cloneUsingHttps`` - Whether to replace any ``git://`` inside URLs with ``https://``. **Default: true** diff --git a/src/nimble.nim b/src/nimble.nim index 9b68367..6770694 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -48,17 +48,17 @@ proc refresh(options: Options) = if parameter.len > 0: if parameter.isUrl: let cmdLine = PackageList(name: "commandline", urls: @[parameter]) - downloadList(cmdLine, options) + fetchList(cmdLine, options) else: if parameter notin options.config.packageLists: let msg = "Package list with the specified name not found." raise newException(NimbleError, msg) - downloadList(options.config.packageLists[parameter], options) + fetchList(options.config.packageLists[parameter], options) else: # Try each package list in config for name, list in options.config.packageLists: - downloadList(list, options) + fetchList(list, options) proc checkInstallFile(pkgInfo: PackageInfo, origDir, file: string): bool = diff --git a/src/nimblepkg/config.nim b/src/nimblepkg/config.nim index b38c157..4f2ab1b 100644 --- a/src/nimblepkg/config.nim +++ b/src/nimblepkg/config.nim @@ -15,6 +15,7 @@ type PackageList* = object name*: string urls*: seq[string] + path*: string proc initConfig(): Config = result.nimbleDir = getHomeDir() / ".nimble" @@ -35,6 +36,7 @@ proc initConfig(): Config = proc initPackageList(): PackageList = result.name = "" result.urls = @[] + result.path = "" proc addCurrentPkgList(config: var Config, currentPackageList: PackageList) = if currentPackageList.name.len > 0: @@ -53,7 +55,6 @@ proc parseConfig*(): Config = if f != nil: display("Warning", "Using deprecated config file at " & confFile, Warning, HighPriority) - if f != nil: display("Reading", "config file at " & confFile, priority = LowPriority) var p: CfgParser @@ -64,6 +65,10 @@ proc parseConfig*(): Config = var e = next(p) case e.kind of cfgEof: + if currentPackageList.urls.len == 0 and currentPackageList.path == "": + raise newException(NimbleError, "Package list '$1' requires either url or path" % currentPackageList.name) + if currentPackageList.urls.len > 0 and currentPackageList.path != "": + raise newException(NimbleError, "Attempted to specify `url` and `path` for the same package list '$1'" % currentPackageList.name) addCurrentPkgList(result, currentPackageList) break of cfgSectionStart: @@ -97,6 +102,14 @@ proc parseConfig*(): Config = of "packagelist": currentPackageList.urls.add(e.value) else: assert false + of "path": + case currentSection.normalize + of "packagelist": + if currentPackageList.path.len > 0: + raise newException(NimbleError, "Attempted to specify more than one `path` for the same package list.") + else: + currentPackageList.path = e.value.normalize + else: assert false else: raise newException(NimbleError, "Unable to parse config file:" & " Unknown key: " & e.key) diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim index ca2e1f4..95d093e 100644 --- a/src/nimblepkg/packageinfo.nim +++ b/src/nimblepkg/packageinfo.nim @@ -160,53 +160,69 @@ proc validatePackagesList(path: string): bool = except ValueError, JsonParsingError: return false -proc downloadList*(list: PackageList, options: Options) = - ## Downloads the specified package list and saves it in $nimbleDir. - display("Downloading", list.name & " package list", priority = HighPriority) +proc fetchList*(list: PackageList, options: Options) = + ## Downloads or copies the specified package list and saves it in $nimbleDir. + let verb = if list.urls.len > 0: "Downloading" else: "Copying" + display(verb, list.name & " package list", priority = HighPriority) - var lastError = "" - for i in 0 .. 0: maskedUrl.password = "***" - display("Connecting", "to proxy at " & $maskedUrl, - priority = LowPriority) - - try: - downloadFile(url, tempPath, proxy = getProxy(options)) - except: - let message = "Could not download: " & getCurrentExceptionMsg() - display("Warning:", message, Warning) - lastError = message - continue - - if not validatePackagesList(tempPath): - lastError = "Downloaded packages.json file is invalid" - display("Warning:", lastError & ", discarding.", Warning) - continue - - copyFile(tempPath, - options.getNimbleDir() / "packages_$1.json" % list.name.toLowerAscii()) - display("Success", "Package list downloaded.", Success, HighPriority) + var lastError = "" - break + copyFromPath = "" + if list.urls.len > 0: + let httpclient = newHttpClient(proxy = getProxy(options)) + for i in 0 .. 0: maskedUrl.password = "***" + display("Connecting", "to proxy at " & $maskedUrl, + priority = LowPriority) + + try: + httpclient.downloadFile(url, tempPath) + except: + let message = "Could not download: " & getCurrentExceptionMsg() + display("Warning:", message, Warning) + lastError = message + continue + + if not validatePackagesList(tempPath): + lastError = "Downloaded packages.json file is invalid" + display("Warning:", lastError & ", discarding.", Warning) + continue + + copyFromPath = tempPath + display("Success", "Package list downloaded.", Success, HighPriority) + lastError = "" + break + + elif list.path != "": + if not validatePackagesList(list.path): + lastError = "Copied packages.json file is invalid" + display("Warning:", lastError & ", discarding.", Warning) + else: + copyFromPath = list.path + display("Success", "Package list copied.", Success, HighPriority) if lastError.len != 0: raise newException(NimbleError, "Refresh failed\n" & lastError) + if copyFromPath.len > 0: + copyFile(copyFromPath, + options.getNimbleDir() / "packages_$1.json" % list.name.toLowerAscii()) + proc readPackageList(name: string, options: Options): JsonNode = # If packages.json is not present ask the user if they want to download it. if needsRefresh(options): if options.prompt("No local packages.json found, download it from " & "internet?"): for name, list in options.config.packageLists: - downloadList(list, options) + fetchList(list, options) else: raise newException(NimbleError, "Please run nimble refresh.") return parseFile(options.getNimbleDir() / "packages_" & diff --git a/tests/issue368/packages.json b/tests/issue368/packages.json new file mode 100644 index 0000000..5cc425e --- /dev/null +++ b/tests/issue368/packages.json @@ -0,0 +1,14 @@ +[ + { + "name": "discordnim", + "url": "https://github.com/Krognol/discordnim", + "method": "git", + "tags": [ + "library", + "discord" + ], + "description": "Discord library for Nim", + "license": "MIT", + "web": "https://github.com/Krognol/discordnim" + } +] diff --git a/tests/tester.nim b/tests/tester.nim index 8cbf3d6..d4ecabc 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -33,7 +33,6 @@ proc execNimble(args: varargs[string]): tuple[output: string, exitCode: int] = quotedArgs = quoted_args.map((x: string) => ("\"" & x & "\"")) result = execCmdEx(quotedArgs.join(" ")) - #echo(result.output) proc processOutput(output: string): seq[string] = output.strip.splitLines().filter((x: string) => (x.len > 0)) @@ -138,9 +137,9 @@ proc safeMoveFile(src, dest: string) = copyFile(src, dest) removeFile(src) -test "can refresh with custom urls": +template testRefresh(body: untyped) = # Backup current config - let configFile = getConfigDir() / "nimble" / "nimble.ini" + let configFile {.inject.} = getConfigDir() / "nimble" / "nimble.ini" let configBakFile = getConfigDir() / "nimble" / "nimble.ini.bak" if fileExists(configFile): safeMoveFile(configFile, configBakFile) @@ -148,29 +147,73 @@ test "can refresh with custom urls": # Ensure config dir exists createDir(getConfigDir() / "nimble") - writeFile(configFile, """ - [PackageList] - name = "official" - url = "http://google.com" - url = "http://google.com/404" - url = "http://irclogs.nim-lang.org/packages.json" - url = "http://nim-lang.org/nimble/packages.json" - """.unindent) - - let (output, exitCode) = execNimble(["refresh", "--verbose"]) - let lines = output.strip.splitLines() - check exitCode == QuitSuccess - check inLines(lines, "config file at") - check inLines(lines, "official package list") - check inLines(lines, "http://google.com") - check inLines(lines, "packages.json file is invalid") - check inLines(lines, "404 not found") - check inLines(lines, "Package list downloaded.") + body # Restore config if fileExists(configBakFile): safeMoveFile(configBakFile, configFile) +test "can refresh with custom urls": + testRefresh(): + writeFile(configFile, """ + [PackageList] + name = "official" + url = "http://google.com" + url = "http://google.com/404" + url = "http://irclogs.nim-lang.org/packages.json" + url = "http://nim-lang.org/nimble/packages.json" + """.unindent) + + let (output, exitCode) = execNimble(["refresh", "--verbose"]) + let lines = output.strip.splitLines() + check exitCode == QuitSuccess + check inLines(lines, "config file at") + check inLines(lines, "official package list") + check inLines(lines, "http://google.com") + check inLines(lines, "packages.json file is invalid") + check inLines(lines, "404 not found") + check inLines(lines, "Package list downloaded.") + +test "can refresh with local package list": + testRefresh(): + writeFile(configFile, """ + [PackageList] + name = "local" + path = "$1" + """.unindent % (getCurrentDir() / "issue368" / "packages.json")) + let (output, exitCode) = execNimble(["refresh", "--verbose"]) + let lines = output.strip.splitLines() + check inLines(lines, "config file at") + check inLines(lines, "Copying") + check inLines(lines, "Package list copied.") + check exitCode == QuitSuccess + +test "package list source required": + testRefresh(): + writeFile(configFile, """ + [PackageList] + name = "local" + """) + let (output, exitCode) = execNimble(["refresh", "--verbose"]) + let lines = output.strip.splitLines() + check inLines(lines, "config file at") + check inLines(lines, "Package list 'local' requires either url or path") + check exitCode == QuitFailure + +test "package list can only have one source": + testRefresh(): + writeFile(configFile, """ + [PackageList] + name = "local" + path = "$1" + url = "http://nim-lang.org/nimble/packages.json" + """) + let (output, exitCode) = execNimble(["refresh", "--verbose"]) + let lines = output.strip.splitLines() + check inLines(lines, "config file at") + check inLines(lines, "Attempted to specify `url` and `path` for the same package list 'local'") + check exitCode == QuitFailure + test "can install nimscript package": cd "nimscript": check execNimble(["install", "-y"]).exitCode == QuitSuccess