bashly 1.0.7 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/lib/bashly/cli.rb +1 -0
  4. data/lib/bashly/commands/completions.rb +1 -1
  5. data/lib/bashly/commands/render.rb +103 -0
  6. data/lib/bashly/concerns/validation_helpers.rb +1 -1
  7. data/lib/bashly/config.rb +0 -2
  8. data/lib/bashly/extensions/string.rb +17 -1
  9. data/lib/bashly/extensions/yaml.rb +4 -1
  10. data/lib/bashly/libraries/config/config.sh +74 -94
  11. data/lib/bashly/libraries/ini/ini.sh +110 -0
  12. data/lib/bashly/libraries/libraries.yml +40 -1
  13. data/lib/bashly/libraries/render/mandoc/README.md +45 -0
  14. data/lib/bashly/libraries/render/mandoc/mandoc.gtx +196 -0
  15. data/lib/bashly/libraries/render/mandoc/render.rb +34 -0
  16. data/lib/bashly/libraries/render/mandoc/summary.txt +1 -0
  17. data/lib/bashly/libraries/render/markdown/README.md +42 -0
  18. data/lib/bashly/libraries/render/markdown/markdown.gtx +186 -0
  19. data/lib/bashly/libraries/render/markdown/render.rb +23 -0
  20. data/lib/bashly/libraries/render/markdown/summary.txt +1 -0
  21. data/lib/bashly/refinements/compose_refinements.rb +0 -2
  22. data/lib/bashly/render_context.rb +30 -0
  23. data/lib/bashly/render_source.rb +71 -0
  24. data/lib/bashly/script/base.rb +2 -1
  25. data/lib/bashly/script/command.rb +17 -0
  26. data/lib/bashly/version.rb +1 -1
  27. data/lib/bashly/views/command/dependencies_filter.gtx +1 -1
  28. data/lib/bashly/views/command/footer.gtx +1 -1
  29. data/lib/bashly/views/command/parse_requirements_while.gtx +1 -1
  30. data/lib/bashly/views/command/usage.gtx +2 -2
  31. data/lib/bashly/views/command/usage_commands.gtx +2 -2
  32. data/lib/bashly.rb +56 -11
  33. metadata +41 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a333ce419f5ba64e38000497f245fc5baf822243cd2765c1805ebdf6de2e3b46
4
- data.tar.gz: 0c9ab24cc2cecd88cbd20a06ab11028740ce3000aec75080ca9a12c6769110c1
3
+ metadata.gz: d333139a3555fdc50cdb3ddf21119712fcada4ab837f5703f29529f346c6ad24
4
+ data.tar.gz: 0a40e42bfa542ed5d4f8d59792aa2a19518ae1c9098db0309f9349db391c74b2
5
5
  SHA512:
6
- metadata.gz: ff95fd919a29a16d610d8f3c069f2de17b9984a41251de56e484a5dc49e86dd4114934c47ccb2274521f7f8bd5078287a54ef10478c5e18b6c8a93f462f1c503
7
- data.tar.gz: 65a1be57601cc7a560a19ef48dc75b09647244fe477327f23ae52a37f8114afca4ee3871a1f72e1b046589e252a57701956a93e9015907bf76fd24455da341ff
6
+ metadata.gz: e601e098208e0a5de186279e67c001ff58c44536f09e5cea6815722e9bef3ec3dc1b257c1b9ca2267e94977f05514916a66f03721c88c5a795cab93e8eadb20d
7
+ data.tar.gz: 670bcae80903cdabc5d76b5139b1c6d80b1ee3ca33af03078a340a847c4ef1385ca74ca08b2b0a274845f0be5ecfc7ecd9822575b8f8ea8cb3592b821df22760
data/README.md CHANGED
@@ -71,7 +71,8 @@ Bashly is responsible for:
71
71
  - **Config file management** (INI format).
72
72
  - **YAML parsing**.
73
73
  - **Bash completions**.
74
- - and more.
74
+ - *and more*.
75
+ - Auto-generating **markdown and man page documentation** for your script.
75
76
 
76
77
  ## Contributing / Support
77
78
 
data/lib/bashly/cli.rb CHANGED
@@ -16,6 +16,7 @@ module Bashly
16
16
  runner.route 'add', to: Commands::Add
17
17
  runner.route 'doc', to: Commands::Doc
