El uso principal de configlet es el análisis estático: comprobar que los archivos de configuración de un track estén correctamente estructurados, tanto sintáctica como semánticamente. Los tracks mal configurados pueden no sincronizarse correctamente, verse mal en el sitio web o dar una experiencia de usuario poco óptima, así que las comprobaciones de configlet cumplen un papel importante a la hora de mantener la integridad de Exercism.
El comando lint se puede usar para analizar 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 análisis estático que se comprueban.
El linter debería comprobar que estén presentes todos los archivos requeridos. Los archivos 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 archivos 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 eso.
Cada concepto listado en config.json debería tener los siguientes archivos:
concepts/<slug>/about.mdconcepts/<slug>/introduction.mdconcepts/<slug>/links.jsonEl archivo config.json debe superar las siguientes comprobaciones:
"language" es obligatoria"language" debe ser un string¹ no vacío con una longitud <= 255"slug" es obligatoria"slug" debe ser un string² en kebab-case con una longitud <= 255"active" es obligatoria"active" debe ser un Boolean"blurb" es obligatoria"blurb" debe ser un string¹ no vacío con una 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 vacío"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 archivos de "files.solution" y "files.test" pueden solaparse"files.example y "files.exemplar" 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 vacío"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 una 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 una 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 nunca debe cambiar"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 en exactamente un ejercicio, que sí puede 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 una 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 una 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 nunca debe cambiar"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 individual 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", si "exercises.practice[].slug" es igual a hello-world, debe 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 nunca debe cambiar"concepts[].slug" es obligatoria"concepts[].slug" debe ser un string² en kebab-case con una longitud <= 255"concepts[].name" es obligatoria"concepts[].name" debe ser un string³ en Title Case con una longitud <= 255"concepts[].tags" es opcional"concepts[].tags.all" es opcional, salvo que "concepts[].tags.any" esté vacío"concepts[].tags.all" debe ser un array"concepts[].tags.all" deben ser tags⁷ no vacíos con una longitud <= 255"concepts[].tags.all" no deben tener duplicados"concepts[].tags.any" es opcional, salvo que "concepts[].tags.all" esté vacío"concepts[].tags.any" debe ser un array"concepts[].tags.any" deben ser tags⁷ no vacíos con una 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 tags⁷ no vacíos con una longitud <= 255"concepts[].tags.not" no deben tener duplicados"concepts" debe tener un archivo concept/<concepts.slug>/about.md. Las reglas de análisis estático para este archivo se especifican a continuación."concepts" debe tener un archivo concept/<concepts.slug>/introduction.md. Las reglas de análisis estático para este archivo se especifican a continuación."concepts" debe tener un archivo concept/<concepts.slug>/links.json. Las reglas de análisis estático para este archivo se especifican a continuación."key_features" es opcional"key_features" debe ser un array con una 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 una longitud <= 25"key_features[].content" es obligatoria"key_features[].content" debe ser un string¹ no vacío con una 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 tag predefinidos
"blurb" es obligatoria"blurb" debe ser un string¹ no vacío con una longitud <= 350"source" es opcional"source" debe ser un string¹ no vacío"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 vacíos"authors" no deben tener duplicados"authors" se tratan sin distinguir entre mayúsculas y minúsculas"contributors" es opcional"contributors" debe ser un array"contributors" deben ser strings¹ no vacíos"contributors" no deben tener duplicados"contributors" se tratan sin distinguir entre mayúsculas y 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 archivos de "files.solution" y "files.test" 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 coincide con el número de tarea del encabezado 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 vacío con una longitud <= 350"source" es opcional"source" debe ser un string¹ no vacío"source_url" es opcional"source_url" debe ser una URL"authors" es opcional"authors" debe ser un array"authors" deben ser strings¹ no vacíos"authors" no deben tener duplicados"authors" se tratan sin distinguir entre mayúsculas y minúsculas"contributors" es opcional"contributors" debe ser un array"contributors" deben ser strings¹ no vacíos"contributors" no deben tener duplicados"contributors" se tratan sin distinguir entre mayúsculas y 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 archivos de "files.solution" y "files.test" 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 vacíos"introduction.authors" no deben tener duplicados"introduction.authors" se tratan sin distinguir entre mayúsculas y minúsculas"introduction.authors" no está vacío, debe haber un archivo introduction.md no vacío"introduction.contributors" es opcional"introduction.contributors" debe ser un array"introduction.contributors" deben ser strings¹ no vacíos"introduction.contributors" no deben tener duplicados"introduction.contributors" se tratan sin distinguir entre mayúsculas y minúsculas"introduction.contributors" no está vacío, debe haber un archivo introduction.md no vacío"introduction.authors" o "introduction.contributors" (sin solapamiento)"approaches" es opcional, salvo que haya un directorio hermano presente (que contiene los archivos 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 nunca debe cambiar"approaches[].slug" es obligatoria"approaches[].slug" debe ser un string² en kebab-case con una longitud <= 255"approaches[].slug" debe tener un archivo <slug>/content.md no vacío correspondiente"approaches[].slug" debe tener un archivo <slug>/snippet.txt no vacío correspondiente"approaches[].title" es obligatoria"approaches[].title" debe ser un string³ en Title Case con una longitud <= 255"approaches[].blurb" es obligatoria"approaches[].blurb" debe ser un string¹ no vacío con una longitud <= 350"approaches[].authors" es obligatoria"approaches[].authors" debe ser un array no vacío"approaches[].authors" deben ser strings¹ no vacíos"approaches[].authors" no deben tener duplicados"approaches[].authors" se tratan sin distinguir entre mayúsculas y minúsculas"approaches[].contributors" es opcional"approaches[].contributors" debe ser un array"approaches[].contributors" deben ser strings¹ no vacíos"approaches[].contributors" no deben tener duplicados"approaches[].contributors" se tratan sin distinguir entre mayúsculas y minúsculas"approaches[].authors" o "approaches[].contributors" (sin solapamiento)"approaches[].tags" es opcional"approaches[].tags.all" es opcional, salvo que "approaches[].tags.any" esté vacío"approaches[].tags.all" debe ser un array"approaches[].tags.all" deben ser tags⁷ no vacíos con una longitud <= 255"approaches[].tags.all" no deben tener duplicados"approaches[].tags.any" es opcional, salvo que "approaches[].tags.all" esté vacío"approaches[].tags.any" debe ser un array"approaches[].tags.any" deben ser tags⁷ no vacíos con una 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 tags⁷ no vacíos con una longitud <= 255"approaches[].tags.not" no deben tener duplicados"approaches[].slug" que coincida en el archivo .approaches/config.json
"approaches[].slug" que coincida en el archivo .approaches/config.json
approaches.snippet_extension en el archivo config.json del track"articles" es opcional, salvo que haya un directorio hermano presente (que contiene los archivos 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 nunca debe cambiar"articles[].slug" es obligatoria"articles[].slug" debe ser un string² en kebab-case con una longitud <= 255"articles[].slug" debe tener un archivo <slug>/content.md no vacío correspondiente"articles[].slug" debe tener un archivo <slug>/snippet.md no vacío correspondiente"articles[].title" es obligatoria"articles[].title" debe ser un string³ en Title Case con una longitud <= 255"articles[].blurb" es obligatoria"articles[].blurb" debe ser un string¹ no vacío con una longitud <= 350"articles[].authors" es obligatoria"articles[].authors" debe ser un array no vacío"articles[].authors" deben ser strings¹ no vacíos"articles[].authors" no deben tener duplicados"articles[].authors" se tratan sin distinguir entre mayúsculas y minúsculas"articles[].contributors" es opcional"articles[].contributors" debe ser un array"articles[].contributors" deben ser strings¹ no vacíos"articles[].contributors" no deben tener duplicados"articles[].contributors" se tratan sin distinguir entre mayúsculas y minúsculas"articles[].authors" o "articles[].contributors" (sin solapamiento)"articles[].slug" que coincida en el archivo .articles/config.json
"articles[].slug" que coincida en el archivo .articles/config.json
"[].url" es obligatoria"[].url" debe ser una URL"[].description" es obligatoria"[].description" debe ser un string¹ no vacío"[].icon_url" es opcional"[].icon_url" debe ser una URL"blurb" es obligatoria"blurb" debe ser un string¹ no vacío con una longitud <= 350"authors" es obligatoria"authors" debe ser un array"authors" deben ser strings¹ no vacíos"authors" no deben tener duplicados"authors" se tratan sin distinguir entre mayúsculas y minúsculas"contributors" es opcional"contributors" debe ser un array"contributors" deben ser strings¹ no vacíos"contributors" no deben tener duplicados"contributors" se tratan sin distinguir entre mayúsculas y minúsculas"authors" o "contributors" (sin solapamiento)String no vacío: 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], separados opcionalmente 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 vacío que sigue las siguientes pautas del Manual de Estilo de Chicago:
String en Sentence Case: un string no vacío que sigue las siguientes pautas:
Patrón de files válido: un string¹ no vacío que especifica la ubicación de un archivo 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 de nivel de track de un track de Exercism dadoconfig.json de 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 vacío con el formato <category>:<thing>.
El valor de <category> debe ser uno de los siguientes:
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 vacío.