#!/bin/bash license=" # # Copyright (c) 2018 Intel Corporation # # SPDX-License-Identifier: Apache-2.0 " set -e [[ -n "${DEBUG}" ]] && set -x script_name="${0##*/}" typeset -r warning="WARNING: Do *NOT* run the generated script without reviewing it carefully first!" # github markdown markers used to surround a code block. All text within the # markers is rendered in a fixed font. typeset -r bash_block_open="\`\`\`bash" typeset -r block_open="\`\`\`" typeset -r block_close="\`\`\`" # GitHub issue templates have a special metadata section at the top delimited # by this string. See: # # https://raw.githubusercontent.com/kata-containers/.github/master/.github/ISSUE_TEMPLATE/bug_report.md typeset -r metadata_block='---' # Used to delimit inline code blocks typeset -r backtick="\`" # convention used in all documentation to represent a non-privileged users # shell prompt. All lines starting with this value inside a code block are # commands the user should run. typeset -r code_prompt="\$ " # files are expected to match this regular expression typeset -r extension_regex="\.md$" strict="no" require_commands="no" check_only="no" invert="no" verbose="no" usage() { cat < [ []] This script will convert a github-flavoured markdown document file into a bash(1) script to stdout by extracting the bash code blocks. Options: -c : check the file but don't create the script (sets exit code). -h : show this usage. -i : invert output (remove code blocks and inline code, displaying the remaining parts of the document). Incompatible with '-c'. -r : require atleast one command block to be found. -s : strict mode - perform extra checks. -v : verbose mode. Example usage: $ ${script_name} foo.md foo.md.sh Notes: - If a description is specified, it will be added to the script as a comment. - may be specified as '-' meaning send output to stdout. Limitations: - The script is unable to handle embedded code blocks like this: \`\`\` \`\`\`bash \$ echo code in an embedded set of backticks \`\`\` \`\`\` To overcome this issue, ensure that the outer set of backticks are replaced with an HTML PRE tag:

      \`\`\`bash
      \$ echo code in an embedded set of backticks
      \`\`\`

  
This will both render correctly on GitHub and allow this script to remove the code block. Note: this solves one problem but introduces another - this script will not remove the HTML tags. ${warning} EOF exit 0 } die() { local msg="$*" echo "ERROR: ${msg}" >&2 exit 1 } script_header() { local -r description="$1" cat <<-EOF #!/bin/bash ${license} #---------------------------------------------- # WARNING: Script auto-generated from '${file}'. # # ${warning} #---------------------------------------------- #---------------------------------------------- # Description: ${description} #---------------------------------------------- # fail the entire script if any simple command fails set -e EOF } # Convert the specified github-flavoured markdown format file # into a bash script by extracting the bash blocks. doc_to_script() { file="$1" outfile="$2" description="$3" invert="$4" [[ -n "${file}" ]] || die "need file" [[ "${check_only}" = "no" ]] && [[ -z "${outfile}" ]] && die "need output file" [[ "${outfile}" = '-' ]] && outfile="/dev/stdout" if [[ "${invert}" = "yes" ]] then # First, remove code blocks. # Next, remove inline code in backticks. # Finally, remove a metadata block as used in GitHub issue # templates. sed -e "/^[ \>]*${block_open}/,/^[ \>]*${block_close}/d" \ -e "s/${backtick}[^${backtick}]*${backtick}//g" \ -e "/^${metadata_block}$/,/^${metadata_block}$/d" \ < "${file}" > "${outfile}" return fi all=$(mktemp) body=$(mktemp) sed -n "/^ *${bash_block_open}/,/^ *${block_close}/ p" < "${file}" |\ sed -e "/^ *${block_close}/ d" \ -e "s/^ *${code_prompt}//g" \ -e 's/^ *//g' > "${body}" [[ "${require_commands}" = "yes" ]] && [[ ! -s "${body}" ]] && die "no commands found in file '${file}'" script_header "${description}" > "${all}" cat "${body}" >> "${all}" # sanity check [[ "${check_only}" = "yes" ]] && redirect="1>/dev/null 2>/dev/null" # $redirect is intentionally unquoted; it contains redirections interpreted by eval # shellcheck disable=SC2086 { local ret; eval bash -n "${all}" ${redirect}; ret=$?; } || true [[ "${ret}" -ne 0 ]] && die "shell code in file '${file}' is not valid" # create output file [[ "${check_only}" = "no" ]] && cp "${all}" "${outfile}" # clean up rm -f "${body}" "${all}" } main() { while getopts "chirsv" opt do case ${opt} in c) check_only="yes" ;; h) usage ;; i) invert="yes" ;; r) require_commands="yes" ;; s) strict="yes" ;; v) verbose="yes" ;; *) die "invalid option: -${OPTARG}" ;; esac done shift $((OPTIND - 1)) file="$1" outfile="$2" description="$3" [[ -n "${file}" ]] || die "need file" [[ "${verbose}" = "yes" ]] && echo "INFO: processing file '${file}'" if [[ "${strict}" = "yes" ]] then echo "${file}"|grep -q "${extension_regex}" ||\ die "file '${file}' doesn't match pattern '${extension_regex}'" fi doc_to_script "${file}" "${outfile}" "${description}" "${invert}" } main "$@"