瀏覽代碼

Refactor versioning and release

Some parts got a bit confusing over time, also there was repetition.

In this commit:

 *  the release fact gathering routines have been moved to separate
    module,

 *  some less useful routines have been removed,

 *  some internal interfaces have been changed to allow for more
    readable code.
Alois Mahdal 9 年之前
父節點
當前提交
dba6f86d34
共有 5 個檔案被更改,包括 227 行新增193 行删除
  1. 5
    86
      src/include/build.sh
  2. 130
    0
      src/include/facts.sh
  3. 21
    0
      src/include/ini.sh
  4. 0
    21
      src/include/mkit.sh
  5. 71
    86
      src/include/release.sh

+ 5
- 86
src/include/build.sh 查看文件

@@ -1,6 +1,7 @@
1 1
 #!/bin/bash
2 2
 
3
-. "$MKIT_DIR/include/ini.sh" || die "cannot import ini.sh"
3
+. "$MKIT_DIR/include/ini.sh"   || die "cannot import ini.sh"
4
+. "$MKIT_DIR/include/facts.sh" || die "cannot import facts.sh"
4 5
 
5 6
 
6 7
 _build1() {
@@ -78,7 +79,7 @@ _expand_variables() {
78 79
         echo "s|__MKIT_PROJ_CODENAME__|$(ini 1value project:codename)|g;"
79 80
         echo "s|__MKIT_PROJ_PKGNAME__|$(ini 1value project:pkgname)|g;"
80 81
         echo "s|__MKIT_PROJ_TAGLINE__|$(ini 1value project:tagline)|g;"
81
-        echo "s|__MKIT_PROJ_VERSION__|$(get_version)|g;"
82
+        echo "s|__MKIT_PROJ_VERSION__|$(semver)|g;"
82 83
         echo "s|__MKIT_SELF_VERSION__|$MKIT_VERSION|g;"
83 84
     } >> "$script"
84 85
     perl -wp "$script" || die "_expand_variables failed"
@@ -144,7 +145,7 @@ debstuff() {
144 145
     #
145 146
     # Build Debian stuff (eamed tarball, debian dir)
146 147
     #
147
-    local version="$(get_version)"
148
+    local version="$(semver)"
148 149
 
149 150
     # tarball - we should already have by means of 'dist'
150 151
     #
@@ -177,7 +178,7 @@ dist() {
177 178
     #FIXME: lacking Makefile skills, we do this step twice fot
178 179
     #       rpmstuff, hence -f hack for gzip
179 180
     #
180
-    local version=$(get_version)
181
+    local version=$(semver)
181 182
     local dirname=$MKIT_PROJ_PKGNAME-$version
182 183
     mkdir -p "$dirname"
183 184
     ini values "lists:dist" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname"
@@ -189,88 +190,6 @@ dist() {
189 190
     rm -rf "$dirname"
190 191
 }
191 192
 
192
-get_version() {
193
-    #
194
-    # Build semver version string with build metadata
195
-    #
196
-    # Build version string from available info using following
197
-    # logic:
198
-    #
199
-    #  1. use project.version (from mkit.ini)
200
-    #  2. if we are in git, override the version with last tag
201
-    #  3. if set, add project:prerl (from mkit.ini) as pre-release ID
202
-    #     (afer dash)
203
-    #  4. if we are at a later commit than the last tag, add branch
204
-    #     name and commit sha1 to build metadata (after plus sign)
205
-    #  5. if the tree is "dirty", i.e. has uncommited changes,
206
-    #     add "dirty" to build metadata
207
-    #
208
-    # The version is compatible with SemVer 2.0.0.
209
-    #
210
-    # Examples:
211
-    #
212
-    #     myprog v1.0.7                         # all clear
213
-    #     myprog v1.0.7-alpha                   # mkit.ini: project:prerl="alpha"
214
-    #     myprog v1.0.7-alpha+g1aef811.master   # ^^ + some commits after
215
-    #     myprog v1.0.7-alpha+gf14fc4f.api2     # ^^ + on a feature branch
216
-    #     myprog v1.0.7-alpha+gf14fc4f.api2.dirty  # ^^ + tree edited
217
-    #     myprog v1.0.7-alpha+dirty             # tag OK but tree edited
218
-    #     myprog v1.0.7+dirty                   # ^^ but no pre-release id
219
-    #
220
-    # Note that versions with "dirty" should be perceived as kind of
221
-    # dangerous outside developer's own machine.  Versions with sha1 are
222
-    # safer but must not be released.
223
-    #
224
-    # I have considered decorating the git commit refs to make them
225
-    # sort of sortable (e.g. "r1.g1aef811"), but on second thought,
226
-    # I don't think it's good idea to give *any* semantics to meta-data
227
-    # at all.  First, there is no rule that r1<r2<r3; a commit can be
228
-    # removing what other just added and one change can be split to
229
-    # multiple commits.  Also, the whole thing breaks anyway once you
230
-    # rebase your branch (no, it's not a sin).  The sole purpose of
231
-    # meta-data is to *identify* the code, and provide safe path back
232
-    # to tree; commit refs are already perfect for that.
233
-    #
234
-    # FIXME:  Using project:prerl for release IDs may not be compatible with
235
-    #         release strategy implemented in release.sh
236
-    #
237
-    local version=$(ini 1value project:version)
238
-    local prerl=$(ini 1value project:prerl)
239
-    grep ":" <<<"$prerl" && warn "colon in project:prerl may corrupt version data: $prerl"
240
-    if git rev-parse HEAD >&/dev/null;
241
-    then    # we are in git repo... so we can get smart
242
-        local latest_tag=$(
243
-            git log --format="%D" \
244
-              | sed 's/,/\n/g' \
245
-              | sed 's/^[[:blank:]]*//; ' \
246
-              | grep -E '^tag: v[[:digit:]]+\.' \
247
-              | cut -d' ' -f2 \
248
-              | head -1
249
-        )
250
-        if ! git describe --tags --exact-match HEAD >&/dev/null;
251
-        then    # we are at a later commit than the last tag
252
-            local sha=g$(git log -1 --pretty=format:%h HEAD)
253
-            local curbranch=$(git rev-parse --abbrev-ref HEAD)
254
-            local commit="$curbranch.$sha"
255
-        fi
256
-        if test "$(git diff --shortstat 2>/dev/null)" != "";
257
-        then    # the tree is "dirty", i.e. has been edited
258
-            local dirty=dirty
259
-        fi
260
-        test -n "$latest_tag" && version=${latest_tag:1}
261
-        local suffix=""
262
-        case "$commit:$dirty" in
263
-            :)       suffix=""                ;;
264
-            :dirty)  suffix="+$dirty"         ;;
265
-            *:)      suffix="+$commit"        ;;
266
-            *:dirty) suffix="+$commit.$dirty" ;;
267
-        esac
268
-        test -b "$prerl" && suffix="-$prerl$suffix"
269
-        version="$version$suffix"
270
-    fi
271
-    echo "$version"
272
-}
273
-
274 193
 rpmstuff() {
275 194
     #
276 195
     # Build specfile

+ 130
- 0
src/include/facts.sh 查看文件

@@ -0,0 +1,130 @@
1
+#!/bin/bash
2
+
3
+. "$MKIT_DIR/include/ini.sh" || die "cannot import ini.sh"
4
+
5
+git_fact() {
6
+    #
7
+    # Get git fact $1
8
+    #
9
+    local fact_name="$1"
10
+    git_present || warn "can't give fact outside git repo: $fact_name"
11
+    case "$fact_name" in
12
+        latest_tag)
13
+            git log --format="%D" \
14
+              | sed 's/,/\n/g' \
15
+              | sed 's/^[[:blank:]]*//; ' \
16
+              | grep -E '^tag: v[[:digit:]]+\.' \
17
+              | cut -d' ' -f2 \
18
+              | head -1
19
+            ;;
20
+        latest_version)
21
+            git_fact latest_tag | git_tag2ver
22
+            ;;
23
+        current_branch)
24
+            git rev-parse --abbrev-ref HEAD
25
+            ;;
26
+        reldiff)
27
+            git log --oneline "$(git_fact latest_tag)..HEAD" --name-only
28
+            ;;
29
+        latest_sha)
30
+            git log -1 --pretty=format:%h HEAD
31
+            ;;
32
+        *)
33
+            warn "unknown git fact asked: $fact_name"
34
+            ;;
35
+    esac
36
+}
37
+
38
+git_present() {
39
+    #
40
+    # True if we're in a git repo
41
+    #
42
+    git rev-parse HEAD >&/dev/null
43
+}
44
+
45
+git_tag2ver() {
46
+    #
47
+    # Convert tag to version
48
+    #
49
+    sed s/^v//
50
+}
51
+
52
+git_ver2tag() {
53
+    #
54
+    # Convert version to tag
55
+    #
56
+    sed s/^/v/
57
+}
58
+
59
+semver() {
60
+    #
61
+    # Build semver version string with build metadata
62
+    #
63
+    # Build version string from available info using following
64
+    # logic:
65
+    #
66
+    #  1. use project.version (from mkit.ini)
67
+    #  2. if we are in git, override the version with last tag
68
+    #  3. if set, add project:prerl (from mkit.ini) as pre-release ID
69
+    #     (afer dash)
70
+    #  4. if we are at a later commit than the last tag, add branch
71
+    #     name and commit sha1 to build metadata (after plus sign)
72
+    #  5. if the tree is "dirty", i.e. has uncommited changes,
73
+    #     add "dirty" to build metadata
74
+    #
75
+    # The version is compatible with SemVer 2.0.0.
76
+    #
77
+    # Examples:
78
+    #
79
+    #     myprog v1.0.7                         # all clear
80
+    #     myprog v1.0.7-alpha                   # mkit.ini: project:prerl="alpha"
81
+    #     myprog v1.0.7-alpha+g1aef811.master   # ^^ + some commits after
82
+    #     myprog v1.0.7-alpha+gf14fc4f.api2     # ^^ + on a feature branch
83
+    #     myprog v1.0.7-alpha+gf14fc4f.api2.dirty  # ^^ + tree edited
84
+    #     myprog v1.0.7-alpha+dirty             # tag OK but tree edited
85
+    #     myprog v1.0.7+dirty                   # ^^ but no pre-release id
86
+    #
87
+    # Note that versions with "dirty" should be perceived as kind of
88
+    # dangerous outside developer's own machine.  Versions with sha1 are
89
+    # safer but must not be released.
90
+    #
91
+    # I have considered decorating the git commit refs to make them
92
+    # sort of sortable (e.g. "r1.g1aef811"), but on second thought,
93
+    # I don't think it's good idea to give *any* semantics to meta-data
94
+    # at all.  First, there is no rule that r1<r2<r3; a commit can be
95
+    # removing what other just added and one change can be split to
96
+    # multiple commits.  Also, the whole thing breaks anyway once you
97
+    # rebase your branch (no, it's not a sin).  The sole purpose of
98
+    # meta-data is to *identify* the code, and provide safe path back
99
+    # to tree; commit refs are already perfect for that.
100
+    #
101
+    # FIXME:  Using project:prerl for release IDs may not be compatible with
102
+    #         release strategy implemented in release.sh
103
+    #
104
+    local version=$(ini 1value project:version)
105
+    local prerl=$(ini 1value project:prerl)
106
+    grep ":" <<<"$prerl" && warn "colon in project:prerl may corrupt version data: $prerl"
107
+    if git_present;
108
+    then    # we are in git repo... so we can get smart
109
+        local latest_tag=$(git_fact latest_tag)
110
+        if ! git describe --tags --exact-match HEAD >&/dev/null;
111
+        then    # we are at a later commit than the last tag
112
+            local commit="$(git_fact current_branch).g$(git_fact latest_sha)"
113
+        fi
114
+        if test "$(git diff --shortstat 2>/dev/null)" != "";
115
+        then    # the tree is "dirty", i.e. has been edited
116
+            local dirty=dirty
117
+        fi
118
+        test -n "$latest_tag" && version=${latest_tag:1}
119
+        local suffix=""
120
+        case "$commit:$dirty" in
121
+            :)       suffix=""                ;;
122
+            :dirty)  suffix="+$dirty"         ;;
123
+            *:)      suffix="+$commit"        ;;
124
+            *:dirty) suffix="+$commit.$dirty" ;;
125
+        esac
126
+        test -b "$prerl" && suffix="-$prerl$suffix"
127
+        version="$version$suffix"
128
+    fi
129
+    echo "$version"
130
+}