18
18
  runner.route 'completions', to: Commands::Completions
19
+ runner.route 'render', to: Commands::Render
19
20
  runner.route 'shell', to: Commands::Shell unless ENV['BASHLY_SHELL']
20
21
 
21
22
  runner
@@ -4,7 +4,7 @@ module Bashly
4
4
  summary 'Install bash completions for bashly itself'
5
5
  help 'Display the bash completions script or install it directly to your bash completions directory'
6
6
 
7
- usage 'bashly completions [--install --uninstall]'
7
+ usage 'bashly completions [--install|--uninstall]'
8
8
  usage 'bashly completions (-h|--help)'
9
9
 
10
10
  option '-i --install', 'Install the completions script to your bash completions directory'
@@ -0,0 +1,103 @@
1
+ require 'filewatcher'
2
+ require 'tty-markdown'
3
+
4
+ module Bashly
5
+ module Commands
6
+ class Render < Base
7
+ help 'Render the bashly data structure using cutsom templates'
8
+
9
+ usage 'bashly render SOURCE TARGET [--watch --show PATH]'
10
+ usage 'bashly render SOURCE --about'
11
+ usage 'bashly render --list'
12
+ usage 'bashly render (-h|--help)'
13
+
14
+ param 'SOURCE', <<~HELP
15
+ An ID to an internal templates source, or a path to a custom templates directory.
16
+
17
+ A leading colon (:) denotes an internal ID (see `--list`).
18
+ HELP
19
+
20
+ param 'TARGET', 'Output directory'
21
+
22
+ option '-w --watch', 'Watch bashly.yml and the templates source for changes and render on change'
23
+ option '-s --show PATH', <<~USAGE
24
+ After rendering, show the result generated in PATH.
25
+
26
+ The provided PATH is treated as relative TARGET.
27
+
28
+ Note that this works only if the template source supports it.
29
+ USAGE
30
+
31
+ option '-l --list', 'Show list of built-in templates'
32
+ option '-a --about', 'Show information about a given templates source'
33
+
34
+ example 'bashly render --list'
35
+ example 'bashly render :markdown --about'
36
+ example 'bashly render :markdown docs --watch'
37
+ example 'bashly render :markdown docs --show "cli-download.1"'
38
+ example 'bashly render /path/to/templates ./out_path'
39
+
40
+ attr_reader :watching, :target, :source
41
+
42
+ def run
43
+ if args['--list'] then show_list
44
+ elsif args['--about'] then show_about
45
+ else
46
+ start_render
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def show_list
53
+ RenderSource.internal.each_value do |source|
54
+ say "g`:#{source.selector.to_s.ljust 10}` #{source.summary}"
55
+ end
56
+ end
57
+
58
+ def show_about
59
+ puts TTY::Markdown.parse(render_source.readme)
60
+ end
61
+
62
+ def start_render
63
+ @target = args['TARGET']
64
+ @watching = args['--watch']
65
+
66
+ render
67
+ watch if watching
68
+ end
69
+
70
+ def render
71
+ render_source.render target, show: args['--show']
72
+ end
73
+
74
+ def watch
75
+ say "g`watching`\n"
76
+
77
+ Filewatcher.new(watchables).watch do
78
+ render
79
+ say "g`waiting`\n"
80
+ end
81
+ end
82
+
83
+ def render_source
84
+ @render_source ||= begin
85
+ source = RenderSource.new selector
86
+ raise "Invalid render source: #{args['SOURCE']}" unless source.exist?
87
+
88
+ source
89
+ end
90
+ end
91
+
92
+ def selector
93
+ return args['SOURCE'] unless args['SOURCE'].start_with? ':'
94
+
95
+ args['SOURCE'][1..].to_sym
96
+ end
97
+
98
+ def watchables
99
+ @watchables ||= [Settings.config_path, render_source.path]
100
+ end
101
+ end
102
+ end
103
+ end
@@ -40,7 +40,7 @@ module Bashly
40
40
 
41
41
  return unless keys
42
42
 
43
- invalid_keys = value.keys.map(&:to_sym) - keys
43
+ invalid_keys = (value.keys.map(&:to_sym) - keys).reject { |k| k.start_with? 'x_' }
44
44
  assert invalid_keys.empty?, "#{key} contains invalid options: #{invalid_keys.join ', '}"
