22 Ревизии

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

+ 5
- 0
.gitlab-ci.yml Целия файл

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

+ 4
- 4
mkit.ini Целия файл

1
 [project]
1
 [project]
2
-    version     = 0.0.15
2
+    version     = 0.0.21
3
     name        = imapdomo
3
     name        = imapdomo
4
     pkgname     = imapdomo
4
     pkgname     = imapdomo
5
     maintainer  = Alois Mahdal <netvor+imapdomo@vornet.cz>
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
     tagline     = imapfilter convenience wrapper
7
     tagline     = imapfilter convenience wrapper
8
-    relsrc      = master
8
+    relsrc      = main
9
     reldst      = latest
9
     reldst      = latest
10
 
10
 
11
 [dist]
11
 [dist]
42
     share   = src/imaprules.lua
42
     share   = src/imaprules.lua
43
     share   = src/main.lua
43
     share   = src/main.lua
44
 
44
 
45
-#mkit version=0.0.40
45
+#mkit version=0.1.3

+ 1
- 0
packaging/debian/control Целия файл

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

+ 1
- 0
packaging/template.spec Целия файл

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

+ 16
- 2
src/imapdomo.lua Целия файл

57
 -- mail getters                                                             --
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
     -- Get queue from *mbox* from *acct* or nil (no messages)
62
     -- Get queue from *mbox* from *acct* or nil (no messages)
63
     --
63
     --
64
     -- If mbox is not specified, "FILTER_QUEUE" is used
64
     -- If mbox is not specified, "FILTER_QUEUE" is used
65
     --
65
     --
66
     mbox = mbox or "FILTER_QUEUE"
66
     mbox = mbox or "FILTER_QUEUE"
67
+    size = size or 512
67
     local exist = acct[mbox]:check_status()
68
     local exist = acct[mbox]:check_status()
68
     if exist > 0 then
69
     if exist > 0 then
69
-        return acct[mbox]:select_all()
70
+        return pkg.head(size, acct[mbox]:select_all())
70
     end
71
     end
71
     return nil
72
     return nil
72
 end
73
 end
203
     return result
204
     return result
204
 end
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
 pkg.filter_part_like = function(query, seq)
220
 pkg.filter_part_like = function(query, seq)
207
     --
221
     --
208
     -- Run MIME part query on *seq* sequence of messages
222
     -- Run MIME part query on *seq* sequence of messages

+ 12
- 1
src/imapdomo.skel Целия файл

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

+ 226
- 145
utils/mkit/include/build.sh Целия файл

4
 
4
 
5
 mkit_import ini
5
 mkit_import ini
6
 mkit_import facts
6
 mkit_import facts
7
+mkit_import plugin
8
+mkit_import util
7
 
9
 
8
 
10
 
9
-__build1() {
11
+build__file() {
10
     #
12
     #
11
     # Process one skeleton $1 of type $3 (or guessed) to path $2
13
     # Process one skeleton $1 of type $3 (or guessed) to path $2
12
     #
14
     #
14
     local dstpath=$2    # destination meaty animal path
16
     local dstpath=$2    # destination meaty animal path
15
     local ftype=$3      # file/builder type
17
     local ftype=$3      # file/builder type
16
     test -n "$dstpath"  || dstpath=${srcpath%.skel}
18
     test -n "$dstpath"  || dstpath=${srcpath%.skel}
17
-    test -n "$ftype"    || ftype=$(__guess_ftype "$dstpath")
19
+    test -n "$ftype"    || ftype=MKIT_COMMON
18
     debug_var srcpath dstpath ftype
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
     # Build a file of type $1; fom stdin to stdout
27
     # Build a file of type $1; fom stdin to stdout
26
     #
28
     #
27
     local ftype=$1      # file/builder type
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
     # If macro value has multiple lines, repeat original line with
71
     # If macro value has multiple lines, repeat original line with
41
     # different substitution.
72
     # different substitution.
44
     # line `see: "__FOO__"` will expand to two lines: `see: "foo"`
75
     # line `see: "__FOO__"` will expand to two lines: `see: "foo"`
45
     # and `see: "bar"`.
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
         fi
90
         fi
60
-        line=$xline
91
+        raw_line=$expanded_line
61
     done
92
     done
62
-    echo "$xline"
93
+    echo "$expanded_line"
63
     return 1
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
     local line          # each line on stdin
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
     done
112
     done
90
-    debug_var MacroMap
91
     while IFS= read -r line; do
113
     while IFS= read -r line; do
92
-        __expand_line "$line"
114
+        __build__macro_expand_line "$line"
93
     done
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
     # Cached value $1 of function $1()
120
     # Cached value $1 of function $1()
119
     #
121
     #
126
     # arguments).
128
     # arguments).
127
     #
129
     #
128
     local name=$1
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
     "$name" | __local_putb "$name"
133
     "$name" | __local_putb "$name"
131
     __local_get "$name"
134
     __local_get "$name"
132
 }
135
 }
