From a7e779bb7188750581075ded8121a5b4ab3dbf9d Mon Sep 17 00:00:00 2001 From: Roland Walker Date: Thu, 30 Jan 2014 10:49:00 -0500 Subject: [PATCH] devscript: add command get_release_tag update RELEASING.md accordingly --- RELEASING.md | 60 ++++++++---- developer/bin/get_release_tag | 170 ++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+), 20 deletions(-) create mode 100755 developer/bin/get_release_tag diff --git a/RELEASING.md b/RELEASING.md index 8901f8e3d1..d9ed3e33c7 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -11,35 +11,55 @@ Since we are still pre-1.0, this essentially means that we bump the PATCH number when we fix bugs, and we bump the MINOR number when we add stuff. Pretty simple. +The script `developer/bin/get_release_tag` can tell you the latest release +tag that exists and/or calculate the proposed next release tag. Docs are at +`get_release_tag -help`. + ## Release Process We'll get this scripted someday, but until then it's better to have it written -down then floating in a brain somewhere. +down than floating in a brain somewhere. 1. Do a `git log` since the last release to see what changed. You can scope it to - `lib` to just pick up code changes and filter out Casks noise. - Like this: `git log v0.25.0..HEAD lib` -2. Optionally run `developer/bin/project_stats ` for - overall release stats. -3. Bump the version, which is stored in the file `lib/cask/version.rb`. Decide - whether to bump minor or patch based on whether or not features were added. -4. Populate the CHANGELOG with a new section for the release you are creating. + `lib` to just pick up code changes and filter out Casks noise. Like this: + ```bash + git log $(developer/bin/get_release_tag)..HEAD lib + ``` +2. Decide whether to bump the minor or patch fields in the next tag, based on + whether or not features were added. Run the shell command + ```bash + new_tag=$(developer/bin/get_release_tag -next) # or -next -patch + ``` + and make sure the value in `$new_tag` is what you want. +3. Optionally run `developer/bin/project_stats release` for overall release stats. +4. Bump the `VERSION` string which is stored in the file `lib/cask/version.rb`. + It should match `$new_tag`. +5. Populate the CHANGELOG with a new section for the release you are creating. Follow the patterns used elsewhere in the file. -5. Make a commit containing the CHANGELOG and version-bump. Like this: +6. Make a commit containing the CHANGELOG and version-bump. Like this: ```bash git add CHANGELOG lib/cask/version.rb - git commit -m 'cut v0.26.0' + git commit -m "cut $new_tag" ``` -6. Tag that commit, ensuring that you provide a message so we get an annotated - tag. - Like this: `git tag -m v0.26.0 v0.26.0` -7. Push the commit and the tag: `git push --follow-tags` -8. Open your browser to . - Then click the link for your newly pushed tag. Click the "Edit Tag" button in - the top right corner of that page. -9. Paste the markdown summary from the CHANGELOG in the body of the release and - click "Publish Release". -10. Rejoice! Have a :cookie:. +7. Tag that commit, ensuring that you provide a message so we get an annotated + tag. Like this: + ```bash + git tag -m "$new_tag" "$new_tag" + ``` +8. Push the commit and the tag: + ```bash + git push --follow-tags + ``` +9. Unset `$new_tag`, you don't need it anymore. + ```bash + unset new_tag + ``` +10. Open your browser to . + Then click the link for your newly pushed tag. Click the "Edit Tag" button in + the top right corner of that page. +11. Paste the markdown summary from the CHANGELOG in the body of the release and + click "Publish Release". +12. Rejoice! Have a :cookie:. ## Things to Consider diff --git a/developer/bin/get_release_tag b/developer/bin/get_release_tag new file mode 100755 index 0000000000..da7ecd8086 --- /dev/null +++ b/developer/bin/get_release_tag @@ -0,0 +1,170 @@ +#!/bin/bash +# +# get_release_tag +# + +set -e; # exit on any uncaught error +set +o histexpand; # don't expand history expressions +shopt -s nocasematch; # case-insensitive regular expressions + +opt_next=''; +opt_latest=''; +opt_verbose=''; + +die () { + local message="$@"; + if ! [[ $message =~ "\n"$ ]]; then + message="${message}\n" + fi + printf "$message" 1>&2 + exit 1 +} + +cd_to_project_root () { + local script_dir="$(/usr/bin/dirname $0)" + cd "$script_dir" + local git_root="$(git rev-parse --show-toplevel)" + if [[ -z "$git_root" ]]; then + die "Could not find git project root" + fi + cd "$git_root" +} + +verify_git_object_is_new () { + if git rev-parse --verify "$1" >/dev/null 2>&1; then + die "\nERROR: proposed new tag: '$1' already exists as a commit object\n\n" + fi +} + +sanity_check_parsed_version () { + if [[ $# -ne 3 ]]; then + die "Error: can't parse version tag, wrong number of elements" + fi + if ! [[ $1 =~ ^v[0-9]+$ ]]; then + die "Error: can't parse version tag, does not start with v[0-9]" + fi +} + +output_proposed_tag () { + local new_tag="$1.$2.$3" + sanity_check_parsed_version "$@" + verify_git_object_is_new "$new_tag" + if [[ -n "$opt_verbose" ]]; then + printf "Latest tag\t$latest_tag\n" + printf "Proposed new tag\t$new_tag\n" + else + printf "$new_tag\n" + fi +} + +generate_next_major_tag () { + local -a version_elts + IFS='.' read -a version_elts <<< "$1" + sanity_check_parsed_version "${version_elts[@]}" + version_elts[0]="${version_elts[0]#v}" # mangle v into number + (( version_elts[0] += 1 )) # increment version field + version_elts[0]="v${version_elts[0]}" # restore text v + version_elts[1]='0' # reset minor field + version_elts[2]='0' # reset patch field + output_proposed_tag "${version_elts[@]}" +} + +generate_next_minor_tag () { + local -a version_elts + IFS='.' read -a version_elts <<< "$1"; + sanity_check_parsed_version "${version_elts[@]}" + (( version_elts[1] += 1 )) # increment minor field + version_elts[2]='0' # reset patch field + output_proposed_tag "${version_elts[@]}" +} + +generate_next_patch_tag () { + local -a version_elts + IFS='.' read -a version_elts <<< "$1" + sanity_check_parsed_version ${version_elts[@]} + (( version_elts[2] += 1 )) # increment patch field + output_proposed_tag "${version_elts[@]}" +} + +process_args () { + local arg + for arg in "$@"; do + if [[ $arg =~ ^-+h(elp)?$ ]]; then + printf "get_release_tag [ -latest | -next | -patch | -major | -verbose | -help ] + +Retrieve or calculate a release tag. + +Arguments: + + -latest Show the most recent tag in the repository, which is typically + the designation of the latest release. This is the default + when invoked with no arguments. + + -next Calculate the next minor-release tag bump and return it. + + -patch Calculate the next patch-release tag bump and return it. + Overrides -next if both are given. + + -major Calculate the next major-release tag bump and return it. + Overrides -next if both are given. + + -verbose Output a table, possibly including both latest existing and + proposed new tags. + +See RELEASING.md for more information. + +" + exit + elif [[ $arg =~ ^-+v(erbose)?$ ]]; then + opt_verbose='true' + elif [[ $arg =~ ^-+next$ ]]; then + if [[ -z "$opt_next" ]]; then + opt_next='minor' + fi + elif [[ $arg =~ ^-+patch$ ]]; then + if [[ "$opt_next" == 'major' ]]; then + die "Error: -patch is incompatible with -major"; + fi + opt_next='patch' + elif [[ $arg =~ ^-+major$ ]]; then + if [[ "$opt_next" == 'patch' ]]; then + die "Error: -patch is incompatible with -major"; + fi + opt_next='major' + elif [[ $arg =~ ^-+latest$ ]]; then + opt_latest='true' + else + die "Error: unknown argument '$arg'" + fi + if [[ -n "$opt_latest" && -n "$opt_next" ]]; then + die "Error: -latest is incompatible with -next/-patch/-major" + fi + done +} + +_get_release_tag () { + cd_to_project_root; + local latest_tag="$(git describe --tags --abbrev=0 2>/dev/null)" + if [[ -z "$latest_tag" ]]; then + die "Error: no recent tag found" + elif [[ -z "$opt_next" ]]; then + if [[ -n "$opt_verbose" ]]; then + printf "Latest tag\t" + fi + printf "$latest_tag\n" + elif [[ "$opt_next" == 'major' ]]; then + generate_next_major_tag "$latest_tag" + elif [[ "$opt_next" == 'minor' ]]; then + generate_next_minor_tag "$latest_tag" + elif [[ "$opt_next" == 'patch' ]]; then + generate_next_patch_tag "$latest_tag" + else + die "Error: should not happen. Unknown argument?" + fi +} + +process_args "$@"; + +_get_release_tag; + +#