From def968409da6eef45df6a144eb958fb730a74218 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Sun, 4 Feb 2018 07:24:53 -0800 Subject: [PATCH] lint: Check for README presence (#98) * exercise: add HasReadme * fixtures: Add READMEs to linted tracks After https://github.com/exercism/meta/issues/15, READMEs are to be generated and placed into each track's exercise implementation directory. This may even become required at some point. Prepare for this requirement by placing READMEs in the correct places. Note: numbers/zero does not require a README as it is foregone. * lint: Check for README presence After https://github.com/exercism/meta/issues/15, READMEs are to be generated and placed into each track's exercise implementation directory. My (unsubstantiated) assumption is that they will be required to be present after Nextercism, as we don't want to keep generating READMEs on the fly. If this assumption is correct, it seems necessary to check that READMEs are present on all exercises. With the attached fixture change and attached test changes: * The added TestMissingReadme would fail if the attached code change were incorrect. * The added TestLintTrack case on missing-readmes would fail without the attached code change. Closes https://github.com/exercism/discussions/issues/200 Closes https://github.com/exercism/configlet/issues/86 --- cmd/lint.go | 23 ++++++++++++++++ cmd/lint_test.go | 26 +++++++++++++++++++ .../valid-track/exercises/aluminum/README.md | 0 fixtures/missing-readme/config.json | 14 ++++++++++ .../exercises/missing-readme/example.ext | 0 .../exercises/missing-readme/test.ext | 0 fixtures/numbers/exercises/one/README.md | 0 fixtures/numbers/exercises/three/README.md | 0 fixtures/numbers/exercises/two/README.md | 0 track/exercise.go | 11 ++++++++ 10 files changed, 74 insertions(+) create mode 100644 fixtures/lint/valid-track/exercises/aluminum/README.md create mode 100644 fixtures/missing-readme/config.json create mode 100644 fixtures/missing-readme/exercises/missing-readme/example.ext create mode 100644 fixtures/missing-readme/exercises/missing-readme/test.ext create mode 100644 fixtures/numbers/exercises/one/README.md create mode 100644 fixtures/numbers/exercises/three/README.md create mode 100644 fixtures/numbers/exercises/two/README.md diff --git a/cmd/lint.go b/cmd/lint.go index 1ee5056..dc0d2aa 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -89,6 +89,10 @@ func lintTrack(path string) bool { check: missingMetadata, msg: "An implementation for '%v' was found, but config.json does not reference this exercise.", }, + { + check: missingReadme, + msg: "The implementation for '%v' is missing a README.", + }, { check: missingSolution, msg: "The implementation for '%v' is missing an example solution.", @@ -201,6 +205,25 @@ func missingSolution(t track.Track) []string { return slugs } +func missingReadme(t track.Track) []string { + readmes := map[string]bool{} + for _, exercise := range t.Exercises { + readmes[exercise.Slug] = exercise.HasReadme() + } + // Don't complain about missing readmes in foregone exercises. + for _, slug := range t.Config.ForegoneSlugs { + readmes[slug] = true + } + + slugs := []string{} + for slug, ok := range readmes { + if !ok { + slugs = append(slugs, slug) + } + } + return slugs +} + func missingTestSuite(t track.Track) []string { tests := map[string]bool{} for _, exercise := range t.Exercises { diff --git a/cmd/lint_test.go b/cmd/lint_test.go index 44ee1ae..9fced1f 100644 --- a/cmd/lint_test.go +++ b/cmd/lint_test.go @@ -45,6 +45,11 @@ func TestLintTrack(t *testing.T) { path: "../fixtures/broken-maintainers", expected: true, }, + { + desc: "should fail when given a track missing READMEs.", + path: "../fixtures/missing-readme", + expected: true, + }, { desc: "should not fail when given a track with all of its bits in place.", path: "../fixtures/lint/valid-track", @@ -110,6 +115,27 @@ func TestMissingMetadata(t *testing.T) { assert.Equal(t, "cherry", slugs[1]) } +func TestMissingReadme(t *testing.T) { + track := track.Track{ + Exercises: []track.Exercise{ + {Slug: "apple"}, + {Slug: "banana", ReadmePath: "README.md"}, + {Slug: "cherry"}, + }, + } + + slugs := missingReadme(track) + + if len(slugs) != 2 { + t.Fatalf("Expected missing READMEs in 2 exercises, missing in %d", len(slugs)) + } + + sort.Strings(slugs) + + assert.Equal(t, "apple", slugs[0]) + assert.Equal(t, "cherry", slugs[1]) +} + func TestMissingSolution(t *testing.T) { track := track.Track{ Exercises: []track.Exercise{ diff --git a/fixtures/lint/valid-track/exercises/aluminum/README.md b/fixtures/lint/valid-track/exercises/aluminum/README.md new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/missing-readme/config.json b/fixtures/missing-readme/config.json new file mode 100644 index 0000000..352a99b --- /dev/null +++ b/fixtures/missing-readme/config.json @@ -0,0 +1,14 @@ +{ + "slug": "missing-readme", + "language": "Missing Readme", + "repository": "https://github.com/exercism/missing-readme", + "active": true, + "exercises": [ + { + "uuid": "missingmissing", + "slug": "missing-readme", + "topics": [], + "difficulty": 1 + } + ] +} diff --git a/fixtures/missing-readme/exercises/missing-readme/example.ext b/fixtures/missing-readme/exercises/missing-readme/example.ext new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/missing-readme/exercises/missing-readme/test.ext b/fixtures/missing-readme/exercises/missing-readme/test.ext new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/numbers/exercises/one/README.md b/fixtures/numbers/exercises/one/README.md new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/numbers/exercises/three/README.md b/fixtures/numbers/exercises/three/README.md new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/numbers/exercises/two/README.md b/fixtures/numbers/exercises/two/README.md new file mode 100644 index 0000000..e69de29 diff --git a/track/exercise.go b/track/exercise.go index 944223d..aac51f8 100644 --- a/track/exercise.go +++ b/track/exercise.go @@ -11,6 +11,7 @@ import ( // Exercise is an implementation of an Exercism exercise. type Exercise struct { Slug string + ReadmePath string SolutionPath string TestSuitePath string } @@ -31,6 +32,11 @@ func NewExercise(root string, pg PatternGroup) (Exercise, error) { return ex, err } + err = setPath(root, "README\\.md", &ex.ReadmePath) + if err != nil { + return ex, err + } + return ex, err } @@ -62,6 +68,11 @@ func setPath(root, pattern string, field *string) error { return filepath.Walk(root, walkFn) } +// HasReadme checks that an exercise has a README. +func (ex Exercise) HasReadme() bool { + return ex.ReadmePath != "" +} + // HasTestSuite checks that an exercise has a test suite. func (ex Exercise) HasTestSuite() bool { return ex.TestSuitePath != ""