Merge branch 'master' into native-pkg-support
This commit is contained in:
commit
e813aa6448
14 changed files with 646 additions and 305 deletions
166
src/nimblepkg/cli.nim
Normal file
166
src/nimblepkg/cli.nim
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||
# BSD License. Look at license.txt for more info.
|
||||
#
|
||||
# Rough rules/philosophy for the messages that Nimble displays are the following:
|
||||
# - Green is only shown when the requested operation is successful.
|
||||
# - Blue can be used to emphasise certain keywords, for example actions such
|
||||
# as "Downloading" or "Reading".
|
||||
# - Red is used when the requested operation fails with an error.
|
||||
# - Yellow is used for warnings.
|
||||
#
|
||||
# - Dim for LowPriority.
|
||||
# - Bright for HighPriority.
|
||||
# - Normal for MediumPriority.
|
||||
|
||||
import logging, terminal, sets, strutils
|
||||
|
||||
type
|
||||
CLI* = ref object
|
||||
level: Priority
|
||||
warnings: HashSet[(string, string)]
|
||||
suppressionCount: int ## Amount of messages which were not shown.
|
||||
|
||||
Priority* = enum
|
||||
DebugPriority, LowPriority, MediumPriority, HighPriority
|
||||
|
||||
DisplayType* = enum
|
||||
Error, Warning, Message, Success
|
||||
|
||||
ForcePrompt* = enum
|
||||
dontForcePrompt, forcePromptYes, forcePromptNo
|
||||
|
||||
const
|
||||
longestCategory = len("Downloading")
|
||||
foregrounds: array[Error .. Success, ForegroundColor] =
|
||||
[fgRed, fgYellow, fgCyan, fgGreen]
|
||||
styles: array[DebugPriority .. HighPriority, set[Style]] =
|
||||
[{styleDim}, {styleDim}, {}, {styleBright}]
|
||||
|
||||
proc newCLI(): CLI =
|
||||
result = CLI(
|
||||
level: HighPriority,
|
||||
warnings: initSet[(string, string)](),
|
||||
suppressionCount: 0
|
||||
)
|
||||
|
||||
var globalCLI = newCLI()
|
||||
|
||||
|
||||
proc calculateCategoryOffset(category: string): int =
|
||||
assert category.len <= longestCategory
|
||||
return longestCategory - category.len
|
||||
|
||||
proc displayCategory(category: string, displayType: DisplayType,
|
||||
priority: Priority) =
|
||||
# Calculate how much the `category` must be offset to align along a center
|
||||
# line.
|
||||
let offset = calculateCategoryOffset(category)
|
||||
# Display the category.
|
||||
if priority != DebugPriority:
|
||||
setForegroundColor(stdout, foregrounds[displayType])
|
||||
writeStyled("$1$2 " % [repeatChar(offset), category], styles[priority])
|
||||
resetAttributes()
|
||||
|
||||
proc displayLine(category, line: string, displayType: DisplayType,
|
||||
priority: Priority) =
|
||||
displayCategory(category, displayType, priority)
|
||||
|
||||
# Display the message.
|
||||
echo(line)
|
||||
|
||||
proc display*(category, msg: string, displayType = Message,
|
||||
priority = MediumPriority) =
|
||||
# Multiple warnings containing the same messages should not be shown.
|
||||
let warningPair = (category, msg)
|
||||
if displayType == Warning:
|
||||
if warningPair in globalCLI.warnings:
|
||||
return
|
||||
else:
|
||||
globalCLI.warnings.incl(warningPair)
|
||||
|
||||
# Suppress this message if its priority isn't high enough.
|
||||
# TODO: Per-priority suppression counts?
|
||||
if priority < globalCLI.level:
|
||||
if priority != DebugPriority:
|
||||
globalCLI.suppressionCount.inc
|
||||
return
|
||||
|
||||
# Display each line in the message.
|
||||
var i = 0
|
||||
for line in msg.splitLines():
|
||||
if len(line) == 0: continue
|
||||
displayLine(if i == 0: category else: "...", line, displayType, priority)
|
||||
i.inc
|
||||
|
||||
proc displayDebug*(category, msg: string) =
|
||||
## Convenience for displaying debug messages.
|
||||
display(category, msg, priority = DebugPriority)
|
||||
|
||||
proc displayDebug*(msg: string) =
|
||||
## Convenience for displaying debug messages with a default category.
|
||||
displayDebug("Debug:", msg)
|
||||
|
||||
proc displayTip*() =
|
||||
## Called just before Nimble exits. Shows some tips for the user, for example
|
||||
## the amount of messages that were suppressed and how to show them.
|
||||
if globalCLI.suppressionCount > 0:
|
||||
let msg = "$1 messages have been suppressed, use --verbose to show them." %
|
||||
$globalCLI.suppressionCount
|
||||
display("Tip:", msg, Warning, HighPriority)
|
||||
|
||||
proc prompt*(forcePrompts: ForcePrompt, question: string): bool =
|
||||
case forcePrompts
|
||||
of forcePromptYes:
|
||||
display("Prompt:", question & " -> [forced yes]", Warning, HighPriority)
|
||||
return true
|
||||
of forcePromptNo:
|
||||
display("Prompt:", question & " -> [forced no]", Warning, HighPriority)
|
||||
return false
|
||||
of dontForcePrompt:
|
||||
display("Prompt:", question & " [y/N]", Warning, HighPriority)
|
||||
displayCategory("Answer:", Warning, HighPriority)
|
||||
let yn = stdin.readLine()
|
||||
case yn.normalize
|
||||
of "y", "yes":
|
||||
return true
|
||||
of "n", "no":
|
||||
return false
|
||||
else:
|
||||
return false
|
||||
|
||||
proc promptCustom*(question, default: string): string =
|
||||
if default == "":
|
||||
display("Prompt:", question, Warning, HighPriority)
|
||||
displayCategory("Answer:", Warning, HighPriority)
|
||||
let user = stdin.readLine()
|
||||
if user.len == 0: return promptCustom(question, default)
|
||||
else: return user
|
||||
else:
|
||||
display("Prompt:", question & " [" & default & "]", Warning, HighPriority)
|
||||
displayCategory("Answer:", Warning, HighPriority)
|
||||
let user = stdin.readLine()
|
||||
if user == "": return default
|
||||
else: return user
|
||||
|
||||
proc setVerbosity*(level: Priority) =
|
||||
globalCLI.level = level
|
||||
|
||||
when isMainModule:
|
||||
display("Reading", "config file at /Users/dom/.config/nimble/nimble.ini",
|
||||
priority = LowPriority)
|
||||
|
||||
display("Reading", "official package list",
|
||||
priority = LowPriority)
|
||||
|
||||
display("Downloading", "daemonize v0.0.2 using Git",
|
||||
priority = HighPriority)
|
||||
|
||||
display("Warning", "dashes in package names will be deprecated", Warning,
|
||||
priority = HighPriority)
|
||||
|
||||
display("Error", """Unable to read package info for /Users/dom/.nimble/pkgs/nimble-0.7.11
|
||||
Reading as ini file failed with:
|
||||
Invalid section: .
|
||||
Evaluating as NimScript file failed with:
|
||||
Users/dom/.nimble/pkgs/nimble-0.7.11/nimble.nimble(3, 23) Error: cannot open 'src/nimblepkg/common'.
|
||||
""", Error, HighPriority)
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||
# BSD License. Look at license.txt for more info.
|
||||
#
|
||||
# Various miscellaneous common types reside here, to avoid problems with
|
||||
|
|
@ -7,7 +8,7 @@ when not defined(nimscript):
|
|||
import sets
|
||||
|
||||
import version
|
||||
export version.NimbleError
|
||||
export version.NimbleError # TODO: Surely there is a better way?
|
||||
|
||||
type
|
||||
BuildFailed* = object of NimbleError
|
||||
|
|
@ -37,5 +38,13 @@ when not defined(nimscript):
|
|||
backend*: string
|
||||
foreignDeps*: seq[string]
|
||||
|
||||
## Same as quit(QuitSuccess), but allows cleanup.
|
||||
NimbleQuit* = ref object of Exception
|
||||
|
||||
proc raiseNimbleError*(msg: string, hint = "") =
|
||||
var exc = newException(NimbleError, msg)
|
||||
exc.hint = hint
|
||||
raise exc
|
||||
|
||||
const
|
||||
nimbleVersion* = "0.7.11"
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
# BSD License. Look at license.txt for more info.
|
||||
import parsecfg, streams, strutils, os, tables, Uri
|
||||
|
||||
import tools, version, common
|
||||
import tools, version, common, cli
|
||||
|
||||
type
|
||||
Config* = object
|
||||
nimbleDir*: string
|
||||
chcp*: bool # Whether to change the code page in .cmd files on Win.
|
||||
packageLists*: Table[string, PackageList] ## URLs to packages.json files
|
||||
packageLists*: Table[string, PackageList] ## Names -> packages.json files
|
||||
cloneUsingHttps*: bool # Whether to replace git:// for https://
|
||||
httpProxy*: Uri # Proxy for package list downloads.
|
||||
|
||||
|
|
@ -50,13 +50,15 @@ proc parseConfig*(): Config =
|
|||
var f = newFileStream(confFile, fmRead)
|
||||
if f == nil:
|
||||
# Try the old deprecated babel.ini
|
||||
# TODO: This can be removed.
|
||||
confFile = getConfigDir() / "babel" / "babel.ini"
|
||||
f = newFileStream(confFile, fmRead)
|
||||
if f != nil:
|
||||
echo("[Warning] Using deprecated config file at ", confFile)
|
||||
display("Warning", "Using deprecated config file at " & confFile,
|
||||
Warning, HighPriority)
|
||||
|
||||
if f != nil:
|
||||
echo("Reading from config file at ", confFile)
|
||||
display("Reading", "config file at " & confFile, priority = LowPriority)
|
||||
var p: CfgParser
|
||||
open(p, f, confFile)
|
||||
var currentSection = ""
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import parseutils, os, osproc, strutils, tables, pegs
|
||||
|
||||
import packageinfo, packageparser, version, tools, common, options
|
||||
import packageinfo, packageparser, version, tools, common, options, cli
|
||||
|
||||
type
|
||||
DownloadMethod* {.pure.} = enum
|
||||
|
|
@ -142,7 +142,6 @@ proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
|
|||
##
|
||||
## Returns the version of the repository which has been downloaded.
|
||||
template getLatestByTag(meth: untyped) {.dirty.} =
|
||||
echo("Found tags...")
|
||||
# Find latest version that fits our ``verRange``.
|
||||
var latest = findLatest(verRange, versions)
|
||||
## Note: HEAD is not used when verRange.kind is verAny. This is
|
||||
|
|
@ -150,7 +149,7 @@ proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
|
|||
|
||||
# If no tagged versions satisfy our range latest.tag will be "".
|
||||
# We still clone in that scenario because we want to try HEAD in that case.
|
||||
# https://github.com/nimrod-code/nimble/issues/22
|
||||
# https://github.com/nim-lang/nimble/issues/22
|
||||
meth
|
||||
if $latest.ver != "":
|
||||
result = parseVersionRange($latest.ver)
|
||||
|
|
@ -188,7 +187,8 @@ proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
|
|||
let versions = getTagsListRemote(url, downMethod).getVersionList()
|
||||
if versions.len > 0:
|
||||
getLatestByTag:
|
||||
echo("Cloning latest tagged version: ", latest.tag)
|
||||
display("Cloning", "latest tagged version: " & latest.tag,
|
||||
priority = MediumPriority)
|
||||
doClone(downMethod, url, downloadDir, latest.tag)
|
||||
else:
|
||||
# If no commits have been tagged on the repo we just clone HEAD.
|
||||
|
|
@ -202,7 +202,8 @@ proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
|
|||
|
||||
if versions.len > 0:
|
||||
getLatestByTag:
|
||||
echo("Switching to latest tagged version: ", latest.tag)
|
||||
display("Switching", "to latest tagged version: " & latest.tag,
|
||||
priority = MediumPriority)
|
||||
doCheckout(downMethod, downloadDir, latest.tag)
|
||||
|
||||
verifyClone()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from compiler/scriptconfig import setupVM
|
|||
from compiler/astalgo import strTableGet
|
||||
import compiler/options as compiler_options
|
||||
|
||||
import common, version, options, packageinfo
|
||||
import common, version, options, packageinfo, cli
|
||||
import os, strutils, strtabs, times, osproc, sets
|
||||
|
||||
when not declared(resetAllModulesHard):
|
||||
|
|
@ -299,7 +299,7 @@ proc readPackageInfoFromNims*(scriptName: string, options: Options,
|
|||
if msgs.gErrorCounter > 0:
|
||||
raise newException(NimbleError, previousMsg)
|
||||
elif previousMsg.len > 0:
|
||||
echo(previousMsg)
|
||||
display("Info", previousMsg, priority = HighPriority)
|
||||
if output.normalize.startsWith("error"):
|
||||
raise newException(NimbleError, output)
|
||||
previousMsg = output
|
||||
|
|
@ -387,7 +387,8 @@ proc execTask*(scriptName, taskName: string,
|
|||
result.success = true
|
||||
result.flags = newStringTable()
|
||||
compiler_options.command = internalCmd
|
||||
echo("Executing task ", taskName, " in ", scriptName)
|
||||
display("Executing", "task $# in $#" % [taskName, scriptName],
|
||||
priority = HighPriority)
|
||||
|
||||
let thisModule = execScript(scriptName, result.flags, options)
|
||||
let prc = thisModule.tab.strTableGet(getIdent(taskName & "Task"))
|
||||
|
|
@ -417,7 +418,8 @@ proc execHook*(scriptName, actionName: string, before: bool,
|
|||
let hookName =
|
||||
if before: actionName.toLowerAscii & "Before"
|
||||
else: actionName.toLowerAscii & "After"
|
||||
echo("Attempting to execute hook ", hookName, " in ", scriptName)
|
||||
display("Attempting", "to execute hook $# in $#" % [hookName, scriptName],
|
||||
priority = MediumPriority)
|
||||
|
||||
let thisModule = execScript(scriptName, result.flags, options)
|
||||
# Explicitly execute the task procedure, instead of relying on hack.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import json, strutils, os, parseopt, strtabs, uri, tables
|
||||
from httpclient import Proxy, newProxy
|
||||
|
||||
import config, version, tools, common
|
||||
import config, version, tools, common, cli
|
||||
|
||||
type
|
||||
Options* = object
|
||||
|
|
@ -12,6 +12,8 @@ type
|
|||
depsOnly*: bool
|
||||
queryVersions*: bool
|
||||
queryInstalled*: bool
|
||||
nimbleDir*: string
|
||||
verbosity*: cli.Priority
|
||||
action*: Action
|
||||
config*: Config
|
||||
nimbleData*: JsonNode ## Nimbledata.json
|
||||
|
|
@ -21,12 +23,11 @@ type
|
|||
actionNil, actionRefresh, actionInit, actionDump, actionPublish,
|
||||
actionInstall, actionSearch,
|
||||
actionList, actionBuild, actionPath, actionUninstall, actionCompile,
|
||||
actionCustom, actionTasks, actionVersion
|
||||
actionCustom, actionTasks
|
||||
|
||||
Action* = object
|
||||
case typ*: ActionType
|
||||
of actionNil, actionList, actionBuild, actionPublish, actionTasks,
|
||||
actionVersion: nil
|
||||
of actionNil, actionList, actionBuild, actionPublish, actionTasks: nil
|
||||
of actionRefresh:
|
||||
optionalURL*: string # Overrides default package list.
|
||||
of actionInstall, actionPath, actionUninstall:
|
||||
|
|
@ -44,9 +45,6 @@ type
|
|||
arguments*: seq[string]
|
||||
flags*: StringTableRef
|
||||
|
||||
ForcePrompt* = enum
|
||||
dontForcePrompt, forcePromptYes, forcePromptNo
|
||||
|
||||
|
||||
const
|
||||
help* = """
|
||||
|
|
@ -54,6 +52,7 @@ Usage: nimble COMMAND [opts]
|
|||
|
||||
Commands:
|
||||
install [pkgname, ...] Installs a list of packages.
|
||||
[-d, --depsOnly] Install only dependencies.
|
||||
init [pkgname] Initializes a new Nimble project.
|
||||
publish Publishes a package on nim-lang/packages.
|
||||
The current working directory needs to be the
|
||||
|
|
@ -64,9 +63,11 @@ Commands:
|
|||
to the Nim compiler.
|
||||
refresh [url] Refreshes the package list. A package list URL
|
||||
can be optionally specified.
|
||||
search [--ver] pkg/tag Searches for a specified package. Search is
|
||||
search pkg/tag Searches for a specified package. Search is
|
||||
performed by tag and by name.
|
||||
list [--ver] Lists all packages.
|
||||
[--ver] Query remote server for package version.
|
||||
list Lists all packages.
|
||||
[--ver] Query remote server for package version.
|
||||
[-i, --installed] Lists all installed packages.
|
||||
tasks Lists the tasks specified in the Nimble
|
||||
package's Nimble file.
|
||||
|
|
@ -83,26 +84,29 @@ Options:
|
|||
--ver Query remote server for package version
|
||||
information when searching or listing packages
|
||||
--nimbleDir dirname Set the Nimble directory.
|
||||
-d --depsOnly Install only dependencies.
|
||||
--verbose Show all non-debug output.
|
||||
--debug Show all output including debug messages.
|
||||
|
||||
For more information read the Github readme:
|
||||
https://github.com/nim-lang/nimble#readme
|
||||
"""
|
||||
|
||||
proc writeHelp*() =
|
||||
proc writeHelp*(quit=true) =
|
||||
echo(help)
|
||||
quit(QuitSuccess)
|
||||
if quit:
|
||||
raise NimbleQuit(msg: "")
|
||||
|
||||
proc writeVersion() =
|
||||
echo("nimble v$# compiled at $# $#" %
|
||||
[nimbleVersion, CompileDate, CompileTime])
|
||||
raise NimbleQuit(msg: "")
|
||||
|
||||
proc parseActionType*(action: string): ActionType =
|
||||
case action.normalize()
|
||||
of "install", "path":
|
||||
case action.normalize()
|
||||
of "install":
|
||||
result = actionInstall
|
||||
of "path":
|
||||
result = actionPath
|
||||
else:
|
||||
discard
|
||||
of "install":
|
||||
result = actionInstall
|
||||
of "path":
|
||||
result = actionPath
|
||||
of "build":
|
||||
result = actionBuild
|
||||
of "c", "compile", "js", "cpp", "cc":
|
||||
|
|
@ -153,30 +157,14 @@ proc initAction*(options: var Options, key: string) =
|
|||
options.action.arguments = @[]
|
||||
options.action.flags = newStringTable()
|
||||
of actionBuild, actionPublish, actionList, actionTasks,
|
||||
actionNil, actionVersion: discard
|
||||
actionNil: discard
|
||||
|
||||
proc prompt*(options: Options, question: string): bool =
|
||||
## Asks an interactive question and returns the result.
|
||||
##
|
||||
## The proc will return immediately without asking the user if the global
|
||||
## forcePrompts has a value different than dontForcePrompt.
|
||||
case options.forcePrompts
|
||||
of forcePromptYes:
|
||||
echo(question & " -> [forced yes]")
|
||||
return true
|
||||
of forcePromptNo:
|
||||
echo(question & " -> [forced no]")
|
||||
return false
|
||||
of dontForcePrompt:
|
||||
echo(question & " [y/N]")
|
||||
let yn = stdin.readLine()
|
||||
case yn.normalize
|
||||
of "y", "yes":
|
||||
return true
|
||||
of "n", "no":
|
||||
return false
|
||||
else:
|
||||
return false
|
||||
return prompt(options.forcePrompts, question)
|
||||
|
||||
proc renameBabelToNimble(options: Options) {.deprecated.} =
|
||||
let babelDir = getHomeDir() / ".babel"
|
||||
|
|
@ -191,7 +179,13 @@ proc renameBabelToNimble(options: Options) {.deprecated.} =
|
|||
removeFile(nimbleDir / "babeldata.json")
|
||||
|
||||
proc getNimbleDir*(options: Options): string =
|
||||
expandTilde(options.config.nimbleDir)
|
||||
result =
|
||||
if options.nimbleDir.len == 0:
|
||||
options.config.nimbleDir
|
||||
else:
|
||||
options.nimbleDir
|
||||
|
||||
return expandTilde(result)
|
||||
|
||||
proc getPkgsDir*(options: Options): string =
|
||||
options.getNimbleDir() / "pkgs"
|
||||
|
|
@ -235,33 +229,53 @@ proc parseArgument*(key: string, result: var Options) =
|
|||
discard
|
||||
|
||||
proc parseFlag*(flag, val: string, result: var Options) =
|
||||
case result.action.typ
|
||||
of actionCompile:
|
||||
if val == "":
|
||||
result.action.compileOptions.add("--" & flag)
|
||||
var wasFlagHandled = true
|
||||
|
||||
# Global flags.
|
||||
case flag.normalize()
|
||||
of "help", "h": writeHelp()
|
||||
of "version", "v": writeVersion()
|
||||
of "accept", "y": result.forcePrompts = forcePromptYes
|
||||
of "reject", "n": result.forcePrompts = forcePromptNo
|
||||
of "nimbledir": result.nimbleDir = val
|
||||
of "verbose": result.verbosity = LowPriority
|
||||
of "debug": result.verbosity = DebugPriority
|
||||
# Action-specific flags.
|
||||
of "installed", "i":
|
||||
if result.action.typ in [actionSearch, actionList]:
|
||||
result.queryInstalled = true
|
||||
else:
|
||||
result.action.compileOptions.add("--" & flag & ":" & val)
|
||||
of actionCustom:
|
||||
result.action.flags[flag] = val
|
||||
wasFlagHandled = false
|
||||
of "depsonly", "d":
|
||||
if result.action.typ == actionInstall:
|
||||
result.depsOnly = true
|
||||
else:
|
||||
wasFlagHandled = false
|
||||
of "ver":
|
||||
if result.action.typ in [actionSearch, actionList]:
|
||||
result.queryVersions = true
|
||||
else:
|
||||
wasFlagHandled = false
|
||||
else:
|
||||
# TODO: These should not be global.
|
||||
case flag.normalize()
|
||||
of "help", "h": writeHelp()
|
||||
of "version", "v":
|
||||
assert result.action.typ == actionNil
|
||||
result.action.typ = actionVersion
|
||||
of "accept", "y": result.forcePrompts = forcePromptYes
|
||||
of "reject", "n": result.forcePrompts = forcePromptNo
|
||||
of "ver": result.queryVersions = true
|
||||
of "nimbledir": result.config.nimbleDir = val # overrides option from file
|
||||
of "installed", "i": result.queryInstalled = true
|
||||
of "depsonly", "d": result.depsOnly = true
|
||||
case result.action.typ
|
||||
of actionCompile:
|
||||
if val == "":
|
||||
result.action.compileOptions.add("--" & flag)
|
||||
else:
|
||||
result.action.compileOptions.add("--" & flag & ":" & val)
|
||||
of actionCustom:
|
||||
result.action.flags[flag] = val
|
||||
else:
|
||||
raise newException(NimbleError, "Unknown option: --" & flag)
|
||||
wasFlagHandled = false
|
||||
|
||||
if not wasFlagHandled:
|
||||
raise newException(NimbleError, "Unknown option: --" & flag)
|
||||
|
||||
proc initOptions*(): Options =
|
||||
result.action.typ = actionNil
|
||||
result.pkgInfoCache = newTable[string, PackageInfo]()
|
||||
result.nimbleDir = ""
|
||||
result.verbosity = HighPriority
|
||||
|
||||
proc parseMisc(options: var Options) =
|
||||
# Load nimbledata.json
|
||||
|
|
@ -278,9 +292,9 @@ proc parseMisc(options: var Options) =
|
|||
|
||||
proc parseCmdLine*(): Options =
|
||||
result = initOptions()
|
||||
result.config = parseConfig()
|
||||
|
||||
# Parse command line params.
|
||||
# Parse command line params first. A simple `--version` shouldn't require
|
||||
# a config to be parsed.
|
||||
for kind, key, val in getOpt():
|
||||
case kind
|
||||
of cmdArgument:
|
||||
|
|
@ -292,6 +306,12 @@ proc parseCmdLine*(): Options =
|
|||
parseFlag(key, val, result)
|
||||
of cmdEnd: assert(false) # cannot happen
|
||||
|
||||
# Set verbosity level.
|
||||
setVerbosity(result.verbosity)
|
||||
|
||||
# Parse config.
|
||||
result.config = parseConfig()
|
||||
|
||||
# Parse other things, for example the nimbledata.json file.
|
||||
parseMisc(result)
|
||||
|
||||
|
|
@ -310,8 +330,8 @@ proc getProxy*(options: Options): Proxy =
|
|||
elif existsEnv("https_proxy"):
|
||||
url = getEnv("https_proxy")
|
||||
except ValueError:
|
||||
echo("WARNING: Unable to parse proxy from environment: ",
|
||||
getCurrentExceptionMsg())
|
||||
display("Warning:", "Unable to parse proxy from environment: " &
|
||||
getCurrentExceptionMsg(), Warning, HighPriority)
|
||||
|
||||
if url.len > 0:
|
||||
var parsed = parseUri(url)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||
# BSD License. Look at license.txt for more info.
|
||||
import parsecfg, json, streams, strutils, parseutils, os, sets, tables
|
||||
import version, tools, common, options
|
||||
import version, tools, common, options, cli
|
||||
|
||||
type
|
||||
Package* = object
|
||||
|
|
@ -117,13 +117,10 @@ proc fromJson(obj: JSonNode): Package =
|
|||
proc readMetaData*(path: string): MetaData =
|
||||
## Reads the metadata present in ``~/.nimble/pkgs/pkg-0.1/nimblemeta.json``
|
||||
var bmeta = path / "nimblemeta.json"
|
||||
if not existsFile(bmeta):
|
||||
bmeta = path / "babelmeta.json"
|
||||
if existsFile(bmeta):
|
||||
echo("WARNING: using deprecated babelmeta.json file in " & path)
|
||||
if not existsFile(bmeta):
|
||||
result.url = ""
|
||||
echo("WARNING: No nimblemeta.json file found in " & path)
|
||||
display("Warning:", "No nimblemeta.json file found in " & path,
|
||||
Warning, HighPriority)
|
||||
return
|
||||
# TODO: Make this an error.
|
||||
let cont = readFile(bmeta)
|
||||
|
|
@ -139,7 +136,7 @@ proc getPackage*(pkg: string, options: Options,
|
|||
## convenience the proc returns a boolean specifying if the ``resPkg`` was
|
||||
## successfully filled with good data.
|
||||
for name, list in options.config.packageLists:
|
||||
echo("Searching in \"", name, "\" package list...")
|
||||
display("Reading", "$1 package list" % name, priority = LowPriority)
|
||||
let packages = parseFile(options.getNimbleDir() /
|
||||
"packages_" & name.toLowerAscii() & ".json")
|
||||
for p in packages:
|
||||
|
|
@ -179,8 +176,8 @@ proc findNimbleFile*(dir: string; error: bool): string =
|
|||
raise newException(NimbleError,
|
||||
"Specified directory does not contain a .nimble file.")
|
||||
else:
|
||||
# TODO: Abstract logging.
|
||||
echo("WARNING: No .nimble file found for ", dir)
|
||||
display("Warning:", "No .nimble file found for " & dir, Warning,
|
||||
HighPriority)
|
||||
|
||||
proc getInstalledPkgsMin*(libsDir: string, options: Options):
|
||||
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
|
||||
|
|
@ -279,7 +276,8 @@ proc validatePackagesList*(path: string): bool =
|
|||
let pkgList = parseFile(path)
|
||||
if pkgList.kind == JArray:
|
||||
if pkgList.len == 0:
|
||||
echo("WARNING: ", path, " contains no packages.")
|
||||
display("Warning:", path & " contains no packages.", Warning,
|
||||
HighPriority)
|
||||
return true
|
||||
except ValueError, JsonParsingError:
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (C) Dominik Picheta. All rights reserved.
|
||||
# BSD License. Look at license.txt for more info.
|
||||
import parsecfg, json, streams, strutils, parseutils, os, tables
|
||||
import version, tools, common, nimscriptsupport, options, packageinfo
|
||||
import version, tools, common, nimscriptsupport, options, packageinfo, cli
|
||||
|
||||
## Contains procedures for parsing .nimble files. Moved here from ``packageinfo``
|
||||
## because it depends on ``nimscriptsupport`` (``nimscriptsupport`` also
|
||||
|
|
@ -20,11 +20,7 @@ proc newValidationError(msg: string, warnInstalled: bool): ref ValidationError =
|
|||
result.warnInstalled = warnInstalled
|
||||
|
||||
proc raiseNewValidationError(msg: string, warnInstalled: bool) =
|
||||
if warnInstalled:
|
||||
# TODO: We warn everywhere for now. Raise the error in the next version.
|
||||
echo("WARNING: ", msg, ". Will be an error in next version!")
|
||||
else:
|
||||
raise newValidationError(msg, warnInstalled)
|
||||
raise newValidationError(msg, warnInstalled)
|
||||
|
||||
proc validatePackageName*(name: string) =
|
||||
## Raises an error if specified package name contains invalid characters.
|
||||
|
|
@ -91,10 +87,11 @@ proc validatePackageInfo(pkgInfo: PackageInfo, path: string) =
|
|||
|
||||
proc nimScriptHint*(pkgInfo: PackageInfo) =
|
||||
if not pkgInfo.isNimScript:
|
||||
# TODO: Turn this into a warning.
|
||||
# TODO: Add a URL explaining more.
|
||||
echo("NOTE: The .nimble file for this project could make use of " &
|
||||
"additional features, if converted into the new NimScript format.")
|
||||
display("Warning:", "The .nimble file for this project could make use of " &
|
||||
"additional features, if converted into the new NimScript format." &
|
||||
"\nFor more details see:" &
|
||||
"https://github.com/nim-lang/nimble#creating-packages",
|
||||
Warning, HighPriority)
|
||||
|
||||
proc multiSplit(s: string): seq[string] =
|
||||
## Returns ``s`` split by newline and comma characters.
|
||||
|
|
@ -242,6 +239,18 @@ proc getInstalledPkgs*(libsDir: string, options: Options):
|
|||
## Gets a list of installed packages.
|
||||
##
|
||||
## ``libsDir`` is in most cases: ~/.nimble/pkgs/
|
||||
const
|
||||
readErrorMsg = "Installed package $1 v$2 is outdated or corrupt."
|
||||
validationErrorMsg = readErrorMsg & "\nPackage did not pass validation: $3"
|
||||
hintMsg = "The corrupted package will need to be removed manually. To fix" &
|
||||
" this error message, remove $1."
|
||||
|
||||
proc createErrorMsg(tmplt, path, msg: string): string =
|
||||
let (name, version) = getNameVersion(path)
|
||||
return tmplt % [name, version, msg]
|
||||
|
||||
display("Loading", "list of installed packages", priority = MediumPriority)
|
||||
|
||||
result = @[]
|
||||
for kind, path in walkDir(libsDir):
|
||||
if kind == pcDir:
|
||||
|
|
@ -254,17 +263,17 @@ proc getInstalledPkgs*(libsDir: string, options: Options):
|
|||
result.add((pkg, meta))
|
||||
except ValidationError:
|
||||
let exc = (ref ValidationError)(getCurrentException())
|
||||
exc.msg = createErrorMsg(validationErrorMsg, path, exc.msg)
|
||||
exc.hint = hintMsg % path
|
||||
if exc.warnInstalled:
|
||||
echo("WARNING: Unable to read package info for " & path & "\n" &
|
||||
" Package did not pass validation: " & exc.msg)
|
||||
display("Warning:", exc.msg, Warning, HighPriority)
|
||||
else:
|
||||
exc.msg = "Unable to read package info for " & path & "\n" &
|
||||
" Package did not pass validation: " & exc.msg
|
||||
raise exc
|
||||
except:
|
||||
let exc = getCurrentException()
|
||||
exc.msg = "Unable to read package info for " & path & "\n" &
|
||||
" Error: " & exc.msg
|
||||
let tmplt = readErrorMsg & "\nMore info: $3"
|
||||
let msg = createErrorMsg(tmplt, path, getCurrentException().msg)
|
||||
var exc = newException(NimbleError, msg)
|
||||
exc.hint = hintMsg % path
|
||||
raise exc
|
||||
|
||||
proc isNimScript*(nf: string, options: Options): bool =
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
## Implements 'nimble publish' to create a pull request against
|
||||
## nim-lang/packages automatically.
|
||||
|
||||
import system except TResult
|
||||
import httpclient, base64, strutils, rdstdin, json, os, browsers, times, uri
|
||||
import tools, common
|
||||
import tools, common, cli
|
||||
|
||||
type
|
||||
Auth = object
|
||||
|
|
@ -22,19 +23,20 @@ proc createHeaders(a: Auth): string =
|
|||
"Accept: */*\c\L")
|
||||
|
||||
proc getGithubAuth(): Auth =
|
||||
echo("Please create a new personal access token on Github in order to " &
|
||||
"allow Nimble to fork the packages repository.")
|
||||
display("Info:", "Please create a new personal access token on Github in" &
|
||||
" order to allow Nimble to fork the packages repository.",
|
||||
priority = HighPriority)
|
||||
sleep(5000)
|
||||
echo("Your default browser should open with the following URL: " &
|
||||
"https://github.com/settings/tokens/new")
|
||||
display("Info:", "Your default browser should open with the following URL: " &
|
||||
"https://github.com/settings/tokens/new", priority = HighPriority)
|
||||
sleep(3000)
|
||||
openDefaultBrowser("https://github.com/settings/tokens/new")
|
||||
result.token = readLineFromStdin("Personal access token: ").strip()
|
||||
result.token = promptCustom("Personal access token?", "").strip()
|
||||
let resp = getContent("https://api.github.com/user",
|
||||
extraHeaders=createHeaders(result)).parseJson()
|
||||
|
||||
result.user = resp["login"].str
|
||||
echo("Successfully verified as ", result.user)
|
||||
display("Success:", "Verified as " & result.user, Success, HighPriority)
|
||||
|
||||
proc isCorrectFork(j: JsonNode): bool =
|
||||
# Check whether this is a fork of the nimble packages repo.
|
||||
|
|
@ -56,7 +58,7 @@ proc createFork(a: Auth) =
|
|||
extraHeaders=createHeaders(a))
|
||||
|
||||
proc createPullRequest(a: Auth, packageName, branch: string) =
|
||||
echo("Creating PR")
|
||||
display("Info", "Creating PR", priority = HighPriority)
|
||||
discard postContent("https://api.github.com/repos/nim-lang/packages/pulls",
|
||||
extraHeaders=createHeaders(a),
|
||||
body="""{"title": "Add package $1", "head": "$2:$3",
|
||||
|
|
@ -129,17 +131,19 @@ proc publish*(p: PackageInfo) =
|
|||
var pkgsDir = getTempDir() / "nimble-packages-fork"
|
||||
if not forkExists(auth):
|
||||
createFork(auth)
|
||||
echo "waiting 10s to let Github create a fork ..."
|
||||
display("Info:", "Waiting 10s to let Github create a fork",
|
||||
priority = HighPriority)
|
||||
os.sleep(10_000)
|
||||
|
||||
echo "... done"
|
||||
display("Info:", "Finished waiting", priority = LowPriority)
|
||||
if dirExists(pkgsDir):
|
||||
echo("Removing old packages fork git directory.")
|
||||
display("Removing", "old packages fork git directory.",
|
||||
priority = LowPriority)
|
||||
removeDir(pkgsDir)
|
||||
echo "Cloning packages into: ", pkgsDir
|
||||
display("Cloning", "packages into: " & pkgsDir, priority = HighPriority)
|
||||
doCmd("git clone git@github.com:" & auth.user & "/packages " & pkgsDir)
|
||||
# Make sure to update the clone.
|
||||
echo("Updating the fork...")
|
||||
display("Updating", "the fork", priority = HighPriority)
|
||||
cd pkgsDir:
|
||||
doCmd("git pull https://github.com/nim-lang/packages.git master")
|
||||
doCmd("git push origin master")
|
||||
|
|
@ -175,20 +179,20 @@ proc publish*(p: PackageInfo) =
|
|||
"No .git nor .hg directory found. Stopping.")
|
||||
|
||||
if url.len == 0:
|
||||
url = readLineFromStdin("Github URL of " & p.name & ": ")
|
||||
url = promptCustom("Github URL of " & p.name & "?", "")
|
||||
if url.len == 0: userAborted()
|
||||
|
||||
let tags = readLineFromStdin("Please enter a whitespace separated list of tags: ")
|
||||
let tags = promptCustom("Whitespace separated list of tags?", "")
|
||||
|
||||
cd pkgsDir:
|
||||
editJson(p, url, tags, downloadMethod)
|
||||
let branchName = "add-" & p.name & getTime().getGMTime().format("HHmm")
|
||||
doCmd("git checkout -B " & branchName)
|
||||
doCmd("git commit packages.json -m \"Added package " & p.name & "\"")
|
||||
echo("Pushing to remote of fork.")
|
||||
display("Pushing", "to remote of fork.", priority = HighPriority)
|
||||
doCmd("git push " & getPackageOriginUrl(auth) & " " & branchName)
|
||||
createPullRequest(auth, p.name, branchName)
|
||||
echo "Pull request successful."
|
||||
display("Success:", "Pull request successful.", Success, HighPriority)
|
||||
|
||||
when isMainModule:
|
||||
import packageinfo
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# Various miscellaneous utility functions reside here.
|
||||
import osproc, pegs, strutils, os, uri, sets, json, parseutils
|
||||
import version, common
|
||||
import version, common, cli
|
||||
|
||||
proc extractBin(cmd: string): string =
|
||||
if cmd[0] == '"':
|
||||
|
|
@ -20,11 +20,16 @@ proc doCmd*(cmd: string) =
|
|||
stdout.flushFile()
|
||||
stderr.flushFile()
|
||||
|
||||
let exitCode = execCmd(cmd)
|
||||
displayDebug("Executing", cmd)
|
||||
let (output, exitCode) = execCmdEx(cmd)
|
||||
displayDebug("Finished", "with exit code " & $exitCode)
|
||||
# TODO: Improve to show output in real-time.
|
||||
displayDebug("Output", output)
|
||||
|
||||
if exitCode != QuitSuccess:
|
||||
raise newException(NimbleError,
|
||||
"Execution failed with exit code " & $exitCode)
|
||||
"Execution failed with exit code $1\nCommand: $2\nOutput: $3" %
|
||||
[$exitCode, cmd, output])
|
||||
|
||||
proc doCmdEx*(cmd: string): tuple[output: TaintedString, exitCode: int] =
|
||||
let bin = extractBin(cmd)
|
||||
|
|
@ -50,7 +55,7 @@ proc getNimrodVersion*: Version =
|
|||
let vOutput = doCmdEx('"' & nimBin & "\" -v").output
|
||||
var matches: array[0..MaxSubpatterns, string]
|
||||
if vOutput.find(peg"'Version'\s{(\d+\.)+\d}", matches) == -1:
|
||||
quit("Couldn't find Nim version.", QuitFailure)
|
||||
raise newException(NimbleError, "Couldn't find Nim version.")
|
||||
newVersion(matches[0])
|
||||
|
||||
proc samePaths*(p1, p2: string): bool =
|
||||
|
|
@ -75,14 +80,14 @@ proc changeRoot*(origRoot, newRoot, path: string): string =
|
|||
|
||||
proc copyFileD*(fro, to: string): string =
|
||||
## Returns the destination (``to``).
|
||||
echo(fro, " -> ", to)
|
||||
display("Copying", "file $# to $#" % [fro, to], priority = LowPriority)
|
||||
copyFileWithPermissions(fro, to)
|
||||
result = to
|
||||
|
||||
proc copyDirD*(fro, to: string): seq[string] =
|
||||
## Returns the filenames of the files in the directory that were copied.
|
||||
result = @[]
|
||||
echo("Copying directory: ", fro, " -> ", to)
|
||||
display("Copying", "directory $# to $#" % [fro, to], priority = LowPriority)
|
||||
for path in walkDirRec(fro):
|
||||
createDir(changeRoot(fro, to, path.splitFile.dir))
|
||||
result.add copyFileD(path, changeRoot(fro, to, path))
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ type
|
|||
|
||||
ParseVersionError* = object of ValueError
|
||||
NimbleError* = object of Exception
|
||||
hint*: string
|
||||
|
||||
proc newVersion*(ver: string): Version = return Version(ver)
|
||||
proc newSpecial*(spe: string): Special = return Special(spe)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue