Browse Source

version: greatly simplify redo nonsense, now that we use VERSION.

Signed-off-by: David Anderson <[email protected]>
David Anderson 5 years ago
parent
commit
65bad9a8bd

+ 1 - 0
version/.gitignore

@@ -3,6 +3,7 @@ long.txt
 short.txt
 gitcommit.txt
 extragitcommit.txt
+version-info.sh
 version.h
 version.xcconfig
 ver.go

+ 0 - 26
version/describe.sh

@@ -1,26 +0,0 @@
-#!/bin/sh
-#
-# Constructs a "git describe" compatible version number by using the
-# information in the VERSION file, rather than git tags.
-
-set -eu
-
-dir="$(dirname $0)"
-verfile="$dir/../VERSION"
-
-read -r version hash <"$verfile"
-
-if [ -z "$hash" ]; then
-    # If no explicit hash was given, use the last time the version
-    # file changed as the "origin" hash for this version.
-    hash="$(git rev-list --max-count=1 HEAD -- $verfile)"
-fi
-
-if [ -z "$hash" ]; then
-    echo "Couldn't find base git hash for version '$version'" >2
-    exit 1
-fi
-
-head="$(git rev-parse --short=9 HEAD)"
-changecount="$(git rev-list ${hash}..HEAD | wc -l)"
-echo "v${version}-${changecount}-g${head}"

+ 0 - 6
version/extragitcommit.txt.do

@@ -1,6 +0,0 @@
-# --abbrev=200 is an arbitrary large number to capture the entire git
-# hash without trying to compact it.
-commit=$(cd ../.. && git describe --dirty --exclude "*" --always --abbrev=200)
-echo "$commit" >$3
-redo-always
-redo-stamp <$3

+ 0 - 6
version/gitcommit.txt.do

@@ -1,6 +0,0 @@
-# --abbrev=200 is an arbitrary large number to capture the entire git
-# hash without trying to compact it.
-commit=$(git describe --dirty --exclude "*" --always --abbrev=200)
-echo "$commit" >$3
-redo-always
-redo-stamp <$3

+ 0 - 5
version/long.txt.do

@@ -1,5 +0,0 @@
-redo-ifchange mkversion.sh describe.txt extragitcommit.txt
-read -r describe <describe.txt
-read -r other <extragitcommit.txt
-ver=$(./mkversion.sh long "$describe" "$other")
-echo "$ver" >$3

+ 0 - 139
version/mkversion.sh

