22 Commits

Autor SHA1 Mensagem Data
  Alois Mahdal 8c0952ceaf Bump version to 0.0.21 5 meses atrás
  Alois Mahdal d031c8c635 Update release source branch to 'main' 5 meses atrás
  Alois Mahdal 0e1d02393f Update MKit to v0.1.3 5 meses atrás
  Alois Mahdal 3d0eb8e16b Update CI config after big gladness cleanup 7 meses atrás
  Alois Mahdal 330d7dba83 Bump version to 0.0.20 3 anos atrás
  Alois Mahdal 0525eb079c Fix non-existent cache on first run breaking locking 3 anos atrás
  Alois Mahdal 2ba6866a96 Bump version to 0.0.19 3 anos atrás
  Alois Mahdal 68fda08d5d Add missing make build dependency 3 anos atrás
  Alois Mahdal 26de8e00fc Bump version to 0.0.18 3 anos atrás
  Alois Mahdal 278f53ca87 Add explanation of CA certificate storage logic 3 anos atrás
  Alois Mahdal 122e83b51b Only use 'certificates' by default if it exists 3 anos atrás
  Alois Mahdal 1dabaeba4d Allow disabling passing of cert file 3 anos atrás
  Alois Mahdal 320a0fd6f6 Add missing option description in usage message 3 anos atrás
  Alois Mahdal 860afc6edc Bump version to 0.0.17 3 anos atrás
  Alois Mahdal b489e7d819 Set default queue size 512 for get_queue 3 anos atrás
  Alois Mahdal d584e65b5e Return nothing gracefully if no sequence is provided 3 anos atrás
  Alois Mahdal 693b8abf6a Bump version to 0.0.16 3 anos atrás
  Alois Mahdal 78747b5460 Add head() method to get first N messages in sequence 3 anos atrás
  Alois Mahdal 7a6cbb22ab Update URLs after migrating to GitLab.com 3 anos atrás
  Alois Mahdal 1450174533 Enable Gladness pipeline 3 anos atrás
  Alois Mahdal f2112e2588 Remove unnecessary execution bit in src 3 anos atrás
  Alois Mahdal 1b3bfa7249 Update URLs after migrating from pagure.io 3 anos atrás

+ 5
- 0
.gitlab-ci.yml Ver arquivo

@@ -0,0 +1,5 @@
1
+---
2
+include:
3
+  - project: 'vornet/infra/gladness'
4
+    ref: main
5
+    file: '/pipelines/mkit.yaml'

+ 4
- 4
mkit.ini Ver arquivo

@@ -1,11 +1,11 @@
1 1
 [project]
2
-    version     = 0.0.15
2
+    version     = 0.0.21
3 3
     name        = imapdomo
4 4
     pkgname     = imapdomo
5 5
     maintainer  = Alois Mahdal <netvor+imapdomo@vornet.cz>
6
-    vcs_browser = https://pagure.io/imapdomo
6
+    vcs_browser = https://gitlab.com/vornet/hacktop/imapdomo
7 7
     tagline     = imapfilter convenience wrapper
8
-    relsrc      = master
8
+    relsrc      = main
9 9
     reldst      = latest
10 10
 
11 11
 [dist]
@@ -42,4 +42,4 @@
42 42
     share   = src/imaprules.lua
43 43
     share   = src/main.lua
44 44
 
45
-#mkit version=0.0.40
45
+#mkit version=0.1.3

+ 1
- 0
packaging/debian/control Ver arquivo

@@ -6,6 +6,7 @@ Priority: extra
6 6
 Standards-Version: 3.9.2
7 7
 Build-Depends:
8 8
  debhelper (>= 9),
9
+ make,
9 10
 
10 11
 Package: __MKIT_PROJ_PKGNAME__
11 12
 Architecture: all

+ 1
- 0
packaging/template.spec Ver arquivo

@@ -8,6 +8,7 @@ URL:        __MKIT_PROJ_VCS_BROWSER__
8 8
 License:    GPLv2
9 9
 Source0:    %{name}-%{version}.tar.gz
10 10
 BuildArch:  noarch
11
+BuildRequires: make
11 12
 
12 13
 Requires: %shellfu_req
13 14
 Requires: imapfilter

+ 16
- 2
src/imapdomo.lua Ver arquivo

@@ -57,16 +57,17 @@ end
57 57
 -- mail getters                                                             --
58 58
 ------------------------------------------------------------------------------
59 59
 
60
-pkg.get_queue = function(acct, mbox)
60
+pkg.get_queue = function(acct, mbox, size)
61 61
     --
62 62
     -- Get queue from *mbox* from *acct* or nil (no messages)
63 63
     --
64 64
     -- If mbox is not specified, "FILTER_QUEUE" is used
65 65
     --
66 66
     mbox = mbox or "FILTER_QUEUE"
67
+    size = size or 512
67 68
     local exist = acct[mbox]:check_status()
68 69
     if exist > 0 then
69
-        return acct[mbox]:select_all()
70
+        return pkg.head(size, acct[mbox]:select_all())
70 71
     end
71 72
     return nil
72 73
 end
@@ -203,6 +204,19 @@ pkg.filter_header_saved = function(seq, name)
203 204
     return result
204 205
 end
205 206
 
207
+pkg.head = function(num, seq)
208
+    --
209
+    -- Return first *num* elements from sequence
210
+    --
211
+    if not seq then return seq end
212
+    local result = seq:is_smaller(0)    -- HACK to generate empty sequence
213
+    for idx, value in ipairs(seq) do
214
+        if idx > num then break end
215
+        table.insert(result, value)
216
+    end
217
+    return result
218
+end
219
+
206 220
 pkg.filter_part_like = function(query, seq)
207 221
     --
208 222
     -- Run MIME part query on *seq* sequence of messages

+ 12
- 1
src/imapdomo.skel Ver arquivo

@@ -16,9 +16,12 @@ usage() {
16 16
             "-c DIR      change to DIR before doing anything"                 \
17 17
             "-l          list handlers and exit"                              \
18 18
             "-d          turn on debugging mode"                              \
19
+            "-t CERTS    Use certificates file CERTS."                        \
20
+            "-T          Use system CA certificate storage."                  \
19 21
             "-N          turn off locking; make sure that handlers cannot"    \
20 22
             "            interfere with each other or that another locking"   \
21 23
             "            mechanism is in place.  Also see NOTE below."        \
24
+            "-V          Show imapdomo version and exit."                     \
22 25
        --                                                                     \
23 26
        "imapdomo will try to read init.lua and handlers/ACTION.lua from its"  \
24 27
        "configuration directory."                                             \
@@ -26,6 +29,12 @@ usage() {
26 29
        "See imapfilter_config(5)) for guide and API reference.  Few functions"\
27 30
        "are also available in .imapdomo/imapdomo.lua"                         \
28 31
        ""                                                                     \
32
+       "By default, imapdomo will look for 'certificates' file under the"     \
33
+       "directory (__IMAPDOMO_CONFIG_USER__), and if it exists, it will tell" \
34
+       "imapfilter to use it via -t argument.  If it does not exist, it will" \
35
+       "leave the argument out, meaning imapfilter will use system cert"      \
36
+       "storage.  Use -T to force system storage or -t to force another file."\
37
+       ""                                                                     \
29 38
        "NOTE: Be aware that it's your responsibility to ensure that filters"  \
30 39
        "don't cause performance problems. (Trust me, it's not so hard to"     \
31 40
        "\"earn\" yourself a nice server ban--I learned that the hard way)."
@@ -88,6 +97,7 @@ lock() {
88 97
     #
89 98
     debug -v NoLock
90 99
     $NoLock && return 0
100
+    mkdir -p "$IMAPDOMO_USER_CACHE"
91 101
     date +"$$@%s" > "$IMAPDOMO_USER_CACHE/lock"
92 102
     debug -f "$IMAPDOMO_USER_CACHE/lock"
93 103
 }
@@ -157,7 +167,7 @@ main() {
157 167
     CfgDir="$IMAPDOMO_CFGDIR"
158 168
     LogDir="$IMAPDOMO_USER_CACHE/logs"
159 169
     HeaderDir="$IMAPDOMO_USER_CACHE/headers"
160
-    Certs="$IMAPDOMO_CFGDIR/certificates"
170
+    test -f "$IMAPDOMO_CFGDIR/certificates" && Certs="$IMAPDOMO_CFGDIR/certificates"
161 171
     NoLock=false
162 172
     Debug=false
163 173
     #shellcheck disable=SC2034
@@ -167,6 +177,7 @@ main() {
167 177
         -t) Certs="$2"; shift 2 || usage -w "missing value to: $1" ;;
168 178
         -l) lshandlers; exit ;;
169 179
         -N) NoLock=true; shift ;;
180
+        -T) Certs=; shift ;;
170 181
         -V|--version-semver) show_semversion ;;
171 182
         --version) show_version ;;
172 183
         -*) usage -w "unknown argument: '$1'" ;;

+ 226
- 145
utils/mkit/include/build.sh Ver arquivo

@@ -4,9 +4,11 @@
4 4
 
5 5
 mkit_import ini
6 6
 mkit_import facts
7
+mkit_import plugin
8
+mkit_import util
7 9
 
8 10
 
9
-__build1() {
11
+build__file() {
10 12
     #
11 13
     # Process one skeleton $1 of type $3 (or guessed) to path $2
12 14
     #
@@ -14,28 +16,57 @@ __build1() {
14 16
     local dstpath=$2    # destination meaty animal path
15 17
     local ftype=$3      # file/builder type
16 18
     test -n "$dstpath"  || dstpath=${srcpath%.skel}
17
-    test -n "$ftype"    || ftype=$(__guess_ftype "$dstpath")
19
+    test -n "$ftype"    || ftype=MKIT_COMMON
18 20
     debug_var srcpath dstpath ftype
19
-    <"$srcpath" __build1_ftype "$ftype" >"$dstpath"
20
-    __rec_built "$dstpath"
21
+    <"$srcpath" __build__file_type "$ftype" >"$dstpath"
22
+    build__record "$dstpath"
21 23
 }
22 24
 
23
-__build1_ftype() {
25
+__build__file_type() {
24 26
     #
25 27
     # Build a file of type $1; fom stdin to stdout
26 28
     #
27 29
     local ftype=$1      # file/builder type
28
-    case $ftype in
29
-        MKIT_COMMON)    __expand_macros "macros" ;;
30
-        rpmstuff)       __expand_macros "macros" "rpmstuff:macros" ;;
31
-        debstuff)       __expand_macros "macros" "debstuff:macros" ;;
32
-        *)              die "unknown file type: $ftype" ;;
33
-    esac
30
+    if test "$ftype" == MKIT_COMMON; then
31
+        __build__macro_expand "build:macros"
32
+    elif plugin__isvalid "$ftype"; then
33
+        __build__macro_expand "$ftype:macros"
34
+    else
35
+        die "unknown file type: $ftype"
36
+    fi
34 37
 }
35 38
 