136
     # Make file $1 in $MKIT_LOCAL from stdin and mark as built
139
     # Make file $1 in $MKIT_LOCAL from stdin and mark as built
137
     #
140
     #
138
     local fpath=$1
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
 __local_put() {
145
 __local_put() {
156
     cat "$fpath" 2>/dev/null
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
     # Record file $1 for deletion on `clean`
181
     # Record file $1 for deletion on `clean`
162
     #
182
     #
165
     echo "1:$file" >> "$MKIT_LOCAL/built.lst"
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
     local macro
201
     local macro
173
     local section
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
         done
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
 build() {
325
 build() {
206
     local srcpath   # each source path
329
     local srcpath   # each source path
207
     find . -type f -name '*.skel' \
330
     find . -type f -name '*.skel' \
208
      | while read -r srcpath; do
331
      | while read -r srcpath; do
209
-           __build1 "$srcpath"
332
+           build__file "$srcpath"
210
        done
333
        done
211
 }
334
 }
212
 
335
 
242
     true
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
 dist() {
368
 dist() {
278
     #
369
     #
279
     # Create distributable tarball
370
     # Create distributable tarball
287
     version=$(semver)
378
     version=$(semver)
288
     dirname=$MKIT_PROJ_PKGNAME-$version
379
     dirname=$MKIT_PROJ_PKGNAME-$version
289
     git_lasthash=$(git_lasthash)
380
     git_lasthash=$(git_lasthash)
381
+    debug_var version dirname git_lasthash
290
     mkdir -p "$dirname"
382
     mkdir -p "$dirname"
291
     ini values "dist:tarball" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname"
383
     ini values "dist:tarball" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname"
292
     mkdir -p "$dirname/.mkit"
384
     mkdir -p "$dirname/.mkit"
293
     echo -n "$version" > "$dirname/.mkit/semver"
385
     echo -n "$version" > "$dirname/.mkit/semver"
294
     echo -n "$git_lasthash" > "$dirname/.mkit/git_lasthash"
386
     echo -n "$git_lasthash" > "$dirname/.mkit/git_lasthash"
387
+    cp -r "$MKIT_LOCAL/data" "$dirname/.mkit/"
295
     tar -cf "$dirname.tar" "$dirname"
388
     tar -cf "$dirname.tar" "$dirname"
296
     gzip -f "$dirname.tar"      # see above FIXME
389
     gzip -f "$dirname.tar"      # see above FIXME
297
-    __rec_built "$dirname.tar.gz"
390
+    build__record "$dirname.tar.gz"
298
     rm -rf "$dirname"
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 Целия файл

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

+ 51
- 23
utils/mkit/include/facts.sh Целия файл

9
     # Get git bool (ie. exit status counts) $1
9
     # Get git bool (ie. exit status counts) $1
10
     #
10
     #
11
     local bool_name=$1      # name of boolean to get
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
     case "$bool_name" in
16
     case "$bool_name" in
14
         dirty_files)
17
         dirty_files)
15
             git diff-files | grep -qm 1 .
18
             git diff-files | grep -qm 1 .
20
         dirty)
23
         dirty)
