Skip to content

Commit 1ca17f6

Browse files
author
ffranr
authored
Merge pull request #720 from lightninglabs/validate-release-version
makefile: add command to generate git release tags
2 parents eb3ed93 + 4d6aa3a commit 1ca17f6

File tree

6 files changed

+180
-51
lines changed

6 files changed

+180
-51
lines changed

.github/workflows/release.yaml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,28 @@ jobs:
2222
with:
2323
fetch-depth: 0
2424

25+
- name: Set env
26+
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
27+
28+
- name: Validate release tag ${{ env.RELEASE_VERSION }}
29+
run: |
30+
expected_tag=$(./scripts/get-git-tag-name.sh version.go)
31+
actual_tag=${{ env.RELEASE_VERSION }}
32+
33+
if [ "$actual_tag" = "$expected_tag" ]; then
34+
echo "Git tag release string is as expected."
35+
else
36+
echo "Error: Versions are not equal. Actual: $actual_tag, Expected: $expected_tag"
37+
exit 1
38+
fi
39+
2540
- name: setup go ${{ env.GO_VERSION }}
2641
uses: actions/setup-go@v2
2742
with:
2843
go-version: '${{ env.GO_VERSION }}'
2944

30-
- name: Set env
31-
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
32-
3345
- name: build release for all architectures
34-
run: SKIP_VERSION_CHECK=1 make release tag=${{ env.RELEASE_VERSION }}
46+
run: make release tag=${{ env.RELEASE_VERSION }}
3547

3648
- name: Create Release
3749
uses: lightninglabs/gh-actions/[email protected]

Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ GOACC_BIN := $(GO_BIN)/go-acc
1111
GOIMPORTS_BIN := $(GO_BIN)/gosimports
1212
MIGRATE_BIN := $(GO_BIN)/migrate
1313

14+
# VERSION_GO_FILE is the golang file which defines the current project version.
15+
VERSION_GO_FILE := "version.go"
16+
1417
COMMIT := $(shell git describe --tags --dirty)
1518

1619
GOBUILD := GOEXPERIMENT=loopvar GO111MODULE=on go build -v
@@ -115,6 +118,17 @@ release:
115118
$(VERSION_CHECK)
116119
./scripts/release.sh build-release "$(VERSION_TAG)" "$(BUILD_SYSTEM)" "$(RELEASE_TAGS)" "$(RELEASE_LDFLAGS)"
117120

121+
release-tag:
122+
@$(call print, "Adding release tag.")
123+
124+
tag=$$(./scripts/get-git-tag-name.sh ${VERSION_GO_FILE}); \
125+
exit_status=$$?; \
126+
if [ $$exit_status -ne 0 ]; then \
127+
echo "Script encountered an error with exit status $$exit_status."; \
128+
fi; \
129+
echo "Adding git tag: $$tag"; \
130+
git tag -as -m "Tag generated using command \`make release-tag\`." "$$tag";
131+
118132
docker-release:
119133
@$(call print, "Building release helper docker image.")
120134
if [ "$(tag)" = "" ]; then echo "Must specify tag=<commit_or_tag>!"; exit 1; fi

make/release_flags.mk

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1+
# One can either specify a git tag as the version suffix or one that is
2+
# generated from the current date.
13
VERSION_TAG = $(shell date +%Y%m%d)-01
24
VERSION_CHECK = @$(call print, "Building master with date version tag")
35

6+
ifneq ($(tag),)
7+
VERSION_TAG = $(tag)
8+
VERSION_CHECK = ./scripts/release.sh check-tag "$(VERSION_TAG)" "$(VERSION_GO_FILE)"
9+
endif
10+
411
DOCKER_RELEASE_HELPER = docker run \
512
-it \
613
--rm \
@@ -22,13 +29,6 @@ windows-amd64
2229

2330
RELEASE_TAGS = monitoring
2431

25-
# One can either specify a git tag as the version suffix or one is generated
26-
# from the current date.
27-
ifneq ($(tag),)
28-
VERSION_TAG = $(tag)
29-
VERSION_CHECK = ./scripts/release.sh check-tag "$(VERSION_TAG)"
30-
endif
31-
3232
# By default we will build all systems. But with the 'sys' tag, a specific
3333
# system can be specified. This is useful to release for a subset of
3434
# systems/architectures.

