git-release 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|