@@ -1,139 +0,0 @@
-#!/bin/sh
-
-set -eu
-
-mode=$1
-describe=$2
-other=$3
-
-# Git describe output overall looks like
-# MAJOR.MINOR.PATCH-NUMCOMMITS-GITHASH. Depending on the tag being
-# described and the state of the repo, ver can be missing PATCH,
-# NUMCOMMITS, or both.
-#
-# Valid values look like: 1.2.3-1234-abcdef, 0.98-1234-abcdef,
-# 1.0.0-abcdef, 0.99-abcdef.
-ver="${describe#v}"
-stem="${ver%%-*}" # Just the semver-ish bit e.g. 1.2.3, 0.98
-suffix="${ver#$stem}" # The rest e.g. -23-abcdef, -abcdef
-
-# Normalize the stem into a full major.minor.patch semver. We might
-# not use all those pieces depending on what kind of version we're
-# making, but it's good to have them all on hand.
-case "$stem" in
-    *.*.*)
-        # Full SemVer, nothing to do
-        stem="$stem"
-        ;;
-    *.*)
-        # Old style major.minor, add a .0
-        stem="${stem}.0"
-        ;;
-    *)
-        echo "Unparseable version $stem" >&2
-        exit 1
-        ;;
-esac
-major=$(echo "$stem" | cut -f1 -d.)
-minor=$(echo "$stem" | cut -f2 -d.)
-patch=$(echo "$stem" | cut -f3 -d.)
-
-# Extract the change count and git ID from the suffix.
-case "$suffix" in
-    -*-*)
-        # Has both a change count and a commit hash.
-        changecount=$(echo "$suffix" | cut -f2 -d-)
-        githash=$(echo "$suffix" | cut -f3 -d-)
-        ;;
-    -*)
-        # Git hash only, change count is zero.
-        changecount="0"
-        githash=$(echo "$suffix" | cut -f2 -d-)
-        ;;
-    *)
-        echo "Unparseable version suffix $suffix" >&2
-        exit 1
-        ;;
-esac
-
-# The git hash is of the form "gCOMMITHASH". We want to replace the
-# 'g' with a 't', for "tailscale", to convey that it's specifically
-# the commit hash of the tailscale repo.
-if [ -n "$githash" ]; then
-    # POSIX shell doesn't understand ${foo:1:9} syntax, gaaah.
-    githash="$(echo $githash | cut -c2-10)"
-    githash="t${githash}"
-fi
-
-# "other" is a second git commit hash for another repository used to
-# build the Tailscale code. In practice it's either the commit hash in
-# the Android repository, or the commit hash of Tailscale's
-# proprietary repository (which pins a bunch things like build scripts
-# used and Go toolchain version).
-if [ -n "$other" ]; then
-    other="$(echo $other | cut -c1-9)"
-    other="-g${other}"
-fi
-
-# Validate that the version data makes sense. Rules:
-#  - Odd number minors are unstable. Patch must be 0, and gets
-#    replaced by changecount.
-#  - Even number minors are stable. Changecount must be 0, and
-#    gets removed.
-#
-# After this section, we only use major/minor/patch, which have been
-# tweaked as needed.
-if expr "$minor" : "[0-9]*[13579]$" >/dev/null; then
-    # Unstable
-    if [ "$patch" != "0" ]; then
-        # This is a fatal error, because a non-zero patch number
-        # indicates that we created an unstable git tag in violation
-        # of our versioning policy, and we want to blow up loudly to
-        # get that fixed.
-        echo "Unstable release $describe has a non-zero patch number, which is not allowed" >&2
-        exit 1
-    fi
-    patch="$changecount"
-else
-    # Stable
-    if [ "$changecount" != "0" ]; then
-        # This is a commit that's sitting between two stable
-        # releases. We never want to release such a commit to the
-        # pbulic, but it's useful to be able to build it for
-        # debugging. Just force the version to 0.0.0, so that we're
-        # forced to rely on the git commit hash.
-        major="0"
-        minor="0"
-        patch="0"
-    fi
-fi
-
-if [ "$minor" -eq 1 ]; then
-    # Hack for 1.1: add 1000 to the patch number, so that builds that
-    # use the OSS change count order after the builds that used the
-    # proprietary repo's changecount. Otherwise, the version numbers
-    # would go backwards and things would be unhappy.
-    patch=$((patch + 1000))
-fi
-
-case "$1" in
-    long)
-        echo "${major}.${minor}.${patch}-${githash}${other}"
-        ;;
-    short)
-        echo "${major}.${minor}.${patch}"
-        ;;
-    xcode)
-        # CFBundleShortVersionString: the "short name" used in the App
-        # Store.  eg. 0.92.98
-        echo "VERSION_NAME = ${major}.${minor}.${patch}"
-        # CFBundleVersion: the build number. Needs to be 3 numeric
-        # sections that increment for each release according to SemVer
-        # rules.
-        #
-        # We start counting at 100 because we submitted using raw
-        # build numbers before, and Apple doesn't let you start over.
-        # e.g. 0.98.3 -> 100.98.3
-        echo "VERSION_ID = $((major + 100)).${minor}.${patch}"        
-        ;;
-esac

+ 70 - 48
version/mkversion_test.go

@@ -8,22 +8,21 @@ import (
 	"fmt"
 	"os/exec"
 	"runtime"
+	"strconv"
 	"strings"
 	"testing"
-)
 
-func xcode(short, long string) string {
-	return fmt.Sprintf("VERSION_NAME = %s\nVERSION_ID = %s", short, long)
-}
+	"github.com/google/go-cmp/cmp"
+)
 