scripts/get-git-tag-name.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/bin/bash
2+
3+
# This script derives a git tag name from the version fields found in a given Go
4+
# file. It also checks if the derived git tag name is a valid SemVer compliant
5+
# version string.
6+
7+
# get_git_tag_name reads the version fields from the given file and then
8+
# constructs and returns a git tag name.
9+
get_git_tag_name() {
10+
local file_path="$1"
11+
12+
# Check if the file exists
13+
if [ ! -f "$file_path" ]; then
14+
echo "Error: File not found at $file_path" >&2
15+
exit 1
16+
fi
17+
18+
# Read and parse the version fields. We interpret these fields using regex
19+
# matching which effectively serves as a basic sanity check.
20+
local app_major
21+
app_major=$(grep -oP 'AppMajor\s*uint\s*=\s*\K\d+' "$file_path")
22+
23+
local app_minor
24+
app_minor=$(grep -oP 'AppMinor\s*uint\s*=\s*\K\d+' "$file_path")
25+
26+
local app_patch
27+
app_patch=$(grep -oP 'AppPatch\s*uint\s*=\s*\K\d+' "$file_path")
28+
29+
local app_status
30+
app_status=$(grep -oP 'AppStatus\s*=\s*"\K([a-z]*)' "$file_path")
31+
32+
local app_pre_release
33+
app_pre_release=$(grep -oP 'AppPreRelease\s*=\s*"\K([a-z0-9]*)' "$file_path")
34+
35+
# Parse the GitTagIncludeStatus field.
36+
local git_tag_include_status
37+
git_tag_include_status=false
38+
39+
if grep -q 'GitTagIncludeStatus = true' "$file_path"; then
40+
git_tag_include_status=true
41+
elif grep -q 'GitTagIncludeStatus = false' "$file_path"; then
42+
git_tag_include_status=false
43+
else
44+
echo "Error: GitTagIncludeStatus is not present in the Go version file."
45+
exit 1
46+
fi
47+
48+
# Construct the git tag name with conditional inclusion of app_status and
49+
# app_pre_release.
50+
tag_name="v${app_major}.${app_minor}.${app_patch}"
51+
52+
# Append app_status if git_tag_include_status is true and app_status if
53+
# specified.
54+
if [ "$git_tag_include_status" = true ] && [ -n "$app_status" ]; then
55+
tag_name+="-${app_status}"
56+
57+
# Append app_pre_release if specified.
58+
if [ -n "$app_pre_release" ]; then
59+
tag_name+=".${app_pre_release}"
60+
fi
61+
else
62+
# If the app_status field is not specified, then append
63+
# app_pre_release (if specified) using a dash prefix.
64+
if [ -n "$app_pre_release" ]; then
65+
tag_name+="-${app_pre_release}"
66+
fi
67+
fi
68+
69+
echo "$tag_name"
70+
}
71+
72+
file_path="$1"
73+
echo "Reading version fields from file: $file_path" >&2
74+
tag_name=$(get_git_tag_name "$file_path")
75+
echo "Derived git tag name: $tag_name" >&2
76+
77+
echo "$tag_name"

scripts/release.sh

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,10 @@ function red() {
8080

8181
# check_tag_correct makes sure the given git tag is checked out and the git tree
8282
# is not dirty.
83-
# arguments: <version-tag>
83+
# arguments: <version-tag> <version-file-path>
8484
function check_tag_correct() {
8585
local tag=$1
86+
local version_file_path=$2
8687

8788
# For automated builds we can skip this check as they will only be triggered
8889
# on tags.
@@ -102,31 +103,16 @@ function check_tag_correct() {
102103
echo "Tag $tag checked out. Git commit: $commit_hash"
103104
fi
104105

105-
# Build tapd to extract version.
106-
go build ${PKG}/cmd/tapd
106+
# Ensure that the git tag matches the version string derived from the version
107+
# file.
108+
local expected_tag
109+
expected_tag=$(./scripts/get-git-tag-name.sh "$version_file_path")
107110

108-
# Extract version command output.
109-
tapd_version_output=$(./tapd --version)
110-
111-
# Use a regex to isolate the version string.
112-
if [[ $tapd_version_output =~ $TAPD_VERSION_REGEX ]]; then
113-
# Prepend 'v' to match git tag naming scheme.
114-
tapd_version="v${BASH_REMATCH[1]}"
115-
green "version: $tapd_version"
116-
117-
# If the tapd reported version contains a suffix, remove it, so we can match
118-
# the tag properly.
119-
# shellcheck disable=SC2001
120-
tapd_version=$(echo "$tapd_version" | sed -e 's/-\(alpha\|beta\)\(\.rc[0-9]\+\)\?//g')
121-
122-
# Match git tag with tapd version.
123-
if [[ $tag != "${tapd_version}" ]]; then
124-
red "tapd version $tapd_version does not match tag $tag"
125-
exit 1
126-
fi
127-
else
128-
red "malformed tapd version output"
111+
if [[ $tag != "$expected_tag" ]]; then
112+
red "Error: tag $tag does not match git tag version string derived from $version_file_path"
129113
exit 1
114+
else
115+
green "tag $tag matches git tag version string derived from $version_file_path"
130116
fi
131117
}
132118

version.go

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ var (
3131
GoVersion string
3232
)
3333

34-
// semanticAlphabet is the set of characters that are permitted for use in an
35-
// AppPreRelease.
36-
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."
34+
// versionFieldsAlphabet is the set of characters that are permitted for use in
35+
// a version string field.
36+
const versionFieldsAlphabet = "0123456789abcdefghijklmnopqrstuvwxyz"
3737

3838
// These constants define the application version and follow the semantic
3939
// versioning 2.0.0 spec (http://semver.org/).
@@ -47,9 +47,21 @@ const (
4747
// AppPatch defines the application patch for this binary.
4848
AppPatch uint = 2
4949

50-
// AppPreRelease MUST only contain characters from semanticAlphabet
51-
// per the semantic versioning spec.
52-
AppPreRelease = "alpha"
50+
// AppStatus defines the release status of this binary (e.g. beta).
51+
AppStatus = "alpha"
52+
53+
// AppPreRelease defines the pre-release version of this binary.
54+
// It MUST only contain characters from the semantic versioning spec.
55+
AppPreRelease = ""
56+
57+
// GitTagIncludeStatus indicates whether the status should be included
58+
// in the git tag name.
59+
//
60+
// Including the app version status in the git tag may be problematic
61+
// for golang projects when importing them as dependencies. We therefore
62+
// include this flag to allow toggling the status on and off in a
63+
// standardised way across our projects.
64+
GitTagIncludeStatus = false
5365

5466
// defaultAgentName is the default name of the software that is added as
5567
// the first part of the user agent string.
@@ -66,8 +78,10 @@ var agentName = defaultAgentName
6678
// the software tapd is bundled in (for example LiT). This function panics if
6779
// the agent name contains characters outside of the allowed semantic alphabet.
6880
func SetAgentName(newAgentName string) {
81+
agentNameAlphabet := versionFieldsAlphabet + "-. "
82+
6983
for _, r := range newAgentName {
70-
if !strings.ContainsRune(semanticAlphabet, r) {
84+
if !strings.ContainsRune(agentNameAlphabet, r) {
7185
panic(fmt.Errorf("rune: %v is not in the semantic "+
7286
"alphabet", r))
7387
}
@@ -81,7 +95,7 @@ func SetAgentName(newAgentName string) {
8195
func UserAgent(initiator string) string {
8296
// We'll only allow "safe" characters in the initiator portion of the
8397
// user agent string and spaces only if surrounded by other characters.
84-
initiatorAlphabet := semanticAlphabet + ". "
98+
initiatorAlphabet := versionFieldsAlphabet + "-. "
8599
cleanInitiator := normalizeVerString(
86100
strings.TrimSpace(initiator), initiatorAlphabet,
87101
)
@@ -104,12 +118,22 @@ func UserAgent(initiator string) string {
104118
}
105119

106120
func init() {
121+
// Assert that AppStatus is valid according to the semantic versioning
122+
// guidelines for pre-release version and build metadata strings. In
123+
// particular, it MUST only contain characters in versionFieldsAlphabet.
124+
for _, r := range AppStatus {
125+
if !strings.ContainsRune(versionFieldsAlphabet, r) {
126+
panic(fmt.Errorf("rune: %v is not in the semantic "+
127+
"alphabet", r))
128+
}
129+
}
130+
107131
// Assert that AppPreRelease is valid according to the semantic
108132
// versioning guidelines for pre-release version and build metadata
109-
// strings. In particular it MUST only contain characters in
110-
// semanticAlphabet.
133+
// strings. In particular, it MUST only contain characters in
134+
// versionFieldsAlphabet.
111135
for _, r := range AppPreRelease {
112-
if !strings.ContainsRune(semanticAlphabet, r) {
136+
if !strings.ContainsRune(versionFieldsAlphabet, r) {
113137
panic(fmt.Errorf("rune: %v is not in the semantic "+
114138
"alphabet", r))
115139
}
@@ -162,12 +186,28 @@ func semanticVersion() string {
162186
// Start with the major, minor, and patch versions.
163187
version := fmt.Sprintf("%d.%d.%d", AppMajor, AppMinor, AppPatch)
164188

165-
// Append pre-release version if there is one. The hyphen called for
166-
// by the semantic versioning spec is automatically appended and should
167-
// not be contained in the pre-release string. The pre-release version
189+
// If defined, we will now sanitise the release status string. The
190+
// hyphen called for by the semantic versioning spec is automatically
191+
// appended and should not be contained in the status string. The status
168192
// is not appended if it contains invalid characters.
169-
preRelease := normalizeVerString(AppPreRelease, semanticAlphabet)
170-
if preRelease != "" {
193+
appStatus := normalizeVerString(AppStatus, versionFieldsAlphabet)
194+
195+
// If defined, we will now sanitise the pre-release version string. The
196+
// hyphen called for by the semantic versioning spec is automatically
197+
// appended and should not be contained in the pre-release string.
198+
// The pre-release version is not appended if it contains invalid
199+
// characters.
200+
preRelease := normalizeVerString(AppPreRelease, versionFieldsAlphabet)
201+
202+
// Append any status and pre-release strings to the version string.
203+
switch {
204+
case appStatus != "" && preRelease != "":
205+
version = fmt.Sprintf(
206+
"%s-%s.%s", version, appStatus, preRelease,
207+
)
208+
case appStatus != "":
209+
version = fmt.Sprintf("%s-%s", version, appStatus)
210+
case preRelease != "":
171211
version = fmt.Sprintf("%s-%s", version, preRelease)
172212
}
173213

0 commit comments

Comments
 (0)