21
             git_bool dirty_files || git_bool dirty_index
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
             warn "unknown git bool asked: $bool_name"
34
             warn "unknown git bool asked: $bool_name"
25
             return 2
35
             return 2
51
         reldiff)
61
         reldiff)
52
             git log --oneline "$(git_fact latest_tag)..HEAD" --name-only
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
         latest_sha)
67
         latest_sha)
55
             git log -1 --pretty=format:%h HEAD
68
             git log -1 --pretty=format:%h HEAD
56
             ;;
69
             ;;
124
       | cut -d' ' -f2-
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
 semver() {
176
 semver() {
128
     #
177
     #
129
     # Build proper SemVer version string
178
     # Build proper SemVer version string
162
     local xyz           # base version string
211
     local xyz           # base version string
163
     local prerl         # pre-release keyword (from mkit.ini, eg. 'beta')
212
     local prerl         # pre-release keyword (from mkit.ini, eg. 'beta')
164
     local latest_tag    # latest git tag
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
     local stamp         # timestamp or nothing (see $MKIT_TSTAMP)
214
     local stamp         # timestamp or nothing (see $MKIT_TSTAMP)
170
     local suffix        # version suffix
215
     local suffix        # version suffix
171
     prerl=$(ini 1value project:prerl)
216
     prerl=$(ini 1value project:prerl)
189
         *)  warn "bad form of last tag, using base version from mkit.ini: tag is '$latest_tag'"
234
         *)  warn "bad form of last tag, using base version from mkit.ini: tag is '$latest_tag'"
190
             xyz=$(ini 1value project:version) ;;
235
             xyz=$(ini 1value project:version) ;;
191
     esac
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
     test -n "$prerl" && suffix="-$prerl$suffix"
238
     test -n "$prerl" && suffix="-$prerl$suffix"
211
     echo "$xyz$suffix"
239
     echo "$xyz$suffix"
212
 }
240
 }

+ 10
- 32
utils/mkit/include/ini.sh Целия файл

35
         ((Depth++))
35
         ((Depth++))
36
         debug_var line_todo
36
         debug_var line_todo
37
         test "$Depth" -le "$MaxDepth" || {
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
             return 3
41
             return 3
42
         }
42
         }
43
         line_done=$(__ini_expandln_once "$line_todo")
43
         line_done=$(__ini_expandln_once "$line_todo")