+ 21
- 0
src/include/ini.sh 查看文件

@@ -121,3 +121,24 @@ ini() {
121 121
     esac
122 122
     <"$MKIT_INI" $fn "$arg" | $limit
123 123
 }
124
+
125
+update_version() {
126
+    #
127
+    # Change project.version in mkit.ini at path $2 to version $1
128
+    #
129
+    local version="$1"
130
+    local inifile="$2"
131
+    local tmp=$(mktemp -t mkit.update_version.XXXXXXXX)
132
+    <"$inifile" perl -e '
133
+        my $hit = 0;
134
+        my $done = 0;
135
+        foreach (<STDIN>) {
136
+            if      ($done) { print; next; }
137
+            elsif   (m/\[project\]/) { $hit++; print; next; }
138
+            elsif   (m/\[/) { $hit = 0; print; next; }
139
+            elsif   ($hit) { s/\bversion\b *=.*/version = $ARGV[0]/ and $done++; print; }
140
+            else { print; next; }
141
+        }
142
+    ' "$version" >"$tmp" || die "failed to update version in mkit.ini"
143
+    mv "$tmp" "$inifile"
144
+}

+ 0
- 21
src/include/mkit.sh 查看文件

@@ -92,27 +92,6 @@ route() {
92 92
     fi
93 93
 }
94 94
 
95
-update_version() {
96
-    #
97
-    # Change project.version in mkit.ini at path $2 to version $1
98
-    #
99
-    local version="$1"
100
-    local inifile="$2"
101
-    local tmp=$(mktemp -t mkit.update_version.XXXXXXXX)
102
-    <"$inifile" perl -e '
103
-        my $hit = 0;
104
-        my $done = 0;
105
-        foreach (<STDIN>) {
106
-            if      ($done) { print; next; }
107
-            elsif   (m/\[project\]/) { $hit++; print; next; }
108
-            elsif   (m/\[/) { $hit = 0; print; next; }
109
-            elsif   ($hit) { s/\bversion\b *=.*/version = $ARGV[0]/ and $done++; print; }
110
-            else { print; next; }
111
-        }
112
-    ' "$version" >"$tmp" || die "failed to update version in mkit.ini"
113
-    mv "$tmp" "$inifile"
114
-}
115
-
116 95
 warn() {
117 96
     #
118 97
     # Print warning message

+ 71
- 86
src/include/release.sh 查看文件

@@ -1,92 +1,78 @@
1 1
 #!/bin/bash
2 2
 
3
-_die_if() {
3
+. "$MKIT_DIR/include/ini.sh"   || die "cannot import ini.sh"
4
+. "$MKIT_DIR/include/facts.sh" || die "cannot import facts.sh"
5
+
6
+_bump_version() {
7
+    #
8
+    # Bump version on stdin by level $1 (x, y or z)
9
+    #
10
+    local rlevel=$1
11
+    local old
12
+    read -r old
13
+    local oldx=${old%.*.*}
14
+    local oldz=${old#*.*.}
15
+    local tmpy=${old%.*}
16
+    local oldy=${tmpy#*.}
17
+    local new=""
18
+    case $rlevel in
19
+        x) new="$((oldx+1)).0.0"            ;;
20
+        y) new="$oldx.$((oldy+1)).0"        ;;
21
+        z) new="$oldx.$oldy.$((oldz+1))"    ;;
22
+        *) die "invalid release level: $1"  ;;
23
+    esac
24
+    echo "$new"
25
+}
26
+
27
+_relck() {
4 28
     #
5 29
     # Die if blocking condition $1 is detected
6 30
     #
7 31
     local condition="$1"
8 32
     local x
9 33
     case "$condition" in
10
-        nogit)
34
+        git_present)
11 35
             git rev-parse HEAD >&/dev/null\
12 36
              || die "cannot do this outside git repo"
13 37
             ;;
14
-        norelbr)
38
+        at_relsrc)
15 39
             local relsrc=$(ini 1value project:relsrc)
