El uso principal de configlet es el linting: comprobar si los ficheros de configuración de un track están bien estructurados, tanto sintáctica como semánticamente. Los tracks mal configurados puede que no se sincronicen correctamente, que se vean mal en el sitio web o que ofrezcan una experiencia de usuario mejorable, así que las comprobaciones de configlet desempeñan un papel importante a la hora de mantener la integridad de Exercism.
El comando lint sirve para hacer linting de un track.
configlet [global-options] lint [command-options]
Global options:
-h, --help Show this help message and exit
--version Show this tool's version information and exit
-t, --track-dir <dir> Specify a track directory to use instead of the current directory
-v, --verbosity <verbosity> The verbosity of output. Allowed values: q[uiet], n[ormal], d[etailed]
Estas son las reglas de linting que se comprueban.
El linter debe comprobar que todos los ficheros obligatorios estén presentes. Los ficheros que no son específicos de un ejercicio y que deben estar presentes son:
config.jsondocs/ABOUT.mddocs/INSTALLATION.mddocs/LEARNING.mddocs/RESOURCES.mddocs/SNIPPET.txtdocs/TESTS.mdexercises/shared/.docs/help.mdexercises/shared/.docs/tests.mdLos ficheros específicos de los ejercicios de concepto que deben estar presentes son:
exercises/concept/<slug>/.docs/hints.mdexercises/concept/<slug>/.docs/instructions.mdexercises/concept/<slug>/.docs/introduction.mdexercises/concept/<slug>/.meta/config.jsonHabrá una lista similar para los ejercicios de práctica, pero todavía no hemos definido la especificación para ello.
Cada concepto listado en config.json debe tener los siguientes ficheros:
concepts/<slug>/about.mdconcepts/<slug>/introduction.mdconcepts/<slug>/links.jsonEl fichero config.json debe cumplir las siguientes comprobaciones:
"language" es obligatoria"language" debe ser un string¹ no en blanco con longitud <= 255"slug" es obligatoria"slug" debe ser un string² en kebab-case con longitud <= 255"active" es obligatoria"active" debe ser un Boolean"blurb" es obligatoria"blurb" debe ser un string¹ no en blanco con longitud <= 400"version" es obligatoria"version" debe ser el entero 3
"status.concept_exercises" es obligatoria"status.concept_exercises" debe ser un Boolean"status.test_runner" es obligatoria"status.test_runner" debe ser un Boolean"status.representer" es obligatoria"status.representer" debe ser un Boolean"status.analyzer" es obligatoria"status.analyzer" debe ser un Boolean"online_editor.indent_style" es obligatoria"online_editor.indent_style" debe ser el string space o tab
"online_editor.indent_size" es obligatoria"online_editor.indent_size" debe ser un entero >= 0 y <= 8"online_editor.highlightjs_language" es opcional"online_editor.highlightjs_language" debe ser un string¹ no en blanco"files" es opcional"files" debe ser un objeto"files.solution" es opcional"files.solution" debe ser un array"files.solution" deben ser patrones⁵ válidos"files.solution" no deben tener duplicados"files.test" es opcional"files.test" debe ser un array"files.test" deben ser patrones⁵ válidos"files.test" no deben tener duplicados"files.example" es opcional"files.example" debe ser un array"files.example" deben ser patrones⁵ válidos"files.example" no deben tener duplicados"files.exemplar" es opcional"files.exemplar" debe ser un array"files.exemplar" deben ser patrones⁵ válidos"files.exemplar" no deben tener duplicados"files.editor" es opcional"files.editor" debe ser un array"files.editor" deben ser patrones⁵ válidos"files.editor" no deben tener duplicados"files.invalidator" es opcional"files.invalidator" debe ser un array"files.invalidator" deben ser patrones⁵ válidos"files.invalidator" no deben tener duplicados"files.solution", "files.test", "files.example", "files.exemplar", "files.editor" o "files.invalidator" (sin solapamiento)
d o plsql, los ficheros de "files.solution" y "files.test" sí pueden solaparse"files.example y "files.exemplar" sí pueden solaparse"test_runner.average_run_time" es obligatoria si "status.test_runner" es igual a true
"test_runner.average_run_time" debe ser un entero > 0"approaches.snippet_extension" es obligatoria si el track tiene uno o más enfoques"approaches.snippet_extension" debe ser un string¹ no en blanco"exercises" es obligatoria"exercises.concept" es obligatoria"exercises.concept" debe ser un array"exercises.concept[].slug" es obligatoria"exercises.concept[].slug" debe ser un string² en kebab-case con longitud <= 255"exercises.concept[].slug" debe ser único en "exercises.concept[].slug" y no puede existir en "exercises.practice[].slug"
"exercises.concept[].name" es obligatoria"exercises.concept[].name" debe ser un string³ en Title Case con longitud <= 255"exercises.concept[].uuid" es obligatoria"exercises.concept[].uuid" debe ser un string⁶ de UUID versión 4 único"exercises.concept[].uuid" de cada ejercicio no debe cambiar nunca"exercises.concept[].concepts" es obligatoria"exercises.concept[].concepts" debe ser un array no vacío de strings si "exercises.concept[].status" no es igual a deprecated
"exercises.concept[].concepts" debe ser un array vacío si "exercises.concept[].status" es igual a deprecated
"exercises.concept[].concepts" deben ser strings² en kebab-case"exercises.concept[].concepts" no deben tener duplicados"exercises.concept[].concepts" no deben estar en la propiedad "concepts" de ningún otro ejercicio de concepto"exercises.concept[].concepts" deben coincidir con la propiedad "concepts.slug" de uno de los conceptos"exercises.concept[].prerequisites" es obligatoria"exercises.concept[].prerequisites" debe ser un array no vacío de strings si "exercises.concept[].status" no es igual a deprecated, excepto exactamente un ejercicio al que sí se le permite tener un array vacío como valor"exercises.concept[].prerequisites" debe ser un array vacío si "exercises.concept[].status" es igual a deprecated
"exercises.concept[].prerequisites" deben ser strings² en kebab-case"exercises.concept[].prerequisites" no deben tener duplicados"exercises.concept[].prerequisites" deben coincidir con los valores de la propiedad "concepts" de algún otro ejercicio de concepto"exercises.concept[].prerequisites" no deben coincidir con ninguno de los valores de la propiedad "exercises.concept[].concepts" del ejercicio"exercises.concept[].prerequisites" deben coincidir con la propiedad "concepts.slug" de uno de los conceptos"exercises.concept[].concepts" y "exercises.concept[].prerequisites"
"exercises.concept[].status" es opcional"exercises.concept[].status" debe ser el string wip, beta, active o deprecated
"exercises.practice" es obligatoria"exercises.practice" debe ser un array"exercises.practice[].slug" es obligatoria"exercises.practice[].slug" debe ser un string² en kebab-case con longitud <= 255"exercises.practice[].slug" debe ser único en "exercises.practice[].slug" y no puede existir en "exercises.concept[].slug"
"exercises.practice[].slug" que sea el string hello-world
"exercises.practice[].name" es obligatoria"exercises.practice[].name" debe ser un string³ en Title Case con longitud <= 255"exercises.practice[].uuid" es obligatoria"exercises.practice[].uuid" debe ser un string⁶ de UUID versión 4 único"exercises.practice[].uuid" de cada ejercicio no debe cambiar nunca"exercises.practice[].difficulty" es obligatoria"exercises.practice[].difficulty" debe ser un entero >= 1 y <= 10"exercises.practice[].practices" es obligatoria"exercises.practice[].practices" debe ser un array no vacío de strings si "exercises.practice[].status" no es igual a deprecated
"exercises.practice[].practices" debe ser un array vacío si "exercises.practice[].status" es igual a deprecated
"exercises.practice[].practices" deben ser strings² en kebab-case"exercises.practice[].practices" no deben tener duplicados"exercises.practice[].practices" deben coincidir con la propiedad "concepts[].slug" de uno de los conceptos"exercises.practice[].practices" deben referirse al slug de un concepto concreto como máximo 10 veces (en todos los ejercicios)"exercises.practice[].prerequisites" es obligatoria"exercises.practice[].prerequisites" debe ser un array no vacío de strings si "exercises.practice[].status" no es igual a deprecated
"exercises.practice[].prerequisites" debe ser un array vacío si "exercises.practice[].status" es igual a deprecated
"exercises.practice[].prerequisites" debe ser un array vacío si "exercises.practice[].slug" es igual a hello-world
"exercises.practice[].prerequisites" deben ser strings² en kebab-case"exercises.practice[].prerequisites" no deben tener duplicados"exercises.practice[].prerequisites" deben coincidir con los valores de "exercises.concept[].concepts" de algún ejercicio de concepto"exercises.practice[].prerequisites" deben coincidir con la propiedad "concepts[].slug" de uno de los conceptos"exercises.practice[].status" es opcional"exercises.practice[].status" debe ser el string wip, beta, active o deprecated
"exercises.practice[].status" debe, si "exercises.practice[].slug" es igual a hello-world, omitirse o ser el string active
"exercises.foregone" es opcional"exercises.foregone" debe ser un array"exercises.foregone" deben ser strings² en kebab-case"exercises.foregone" no deben tener duplicados"exercises.foregone" no deben coincidir con ninguno de los slugs de los ejercicios de concepto o de práctica"concepts" es obligatoria"concepts" debe ser un array"concepts[].uuid" es obligatoria"concepts[].uuid" debe ser un string⁶ de UUID versión 4 único"concepts[].uuid" de cada concepto no debe cambiar nunca"concepts[].slug" es obligatoria"concepts[].slug" debe ser un string² en kebab-case con longitud <= 255"concepts[].name" es obligatoria"concepts[].name" debe ser un string³ en Title Case con longitud <= 255"concepts[].tags" es opcional"concepts[].tags.all" es opcional, a menos que "concepts[].tags.any" esté vacío"concepts[].tags.all" debe ser un array"concepts[].tags.all" deben ser strings⁷ de etiqueta no en blanco con longitud <= 255"concepts[].tags.all" no deben tener duplicados"concepts[].tags.any" es opcional, a menos que "concepts[].tags.all" esté vacío"concepts[].tags.any" debe ser un array"concepts[].tags.any" deben ser strings⁷ de etiqueta no en blanco con longitud <= 255"concepts[].tags.any" no deben tener duplicados"concepts[].tags.not" es opcional"concepts[].tags.not" debe ser un array"concepts[].tags.not" deben ser strings⁷ de etiqueta no en blanco con longitud <= 255"concepts[].tags.not" no deben tener duplicados"concepts" debe tener un fichero concept/<concepts.slug>/about.md. Las reglas de linting para este fichero se especifican a continuación."concepts" debe tener un fichero concept/<concepts.slug>/introduction.md. Las reglas de linting para este fichero se especifican a continuación."concepts" debe tener un fichero concept/<concepts.slug>/links.json. Las reglas de linting para este fichero se especifican a continuación."key_features" es opcional"key_features" debe ser un array con longitud = 6"key_features[].icon" es obligatoria"key_features[].icon" debe usar uno de los valores de icono predefinidos
"key_features[].title" es obligatoria"key_features[].title" debe ser un string⁴ en Sentence Case con longitud <= 25"key_features[].content" es obligatoria"key_features[].content" debe ser un string¹ no en blanco con longitud <= 100"tags" es obligatoria"tags" debe ser un array de strings"tags" no deben tener duplicados"tags" deben usar uno de los valores de etiqueta predefinidos
"blurb" es obligatoria"blurb" debe ser un string¹ no en blanco con longitud <= 350"source" es opcional"source" debe ser un string¹ no en blanco"source_url" es opcional"source_url" debe ser una URL"authors" es obligatoria"authors" debe ser un array no vacío"authors" deben ser strings¹ no en blanco"authors" no deben tener duplicados"authors" se tratan sin distinguir mayúsculas de minúsculas"contributors" es opcional"contributors" debe ser un array"contributors" deben ser strings¹ no en blanco"contributors" no deben tener duplicados"contributors" se tratan sin distinguir mayúsculas de minúsculas"authors" o "contributors" (sin solapamiento)"files.solution" es obligatoria"files.solution" debe ser un array no vacío"files.solution" no deben tener duplicados"files.test" es obligatoria"files.test" debe ser un array no vacío"files.test" no deben tener duplicados"files.exemplar" es obligatoria"files.exemplar" debe ser un array no vacío"files.exemplar" no deben tener duplicados"files.editor" es opcional"files.editor" debe ser un array"files.editor" no deben tener duplicados"files.invalidator" es opcional"files.invalidator" debe ser un array"files.invalidator" no deben tener duplicados"files.solution" deben existir"files.test" deben existir"files.exemplar" deben existir"files.editor" deben existir"files.invalidator" deben existir"files.solution", "files.test", "files.exemplar" o "files.invalidator" (sin solapamiento)
d o plsql, los ficheros de "files.solution" y "files.test" sí pueden solaparse"forked_from" es opcional"forked_from" debe ser un array"forked_from" deben ser strings con el formato <track-slug>/<exercise-slug> (p. ej. fsharp/bird-watcher)"forked_from" deben referirse a ejercicios realmente implementados"forked_from" no deben tener duplicados"language_versions" es opcional"language_versions" debe ser un string"representer.version" es opcional"representer.version" debe ser un entero >= 1"icon" es opcional"icon" debe ser un string² en kebab-case## General o ## X. <task>, donde X coincida con el encabezado del número de tarea en instructions.md
## 1. Do X
"concepts.slug" de uno de los conceptos del config.json del track."blurb" es obligatoria"blurb" debe ser un string¹ no en blanco con longitud <= 350"source" es opcional"source" debe ser un string¹ no en blanco"source_url" es opcional"source_url" debe ser una URL"authors" es opcional"authors" debe ser un array"authors" deben ser strings¹ no en blanco"authors" no deben tener duplicados"authors" se tratan sin distinguir mayúsculas de minúsculas"contributors" es opcional"contributors" debe ser un array"contributors" deben ser strings¹ no en blanco"contributors" no deben tener duplicados"contributors" se tratan sin distinguir mayúsculas de minúsculas"authors" o "contributors" (sin solapamiento)"files.solution" es obligatoria"files.solution" debe ser un array no vacío"files.solution" no deben tener duplicados"files.test" es obligatoria"files.test" debe ser un array no vacío"files.test" no deben tener duplicados"files.example" es obligatoria"files.example" debe ser un array no vacío"files.example" no deben tener duplicados"files.editor" es opcional"files.editor" debe ser un array"files.editor" no deben tener duplicados"files.invalidator" es opcional"files.invalidator" debe ser un array"files.invalidator" no deben tener duplicados"files.solution" deben existir"files.test" deben existir"files.example" deben existir"files.editor" deben existir"files.invalidator" deben existir"files.solution", "files.test", "files.example o "files.invalidator" (sin solapamiento)
d o plsql, los ficheros de "files.solution" y "files.test" sí pueden solaparse"language_versions" es opcional"language_versions" debe ser un string"test_runner" es opcional"test_runner" debe ser un Boolean"representer.version" es opcional"representer.version" debe ser un entero >= 1"icon" es opcional"icon" debe ser un string² en kebab-caseintroduction.md o un directorio hermano"introduction.authors" es opcional"introduction.authors" debe ser un array"introduction.authors" deben ser strings¹ no en blanco"introduction.authors" no deben tener duplicados"introduction.authors" se tratan sin distinguir mayúsculas de minúsculas"introduction.authors" no está vacío, debe haber un fichero introduction.md no vacío"introduction.contributors" es opcional"introduction.contributors" debe ser un array"introduction.contributors" deben ser strings¹ no en blanco"introduction.contributors" no deben tener duplicados"introduction.contributors" se tratan sin distinguir mayúsculas de minúsculas"introduction.contributors" no está vacío, debe haber un fichero introduction.md no vacío"introduction.authors" o "introduction.contributors" (sin solapamiento)"approaches" es opcional, a menos que haya un directorio hermano presente (que contiene los ficheros del enfoque)"approaches" debe ser un array de objetos"approaches[].uuid" es obligatoria"approaches[].uuid" debe ser un string⁶ de UUID versión 4 único"approaches[].uuid" de cada concepto no debe cambiar nunca"approaches[].slug" es obligatoria"approaches[].slug" debe ser un string² en kebab-case con longitud <= 255"approaches[].slug" debe tener un fichero <slug>/content.md no vacío correspondiente"approaches[].slug" debe tener un fichero <slug>/snippet.txt no vacío correspondiente"approaches[].title" es obligatoria"approaches[].title" debe ser un string³ en Title Case con longitud <= 255"approaches[].blurb" es obligatoria"approaches[].blurb" debe ser un string¹ no en blanco con longitud <= 350"approaches[].authors" es obligatoria"approaches[].authors" debe ser un array no vacío"approaches[].authors" deben ser strings¹ no en blanco"approaches[].authors" no deben tener duplicados"approaches[].authors" se tratan sin distinguir mayúsculas de minúsculas"approaches[].contributors" es opcional"approaches[].contributors" debe ser un array"approaches[].contributors" deben ser strings¹ no en blanco"approaches[].contributors" no deben tener duplicados"approaches[].contributors" se tratan sin distinguir mayúsculas de minúsculas"approaches[].authors" o "approaches[].contributors" (sin solapamiento)"approaches[].tags" es opcional"approaches[].tags.all" es opcional, a menos que "approaches[].tags.any" esté vacío"approaches[].tags.all" debe ser un array"approaches[].tags.all" deben ser strings⁷ de etiqueta no en blanco con longitud <= 255"approaches[].tags.all" no deben tener duplicados"approaches[].tags.any" es opcional, a menos que "approaches[].tags.all" esté vacío"approaches[].tags.any" debe ser un array"approaches[].tags.any" deben ser strings⁷ de etiqueta no en blanco con longitud <= 255"approaches[].tags.any" no deben tener duplicados"approaches[].tags.not" es opcional"approaches[].tags.not" debe ser un array"approaches[].tags.not" deben ser strings⁷ de etiqueta no en blanco con longitud <= 255"approaches[].tags.not" no deben tener duplicados"approaches[].slug" correspondiente en el fichero .approaches/config.json
"approaches[].slug" correspondiente en el fichero .approaches/config.json
approaches.snippet_extension del fichero config.json del track"articles" es opcional, a menos que haya un directorio hermano presente (que contiene los ficheros del artículo)"articles" debe ser un array de objetos"articles[].uuid" es obligatoria"articles[].uuid" debe ser un string⁶ de UUID versión 4 único"articles[].uuid" de cada concepto no debe cambiar nunca"articles[].slug" es obligatoria"articles[].slug" debe ser un string² en kebab-case con longitud <= 255"articles[].slug" debe tener un fichero <slug>/content.md no vacío correspondiente"articles[].slug" debe tener un fichero <slug>/snippet.md no vacío correspondiente"articles[].title" es obligatoria"articles[].title" debe ser un string³ en Title Case con longitud <= 255"articles[].blurb" es obligatoria"articles[].blurb" debe ser un string¹ no en blanco con longitud <= 350"articles[].authors" es obligatoria"articles[].authors" debe ser un array no vacío"articles[].authors" deben ser strings¹ no en blanco"articles[].authors" no deben tener duplicados"articles[].authors" se tratan sin distinguir mayúsculas de minúsculas"articles[].contributors" es opcional"articles[].contributors" debe ser un array"articles[].contributors" deben ser strings¹ no en blanco"articles[].contributors" no deben tener duplicados"articles[].contributors" se tratan sin distinguir mayúsculas de minúsculas"articles[].authors" o "articles[].contributors" (sin solapamiento)"articles[].slug" correspondiente en el fichero .articles/config.json
"articles[].slug" correspondiente en el fichero .articles/config.json
"[].url" es obligatoria"[].url" debe ser una URL"[].description" es obligatoria"[].description" debe ser un string¹ no en blanco"[].icon_url" es opcional"[].icon_url" debe ser una URL"blurb" es obligatoria"blurb" debe ser un string¹ no en blanco con longitud <= 350"authors" es obligatoria"authors" debe ser un array"authors" deben ser strings¹ no en blanco"authors" no deben tener duplicados"authors" se tratan sin distinguir mayúsculas de minúsculas"contributors" es opcional"contributors" debe ser un array"contributors" deben ser strings¹ no en blanco"contributors" no deben tener duplicados"contributors" se tratan sin distinguir mayúsculas de minúsculas"authors" o "contributors" (sin solapamiento)String no en blanco: un string que contiene al menos un carácter que no sea un espacio en blanco.
String en kebab-case: un string que solo contiene caracteres del rango [a-z0-9], opcionalmente separados por guiones (p. ej. «two-fer»). Debe coincidir con la expresión regular: ^[a-z0-9]+(-[a-z0-9]+)*$
String en Title Case: un string no en blanco que sigue las siguientes pautas del Chicago Manual of Style:
String en Sentence Case: un string no en blanco que sigue las siguientes pautas:
Patrón de files válido: Un string¹ no en blanco que especifica la ubicación de un fichero usado en un ejercicio, relativa al directorio del ejercicio. Un patrón puede usar uno de los siguientes marcadores de posición:
%{kebab_slug}: el slug del ejercicio en kebab-case (p. ej. bit-manipulation)%{snake_slug}: el slug del ejercicio en snake_case (p. ej. bit_manipulation)%{camel_slug}: el slug del ejercicio en camelCase (p. ej. bitManipulation)%{pascal_slug}: el slug del ejercicio en PascalCase (p. ej. BitManipulation)String de UUID versión 4 único: Un string que cumple todas estas condiciones:
config.json a nivel de track de un track de Exercism dadoconfig.json a nivel de track de ningún otro track de Exercismcanonical-data.json de https://github.com/exercism/problem-specifications^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
Por ejemplo, el siguiente UUID tiene la forma correcta:
d334ffe3-657e-4725-a950-290b284b6d9f
Puedes ejecutar configlet uuid para generar un UUID adecuado.
tag de analyzer válido: Un string¹ no en blanco con el formato <category>:<thing>.
El valor de <category> debe ser uno de:
paradigm (p. ej. paradigm:functional)technique (p. ej. technique:recursion)construct (p. ej. construct:bitwise-and)uses (p. ej. uses:DateTime.add_seconds)El valor de <thing> debe ser un string¹ no en blanco.