diff --git a/admin/rfc.sh b/admin/rfc.sh new file mode 100755 index 000000000..961d3d14c --- /dev/null +++ b/admin/rfc.sh @@ -0,0 +1,321 @@ +#!/bin/bash + + +function usage() +{ + less -e <<"EOF" +Script to maintain Lumiera RFC's + +usage: + ./admin/rfc.sh [options] + +options: + title - Quoted string used as RFC title + rfc - Name of the RFC, smart matched + regex - Regex matched against the content of a RFC + chapter - Heading of a section + +commands (with and [optional] parameters): + help - Show this help + find [regex] - List all matching RFC's (matching 'regex') + show [regex] - Read RFC's (matching 'regex') + edit [chapter] - Edit RFC at chapter + process - Do automatic maintenance work + create - Create a new RFC + draft <rfc> - Change RFC to Draft state + park <rfc> - Change RFC to Parked state + final <rfc> - Change RFC to Final state + drop <rfc> - Change RFC to Dropped state + comment <rfc> - Add a new comment to a RFC + discard <rfc> - Delete an RFC + +Smart matching: + RFC names don't need to be given exactly, they use a globbing pattern. + This is: + - case insensitive + - whitespaces are ignored + - '*' stands for any number of parameters + - '?' is any single character + - when starting with '/' they are matched against the begin of the name + - some regex operators work too + 'find' and 'show' can operate on many matches so the given rfc name doesn't + need to be unique. The other commands will complain when the RFC name given + doesn't resolve to one unique RFC. + +Notes: + When less presents multiple files one can go forth and back with the ':n' + and ':p' commands. + + The tile for 'create' should be a normal document title. Possibly quoted + since it may contain spaces and not too long. The filename is this title + in CamelCase with all spaces and special characters removed. + + Chapter machching single lines containing this word, special asciidoc + comments in the form '//word:.*' and asciidoc block attributes '[word.*]' + on a single line. When a chapter pattern is not unique, the last one is + picked. + + rfc.sh executes git add/rm/mv commands, but never commits. One should do a + commit as soon he finished editing. + +EOF +} + + +function camel_case() +{ + local c + local u + local t=" $1" + + for c in {a..z}; do + u=$(tr "a-z" "A-Z" <<<"$c") + t="${t// $c/$u}" + done + + echo "$(tr -c -d "A-Za-z" <<<"$t")" +} + + +function find_rfc() +{ + # find rfc by shortname + local file + local match="$1" + match="${match// /}" + match="${match//./\.}" + match="${match//\?/.}" + match="${match//\*/.*}" + + local globstate=$(shopt -p nocasematch) + shopt -s nocasematch + + for file in $(find ./doc/devel/rfc* -name '*.txt'); + do + local name="/${file##*/}" + if [[ "$name" =~ $match ]]; then + echo "$file" + fi + done + $globstate +} + + + +function find_chapter() +{ + # find chapter + local file="$1" + local chapter="$2" + local found=$(grep -n -i "^\($chapter\|//$chapter:.*\|\[$chapter.*\]\)\$" "$file" | tail -1) + if [[ "$found" ]]; then + echo "${found%%:*}" + fi +} + + + +function process() +{ + local file="$1" + local path="${1%/*}" + local state=$(grep '^\*State\* *' "$file") + + case "$state" in + *Final*) + if [[ "$path" != "./doc/devel/rfc" ]]; then + git mv "$file" "./doc/devel/rfc" + fi + ;; + *Idea*|*Draft*) + if [[ "$path" != "./doc/devel/rfc_pending" ]]; then + git mv "$file" "./doc/devel/rfc_pending" + fi + ;; + *Parked*|*Dropped*) + if [[ "$path" != "./doc/devel/rfc_dropped" ]]; then + git mv "$file" "./doc/devel/rfc_dropped" + fi + ;; + esac +} + + +function edit() +{ + # filename lineoffset chapter + EDITOR="${EDITOR:-$(git config --get core.editor)}" + EDITOR="${EDITOR:-VISUAL}" + + local file="$1" + local line=0 + + if [[ "$3" ]]; then + line=$(find_chapter "$file" "$3") + fi + + $EDITOR +$(($line+${2:-1})) $file +} + + +function unique_name() +{ + local files=($(find_rfc "$1")) + + if [[ ${#files[*]} -gt 1 ]]; then + echo "multiple matches:" >&2 + ( + IFS=$'\n' + echo "${files[*]}" >&2 + ) + elif [[ ${#files[*]} -eq 0 ]]; then + echo "no matches" >&2 + else + echo ${files[0]} + fi +} + + +function add_comment() +{ + local name="$1" + local nl=$'\n' + local comment="//edit comment$nl $(date +%c) $(git config --get user.name) <$(git config --get user.email)>$nl" + + ed "$name" 2>/dev/null <<EOF +/endof_comments:/-1i +$comment +. +wq +EOF + + edit "$name" -4 "endof_comments" +} + +function edit_state() +{ + local name="$1" + local state="$2" + local comment="$3" + + ed "$name" 2>/dev/null <<EOF +/^\*State\*/s/\(\*State\* *_\).*_/\1${state}_/ +wq +EOF + + if [[ "$comment" ]]; then + + ed "$name" 2>/dev/null <<EOF +/endof_comments:/-1i +$comment +. +wq +EOF + + fi +} + + +function change_state() +{ + #rfcname state + local name="$1" + local state="$2" + + local nl=$'\n' + local comment="$state$nl//add reason$nl $(date +%c) $(git config --get user.name) <$(git config --get user.email)>$nl" + edit_state "$name" "$state" "$comment" + edit "$name" -4 "endof_comments" + process "$name" +} + + +command="$1" +shift + +case "$command" in +process) + # for all rfc's + for file in $(find ./doc/devel/rfc* -name '*.txt'); + do + process "$file" + done + : + ;; +find|list|ls) + if [[ "$2" ]]; then + find_rfc "$1" | xargs grep -i -C3 -n "$2" + else + find_rfc "$1" + fi + ;; +show|less|more) + if [[ "$2" ]]; then + less $(find_rfc "$1" | xargs grep -i -l "$2") + else + less $(find_rfc "$1") + fi + ;; +create) + TITLE="$@" + name=$(camel_case "$TITLE") + if [[ -f "./doc/devel/rfc/${name}.txt" || + -f "./doc/devel/rfc_pending/${name}.txt" || + -f "./doc/devel/rfc_dropped/${name}.txt" ]]; then + echo "$name.txt exists already" + else + source ./doc/template/rfc.txt >"./doc/devel/rfc_pending/${name}.txt" + edit "./doc/devel/rfc_pending/${name}.txt" 2 abstract + git add "./doc/devel/rfc_pending/${name}.txt" + process "./doc/devel/rfc_pending/${name}.txt" + fi + ;; +edit) + name=$(unique_name "$1") + if [[ "$name" ]]; then + edit "${name}" 2 "$2" + fi + ;; +draft) + name=$(unique_name "$1") + if [[ "$name" ]]; then + change_state "$name" Draft + fi + ;; +park) + name=$(unique_name "$1") + if [[ "$name" ]]; then + change_state "$name" Parked + fi + ;; +final) + name=$(unique_name "$1") + if [[ "$name" ]]; then + change_state "$name" Final + fi + ;; +drop) + name=$(unique_name "$1") + if [[ "$name" ]]; then + change_state "$name" Dropped + fi + ;; +comment) + name=$(unique_name "$1") + if [[ "$name" ]]; then + add_comment "${name}" + fi + ;; +discard) + name=$(unique_name "$1") + if [[ "$name" ]]; then + git rm "${name}" + fi + ;; +help|*) + usage + ;; +esac + + + diff --git a/doc/devel/images/DIR_INFO b/doc/devel/images/DIR_INFO new file mode 100644 index 000000000..5ac933ec9 --- /dev/null +++ b/doc/devel/images/DIR_INFO @@ -0,0 +1 @@ +illustrations refered by devel docs diff --git a/doc/devel/rfc/DIR_INFO b/doc/devel/rfc/DIR_INFO new file mode 100644 index 000000000..393ae51b8 --- /dev/null +++ b/doc/devel/rfc/DIR_INFO @@ -0,0 +1 @@ +accepted design proposals diff --git a/doc/devel/rfc_dropped/DIR_INFO b/doc/devel/rfc_dropped/DIR_INFO new file mode 100644 index 000000000..4b6ab26c8 --- /dev/null +++ b/doc/devel/rfc_dropped/DIR_INFO @@ -0,0 +1 @@ +dropped or parked design proposals diff --git a/doc/devel/rfc_pending/DIR_INFO b/doc/devel/rfc_pending/DIR_INFO new file mode 100644 index 000000000..a3e726904 --- /dev/null +++ b/doc/devel/rfc_pending/DIR_INFO @@ -0,0 +1 @@ +unfinished/undecided design proposals diff --git a/doc/template/DIR_INFO b/doc/template/DIR_INFO new file mode 100644 index 000000000..d8329334d --- /dev/null +++ b/doc/template/DIR_INFO @@ -0,0 +1 @@ +skeletons for formal documents diff --git a/doc/template/rfc.txt b/doc/template/rfc.txt new file mode 100644 index 000000000..dab031431 --- /dev/null +++ b/doc/template/rfc.txt @@ -0,0 +1,72 @@ +# this template is source by a shell script, caveat ` characters need to be escaped +cat <<EOF +$TITLE +${TITLE//?/=} + +// please don't remove the //word: comments + +[grid="all"] +\`------------\`----------------------- +*State* _Idea_ +*Date* _$(date +%c)_ +*Proposed by* $(git config --get user.name) <$(git config --get user.email)> +------------------------------------- + +[abstract] +****************************************************************************** + +****************************************************************************** + + +Description +----------- +//description: add a detailed description: + + + +Tasks +~~~~~ +// List what would need to be done to implement this Proposal in a few words: +// * item ... + + +Pros +^^^^ +// add just a fact list/enumeration which make this suitable: +// * foo +// * bar ... + + + +Cons +^^^^ +// fact list of the known/considered bad implications: + + + +Alternatives +------------ +//alternatives: if possible explain/link alternatives and tell why they are not viable: + + + +Rationale +--------- +//rationale: Describe why it should be done *this* way: + + + +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) write some conclusions about its process: + + + + +Comments +-------- +//comments: append below + + +//endof_comments: +EOF