git-release 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +150 -0
- data/Rakefile +1 -0
- data/bin/git-release +135 -0
- data/bin/git-release-deployed +140 -0
- data/git-release.gemspec +23 -0
- data/lib/git-release.rb +1 -0
- data/lib/git_release/version.rb +3 -0
- data/support/changelog-functions.sh +200 -0
- data/support/git-functions.sh +78 -0
- data/support/git-release-functions.sh +125 -0
- data/support/language-bash.sh +30 -0
- data/support/support-functions.sh +10 -0
- data/test/bin/run_all +7 -0
- data/test/integration/releaseable-deployed-test.sh +359 -0
- data/test/integration/releaseable-test.sh +409 -0
- data/test/support/roundup +310 -0
- data/test/test_helper.sh +95 -0
- data/test/unit/changelog-functions-test.sh +440 -0
- data/test/unit/git-functions-test.sh +215 -0
- data/test/unit/releaseable-test.sh +185 -0
- metadata +112 -0
data/git-release.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'git_release/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "git-release"
|
8
|
+
spec.version = GitRelease::VERSION
|
9
|
+
spec.authors = ["Tom Meier"]
|
10
|
+
spec.email = ["tom@venombytes.com"]
|
11
|
+
spec.description = %q{Changelog and release tag generator from GIT}
|
12
|
+
spec.summary = %q{Generate changelog from git commits and tag your release versions}
|
13
|
+
spec.homepage = "http://github.com/tommeier/git-release"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/lib/git-release.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "git_release/version"
|
@@ -0,0 +1,200 @@
|
|
1
|
+
#!/bin/bash -e
|
2
|
+
|
3
|
+
############################################################
|
4
|
+
##### CHANGELOG FUNCTIONS #####
|
5
|
+
############################################################
|
6
|
+
|
7
|
+
get_current_release_date() {
|
8
|
+
echo $( date "+%A %d %B, %Y %l:%M%p" )
|
9
|
+
}
|
10
|
+
|
11
|
+
changelog_divider() {
|
12
|
+
echo "+=========================================================+"
|
13
|
+
}
|
14
|
+
|
15
|
+
changelog_footer() {
|
16
|
+
local output=""
|
17
|
+
! read -d '' output <<"EOF"
|
18
|
+
|| _____ _ _ ||
|
19
|
+
|| / ____| | | | ||
|
20
|
+
|| | | | |__ __ _ _ __ __ _ ___| | ___ __ _ ||
|
21
|
+
|| | | | '_ \\ / _` | '_ \\ / _` |/ _ \\ |/ _ \\ / _` | ||
|
22
|
+
|| | |____| | | | (_| | | | | (_| | __/ | (_) | (_| | ||
|
23
|
+
|| \\_____|_| |_|\\__,_|_| |_|\\__, |\\___|_|\\___/ \\__, | ||
|
24
|
+
|| __/ | __/ | ||
|
25
|
+
|| |___/ |___/ ||
|
26
|
+
|| ||
|
27
|
+
EOF
|
28
|
+
echo "$(changelog_divider)
|
29
|
+
$output
|
30
|
+
$(changelog_divider)"
|
31
|
+
}
|
32
|
+
|
33
|
+
get_changelog_text_for_commits() {
|
34
|
+
#Pass in commits array of SHA's
|
35
|
+
#Return formatted changelog text, with tags handled
|
36
|
+
#Optional first argument for the format "--format=%H"
|
37
|
+
local previous_shopt_extglob=$(shopt -p extglob)
|
38
|
+
local existing_shopt_nocasematch=$(shopt -p nocasematch)
|
39
|
+
shopt -s nocasematch
|
40
|
+
shopt -s extglob
|
41
|
+
|
42
|
+
local commit_shas=($@)
|
43
|
+
|
44
|
+
local feature_tag_lines=""
|
45
|
+
local bug_tag_lines=""
|
46
|
+
local security_tag_lines=""
|
47
|
+
local general_release_lines=""
|
48
|
+
|
49
|
+
local log_format="--format=%s"
|
50
|
+
local log_format_matcher="\-\-format\="
|
51
|
+
|
52
|
+
#Capture line by line unless first argument provides a custom format
|
53
|
+
for i in "${!commit_shas[@]}"; do
|
54
|
+
if [[ "${i}" = '0' ]]; then
|
55
|
+
if echo "${commit_shas[$i]}" | grep -q "${log_format_matcher}"; then
|
56
|
+
log_format="${commit_shas[$i]}";
|
57
|
+
continue;
|
58
|
+
fi;
|
59
|
+
fi;
|
60
|
+
|
61
|
+
local body_result="`git show -s ${log_format} ${commit_shas[$i]}`"
|
62
|
+
local newline=$'\n'
|
63
|
+
regex="^\s*\[(features?|bugs?|security)\]\s*(.*)\s*$"
|
64
|
+
if [[ $body_result =~ $regex ]]; then
|
65
|
+
#Tagged entry
|
66
|
+
local full_tag=$BASH_REMATCH
|
67
|
+
local tag_type="${BASH_REMATCH[1]}"
|
68
|
+
#Remove leading spaces (regex in bash capturing always)
|
69
|
+
local tag_content="${BASH_REMATCH[2]##*( )}"
|
70
|
+
#Add leading 2 spaces with bullet point for tagged line prefix & remove trailing spaces
|
71
|
+
tag_content=" ${tag_content%%*( )}${newline}"
|
72
|
+
#Sort matching tags
|
73
|
+
case "$tag_type" in
|
74
|
+
[fF][eE][aA][tT][uU][rR][eE] | [fF][eE][aA][tT][uU][rR][eE][sS] )
|
75
|
+
feature_tag_lines+="${tag_content}";;
|
76
|
+
[bB][uU][gG] | [bB][uU][gG][sS] )
|
77
|
+
bug_tag_lines+="$tag_content";;
|
78
|
+
[sS][eE][cC][uU][rR][iI][tT][yY] )
|
79
|
+
security_tag_lines+="$tag_content";;
|
80
|
+
* )
|
81
|
+
general_release_lines+="$tag_content";;
|
82
|
+
esac;
|
83
|
+
else
|
84
|
+
#Normal entry
|
85
|
+
general_release_lines+="$body_result${newline}"
|
86
|
+
fi;
|
87
|
+
done;
|
88
|
+
|
89
|
+
#Return previous setup for bash
|
90
|
+
eval $previous_shopt_extglob
|
91
|
+
eval $existing_shopt_nocasematch
|
92
|
+
|
93
|
+
if [[ $feature_tag_lines != '' ]]; then
|
94
|
+
echo "Features:
|
95
|
+
${feature_tag_lines}"
|
96
|
+
fi;
|
97
|
+
if [[ $security_tag_lines != '' ]]; then
|
98
|
+
echo "Security:
|
99
|
+
${security_tag_lines}"
|
100
|
+
fi;
|
101
|
+
if [[ $bug_tag_lines != '' ]]; then
|
102
|
+
echo "Bugs:
|
103
|
+
${bug_tag_lines}"
|
104
|
+
fi;
|
105
|
+
echo "$general_release_lines"
|
106
|
+
}
|
107
|
+
|
108
|
+
#generate_changelog_content "$last_tag_name" "$next_tag_name" ":all/:pulls_only"
|
109
|
+
generate_changelog_content() {
|
110
|
+
local release_name="$1"
|
111
|
+
local commit_filter="$2" #all_commits or pulls_only
|
112
|
+
local starting_point="$3" #optional
|
113
|
+
local end_point="$4" #optional
|
114
|
+
local changelog_format="--format=%s" #default -> display title
|
115
|
+
|
116
|
+
if [[ "$release_name" = "" ]]; then
|
117
|
+
echo "Error : Release name required for changelog generation."
|
118
|
+
exit 1;
|
119
|
+
fi;
|
120
|
+
|
121
|
+
case "$commit_filter" in
|
122
|
+
':all_commits' | 'all_commits' )
|
123
|
+
#No filter
|
124
|
+
commit_filter='';;
|
125
|
+
':pulls_only' | 'pulls_only' )
|
126
|
+
#Filter by merged pull requests only
|
127
|
+
commit_filter=$'^Merge pull request #.* from .*';
|
128
|
+
changelog_format="--format=%b";; #use body of pull requests
|
129
|
+
* )
|
130
|
+
echo "Error : Commit filter required. Please specify :all or :pulls_only."
|
131
|
+
exit 1;;
|
132
|
+
esac
|
133
|
+
|
134
|
+
local commits=$(get_commits_between_points "$starting_point" "$end_point" "$commit_filter")
|
135
|
+
local commit_output=$(get_changelog_text_for_commits "$changelog_format" $commits)
|
136
|
+
local release_date=$(get_current_release_date)
|
137
|
+
|
138
|
+
echo "$(changelog_divider)
|
139
|
+
|| Release: ${release_name}
|
140
|
+
|| Released on ${release_date}
|
141
|
+
$(changelog_divider)
|
142
|
+
${commit_output}
|
143
|
+
$(changelog_divider)"
|
144
|
+
}
|
145
|
+
|
146
|
+
#generate_version_file "$version_number" "$optional_file_name"
|
147
|
+
generate_version_file(){
|
148
|
+
local version_number="$1"
|
149
|
+
if [[ "$version_number" = "" ]]; then
|
150
|
+
echo "Error : Version number required for version file generation."
|
151
|
+
exit 1;
|
152
|
+
fi;
|
153
|
+
if [[ "$2" != '' ]]; then
|
154
|
+
local version_file="$2"
|
155
|
+
else
|
156
|
+
local version_file="VERSION" #optional
|
157
|
+
fi;
|
158
|
+
|
159
|
+
touch $version_file
|
160
|
+
echo "${version_number}" > $version_file
|
161
|
+
}
|
162
|
+
|
163
|
+
#generate_changelog_file "$changelog_content" ":overwrite/:append" "$optional_file_name"
|
164
|
+
generate_changelog_file(){
|
165
|
+
local changelog_content="$1"
|
166
|
+
local generate_strategy="$2"
|
167
|
+
|
168
|
+
if [[ "$changelog_content" = "" ]]; then
|
169
|
+
echo "Error : Changelog content required for version file generation."
|
170
|
+
exit 1;
|
171
|
+
fi;
|
172
|
+
if [[ "$3" != '' ]]; then
|
173
|
+
local changelog_file="$3"
|
174
|
+
else
|
175
|
+
local changelog_file="CHANGELOG" #optional
|
176
|
+
fi;
|
177
|
+
|
178
|
+
case "$generate_strategy" in
|
179
|
+
':overwrite' | 'overwrite' )
|
180
|
+
#Overwrite;
|
181
|
+
echo "$changelog_content
|
182
|
+
$(changelog_footer)" > $changelog_file;;
|
183
|
+
':append' | 'append' )
|
184
|
+
if [[ ! -f $changelog_file ]]; then
|
185
|
+
#Initialise new file
|
186
|
+
touch $changelog_file;
|
187
|
+
echo "$changelog_content
|
188
|
+
$(changelog_footer)" > $changelog_file
|
189
|
+
else
|
190
|
+
#Append to start of file
|
191
|
+
local existing_content=$(cat $changelog_file);
|
192
|
+
|
193
|
+
echo "$changelog_content
|
194
|
+
$existing_content" > $changelog_file;
|
195
|
+
fi;;
|
196
|
+
* )
|
197
|
+
echo "Error : Generate strategy required. Please specify :overwrite or :append."
|
198
|
+
exit 1;;
|
199
|
+
esac
|
200
|
+
}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/bin/bash -e
|
2
|
+
|
3
|
+
############################################################
|
4
|
+
##### GIT FUNCTIONS #####
|
5
|
+
############################################################
|
6
|
+
|
7
|
+
check_tag_exists() {
|
8
|
+
local tag_find=$(git tag -l "$1")
|
9
|
+
if [[ "$tag_find" = '' ]]; then
|
10
|
+
return 1;
|
11
|
+
else
|
12
|
+
return 0;
|
13
|
+
fi;
|
14
|
+
}
|
15
|
+
|
16
|
+
ensure_git_directory() {
|
17
|
+
if [[ ! -d '.git' ]]; then
|
18
|
+
echo "Error - Not a git repository please run from the base of your git repo." >&2
|
19
|
+
exit 1
|
20
|
+
fi;
|
21
|
+
}
|
22
|
+
|
23
|
+
ensure_git_is_clean() {
|
24
|
+
local result=$(git status --porcelain)
|
25
|
+
|
26
|
+
if [[ "$result" != '' ]]; then
|
27
|
+
result=$(git status)
|
28
|
+
echo "Error - Current branch is in a dirty state, please commit your changes first."
|
29
|
+
echo "$result"
|
30
|
+
exit 1
|
31
|
+
fi;
|
32
|
+
}
|
33
|
+
|
34
|
+
get_sha_for_tag_name() {
|
35
|
+
local result=$(git show-ref --tags --hash $1)
|
36
|
+
echo "$result"
|
37
|
+
}
|
38
|
+
|
39
|
+
get_sha_for_first_commit() {
|
40
|
+
local filter=$1
|
41
|
+
local result=$(git log --reverse --format="%H" $filter | head -1)
|
42
|
+
echo "$result"
|
43
|
+
}
|
44
|
+
|
45
|
+
get_commit_message_for_first_commit() {
|
46
|
+
local filter=$1
|
47
|
+
local result=$(git log --reverse --format="%s" $filter | head -1)
|
48
|
+
echo "$result"
|
49
|
+
}
|
50
|
+
|
51
|
+
get_commit_message_for_latest_commit() {
|
52
|
+
local filter=$1
|
53
|
+
local result=$(git log -n1 --format="%s" $filter)
|
54
|
+
echo "$result"
|
55
|
+
}
|
56
|
+
|
57
|
+
get_commits_between_points() {
|
58
|
+
local starting_point="$1" #optional
|
59
|
+
local end_point="$2" #optional
|
60
|
+
local log_filter="$3" #optional
|
61
|
+
|
62
|
+
local git_command="git log";
|
63
|
+
local log_options="--no-notes --format=%H"
|
64
|
+
local git_range=""
|
65
|
+
|
66
|
+
if [[ "$log_filter" != '' ]]; then
|
67
|
+
log_options="$log_options --grep="'"'"$log_filter"'"'""
|
68
|
+
fi;
|
69
|
+
if [[ "$starting_point" != '' && "$end_point" != '' ]]; then
|
70
|
+
git_range="${starting_point}^1..${end_point}";
|
71
|
+
elif [[ "$end_point" != '' ]]; then
|
72
|
+
git_range="${end_point}"
|
73
|
+
elif [[ "$starting_point" != '' ]]; then
|
74
|
+
git_range="${starting_point}^1..HEAD"
|
75
|
+
fi;
|
76
|
+
|
77
|
+
eval $git_command $log_options $git_range
|
78
|
+
}
|
@@ -0,0 +1,125 @@
|
|
1
|
+
#!/bin/bash -e
|
2
|
+
|
3
|
+
############################################################
|
4
|
+
##### GLOBALS #####
|
5
|
+
############################################################
|
6
|
+
|
7
|
+
TAG_VERSION_NUMBER_REGEX="([0-9]+)\\.([0-9]+)\\.([0-9]+)$"
|
8
|
+
|
9
|
+
############################################################
|
10
|
+
##### SUPPORT FUNCTIONS #####
|
11
|
+
############################################################
|
12
|
+
|
13
|
+
validate_version_type() {
|
14
|
+
#Confirm version type is in the accepted types
|
15
|
+
local v="$1"
|
16
|
+
local error_output="$2"
|
17
|
+
|
18
|
+
if [[ $v != "major" && $v != 'minor' && $v != 'patch' ]]; then
|
19
|
+
printf "incorrect versioning type: '%s'\\n" "$v" >&2
|
20
|
+
echo "Please set to one of 'major', 'minor' or 'patch'" >&2
|
21
|
+
echo "$error_output" >&2
|
22
|
+
exit 1
|
23
|
+
fi;
|
24
|
+
}
|
25
|
+
|
26
|
+
#git-release-deployed only
|
27
|
+
validate_deploy_tag() {
|
28
|
+
local t="$1"
|
29
|
+
local error_output="$2"
|
30
|
+
|
31
|
+
if [[ "$t" = '' ]]; then
|
32
|
+
echo "Required parameter: Please enter the deploy tag released."
|
33
|
+
echo "$error_output"
|
34
|
+
exit 1;
|
35
|
+
elif [[ "$(git tag -l $t )" = '' ]]; then
|
36
|
+
echo "Error: Unable to find tag '${t}'. Please check and try again."
|
37
|
+
exit 1;
|
38
|
+
fi;
|
39
|
+
}
|
40
|
+
|
41
|
+
############################################################
|
42
|
+
##### TAG FUNCTIONS #####
|
43
|
+
############################################################
|
44
|
+
|
45
|
+
get_release_tags() {
|
46
|
+
local filter=""
|
47
|
+
local tag_names=""
|
48
|
+
|
49
|
+
if [[ $1 ]]; then
|
50
|
+
local tag_pattern=$1
|
51
|
+
filter="${tag_pattern}*"
|
52
|
+
fi;
|
53
|
+
tag_names=$(git tag -l $filter)
|
54
|
+
|
55
|
+
#<ref> tags/<release_prefix><version_number>
|
56
|
+
echo "$tag_names"
|
57
|
+
}
|
58
|
+
|
59
|
+
get_last_tag_name() {
|
60
|
+
local versioning_prefix=$1
|
61
|
+
|
62
|
+
local tags=$(get_release_tags $versioning_prefix)
|
63
|
+
echo "$tags" | tail -1
|
64
|
+
}
|
65
|
+
|
66
|
+
get_versioning_prefix_from_tag() {
|
67
|
+
local tag_name="$1"
|
68
|
+
if [[ $tag_name =~ $TAG_VERSION_NUMBER_REGEX ]]; then
|
69
|
+
local version_number=$BASH_REMATCH
|
70
|
+
local version_prefix="${tag_name%%$version_number}"
|
71
|
+
else
|
72
|
+
echo "Error : Unable to determine version prefix from '${tag_name}'"
|
73
|
+
exit 1;
|
74
|
+
fi;
|
75
|
+
echo "${version_prefix}"
|
76
|
+
}
|
77
|
+
|
78
|
+
get_version_number_from_tag() {
|
79
|
+
local tag_name="$1"
|
80
|
+
if [[ $tag_name =~ $TAG_VERSION_NUMBER_REGEX ]]; then
|
81
|
+
local full_version=$BASH_REMATCH
|
82
|
+
else
|
83
|
+
echo "Error : Unable to determine version number from '${tag_name}'"
|
84
|
+
exit 1;
|
85
|
+
fi;
|
86
|
+
#full stop delimited version number
|
87
|
+
echo "$full_version"
|
88
|
+
}
|
89
|
+
|
90
|
+
get_next_version_number_from_tag() {
|
91
|
+
local versioning_type=$1
|
92
|
+
local tag_name=$2
|
93
|
+
|
94
|
+
if [[ "$versioning_type" = "" ]]; then
|
95
|
+
echo "Error : Versioning type required. eg. major"
|
96
|
+
exit 1;
|
97
|
+
fi;
|
98
|
+
|
99
|
+
if [[ $tag_name = '' ]]; then
|
100
|
+
#No original tag name for version prefix - start increment
|
101
|
+
local tag_name="0.0.0"
|
102
|
+
fi;
|
103
|
+
|
104
|
+
if [[ $tag_name =~ $TAG_VERSION_NUMBER_REGEX ]]; then
|
105
|
+
local full_version=$BASH_REMATCH
|
106
|
+
local major_version="${BASH_REMATCH[1]}"
|
107
|
+
local minor_version="${BASH_REMATCH[2]}"
|
108
|
+
local patch_version="${BASH_REMATCH[3]}"
|
109
|
+
else
|
110
|
+
echo "Error : Unable to determine version number from '${tag_name}'"
|
111
|
+
exit 1;
|
112
|
+
fi;
|
113
|
+
|
114
|
+
#Increment version
|
115
|
+
case "$versioning_type" in
|
116
|
+
'major' )
|
117
|
+
major_version=$(( $major_version + 1 ));;
|
118
|
+
'minor' )
|
119
|
+
minor_version=$(( $minor_version + 1 ));;
|
120
|
+
'patch' )
|
121
|
+
patch_version=$(( $patch_version + 1 ));;
|
122
|
+
esac
|
123
|
+
|
124
|
+
echo "${major_version}.${minor_version}.${patch_version}"
|
125
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/bin/sh -e
|
2
|
+
|
3
|
+
#Language specific naming, to be overridden by whichever language is being used
|
4
|
+
|
5
|
+
ARG_ASSIGNER=" " #Argument assigner. space or = for assigning arguments
|
6
|
+
ARG_PREFIX="-" #Argument prefix (used in help text display such as '-' for '-v')
|
7
|
+
#Arguments for running script
|
8
|
+
ARG_VERSION="v"
|
9
|
+
ARG_DEPLOYED_TAG="d"
|
10
|
+
ARG_RELEASE_PREFIX="p"
|
11
|
+
ARG_START="s"
|
12
|
+
ARG_FINISH="f"
|
13
|
+
ARG_APPEND="A"
|
14
|
+
ARG_PULL_REQUESTS="P"
|
15
|
+
ARG_CHANGELOG="C"
|
16
|
+
ARG_VERSION_FILE="V"
|
17
|
+
ARG_FORCE="F"
|
18
|
+
ARG_HELP_TEXT="h"
|
19
|
+
|
20
|
+
#Display arg with language specific assigner and prefix
|
21
|
+
arg_for() {
|
22
|
+
local arg="$1"
|
23
|
+
local val="$2"
|
24
|
+
if [[ $val != '' ]]; then
|
25
|
+
echo "${ARG_PREFIX}${1}${ARG_ASSIGNER}${2}"
|
26
|
+
else
|
27
|
+
echo "${ARG_PREFIX}${1}"
|
28
|
+
fi;
|
29
|
+
}
|
30
|
+
|