From 6f7686ba34b15da083f29894e2b63b36dd311c86 Mon Sep 17 00:00:00 2001 From: Lars Strojny Date: Thu, 17 Nov 2022 13:19:22 +0100 Subject: [PATCH] Auto-generate configuration documentation in README (#26) Auto-generate the documentation of the configuration options in `README.md` from `config.schema.json`. --- README.md | 88 +++++++++++++++++++--------- code-generation/config-scheme-gen.js | 72 ++++++++++++++++++++++- 2 files changed, 130 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 08ee9a6..6d2ec44 100644 --- a/README.md +++ b/README.md @@ -116,51 +116,81 @@ Once *Prometheus* is restarted, metrics with the `homebridge_` prefix should sta *homebridge-prometheus-exporter* offers a few advanced settings to customize its behavior. -```json lines + +```json5 { - // … + // ... "platforms": [ { "platform": "PrometheusExporter", - // Homebridge PIN for service authentication. String of digits, format XXX-XX-XXX. Required - "pin": string, - // Toggle debug mode. Run homebridge with -D if you want to see the debug output. Default: false - "debug": boolean, - // Prefix for all metrics. Default: "homebridge" - "prefix": string, + // Pin + // + // Homebridge PIN for service authentication + "pin": "", - // TCP port where the Prometheus metrics server listens. Default: 36123 - "port": number, - // Interface where the Prometheus metrics server listens. Default: "::" which means "any interface". - // Can be an IP, a hostname, "0.0.0.0" for all IPv4 interfaces, "::1" for all IPv6 interfaces. - // Default is "::" which means any interface - "interface": string, + // Debug + "debug": "", - // How frequently the services should be rediscovered (in seconds). Default: 60 - "refresh_interval": number, - // Timeout for the HTTP request that retrieves the homekit devices (in seconds). Default: 10 - "request_timeout": number, + // Metrics prefix + "prefix": "", - // Timeout for the service discovery (in seconds). Default: 20 - "discovery_timeout": number, + // Metrics server port + // + // TCP port where the Prometheus metrics server listens + "port": "", + + + // Metrics server interface + // + // Interface where the Prometheus metrics server listens. Can be an IP, a + // hostname, "0.0.0.0" for all IPv4 interfaces, "::1" for all IPv6 interfaces. + // Default is "::" which means "any interface" + "interface": "", + + + // Service refresh interval + // + // Discover new services every seconds + "refresh_interval": "", + + + // Request timeout + // + // Request timeout when interacting with homebridge instances + "request_timeout": "", + + + // Service discovery timeout + // + // Discovery timeout after which the current discovery is considered failed + "discovery_timeout": "", + + + // TLS cert file + // // Path to TLS certificate file (in PEM format) - "tls_cert_file": string, + "tls_cert_file": "", + + // TLS key file + // // Path to TLS key file - "tls_key_file": string, + "tls_key_file": "", - // Usernames and passwords for basic auth. Key is the username, value is the password. - // Password must be encoded with bcrypt - "basic_auth": { - "username": "" - } - }, - // … + + // Basic auth username/password pairs + // + // Usernames and passwords for basic auth. Object key is the username, object + // value is the password. Password must be encoded with bcrypt. Example: + // {"joanna": "$2a$12$5/mmmRB28wg9yzaXhee5Iupq3UrFr/qMgAe9LvAxGoY5jLcfVGTUq"} + "basic_auth": "" + } ] } ``` + \ No newline at end of file diff --git a/code-generation/config-scheme-gen.js b/code-generation/config-scheme-gen.js index 8989485..1d31d64 100755 --- a/code-generation/config-scheme-gen.js +++ b/code-generation/config-scheme-gen.js @@ -5,7 +5,7 @@ const { schema } = require('../config.schema.json') const { format } = require('prettier') const { join, basename } = require('path') const prettierConfig = require('../prettier.config') -const { writeFileSync } = require('fs') +const { writeFileSync, readFileSync } = require('fs') const file = join(__dirname, '../src/generated/config_boundary.ts') @@ -26,4 +26,74 @@ export const ConfigBoundary = ${zodSchema} writeFileSync(file, code) +const note = 'AUTOGENERATED CONFIG DOCS' +const comment = (...strings) => `` +const readmePath = join(__dirname, '../README.md') +const readme = readFileSync(readmePath).toString() +const regex = new RegExp(`${comment(note, 'BEGIN')}.*${comment(note, 'END')}`, 'mgs') +if (!readme.match(regex)) { + console.log('Could not update README.md') + process.exit(1) +} + +writeFileSync( + readmePath, + readme.replace( + regex, + `${comment(note, 'BEGIN')}\n\`\`\`json5\n${generateDocs(schema)}\n\`\`\`\n${comment(note, 'END')}`, + ), +) + +function generateDocs(schema) { + const doc = indent( + Object.entries(schema.properties) + .map(([property, definition]) => { + const lines = [] + + if (definition.title) { + lines.push(`// ${definition.title}`) + } + if (definition.description) { + lines.push(`//\n// ${wordwrap(definition.description, 80, '\n// ')}`) + } + + lines.push(`${JSON.stringify(property)}: ${JSON.stringify('<' + definition.type + '>')}`) + + return lines.join('\n') + }) + .join(',\n\n\n'), + 6, + ) + + return `{ + // ... + "platforms": [ + { + "platform": "PrometheusExporter", + + +${doc} + } + ] +}` +} + +function wordwrap(word, length, wrap = '\n') { + const wrapped = [] + while (word.length > length) { + const cut = word.substring(0, length) + const pos = cut.lastIndexOf(' ') + wrapped.push(cut.substring(0, pos)) + word = word.substring(pos + 1) + } + + wrapped.push(word) + + return wrapped.join(wrap) +} + +function indent(string, indent) { + return string.replace(/^(.+)$/gm, `${' '.repeat(indent)}$1`) +} + console.log(`Finished code generation for ${file}`)