From 5c83f09d880dadfc240cbef08b99a4ad2c60758a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20SZKIBA?= Date: Thu, 14 Nov 2024 17:31:41 +0100 Subject: [PATCH 1/3] feat: added TypeScript API declaration file checker (index.d.ts) --- README.md | 1 + checker_module.go | 49 +++++++++++++++++++++++++++++++++++++ checks.go | 1 + compliance_gen.go | 1 + docs/compliance.schema.json | 3 ++- docs/compliance.schema.yaml | 1 + 6 files changed, 55 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 677ebbb..de607ca 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ The detailed result of the checks are described in a [JSON schema](https://grafa - `build` - checks if k6 can be built with the extension - `smoke` - checks if the smoke test script exists and runs successfully (`smoke.js`, `smoke.ts`, `smoke.test.js` or `smoke.test.ts` in the `test`,`tests`, `examples` or the base directory) - `codeowners` - checks if there is a CODEOWNERS file (for official extensions) + - `types` - checks if the TypeScript API declaration file exists (`index.d.ts` in the `docs` or the base directory) ## Install diff --git a/checker_module.go b/checker_module.go index 93e967b..7f491c3 100644 --- a/checker_module.go +++ b/checker_module.go @@ -14,6 +14,7 @@ import ( type moduleChecker struct { file *modfile.File exe string + js bool } func newModuleChecker() *moduleChecker { @@ -58,6 +59,28 @@ func (mc *moduleChecker) canBuild(ctx context.Context, dir string) *checkResult return checkError(err) } + out, err := exec.CommandContext(ctx, exe, "version").CombinedOutput() //nolint:gosec + if err != nil { + return checkError(err) + } + + rex, err := regexp.Compile("(?i) " + mc.file.Module.Mod.String() + "[^,]+, [^ ]+ \\[(?P[a-z]+)\\]") + if err != nil { + return checkError(err) + } + + subs := rex.FindAllSubmatch(out, -1) + if subs == nil { + return checkFailed(mc.file.Module.Mod.String() + " is not in the version command's output") + } + + for _, one := range subs { + if string(one[rex.SubexpIndex("type")]) == "js" { + mc.js = true + break + } + } + mc.exe = exe return checkPassed("can be built with the latest k6 version") @@ -96,3 +119,29 @@ func (mc *moduleChecker) smoke(ctx context.Context, dir string) *checkResult { return checkPassed("`%s` successfully run with k6", shortname) } + +var reIndexDTS = regexp.MustCompile("^index.d.ts$") + +func (mc *moduleChecker) types(_ context.Context, dir string) *checkResult { + if mc.exe == "" { + return checkFailed("can't build") + } + + if !mc.js { + return checkPassed("skipped due to output extension") + } + + _, shortname, err := findFile(reIndexDTS, + dir, + filepath.Join(dir, "docs"), + ) + if err != nil { + return checkError(err) + } + + if len(shortname) > 0 { + return checkPassed("found `index.d.ts` file") + } + + return checkFailed("no `index.d.ts` file found") +} diff --git a/checks.go b/checks.go index 970d207..8e9e5f2 100644 --- a/checks.go +++ b/checks.go @@ -45,6 +45,7 @@ func checkDefinitions(official bool) []checkDefinition { {id: CheckerVersions, score: 5, fn: gitCheck.hasVersions}, {id: CheckerBuild, score: 5, fn: modCheck.canBuild}, {id: CheckerSmoke, score: 2, fn: modCheck.smoke}, + {id: CheckerTypes, score: 2, fn: modCheck.types}, } if !official { diff --git a/compliance_gen.go b/compliance_gen.go index 0ffcdab..7dc729f 100644 --- a/compliance_gen.go +++ b/compliance_gen.go @@ -33,6 +33,7 @@ const CheckerModule Checker = "module" const CheckerReadme Checker = "readme" const CheckerReplace Checker = "replace" const CheckerSmoke Checker = "smoke" +const CheckerTypes Checker = "types" const CheckerVersions Checker = "versions" // The result of the extension's k6 compliance checks. diff --git a/docs/compliance.schema.json b/docs/compliance.schema.json index 6d03e65..64e895b 100644 --- a/docs/compliance.schema.json +++ b/docs/compliance.schema.json @@ -84,7 +84,8 @@ "versions", "build", "smoke", - "codeowners" + "codeowners", + "types" ] } } diff --git a/docs/compliance.schema.yaml b/docs/compliance.schema.yaml index 5e98be3..af24bb0 100644 --- a/docs/compliance.schema.yaml +++ b/docs/compliance.schema.yaml @@ -80,3 +80,4 @@ $defs: - build - smoke - codeowners + - types From 06c667801591fe32b692411ff337e31a8fd9dc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20SZKIBA?= Date: Thu, 14 Nov 2024 17:32:59 +0100 Subject: [PATCH 2/3] feat: skip smoke check for output extensions --- checker_module.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/checker_module.go b/checker_module.go index 7f491c3..95f0149 100644 --- a/checker_module.go +++ b/checker_module.go @@ -94,6 +94,10 @@ func (mc *moduleChecker) smoke(ctx context.Context, dir string) *checkResult { return checkFailed("can't build") } + if !mc.js { + return checkPassed("skipped due to output extension") + } + filename, shortname, err := findFile(reSmoke, dir, filepath.Join(dir, "test"), From cf26d6464e4cf6452fe0c2490facd4c31b6ca502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20SZKIBA?= Date: Thu, 14 Nov 2024 17:42:22 +0100 Subject: [PATCH 3/3] feat: skip "examples" directory check for output extensions --- checker_examples.go | 50 -------------------------------------------- checker_module.go | 51 +++++++++++++++++++++++++++++++++++++++++++++ checks.go | 2 +- 3 files changed, 52 insertions(+), 51 deletions(-) delete mode 100644 checker_examples.go diff --git a/checker_examples.go b/checker_examples.go deleted file mode 100644 index ca7d718..0000000 --- a/checker_examples.go +++ /dev/null @@ -1,50 +0,0 @@ -package k6lint - -import ( - "context" - "errors" - "io/fs" - "os" - "path/filepath" -) - -//nolint:forbidigo -func checkerExamples(_ context.Context, dir string) *checkResult { - dir = filepath.Join(dir, "examples") - - info, err := os.Stat(dir) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return checkFailed("missing `examples` directory") - } - - return checkError(err) - } - - if !info.IsDir() { - return checkFailed("`examples` is not a directory") - } - - hasRegular := false - - err = filepath.WalkDir(dir, func(_ string, entry fs.DirEntry, err error) error { - if err != nil { - return err - } - - if entry.Type().IsRegular() { - hasRegular = true - } - - return nil - }) - if err != nil { - return checkError(err) - } - - if hasRegular { - return checkPassed("found `examples` as examples directory") - } - - return checkFailed("no examples found in the `examples` directory") -} diff --git a/checker_module.go b/checker_module.go index 95f0149..941a01f 100644 --- a/checker_module.go +++ b/checker_module.go @@ -2,7 +2,9 @@ package k6lint import ( "context" + "errors" "fmt" + "io/fs" "os" "os/exec" "path/filepath" @@ -149,3 +151,52 @@ func (mc *moduleChecker) types(_ context.Context, dir string) *checkResult { return checkFailed("no `index.d.ts` file found") } + +//nolint:forbidigo +func (mc *moduleChecker) examples(_ context.Context, dir string) *checkResult { + if mc.exe == "" { + return checkFailed("can't build") + } + + if !mc.js { + return checkPassed("skipped due to output extension") + } + + dir = filepath.Join(dir, "examples") + + info, err := os.Stat(dir) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return checkFailed("missing `examples` directory") + } + + return checkError(err) + } + + if !info.IsDir() { + return checkFailed("`examples` is not a directory") + } + + hasRegular := false + + err = filepath.WalkDir(dir, func(_ string, entry fs.DirEntry, err error) error { + if err != nil { + return err + } + + if entry.Type().IsRegular() { + hasRegular = true + } + + return nil + }) + if err != nil { + return checkError(err) + } + + if hasRegular { + return checkPassed("found `examples` as examples directory") + } + + return checkFailed("no examples found in the `examples` directory") +} diff --git a/checks.go b/checks.go index 8e9e5f2..1a84e36 100644 --- a/checks.go +++ b/checks.go @@ -39,12 +39,12 @@ func checkDefinitions(official bool) []checkDefinition { {id: CheckerModule, score: 2, fn: modCheck.hasGoModule}, {id: CheckerReplace, score: 2, fn: modCheck.hasNoReplace}, {id: CheckerReadme, score: 5, fn: checkerReadme}, - {id: CheckerExamples, score: 2, fn: checkerExamples}, {id: CheckerLicense, score: 5, fn: checkerLicense}, {id: CheckerGit, score: 1, fn: gitCheck.isWorkDir}, {id: CheckerVersions, score: 5, fn: gitCheck.hasVersions}, {id: CheckerBuild, score: 5, fn: modCheck.canBuild}, {id: CheckerSmoke, score: 2, fn: modCheck.smoke}, + {id: CheckerExamples, score: 2, fn: modCheck.examples}, {id: CheckerTypes, score: 2, fn: modCheck.types}, }