-func mkversion(t *testing.T, mode, describe, other string) (string, bool) {
+func mkversion(t *testing.T, gitHash, otherHash string, major, minor, patch, changeCount int) (string, bool) {
 	t.Helper()
-	bs, err := exec.Command("./mkversion.sh", mode, describe, other).CombinedOutput()
+	bs, err := exec.Command("./version.sh", gitHash, otherHash, strconv.Itoa(major), strconv.Itoa(minor), strconv.Itoa(patch), strconv.Itoa(changeCount)).CombinedOutput()
+	out := strings.TrimSpace(string(bs))
 	if err != nil {
-		t.Logf("mkversion.sh output: %s", string(bs))
-		return "", false
+		return out, false
 	}
-	return strings.TrimSpace(string(bs)), true
+	return out, true
 }
 
 func TestMkversion(t *testing.T) {
@@ -31,50 +30,73 @@ func TestMkversion(t *testing.T) {
 		t.Skip("skip test on Windows, because there is no shell to execute mkversion.sh.")
 	}
 	tests := []struct {
-		describe string
-		other    string
-		ok       bool
-		long     string
-		short    string
-		xcode    string
+		gitHash, otherHash               string
+		major, minor, patch, changeCount int
+		want                             string
 	}{
-		{"v0.98-gabcdef", "", true, "0.98.0-tabcdef", "0.98.0", xcode("0.98.0", "100.98.0")},
-		{"v0.98.1-gabcdef", "", true, "0.98.1-tabcdef", "0.98.1", xcode("0.98.1", "100.98.1")},
-		{"v1.1.0-37-gabcdef", "", true, "1.1.1037-tabcdef", "1.1.1037", xcode("1.1.1037", "101.1.1037")},
-		{"v1.2.9-gabcdef", "", true, "1.2.9-tabcdef", "1.2.9", xcode("1.2.9", "101.2.9")},
-		{"v1.2.9-0-gabcdef", "", true, "1.2.9-tabcdef", "1.2.9", xcode("1.2.9", "101.2.9")},
-		{"v1.15.0-129-gabcdef", "", true, "1.15.129-tabcdef", "1.15.129", xcode("1.15.129", "101.15.129")},
-
-		{"v0.98-123-gabcdef", "", true, "0.0.0-tabcdef", "0.0.0", xcode("0.0.0", "100.0.0")},
-		{"v1.0.0-37-gabcdef", "", true, "0.0.0-tabcdef", "0.0.0", xcode("0.0.0", "100.0.0")},
-		{"v1.1.0-129-gabcdef", "0123456789abcdef0123456789abcdef", true, "1.1.1129-tabcdef-g012345678", "1.1.1129", xcode("1.1.1129", "101.1.1129")},
-		{"v0.99.5-0-gabcdef", "", false, "", "", ""},   // unstable, patch not allowed
-		{"v0.99.5-123-gabcdef", "", false, "", "", ""}, // unstable, patch not allowed
-		{"v1-gabcdef", "", false, "", "", ""},          // bad semver
-		{"v1.0", "", false, "", "", ""},                // missing suffix
+		{"abcdef", "", 0, 98, 0, 0, `
+           VERSION_SHORT="0.98.0"
+           VERSION_LONG="0.98.0-tabcdef"
+           VERSION_GIT_HASH="abcdef"
+           VERSION_EXTRA_HASH=""
+           VERSION_XCODE="100.98.0"
+           VERSION_WINRES="0,98,0,0"`},
+		{"abcdef", "", 0, 98, 1, 0, `
+           VERSION_SHORT="0.98.1"
+           VERSION_LONG="0.98.1-tabcdef"
+           VERSION_GIT_HASH="abcdef"
+           VERSION_EXTRA_HASH=""
+           VERSION_XCODE="100.98.1"
+           VERSION_WINRES="0,98,1,0"`},
+		{"abcdef", "", 1, 1, 0, 37, `
+           VERSION_SHORT="1.1.1037"
+           VERSION_LONG="1.1.1037-tabcdef"
+           VERSION_GIT_HASH="abcdef"
+           VERSION_EXTRA_HASH=""
+           VERSION_XCODE="101.1.1037"
+           VERSION_WINRES="1,1,1037,0"`},
+		{"abcdef", "", 1, 2, 9, 0, `
+           VERSION_SHORT="1.2.9"
+           VERSION_LONG="1.2.9-tabcdef"
+           VERSION_GIT_HASH="abcdef"
+           VERSION_EXTRA_HASH=""
+           VERSION_XCODE="101.2.9"
+           VERSION_WINRES="1,2,9,0"`},
+		{"abcdef", "", 1, 15, 0, 129, `
+           VERSION_SHORT="1.15.129"
+           VERSION_LONG="1.15.129-tabcdef"
+           VERSION_GIT_HASH="abcdef"
+           VERSION_EXTRA_HASH=""
+           VERSION_XCODE="101.15.129"
+           VERSION_WINRES="1,15,129,0"`},
+		{"abcdef", "", 1, 2, 0, 17, `
+           VERSION_SHORT="0.0.0"
+           VERSION_LONG="0.0.0-tabcdef"
+           VERSION_GIT_HASH="abcdef"
+           VERSION_EXTRA_HASH=""
+           VERSION_XCODE="100.0.0"
+           VERSION_WINRES="0,0,0,0"`},
+		{"abcdef", "defghi", 1, 15, 0, 129, `
+           VERSION_SHORT="1.15.129"
+           VERSION_LONG="1.15.129-tabcdef-gdefghi"
+           VERSION_GIT_HASH="abcdef"
+           VERSION_EXTRA_HASH="defghi"
+           VERSION_XCODE="101.15.129"
+           VERSION_WINRES="1,15,129,0"`},
+		{"abcdef", "", 0, 99, 5, 0, ""},   // unstable, patch number not allowed
+		{"abcdef", "", 0, 99, 5, 123, ""}, // unstable, patch number not allowed
 	}
 
 	for _, test := range tests {
-		gotlong, longOK := mkversion(t, "long", test.describe, test.other)
-		if longOK != test.ok {
-			t.Errorf("mkversion.sh long %q ok=%v, want %v", test.describe, longOK, test.ok)
-		}
-		gotshort, shortOK := mkversion(t, "short", test.describe, test.other)
-		if shortOK != test.ok {
-			t.Errorf("mkversion.sh short %q ok=%v, want %v", test.describe, shortOK, test.ok)
-		}
-		gotxcode, xcodeOK := mkversion(t, "xcode", test.describe, test.other)
-		if xcodeOK != test.ok {
-			t.Errorf("mkversion.sh xcode %q ok=%v, want %v", test.describe, xcodeOK, test.ok)
-		}
-		if longOK && gotlong != test.long {
-			t.Errorf("mkversion.sh long %q: got %q, want %q", test.describe, gotlong, test.long)
-		}
-		if shortOK && gotshort != test.short {
-			t.Errorf("mkversion.sh short %q: got %q, want %q", test.describe, gotshort, test.short)
+		want := strings.ReplaceAll(strings.TrimSpace(test.want), " ", "")
+		got, ok := mkversion(t, test.gitHash, test.otherHash, test.major, test.minor, test.patch, test.changeCount)
+		invoc := fmt.Sprintf("version.sh %s %s %d %d %d %d", test.gitHash, test.otherHash, test.major, test.minor, test.patch, test.changeCount)
+		if want == "" && ok {
+			t.Errorf("%s ok=true, want false", invoc)
+			continue
 		}
-		if xcodeOK && gotxcode != test.xcode {
-			t.Errorf("mkversion.sh xcode %q: got %q, want %q", test.describe, gotxcode, test.xcode)
+		if diff := cmp.Diff(got, want); want != "" && diff != "" {
+			t.Errorf("%s wrong output (-got+want):\n%s", invoc, diff)
 		}
 	}
 }

+ 0 - 5
version/short.txt.do

@@ -1,5 +0,0 @@
-redo-ifchange mkversion.sh describe.txt extragitcommit.txt
-read -r describe <describe.txt
-read -r other <extragitcommit.txt
-ver=$(./mkversion.sh short "$describe" "$other")
-echo "$ver" >$3

+ 6 - 9
version/ver.go.do

@@ -1,12 +1,9 @@
-redo-ifchange long.txt short.txt gitcommit.txt extragitcommit.txt ver.go.in
+redo-ifchange version-info.sh ver.go.in
 
-read -r LONGVER <long.txt
-read -r SHORTVER <short.txt
-read -r GITCOMMIT <gitcommit.txt
-read -r EXTRAGITCOMMIT <extragitcommit.txt
+. ./version-info.sh
 
-sed -e "s/{LONGVER}/$LONGVER/g" \
-    -e "s/{SHORTVER}/$SHORTVER/g" \
-    -e "s/{GITCOMMIT}/$GITCOMMIT/g" \
-    -e "s/{EXTRAGITCOMMIT}/$EXTRAGITCOMMIT/g" \
+sed -e "s/{LONGVER}/$VERSION_LONG/g" \
+    -e "s/{SHORTVER}/$VERSION_SHORT/g" \
+    -e "s/{GITCOMMIT}/$VERSION_GIT_HASH/g" \
+    -e "s/{EXTRAGITCOMMIT}/$VERSION_EXTRA_HASH/g" \
     <ver.go.in >$3

+ 1 - 1
version/describe.txt.do → version/version-info.sh.do

@@ -1,3 +1,3 @@
-./describe.sh >$3
+./version.sh ../.. >$3
 redo-always
 redo-stamp <$3

+ 7 - 15
version/version.h.do

@@ -1,17 +1,9 @@
-redo-ifchange long.txt short.txt
-read -r long <long.txt
-read -r short <short.txt
+redo-ifchange version-info.sh
 
-# get it into "major.minor.patch" format
-ver=$(echo "$ver" | sed -e 's/-/./')
+. ./version-info.sh
 
-# winres is the MAJOR,MINOR,BUILD,REVISION 4-tuple used to identify
-# the version of Windows binaries. We always set REVISION to 0, which
-# seems to be how you map SemVer.
-winres=$(echo "$short,0" | sed -e 's/\./,/g')
-
-(
-	printf '#define TAILSCALE_VERSION_LONG "%s"\n' "$long"
-	printf '#define TAILSCALE_VERSION_SHORT "%s"\n' "$short"
-	printf '#define TAILSCALE_VERSION_WIN_RES %s\n' "$winres"
-) >$3
+cat >$3 <<EOF
+#define TAILSCALE_VERSION_LONG "$VERSION_LONG"
+#define TAILSCALE_VERSION_SHORT "$VERSION_SHORT"
+#define TAILSCALE_VERSION_WIN_RES $VERSION_WINRES
+EOF

+ 109 - 0
version/version.sh

@@ -0,0 +1,109 @@
+#!/bin/sh
+
+set -eu
+
+case $# in
+    0|1)
+        # extra_hash describes a git repository other than the current
+        # one. It gets embedded as an additional commit hash in built
+        # binaries, to help us locate the exact set of tools and code
+        # that were used.
+        extra_hash="${1:-}"
+        if [ -z "$extra_hash" ]; then
+            # Nothing, empty extra hash is fine.
+            extra_hash=""
+        elif [ -d "$extra_hash/.git" ]; then
+            extra_hash=$(cd "$extra_hash" && git describe --always --dirty --exclude '*' --abbrev=200)
+        elif ! expr "$extra_hash" : "^[0-9a-f]*$"; then
+            echo "Invalid extra hash '$extra_hash', must be a git commit hash or path to a git repo" >&2
+            exit 1
+        fi
+
+        # Load the base version and optional corresponding git hash
+        # from the VERSION file. If there is no git hash in the file,
+        # we use the hash of the last change to the VERSION file.
+        version_file="$(dirname $0)/../VERSION"
+        IFS=".$IFS" read -r major minor patch base_git_hash <"$version_file"
+        if [ -z "$base_git_hash" ]; then
+            base_git_hash=$(git rev-list --max-count=1 HEAD -- $version_file)
+        fi
+
+        # The full git has we're currently building at. --abbrev=200 is an
+        # arbitrary large number larger than all currently-known hashes, so
+        # that git displays the full commit hash.
+        git_hash=$(git describe --always --dirty --exclude '*' --abbrev=200)
+        # The number of extra commits between the release base to git_hash.
+        change_count=$(git rev-list ${base_git_hash}..HEAD | wc -l)
+        ;;
+    6)
+        # Test mode: rather than run git commands and whatnot, take in
+        # all the version pieces as arguments.
+        git_hash=$1
+        extra_hash=$2
+        major=$3
+        minor=$4
+        patch=$5
+        change_count=$6
+        ;;
+    *)
+        echo "Usage: $0 [extra-git-hash-or-checkout]"
+        exit 1
+esac
+
+# Shortened versions of git hashes, so that they fit neatly into an
+# "elongated" but still human-readable version number.
+short_git_hash=$(echo $git_hash | cut -c-9)
+short_extra_hash=$(echo $extra_hash | cut -c-9)
+
+# Convert major/minor/patch/change_count into an adjusted
+# major/minor/patch. This block is where all our policies on
+# versioning are.
+if expr "$minor" : "[0-9]*[13579]$" >/dev/null; then
+    # Odd minor numbers are unstable builds.
+    if [ "$patch" != "0" ]; then
+        # This is a fatal error, because a non-zero patch number
+        # indicates that we created an unstable git tag in violation
+        # of our versioning policy, and we want to blow up loudly to
+        # get that fixed.
+        echo "Unstable release $major.$minor.$patch has a non-zero patch number, which is not allowed" >&2
+        exit 1
+    fi
+    patch="$change_count"
+elif [ "$change_count" != "0" ]; then
+    # Even minor numbers are stable builds, but stable builds are
+    # supposed to have a zero change count. Therefore, we're currently
+    # describing a commit that's on a release branch, but hasn't been
+    # tagged as a patch release yet. We allow these commits to build
+    # for testing purposes, but force their version number to 0.0.0,
+    # to reflect that they're an unreleasable build. The git hashes
+    # still completely describe the build commit, so we can still
+    # figure out what this build is if it escapes into the wild.
+    major="0"
+    minor="0"
+    patch="0"
+fi
+
+# Hack for 1.1: add 1000 to the patch number. We switched from using
+# the proprietary repo's change_count over to using the OSS repo's
+# change_count, and this was necessary to avoid a backwards jump in
+# release numbers.
+if [ "$major.$minor" = "1.1" ]; then
+    patch="$((patch + 1000))"
+fi
+
+# At this point, the version number correctly reflects our
+# policies. All that remains is to output the various vars that other
+# code can use to embed version data.
+if [ -z "$extra_hash" ]; then
+    long_version_suffix="-t$short_git_hash"
+else
+    long_version_suffix="-t${short_git_hash}-g${short_extra_hash}"
+fi
+cat <<EOF
+VERSION_SHORT="${major}.${minor}.${patch}"
+VERSION_LONG="${major}.${minor}.${patch}${long_version_suffix}"
+VERSION_GIT_HASH="${git_hash}"
+VERSION_EXTRA_HASH="${extra_hash}"
+VERSION_XCODE="$((major + 100)).${minor}.${patch}"
+VERSION_WINRES="${major},${minor},${patch},0"
+EOF

+ 14 - 5
version/version.xcconfig.do

@@ -1,5 +1,14 @@
-redo-ifchange mkversion.sh describe.txt extragitcommit.txt
-read -r describe <describe.txt
-read -r other <extragitcommit.txt
-ver=$(./mkversion.sh xcode "$describe" "$other")
-echo "$ver" >$3
+redo-ifchange version-info.sh
+
+. ./version-info.sh
+
+# CFBundleShortVersionString: the "short name" used in the App Store.
+# eg. 0.92.98
+echo "VERSION_NAME = $VERSION_SHORT"
+# CFBundleVersion: the build number. Needs to be 3 numeric sections
+# that increment for each release according to SemVer rules.
+#
+# We start counting at 100 because we submitted using raw build
+# numbers before, and Apple doesn't let you start over.  e.g. 0.98.3
+# -> 100.98.3
+echo "VERSION_ID = $VERSION_XCODE"