36
-__expand_line() {
39
+__build__macro_ls() {
37 40
     #
38
-    # Expand macro from $MacroMap in single line $1
41
+    # List known macros
42
+    #
43
+    find "${MacroDirs[@]}" -mindepth 1 -depth -printf '%P\n' \
44
+      | sed 's/[.]/:/g'
45
+}
46
+
47
+__build__macro_read() {
48
+    #
49
+    # Read macro $1
50
+    #
51
+    local key=$1
52
+    find "${MacroDirs[@]}" -mindepth 1 -depth -name "$key" \
53
+      | head -1 \
54
+      | xargs -r cat
55
+}
56
+
57
+__build__macro_put() {
58
+    #
59
+    # Write stdin as macro $1
60
+    #
61
+    local fqkey=$1
62
+    local file="$MKIT_LOCAL/data/${fqkey//:/.}"
63
+    mkdir -p "${file%/*}"
64
+    cat >"$file"
65
+}
66
+
67
+__build__macro_expand_line() {
68
+    #
69
+    # Expand macro from config in single line $1
39 70
     #
40 71
     # If macro value has multiple lines, repeat original line with
41 72
     # different substitution.
@@ -44,76 +75,47 @@ __expand_line() {
44 75
     # line `see: "__FOO__"` will expand to two lines: `see: "foo"`
45 76
     # and `see: "bar"`.
46 77
     #
47
-    local line=$1   # line to process
48
-    local mname     # macro name
49
-    local mvline    # line of macro value
50
-    local xline     # expanded line
51
-    xline=$line
52
-    for mname in "${!MacroMap[@]}"; do
53
-        if ! test "${line//$mname}" == "$line"; then
54
-            xline=$(
55
-                while IFS= read -r mvline; do
56
-                    echo "${line//$mname/$mvline}"
57
-                done <<<"${MacroMap[$mname]}"
78
+    local raw_line=$1       # line to process
79
+    local macro_name        # macro name
80
+    local macro_value_line  # line of macro value
81
+    local expanded_line     # expanded line
82
+    expanded_line=$raw_line
83
+    for macro_name in $(__build__macro_ls); do
84
+        if ! test "${raw_line//$macro_name}" == "$raw_line"; then
85
+            expanded_line=$(
86
+                while IFS= read -r macro_value_line; do
87
+                    echo "${raw_line//"$macro_name"/"$macro_value_line"}"
88
+                done <<<"$(__build__macro_read "$macro_name")"
58 89
             )
59 90
         fi
60
-        line=$xline
91
+        raw_line=$expanded_line
61 92
     done
62
-    echo "$xline"
93
+    echo "$expanded_line"
63 94
     return 1
64 95
 }
65 96
 
66
-__expand_macros() {
97
+__build__macro_expand() {
67 98
     #
68
-    # Read stdin, expanding macros from sections $@
99
+    # Read stdin, expanding macros from extra sections $@
69 100
     #
70
-    local section       # each section to expand macros from
101
+    local MacroDirs=()  # directories to lookup macros in
102
+    local mdir          # every ^^
71 103
     local line          # each line on stdin
72
-    local mname         # each macro name
73
-    local -A MacroMap   # macro value map
74
-    MacroMap[__MKIT_MKIT_VERSION__]=$MKIT_VERSION
75
-    MacroMap[__MKIT_PROJ_NAME__]=$(ini 1value project:name)
76
-    MacroMap[__MKIT_PROJ_CODENAME__]=$(ini 1value project:codename)
77
-    MacroMap[__MKIT_PROJ_LICENSE__]=$(ini 1value project:license)
78
-    MacroMap[__MKIT_PROJ_PKGNAME__]=$(ini 1value project:pkgname)
79
-    MacroMap[__MKIT_PROJ_TAGLINE__]=$(ini 1value project:tagline)
80
-    MacroMap[__MKIT_PROJ_MAINTAINER__]=$(ini 1value project:maintainer)
81
-    MacroMap[__MKIT_PROJ_VCS_BROWSER__]=$(ini 1value project:vcs_browser)
82
-    MacroMap[__MKIT_PROJ_GIT_LASTHASH__]=$(__cached git_lasthash)
83
-    MacroMap[__MKIT_PROJ_GIT_LASTSUMMARY__]=$(__cached git_lastsummary)
84
-    MacroMap[__MKIT_PROJ_VERSION__]=$(__cached semver)
85
-    for section in "$@"; do
86
-        for mname in $(ini lskeys "$section"); do
87
-            MacroMap[$mname]=$(ini values "$section:$mname")
88
-        done
104
+    local bltndata="$MKIT_LOCAL/data/MKIT_BUILTIN"
105
+    local usrdata="$MKIT_LOCAL/data/macros"
106
+    test -d "$bltndata" && MacroDirs+=("$bltndata")
107
+    test -d "$usrdata" && MacroDirs+=("$usrdata")
108
+    for extra in "$@"; do
109
+        mdir="$MKIT_LOCAL/data/${extra//:/.}"
110
+        test -d "$mdir" || continue
111
+        MacroDirs+=("$mdir")
89 112
     done
90
-    debug_var MacroMap
91 113
     while IFS= read -r line; do
92
-        __expand_line "$line"
114
+        __build__macro_expand_line "$line"
93 115
     done
94 116
 }
95 117
 
96
-__guess_ftype() {
97
-    #
98
-    # Guess file type from destination path $1
99
-    #
100
-    local dstpath=$1    # destination path
101
-    case $dstpath in
102
-        *)    echo MKIT_COMMON ;;
103
-    esac
104
-}
105
-
106
-__qfs() {
107
-    #
108
-    # Quote for our sed scipt's RHS
109
-    #
110
-    sed '
111
-        s:\\:\\\\:g
112
-        s:|:\\|:g
113
-    '
114
-}
115
-
116
-__cached() {
118
+build__cached() {
117 119
     #
118 120
     # Cached value $1 of function $1()
119 121
     #
@@ -126,7 +128,8 @@ __cached() {
126 128
     # arguments).
127 129
     #
128 130
     local name=$1
129
-    __local_get "$name" && return 0
131
+    __local_get "$name" && debug "cache hit with: $name()" && return 0
132
+    debug "cache miss with: $name()"
130 133
     "$name" | __local_putb "$name"
131 134
     __local_get "$name"
132 135
 }
@@ -136,7 +139,7 @@ __local_putb() {
136 139
     # Make file $1 in $MKIT_LOCAL from stdin and mark as built
137 140
     #
138 141
     local fpath=$1
139
-    __local_put "$fpath" && __rec_built "$MKIT_LOCAL/$fpath"
142
+    __local_put "$fpath" && build__record "$MKIT_LOCAL/$fpath"
140 143
 }
141 144
 
142 145
 __local_put() {
@@ -156,7 +159,24 @@ __local_get() {
156 159
     cat "$fpath" 2>/dev/null
157 160
 }
158 161
 
159
-__rec_built() {
162
+build__fact() {
163
+    #
164
+    # Make fact file $1 in $MKIT_LOCAL from stdin; mark as built
165
+    #
166
+    local name=$1
167
+    __local_put "facts/$name" || die
168
+    build__recordr "$MKIT_LOCAL/facts"
169
+}
170
+
171
+build__factr() {
172
+    #
173
+    # Read fact file $1 from $MKIT_LOCAL
174
+    #
175
+    local name=$1
176
+    cat "$MKIT_LOCAL/facts/$name" || die
177
+}
178
+
179
+build__record() {
160 180
     #
161 181
     # Record file $1 for deletion on `clean`
162 182
     #
@@ -165,38 +185,141 @@ __rec_built() {
165 185
     echo "1:$file" >> "$MKIT_LOCAL/built.lst"
166 186
 }
167 187
 
168
-_mkit_data() {
188
+build__recordr() {
169 189
     #
170
-    # Build sampler showing all macro values
190
+    # Record dir $1 for recursive  deletion on `clean`
191
+    #
192
+    local path=$1
193
+    mkdir -p "$MKIT_LOCAL"
194
+    echo "r:$path" >> "$MKIT_LOCAL/built.lst"
195
+}
196
+
197
+_mkit_builddata() {
198
+    #
199
+    # Build config data
171 200
     #
172 201
     local macro
173 202
     local section
174
-    local sections
175
-    sections=()
176
-    ini lskeys macros | grep -q . && sections=(macros)
177
-    sections+=( $(ini lssect | grep ':macros$') )
178
-    {
179
-        echo "(builtin):"
180
-        echo "  x_MKIT_MKIT_VERSION__ => '__MKIT_MKIT_VERSION__'"
181
-        echo "  x_MKIT_PROJ_NAME__ => '__MKIT_PROJ_NAME__'"
182
-        echo "  x_MKIT_PROJ_CODENAME__ => '__MKIT_PROJ_CODENAME__'"
183
-        echo "  x_MKIT_PROJ_LICENSE__ => '__MKIT_PROJ_LICENSE__'"
184
-        echo "  x_MKIT_PROJ_PKGNAME__ => '__MKIT_PROJ_PKGNAME__'"
185
-        echo "  x_MKIT_PROJ_TAGLINE__ => '__MKIT_PROJ_TAGLINE__'"
186
-        echo "  x_MKIT_PROJ_MAINTAINER__ => '__MKIT_PROJ_MAINTAINER__'"
187
-        echo "  x_MKIT_PROJ_VCS_BROWSER__ => '__MKIT_PROJ_VCS_BROWSER__'"
188
-        echo "  x_MKIT_PROJ_GIT_LASTHASH__ => '__MKIT_PROJ_GIT_LASTHASH__'"
189
-        echo "  x_MKIT_PROJ_GIT_LASTSUMMARY__ => '__MKIT_PROJ_GIT_LASTSUMMARY__'"
190
-        echo "  x_MKIT_PROJ_VERSION__ => '__MKIT_PROJ_VERSION__'"
191
-        for section in "${sections[@]}"; do
192
-            echo "$section:"
193
-            for macro in $(ini lskeys "$section"); do
194
-                echo "  x${macro:1} => '$macro'"
195
-            done
203
+    test -d "$MKIT_LOCAL/data/build.macros" && {
204
+        warn "mkit: using cached _mkit_builddata: $(date -Isec -r "$MKIT_LOCAL/data/build.macros")" \
205
+             "mkit:   (hint: run 'make clean' to regenerate it)"
206
+        return 0
207
+    }
208
+    build__recordr "$MKIT_LOCAL/data"
209
+    for macro in $(ini lskeys "build:macros"); do
210
+        ini values "build:macros:$macro" | __build__macro_put "build:macros/$macro"
211
+    done
212
+}
213
+
214
+_mkit_inidata() {
215
+    #
216
+    # Build INI data
217
+    #
218
+    test -d "$MKIT_LOCAL/data/ini" && {
219
+        warn "mkit: using cached _mkit_inidata: $(date -Isec -r "$MKIT_LOCAL/data/ini")" \
220
+             "mkit:   (hint: run 'make clean' to regenerate it)"
221
+        return 0
222
+    }
223
+    build__recordr "$MKIT_LOCAL/data"
224
+    local sect
225
+    local key
226
+    ini lssect \
227
+      | while read -r sect; do
228
+            mkdir -p "$MKIT_LOCAL/data/ini/$sect"
229
+            ini lskeys "$sect" \
230
+              | while read -r key; do
231
+                    ini values "$sect:$key" >"$MKIT_LOCAL/data/ini/$sect/$key"
232
+                done
196 233
         done
197
-    } \
198
-      | __expand_macros "MKIT_BUILTIN" "${sections[@]}" \
199
-      | sed '/^  x/ s|x|_|'
234
+}
235
+
236
+_mkit_metadata() {
237
+    #
238
+    # Build meta data
239
+    #
240
+    local plg_sections=()
241
+    test -d "$MKIT_LOCAL/data/MKIT_BUILTIN" && {
242
+        warn "mkit: using cached _mkit_metadata: $(date -Isec -r "$MKIT_LOCAL/data/MKIT_BUILTIN")" \
243
+             "mkit:   (hint: run 'make clean' to regenerate it)"
244
+        return 0
245
+    }
246
+    build__recordr "$MKIT_LOCAL/data"
247
+    echo "$MKIT_VERSION"            | __build__macro_put MKIT_BUILTIN/__MKIT_MKIT_VERSION__
248
+    ini 1value project:name         | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_NAME__
249
+    ini 1value project:codename     | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_CODENAME__
250
+    ini 1value project:license      | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_LICENSE__
251
+    ini 1value project:pkgname      | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_PKGNAME__
252
+    ini 1value project:tagline      | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_TAGLINE__
253
+    ini 1value project:maintainer   | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_MAINTAINER__
254
+    ini 1value project:vcs_browser  | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_VCS_BROWSER__
255
+    build__cached git_lasthash      | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_GIT_LASTHASH__
256
+    build__cached git_lastsummary   | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_GIT_LASTSUMMARY__
257
+    build__cached semver            | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_VERSION__
258
+    util__loadarr plg_sections __plugin_macro_sections
259
+    debug_var plg_sections
260
+    for section in macros "${plg_sections[@]}"; do
261
+        for macro in $(ini lskeys "$section"); do
262
+            ini values "$section:$macro" | __build__macro_put "$section/$macro"
263
+        done
264
+    done
265
+}
266
+
267
+__subdirs() {
268
+    #
269
+    # List direct sub-directories of $1
270
+    #
271
+    find "$1" -maxdepth 1 -mindepth 1 -printf '%P\n' -type d
272
+}
273
+
274
+__subfiles() {
275
+    #
276
+    # List direct sub-files of $1
277
+    #
278
+    find "$1" -maxdepth 1 -mindepth 1 -printf '%P\n' -type f
279
+}
280
+
281
+__plugin_macro_sections() {
282
+    #
283
+    # List macro sections for plugins
284
+    #
285
+    plugin__ls \
286
+      | sed 's/$/:macros/'
287
+}
288
+
289
+_mkit_show_metadata() {
290
+    #
291
+    # Show sampler of macro values
292
+    #
293
+    __show_msection MKIT_BUILTIN
294
+    __show_msection macros
295
+    __plugin_macro_sections \
296
+      | while read -r section; do
297
+            __show_msection "$section"
298
+        done
299
+}
300
+
301
+_mkit_show_builddata() {
302
+    #
303
+    # Show sampler of config values
304
+    #
305
+    __show_msection build:macros
306
+}
307
+
308
+__show_msection() {
309
+    #
310
+    # Show macros of section $1
311
+    #
312
+    local section=$1
313
+    local macro_name
314
+    local first=true
315
+    local label=$section
316
+    local secdir="$MKIT_LOCAL/data/${section//:/.}"
317
+    test -d "$secdir" || return 0
318
+    test "$section" == MKIT_BUILTIN && label="(builtin)"
319
+    for macro_name in $(__subfiles "$secdir"); do
320
+        $first && echo "$label:"; first=false
321
+        echo "    $macro_name => '$(<"$secdir/$macro_name")'"
322
+    done
200 323
 }
201 324
 
202 325
 build() {
@@ -206,7 +329,7 @@ build() {
206 329
     local srcpath   # each source path
207 330
     find . -type f -name '*.skel' \
208 331
      | while read -r srcpath; do
209
-           __build1 "$srcpath"
332
+           build__file "$srcpath"
210 333
        done
211 334
 }
212 335
 
@@ -242,38 +365,6 @@ clean() {
242 365
     true
243 366
 }
244 367
 
245
-debstuff() {
246
-    #
247
-    # Build Debian stuff (eamed tarball, debian dir)
248
-    #
249
-    local version       # package version
250
-    local debian_skel   # 'debian' folder skeleton
251
-    local dfsrc         # each source file from ^^
252
-    local dftgt         # each built packaging file
253
-    version=$(__cached semver)
254
-
255
-    # tarball - we should already have by means of 'dist'
256
-    #
257
-    mv "${MKIT_PROJ_PKGNAME}-$version.tar.gz" \
258
-       "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz" \
259
-     || die "could not rename tarball"
260
-    __rec_built "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz"
261
-
262
-    # read content of each mandatory file from debian_skel
263
-    #
264
-    debian_skel=$(ini 1value dist:debstuff)
265
-    test -n "$debian_skel" || die "dist:debstuff not specified"
266
-    test -d "$debian_skel" || die "debian directory template found: $debian_skel"
267
-    mkdir -p debian/source
268
-    find "$debian_skel" -type f \
269
-      | while read -r dfsrc; do
270
-            dftgt="debian/${dfsrc#$debian_skel}"
271
-            mkdir -p "$(dirname "$dftgt")"
272
-            __build1 "$dfsrc" "$dftgt" debstuff
273
-        done
274
-    __rec_built debian
275
-}
276
-
277 368
 dist() {
278 369
     #
279 370
     # Create distributable tarball
@@ -287,25 +378,15 @@ dist() {
287 378
     version=$(semver)
288 379
     dirname=$MKIT_PROJ_PKGNAME-$version
289 380
     git_lasthash=$(git_lasthash)
381
+    debug_var version dirname git_lasthash
290 382
     mkdir -p "$dirname"
291 383
     ini values "dist:tarball" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname"
292 384
     mkdir -p "$dirname/.mkit"
293 385
     echo -n "$version" > "$dirname/.mkit/semver"
294 386
     echo -n "$git_lasthash" > "$dirname/.mkit/git_lasthash"
387
+    cp -r "$MKIT_LOCAL/data" "$dirname/.mkit/"
295 388
     tar -cf "$dirname.tar" "$dirname"
296 389
     gzip -f "$dirname.tar"      # see above FIXME
297
-    __rec_built "$dirname.tar.gz"
390
+    build__record "$dirname.tar.gz"
298 391
     rm -rf "$dirname"
299 392
 }
300
-
301
-rpmstuff() {
302
-    #
303
-    # Build specfile
304
-    #
305
-    local specname=$MKIT_PROJ_PKGNAME.spec      # .spec filename
306
-    local specsrc                               # source of specfile
307
-    specsrc="$(ini 1value "dist:rpmstuff")"
308
-    test -n "$specsrc" || die "dist:rpmstuff not specified"
309
-    test -f "$specsrc" || die "specfile template not found: $specsrc"
310
-    __build1 "$specsrc" "$specname" rpmstuff
311
-}

+ 13
- 12
utils/mkit/include/deploy.sh Ver arquivo

@@ -3,12 +3,13 @@
3 3
 # See LICENSE file for copyright and license details.
4 4
 
5 5
 mkit_import ini
6
+mkit_import build
6 7
 
7
-__deploy_item() {
8
+__deploy__item() {
8 9
     #
9 10
     # Deploy item and make it look like wanted
10 11
     #
11
-    # usage: __deploy_item src dst [mode]
12
+    # usage: __deploy__item src dst [mode]
12 13
     #
13 14
     # Both src and dst must be names of actual items[1],
14 15
     # whereas dst must not exist.  On update, dst is
@@ -37,15 +38,15 @@ __deploy_item() {
37 38
                 [[ $item =~ .skel$ ]] \
38 39
                  && grep -qe "${item%.skel}" "$MKIT_LOCAL/built.lst" \
39 40
                  && continue
40
-                __deploy_item "$item" "$dst${item#$src}" "$mode"
41
+                __deploy__item "$item" "$dst${item#"$src"}" "$mode"
41 42
             done
42 43
     else
43 44
         test "$mode" == "SRC" && mode=$(stat -c "%a" "$src")
44
-        __maybe install -DTvm "$mode" "$src" "$dst"
45
+        __deploy__maybe install -DTvm "$mode" "$src" "$dst"
45 46
     fi
46 47
 }
47 48
 
48
-__get_dst() {
49
+__deploy__dst() {
49 50
     #
50 51
     # Find out target path for src file $2 of group $1
51 52
     #
@@ -53,10 +54,10 @@ __get_dst() {
53 54
     local src=$2        # each source
54 55
     local dst=$3        # alternative destination name
55 56
     test -n "$dst" || dst=${src##*/}
56
-    echo "$(__get_root "$grp")/$dst"
57
+    echo "$(__deploy__root "$grp")/$dst"
57 58
 }
58 59
 
59
-__get_root() {
60
+__deploy__root() {
60 61
     #
61 62
     # Find out target root for group $1
62 63
     #
@@ -73,7 +74,7 @@ __get_root() {
73 74
     esac
74 75
 }
75 76
 
76
-__maybe() {
77
+__deploy__maybe() {
77 78
     #
78 79
     # Call the deploy command $1 $@ unless in dry mode
79 80
     #
@@ -102,8 +103,8 @@ install() {
102 103
             mode=$(ini 1value "modes:$group")
103 104
             ini values "files:$group" \
104 105
               | while read -r src dst; do
105
-                    dst=$(__get_dst "$group" "$src" "$dst")
106
-                    __deploy_item "$src" "$dst" "$mode"
106
+                    dst=$(__deploy__dst "$group" "$src" "$dst")
107
+                    __deploy__item "$src" "$dst" "$mode"
107 108
                 done
108 109
         done
109 110
     test -f "$MKIT_LOCAL/autoclean" && clean
@@ -123,8 +124,8 @@ uninstall() {
123 124
       | while read -r group; do
124 125
             ini values "files:$group" \
125 126
               | while read -r src dst; do
126
-                    dst=$(__get_dst "$group" "$src" "$dst")
127
-                    __maybe rm -vrf "$dst"
127
+                    dst=$(__deploy__dst "$group" "$src" "$dst")
128
+                    __deploy__maybe rm -vrf "$dst"
128 129
                 done
129 130
         done
130 131
 }

+ 51
- 23
utils/mkit/include/facts.sh Ver arquivo

@@ -9,7 +9,10 @@ git_bool() {
9 9
     # Get git bool (ie. exit status counts) $1
10 10
     #
11 11
     local bool_name=$1      # name of boolean to get
12
-    git_present || warn "can't give bool outside git repo: $bool_name"
12
+    git_present || {
13
+        warn "can't give bool outside git repo: $bool_name"
14
+        return 3
15
+    }
13 16
     case "$bool_name" in
14 17
         dirty_files)
15 18
             git diff-files | grep -qm 1 .
@@ -20,6 +23,13 @@ git_bool() {
20 23
         dirty)
21 24
             git_bool dirty_files || git_bool dirty_index
22 25
             ;;
26
+        async)
27
+            local status_desc   # status description (in square brackets)
28
+            status_desc=$(git status -sb | grep '^##.*' | grep -o '\[[^]]*\]$')
29
+            grep -qw behind <<<"$status_desc" && return 0
30
+            grep -qw ahead <<<"$status_desc" && return 0
31
+            return 1
32
+            ;;
23 33
         *)
24 34
             warn "unknown git bool asked: $bool_name"
25 35
             return 2
@@ -51,6 +61,9 @@ git_fact() {
51 61
         reldiff)
52 62
             git log --oneline "$(git_fact latest_tag)..HEAD" --name-only
53 63
             ;;
64
+        reldiff_brief)
65
+            git rev-list --oneline "$(git_fact latest_tag)..HEAD"
66
+            ;;
54 67
         latest_sha)
55 68
             git log -1 --pretty=format:%h HEAD
56 69
             ;;
@@ -124,6 +137,42 @@ git_lastsummary() {
124 137
       | cut -d' ' -f2-
125 138
 }
126 139
 
140
+make_bmeta() {
141
+    #
142
+    # Compose build metadata string
143
+    #
144
+    local is_tagged=T   # T if tagged (clean, no metadata), F if devel
145
+    local brname        # current branch name
146
+    local ghash         # current commit short hash
147
+    if ! git describe --tags --exact-match HEAD >&/dev/null;
148
+    then    # we are at a later commit than the last tag
149
+        is_tagged=F
150
+        brname=$(git_fact current_branch | sed 's/[^[:alnum:]]/_/g')
151
+        ghash=$(git_fact latest_sha)
152
+    fi
153
+    {
154
+        if test "$is_tagged" == F; then
155
+            test -n "$stamp" && echo "t$stamp"
156
+            echo "$brname"
157
+            echo "g$ghash"
158
+            test -n "$MKIT_UPSTREAM" && echo "u$MKIT_UPSTREAM"
159
+            git_bool async && "$MKIT_ASYNC" && echo async
160
+            "$MKIT_IN_CI" && test -z "$MKIT_UPSTREAM" && git_bool async && {
161
+                warn "branch is out-of sync with upstream; git hash might be misleading" \
162
+                     "  (hint: if this is due to CI auto-rebase, set MKIT_UPSTREAM to original shorthash)"
163
+            }
164
+        fi
165
+        git_bool dirty && echo dirty
166
+    } | paste -sd.
167
+}
168
+
169
+make_suffix() {
170
+    local bmeta
171
+    bmeta=$(make_bmeta)
172
+    test -n "$bmeta" || return 0
173
+    echo "+$bmeta"
174
+}
175
+
127 176
 semver() {
128 177
     #
129 178
     # Build proper SemVer version string
@@ -162,10 +211,6 @@ semver() {
162 211
     local xyz           # base version string
163 212
     local prerl         # pre-release keyword (from mkit.ini, eg. 'beta')
164 213
     local latest_tag    # latest git tag
165
-    local brname        # current branch name
166
-    local ghash         # current commit short hash
167
-    local is_tagged=T   # T if tagged (clean, no metadata), F if devel
168
-    local is_dirty=F    # F if dirty, T if clean
169 214
     local stamp         # timestamp or nothing (see $MKIT_TSTAMP)
170 215
     local suffix        # version suffix
171 216
     prerl=$(ini 1value project:prerl)
@@ -189,24 +234,7 @@ semver() {
189 234
         *)  warn "bad form of last tag, using base version from mkit.ini: tag is '$latest_tag'"
190 235
             xyz=$(ini 1value project:version) ;;
191 236
     esac
192
-    if ! git describe --tags --exact-match HEAD >&/dev/null;
193
-    then    # we are at a later commit than the last tag
194
-        is_tagged=F
195
-        brname=$(git_fact current_branch | sed 's/[^[:alnum:]]/_/g')
196
-        ghash=$(git_fact latest_sha)
197
-    fi
198
-    git_bool dirty && is_dirty=T
199
-    case "$is_dirty:$is_tagged:$stamp" in
200
-        F:T:*)  suffix=""                                 ;;
201
-        T:T:)   suffix="+dirty"                           ;;
202
-        T:T:*)  suffix="+t$stamp.dirty"                   ;;
203
-        F:F:)   suffix="+$brname.g$ghash"                 ;;
204
-        F:F:*)  suffix="+t$stamp.$brname.g$ghash"         ;;
205
-        T:F:)   suffix="+$brname.g$ghash.dirty"           ;;
206
-        T:F:*)  suffix="+t$stamp.$brname.g$ghash.dirty"   ;;
207
-        *)      suffix=MKIT_BUG
208
-                warn "MKIT_BUG: bad dirt/commit detection" ;;
209
-    esac
237
+    suffix=$(make_suffix)
210 238
     test -n "$prerl" && suffix="-$prerl$suffix"
211 239
     echo "$xyz$suffix"
212 240
 }

+ 10
- 32
utils/mkit/include/ini.sh Ver arquivo

@@ -35,9 +35,9 @@ __ini_expandln() {
35 35
         ((Depth++))
36 36
         debug_var line_todo
37 37
         test "$Depth" -le "$MaxDepth" || {
38
-            warn "expansion error: reached maximum depth: $Depth > $MaxDepth"
39
-            warn "    original line: $line_orig"
40
-            warn "    expanded line: $line_todo"
38
+            warn "expansion error: reached maximum depth: $Depth > $MaxDepth" \
39
+                 "    original line: $line_orig" \
40
+                 "    expanded line: $line_todo"
41 41
             return 3
42 42
         }
43 43
         line_done=$(__ini_expandln_once "$line_todo")
@@ -64,7 +64,7 @@ __ini_expandln_once() {
64 64
         ipath=${ref#[}; ipath=${ipath%]}
65 65
         value=$(ini 1value "$ipath")
66 66
         debug_var line ref ipath value
67
-        line=$(sed "s|\\[$ipath\\]|$value|" <<<"$line")
67
+        line=${line//"[$ipath]"/"$value"}
68 68
     done
69 69
     echo "$line"
70 70
 }
@@ -78,11 +78,10 @@ __ini_grepcmt() {
78 78
 
79 79
 __ini_grepkey() {
80 80
     #
81
-    # Read key from a section
81
+    # Read key from a normalized `key=value` section
82 82
     #
83 83
     local wnt=$1    # wanted key
84 84
     grep '.' \
85
-      | sed -e 's/ *= */=/; s/ +$//; s/^//;' \
86 85
       | grep -e "^$wnt=" \
87 86
       | cut -d= -f2- \
88 87
       | __ini_maybe_expand
@@ -99,7 +98,7 @@ __ini_greppath() {
99 98
     #
100 99
     local wnt=$1                    # wanted path
101 100
     local wntkey=${wnt##*:}         # ^^ key part
102
-    local wntsec=${wnt%:$wntkey}    # ^^ section part
101
+    local wntsec=${wnt%:"$wntkey"}  # ^^ section part
103 102
     local override                  # ENV override (only ENV section)
104 103
     if test "$wntsec" = 'ENV'; then
105 104
         override=${!wntkey}
@@ -120,13 +119,14 @@ __ini_grepsec() {
120 119
     grep '.' \
121 120
       | while read -r line; do
122 121
             case "$line" in
123
-                \[$wnt\]) ok=true;  continue ;;
124
-                \[*\])    ok=false; continue ;;
122
+                \["$wnt"\]) ok=true;  continue ;;
123
+                \[*\])      ok=false; continue ;;
125 124
             esac
126 125
             $ok || continue
127 126
             printf -- '%s\n' "$line"
128 127
         done \
129
-      | sed -e 's/ *= */=/; s/ +$//; s/^//;'
128
+      | grep '^[[:space:]]*[^[:space:]:][^:]*[[:space:]]*[=]' \
129
+      | sed -e 's/[[:space:]]*=[[:space:]]*/=/; s/[[:space:]]*$//; s/^[[:space:]]*//;'
130 130
 }
131 131
 
132 132
 __ini_lskeys() {
@@ -191,25 +191,3 @@ ini() {
191 191
     esac
192 192
     __ini_body | $fn "$arg" | $limit
193 193
 }
194
-
195
-update_version() {
196
-    #
197
-    # Change project:version in mkit.ini at path $2 to value $1
198
-    #
199
-    local version=$1    # new version
200
-    local inifile=$2    # mkit.ini path
201
-    local tmp           # mkit.ini cache
202
-    tmp=$(mktemp -t mkit.update_version.XXXXXXXX)
203
-    <"$inifile" perl -e '
204
-        my $hit = 0;
205
-        my $done = 0;
206
-        foreach (<STDIN>) {
207
-            if      ($done) { print; next; }
208
-            elsif   (m/\[project\]/) { $hit++; print; next; }
209
-            elsif   (m/\[/) { $hit = 0; print; next; }
210
-            elsif   ($hit) { s/\bversion\b( *)=( *).*/version$1=$2$ARGV[0]/ and $done++; print; }
211
-            else { print; next; }
212
-        }
213
-    ' "$version" >"$tmp" || die "failed to update version in mkit.ini"
214
-    mv "$tmp" "$inifile"
215
-}

+ 88
- 59
utils/mkit/include/mkit.sh Ver arquivo

@@ -2,6 +2,8 @@
2 2
 # MKit - simple install helper
3 3
 # See LICENSE file for copyright and license details.
4 4
 
5
+_MKIT_IMPORTED=""
6
+
5 7
 die() {
6 8
     #
7 9
     # Exit with message and non-zero exit status
@@ -18,40 +20,27 @@ mkit_import() {
18 20
     #
19 21
     local modname=$1
20 22
     local modpath
23
+    test "$modname" == mkit && die "cannot re-import core module: $modname"
24
+    __mkit__is_imported "$modname" && return 0
21 25
     modpath="$MKIT_DIR/include/$modname.sh"
22 26
     test -f "$modpath" || die "no such module: $modpath"
23 27
     bash -n "$modpath" || die "bad syntax: $modpath"
24
-    #shellcheck disable=SC1090
28
+    #shellcheck disable=SC1090,SC1091
25 29
     . "$modpath" || die "failed to import: $modname"
30
+    _MKIT_IMPORTED+=":$modname"
26 31
 }
27 32
 
28
-mkit_import build
29
-mkit_import deploy
30
-mkit_import release
31
-mkit_import ini
32
-
33
-__valid_targets() {
34
-    #
35
-    # List valid routes
36
-    #
37
-    echo _mkit_data
38
-    echo build
39
-    echo clean
40
-    echo debstuff
41
-    echo dist
42
-    echo install
43
-    echo release
44
-    echo release_x
45
-    echo release_y
46
-    echo release_z
47
-    echo rpmstuff
48
-    echo uninstall
49
-    echo vbump
50
-    echo vbump_x
51
-    echo vbump_y
52
-    echo vbump_z
33
+__mkit__is_imported() {
34
+    #
35
+    # True if module $1 is already imported
36
+    #
37
+    local modname=$1
38
+    grep -qw -e "$modname" <<<"$_MKIT_IMPORTED"
53 39
 }
54 40
 
41
+mkit_import ini
42
+mkit_import target
43
+
55 44
 debug() {
56 45
     #
57 46
     # Print debug message
@@ -80,27 +69,82 @@ __compver() {
80 69
     #
81 70
     # True if version $1 matches our version
82 71
     #
83
-    # If our x is 0, check first two fragments, otherwise check just
84
-    # the x.  Fragments must equal.
72
+    # We're following a particular convention described in SemVer
73
+    # issue #363:
74
+    #
75
+    #     https://github.com/semver/semver/issues/363
76
+    #
77
+    # In short: If our X is 0, check first two fragments, otherwise
78
+    # check just the x.  Fragments must equal.
85 79
     #
86 80
     local their_ver=$1      # their version
87 81
     local our_x             # our X
88 82
     local our_y             # our Y
83
+    local our_z             # our z
89 84
     local their_x           # their X
90 85
     local their_y           # their Y
91
-    their_x=${their_ver%%.*}
92
-    their_y=${their_ver##$their_x.}
93
-    their_y=${their_y%%.*}
94
-    our_x=${MKIT_VERSION%%.*}
95
-    our_y=${MKIT_VERSION##$our_x.}
96
-    our_y=${our_y%%.*}
97
-    debug_var MKIT_VERSION our_x our_y their_ver their_x their_y
98
-    test "$their_x" -eq "$our_x" || return 1
99
-    test "$our_x" -eq 0 && {
100
-        test "$their_y" = "$our_y"
101
-        return $?
86
+    local their_z           # their Z
87
+    local diff_x='='        # difference in X: '=' for equal, 'o' for different
88
+    local diff_y='='        #  ^^ ...... in Y
89
+    local diff_z='='        #  ^^ ...... in Z
90
+    read -r their_x their_y their_z <<<"$(__parse_ver_xyz "$their_ver")" || die
91
+    read -r our_x   our_y   our_z   <<<"$(__parse_ver_xyz "$MKIT_VERSION")" || die
92
+    test "$their_x" -eq "$our_x" || diff_x=o
93
+    test "$their_y" -eq "$our_y" || diff_y=o
94
+    test "$their_z" -eq "$our_z" || diff_z=o
95
+    debug_var MKIT_VERSION our_x our_y our_z their_ver their_x their_y their_z diff_x diff_y diff_z
96
+    case $our_x.$our_y:$diff_x.$diff_y.$diff_z in
97
+        *.*:=.=.=)  return 0 ;;
98
+        *.*:o.?.?)  return 1 ;;
99
+        0.0:=.=.o)  __compver_hint_async ;;
100
+        0.*:=.=.?)  __compver_hint_async ;;
101
+        0.*:=.o.?)  return 1 ;;
102
+        *.*:=.=.o)  __compver_hint_async ;;
103
+        *)          warn "MKit bug while comparing versions!" \
104
+                         " .. MKit could not handle version difference; here's dump:" \
105
+                         " ...... MKIT_VERSION=$MKIT_VERSION" \
106
+                         " ...... their_ver=$their_ver" \
107
+                         " ...... our_x=$our_x our_y=$our_y our_z=$our_z" \
108
+                         " ...... their_x=$their_x their_y=$their_y their_z=$their_z" \
109
+                         " ...... diff_x=$diff_x diff_y=$diff_y diff_z=$diff_z"
110
+                    return 1 ;;
111
+    esac
112
+}
113
+
114
+__parse_ver_xyz() {
115
+    #
116
+    # Print space-separated X, Y and Z from version $1
117
+    #
118
+    local ver=$1
119
+    local xyz
120
+    xyz=$(grep -oE '^[0-9]+[.][0-9]+[.][0-9]+' <<<"$ver")
121
+    test -n "$xyz" || {
122
+        warn "could not parse version: $ver"
102 123
     }
103
-    return 0
124
+    echo "${xyz//./ }"
125
+}
126
+
127
+__compver_hint_async() {
128
+    #
129
+    # Warn about the version being async
130
+    #
131
+    # Versions where later fragments are different are considered
132
+    # compatible, but when MKit is updated, the best practice is
133
+    # to consult the changelog and see if fixes or new features
134
+    # enable optimizing or removing workarounds from the mkit.ini
135
+    # file.
136
+    #
137
+    # For that reason we will inform the maintainer about this
138
+    # fact.
139
+    #
140
+    # Because in typical MKit usage the MKit itself is embedded
141
+    # in the same git repository as the file, it should be easy
142
+    # to maintain the synchronicity.
143
+    #
144
+    warn "hint: mkit.ini version is out of sync: MKit: $MKIT_VERSION, mkit.ini: $their_ver" \
145
+         "hint:  .. To hide this hint, consider updating mkit.ini based" \
146
+         "hint:  .. on changes between these versions and update version" \
147
+         "hint:  .. directive (\`#mkit version=..\`) to match MKit."
104 148
 }
105 149
 
106 150
 __chkiniversion() {
@@ -116,10 +160,10 @@ __chkiniversion() {
116 160
         {
117 161
             head -3 "$MKIT_INI"
118 162
             tail -3 "$MKIT_INI"
119
-        } | grep -m 1 -E '^# *mkit +version *= *v?[0-9]+\.[0-9]+\.[0-9]+'
163
+        } | grep -m 1 -E '^#mkit +version *= *v?[0-9]+\.[0-9]+\.[0-9]+'
120 164
     )
121 165
     test -n "$ver_line" \
122
-     || die "version mark ('#mkit version=x.y.z') not found in: $MKIT_INI"
166
+     || die "version directive ('#mkit version=x.y.z') not found in: $MKIT_INI"
123 167
     their_ver="$(tr -d '[:blank:]v' <<<"${ver_line##*=}")"
124 168
     __compver "$their_ver" \
125 169
      || die "bad mkit.ini version: $their_ver does not match $MKIT_VERSION"
@@ -134,29 +178,14 @@ mkit_init() {
134 178
     $MKIT_DRY && MKIT_DEBUG=true
135 179
     #shellcheck disable=SC2034
136 180
     MKIT_PROJ_PKGNAME=$(ini 1value "project:pkgname")
181
+    test -n "$MKIT_PROJ_PKGNAME" || die "failed to determine pkgname: project:pkgname in $MKIT_INI"
137 182
     test -f "$MKIT_INI" || die "cannot find mkit.ini: $MKIT_INI"
138 183
     __chkiniversion
139 184
 }
140 185
 
141
-route() {
142
-    #
143
-    # Call correct function based on $1
144
-    #
145
-    if __valid_targets | grep -qwx "^$1"; then
146
-        "$1"
147
-    else
148
-        {
149
-            echo "usage: $(basename "$0") TARGET"
150
-            echo
151
-            echo "valid targets:"
152
-            __valid_targets | sed 's/^/    /'
153
-        } >&2
154
-    fi
155
-}
156
-
157 186
 warn() {
158 187
     #
159 188
     # Print warning message
160 189
     #
161
-    echo "$@" >&2
190
+    printf '%s\n' "$@" >&2
162 191
 }

+ 195
- 0
utils/mkit/include/plugin.sh Ver arquivo

@@ -0,0 +1,195 @@
1
+#!/bin/bash
2
+# MKit - simple install helper
3
+# See LICENSE file for copyright and license details.
4
+
5
+mkit_import ini
6
+mkit_import util
7
+
8
+plugin__lspaths() {
9
+    #
10
+    # List existing paths from $MKIT_PLUGINPATH
11
+    #
12
+    local path
13
+    debug_var MKIT_PLUGINPATH
14
+    tr ':' '\n' <<<"$MKIT_PLUGINPATH" \
15
+      | while read -r path; do
16
+            debug_var path
17
+            test -d "$path" || continue
18
+            echo "$path"
19
+        done
20
+}
21
+
22
+plugin__ls() {
23
+    plugin__lspaths \
24
+      | while read -r path; do
25
+            debug_var path
26
+            find "$path" -mindepth 1 -maxdepth 1 -printf '%P\n'
27
+        done
28
+}
29
+
30
+plugin__option_bool() {
31
+    #
32
+    # Safely load boolean option $1
33
+    #
34
+    local name=$1
35
+    __plugin__option_safeload \
36
+        "$name" \
37
+        "util__isa_bool" \
38
+        "not a boolean (true|false)"
39
+}
40
+
41
+plugin__option_enum() {
42
+    #
43
+    # Safely load enum option $1, allowing values $2..
44
+    #
45
+    __plugin__check_call || return 5
46
+    local name=$1; shift
47
+    local allowed_values=("$@")
48
+    local value
49
+    local desc_allowed
50
+    value=$(plugin__option_single "$name")
51
+    debug_var name value
52
+    test -n "$value" || return 1
53
+    util__isa_enum "$value" "${allowed_values[@]}" || {
54
+        desc_allowed=$(util__join "|" "${allowed_values[@]}")
55
+        warn "not one of supported values ($desc_allowed): $name = $value"
56
+        return 3
57
+    }
58
+    echo "$value"
59
+}
60
+
61
+plugin__option_multi() {
62
+    #
63
+    # Print multi-line plugin option $1
64
+    #
65
+    __plugin__check_call || return 5
66
+    local name=$1
67
+    ini values "options:$_MKIT__PLUGIN__NAME:$name" \
68
+      | util__gate_printable
69
+}
70
+
71
+plugin__option_single() {
72
+    #
73
+    # Print single-line plugin option $1
74
+    #
75
+    __plugin__check_call || return 5
76
+    local name=$1
77
+    ini 1value "options:$_MKIT__PLUGIN__NAME:$name" \
78
+      | grep .
79
+}
80
+
81
+plugin__option_int() {
82
+    #
83
+    # Safely load boolean option $1
84
+    #
85
+    local name=$1
86
+    __plugin__option_safeload \
87
+        "$name" \
88
+        "util__isa_int" \
89
+        "not an integer"
90
+}
91
+
92
+plugin__option_name() {
93
+    #
94
+    # Safely load boolean option $1
95
+    #
96
+    local name=$1
97
+    __plugin__option_safeload \
98
+        "$name" \
99
+        "util__isa_name" \
100
+        "not a valid name (alphanumeric, starting with letter)"
101
+}
102
+
103
+plugin__option_posint() {
104
+    #
105
+    # Safely load boolean option $1
106
+    #
107
+    local name=$1
108
+    __plugin__option_safeload \
109
+        "$name" \
110
+        "util__isa_posint" \
111
+        "not a positive integer"
112
+}
113
+
114
+plugin__path() {
115
+    #
116
+    # Print path to plugin file $1
117
+    #
118
+    local name=$1
119
+    local binpath
120
+    plugin__lspaths \
121
+      | while read -r path; do
122
+            binpath="$path/$name"
123
+            debug_var binpath
124
+            test -x "$binpath" || continue
125
+            echo "$binpath"
126
+        done
127
+}
128
+
129
+plugin__handle() {
130
+    #
131
+    # Run method $2 of plugin $1
132
+    #
133
+    local _MKIT__PLUGIN__NAME=$1
134
+    local method=$2
135
+    local require=true
136
+    local path
137
+    local fn
138
+    test "${method:0:1}" == "." && {
139
+        require=false
140
+        method=${method:1}
141
+    }
142
+    fn=${_MKIT__PLUGIN__NAME}__${method}
143
+    path=$(plugin__path "$_MKIT__PLUGIN__NAME")
144
+    test -n "$path" || die "plugin not found: $_MKIT__PLUGIN__NAME"
145
+    bash -n "$path" || die "syntax errors in plugin file: $path"
146
+    #shellcheck disable=SC1090
147
+    . "$path" || die "error importing plugin: $_MKIT__PLUGIN__NAME at $path"
148
+    type -t "$fn" | grep -qw function || {
149
+        $require && die "missing handler: $fn() in plugin $_MKIT__PLUGIN__NAME at $path"
150
+        debug "SILENTLY SKIPPING UNDEFINED HANDLER: $fn() in $_MKIT__PLUGIN__NAME at $path"
151
+        return 0
152
+    }
153
+    debug "RUNNING HANDLER"
154
+    debug_var fn
155
+    "$fn"
156
+}
157
+
158
+plugin__isvalid() {
159
+    #
160
+    # True if plugin $1 is valid
161
+    #
162
+    local plugin=$1
163
+    plugin__ls | grep -qwxe "$plugin"
164
+}
165
+
166
+__plugin__check_call() {
167
+    #
168
+    # Check that this call is valid
169
+    #
170
+    local func=${FUNCNAME[1]}
171
+    local caller=${FUNCNAME[2]}
172
+    test -n "$_MKIT__PLUGIN__NAME" && return 0
173
+    warn "illegal call: $func() from $caller() _MKIT__PLUGIN__NAME=$_MKIT__PLUGIN__NAME" \
174
+         "  hint: are we inside plugin?"
175
+    return 3
176
+}
177
+
178
+__plugin__option_safeload() {
179
+    #
180
+    # Safely load option $1 with validator $2 and error message $3
181
+    #
182
+    __plugin__check_call || return 5
183
+    local name=$1
184
+    local validator=$2
185
+    local msg=$3
186
+    local value
187
+    value=$(plugin__option_single "$name")
188
+    debug_var name value
189
+    test -n "$value" || return 1
190
+    "$validator" "$value" || {
191
+        warn "$msg: $name=$value"
192
+        return 3
193
+    }
194
+    echo "$value"
195
+}

+ 52
- 27
utils/mkit/include/release.sh Ver arquivo

@@ -5,7 +5,7 @@
5 5
 mkit_import ini
6 6
 mkit_import facts
7 7
 
8
-__bump_version() {
8
+__release__bump_version() {
9 9
     #
10 10
     # Bump version on stdin by level $1 (x, y or z)
11 11
     #
@@ -30,7 +30,7 @@ __bump_version() {
30 30
     echo "$new"
31 31
 }
32 32
 
33
-__relck() {
33
+__release__check() {
34 34
     #
35 35
     # Die if blocking condition $1 is detected
36 36
     #
@@ -63,12 +63,15 @@ __relck() {
63 63
              || die "last change must be version bump in mkit.ini"
64 64
             ;;
65 65
         no_wip)
66
-            git_fact reldiff \
67
-              | grep '^....... WIP ' \
66
+            git_fact reldiff_brief \
67
+              | grep -iE \
68
+                    -e '^[^ ]+ WIP ' \
69
+                    -e '^[^ ]+ WIP$'  \
70
+                    -e '^[^ ]+ WIP:'  \
68 71
              && die "WIP commit since $(git_fact latest_tag)"
69 72
             ;;
70 73
         ini_version)
71
-            oracle=$(git_fact latest_version | __bump_version "$rlevel")
74
+            oracle=$(git_fact latest_version | __release__bump_version "$rlevel")
72 75
             ini 1value project:version  \
73 76
               | grep -qFxe "$oracle" \
74 77
              || die "new version not in mkit.ini: $oracle"
@@ -94,15 +97,15 @@ __release() {
94 97
     local relsrc        # release source branch (if any)
95 98
     local reldst        # release destination branch (if any)
96 99
 
97
-    __relck git_present
98
-    __relck at_relsrc
99
-    __relck not_dirty
100
-    __relck tags_ok
101
-    __relck vbump_hot
102
-    __relck no_wip
103
-    __relck ini_version
100
+    __release__check git_present
101
+    __release__check at_relsrc
102
+    __release__check not_dirty
103
+    __release__check tags_ok
104
+    __release__check vbump_hot
105
+    __release__check no_wip
106
+    __release__check ini_version
104 107
 
105
-    newtag=$(git_fact latest_version | __bump_version "$rlevel" | git_ver2tag )
108
+    newtag=$(git_fact latest_version | __release__bump_version "$rlevel" | git_ver2tag )
106 109
     set -e
107 110
     debug_var newtag
108 111
     $MKIT_DRY && return
@@ -121,7 +124,7 @@ __release_msg() {
121 124
     # Generate message for annotated tag
122 125
     #
123 126
     # The last commit before issuing a release must be "Bump version" commit
124
-    # suggested by _vbump_gitmsg() and  manually edited by user.  Since the
127
+    # suggested by __release__vbump_gitmsg() and  manually edited by user.  Since the
125 128
     # commit contains changelog, this function just uses the message body.
126 129
     #
127 130
     # The sort message (first line) is replaced with a nicer one (with project
@@ -133,7 +136,29 @@ __release_msg() {
133 136
       | tail -n +3
134 137
 }
135 138
 
136
-__vbump() {
139
+__release__update_ini() {
140
+    #
141
+    # Change project:version in mkit.ini at path $2 to value $1
142
+    #
143
+    local version=$1    # new version
144
+    local inifile=$2    # mkit.ini path
145
+    local tmp           # mkit.ini cache
146
+    tmp=$(mktemp -t mkit.__release__update_ini.XXXXXXXX)
147
+    <"$inifile" perl -e '
148
+        my $hit = 0;
149
+        my $done = 0;
150
+        foreach (<STDIN>) {
151
+            if      ($done) { print; next; }
152
+            elsif   (m/\[project\]/) { $hit++; print; next; }
153
+            elsif   (m/\[/) { $hit = 0; print; next; }
154
+            elsif   ($hit) { s/\bversion\b( *)=( *).*/version$1=$2$ARGV[0]/ and $done++; print; }
155
+            else { print; next; }
156
+        }
157
+    ' "$version" >"$tmp" || die "failed to update version in mkit.ini"
158
+    mv "$tmp" "$inifile"
159
+}
160
+
161
+__release__vbump() {
137 162
     #
138 163
     # Do version bump at level $1
139 164
     #
@@ -143,23 +168,23 @@ __vbump() {
143 168
     local rlevel=$1     # bump level (x, y or z)
144 169
     local nextver       # version after the bump
145 170
     local cache         # cache for the message
146
-    __relck git_present
147
-    __relck at_relsrc
148
-    __relck not_dirty
149
-    nextver=$(ini 1value project:version | __bump_version "$rlevel")
171
+    __release__check git_present
172
+    __release__check at_relsrc
173
+    __release__check not_dirty
174
+    nextver=$(ini 1value project:version | __release__bump_version "$rlevel")
150 175
     debug_var nextver
151 176
     $MKIT_DRY && return
152
-    update_version "$nextver" mkit.ini \
177
+    __release__update_ini "$nextver" mkit.ini \
153 178
       || die "failed to update version in mkit.ini"
154 179
     git add mkit.ini \
155 180
       || die "failed to add mkit.ini to the index"
156
-    cache=$(mktemp -t "mkit._vbump_gitmsg.XXXXXXXX")
157
-    _vbump_gitmsg "$nextver" > "$cache"
181
+    cache=$(mktemp -t "mkit.__release__vbump_gitmsg.XXXXXXXX")
182
+    __release__vbump_gitmsg "$nextver" > "$cache"
158 183
     git commit -e -F "$cache"   # note: reading from stdin will break vim
159 184
     rm "$cache"
160 185
 }
161 186
 
162
-_vbump_gitmsg() {
187
+__release__vbump_gitmsg() {
163 188
     #
164 189
     # Compose git message template for 'Bump version' commit
165 190
     #
@@ -208,26 +233,26 @@ vbump() {
208 233
     #
209 234
     # Perform version bump on Z level
210 235
     #
211
-    __vbump z
236
+    __release__vbump z
212 237
 }
213 238
 
214 239
 vbump_x() {
215 240
     #
216 241
     # Perform version bump on X level
217 242
     #
218
-    __vbump x
243
+    __release__vbump x
219 244
 }
220 245
 
221 246
 vbump_y() {
222 247
     #
223 248
     # Perform version bump on Y level
224 249
     #
225
-    __vbump y
250
+    __release__vbump y
226 251
 }
227 252
 
228 253
 vbump_z() {
229 254
     #
230 255
     # Perform version bump on Z level
231 256
     #
232
-    __vbump z
257
+    __release__vbump z
233 258
 }

+ 91
- 0
utils/mkit/include/target.sh Ver arquivo

@@ -0,0 +1,91 @@
1
+#!/bin/bash
2
+# MKit - simple install helper
3
+# See LICENSE file for copyright and license details.
4
+
5
+mkit_import ini
6
+mkit_import plugin
7
+
8
+target__ls() {
9
+    #
10
+    # List valid routes
11
+    #
12
+    target__map | cut -d' ' -f1
13
+}
14
+
15
+target__map() {
16
+    #
17
+    # List valid routes and corresponding module:functions
18
+    #
19
+    #shellcheck disable=SC2291
20
+    {
21
+        echo _mkit_builddata        build:_mkit_builddata
22
+        echo _mkit_inidata          build:_mkit_inidata
23
+        echo _mkit_metadata         build:_mkit_metadata
24
+        echo _mkit_show_builddata   build:_mkit_show_builddata
25
+        echo _mkit_show_metadata    build:_mkit_show_metadata
26
+        echo build                  build:build
27
+        echo clean                  build:clean
28
+        echo dist                   build:dist
29
+        echo install                deploy:install
30
+        echo release                release:release
31
+        echo release_x              release:release_x
32
+        echo release_y              release:release_y
33
+        echo release_z              release:release_z
34
+        echo uninstall              deploy:uninstall
35
+        echo vbump                  release:vbump
36
+        echo vbump_x                release:vbump_x
37
+        echo vbump_y                release:vbump_y
38
+        echo vbump_z                release:vbump_z
39
+    }
40
+}
41
+
42
+target__isvalid() {
43
+    #
44
+    # True if target $1 is valid
45
+    #
46
+    local target=$1
47
+    target__map | grep -qwe "^$target"
48
+}
49
+
50
+target__route() {
51
+    #
52
+    # Call correct function based on $1
53
+    #
54
+    local target=$1
55
+    local es
56
+    if target__isvalid "$target"; then
57
+        target__run "$target"; es=$?
58
+    elif plugin__isvalid "$target"; then
59
+        plugin__handle "$target" main; es=$?
60
+    else
61
+        {
62
+            echo "usage: $(basename "$0") TARGET"
63
+            echo
64
+            echo "valid targets (built-in):"
65
+            target__ls | sed 's/^/    /'
66
+            echo
67
+            echo "valid targets (from plugins):"
68
+            plugin__ls | sed 's/^/    /'
69
+        } >&2
70
+        es=2
71
+    fi
72
+    return "$es"
73
+}
74
+
75
+target__run() {
76
+    #
77
+    # Run target $1
78
+    #
79
+    local target=$1
80
+    local module
81
+    local fn
82
+    read -r module fn <<<"$(
83
+        target__map \
84
+          | tr : ' ' \
85
+          | grep -we "^$target" \
86
+          | cut -d' ' -f2-
87
+    )"
88
+    debug_var target module fn
89
+    mkit_import "$module"
90
+    "$fn"
91
+}

+ 322
- 0
utils/mkit/include/util.sh Ver arquivo

@@ -0,0 +1,322 @@
1
+#!/bin/bash
2
+
3
+util__gate_printable() {
4
+    #
5
+    # True if stdin had non-whitespace values
6
+    #
7
+    local value
8
+    value=$(cat)
9
+    grep -q '[[:print:]]' <<<"$value" || return 1
10
+    echo "$value"
11
+}
12
+
13
+util__isa_bool() {
14
+    #
15
+    # True if $1 is a boolean value
16
+    #
17
+    test "$1" == false && return 0
18
+    test "$1" == true && return 0
19
+    return 1
20
+}
21
+
22
+util__isa_enum() {
23
+    #
24
+    # True if $1 is one of $2..
25
+    #
26
+    local value=$1; shift
27
+    local alloweds=("$@")
28
+    local allowed
29
+    for allowed in "${alloweds[@]}"; do
30
+        test "$value" == "$allowed" && return 0
31
+    done
32
+    return 1
33
+}
34
+
35
+util__isa_int() {
36
+    #
37
+    # True if $1 is an integer
38
+    #
39
+    # Note that $1 does not have to be decimal; in POSIX shells,
40
+    # eg. 0xf1 is a valid hexadecimal integer and 0755 is a valid
41
+    # octal integer.
42
+    #
43
+    {
44
+        test -z "$1"    && return 1
45
+        test "$1" -ge 0 && return 0
46
+        return 1
47
+    } 2>/dev/null
48
+}
49
+
50
+util__isa_name() {
51
+    #
52
+    # True if $1 is a name in most languages
53
+    #
54
+    echo "$1" | grep -qx '[[:alpha:]_][[:alnum:]_]*'
55
+}
56
+
57
+util__isa_posint() {
58
+    #
59
+    # True if $1 is a positive integer
60
+    #
61
+    {
62
+        test -z "$1"    && return 1
63
+        test "$1" -ge 0 && return 0
64
+        return 1
65
+    } 2>/dev/null
66
+}
67
+
68
+util__join() {
69
+    #
70
+    # Use $1 to join items $2..
71
+    #
72
+    local dlm=$1; shift
73
+    local items=("$@")
74
+    local item
75
+    local head=true
76
+    for item in "${items[@]}"; do
77
+        $head || echo -n "$dlm"
78
+        $head && head=false
79
+        echo -n "$item"
80
+    done
81
+}
82
+
83
+util__loadarr() {
84
+    #
85
+    # Save output lines from command $2.. to array named $1
86
+    #
87
+    # Usage:
88
+    #     util__loadarr ARRAY CMD [ARG]..
89
+    #
90
+    # Unlike `mapfile -t <<<"$(command)", this produces zero
91
+    # items if command output was empty.
92
+    #
93
+    local __util__arr_fromcmd__oarr=$1; shift
94
+    local __util__arr_fromcmd__cmd=("$@")
95
+    local __util__arr_fromcmd__tmp
96
+    local __util__arr_fromcmd__es
97
+    __util__arr_val_oarr "$__util__arr_fromcmd__oarr" || return 2
98
+    __util__arr_fromcmd__tmp=$(mktemp /tmp/util__loadarr.__util__arr_fromcmd__tmp.XXXXX)
99
+    "${__util__arr_fromcmd__cmd[@]}" > "$__util__arr_fromcmd__tmp"; __util__arr_fromcmd__es=$?
100
+    mapfile -t "$__util__arr_fromcmd__oarr" <"$__util__arr_fromcmd__tmp"
101
+    rm "$__util__arr_fromcmd__tmp"
102
+    return $__util__arr_fromcmd__es
103
+}
104
+
105
+util__loadenv() {
106
+    #
107
+    # Safely load value from envvar $1
108
+    #
109
+    local name=$1
110
+    local value
111
+    value=${!name}
112
+    test -n "$value" || return 1
113
+    echo "$value"
114
+}
115
+
116
+util__loadenv_bool() {
117
+    #
118
+    # Safely load integer from envvar $1
119
+    #
120
+    local name=$1
121
+    __util__safe_loadenv \
122
+        "$name" \
123
+        util__isa_bool \
124
+        "not a boolean (true|false): $name=$value"
125
+}
126
+
127
+util__loadenv_enum() {
128
+    #
129
+    # Safely load enum from envvar $1, allowing values $2..
130
+    #
131
+    local name=$1; shift
132
+    local allowed_values=("$@")
133
+    local value
134
+    local desc_allowed
135
+    value=${!name}
136
+    test -n "$value" || return 1
137
+    util__isa_enum "$value" "${allowed_values[@]}" || {
138
+        desc_allowed=$(util__join "|" "${allowed_values[@]}")
139
+        warn "not one of supported values ($desc_allowed): $name = $value"
140
+        return 3
141
+    }
142
+    echo "$value"
143
+}
144
+
145
+util__loadenv_int() {
146
+    #
147
+    # Safely load integer from envvar $1
148
+    #
149
+    local name=$1
150
+    __util__safe_loadenv \
151
+        "$name" \
152
+        util__isa_int \
153
+        "not an integer"
154
+}
155
+
156
+util__loadenv_name() {
157
+    #
158
+    # Safely load positive integer from envvar $1
159
+    #
160
+    local name=$1
161
+    __util__safe_loadenv \
162
+        "$name" \
163
+        util__isa_name \
164
+        "not a valid name (alphanumeric, starting with letter)"
165
+}
166
+
167
+util__loadenv_posint() {
168
+    #
169
+    # Safely load positive integer from envvar $1
170
+    #
171
+    local name=$1
172
+    __util__safe_loadenv \
173
+        "$name" \
174
+        util__isa_posint \
175
+        "not a positive integer"
176
+}
177
+
178
+util__sort_by_length() {
179
+    #
180
+    # Sort items on stdin by length
181
+    #
182
+    while IFS= read -r item; do
183
+        echo "${#item} $item"
184
+    done \
185
+      | sort -n \
186
+      | cut -d' ' -f2-
187
+}
188
+
189
+util__warnbox() {
190
+    #
191
+    # Emit warning $2 box-decorated with char $1, follow by lines $3..
192
+    #
193
+    local char=$1; shift
194
+    local subj=$1; shift
195
+    local body=("$@")
196
+    case "${#body[*]}" in
197
+        0)  __util__warnbox1 "$char" "$subj" ;;
198
+        *)  __util__warnbox_sub "$char" "$subj" "${body[@]}" ;;
199
+    esac
200
+}
201
+
202
+__util__warnbox1() {
203
+    #
204
+    # Emit warning $2 box-decorated with char $1
205
+    #
206
+    local char=$1
207
+    local msg=$2
208
+    local line
209
+    local midline="$char$char $msg $char$char"
210
+    line=$(__util__make_hr "$char" "$midline")
211
+    warn "$line" \
212
+         "$midline" \
213
+         "$line"
214
+}
215
+
216
+__util__warnbox_sub() {
217
+    #
218
+    # Emit warning $2 box-decorated with char $1, follow by lines $3..
219
+    #
220
+    local char=$1; shift        # decoration character
221
+    local subj=$1; shift        # local of the message subject
222
+    local body=("$@")           # lines of the message body
223
+    local longest               # longest line from subject and body
224
+    local width                 # length of ^^
225
+    local hr                    # horizontal ruler
226
+    local vr="$char$char"       # vertical ruler point
227
+    local padded_body           # body lines; padded
228
+    local padded_subj           # body line; padded
229
+    local line                  # each of ^^^
230
+    local out_lines=()          # final lines
231
+    longest=$(__util__longest "$subj" "${body[@]}")
232
+    width=${#longest}
233
+    padded_subj=$(__util__rpad "${width}" "$subj")
234
+    util__loadarr padded_body __util__rpad "$width" "${body[@]}"
235
+    debug_var longest width padded_subj padded_body
236
+    hr=$(__util__make_hr "$char" "$vr $longest $vr")
237
+    out_lines+=(
238
+        "$hr"
239
+        "$vr $padded_subj $vr"
240
+        "$hr"
241
+    )
242
+    for line in "${padded_body[@]}"; do
243
+        out_lines+=("$vr $line $vr")
244
+    done
245
+    out_lines+=("$hr")
246
+    warn "${out_lines[@]}"
247
+}
248
+
249
+__util__longest() {
250
+    #
251
+    # Choose longest of $@
252
+    #
253
+    printf '%s\n' "$@" \
254
+      | util__sort_by_length \
255
+      | tail -n 1
256
+}
257
+
258
+__util__make_hr() {
259
+    #
260
+    # Repeat char $1 to the length of $2
261
+    #
262
+    local char=$1
263
+    local stuff=$2
264
+    local length=${#stuff}
265
+    __util__repchar "$char" "$length"
266
+}
267
+
268
+__util__repchar() {
269
+    #
270
+    # Repeat char $1 $2 times
271
+    #
272
+    local char=$1
273
+    local times=$2
274
+    local togo=$times
275
+    while test "$togo" -gt 0; do
276
+        echo -n "$char"
277
+        ((togo--))
278
+    done
279
+    echo
280
+}
281
+
282
+__util__rpad() {
283
+    #
284
+    # Right-pad all of $2.. to length $1 with space
285
+    #
286
+    local wid=$1; shift
287
+    local rest=("$@")
288
+    printf "%-${wid}s"'\n' "${rest[@]}"
289
+}
290
+
291
+__util__safe_loadenv() {
292
+    #
293
+    # Safely load envvar $1 with validator $2 and error message $3
294
+    #
295
+    local name=$1
296
+    local validator=$2
297
+    local msg=$3
298
+    local value
299
+    value=${!name}
300
+    debug_var name value
301
+    test -n "$value" || return 1
302
+    "$validator" "$value" || {
303
+        warn "$msg: $name=$value"
304
+        return 3
305
+    }
306
+    echo "$value"
307
+}
308
+
309
+__util__isa_name() {
310
+    #
311
+    # True if $1 is a name in most languages
312
+    #
313
+    echo "$1" | grep -qx '[[:alpha:]_][[:alnum:]_]*'
314
+}
315
+
316
+__util__arr_val_oarr() {
317
+    #
318
+    # Validate output array named $1
319
+    #
320
+    local name=$1
321
+    __util__isa_name "$name" || { warn "invalid array name: $name"; return 2; }
322
+}

+ 72
- 1
utils/mkit/include/vars.sh Ver arquivo

@@ -4,6 +4,20 @@
4 4
 # See LICENSE file for copyright and license details.
5 5
 
6 6
 
7
+#
8
+# Add 'async' tag to build meta-data (true|false)
9
+#
10
+# If 'true' and during the version computation git branch is behind
11
+# or ahead its remote counterpart, build meta-data (part after '+'
12
+# sign) will contain word 'async'.
13
+#
14
+# This is to warn testers and developers when build was done after
15
+# automatic rebase done by CI system.
16
+#
17
+# Defaults to 'true'.
18
+#
19
+MKIT_ASYNC=${MKIT_ASYNC:-true}
20
+
7 21
 #
8 22
 # Bump size (for vbump_? and release_?)
9 23
 #
@@ -26,6 +40,16 @@ MKIT_DEFAULT_MODE="644"
26 40
 #
27 41
 MKIT_DRY=${MKIT_DRY:-false}
28 42
 
43
+#
44
+# Is MKit running in CI (true|false)
45
+#
46
+# If MKit is running in CI, it's recommended to set this to 'true'
47
+# to enable one or more useful warnings.
48
+#
49
+# Defaults to 'false'.
50
+#
51
+MKIT_IN_CI=${MKIT_IN_CI:-false}
52
+
29 53
 #
30 54
 # Path to mkit.ini
31 55
 #
@@ -48,6 +72,14 @@ MKIT_INI_EXPAND=2
48 72
 #
49 73
 MKIT_LOCAL=${MKIT_LOCAL:-.mkit}
50 74
 
75
+#
76
+# Paths to MKit plugins
77
+#
78
+# Colon-separated list of paths where MKit will recognize
79
+# plug-ins.
80
+#
81
+MKIT_PLUGINPATH=${MKIT_PLUGINPATH:-$MKIT_DIR/plugins:$MKIT_LOCAL/plugins:$MKIT_DIR-plugins}
82
+
51 83
 #
52 84
 # Package name
53 85
 #
@@ -83,7 +115,46 @@ MKIT_PROJ_PKGNAME=""
83 115
 #
84 116
 MKIT_TSTAMP=${MKIT_TSTAMP:-ctime}
85 117
 
118
+#
119
+# Upstream hash to add to build meta-data
120
+#
121
+# If set to non-empty string, it must represent a short-hash of
122
+# upstream commit on which local branch was automatically rebased
123
+# before build.
124
+#
125
+# If CI system automatically rebases branches, the resulting short-hash
126
+# (appearing with `g` prefix) will be meaningless, and might be
127
+# misleading -- testers and developers might see the build as coming
128
+# from an unknown source.
129
+#
130
+# In that case, it's strongly recommended for CI to set this to the
131
+# original pushed short-hash before the rebase.  Resulting meta-data
132
+# will contain both hashes, allowing users to pair test results with
133
+# original push.  The appearance of the extra hash will also warn
134
+# about the fact that the rebase took place.
135
+#
136
+# For example, if developer pushed branch 'foo' that pointed to
137
+# `5faf551`, and CI system rebased it, altering the HEAD to
138
+# `bb4baa4`, by default the version would look like:
139
+#
140
+#     0.0.1+t202103011224.foo.gbb4baa4
141
+#
142
+# This would be meaningless for testing, as the `bb4baa4` hash is
143
+# completely temporary and unknonwn.
144
+#
145
+# However, a conformant CI should set `MKIT_UPSTREAM=5faf551`, which
146
+# will result in version like:
147
+#
148
+#     0.0.1+t202103011224.foo.gbb4baa4.u5faf551
149
+#
150
+# which still has technically correct git hash after `g`, but contains
151
+# the original hash as pushed after `u`.
152
+#
153
+# Defaults to empty value.
154
+#
155
+MKIT_UPSTREAM=${MKIT_UPSTREAM:-}
156
+
86 157
 #
87 158
 # This MKit version
88 159
 #
89
-MKIT_VERSION=0.0.40
160
+MKIT_VERSION=0.1.3

+ 0
- 34
utils/mkit/make Ver arquivo

@@ -1,34 +0,0 @@
1
-#!/bin/bash
2
-#shellcheck disable=SC2034
3
-# mkit - simple install helper
4
-# See LICENSE file for copyright and license details.
5
-
6
-init_core() {
7
-    #
8
-    # Load core module (or die)
9
-    #
10
-    #shellcheck disable=SC1090
11
-    . "$MKIT_DIR/include/mkit.sh" \
12
-     && . "$MKIT_DIR/include/vars.sh" \
13
-     && return 0
14
-    echo "failed to load core; check if MKIT_DIR is set properly: $MKIT_DIR" >&2
15
-    exit 9
16
-}
17
-
18
-#
19
-# Path to MKit dir (where 'include' is)
20
-#
21
-MKIT_DIR=${MKIT_DIR:-$(dirname "$0")}
22
-
23
-
24
-init_core
25
-
26
-case "$1" in
27
-    -V|--version-semver)    echo "$MKIT_VERSION"; exit 0 ;;
28
-    --version)              echo "Mkit (Simple Makefile target helper) $MKIT_VERSION"
29
-                            exit 0 ;;
30
-esac
31
-
32
-mkit_init
33
-
34
-route "$@"

+ 94
- 0
utils/mkit/mkit Ver arquivo

@@ -0,0 +1,94 @@
1
+#!/bin/bash
2
+#shellcheck disable=SC2034
3
+# mkit - simple install helper
4
+# See LICENSE file for copyright and license details.
5
+
6
+usage() {
7
+    print_help >&2
8
+    test $# -gt 0 && {
9
+        printf >&2 '%s\n' "" "$@"
10
+    }
11
+    exit 2
12
+}
13
+
14
+print_help() {
15
+    local self=${0##*/}
16
+    local lines=(
17
+        "usage:"
18
+        "  $self TARGET"
19
+        "  $self -i|--ini-1value PATH"
20
+        "  $self -I|--ini-values PATH"
21
+        "  $self --list-plugins"
22
+        "  $self --list-targets"
23
+        "  $self -V|--version-semver"
24
+        "  $self --version"
25
+        ""
26
+        "commands:"
27
+        "   TARGET          build target TARGET"
28
+        "   -i PATH         print single-line value at INI path PATH"
29
+        "                   (ignore all but last instance of this"
30
+        "                   value in the INI)"
31
+        "   -I PATH         like -i, but list all instances, one per line"
32
+        "                   (multi-line values)"
33
+        "   --list-plugins  print list of found plugins and exit"
34
+        "   --list-targets  print list of valid targets and exit"
35
+        "   -V|--version    print version (in SemVer format) and exit"
36
+        "   --help          print this help text and exit"
37
+        ""
38
+    )
39
+    printf '%s\n' "${lines[@]}"
40
+    echo "valid targets (built-in):"
41
+    target__ls | sed 's/^/    /'
42
+    echo
43
+    echo "valid targets (from plugins):"
44
+    plugin__ls | sed 's/^/    /'
45
+}
46
+
47
+
48
+init_core() {
49
+    #
50
+    # Load core module (or die)
51
+    #
52
+    #shellcheck disable=SC1090,SC1091
53
+    . "$MKIT_DIR/include/mkit.sh" \
54
+     && . "$MKIT_DIR/include/vars.sh" \
55
+     && return 0
56
+    echo "failed to load core; check if MKIT_DIR is set properly: $MKIT_DIR" >&2
57
+    exit 9
58
+}
59
+
60
+#
61
+# Path to MKit dir (where 'include' is)
62
+#
63
+MKIT_DIR=${MKIT_DIR:-$(dirname "$0")}
64
+
65
+just_ini() {
66
+    #
67
+    # Just do one mkit.ini operation $1
68
+    #
69
+    local op=$1
70
+    local key=$2
71
+    mkit_init || return $?
72
+    ini "$op" "$key"
73
+}
74
+
75
+main () {
76
+    init_core
77
+    case "$1" in
78
+        -V|--version)           echo "$MKIT_VERSION"; exit 0 ;;
79
+        -i|--ini-1value)        just_ini 1value "$2"; exit $? ;;
80
+        -I|--ini-values)        just_ini values "$2"; exit $? ;;
81
+        --ini-lskeys)           just_ini lskeys "$2"; exit $? ;;
82
+        --ini-lssect)           just_ini lssect "$2"; exit $? ;;
83
+        --ini-sec)              just_ini sec    "$2"; exit $? ;;
84
+        --list-targets)         mkit_init; target__ls; plugin__ls; exit 0 ;;
85
+        --list-plugins)         mkit_init; plugin__ls; exit 0 ;;
86
+        --help)                 print_help; exit 0 ;;
87
+        -*)                     usage "unknown argument: $1" ;;
88
+    esac
89
+    test "$#" -gt 0 || usage
90
+    mkit_init
91
+    target__route "$@"
92
+}
93
+
94
+main "$@"

+ 35
- 23
utils/mkit/mkit.mk Ver arquivo

@@ -3,54 +3,66 @@
3 3
 
4 4
 export MKIT_DIR
5 5
 
6
+plugins := $(shell "$(MKIT_DIR)"/mkit --list-plugins)
7
+
8
+
6 9
 all: build
7 10
 
8
-_mkit_data:
9
-	@"$(MKIT_DIR)"/make _mkit_data
11
+_mkit_builddata: _mkit_metadata
12
+	@"$(MKIT_DIR)"/mkit _mkit_builddata
10 13
 
11
-build:
12
-	@"$(MKIT_DIR)"/make build
14
+_mkit_inidata:
15
+	@"$(MKIT_DIR)"/mkit _mkit_inidata
13 16
 
14
-clean:
15
-	@"$(MKIT_DIR)"/make clean
17
+_mkit_metadata:
18
+	@"$(MKIT_DIR)"/mkit _mkit_metadata
19
+
20
+_mkit_show_metadata: _mkit_metadata
21
+	@"$(MKIT_DIR)"/mkit _mkit_show_metadata
16 22
 
17
-debstuff: dist
18
-	@"$(MKIT_DIR)"/make debstuff
23
+_mkit_show_builddata: _mkit_builddata
24
+	@"$(MKIT_DIR)"/mkit _mkit_show_builddata
19 25
 
20
-dist: clean
21
-	@"$(MKIT_DIR)"/make dist
26
+build: _mkit_builddata
27
+	@"$(MKIT_DIR)"/mkit build
22 28
 
23
-rpmstuff: dist
24
-	@"$(MKIT_DIR)"/make rpmstuff
29
+dist: _mkit_metadata
30
+	@"$(MKIT_DIR)"/mkit dist
31
+
32
+clean:
33
+	@"$(MKIT_DIR)"/mkit clean
25 34
 
26 35
 install: all
27
-	@"$(MKIT_DIR)"/make install
36
+	@"$(MKIT_DIR)"/mkit install
28 37
 
29 38
 release:
30
-	@"$(MKIT_DIR)"/make release
39
+	@"$(MKIT_DIR)"/mkit release
31 40
 
32 41
 release_x:
33
-	@"$(MKIT_DIR)"/make release_x
42
+	@"$(MKIT_DIR)"/mkit release_x
34 43
 
35 44
 release_y:
36
-	@"$(MKIT_DIR)"/make release_y
45
+	@"$(MKIT_DIR)"/mkit release_y
37 46
 
38 47
 release_z:
39
-	@"$(MKIT_DIR)"/make release_z
48
+	@"$(MKIT_DIR)"/mkit release_z
40 49
 
41 50
 uninstall:
42
-	@"$(MKIT_DIR)"/make uninstall
51
+	@"$(MKIT_DIR)"/mkit uninstall
43 52
 
44 53
 vbump:
45
-	@"$(MKIT_DIR)"/make vbump
54
+	@"$(MKIT_DIR)"/mkit vbump
46 55
 
47 56
 vbump_x:
48
-	@"$(MKIT_DIR)"/make vbump_x
57
+	@"$(MKIT_DIR)"/mkit vbump_x
49 58
 
50 59
 vbump_y:
51
-	@"$(MKIT_DIR)"/make vbump_y
60
+	@"$(MKIT_DIR)"/mkit vbump_y
52 61
 
53 62
 vbump_z:
54
-	@"$(MKIT_DIR)"/make vbump_z
63
+	@"$(MKIT_DIR)"/mkit vbump_z
64
+
65
+$(plugins): clean _mkit_metadata
66
+	@"$(MKIT_DIR)"/mkit $@
55 67
 
56
-.PHONY: all _mkit_data clean dist rpmstuff install uninstall release release_x release_y release_z vbump vbump_x vbump_y vbump_z
68
+.PHONY: all _mkit_builddata _mkit_inidata _mkit_metadata _mkit_show_builddata _mkit_show_metadata clean dist rpmstuff install uninstall release release_x release_y release_z vbump vbump_x vbump_y vbump_z $(plugins)

+ 39
- 0
utils/mkit/plugins/debstuff Ver arquivo

@@ -0,0 +1,39 @@
1
+#!/bin/bash
2
+# MKit - simple install helper
3
+# See LICENSE file for copyright and license details.
4
+
5
+mkit_import build
6
+mkit_import target
7
+
8
+debstuff__main() {
9
+    #
10
+    # Build Debian stuff (eamed tarball, debian dir)
11
+    #
12
+    local version       # package version
13
+    local debian_skel   # 'debian' folder skeleton
14
+    local dfsrc         # each source file from ^^
15
+    local dftgt         # each built packaging file
16
+    target__run dist || die
17
+    version=$(build__cached semver)
18
+
19
+    # tarball - we should already have by means of 'dist'
20
+    #
21
+    mv "${MKIT_PROJ_PKGNAME}-$version.tar.gz" \
22
+       "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz" \
23
+     || die "could not rename tarball"
24
+    build__record "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz"
25
+
26
+    # read content of each mandatory file from debian_skel
27
+    #
28
+    debian_skel=$(ini 1value dist:debstuff)
29
+    test -n "$debian_skel" || die "dist:debstuff not specified"
30
+    test -d "$debian_skel" || die "debian directory template found: $debian_skel"
31
+    mkdir -p debian/source
32
+    find "$debian_skel" -type f \
33
+      | while read -r dfsrc; do
34
+            dftgt="debian/${dfsrc#"$debian_skel"}"
35
+            mkdir -p "$(dirname "$dftgt")"
36
+            build__file "$dfsrc" "$dftgt" debstuff
37
+        done
38
+    build__recordr debian
39
+}

+ 127
- 0
utils/mkit/plugins/pystuff Ver arquivo

@@ -0,0 +1,127 @@
1
+#!/bin/bash
2
+# MKit - simple install helper
3
+# See LICENSE file for copyright and license details.
4
+
5
+mkit_import build
6
+mkit_import target
7
+
8
+pystuff__main() {
9
+    #
10
+    # Build setup.py and mkvenv.sh
11
+    #
12
+    local ModName           # Python module name
13
+    local ProjVersion       # MKit project version
14
+    local Skel              # setup.py skeleton
15
+    local MkvenvDir         # venv maker: target venv directory
16
+    local MkvenvScript      # venv maker: script name
17
+    local MkvenvPost        # venv maker: post commands
18
+    target__run build || die
19
+    Skel="$(ini 1value "dist:pystuff")"
20
+    MkvenvDir=$(__pystuff__load_mkvenv_dir)
21
+    MkvenvScript=$(__pystuff__load_mkvenv_script)
22
+    MkvenvPost=$(__pystuff__load_mkvenv_post)
23
+    ProjVersion=$(build__cached semver)
24
+    ModName=$(ini 1value macros:__VDK_PYLIB_MODNAME__)
25
+    build__fact "pystuff/mkvenv_dir" <<<"$MkvenvDir"
26
+    build__fact "pystuff/mkvenv_script" <<<"$MkvenvScript"
27
+    test -n "$Skel" || die "dist:pystuff not specified"
28
+    test -f "$Skel" || die "setup.py template not found: $Skel"
29
+    __pystuff__make_setup_py
30
+    __pystuff__make_mkvenv
31
+}
32
+
33
+__pystuff__find_dist() {
34
+    local candidate
35
+    candidate=$(
36
+        find "dist" -maxdepth 1 -mindepth 1 -printf '%P\n' \
37
+          | grep -m1 -e "$ModName-.*tar.gz"
38
+    )
39
+    test -f "dist/$candidate" && echo "dist/$candidate" && return 0
40
+    #shellcheck disable=SC2016
41
+    warn "could not find dist package in: dist" \
42
+         " .. hint: looking for $ModName-*.tar.gz" \
43
+         ' .. hint: did you run `make pystuff` first?'
44
+    return 3
45
+}
46
+
47
+__pystuff__make_setup_py() {
48
+    build__file "$Skel" "setup.py" pystuff
49
+    build__recordr dist
50
+    build__recordr "$ModName.egg-info"
51
+    python3 -m build
52
+}
53
+
54
+__pystuff__load_mkvenv_dir() {
55
+    #
56
+    # Safely load directory for the venv
57
+    #
58
+    util__loadenv PYSTUFF__VENV_DIR && return 0
59
+    plugin__option_single "dir" && return 0
60
+    echo venv
61
+}
62
+
63
+__pystuff__load_mkvenv_script() {
64
+    #
65
+    # Safely load command for the venv
66
+    #
67
+    util__loadenv PYSTUFF__VENV_SCRIPT && return 0
68
+    plugin__option_single "mkvenv_script" && return 0
69
+    echo mkvenv.sh
70
+}
71
+
72
+__pystuff__load_mkvenv_post() {
73
+    #
74
+    # Safely load command for the venv
75
+    #
76
+    util__loadenv PYSTUFF__VENV_POST && return 0
77
+    plugin__option_multi "mkvenv_post" && return 0
78
+}
79
+
80
+__pystuff__q() {
81
+    #
82
+    # Quote command $@
83
+    #
84
+    printf '%q ' "$@";
85
+    echo
86
+}
87
+
88
+__pystuff__make_mkvenv() {
89
+    #
90
+    # Create the mkvenv.sh script
91
+    #
92
+    local dist
93
+    local req
94
+    dist=$(__pystuff__find_dist) \
95
+     || die "could not find dist"
96
+    build__record "$MkvenvScript"
97
+    {
98
+        echo "#!/bin/sh -e"
99
+        echo "#"
100
+        echo "# created by MKit $MKIT_VERSION pystuff for $ModName-$ProjVersion"
101
+        echo "#"
102
+        echo ""
103
+        __pystuff__q python3 -m venv "$MkvenvDir"
104
+        __pystuff__q . "$MkvenvDir/bin/activate"
105
+        python3 "setup.py" --requires \
106
+          | grep . \
107
+          | while read -r req; do
108
+                __pystuff__q python3 -m pip install --force-reinstall "$req"
109
+            done
110
+        __pystuff__q python3 -m pip install --force-reinstall "$dist"
111
+        echo ""
112
+        echo "echo >&2 'Virtual environment should be ready. run following command:'"
113
+        echo "echo >&2 ''"
114
+        echo "echo >&2 '    source venv/bin/activate'"
115
+        echo "echo >&2 ''"
116
+        echo "echo >&2 'to activate it.'"
117
+        test -n "$MkvenvPost" && {
118
+            echo
119
+            echo "#"
120
+            echo "# user POST action ([options:pystuff:mkvenv_post]"
121
+            echo "#"
122
+            echo "$MkvenvPost"
123
+        }
124
+    } | sed 's/ *$//' >"$MkvenvScript"
125
+    bash -n "$MkvenvScript" || die "script has syntax errors"
126
+    chmod +x "$MkvenvScript"
127
+}

+ 57
- 0
utils/mkit/plugins/rpmstuff Ver arquivo

@@ -0,0 +1,57 @@
1
+#!/bin/bash
2
+# MKit - simple install helper
3
+# See LICENSE file for copyright and license details.
4
+
5
+mkit_import build
6
+mkit_import target
7
+
8
+rpmstuff__main() {
9
+    #
10
+    # Build specfile
11
+    #
12
+    local specname=$MKIT_PROJ_PKGNAME.spec      # .spec filename
13
+    local specsrc                               # source of specfile
14
+    target__run dist || die
15
+    specsrc="$(ini 1value "dist:rpmstuff")"
16
+    test -n "$specsrc" || die "dist:rpmstuff not specified"
17
+    test -f "$specsrc" || die "specfile template not found: $specsrc"
18
+    __rpmstuff__maybe_prefix_v0 "$specsrc"
19
+    build__file "$specsrc" "$specname" rpmstuff
20
+}
21
+
22
+__rpmstuff__maybe_prefix_v0() {
23
+    #
24
+    # Avoid "normalizes to zero" error
25
+    #
26
+    # Scripting on Fedora 38 contains new check, where the version
27
+    # is not allowed to be 0.0.0.
28
+    #
29
+    # We will disable this check for projects where our reported
30
+    # version is indeed 0.0.0, but only if it's also a devel version,
31
+    # in which case it has build data.
32
+    #
33
+    local file=$1
34
+    local tmp="$1.__rpmstuff__maybe_prefix_v0.tmp"
35
+    local version
36
+    version=$(build__cached semver)
37
+    case $version in
38
+        0.0.0+*)
39
+            warn "disabling zero-version rpmbuild check" \
40
+                 " .. hint: We're doing this only because we trust" \
41
+                 " .. hint: that this is an early devel version." \
42
+                 " .. hint: Make sure to bump at least to 0.0.1" \
43
+                 " .. hint: before release."
44
+            ;;
45
+        *)
46
+            return 0
47
+            ;;
48
+    esac
49
+    cp -ar "$file" "$tmp"
50
+    {
51
+        echo "%define _python_dist_allow_version_zero 1"
52
+        echo "# ^^ auto-added by mkit rpmstuff for early devel version"
53
+        echo ""
54
+        cat "$file"
55
+    } > "$tmp"
56
+    mv "$tmp" "$file"
57
+}

+ 175
- 15
utils/mkit/stub Ver arquivo

@@ -6,7 +6,7 @@ init_core() {
6 6
     #
7 7
     # Load core modules (or die)
8 8
     #
9
-    #shellcheck disable=SC1090
9
+    #shellcheck disable=SC1090,SC1091
10 10
     . "$MKIT_DIR/include/mkit.sh" \
11 11
      && . "$MKIT_DIR/include/vars.sh" \
12 12
      && return 0
@@ -63,6 +63,153 @@ deploy() {
63 63
             else
64 64
                 echo "(Nothing to say about this project.)"
65 65
             fi
66
+            echo ""
67
+            echo "See MAINTAINERS.md for instructions how to build and"
68
+            echo "maintain this MKit project."
69
+            ;;
70
+
71
+        MAINTAINERS.md)
72
+            local heading="$any_name - maintainer instructions"
73
+            echo "${heading^^}"
74
+            tr -c '=\n' '=' <<<"$heading"
75
+            echo ''
76
+            echo "<!--"
77
+            echo "    Note: This file has been auto-generated by MKit"
78
+            echo "    v$MKIT_VERSION when initializing the project."
79
+            echo "-->"
80
+            echo ''
81
+            echo "This project is using MKit for packaging and versioning"
82
+            echo "meta-data; this file describes how to do basic maintenance"
83
+            echo "operations."
84
+            echo ''
85
+            echo "MKit uses GNU Make as an interface, so all operations are"
86
+            echo "'make' targets and GNU Make takes care of the dependencies"
87
+            echo "between them.  For example, to install the project it's enough"
88
+            echo "to call 'make install'; GNU Make will call 'make built'"
89
+            echo "automatically if needed."
90
+            echo ""
91
+            echo ""
92
+            echo "Variables"
93
+            echo "---------"
94
+            echo ""
95
+            echo " *  \`DESTDIR\` - root of the installation destination"
96
+            echo " *  \`PREFIX\` - deployment prefix (often \`/usr\` or"
97
+            echo "    \`/usr/local\`)"
98
+            echo " *  \`MKIT_DRY\` - set to \`true\` to prevent MKit from making"
99
+            echo "    any changes"
100
+            echo " *  \`MKIT_DEBUG\` - set to \`true\` to have MKit display"
101
+            echo "    (lots of) debugging information"
102
+            echo ""
103
+            echo "See *utils/mkit/include/vars.sh* for full list oof supported"
104
+            echo "variables. (You can use \`sfdoc utils/mkit/include/vars.sh\`"
105
+            echo "if you have sfdoc installed.)"
106
+            echo ""
107
+            echo ""
108
+            echo "Building and installation"
109
+            echo "-------------------------"
110
+            echo ""
111
+            echo "These operations are allowed on any branch.  Assets built from"
112
+            echo "development branches will be marked using distinctive version"
113
+            echo "number containing timestamp, branch name and commit short-hash."
114
+            echo ""
115
+            echo " *  \`make\` - same as \`make build\`"
116
+            echo " *  \`make build\` - build project files"
117
+            echo " *  \`make install\` - install project to \`\$DESTDIR\`"
118
+            echo " *  \`make uninstall\` - uninstall project from \`\$DESTDIR\`"
119
+            echo " *  \`make rpmstuff\` - create RPM SPEC file and a source"
120
+            echo "    tarball"
121
+            echo " *  \`make debstuff\` - create 'debian' directory and a source"
122
+            echo "    tarball"
123
+            echo " *  \`make clean\` - remove any previously built assets".
124
+            echo ""
125
+            echo ""
126
+            echo "Versioning"
127
+            echo "----------"
128
+            echo ""
129
+            echo "These operations are only allowed on *release source branch*:"
130
+            echo ""
131
+            echo " *  \`make vbump\` - bump version and create bump commit"
132
+            echo " *  \`make release\` - create release tag"
133
+            echo ""
134
+            echo ""
135
+            echo "Recommended workflow"
136
+            echo "--------------------"
137
+            echo ""
138
+            echo ""
139
+            echo "### Development and testing ###"
140
+            echo ""
141
+            echo "Any branch can be used to create any kind of asset.  Assets"
142
+            echo "built from development branch will be marked using distinctive"
143
+            echo "version number in all MKit macros."
144
+            echo ""
145
+            echo "Development is done in branches as needed, but one branch"
146
+            echo "(often called *master*, *main* or *trunk*) is designated as"
147
+            echo "**release source** branch.  Only maintainer can push into this"
148
+            echo "branch and this is where maintainer creates the next release."
149
+            echo ""
150
+            echo ""
151
+            echo "### Versioning and releases ###"
152
+            echo ""
153
+            echo "After all development branches are merged and project is"
154
+            echo "considered ready for release, maintainer will create new"
155
+            echo "release.  This consists of three steps (only two of them are"
156
+            echo "assisted by MKit):"
157
+            echo ""
158
+            echo " 1. \`make vbump\` - will auto-edit [project:version] in"
159
+            echo "     mkit.ini and start creating **bump commit**."
160
+            echo ""
161
+            echo "    **Bump commit** is a special commit which consists of:"
162
+            echo ""
163
+            echo "     *  Bump of version in mkit.ini"
164
+            echo "     *  commit message providing rudimentary human"
165
+            echo "        readable summary of changes."
166
+            echo ""
167
+            echo "    \`make vbump\` target will cause bump of the *last*"
168
+            echo "    version fragment (Z, or PATCH in SemVer).  To bump other"
169
+            echo "    fragments, use \`make vbump_y\` or \`make vbump_x\` (MINOR"
170
+            echo "    or MAJOR in SemVer, respectively)."
171
+            echo ""
172
+            echo "    MKit will create a template of the message by scanning"
173
+            echo "    all commits since last release and invoke 'git commit' so"
174
+            echo "    that maintainer is presented with a window editing the"
175
+            echo "    commit message."
176
+            echo ""
177
+            echo "    Maintainer is responsible for finishing the commit message"
178
+            echo "    by:"
179
+            echo ""
180
+            echo "     *  Re-wording and merging the items as needed."
181
+            echo "     *  Adding any details, if needed."
182
+            echo "     *  Removing short hashes and file lists added by MKit"
183
+            echo "        (these are added only to make editing easier)."
184
+            echo ""
185
+            echo "    Once maintainer is happy with *bump commit* message,"
186
+            echo "    they can exit the editor and move on to next step."
187
+            echo ""
188
+            echo " 2. \`make release\` - will check if latest commit on current"
189
+            echo "    branch is a *bump commit*, and if yes, will create"
190
+            echo "    a corresponding **release tag**."
191
+            echo ""
192
+            echo "    **Release tags** are annotated tags (see *git-tag(1)*)"
193
+            echo "    formed by version number prefixed by letter \`v\` and are"
194
+            echo "    used by MKit to calculate version number when building"
195
+            echo "    assets.  Release tags contain the *bump commit* message"
196
+            echo "    as the annotation."
197
+            echo ""
198
+            echo "    If a *release destination* branch is specified as"
199
+            echo "    [project:reldst] in mkit.ini, MKit will also update that"
200
+            echo "    branch so that it points to the same commmit as the"
201
+            echo "    newly created release tag."
202
+            echo ""
203
+            echo " 3. Maintainer will push branches and commits to any shared"
204
+            echo "    remotes as needed."
205
+            echo ""
206
+            echo "    Warning: This is your last chance to fix any mistakes."
207
+            echo "    git tags are designed to be permanent, \"official\","
208
+            echo "    write-only objects and they will typically quickly spread"
209
+            echo "    to other clones.  *Don't count on getting rid of release"
210
+            echo "    tag*."
211
+            echo ""
212
+
66 213
             ;;
67 214
 
68 215
         */mkit.ini|mkit.ini)
@@ -90,6 +237,7 @@ deploy() {
90 237
                 echo "[dist]"
91 238
                 {
92 239
                     $MkLicense  && echo "tarball = LICENSE.md"
240
+                    $MkReadme   && echo "tarball = MAINTAINERS.md"
93 241
                     $MkMakefile && echo "tarball = Makefile"
94 242
                     $MkReadme   && echo "tarball = README.md"
95 243
                     echo "tarball = mkit.ini"
@@ -115,6 +263,9 @@ deploy() {
115 263
                 echo "[macros]"
116 264
                 {
117 265
                     echo "__${PackageName^^}_FOO__ = Barr.."
266
+                    echo "    __PROJECT_DESC_MAIN__ = MKIT_STUB_DESCRIPTION - replace this with your project"
267
+                    echo "    __PROJECT_DESC_MAIN__ = MKIT_STUB_DESCRIPTION .. description; this will appear"
268
+                    echo "    __PROJECT_DESC_MAIN__ = MKIT_STUB_DESCRIPTION .. in places like rpm -i"
118 269
                 } | reformat_section
119 270
                 echo ""
120 271
                 echo "[modes]"
@@ -135,18 +286,19 @@ deploy() {
135 286
             ;;
136 287
 
137 288
         packaging/template.spec)
138
-            echo 'Name:       __MKIT_PROJ_PKGNAME__'
139
-            echo 'Version:    __MKIT_PROJ_VERSION__'
140
-            echo 'Release:    1%{?dist}'
141
-            echo 'Summary:    __MKIT_PROJ_NAME__ - __MKIT_PROJ_TAGLINE__'
289
+            echo 'Name:           __MKIT_PROJ_PKGNAME__'
290
+            echo 'Version:        __MKIT_PROJ_VERSION__'
291
+            echo 'Release:        1%{?dist}'
292
+            echo 'Summary:        __MKIT_PROJ_NAME__ - __MKIT_PROJ_TAGLINE__'
142 293
             test -n "$VcsBrowser" && echo 'URL:        __MKIT_PROJ_VCS_BROWSER__'
143
-            $MkLicense && echo "License:    $License"
144
-            echo 'Source0:    %{name}-%{version}.tar.gz'
145
-            echo 'BuildArch:  noarch'
294
+            $MkLicense && echo "License:        $License"
295
+            echo 'Source0:        %{name}-%{version}.tar.gz'
296
+            echo 'BuildArch:      noarch'
297
+            echo 'BuildRequires:  make'
146 298
             echo ''
147 299
             echo 'Requires: MKIT_STUB_REQUIRES'
148 300
             echo '%description'
149
-            echo 'MKIT_STUB_DESCRIPTION'
301
+            echo '__PROJECT_DESC_MAIN__'
150 302
             echo ''
151 303
             echo '%prep'
152 304
             echo '%setup -q'
@@ -163,6 +315,7 @@ deploy() {
163 315
             echo '%changelog'
164 316
             echo ''
165 317
             echo '# specfile built with MKit __MKIT_MKIT_VERSION__'
318
+            echo "#  .. based on stub from: $MKIT_VERSION"
166 319
             ;;
167 320
 
168 321
         packaging/debian/copyright)
@@ -178,14 +331,16 @@ deploy() {
178 331
             echo 'Standards-Version: 3.9.2'
179 332
             echo 'Build-Depends:'
180 333
             echo ' debhelper (>= 9),'
334
+            echo ' make,'
181 335
             echo ''
182 336
             echo 'Package: __MKIT_PROJ_PKGNAME__'
183 337
             echo 'Architecture: all'
184 338
             echo 'Depends: MKIT_STUB_REQUIRES'
185 339
             echo 'Description: __MKIT_PROJ_NAME__ - __MKIT_PROJ_TAGLINE__'
186
-            echo ' MKIT_STUB_DESCRIPTION'
340
+            echo ' __PROJECT_DESC_MAIN__'
187 341
             echo ''
188 342
             echo '# control file built with MKit __MKIT_MKIT_VERSION__'
343
+            echo "#  .. based on stub from: $MKIT_VERSION"
189 344
             ;;
190 345
 
191 346
         packaging/debian/changelog)
@@ -205,11 +360,11 @@ deploy() {
205 360
             echo ''
206 361
             echo '%:'
207 362
             echo ''
208
-            echo '	dh $@'
363
+            echo '	PREFIX=/usr dh $@'
209 364
             echo ''
210 365
             echo 'override_dh_auto_install:'
211 366
             echo ''
212
-            echo '	make install PREFIX=/usr DESTDIR=debian/tmp'
367
+            echo '	make install DESTDIR=debian/tmp'
213 368
             ;;
214 369
 
215 370
         packaging/debian/source/format)
@@ -430,11 +585,15 @@ deploy() {
430 585
             if $MkReadme; then
431 586
             echo ""
432 587
             echo ""
433
-            echo "README.md"
434
-            echo "---------"
588
+            echo "README.md and MAINTAINERS.md"
589
+            echo "----------------------------"
435 590
             echo ""
436 591
             echo "Each serious project needs a serious README.  Which is why"
437 592
             echo "*stub* has created a 'stub' of one for you."
593
+            echo ""
594
+            echo "Furthermore MAINTAINERS.md is created to help maintainers"
595
+            echo "do basic MKit operations such as building assets and making"
596
+            echo "releases."
438 597
             fi
439 598
 
440 599
             echo ""
@@ -479,7 +638,7 @@ usage() {
479 638
         echo "    -V VERSION    initial version (default: 0.0.0)"
480 639
         echo "    -a            enable autoclean ('make clean' after"
481 640
         echo "                  each 'make install')"
482
-        echo "    -g            make git commits before and adter"
641
+        echo "    -g            make git commits before and after"
483 642
         echo "                  (implies -y)"
484 643
         echo "    -y            don't ask, just do it"
485 644
         echo "    -R            skip creating README.md"
@@ -699,6 +858,7 @@ main() {
699 858
     deploy src/"$PackageName".skel
700 859
     $MkMakefile     && deploy Makefile
701 860
     $MkReadme       && deploy README.md
861
+    $MkReadme       && deploy MAINTAINERS.md
702 862
     $MkLicense      && deploy LICENSE.md
703 863
     $AutoClean      && deploy .mkit/autoclean
704 864
     $MkPackaging    && deploy_packaging