16
-            _git_info curbranch \
40
+            git_fact current_branch \
17 41
               | grep -qFx "$relsrc" \
18 42
              || die "you are not on release source branch: $relsrc"
19 43
             ;;
20
-        dirty)
44
+        not_dirty)
21 45
             git diff --shortstat 2>/dev/null \
22 46
               | grep -q . \
23 47
              && die "tree is dirty: $dirt"
24 48
             ;;
25
-        novertag)
26
-            _git_info lasttag \
49
+        tags_ok)
50
+            git_fact latest_tag \
27 51
               | grep -q . \
28
-             || die "cannot find last tag"
52
+             || die "cannot find latest tag"
29 53
             ;;
30
-        nobump)
54
+        vbump_hot)
31 55
             git diff-tree --no-commit-id --name-only -r HEAD \
32 56
               | grep -qFx mkit.ini \
33 57
              || die "last change must be version bump in mkit.ini"
34 58
             ;;
35
-        wip)
36
-            _git_info reldiff \
59
+        no_wip)
60
+            git_fact reldiff \
37 61
               | grep '^....... WIP ' \
38
-             && die "WIP commit since $(_git_info lasttag)"
62
+             && die "WIP commit since $(git_fact latest_tag)"
39 63
             ;;
40
-        old_c)
41
-            x=$(_ver_info nextver_g)
42
-            _ver_info currver_c \
43
-              | grep -qFx "$x" \
44
-             || die "new version not in mkit.ini: $x"
64
+        ini_version)
65
+            local oracle=$(git_fact latest_version | _bump_version "$rlevel")
66
+            ini 1value project:version  \
67
+              | grep -qFx "$oracle" \
68
+             || die "new version not in mkit.ini: $oracle"
69
+            ;;
70
+        *)
71
+            die "unknown release check: $condition"
45 72
             ;;
