diff --git a/detect/research_test.go b/detect/research_test.go new file mode 100644 index 0000000..d6decf3 --- /dev/null +++ b/detect/research_test.go @@ -0,0 +1,98 @@ +package detect + +import ( + "slices" + "testing" + + "github.com/git-pkgs/brief" +) + +func TestResearchToolDetectors(t *testing.T) { + dir := t.TempDir() + files := map[string]string{ + "analysis/notebook.ipynb": "{}\n", + "report.qmd": "---\ntitle: Analysis\n---\n", + "workflow/Snakefile": "rule all:\n input: []\n", + "nextflow.config": "manifest { name = 'research-pipeline' }\n", + "main.nf": "workflow { }\n", + "pyproject.toml": "[project]\nname = \"research-tools\"\nversion = \"0.1.0\"\n", + "tox.ini": "[tox]\nenv_list = py312\n", + "benchmarks/asv.conf.json": `{"repo": "."}`, + "DESCRIPTION": `Package: researchtools +Version: 0.1.0 +Suggests: + covr, + knitr, + pkgdown, + rmarkdown, + testthat +Config/roxygen2/version: 7.3.3 +Config/testthat/edition: 3 +VignetteBuilder: knitr +`, + "README.Rmd": "---\ntitle: Research tools\n---\n", + "_pkgdown.yml": "url: https://example.com\n", + "renv.lock": `{"R":{"Version":"4.5.0"}}`, + "Project.toml": "name = \"ResearchTools\"\nuuid = \"11111111-1111-1111-1111-111111111111\"\n", + "src/ResearchTools.jl": "module ResearchTools\nend\n", + "docs/Project.toml": "[deps]\nDocumenter = \"e30172f5-a6a5-5a46-863b-614d45cd2de4\"\n", + "docs/make.jl": "using Documenter\n", + ".JuliaFormatter.toml": "margin = 92\n", + ".github/workflows/format.yml": "name: format\non: push\njobs:\n format:\n runs-on: ubuntu-latest\n steps:\n - uses: fredrikekre/runic-action@v1\n", + "model.f90": "program model\nend program model\n", + "fortitude.toml": "line-length = 100\n", + } + for path, content := range files { + writeProjectFile(t, dir, path, content) + } + + report := runOn(t, dir) + want := []struct { + category string + name string + }{ + {category: "environment", name: "Jupyter"}, + {category: "docs", name: "Quarto"}, + {category: "build", name: "Snakemake"}, + {category: "build", name: "Nextflow"}, + {category: "test", name: "tox"}, + {category: "test", name: "ASV"}, + {category: "test", name: "testthat"}, + {category: "coverage", name: "covr"}, + {category: "docs", name: "pkgdown"}, + {category: "docs", name: "roxygen2"}, + {category: "docs", name: "knitr"}, + {category: "docs", name: "R Markdown"}, + {category: "docs", name: "Documenter.jl"}, + {category: "format", name: "JuliaFormatter"}, + {category: "format", name: "Runic"}, + {category: "lint", name: "Fortitude"}, + } + for _, item := range want { + assertToolDetected(t, report, item.category, item.name) + } + if !slices.ContainsFunc(report.PackageManagers, func(d brief.Detection) bool { + return d.Name == "renv" + }) { + t.Error("expected renv package manager") + } +} + +func TestSnakemakeSmkConfigFile(t *testing.T) { + dir := t.TempDir() + const path = "workflow/rules/common.smk" + const configPattern = "**/*.smk" + writeProjectFile(t, dir, path, "rule all:\n input: []\n") + + report := runOn(t, dir) + for _, tool := range report.Tools["build"] { + if tool.Name != "Snakemake" { + continue + } + if !slices.Contains(tool.ConfigFiles, configPattern) { + t.Errorf("Snakemake config files = %v, want %q", tool.ConfigFiles, configPattern) + } + return + } + t.Fatal("expected Snakemake in build category") +} diff --git a/knowledge/_shared/jupyter.toml b/knowledge/_shared/jupyter.toml new file mode 100644 index 0000000..7021742 --- /dev/null +++ b/knowledge/_shared/jupyter.toml @@ -0,0 +1,22 @@ +[tool] +name = "Jupyter" +category = "environment" +homepage = "https://jupyter.org" +docs = "https://docs.jupyter.org/en/latest/" +repo = "https://github.com/jupyter/jupyter" +description = "Interactive computing environment for notebook documents" + +[detect] +files = ["*.ipynb", "**/*.ipynb"] +dependencies = ["jupyter", "jupyterlab", "notebook"] +dev_dependencies = ["jupyter", "jupyterlab", "notebook"] + +[commands] +run = "jupyter lab" +alternatives = ["jupyter notebook"] + +[config] +files = ["jupyter_server_config.py", "jupyter_notebook_config.py", "jupyter_lab_config.py"] + +[taxonomy] +role = ["application"] diff --git a/knowledge/_shared/nextflow.toml b/knowledge/_shared/nextflow.toml new file mode 100644 index 0000000..d9585ca --- /dev/null +++ b/knowledge/_shared/nextflow.toml @@ -0,0 +1,21 @@ +[tool] +name = "Nextflow" +category = "build" +homepage = "https://www.nextflow.io" +docs = "https://www.nextflow.io/docs/latest/" +repo = "https://github.com/nextflow-io/nextflow" +description = "Workflow system for portable data pipelines" + +[detect] +files = ["nextflow.config", "main.nf", "*.nf", "**/*.nf"] + +[commands] +run = "nextflow run main.nf" + +[config] +files = ["nextflow.config"] + +[taxonomy] +role = ["orchestrator"] +function = ["automation"] +domain = ["scientific-computing"] diff --git a/knowledge/_shared/quarto.toml b/knowledge/_shared/quarto.toml new file mode 100644 index 0000000..4005268 --- /dev/null +++ b/knowledge/_shared/quarto.toml @@ -0,0 +1,22 @@ +[tool] +name = "Quarto" +category = "docs" +homepage = "https://quarto.org" +docs = "https://quarto.org/docs/get-started/" +repo = "https://github.com/quarto-dev/quarto-cli" +description = "Scientific and technical publishing system" + +[detect] +files = ["_quarto.yml", "_quarto.yaml", "*.qmd", "**/*.qmd"] +dependencies = ["quarto", "quarto-dev/quarto-actions/setup"] +dev_dependencies = ["quarto"] + +[commands] +run = "quarto render" +alternatives = ["quarto preview"] + +[config] +files = ["_quarto.yml", "_quarto.yaml"] + +[taxonomy] +function = ["documentation", "site-generation"] diff --git a/knowledge/_shared/snakemake.toml b/knowledge/_shared/snakemake.toml new file mode 100644 index 0000000..d91675e --- /dev/null +++ b/knowledge/_shared/snakemake.toml @@ -0,0 +1,23 @@ +[tool] +name = "Snakemake" +category = "build" +homepage = "https://snakemake.github.io" +docs = "https://snakemake.readthedocs.io/en/stable/" +repo = "https://github.com/snakemake/snakemake" +description = "Workflow system for reproducible data analysis" + +[detect] +files = ["Snakefile", "**/Snakefile", "*.smk", "**/*.smk"] +dependencies = ["snakemake"] +dev_dependencies = ["snakemake"] + +[commands] +run = "snakemake" + +[config] +files = ["Snakefile", "**/Snakefile", "*.smk", "**/*.smk"] + +[taxonomy] +role = ["orchestrator"] +function = ["automation"] +domain = ["scientific-computing"] diff --git a/knowledge/fortran/fortitude.toml b/knowledge/fortran/fortitude.toml new file mode 100644 index 0000000..5ae2e32 --- /dev/null +++ b/knowledge/fortran/fortitude.toml @@ -0,0 +1,26 @@ +[tool] +name = "Fortitude" +category = "lint" +homepage = "https://fortitude.readthedocs.io/" +docs = "https://fortitude.readthedocs.io/" +repo = "https://github.com/PlasmaFAIR/fortitude" +description = "Linter for Fortran" + +[detect] +files = ["fortitude.toml", ".fortitude.toml"] +dependencies = ["fortitude", "fortitude-lint"] +dev_dependencies = ["fortitude", "fortitude-lint"] +ecosystems = ["fortran"] +[detect.key_exists] +"fpm.toml" = ["extra.fortitude"] +"pyproject.toml" = ["tool.fortitude"] + +[commands] +run = "fortitude check" +alternatives = ["fortitude check --fix"] + +[config] +files = ["fortitude.toml", ".fortitude.toml", "fpm.toml", "pyproject.toml"] + +[taxonomy] +role = ["linter"] diff --git a/knowledge/julia/documenter.toml b/knowledge/julia/documenter.toml new file mode 100644 index 0000000..699e6ca --- /dev/null +++ b/knowledge/julia/documenter.toml @@ -0,0 +1,24 @@ +[tool] +name = "Documenter.jl" +category = "docs" +homepage = "https://documenter.juliadocs.org/stable/" +docs = "https://documenter.juliadocs.org/stable/" +repo = "https://github.com/JuliaDocs/Documenter.jl" +description = "Documentation generator for Julia packages" + +[detect] +dependencies = ["Documenter"] +dev_dependencies = ["Documenter"] +ecosystems = ["julia"] +[detect.file_contains] +"docs/Project.toml" = ["Documenter"] +"docs/make.jl" = ["Documenter"] + +[commands] +run = "julia --project=docs docs/make.jl" + +[config] +files = ["docs/Project.toml", "docs/make.jl"] + +[taxonomy] +function = ["documentation", "site-generation"] diff --git a/knowledge/julia/juliaformatter.toml b/knowledge/julia/juliaformatter.toml new file mode 100644 index 0000000..e61d137 --- /dev/null +++ b/knowledge/julia/juliaformatter.toml @@ -0,0 +1,22 @@ +[tool] +name = "JuliaFormatter" +category = "format" +homepage = "https://juliaeditorsupport.github.io/JuliaFormatter.jl/stable/" +docs = "https://juliaeditorsupport.github.io/JuliaFormatter.jl/stable/" +repo = "https://github.com/JuliaEditorSupport/JuliaFormatter.jl" +description = "Code formatter for Julia" + +[detect] +files = [".JuliaFormatter.toml"] +dependencies = ["JuliaFormatter"] +dev_dependencies = ["JuliaFormatter"] +ecosystems = ["julia"] + +[commands] +run = "julia -e 'using JuliaFormatter; format(\".\")'" + +[config] +files = [".JuliaFormatter.toml"] + +[taxonomy] +role = ["formatter"] diff --git a/knowledge/julia/runic.toml b/knowledge/julia/runic.toml new file mode 100644 index 0000000..016416a --- /dev/null +++ b/knowledge/julia/runic.toml @@ -0,0 +1,15 @@ +[tool] +name = "Runic" +category = "format" +homepage = "https://github.com/fredrikekre/Runic.jl" +docs = "https://github.com/fredrikekre/Runic.jl" +repo = "https://github.com/fredrikekre/Runic.jl" +description = "Code formatter for Julia" + +[detect] +dependencies = ["Runic", "fredrikekre/runic-action"] +dev_dependencies = ["Runic"] +ecosystems = ["julia"] + +[taxonomy] +role = ["formatter"] diff --git a/knowledge/python/asv.toml b/knowledge/python/asv.toml new file mode 100644 index 0000000..41af4e0 --- /dev/null +++ b/knowledge/python/asv.toml @@ -0,0 +1,23 @@ +[tool] +name = "ASV" +category = "test" +homepage = "https://asv.readthedocs.io/en/stable/" +docs = "https://asv.readthedocs.io/en/stable/" +repo = "https://github.com/airspeed-velocity/asv" +description = "Python benchmarking tool for tracking performance across revisions" + +[detect] +files = ["asv.conf.json", "**/asv.conf.json"] +dependencies = ["asv"] +dev_dependencies = ["asv"] +ecosystems = ["python"] + +[commands] +run = "asv run" +alternatives = ["asv publish", "asv preview"] + +[config] +files = ["asv.conf.json", "**/asv.conf.json"] + +[taxonomy] +function = ["benchmarking"] diff --git a/knowledge/python/tox.toml b/knowledge/python/tox.toml new file mode 100644 index 0000000..71051e3 --- /dev/null +++ b/knowledge/python/tox.toml @@ -0,0 +1,25 @@ +[tool] +name = "tox" +category = "test" +homepage = "https://tox.wiki/en/stable/" +docs = "https://tox.wiki/en/stable/" +repo = "https://github.com/tox-dev/tox" +description = "Python test environment orchestrator" + +[detect] +files = ["tox.ini"] +dependencies = ["tox"] +dev_dependencies = ["tox"] +ecosystems = ["python"] +[detect.key_exists] +"pyproject.toml" = ["tool.tox"] + +[commands] +run = "tox" + +[config] +files = ["tox.ini", "pyproject.toml"] + +[taxonomy] +role = ["orchestrator"] +function = ["testing"] diff --git a/knowledge/r/covr.toml b/knowledge/r/covr.toml new file mode 100644 index 0000000..4f6338b --- /dev/null +++ b/knowledge/r/covr.toml @@ -0,0 +1,23 @@ +[tool] +name = "covr" +category = "coverage" +homepage = "https://covr.r-lib.org" +docs = "https://covr.r-lib.org" +repo = "https://github.com/r-lib/covr" +description = "Test coverage reporting for R packages" + +[detect] +files = [".covrignore"] +dependencies = ["covr"] +dev_dependencies = ["covr"] +ecosystems = ["r"] + +[commands] +run = "Rscript -e 'covr::package_coverage()'" +alternatives = ["Rscript -e 'covr::report()'"] + +[config] +files = [".covrignore"] + +[taxonomy] +function = ["coverage"] diff --git a/knowledge/r/knitr.toml b/knowledge/r/knitr.toml new file mode 100644 index 0000000..85d4056 --- /dev/null +++ b/knowledge/r/knitr.toml @@ -0,0 +1,17 @@ +[tool] +name = "knitr" +category = "docs" +homepage = "https://yihui.org/knitr/" +docs = "https://yihui.org/knitr/" +repo = "https://github.com/yihui/knitr" +description = "Literate programming engine for R documents" + +[detect] +dependencies = ["knitr"] +dev_dependencies = ["knitr"] +ecosystems = ["r"] +[detect.file_contains] +"DESCRIPTION" = ["VignetteBuilder: knitr"] + +[taxonomy] +function = ["documentation"] diff --git a/knowledge/r/pkgdown.toml b/knowledge/r/pkgdown.toml new file mode 100644 index 0000000..e63a8f7 --- /dev/null +++ b/knowledge/r/pkgdown.toml @@ -0,0 +1,22 @@ +[tool] +name = "pkgdown" +category = "docs" +homepage = "https://pkgdown.r-lib.org" +docs = "https://pkgdown.r-lib.org" +repo = "https://github.com/r-lib/pkgdown" +description = "Static documentation sites for R packages" + +[detect] +files = ["_pkgdown.yml", "_pkgdown.yaml"] +dependencies = ["pkgdown"] +dev_dependencies = ["pkgdown"] +ecosystems = ["r"] + +[commands] +run = "Rscript -e 'pkgdown::build_site()'" + +[config] +files = ["_pkgdown.yml", "_pkgdown.yaml"] + +[taxonomy] +function = ["documentation", "site-generation"] diff --git a/knowledge/r/renv.toml b/knowledge/r/renv.toml new file mode 100644 index 0000000..ae254a0 --- /dev/null +++ b/knowledge/r/renv.toml @@ -0,0 +1,26 @@ +[tool] +name = "renv" +category = "package_manager" +homepage = "https://rstudio.github.io/renv/" +docs = "https://rstudio.github.io/renv/" +repo = "https://github.com/rstudio/renv" +description = "Reproducible package environments for R projects" + +[detect] +files = ["renv.lock", "renv/activate.R"] +dependencies = ["renv"] +dev_dependencies = ["renv"] +ecosystems = ["r"] +[detect.file_contains] +".Rprofile" = ["renv/activate.R", "renv::activate"] + +[commands] +run = "Rscript -e 'renv::restore()'" +alternatives = ["Rscript -e 'renv::snapshot()'"] + +[config] +files = ["renv.lock", ".Rprofile", "renv/settings.json"] +lockfile = "renv.lock" + +[taxonomy] +role = ["package-manager"] diff --git a/knowledge/r/rmarkdown.toml b/knowledge/r/rmarkdown.toml new file mode 100644 index 0000000..183b988 --- /dev/null +++ b/knowledge/r/rmarkdown.toml @@ -0,0 +1,16 @@ +[tool] +name = "R Markdown" +category = "docs" +homepage = "https://rmarkdown.rstudio.com" +docs = "https://rmarkdown.rstudio.com" +repo = "https://github.com/rstudio/rmarkdown" +description = "Dynamic document format for R" + +[detect] +files = ["*.Rmd", "**/*.Rmd"] +dependencies = ["rmarkdown"] +dev_dependencies = ["rmarkdown"] +ecosystems = ["r"] + +[taxonomy] +function = ["documentation"] diff --git a/knowledge/r/roxygen2.toml b/knowledge/r/roxygen2.toml new file mode 100644 index 0000000..21402ed --- /dev/null +++ b/knowledge/r/roxygen2.toml @@ -0,0 +1,24 @@ +[tool] +name = "roxygen2" +category = "docs" +homepage = "https://roxygen2.r-lib.org" +docs = "https://roxygen2.r-lib.org" +repo = "https://github.com/r-lib/roxygen2" +description = "In-source documentation generator for R packages" + +[detect] +files = ["man-roxygen/"] +dependencies = ["roxygen2"] +dev_dependencies = ["roxygen2"] +ecosystems = ["r"] +[detect.file_contains] +"DESCRIPTION" = ["RoxygenNote:", "Config/roxygen2/"] + +[commands] +run = "Rscript -e 'roxygen2::roxygenise()'" + +[config] +files = ["DESCRIPTION"] + +[taxonomy] +function = ["documentation"] diff --git a/knowledge/r/testthat.toml b/knowledge/r/testthat.toml new file mode 100644 index 0000000..80a36a8 --- /dev/null +++ b/knowledge/r/testthat.toml @@ -0,0 +1,26 @@ +[tool] +name = "testthat" +category = "test" +homepage = "https://testthat.r-lib.org" +docs = "https://testthat.r-lib.org" +repo = "https://github.com/r-lib/testthat" +description = "Testing library for R" + +[detect] +files = ["tests/testthat.R", "tests/testthat/"] +dependencies = ["testthat"] +dev_dependencies = ["testthat"] +ecosystems = ["r"] +[detect.file_contains] +"DESCRIPTION" = ["Config/testthat/"] + +[commands] +run = "Rscript -e 'testthat::test_local()'" +alternatives = ["R CMD check ."] + +[config] +files = ["DESCRIPTION", "tests/testthat.R"] + +[taxonomy] +role = ["testing-framework"] +function = ["testing"]