45
45
  end
46
46
 
data/lib/bashly/config.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'yaml'
2
-
3
1
  module Bashly
4
2
  # A convenience class to use either a hash or a filename as a configuration
5
3
  # source.
@@ -1,6 +1,18 @@
1
1
  class String
2
2
  def sanitize_for_print
3
- gsub("\n", '\\n').gsub('"', '\"')
3
+ gsub("\n", '\\n').gsub('"', '\"').gsub('`', '\\\\`')
4
+ end
5
+
6
+ def for_markdown
7
+ gsub('<', '\\<').gsub('>', '\\>').nl2br
8
+ end
9
+
10
+ def for_manpage
11
+ gsub('<', '\\<').gsub('>', '\\>').gsub('`', '**').gsub(" \n", "\n\n")
12
+ end
13
+
14
+ def nl2br
15
+ gsub("\n", " \n")
4
16
  end
5
17
 
6
18
  def indent(offset)
@@ -13,6 +25,10 @@ class String
13
25
  gsub(/(.)([A-Z])/, '\1_\2').gsub(/[- ]/, '_').downcase
14
26
  end
15
27
 
28
+ def to_hyphen
29
+ tr(' ', '-').gsub(/([a-z])([A-Z])/, '\1-\2').downcase
30
+ end
31
+
16
32
  def to_path
17
33
  tr(' ', '/').downcase
18
34
  end
@@ -1,9 +1,12 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+
1
4
  module YAML
2
5
  # We trust our loaded YAMLs
3
6
  # This patch is due to https://bugs.ruby-lang.org/issues/17866
4
7
  # StackOverflow: https://stackoverflow.com/questions/71191685/visit-psych-nodes-alias-unknown-alias-default-psychbadalias/71192990#71192990
5
8
  class << self
6
- alias load unsafe_load
9
+ alias load unsafe_load if YAML.respond_to? :unsafe_load
7
10
 
8
11
  def load_erb_file(path)
9
12
  YAML.load ERB.new(File.read(path)).result
@@ -1,128 +1,108 @@
1
- ## Config functions [@bashly-upgrade config]
1
+ ## Config (INI) functions [@bashly-upgrade config]
2
2
  ## This file is a part of Bashly standard library
3
3
  ##
4
4
  ## Usage:
5
- ## - In your script, set the CONFIG_FILE variable. For rxample:
6
- ## CONFIG_FILE=settings.ini.
7
- ## If it is unset, it will default to 'config.ini'.
8
- ## - Use any of the functions below to access the config file.
9
5
  ##
10
- ## Create a new config file.
11
- ## There is normally no need to use this function, it is used by other
12
- ## functions as needed.
6
+ ## - Set the global variable CONFIG_FILE to the path of your INI file somewhere
7
+ ## in your script (for example, in src/initialize.sh).
8
+ ## - Use any of the following functions to access and manipulate the values.
9
+ ## - INI sections are optional (i.e., sectionless key=value pairs are allowed).
13
10
  ##
