Implements configurable package list URLs. Implements #75.
This commit is contained in:
parent
8d4c6446d1
commit
835157ce34
4 changed files with 164 additions and 38 deletions
|
|
@ -32,8 +32,6 @@ else:
|
||||||
|
|
||||||
const
|
const
|
||||||
nimbleVersion = "0.7.0"
|
nimbleVersion = "0.7.0"
|
||||||
defaultPackageURL =
|
|
||||||
"https://github.com/nim-lang/packages/raw/master/packages.json"
|
|
||||||
|
|
||||||
proc writeVersion() =
|
proc writeVersion() =
|
||||||
echo("nimble v$# compiled at $# $#" %
|
echo("nimble v$# compiled at $# $#" %
|
||||||
|
|
@ -55,16 +53,40 @@ proc promptCustom(question, default: string): string =
|
||||||
proc update(options: Options) =
|
proc update(options: Options) =
|
||||||
## Downloads the package list from the specified URL.
|
## Downloads the package list from the specified URL.
|
||||||
##
|
##
|
||||||
## If the download is successful, the global didUpdatePackages is set to
|
## If the download is not successful, an exception is raised.
|
||||||
## true. Otherwise an exception is raised on error.
|
let parameter =
|
||||||
let url =
|
if options.action.typ == actionUpdate:
|
||||||
if options.action.typ == actionUpdate and options.action.optionalURL != "":
|
|
||||||
options.action.optionalURL
|
options.action.optionalURL
|
||||||
else:
|
else:
|
||||||
defaultPackageURL
|
""
|
||||||
echo("Downloading package list from " & url)
|
|
||||||
downloadFile(url, options.getNimbleDir() / "packages.json")
|
proc downloadList(list: PackageList, options: Options) =
|
||||||
|
echo("Downloading \"", list.name, "\" package list")
|
||||||
|
for i in 0 .. <list.urls.len:
|
||||||
|
let url = list.urls[i]
|
||||||
|
echo("Trying ", url, "...")
|
||||||
|
let tempPath = options.getNimbleDir() / "packages_temp.json"
|
||||||
|
try:
|
||||||
|
downloadFile(url, tempPath)
|
||||||
|
except:
|
||||||
|
if i == <list.urls.len:
|
||||||
|
raise
|
||||||
|
echo("Could not download: ", getCurrentExceptionMsg())
|
||||||
|
continue
|
||||||
|
if not validatePackagesList(tempPath):
|
||||||
|
echo("Downloaded packages.json file is invalid, discarding.")
|
||||||
|
continue
|
||||||
|
copyFile(tempPath,
|
||||||
|
options.getNimbleDir() / "packages_$1.json" % list.name.toLower())
|
||||||
echo("Done.")
|
echo("Done.")
|
||||||
|
break
|
||||||
|
|
||||||
|
if parameter.isUrl:
|
||||||
|
downloadList(PackageList(name: "commandline", urls: @[parameter]), options)
|
||||||
|
else:
|
||||||
|
# Try each package list in config
|
||||||
|
for name, list in options.config.packageLists:
|
||||||
|
downloadList(list, options)
|
||||||
|
|
||||||
proc checkInstallFile(pkgInfo: PackageInfo,
|
proc checkInstallFile(pkgInfo: PackageInfo,
|
||||||
origDir, file: string): bool =
|
origDir, file: string): bool =
|
||||||
|
|
@ -461,13 +483,13 @@ proc getDownloadInfo*(pv: PkgTuple, options: Options,
|
||||||
return (checkUrlType(pv.name), pv.name)
|
return (checkUrlType(pv.name), pv.name)
|
||||||
else:
|
else:
|
||||||
var pkg: Package
|
var pkg: Package
|
||||||
if getPackage(pv.name, options.getNimbleDir() / "packages.json", pkg):
|
if getPackage(pv.name, options, pkg):
|
||||||
return (pkg.downloadMethod.getDownloadMethod(), pkg.url)
|
return (pkg.downloadMethod.getDownloadMethod(), pkg.url)
|
||||||
else:
|
else:
|
||||||
# If package is not found give the user a chance to update
|
# If package is not found give the user a chance to update
|
||||||
# package.json
|
# package.json
|
||||||
if doPrompt and
|
if doPrompt and
|
||||||
options.prompt(pv.name & " not found in local packages.json, " &
|
options.prompt(pv.name & " not found in any local packages.json, " &
|
||||||
"check internet for updated packages?"):
|
"check internet for updated packages?"):
|
||||||
update(options)
|
update(options)
|
||||||
return getDownloadInfo(pv, options, doPrompt)
|
return getDownloadInfo(pv, options, doPrompt)
|
||||||
|
|
@ -481,13 +503,13 @@ proc install(packages: seq[PkgTuple],
|
||||||
result = installFromDir(getCurrentDir(), false, options, "")
|
result = installFromDir(getCurrentDir(), false, options, "")
|
||||||
else:
|
else:
|
||||||
# If packages.json is not present ask the user if they want to download it.
|
# If packages.json is not present ask the user if they want to download it.
|
||||||
if not existsFile(options.getNimbleDir / "packages.json"):
|
if needsRefresh(options):
|
||||||
if doPrompt and
|
if doPrompt and
|
||||||
options.prompt("Local packages.json not found, download it from " &
|
options.prompt("No local packages.json found, download it from " &
|
||||||
"internet?"):
|
"internet?"):
|
||||||
update(options)
|
update(options)
|
||||||
else:
|
else:
|
||||||
quit("Please run nimble update.", QuitFailure)
|
quit("Please run nimble refresh.", QuitFailure)
|
||||||
|
|
||||||
# Install each package.
|
# Install each package.
|
||||||
for pv in packages:
|
for pv in packages:
|
||||||
|
|
@ -555,9 +577,9 @@ proc search(options: Options) =
|
||||||
assert options.action.typ == actionSearch
|
assert options.action.typ == actionSearch
|
||||||
if options.action.search == @[]:
|
if options.action.search == @[]:
|
||||||
raise newException(NimbleError, "Please specify a search string.")
|
raise newException(NimbleError, "Please specify a search string.")
|
||||||
if not existsFile(options.getNimbleDir() / "packages.json"):
|
if needsRefresh(options):
|
||||||
raise newException(NimbleError, "Please run nimble update.")
|
raise newException(NimbleError, "Please run nimble refresh.")
|
||||||
let pkgList = getPackageList(options.getNimbleDir() / "packages.json")
|
let pkgList = getPackageList(options)
|
||||||
var found = false
|
var found = false
|
||||||
template onFound: stmt =
|
template onFound: stmt =
|
||||||
echoPackage(pkg)
|
echoPackage(pkg)
|
||||||
|
|
@ -581,9 +603,9 @@ proc search(options: Options) =
|
||||||
echo("No package found.")
|
echo("No package found.")
|
||||||
|
|
||||||
proc list(options: Options) =
|
proc list(options: Options) =
|
||||||
if not existsFile(options.getNimbleDir() / "packages.json"):
|
if needsRefresh(options):
|
||||||
raise newException(NimbleError, "Please run nimble update.")
|
raise newException(NimbleError, "Please run nimble refresh.")
|
||||||
let pkgList = getPackageList(options.getNimbleDir() / "packages.json")
|
let pkgList = getPackageList(options)
|
||||||
for pkg in pkgList:
|
for pkg in pkgList:
|
||||||
echoPackage(pkg)
|
echoPackage(pkg)
|
||||||
if options.queryVersions:
|
if options.queryVersions:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Copyright (C) Dominik Picheta. All rights reserved.
|
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||||
# BSD License. Look at license.txt for more info.
|
# BSD License. Look at license.txt for more info.
|
||||||
import parsecfg, streams, strutils, os
|
import parsecfg, streams, strutils, os, tables
|
||||||
|
|
||||||
import tools, version, nimbletypes
|
import tools, version, nimbletypes
|
||||||
|
|
||||||
|
|
@ -8,7 +8,11 @@ type
|
||||||
Config* = object
|
Config* = object
|
||||||
nimbleDir*: string
|
nimbleDir*: string
|
||||||
chcp*: bool # Whether to change the code page in .cmd files on Win.
|
chcp*: bool # Whether to change the code page in .cmd files on Win.
|
||||||
|
packageLists*: Table[string, PackageList] ## URLs to packages.json files
|
||||||
|
|
||||||
|
PackageList* = object
|
||||||
|
name*: string
|
||||||
|
urls*: seq[string]
|
||||||
|
|
||||||
proc initConfig(): Config =
|
proc initConfig(): Config =
|
||||||
if getNimrodVersion() > newVersion("0.9.6"):
|
if getNimrodVersion() > newVersion("0.9.6"):
|
||||||
|
|
@ -18,6 +22,22 @@ proc initConfig(): Config =
|
||||||
|
|
||||||
result.chcp = true
|
result.chcp = true
|
||||||
|
|
||||||
|
result.packageLists = initTable[string, PackageList]()
|
||||||
|
let defaultPkgList = PackageList(name: "Official", urls: @[
|
||||||
|
"http://irclogs.nim-lang.org/packages.json",
|
||||||
|
"http://nim-lang.org/nimble/packages.json",
|
||||||
|
"https://github.com/nim-lang/packages/raw/master/packages.json"
|
||||||
|
])
|
||||||
|
result.packageLists["official"] = defaultPkgList
|
||||||
|
|
||||||
|
proc initPackageList(): PackageList =
|
||||||
|
result.name = ""
|
||||||
|
result.urls = @[]
|
||||||
|
|
||||||
|
proc addCurrentPkgList(config: var Config, currentPackageList: PackageList) =
|
||||||
|
if currentPackageList.name.len > 0:
|
||||||
|
config.packageLists[currentPackageList.name.normalize] = currentPackageList
|
||||||
|
|
||||||
proc parseConfig*(): Config =
|
proc parseConfig*(): Config =
|
||||||
result = initConfig()
|
result = initConfig()
|
||||||
var confFile = getConfigDir() / "nimble" / "nimble.ini"
|
var confFile = getConfigDir() / "nimble" / "nimble.ini"
|
||||||
|
|
@ -34,12 +54,23 @@ proc parseConfig*(): Config =
|
||||||
echo("Reading from config file at ", confFile)
|
echo("Reading from config file at ", confFile)
|
||||||
var p: CfgParser
|
var p: CfgParser
|
||||||
open(p, f, confFile)
|
open(p, f, confFile)
|
||||||
|
var currentSection = ""
|
||||||
|
var currentPackageList = initPackageList()
|
||||||
while true:
|
while true:
|
||||||
var e = next(p)
|
var e = next(p)
|
||||||
case e.kind
|
case e.kind
|
||||||
of cfgEof:
|
of cfgEof:
|
||||||
|
addCurrentPkgList(result, currentPackageList)
|
||||||
break
|
break
|
||||||
of cfgSectionStart: discard
|
of cfgSectionStart:
|
||||||
|
addCurrentPkgList(result, currentPackageList)
|
||||||
|
currentSection = e.section
|
||||||
|
case currentSection.normalize
|
||||||
|
of "packagelist":
|
||||||
|
currentPackageList = initPackageList()
|
||||||
|
else:
|
||||||
|
raise newException(NimbleError, "Unable to parse config file:" &
|
||||||
|
" Unknown section: " & e.key)
|
||||||
of cfgKeyValuePair, cfgOption:
|
of cfgKeyValuePair, cfgOption:
|
||||||
case e.key.normalize
|
case e.key.normalize
|
||||||
of "nimbledir":
|
of "nimbledir":
|
||||||
|
|
@ -48,6 +79,16 @@ proc parseConfig*(): Config =
|
||||||
result.nimbleDir = e.value
|
result.nimbleDir = e.value
|
||||||
of "chcp":
|
of "chcp":
|
||||||
result.chcp = parseBool(e.value)
|
result.chcp = parseBool(e.value)
|
||||||
|
of "name":
|
||||||
|
case currentSection.normalize
|
||||||
|
of "packagelist":
|
||||||
|
currentPackageList.name = e.value
|
||||||
|
else: assert false
|
||||||
|
of "url":
|
||||||
|
case currentSection.normalize
|
||||||
|
of "packagelist":
|
||||||
|
currentPackageList.urls.add(e.value)
|
||||||
|
else: assert false
|
||||||
else:
|
else:
|
||||||
raise newException(NimbleError, "Unable to parse config file:" &
|
raise newException(NimbleError, "Unable to parse config file:" &
|
||||||
" Unknown key: " & e.key)
|
" Unknown key: " & e.key)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Copyright (C) Dominik Picheta. All rights reserved.
|
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||||
# BSD License. Look at license.txt for more info.
|
# BSD License. Look at license.txt for more info.
|
||||||
import parsecfg, json, streams, strutils, parseutils, os
|
import parsecfg, json, streams, strutils, parseutils, os, sets, tables
|
||||||
import version, tools, nimbletypes, options
|
import version, tools, nimbletypes, options
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -127,25 +127,35 @@ proc readMetaData*(path: string): MetaData =
|
||||||
let jsonmeta = parseJson(cont)
|
let jsonmeta = parseJson(cont)
|
||||||
result.url = jsonmeta["url"].str
|
result.url = jsonmeta["url"].str
|
||||||
|
|
||||||
proc getPackage*(pkg: string, packagesPath: string, resPkg: var Package): bool =
|
proc getPackage*(pkg: string, options: Options,
|
||||||
## Searches ``packagesPath`` file saving into ``resPkg`` the found package.
|
resPkg: var Package): bool =
|
||||||
|
## Searches any packages.json files defined in ``options.config.packageLists``
|
||||||
|
## Saves the found package into ``resPkg``.
|
||||||
##
|
##
|
||||||
## Pass in ``pkg`` the name of the package you are searching for. As
|
## Pass in ``pkg`` the name of the package you are searching for. As
|
||||||
## convenience the proc returns a boolean specifying if the ``resPkg`` was
|
## convenience the proc returns a boolean specifying if the ``resPkg`` was
|
||||||
## successfully filled with good data.
|
## successfully filled with good data.
|
||||||
let packages = parseFile(packagesPath)
|
for name, list in options.config.packageLists:
|
||||||
|
echo("Searching in \"", name, "\" package list...")
|
||||||
|
let packages = parseFile(options.getNimbleDir() /
|
||||||
|
"packages_" & name.toLower() & ".json")
|
||||||
for p in packages:
|
for p in packages:
|
||||||
if p["name"].str == pkg:
|
if p["name"].str == pkg:
|
||||||
resPkg = p.fromJson()
|
resPkg = p.fromJson()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc getPackageList*(packagesPath: string): seq[Package] =
|
proc getPackageList*(options: Options): seq[Package] =
|
||||||
## Returns the list of packages found at the specified path.
|
## Returns the list of packages found in the downloaded packages.json files.
|
||||||
result = @[]
|
result = @[]
|
||||||
let packages = parseFile(packagesPath)
|
var namesAdded = initSet[string]()
|
||||||
|
for name, list in options.config.packageLists:
|
||||||
|
let packages = parseFile(options.getNimbleDir() /
|
||||||
|
"packages_" & name.toLower() & ".json")
|
||||||
for p in packages:
|
for p in packages:
|
||||||
let pkg: Package = p.fromJson()
|
let pkg: Package = p.fromJson()
|
||||||
|
if pkg.name notin namesAdded:
|
||||||
result.add(pkg)
|
result.add(pkg)
|
||||||
|
namesAdded.incl(pkg.name)
|
||||||
|
|
||||||
proc findNimbleFile*(dir: string; error: bool): string =
|
proc findNimbleFile*(dir: string; error: bool): string =
|
||||||
result = ""
|
result = ""
|
||||||
|
|
@ -250,6 +260,27 @@ proc getDownloadDirName*(pkg: Package, verRange: VersionRange): string =
|
||||||
result.add "_"
|
result.add "_"
|
||||||
result.add verSimple
|
result.add verSimple
|
||||||
|
|
||||||
|
proc needsRefresh*(options: Options): bool =
|
||||||
|
## Determines whether a ``nimble refresh`` is needed.
|
||||||
|
##
|
||||||
|
## In the future this will check a stored time stamp to determine how long
|
||||||
|
## ago the package list was refreshed.
|
||||||
|
result = true
|
||||||
|
for name, list in options.config.packageLists:
|
||||||
|
if fileExists(options.getNimbleDir() / "packages_" & name & ".json"):
|
||||||
|
result = false
|
||||||
|
|
||||||
|
proc validatePackagesList*(path: string): bool =
|
||||||
|
## Determines whether package list at ``path`` is valid.
|
||||||
|
try:
|
||||||
|
let pkgList = parseFile(path)
|
||||||
|
if pkgList.kind == JArray:
|
||||||
|
if pkgList.len == 0:
|
||||||
|
echo("WARNING: ", path, " contains no packages.")
|
||||||
|
return true
|
||||||
|
except ValueError, JsonParsingError:
|
||||||
|
return false
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") ==
|
doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") ==
|
||||||
("packagea", "0.1")
|
("packagea", "0.1")
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,38 @@ template cd*(dir: string, body: stmt) =
|
||||||
proc processOutput(output: string): seq[string] =
|
proc processOutput(output: string): seq[string] =
|
||||||
output.strip.splitLines().filter((x: string) => (x.len > 0))
|
output.strip.splitLines().filter((x: string) => (x.len > 0))
|
||||||
|
|
||||||
|
test "can refresh with default urls":
|
||||||
|
check execCmdEx(path & " refresh").exitCode == QuitSuccess
|
||||||
|
|
||||||
|
test "can refresh with custom urls":
|
||||||
|
# Backup current config
|
||||||
|
let configFile = getConfigDir() / "nimble" / "nimble.ini"
|
||||||
|
let configBakFile = getConfigDir() / "nimble" / "nimble.ini.bak"
|
||||||
|
if fileExists(configFile):
|
||||||
|
moveFile(configFile, configBakFile)
|
||||||
|
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) = execCmdEx(path & " refresh")
|
||||||
|
let lines = output.strip.splitLines()
|
||||||
|
check exitCode == QuitSuccess
|
||||||
|
check "reading from config file" in lines[0].normalize
|
||||||
|
check "downloading \"official\" package list" in lines[1].normalize
|
||||||
|
check "trying http://google.com" in lines[2].normalize
|
||||||
|
check "packages.json file is invalid" in lines[3].normalize
|
||||||
|
check "404 not found" in lines[5].normalize
|
||||||
|
check "done" in lines[^1].normalize
|
||||||
|
|
||||||
|
# Restore config
|
||||||
|
if fileExists(configBakFile):
|
||||||
|
moveFile(configBakFile, configFile)
|
||||||
|
|
||||||
test "can install nimscript package":
|
test "can install nimscript package":
|
||||||
cd "nimscript":
|
cd "nimscript":
|
||||||
check execCmdEx("../" & path & " install -y").exitCode == QuitSuccess
|
check execCmdEx("../" & path & " install -y").exitCode == QuitSuccess
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue