Skip to content

Commit 6691484

Browse files
fmt: support formatting track config (#772)
And add integration tests for `fmt`. Refs: #478 Closes: #479
1 parent 4015ff9 commit 6691484

8 files changed

Lines changed: 685 additions & 27 deletions

File tree

.github/workflows/tests.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ jobs:
3737
- name: Checkout code
3838
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
3939

40+
- name: Configure the git user # Required to create a commit in our binary tests
41+
run: |
42+
git config --global user.email "66069679+exercism-bot@users.noreply.github.com"
43+
git config --global user.name "Exercism Bot"
44+
4045
- name: On Linux, install musl
4146
if: matrix.os == 'linux'
4247
run: ./.github/bin/linux-install-musl

src/exec.nim

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ proc gitCheckout(dir, hash: string) =
108108
let args = ["-C", dir, "checkout", "--force", hash]
109109
discard gitCheck(0, args)
110110

111+
proc fixAverageRunTimeInConfigJson(file, oldValue, newValue: string) =
112+
## For testing purposes, we clone track repos and checkout commits
113+
## where the `average_run_time` value in the `config.json` file
114+
## is a float, which has later since changed to an int.
115+
## In order to not break existing tests, we manually change the value
116+
## from a float to an int.
117+
let configJson = readFile(file)
118+
.replace(
119+
&""""average_run_time": {oldValue}""",
120+
&""""average_run_time": {newValue}""")
121+
writeFile(file, configJson)
122+
let args = ["-C", parentDir(file), "commit", "-a", "-m", "config: convert `average_run_time` to int"]
123+
discard gitCheck(0, args)
124+
111125
proc setupExercismRepo*(repoName, dest, hash: string; shallow = false) =
112126
## If there is no directory at `dest`, clones the Exercism repo named
113127
## `repoName` to `dest`.
@@ -117,6 +131,11 @@ proc setupExercismRepo*(repoName, dest, hash: string; shallow = false) =
117131
cloneExercismRepo(repoName, dest, shallow)
118132
gitCheckout(dest, hash)
119133

134+
if repoName == "nim":
135+
fixAverageRunTimeInConfigJson(dest / "config.json", "3.0", "3")
136+
elif repoName == "elixir":
137+
fixAverageRunTimeInConfigJson(dest / "config.json", "4.1", "4")
138+
120139
func conciseDiff(s: string): string =
121140
## Returns the lines of `s` that begin with a `+` or `-` character.
122141
result = newStringOfCap(s.len)

src/fmt/fmt.nim

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import std/[os, strformat, strutils]
2-
import "."/[approaches, articles, exercises]
2+
import "."/[approaches, articles, exercises, track_config]
33
import ".."/[cli, helpers, logger, sync/sync_common, sync/sync,
44
types_exercise_config, types_track_config]
55

66
type
77
DocumentKind* = enum
8-
dkExerciseConfig,
8+
dkTrackConfig,
9+
dkConceptExerciseConfig,
10+
dkPracticeExerciseConfig,
911
dkApproachesConfig,
1012
dkArticlesConfig
1113

@@ -15,11 +17,23 @@ type
1517
formattedDocument: string
1618

1719
iterator getConfigPaths(trackExerciseSlugs: TrackExerciseSlugs,
18-
trackExercisesDir: string): (ExerciseKind, DocumentKind, string) =
20+
conf: Conf): (DocumentKind, string) =
21+
let trackDir = conf.trackDir
22+
23+
## Yield the track's `config.json` file, but only when the user
24+
## is not formatting a single exercise
25+
if conf.action.exerciseFmt.len == 0:
26+
yield (dkTrackConfig, trackDir / "config.json")
27+
1928
## Yields the `.meta/config.json`, `.approaches/config.json` and
2029
## `.articles/config.json` paths for each exercise in
2130
## `trackExerciseSlugs` in `trackExercisesDir`.
31+
let trackExercisesDir = trackDir / "exercises"
2232
for exerciseKind in [ekConcept, ekPractice]:
33+
let documentKind =
34+
case exerciseKind
35+
of ekConcept: dkConceptExerciseConfig
36+
of ekPractice: dkPracticeExerciseConfig
2337
let slugs =
2438
case exerciseKind
2539
of ekConcept: trackExerciseSlugs.`concept`
@@ -34,34 +48,37 @@ iterator getConfigPaths(trackExerciseSlugs: TrackExerciseSlugs,
3448
for slug in slugs:
3549
trackExerciseConfigPath.truncateAndAdd(startLen, slug)
3650
trackExerciseConfigPath.addExerciseConfigPath()
37-
yield (exerciseKind, dkExerciseConfig, trackExerciseConfigPath)
51+
yield (documentKind, trackExerciseConfigPath)
3852

3953
trackExerciseConfigPath.truncateAndAdd(startLen, slug)
4054
trackExerciseConfigPath.addApproachesConfigPath()
4155
if fileExists(trackExerciseConfigPath):
42-
yield (exerciseKind, dkApproachesConfig, trackExerciseConfigPath)
56+
yield (dkApproachesConfig, trackExerciseConfigPath)
4357

4458
trackExerciseConfigPath.truncateAndAdd(startLen, slug)
4559
trackExerciseConfigPath.addArticlesConfigPath()
4660
if fileExists(trackExerciseConfigPath):
47-
yield (exerciseKind, dkArticlesConfig, trackExerciseConfigPath)
61+
yield (dkArticlesConfig, trackExerciseConfigPath)
4862

4963
proc fmtImpl(trackExerciseSlugs: TrackExerciseSlugs,
50-
trackDir: string): seq[PathAndFormattedDocument] =
51-
## Reads the config files for every slug in `trackExerciseSlugs`
52-
## in `trackExerciseDir`.
64+
conf: Conf): seq[PathAndFormattedDocument] =
65+
## Reads the track config file and all exercise config files
66+
## for every slug in `trackExerciseSlugs` in `trackExerciseDir`.
5367
## This includes `.meta/config.json`, `.approaches/config.json`
54-
## and `.articles/config.json`.
68+
## and `.articles/config.json` for each exercise, and `config.json`
69+
## for the track.
5570
##
5671
## Returns a seq of (document kind, path, formatted document) objects
57-
## containing every exercise's configs that are not already formatted.
58-
let trackExercisesDir = trackDir / "exercises"
72+
## containing every document that is not already formatted.
5973
var seenUnformatted = false
60-
for (exerciseKind, documentKind, configPath) in getConfigPaths(trackExerciseSlugs,
61-
trackExercisesDir):
74+
let trackDir = conf.trackDir
75+
for (documentKind, configPath) in getConfigPaths(trackExerciseSlugs,
76+
conf):
6277
let formatted =
6378
case documentKind
64-
of dkExerciseConfig: formatExerciseConfigFile(exerciseKind, configPath)
79+
of dkTrackConfig: formatTrackConfigFile(configPath)
80+
of dkConceptExerciseConfig: formatExerciseConfigFile(ekConcept, configPath)
81+
of dkPracticeExerciseConfig: formatExerciseConfigFile(ekPractice, configPath)
6582
of dkApproachesConfig: formatApproachesConfigFile(configPath)
6683
of dkArticlesConfig: formatArticlesConfigFile(configPath)
6784

@@ -116,7 +133,7 @@ proc fmt*(conf: Conf) =
116133
logNormal("Looking for exercises that lack a formatted '.meta/config.json', " &
117134
"'.approaches/config.json'\nor '.articles/config.json' file...")
118135

119-
let pairs = fmtImpl(trackExerciseSlugs, conf.trackDir)
136+
let pairs = fmtImpl(trackExerciseSlugs, conf)
120137

121138
let userExercise = conf.action.exerciseFmt
122139
if pairs.len > 0:

0 commit comments

Comments
 (0)