Skip to content

Commit

Permalink
Feature: Add --probSpecsDir option
Browse files Browse the repository at this point in the history
The user can now tell the program to use an existing
`problem-specifications` directory, in which case it doesn't make a
temporary shallow clone.

This saves a significant amount of time, and is probably the biggest
possible speed optimisation given that the program running time in
non-interactive mode is essentially the time it takes to establish an
up-to-date `problem-specifications` directory. But perhaps we could
consider adding an "offline-mode" in the future.

This commit also makes it easier to add tests.

Decisions:
- Show an error if the given `problem-specifications` working directory
  is not clean, rather than asking the user or stashing changes.

Closes: exercism#47
  • Loading branch information
ee7 committed Oct 21, 2020
1 parent 787b22d commit e5a0568
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 9 deletions.
11 changes: 9 additions & 2 deletions src/cli.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ type
exercise*: Option[string]
mode*: Mode
verbosity*: Verbosity
probSpecsDir*: Option[string]

Opt = enum
optExercise, optCheck, optMode, optVerbosity, optHelp, optVersion
optExercise, optCheck, optMode, optVerbosity, optProbSpecsDir,
optHelp, optVersion

OptKey = tuple
short: string
Expand All @@ -32,6 +34,7 @@ const
("c", "check"),
("m", "mode"),
("o", "verbosity"),
("p", "probSpecsDir"),
("h", "help"),
("v", "version"),
]
Expand All @@ -54,6 +57,7 @@ Options:
-{optCheck.short}, --{optCheck.long} Check if there are missing tests. Doesn't update the tests. Terminates with a non-zero exit code if one or more tests are missing
-{optMode.short}, --{optMode.long} <mode> What to do with missing test cases. Allowed values: c[hoose], i[nclude], e[xclude]
-{optVerbosity.short}, --{optVerbosity.long} <verbosity> The verbosity of output. Allowed values: q[uiet], n[ormal], d[etailed]
-{optProbSpecsDir.short}, --{optProbSpecsDir.long} <dir> Use this `problem-specifications` directory, rather than cloning temporarily
-{optHelp.short}, --{optHelp.long} Show this help message and exit
-{optVersion.short}, --{optVersion.long} Show this tool's version information and exit"""

Expand All @@ -63,7 +67,7 @@ proc showVersion =
echo &"Canonical Data Syncer v{NimblePkgVersion}"
quit(0)

proc showError(s: string) =
proc showError*(s: string) =
stdout.styledWrite(fgRed, "Error: ")
stdout.write(s)
stdout.write("\n\n")
Expand Down Expand Up @@ -134,6 +138,9 @@ proc processCmdLine*: Conf =
of optVerbosity.short, optVerbosity.long:
showErrorForMissingVal(kind, key, val)
result.verbosity = parseVerbosity(kind, key, val)
of optProbSpecsDir.short, optProbSpecsDir.long:
showErrorForMissingVal(kind, key, val)
result.probSpecsDir = some(val)
of optHelp.short, optHelp.long:
showHelp()
of optVersion.short, optVersion.long:
Expand Down
49 changes: 42 additions & 7 deletions src/probspecs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,47 @@ proc findProbSpecsExercises(repo: ProbSpecsRepo, conf: Conf): seq[ProbSpecsExerc
if conf.exercise.isNone or conf.exercise.get() == repoExercise.slug:
result.add(initProbSpecsExercise(repoExercise))

proc findProbSpecsExercises*(conf: Conf): seq[ProbSpecsExercise] =
let probSpecsRepo = initProbSpecsRepo()

template withDir(dir: string; body: untyped): untyped =
## Changes the current directory to `dir` temporarily.
let startDir = getCurrentDir()
try:
probSpecsRepo.remove()
probSpecsRepo.clone()
probSpecsRepo.findProbSpecsExercises(conf)
setCurrentDir(dir)
body
finally:
probSpecsRepo.remove()
setCurrentDir(startDir)

proc findProbSpecsExercises*(conf: Conf): seq[ProbSpecsExercise] =
const mainBranchName = "master"

if conf.probSpecsDir.isSome():
let probSpecsDir = conf.probSpecsDir.get()
logDetailed(&"Using user-provided problem-specifications dir: {probSpecsDir}")
if not dirExists(probSpecsDir):
showError(&"the given problem-specifications directory does not exist: '{probSpecsDir}'")
withDir probSpecsDir:
if execCmd("git rev-parse") != 0:
showError(&"the given problem-specifications directory is not a git repository: '{probSpecsDir}'")

if execCmd("git diff-index --quiet HEAD") != 0: # Ignores untracked files.
echo &"\nUnstaged changes in {probSpecsDir}:"
discard execCmd("git status --short --untracked=no")
showError(&"the given problem-specifications working directory is not clean: '{probSpecsDir}'")

if execCmd(&"git checkout --quiet {mainBranchName}") != 0:
showError(&"failed to checkout `{mainBranchName}` in problem-specifications directory: '{probSpecsDir}'")

if execCmd("git fetch --quiet") != 0: # Separate `fetch` and `merge`.
showError(&"failed to fetch `{mainBranchName}` in problem-specifications directory: '{probSpecsDir}'")

if execCmd("git merge --quiet --ff-only") != 0:
showError(&"failed to merge upstream changes in problem-specifications directory: '{probSpecsDir}'")

result = ProbSpecsRepo(dir: probSpecsDir).findProbSpecsExercises(conf)
else:
let probSpecsRepo = initProbSpecsRepo()
try:
probSpecsRepo.remove()
probSpecsRepo.clone()
result = probSpecsRepo.findProbSpecsExercises(conf)
finally:
probSpecsRepo.remove()

0 comments on commit e5a0568

Please sign in to comment.