64
         ipath=${ref#[}; ipath=${ipath%]}
64
         ipath=${ref#[}; ipath=${ipath%]}
65
         value=$(ini 1value "$ipath")
65
         value=$(ini 1value "$ipath")
66
         debug_var line ref ipath value
66
         debug_var line ref ipath value
67
-        line=$(sed "s|\\[$ipath\\]|$value|" <<<"$line")
67
+        line=${line//"[$ipath]"/"$value"}
68
     done
68
     done
69
     echo "$line"
69
     echo "$line"
70
 }
70
 }
78
 
78
 
79
 __ini_grepkey() {
79
 __ini_grepkey() {
80
     #
80
     #
81
-    # Read key from a section
81
+    # Read key from a normalized `key=value` section
82
     #
82
     #
83
     local wnt=$1    # wanted key
83
     local wnt=$1    # wanted key
84
     grep '.' \
84
     grep '.' \
85
-      | sed -e 's/ *= */=/; s/ +$//; s/^//;' \
86
       | grep -e "^$wnt=" \
85
       | grep -e "^$wnt=" \
87
       | cut -d= -f2- \
86
       | cut -d= -f2- \
88
       | __ini_maybe_expand
87
       | __ini_maybe_expand
99
     #
98
     #
100
     local wnt=$1                    # wanted path
99
     local wnt=$1                    # wanted path
101
     local wntkey=${wnt##*:}         # ^^ key part
100
     local wntkey=${wnt##*:}         # ^^ key part
102
-    local wntsec=${wnt%:$wntkey}    # ^^ section part
101
+    local wntsec=${wnt%:"$wntkey"}  # ^^ section part
103
     local override                  # ENV override (only ENV section)
102
     local override                  # ENV override (only ENV section)
104
     if test "$wntsec" = 'ENV'; then
103
     if test "$wntsec" = 'ENV'; then
105
         override=${!wntkey}
104
         override=${!wntkey}
120
     grep '.' \
119
     grep '.' \
121
       | while read -r line; do
120
       | while read -r line; do
122
             case "$line" in
121
             case "$line" in
123
-                \[$wnt\]) ok=true;  continue ;;
124
-                \[*\])    ok=false; continue ;;
122
+                \["$wnt"\]) ok=true;  continue ;;
123
+                \[*\])      ok=false; continue ;;
125
             esac
124
             esac
126
             $ok || continue
125
             $ok || continue
127
             printf -- '%s\n' "$line"
126
             printf -- '%s\n' "$line"
128
         done \
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
 __ini_lskeys() {
132
 __ini_lskeys() {
191
     esac
191
     esac
192
     __ini_body | $fn "$arg" | $limit
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 Целия файл

2
 # MKit - simple install helper
2
 # MKit - simple install helper
3
 # See LICENSE file for copyright and license details.
3
 # See LICENSE file for copyright and license details.
4
 
4
 
5
+_MKIT_IMPORTED=""
6
+
5
 die() {
7
 die() {
6
     #
8
     #
7
     # Exit with message and non-zero exit status
9
     # Exit with message and non-zero exit status
18
     #
20
     #
19
     local modname=$1
21
     local modname=$1
20
     local modpath
22
     local modpath
23
+    test "$modname" == mkit && die "cannot re-import core module: $modname"
24
+    __mkit__is_imported "$modname" && return 0
21
     modpath="$MKIT_DIR/include/$modname.sh"
25
     modpath="$MKIT_DIR/include/$modname.sh"
22
     test -f "$modpath" || die "no such module: $modpath"
26
     test -f "$modpath" || die "no such module: $modpath"
23
     bash -n "$modpath" || die "bad syntax: $modpath"
27
     bash -n "$modpath" || die "bad syntax: $modpath"
24
-    #shellcheck disable=SC1090
28
+    #shellcheck disable=SC1090,SC1091
25
     . "$modpath" || die "failed to import: $modname"
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
 debug() {
44
 debug() {
56
     #
45
     #
57
     # Print debug message
46
     # Print debug message
80
     #
69
     #
81
     # True if version $1 matches our version
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
     local their_ver=$1      # their version
80
     local their_ver=$1      # their version
87
     local our_x             # our X
81
     local our_x             # our X
88
     local our_y             # our Y
82
     local our_y             # our Y
83
+    local our_z             # our z
89
     local their_x           # their X
84
     local their_x           # their X
90
     local their_y           # their Y
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
 __chkiniversion() {
150
 __chkiniversion() {
116
         {
160
         {
117
             head -3 "$MKIT_INI"
161
             head -3 "$MKIT_INI"
118
             tail -3 "$MKIT_INI"
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
     test -n "$ver_line" \
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
     their_ver="$(tr -d '[:blank:]v' <<<"${ver_line##*=}")"
167
     their_ver="$(tr -d '[:blank:]v' <<<"${ver_line##*=}")"
124
     __compver "$their_ver" \
168
     __compver "$their_ver" \
125
      || die "bad mkit.ini version: $their_ver does not match $MKIT_VERSION"
169
      || die "bad mkit.ini version: $their_ver does not match $MKIT_VERSION"
134
     $MKIT_DRY && MKIT_DEBUG=true
178
     $MKIT_DRY && MKIT_DEBUG=true
135
     #shellcheck disable=SC2034
179
     #shellcheck disable=SC2034
136
     MKIT_PROJ_PKGNAME=$(ini 1value "project:pkgname")
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
     test -f "$MKIT_INI" || die "cannot find mkit.ini: $MKIT_INI"
182
     test -f "$MKIT_INI" || die "cannot find mkit.ini: $MKIT_INI"
138
     __chkiniversion
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
 warn() {
186
 warn() {
158
     #
187
     #
159
     # Print warning message
188
     # Print warning message
160
     #
189
     #
161
-    echo "$@" >&2
190
+    printf '%s\n' "$@" >&2
162
 }
191
 }

+ 195
- 0
utils/mkit/include/plugin.sh Целия файл

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 Целия файл

5
 mkit_import ini
5
 mkit_import ini
6
 mkit_import facts
6
 mkit_import facts
7
 
7
 
8
-__bump_version() {
8
+__release__bump_version() {
9
     #
9
     #
10
     # Bump version on stdin by level $1 (x, y or z)
10
     # Bump version on stdin by level $1 (x, y or z)
11
     #
11
     #
30
     echo "$new"
30
     echo "$new"
31
 }
31
 }
32
 
32
 
33
-__relck() {
33
+__release__check() {
34
     #
34
     #
35
     # Die if blocking condition $1 is detected
35
     # Die if blocking condition $1 is detected
36
     #
36
     #
63
              || die "last change must be version bump in mkit.ini"
63
              || die "last change must be version bump in mkit.ini"
64
             ;;
64
             ;;
65
         no_wip)
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
              && die "WIP commit since $(git_fact latest_tag)"
71
              && die "WIP commit since $(git_fact latest_tag)"
69
             ;;
72
             ;;
70
         ini_version)
73
         ini_version)
71
-            oracle=$(git_fact latest_version | __bump_version "$rlevel")
74
+            oracle=$(git_fact latest_version | __release__bump_version "$rlevel")
72
             ini 1value project:version  \
75
             ini 1value project:version  \
73
               | grep -qFxe "$oracle" \
76
               | grep -qFxe "$oracle" \
74
              || die "new version not in mkit.ini: $oracle"
77
              || die "new version not in mkit.ini: $oracle"
94
     local relsrc        # release source branch (if any)
97
     local relsrc        # release source branch (if any)
95
     local reldst        # release destination branch (if any)
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
     set -e
109
     set -e
107
     debug_var newtag
110
     debug_var newtag
108
     $MKIT_DRY && return
111
     $MKIT_DRY && return
121
     # Generate message for annotated tag
124
     # Generate message for annotated tag
122
     #
125
     #
123
     # The last commit before issuing a release must be "Bump version" commit
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
     # commit contains changelog, this function just uses the message body.
128
     # commit contains changelog, this function just uses the message body.
126
     #
129
     #
127
     # The sort message (first line) is replaced with a nicer one (with project
130
     # The sort message (first line) is replaced with a nicer one (with project
133
       | tail -n +3
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
     # Do version bump at level $1
163
     # Do version bump at level $1
139
     #
164
     #
143
     local rlevel=$1     # bump level (x, y or z)
168
     local rlevel=$1     # bump level (x, y or z)
144
     local nextver       # version after the bump
169
     local nextver       # version after the bump
145
     local cache         # cache for the message
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
     debug_var nextver
175
     debug_var nextver
151
     $MKIT_DRY && return
176
     $MKIT_DRY && return
152
-    update_version "$nextver" mkit.ini \
177
+    __release__update_ini "$nextver" mkit.ini \
153
       || die "failed to update version in mkit.ini"
178
       || die "failed to update version in mkit.ini"
154
     git add mkit.ini \
179
     git add mkit.ini \
155
       || die "failed to add mkit.ini to the index"
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
     git commit -e -F "$cache"   # note: reading from stdin will break vim
183
     git commit -e -F "$cache"   # note: reading from stdin will break vim
159
     rm "$cache"
184
     rm "$cache"
160
 }
185
 }
161
 
186
 
162
-_vbump_gitmsg() {
187
+__release__vbump_gitmsg() {
163
     #
188
     #
164
     # Compose git message template for 'Bump version' commit
189
     # Compose git message template for 'Bump version' commit
165
     #
190
     #
208
     #
233
     #
209
     # Perform version bump on Z level
234
     # Perform version bump on Z level
210
     #
235
     #
211
-    __vbump z
236
+    __release__vbump z
212
 }
237
 }
213
 
238
 
214
 vbump_x() {
239
 vbump_x() {
215
     #
240
     #
216
     # Perform version bump on X level
241
     # Perform version bump on X level
217
     #
242
     #
218
-    __vbump x
243
+    __release__vbump x
219
 }
244
 }
220
 
245
 
221
 vbump_y() {
246
 vbump_y() {
222
     #
247
     #
223
     # Perform version bump on Y level
248
     # Perform version bump on Y level
224
     #
249
     #
225
-    __vbump y
250
+    __release__vbump y
226
 }
251
 }
227
 
252
 
228
 vbump_z() {
253
 vbump_z() {
229
     #
254
     #
230
     # Perform version bump on Z level
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 Целия файл

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 Целия файл

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 Целия файл

4
 # See LICENSE file for copyright and license details.
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
 # Bump size (for vbump_? and release_?)
22
 # Bump size (for vbump_? and release_?)
9
 #
23
 #
26
 #
40
 #
27
 MKIT_DRY=${MKIT_DRY:-false}
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
 # Path to mkit.ini
54
 # Path to mkit.ini
31
 #
55
 #
48
 #
72
 #
49
 MKIT_LOCAL=${MKIT_LOCAL:-.mkit}
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
 # Package name
84
 # Package name
53
 #
85
 #
83
 #
115
 #
84
 MKIT_TSTAMP=${MKIT_TSTAMP:-ctime}
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
 # This MKit version
158
 # This MKit version
88
 #
159
 #
89
-MKIT_VERSION=0.0.40
160
+MKIT_VERSION=0.1.3

+ 0
- 34
utils/mkit/make Целия файл

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 Целия файл

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 Целия файл

3
 
3
 
4
 export MKIT_DIR
4
 export MKIT_DIR
5
 
5
 
6
+plugins := $(shell "$(MKIT_DIR)"/mkit --list-plugins)
7
+
8
+
6
 all: build
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
 install: all
35
 install: all
27
-	@"$(MKIT_DIR)"/make install
36
+	@"$(MKIT_DIR)"/mkit install
28
 
37
 
29
 release:
38
 release:
30
-	@"$(MKIT_DIR)"/make release
39
+	@"$(MKIT_DIR)"/mkit release
31
 
40
 
32
 release_x:
41
 release_x:
33
-	@"$(MKIT_DIR)"/make release_x
42
+	@"$(MKIT_DIR)"/mkit release_x
34
 
43
 
35
 release_y:
44
 release_y:
36
-	@"$(MKIT_DIR)"/make release_y
45
+	@"$(MKIT_DIR)"/mkit release_y
37
 
46
 
38
 release_z:
47
 release_z:
39
-	@"$(MKIT_DIR)"/make release_z
48
+	@"$(MKIT_DIR)"/mkit release_z
40
 
49
 
41
 uninstall:
50
 uninstall:
42
-	@"$(MKIT_DIR)"/make uninstall
51
+	@"$(MKIT_DIR)"/mkit uninstall
43
 
52
 
44
 vbump:
53
 vbump:
45
-	@"$(MKIT_DIR)"/make vbump
54
+	@"$(MKIT_DIR)"/mkit vbump
46
 
55
 
47
 vbump_x:
56
 vbump_x:
48
-	@"$(MKIT_DIR)"/make vbump_x
57
+	@"$(MKIT_DIR)"/mkit vbump_x
49
 
58
 
50
 vbump_y:
59
 vbump_y:
51
-	@"$(MKIT_DIR)"/make vbump_y
60
+	@"$(MKIT_DIR)"/mkit vbump_y
52
 
61
 
53
 vbump_z:
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 Целия файл

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 Целия файл

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 Целия файл

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 Целия файл

6
     #
6
     #
7
     # Load core modules (or die)
7
     # Load core modules (or die)
8
     #
8
     #
9
-    #shellcheck disable=SC1090
9
+    #shellcheck disable=SC1090,SC1091
10
     . "$MKIT_DIR/include/mkit.sh" \
10
     . "$MKIT_DIR/include/mkit.sh" \
11
      && . "$MKIT_DIR/include/vars.sh" \
11
      && . "$MKIT_DIR/include/vars.sh" \
12
      && return 0
12
      && return 0
63
             else
63
             else
64
                 echo "(Nothing to say about this project.)"
64
                 echo "(Nothing to say about this project.)"
65
             fi
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
         */mkit.ini|mkit.ini)
215
         */mkit.ini|mkit.ini)
90
                 echo "[dist]"
237
                 echo "[dist]"
91
                 {
238
                 {
92
                     $MkLicense  && echo "tarball = LICENSE.md"
239
                     $MkLicense  && echo "tarball = LICENSE.md"
240
+                    $MkReadme   && echo "tarball = MAINTAINERS.md"
93
                     $MkMakefile && echo "tarball = Makefile"
241
                     $MkMakefile && echo "tarball = Makefile"
94
                     $MkReadme   && echo "tarball = README.md"
242
                     $MkReadme   && echo "tarball = README.md"
95
                     echo "tarball = mkit.ini"
243
                     echo "tarball = mkit.ini"
115
                 echo "[macros]"
263
                 echo "[macros]"
116
                 {
264
                 {
117
                     echo "__${PackageName^^}_FOO__ = Barr.."
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
                 } | reformat_section
269
                 } | reformat_section
119
                 echo ""
270
                 echo ""
120
                 echo "[modes]"
271
                 echo "[modes]"
135
             ;;
286
             ;;
136
 
287
 
137
         packaging/template.spec)
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
             test -n "$VcsBrowser" && echo 'URL:        __MKIT_PROJ_VCS_BROWSER__'
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
             echo ''
298
             echo ''
147
             echo 'Requires: MKIT_STUB_REQUIRES'
299
             echo 'Requires: MKIT_STUB_REQUIRES'
148
             echo '%description'
300
             echo '%description'
149
-            echo 'MKIT_STUB_DESCRIPTION'
301
+            echo '__PROJECT_DESC_MAIN__'
150
             echo ''
302
             echo ''
151
             echo '%prep'
303
             echo '%prep'
152
             echo '%setup -q'
304
             echo '%setup -q'
163
             echo '%changelog'
315
             echo '%changelog'
164
             echo ''
316
             echo ''
165
             echo '# specfile built with MKit __MKIT_MKIT_VERSION__'
317
             echo '# specfile built with MKit __MKIT_MKIT_VERSION__'
318
+            echo "#  .. based on stub from: $MKIT_VERSION"
166
             ;;
319
             ;;
167
 
320
 
168
         packaging/debian/copyright)
321
         packaging/debian/copyright)
178
             echo 'Standards-Version: 3.9.2'
331
             echo 'Standards-Version: 3.9.2'
179
             echo 'Build-Depends:'
332
             echo 'Build-Depends:'
180
             echo ' debhelper (>= 9),'
333
             echo ' debhelper (>= 9),'
334
+            echo ' make,'
181
             echo ''
335
             echo ''
182
             echo 'Package: __MKIT_PROJ_PKGNAME__'
336
             echo 'Package: __MKIT_PROJ_PKGNAME__'
183
             echo 'Architecture: all'
337
             echo 'Architecture: all'
184
             echo 'Depends: MKIT_STUB_REQUIRES'
338
             echo 'Depends: MKIT_STUB_REQUIRES'
185
             echo 'Description: __MKIT_PROJ_NAME__ - __MKIT_PROJ_TAGLINE__'
339
             echo 'Description: __MKIT_PROJ_NAME__ - __MKIT_PROJ_TAGLINE__'
186
-            echo ' MKIT_STUB_DESCRIPTION'
340
+            echo ' __PROJECT_DESC_MAIN__'
187
             echo ''
341
             echo ''
188
             echo '# control file built with MKit __MKIT_MKIT_VERSION__'
342
             echo '# control file built with MKit __MKIT_MKIT_VERSION__'
343
+            echo "#  .. based on stub from: $MKIT_VERSION"
189
             ;;
344
             ;;
190
 
345
 
191
         packaging/debian/changelog)
346
         packaging/debian/changelog)
205
             echo ''
360
             echo ''
206
             echo '%:'
361
             echo '%:'
207
             echo ''
362
             echo ''
208
-            echo '	dh $@'
363
+            echo '	PREFIX=/usr dh $@'
209
             echo ''
364
             echo ''
210
             echo 'override_dh_auto_install:'
365
             echo 'override_dh_auto_install:'
211
             echo ''
366
             echo ''
212
-            echo '	make install PREFIX=/usr DESTDIR=debian/tmp'
367
+            echo '	make install DESTDIR=debian/tmp'
213
             ;;
368
             ;;
214
 
369
 
215
         packaging/debian/source/format)
370
         packaging/debian/source/format)
430
             if $MkReadme; then
585
             if $MkReadme; then
431
             echo ""
586
             echo ""
432
             echo ""
587
             echo ""
433
-            echo "README.md"
434
-            echo "---------"
588
+            echo "README.md and MAINTAINERS.md"
589
+            echo "----------------------------"
435
             echo ""
590
             echo ""
436
             echo "Each serious project needs a serious README.  Which is why"
591
             echo "Each serious project needs a serious README.  Which is why"
437
             echo "*stub* has created a 'stub' of one for you."
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
             fi
597
             fi
439
 
598
 
440
             echo ""
599
             echo ""
479
         echo "    -V VERSION    initial version (default: 0.0.0)"
638
         echo "    -V VERSION    initial version (default: 0.0.0)"
480
         echo "    -a            enable autoclean ('make clean' after"
639
         echo "    -a            enable autoclean ('make clean' after"
481
         echo "                  each 'make install')"
640
         echo "                  each 'make install')"
482
-        echo "    -g            make git commits before and adter"
641
+        echo "    -g            make git commits before and after"
483
         echo "                  (implies -y)"
642
         echo "                  (implies -y)"
484
         echo "    -y            don't ask, just do it"
643
         echo "    -y            don't ask, just do it"
485
         echo "    -R            skip creating README.md"
644
         echo "    -R            skip creating README.md"
699
     deploy src/"$PackageName".skel
858
     deploy src/"$PackageName".skel
700
     $MkMakefile     && deploy Makefile
859
     $MkMakefile     && deploy Makefile
701
     $MkReadme       && deploy README.md
860
     $MkReadme       && deploy README.md
861
+    $MkReadme       && deploy MAINTAINERS.md
702
     $MkLicense      && deploy LICENSE.md
862
     $MkLicense      && deploy LICENSE.md
703
     $AutoClean      && deploy .mkit/autoclean
863
     $AutoClean      && deploy .mkit/autoclean
704
     $MkPackaging    && deploy_packaging
864
     $MkPackaging    && deploy_packaging