46 73
     esac
47 74
 }
48 75
 
49
-_git_info() {
50
-    #
51
-    # Get git info $1
52
-    #
53
-    local info="$1"
54
-    case "$info" in
55
-        lasttag)    git tag | grep ^v | sort -V | tail -n1  ;;
56
-        curbranch)  git rev-parse --abbrev-ref HEAD         ;;
57
-        reldiff)    git log --oneline "$(_git_info lasttag)..HEAD" --name-only ;;
58
-    esac
59
-}
60
-
61
-_git_msg_vbump() {
62
-    echo "Bump version"
63
-    echo ""
64
-    echo "Overview of changes:"
65
-    echo ""
66
-    _git_info reldiff \
67
-      | sed '
68
-            s/^[a-f0-9]\{7\} / *  &/; t PATHS
69
-            s/^/        /
70
-            :PATHS
71
-        '
72
-}
73
-
74
-_make_ver() {
75
-    local level=$1
76
-    local old=$2
77
-    local oldx=${old%.*.*}
78
-    local oldz=${old#*.*.}
79
-    local tmpy=${old%.*}
80
-    local oldy=${tmpy#*.}
81
-    case $level in
82
-        x) new="$((oldx+1)).0.0"            ;;
83
-        y) new="$oldx.$((oldy+1)).0"        ;;
84
-        z) new="$oldx.$oldy.$((oldz+1))"    ;;
85
-        *) die "invalid release level: $1"  ;;
86
-    esac
87
-    echo "$new"
88
-}
89
-
90 76
 _release() {
91 77
     #
92 78
     # Prepare release
@@ -94,21 +80,21 @@ _release() {
94 80
     # Span release routines: make a signed tag, check branch
95 81
     # and update release branch
96 82
     #
97
-    # FIXME: Using project:prerl as build.sh:get_version() does may not be
83
+    # FIXME: Using project:prerl as build.sh:semver() does may not be
98 84
     #        compatible with this release strategy
99 85
     #
100
-    local level=$1
86
+    local rlevel=$1
101 87
     local newtag
102 88
 
103
-    _die_if nogit
104
-    _die_if norelbr
105
-    _die_if dirty
106
-    _die_if novertag
107
-    _die_if nobump
108
-    _die_if wip
109
-    _die_if old_c
89
+    _relck git_present
90
+    _relck at_relsrc
91
+    _relck not_dirty
92
+    _relck tags_ok
93
+    _relck vbump_hot
94
+    _relck no_wip
95
+    _relck ini_version
110 96
 
111
-    newtag=v$(_ver_info nextver_g)
97
+    newtag=$(git_fact latest_version | _bump_version "$rlevel" | git_ver2tag )
112 98
     set -e
113 99
     debug_var newtag
114 100
     $MKIT_DRY && return
@@ -117,34 +103,33 @@ _release() {
117 103
 }
118 104
 
119 105
 _vbump() {
120
-    local level="$1"
106
+    local rlevel="$1"
121 107
     local lastver   # current from mkit.ini
122 108
     local nextver   # after the bump
123
-    _die_if nogit
124
-    _die_if norelbr
125
-    _die_if dirty
126
-    lastver=$(_ver_info currver_c)
127
-    nextver=$(_ver_info nextver_c)
109
+    _relck git_present
110
+    _relck at_relsrc
111
+    _relck not_dirty
112
+    nextver=$(ini 1value project:version | _bump_version "$rlevel")
128 113
     debug_var lastver nextver
129 114
     $MKIT_DRY && return
130 115
     update_version "$nextver" mkit.ini \
131 116
       || die "failed to update version in mkit.ini"
132 117
     git add mkit.ini \
133 118
       || die "failed to add mkit.ini to the index"
134
-    git commit -e -m "$(_git_msg_vbump)"
119
+    git commit -e -m "$(_vbump_gitmsg)"
135 120
 }
136 121
 
137
-_ver_info() {
138
-    #
139
-    # Get git info $1
140
-    #
141
-    local info="$1"
142
-    case "$info" in
143
-        lastver_g)  _git_info lasttag | sed s/^v// ;;
144
-        nextver_g)  _make_ver "$level" "$(_ver_info lastver_g)" ;;
145
-        currver_c)  ini 1value project:version ;;
146
-        nextver_c)  _make_ver "$level" "$(_ver_info currver_c)" ;;
147
-    esac
122
+_vbump_gitmsg() {
123
+    echo "Bump version"
124
+    echo ""
125
+    echo "Overview of changes:"
126
+    echo ""
127
+    git_fact reldiff \
128
+      | sed '
129
+            s/^[a-f0-9]\{7\} / *  &/; t PATHS
130
+            s/^/        /
131
+            :PATHS
132
+        '
148 133
 }
149 134
 
150 135
 release_x() {