cmake: Automate release commit and tag.
Add a TAG_RELEASE cmake variable which engages a cmake script to generate a signed release commit with changelog entries from git and signed tag. The value can be ON/TRUE/1 or a specific next version number, or UNDO to rollback changes just made with this script. The script tries to make sure all the preliminaries are in the right state to do a release, the source is a git clone, on branch master, with a clean working tree and gpg is available. Also add a utility script FileIterator.cmake which is an OO-like line iterator interface for text files, used by the release commit script. Signed-off-by: Rafael Kitover <rkitover@gmail.com>
This commit is contained in:
parent
4e607da067
commit
93f906e2db
|
@ -13,6 +13,10 @@ endif()
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||||
|
|
||||||
|
if(TAG_RELEASE)
|
||||||
|
include(MakeReleaseCommitAndTag)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(VCPKG_DEPS zlib libpng SDL2 SFML gettext wxWidgets)
|
set(VCPKG_DEPS zlib libpng SDL2 SFML gettext wxWidgets)
|
||||||
|
|
||||||
# appveyor job goes over time limit if building ffmpeg during initial cache
|
# appveyor job goes over time limit if building ffmpeg during initial cache
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Interface for iterating over a text file by line.
|
||||||
|
|
||||||
|
function(fi_open_file file)
|
||||||
|
file(READ "${file}" fi_file_contents)
|
||||||
|
set(fi_file_contents "${fi_file_contents}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(fi_get_next_line)
|
||||||
|
string(FIND "${fi_file_contents}" "\n" pos)
|
||||||
|
|
||||||
|
string(SUBSTRING "${fi_file_contents}" 0 ${pos} fi_line)
|
||||||
|
|
||||||
|
math(EXPR pos "${pos} + 1")
|
||||||
|
|
||||||
|
string(SUBSTRING "${fi_file_contents}" ${pos} -1 fi_file_contents)
|
||||||
|
|
||||||
|
set(fi_line "${fi_line}" PARENT_SCOPE)
|
||||||
|
set(fi_file_contents "${fi_file_contents}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(fi_check_done)
|
||||||
|
string(LENGTH "${fi_file_contents}" len)
|
||||||
|
|
||||||
|
set(fi_done FALSE PARENT_SCOPE)
|
||||||
|
|
||||||
|
if(len EQUAL 0)
|
||||||
|
set(fi_done TRUE PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
|
@ -0,0 +1,251 @@
|
||||||
|
# Set TAG_RELEASE to ON/TRUE/1 to increment minor, or to new version, or to
|
||||||
|
# UNDO to undo release.
|
||||||
|
|
||||||
|
# Increment version.
|
||||||
|
# Update CHANGELOG.md with git log.
|
||||||
|
# Make release commit.
|
||||||
|
# Tag release commit.
|
||||||
|
|
||||||
|
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||||
|
message(FATAL_ERROR "releases can only be done from a git clone")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(Git)
|
||||||
|
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
message(FATAL_ERROR "git is required to make a release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_program(GPG_EXECUTABLE gpg)
|
||||||
|
|
||||||
|
if(NOT GPG_EXECUTABLE)
|
||||||
|
message(FATAL_ERROR "gpg must be installed and set up with your key to make a release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# From: https://gist.github.com/alorence/59d18896aaad5188b7b4.
|
||||||
|
macro(CURRENT_DATE RESULT)
|
||||||
|
if(CMAKE_HOST_SYSTEM MATCHES Windows OR ((NOT DEFINED CMAKE_HOST_SYSTEM) AND WIN32))
|
||||||
|
execute_process(COMMAND "cmd" " /C date /T" OUTPUT_VARIABLE ${RESULT})
|
||||||
|
string(REGEX REPLACE ".*(..)/(..)/(....).*" "\\3-\\1-\\2" ${RESULT} ${${RESULT}})
|
||||||
|
else()
|
||||||
|
execute_process(COMMAND "date" "+%Y-%m-%d" OUTPUT_VARIABLE ${RESULT})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
function(make_release_commit_and_tag)
|
||||||
|
|
||||||
|
# First make sure we are on master.
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} rev-parse --short --abbrev-ref=strict HEAD
|
||||||
|
OUTPUT_VARIABLE git_branch
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT git_branch STREQUAL master)
|
||||||
|
message(FATAL_ERROR "you must be on the git master branch to release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} status --porcelain=2
|
||||||
|
OUTPUT_VARIABLE git_status
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
string(LENGTH "${git_status}" git_status_length)
|
||||||
|
|
||||||
|
if(NOT git_status_length EQUAL 0)
|
||||||
|
message(FATAL_ERROR "working tree must be clean to do a release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} tag --sort=-v:refname
|
||||||
|
OUTPUT_VARIABLE git_tags
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
string(REGEX REPLACE "\r?\n" ";" git_tags_lines ${git_tags})
|
||||||
|
|
||||||
|
foreach(line ${git_tags_lines})
|
||||||
|
if(line MATCHES "^v[0-9]")
|
||||||
|
set(git_last_tag ${line})
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT DEFINED git_last_tag)
|
||||||
|
message(FATAL_ERROR "cannot find last release tag")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} log ${git_last_tag}.. "--pretty=format:* %h - %s [%aL]"
|
||||||
|
OUTPUT_VARIABLE release_log
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Calculate new release version, unless it was passed in.
|
||||||
|
|
||||||
|
if(TAG_RELEASE STREQUAL UNDO)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} tag -d ${git_last_tag}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} reset HEAD~1
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} checkout HEAD CHANGELOG.md
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
message(FATAL_ERROR [=[
|
||||||
|
**** RELEASE COMMIT AND TAG HAVE BEEN REMOVED ****"
|
||||||
|
|
||||||
|
The git and working tree state have been restored to their state before the
|
||||||
|
TAG_RELEASE operation.
|
||||||
|
|
||||||
|
Ignore the following cmake error.
|
||||||
|
]=])
|
||||||
|
elseif(NOT (TAG_RELEASE STREQUAL TRUE OR TAG_RELEASE STREQUAL ON OR TAG_RELEASE STREQUAL 1))
|
||||||
|
set(new_tag ${TAG_RELEASE})
|
||||||
|
else()
|
||||||
|
string(REGEX MATCH "\\.[0-9]+$" last_tag_minor_part ${git_last_tag})
|
||||||
|
string(REGEX REPLACE "\\.[0-9]+$" "" last_tag_minor_stripped ${git_last_tag})
|
||||||
|
|
||||||
|
string(SUBSTRING ${last_tag_minor_part} 1 -1 last_tag_minor)
|
||||||
|
|
||||||
|
math(EXPR last_minor_incremented "${last_tag_minor} + 1")
|
||||||
|
|
||||||
|
string(CONCAT new_tag ${last_tag_minor_stripped} . ${last_minor_incremented})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Make sure tag begins with "v".
|
||||||
|
|
||||||
|
if(NOT new_tag MATCHES "^v")
|
||||||
|
set(new_tag "v${new_tag}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# But remove the "v" for the version string.
|
||||||
|
|
||||||
|
string(REGEX REPLACE "^v" "" new_version ${new_tag})
|
||||||
|
|
||||||
|
current_date(current_date)
|
||||||
|
|
||||||
|
# Rewrite CHANGELOG.md
|
||||||
|
|
||||||
|
# First make a copy for backing out.
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_SOURCE_DIR}/CHANGELOG.md
|
||||||
|
${CMAKE_BINARY_DIR}/CHANGELOG.md.orig
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now read it and add the new release.
|
||||||
|
|
||||||
|
include(FileIterator)
|
||||||
|
|
||||||
|
fi_open_file(${CMAKE_SOURCE_DIR}/CHANGELOG.md)
|
||||||
|
|
||||||
|
set(work_file ${CMAKE_BINARY_DIR}/CHANGELOG.md.work)
|
||||||
|
|
||||||
|
file(REMOVE ${work_file})
|
||||||
|
|
||||||
|
set(last_release_found FALSE)
|
||||||
|
|
||||||
|
while(NOT fi_done)
|
||||||
|
fi_get_next_line()
|
||||||
|
|
||||||
|
if(NOT last_release_found AND fi_line MATCHES "^## \\[[0-9]")
|
||||||
|
set(last_release_found TRUE)
|
||||||
|
|
||||||
|
set(tag_line "## [${new_version}] - ${current_date}")
|
||||||
|
|
||||||
|
string(LENGTH "${tag_line}" tag_line_length)
|
||||||
|
|
||||||
|
unset(tag_line_under_bar)
|
||||||
|
|
||||||
|
foreach(i RANGE 1 ${tag_line_length})
|
||||||
|
set(tag_line_under_bar "=${tag_line_under_bar}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
file(APPEND ${work_file} ${tag_line} "\n")
|
||||||
|
file(APPEND ${work_file} ${tag_line_under_bar} "\n")
|
||||||
|
file(APPEND ${work_file} "${release_log}" "\n")
|
||||||
|
file(APPEND ${work_file} "\n")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(APPEND ${work_file} "${fi_line}" "\n")
|
||||||
|
|
||||||
|
fi_check_done()
|
||||||
|
endwhile()
|
||||||
|
|
||||||
|
# Convert to UNIX line endings on Windows, just copy the file otherwise.
|
||||||
|
|
||||||
|
if(CMAKE_HOST_SYSTEM MATCHES Windows OR ((NOT DEFINED CMAKE_HOST_SYSTEM) AND WIN32))
|
||||||
|
execute_process(
|
||||||
|
COMMAND powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command [=[
|
||||||
|
$text = [IO.File]::ReadAllText("CHANGELOG.md.work") -replace "`r`n", "`n"
|
||||||
|
[IO.File]::WriteAllText("CHANGELOG.md", $text)
|
||||||
|
]=]
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_BINARY_DIR}/CHANGELOG.md.work
|
||||||
|
${CMAKE_BINARY_DIR}/CHANGELOG.md
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Copy the new file and add it to the commit.
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_BINARY_DIR}/CHANGELOG.md
|
||||||
|
${CMAKE_SOURCE_DIR}/CHANGELOG.md
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} add CHANGELOG.md
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make the release commit.
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} commit -m "release ${new_tag}" --signoff -S
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make release tag.
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} tag -s -m${new_tag} ${new_tag}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
message(FATAL_ERROR [=[
|
||||||
|
|
||||||
|
Release commit and tag generated.
|
||||||
|
|
||||||
|
**** IF YOU ARE SURE YOU WANT TO RELEASE ****
|
||||||
|
|
||||||
|
Run the following commands to push the release commit and tag:
|
||||||
|
|
||||||
|
git push
|
||||||
|
git push --tags
|
||||||
|
|
||||||
|
Ignore the "configuration incomplete" message following, this mode does not
|
||||||
|
build anything.
|
||||||
|
|
||||||
|
]=])
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
make_release_commit_and_tag()
|
|
@ -21,7 +21,7 @@ function(vcpkg_get_first_upgrade vcpkg_exe)
|
||||||
string(REGEX REPLACE "\r?\n" ";" upgrade_lines "${upgradable}")
|
string(REGEX REPLACE "\r?\n" ";" upgrade_lines "${upgradable}")
|
||||||
|
|
||||||
foreach(line ${upgrade_lines})
|
foreach(line ${upgrade_lines})
|
||||||
if(${line} MATCHES "^ [* ] ")
|
if(line MATCHES "^ [* ] ")
|
||||||
string(REGEX REPLACE "^ [* ] " "" first_upgrade ${line})
|
string(REGEX REPLACE "^ [* ] " "" first_upgrade ${line})
|
||||||
set(first_upgrade ${first_upgrade} PARENT_SCOPE)
|
set(first_upgrade ${first_upgrade} PARENT_SCOPE)
|
||||||
return()
|
return()
|
||||||
|
|
Loading…
Reference in New Issue