14
- config_init() {
15
- CONFIG_FILE=${CONFIG_FILE:=config.ini}
16
- [[ -f "$CONFIG_FILE" ]] || touch "$CONFIG_FILE"
11
+
12
+ ## Show all the key=value pairs from your config file
13
+ config_show() {
14
+ config_load
15
+ ini_show
17
16
  }
18
17
 
19
- ## Get a value from the config.
20
- ## Usage: result=$(config_get hello)
18
+ ## Get a single value from the config file:
19
+ ##
20
+ ## config_get login.email
21
+ ##
22
+ ## Get the value or a default one if it is not set:
23
+ ##
24
+ ## config_get login.user guest
25
+ ##
26
+ ## Assign the result to a variable:
27
+ ##
28
+ ## theme="$(config_get interface.theme)"
29
+ ##
21
30
  config_get() {
22
- local key=$1
23
- local regex="^$key *= *(.+)$"
24
- local value=""
25
-
26
- config_init
31
+ local key="$1"
32
+ local default_value="$2"
27
33
 
28
- while IFS= read -r line || [ -n "$line" ]; do
29
- if [[ $line =~ $regex ]]; then
30
- value="${BASH_REMATCH[1]}"
31
- break
32
- fi
33
- done <"$CONFIG_FILE"
34
-
35
- echo "$value"
34
+ config_load
35
+ echo "${ini["$key"]:-$default_value}"
36
36
  }
37
37
 
38
- ## Add or update a key=value pair in the config.
39
- ## Usage: config_set key value
38
+ ## Create/update a key=value pair:
39
+ ##
40
+ ## config_set cloud.provider aws
41
+ ##
40
42
  config_set() {
41
- local key=$1
43
+ local key="$1"
42
44
  shift
43
45
  local value="$*"
44
46
 
45
- config_init
46
-
47
- local regex="^($key) *= *.+$"
48
- local output=""
49
- local found_key=""
50
- local newline
51
-
52
- while IFS= read -r line || [ -n "$line" ]; do
53
- newline=$line
54
- if [[ $line =~ $regex ]]; then
55
- found_key="${BASH_REMATCH[1]}"
56
- newline="$key = $value"
57
- output="$output$newline\n"
58
- elif [[ $line ]]; then
59
- output="$output$line\n"
60
- fi
61
- done <"$CONFIG_FILE"
62
-
63
- if [[ -z $found_key ]]; then
64
- output="$output$key = $value\n"
65
- fi
66
-
67
- printf "%b\n" "$output" >"$CONFIG_FILE"
47
+ config_load
48
+ ini["$key"]="$value"
49
+ config_save
68
50
  }
69
51
 
70
- ## Delete a key from the config.
71
- ## Usage: config_del key
52
+ ## Delete a key=value pair:
53
+ ##
54
+ ## config_del login.email
55
+ ##
72
56
  config_del() {
73
- local key=$1
57
+ local key="$1"
74
58
 
75
- local regex="^($key) *="
76
- local output=""
77
-
78
- config_init
79
-
80
- while IFS= read -r line || [ -n "$line" ]; do
81
- if [[ $line ]] && [[ ! $line =~ $regex ]]; then
82
- output="$output$line\n"
83
- fi
84
- done <"$CONFIG_FILE"
85
-
86
- printf "%b\n" "$output" >"$CONFIG_FILE"
87
- }
88
-
89
- ## Show the config file
90
- config_show() {
91
- config_init
92
- cat "$CONFIG_FILE"
59
+ config_load
60
+ unset "ini[$key]"
61
+ config_save
93
62
  }
94
63
 
95
- ## Return an array of the keys in the config file.
96
- ## Usage:
64
+ ## Get an array of all keys:
97
65
  ##
98
- ## for k in $(config_keys); do
99
- ## echo "- $k = $(config_get "$k")";
66
+ ## for key in $(config_keys); do
67
+ ## echo "- $key = $(config_get "$key")";
100
68
  ## done
101
69
  ##
102
70
  config_keys() {
103
- local regex="^([a-zA-Z0-9_\-\/\.]+) *="
104
-
105
- config_init
106
-
107
- local keys=()
108
- local key
109
-
110
- while IFS= read -r line || [ -n "$line" ]; do
111
- if [[ $line =~ $regex ]]; then
112
- key="${BASH_REMATCH[1]}"
113
- keys+=("$key")
114
- fi
115
- done <"$CONFIG_FILE"
116
- echo "${keys[@]}"
71
+ config_load
72
+ ini_keys
117
73
  }
118
74
 
119
- ## Returns true if the specified key exists in the config file.
120
- ## Usage:
75
+ ## Check if a key exists:
121
76
  ##
122
- ## if config_has_key "key"; then
77
+ ## if config_has_key login.password; then
123
78
  ## echo "key exists"
124
79
  ## fi
125
80
  ##
126
81
  config_has_key() {
127
82
  [[ $(config_get "$1") ]]
128
83
  }
84
+
85
+ ## Force-load from file
86
+ ## This should normally not be called, unless you suspect that the INI file
87
+ ## was modified by external means during the run of your script.
88
+ config_reload() {
89
+ declare -g config_loaded=false
90
+ config_load
91
+ }
92
+
93
+ ## Load an INI file (unless loaded) and populate the associative array
94
+ ## NOTE: Normally there is no need to call this function, it is called as needed
95
+ config_load() {
96
+ [[ "$config_loaded" == "true" ]] && return
97
+
98
+ declare -g CONFIG_FILE=${CONFIG_FILE:=config.ini}
99
+ declare -g config_loaded=true
100
+ [[ -f "$CONFIG_FILE" ]] || touch "$CONFIG_FILE"
101
+ ini_load "$CONFIG_FILE"
102
+ }
103
+
104
+ ## Save the associative array back to a file
105
+ ## NOTE: Normally there is no need to call this function, it is called as needed
106
+ config_save() {
107
+ ini_save "$CONFIG_FILE"
108
+ }
@@ -0,0 +1,110 @@
1
+ ## INI functions [@bashly-upgrade ini]
2
+ ## This file is a part of Bashly standard library
3
+ ##
4
+ ## Usage:
5
+ ##
6
+ ## - In your script, call `ini_load path/to/config.ini`.
7
+ ## - A global associative array named `ini` will become available to you,
8
+ ## - When updating any of the associative array's values, call
9
+ ## `ini_save path/to/config.ini` to save the data.
10
+ ## - INI sections are optional.
11
+ ##
12
+ ## Get a value:
13
+ ##
14
+ ## ${ini[section1.key1]} # for keys under a [section]
15
+ ## ${ini[key1]} # for keys not under a [section]
16
+ ## ${ini[key1]:-default} # get a default value if the INI key is unset
17
+ ##
18
+ ## Update/create a value:
19
+ ##
20
+ ## ini[section1.key1]="value"
21
+ ## ini_save path/to/config.ini
22
+ ##
23
+ ## Delete a value
24
+ ##
25
+ ## unset ini[section1.key1]
26
+ ## ini_save path/to/config.ini
27
+ ##
28
+
29
+ ## Load an INI file and populate the associative array `ini`.
30
+ ini_load() {
31
+ declare -gA ini
32
+
33
+ local ini_file="$1"
34
+
35
+ local section=""
36
+ local key=""
37
+ local value=""
38
+ local section_regex="^\[(.+)\]"
39
+ local key_regex="^([^ =]+) *= *(.*) *$"
40
+ local comment_regex="^;"
41
+
42
+ while IFS= read -r line; do
43
+ if [[ $line =~ $comment_regex ]]; then
44
+ continue
45
+ elif [[ $line =~ $section_regex ]]; then
46
+ section="${BASH_REMATCH[1]}."
47
+ elif [[ $line =~ $key_regex ]]; then
48
+ key="${BASH_REMATCH[1]}"
49
+ value="${BASH_REMATCH[2]}"
50
+ ini["${section}${key}"]="$value"
51
+ fi
52
+ done <"$ini_file"
53
+ }
54
+
55
+ ## Save the associative array `ini` back to a file
56
+ ini_save() {
57
+ declare -gA ini
58
+
59
+ local ini_file="$1"
60
+
61
+ local current_section=""
62
+ local has_free_keys=false
63
+
64
+ rm -f "$ini_file"
65
+
66
+ for key in $(ini_keys); do
67
+ [[ $key == *.* ]] && continue
68
+ has_free_keys=true
69
+ value="${ini[$key]}"
70
+ echo "$key = $value" >>"$ini_file"
71
+ done
72
+
73
+ [[ "${has_free_keys}" == "true" ]] && echo >>"$ini_file"
74
+
75
+ for key in $(ini_keys); do
76
+ [[ $key == *.* ]] || continue
77
+ value="${ini[$key]}"
78
+ IFS="." read -r section_name key_name <<<"$key"
79
+
80
+ if [[ "$current_section" != "$section_name" ]]; then
81
+ [[ $current_section ]] && echo >>"$ini_file"
82
+ echo "[$section_name]" >>"$ini_file"
83
+ current_section="$section_name"
84
+ fi
85
+
86
+ echo "$key_name = $value" >>"$ini_file"
87
+ done
88
+ }
89
+
90
+ ## Show all loaded key-value pairs
91
+ ini_show() {
92
+ declare -gA ini
93
+
94
+ for key in $(ini_keys); do
95
+ echo "$key = ${ini[$key]}"
96
+ done
97
+ }
98
+
99
+ ## Get an array of all keys:
100
+ ##
101
+ ## for key in $(ini_keys); do
102
+ ## echo "- $key = ${ini[$key]}";
103
+ ## done
104
+ ##
105
+ ini_keys() {
106
+ declare -gA ini
107
+
108
+ local keys=("${!ini[@]}")
109
+ for a in "${keys[@]}"; do echo "$a"; done | sort
110
+ }
@@ -20,10 +20,12 @@ completions_yaml:
20
20
  handler: Bashly::Libraries::CompletionsYAML
21
21
 
22
22
  config:
23
- help: Add standard functions for handling INI files to the lib directory.
23
+ help: Add functions for handling INI configuration files to the lib directory.
24
24
  files:
25
25
  - source: "config/config.sh"
26
26
  target: "%{user_lib_dir}/config.%{user_ext}"
27
+ - source: "ini/ini.sh"
28
+ target: "%{user_lib_dir}/ini.%{user_ext}"
27
29
 
28
30
  help:
29
31
  help: Add a help command, in addition to the standard --help flag.
@@ -39,6 +41,12 @@ hooks:
39
41
  - source: "hooks/after.sh"
40
42
  target: "%{user_source_dir}/after.%{user_ext}"
41
43
 
44
+ ini:
45
+ help: Add low level functions for reading/writing INI files to the lib directory.
46
+ files:
47
+ - source: "ini/ini.sh"
48
+ target: "%{user_lib_dir}/ini.%{user_ext}"
49
+
42
50
  lib:
43
51
  help: |-
44
52
  Create the lib directory for any additional user scripts.
@@ -48,6 +56,37 @@ lib:
48
56
  - source: "lib/sample_function.sh"
49
57
  target: "%{user_lib_dir}/sample_function.%{user_ext}"
50
58
 
59
+ render_markdown:
60
+ help: Copy the markdown templates to your project, allowing you to customize the markdown documentation output.
61
+ skip_src_check: true
62
+ files:
63
+ - source: "render/markdown/README.md"
64
+ target: "templates/markdown/README.md"
65
+ - source: "render/markdown/markdown.gtx"
66
+ target: "templates/markdown/markdown.gtx"
67
+ - source: "render/markdown/render.rb"
68
+ target: "templates/markdown/render.rb"
69
+ post_install_message: |
70
+ Generate your markdown documentation by running:
71
+
72
+ m`$ bashly render templates/markdown docs`
73
+
74
+ render_mandoc:
75
+ help: Copy the mandoc templates to your project, allowing you to customize the man documentation output.
76
+ skip_src_check: true
77
+ files:
78
+ - source: "render/mandoc/README.md"
79
+ target: "templates/mandoc/README.md"
80
+ - source: "render/mandoc/mandoc.gtx"
81
+ target: "templates/mandoc/mandoc.gtx"
82
+ - source: "render/mandoc/render.rb"
83
+ target: "templates/mandoc/render.rb"
84
+ post_install_message: |
85
+ Note that this template requires pandoc.
86
+ Generate your man pages by running:
87
+
88
+ m`$ bashly render templates/mandoc docs`
89
+
51
90
  settings:
52
91
  help: Copy a sample settings.yml file to your project, allowing you to customize some bashly options.
53
92
  skip_src_check: true
@@ -0,0 +1,45 @@
1
+ # Render mandoc
2
+
3
+ Render man pages for your script.
4
+
5
+ Note that this renderer will render specially formatted markdown documents and
6
+ will then use [pandoc](https://command-not-found.com/pandoc) to convert them.
7
+
8
+ ## Usage
9
+
10
+ ```bash
11
+ # Generate all man pages to the ./docs directory
12
+ $ bashly render :mandoc docs
13
+
14
+ # Generate on change, and show one of the files
15
+ $ bashly render :mandoc docs --watch --show cli-download.1
16
+ ```
17
+
18
+ ## Supported custom definitions
19
+
20
+ Add these definitions to your `bashly.yml` to render them in your
21
+ markdown:
22
+
23
+ ### Footer: `x_mandoc_footer`
24
+
25
+ Add additional sections to your man pages. This field is expected
26
+ to be in markdown format.
27
+
28
+ #### Example
29
+
30
+ ```yaml
31
+ x_mandoc_footer: |-
32
+ # ISSUE TRACKER
33
+
34
+ Report issues at <https://github.com/lanalang/smallville>
35
+ ```
36
+
37
+ ### Authors: `x_mandoc_authors`
38
+
39
+ Add an authors string to your man pages.
40
+
41
+ #### Example
42
+
43
+ ```yaml
44
+ x_mandoc_authors: Lana Lang
45
+ ```