Skip to content

Commit

Permalink
lint: error on missing concept/exercise files (#522)
Browse files Browse the repository at this point in the history
* lint(track_config): check concepts with missing dir

With this commit, `configlet lint` now checks if there are concepts in
the `config.json` file's `concepts` array that don't have a
corresponding `concepts/<slug>` directory (which causes the track to
crash during syncing).

* lint(track_config): check exercises with missing dir

With this commit, `configlet lint` now checks if there are exercises in
the `config.json` file's `exercises.practice` or `exercises.concept`
array that don't have a corresponding `exercises/<kind>/<slug>`
directory (which causes the track to crash during syncing).
  • Loading branch information
ErikSchierboom authored Mar 2, 2022
1 parent 02ee5ca commit d0cd08c
Showing 1 changed file with 48 additions and 31 deletions.
79 changes: 48 additions & 31 deletions src/lint/track_config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -785,24 +785,33 @@ proc getExerciseSlugs(data: JsonNode; k: string): HashSet[string] =
if slugStr.len > 0:
result.incl slugStr

proc checkExerciseDirsAreInTrackConfig(trackDir: Path; data: JsonNode;
b: var bool; path: Path) =
proc checkExerciseDirsAndTrackConfigAreInSync(trackDir: Path; data: JsonNode;
b: var bool; path: Path) =
## Sets `b` to `false` if there is an exercise directory that is
## not an exercise `slug` in `data`.
## not an exercise `slug` in `data` and vice versa.
for exerciseKind in ["concept", "practice"]:
let exerciseSlugs = getExerciseSlugs(data, exerciseKind)
if exerciseSlugs.len > 0:
let exercisesDir = trackDir / "exercises" / exerciseKind
if dirExists(exercisesDir):
for exerciseDir in getSortedSubdirs(exercisesDir):
let dirSlug = lastPathPart(exerciseDir.string)
if dirSlug notin exerciseSlugs:
let msg = &"{q $exercisesDir} contains a directory named {q dirSlug}, " &
&"which is not a `slug` in the array of {exerciseKind} " &
"exercises. Please add the exercise to that array. " &
"If the exercise is not ready to be shown on the " &
"website, please set its `status` value to \"wip\""
b.setFalseAndPrint(msg, path)
let exerciseSlugs = getExerciseSlugs(data, exerciseKind)
let exercisesDir = trackDir / "exercises" / exerciseKind
var exerciseDirSlugs = initHashSet[string]()

if dirExists(exercisesDir):
for exerciseDir in getSortedSubdirs(exercisesDir):
exerciseDirSlugs.incl lastPathPart(exerciseDir.string)

for exerciseSlug in exerciseDirSlugs - exerciseSlugs:
let msg = &"{q $exercisesDir} contains a directory named {q exerciseSlug}, " &
&"which is not a `slug` in the array of {exerciseKind} " &
"exercises. Please add the exercise to that array. " &
"If the exercise is not ready to be shown on the " &
"website, please set its `status` value to \"wip\""
b.setFalseAndPrint(msg, path)

for exerciseSlug in exerciseSlugs - exerciseDirSlugs:
let exerciseDir = exercisesDir / exerciseSlug
let msg = &"The {q exerciseSlug} {exerciseKind} exercise is missing its " &
&"required files. Please create the {q $exerciseDir} directory with " &
"its required files"
b.setFalseAndPrint(msg, path)

proc getConceptSlugs(data: JsonNode): HashSet[string] =
result = initHashSet[string]()
Expand All @@ -822,21 +831,29 @@ proc getConceptSlugs(data: JsonNode): HashSet[string] =
if slugStr.len > 0:
result.incl slugStr

proc checkConceptDirsAreInTrackConfig(trackDir: Path; data: JsonNode;
b: var bool; path: Path) =
proc checkConceptDirsAndTrackConfigAreInSync(trackDir: Path; data: JsonNode;
b: var bool; path: Path) =
## Sets `b` to `false` if there is a concept directory that is
## not a concept `slug` in `data`.
## not a concept `slug` in `data` or vice versa.
let conceptSlugs = getConceptSlugs(data)
if conceptSlugs.len > 0:
let conceptsDir = trackDir / "concepts"
if dirExists(conceptsDir):
for conceptDir in getSortedSubdirs(conceptsDir):
let dirSlug = lastPathPart(conceptDir.string)
if dirSlug notin conceptSlugs:
let msg = &"{q $conceptsDir} contains a directory named {q dirSlug}, " &
&"which is not a `slug` in the concepts array. " &
"Please add the concept to that array"
b.setFalseAndPrint(msg, path)
let conceptsDir = trackDir / "concepts"
var conceptDirSlugs = initHashSet[string]()

if dirExists(conceptsDir):
for conceptDir in getSortedSubdirs(conceptsDir):
conceptDirSlugs.incl lastPathPart(conceptDir.string)

for conceptSlug in conceptDirSlugs - conceptSlugs:
let msg = &"{q $conceptsDir} contains a directory named {q conceptSlug}, " &
&"which is not a `slug` in the concepts array. " &
"Please add the concept to that array"
b.setFalseAndPrint(msg, path)

for conceptSlug in conceptSlugs - conceptDirSlugs:
let conceptDir = conceptsDir / conceptSlug
let msg = &"The {q conceptSlug} concept is missing its required files. " &
&"Please create the {q $conceptDir} directory with its required files"
b.setFalseAndPrint(msg, path)

proc isTrackConfigValid*(trackDir: Path): bool =
result = true
Expand All @@ -852,5 +869,5 @@ proc isTrackConfigValid*(trackDir: Path): bool =
result = satisfiesSecondPass(trackConfigContents, trackConfigPath)

if j != nil:
checkExerciseDirsAreInTrackConfig(trackDir, j, result, trackConfigPath)
checkConceptDirsAreInTrackConfig(trackDir, j, result, trackConfigPath)
checkExerciseDirsAndTrackConfigAreInSync(trackDir, j, result, trackConfigPath)
checkConceptDirsAndTrackConfigAreInSync(trackDir, j, result, trackConfigPath)

0 comments on commit d0cd08c

Please sign in to comment.