#! /bin/bash -e
-# Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
For the purpose of signing tags and tar files, use this
key (default: use the default e-mail address’ key).
---no-upload Don't upload to upload@dev.openssl.org.
+--upload-address=<address>
+ The location to upload release files to (default:
+ upload@dev.openssl.org)
+--no-upload Don't upload the release files.
--no-update Don't perform 'make update' and 'make update-fips-checksums'.
+--quiet Really quiet, only the final output will still be output.
--verbose Verbose output.
--debug Include debug output. Implies --no-upload.
+--porcelain Give the output in an easy-to-parse format for scripts.
--force Force execution
do_clean=true
do_upload=true
do_update=true
+ECHO=echo
DEBUG=:
VERBOSE=:
git_quiet=-q
+do_porcelain=false
force=false
TEMP=$(getopt -l 'alpha,next-beta,beta,final' \
-l 'branch' \
+ -l 'upload-address:' \
-l 'no-upload,no-update' \
- -l 'verbose,debug' \
+ -l 'quiet,verbose,debug' \
+ -l 'porcelain' \
-l 'local-user:' \
-l 'reviewer:' \
-l 'force' \
warn_branch=true
shift
;;
+ --upload-address )
+ shift
+ upload_address="$1"
+ shift
+ ;;
--no-upload )
do_upload=false
shift
do_update=false
shift
;;
+ --quiet )
+ ECHO=:
+ VERBOSE=:
+ shift
+ ;;
--verbose )
+ ECHO=echo
VERBOSE=echo
git_quiet=
shift
shift
shift
;;
+ --porcelain )
+ do_porcelain=true
+ shift
+ ;;
--force )
force=true
shift
# Setup ##############################################################
-# Make sure we're in the work directory
-cd $(dirname $0)/..
-HERE=$(pwd)
-
# Check that we have the scripts that define functions we use
+RELEASE_AUX=$(cd $(dirname $0)/release-aux; pwd)
found=true
-for fn in "$HERE/dev/release-aux/release-version-fn.sh" \
- "$HERE/dev/release-aux/release-state-fn.sh"; do
+for fn in "$RELEASE_AUX/release-version-fn.sh" \
+ "$RELEASE_AUX/release-state-fn.sh" \
+ "$RELEASE_AUX/upload-fn.sh"; do
if ! [ -f "$fn" ]; then
echo >&2 "'$fn' is missing"
found=false
fi
# Load version functions
-. $HERE/dev/release-aux/release-version-fn.sh
-. $HERE/dev/release-aux/release-state-fn.sh
+. $RELEASE_AUX/release-version-fn.sh
+. $RELEASE_AUX/release-state-fn.sh
+# Load upload backend functions
+. $RELEASE_AUX/upload-fn.sh
+
+# Make sure we're in the work directory, and remember it
+if HERE=$(git rev-parse --show-toplevel); then
+ :
+else
+ echo >&2 "Not in a git worktree"
+ exit 1
+fi
+
+# Make sure that it's a plausible OpenSSL work tree, by checking
+# that a version file is found
+get_version
+
+if [ -z "$VERSION_FILE" ]; then
+ echo >&2 "Couldn't find OpenSSL version data"
+ exit 1
+fi
# Make sure it's a branch we recognise
orig_branch=$(git rev-parse --abbrev-ref HEAD)
fi
orig_HEAD=$(git rev-parse HEAD)
-# Initialize #########################################################
+# Make sure that we have fixup scripts for all the files that need
+# to be modified for a release. We trust this, because we're not
+# going to change versioning scheme in the middle of a release.
+save_IFS=$IFS
+IFS=';'
+found=true
+for fn in $RELEASE_FILES; do
+ for file in "$RELEASE_AUX/fixup-$fn-release.pl" \
+ "$RELEASE_AUX/fixup-$fn-postrelease.pl"; do
+ if ! [ -f "$file" ]; then
+ echo >&2 "'$file' is missing"
+ found=false
+ fi
+ done
+done
+IFS=$save_IFS
+if ! $found; then
+ exit 1
+fi
-echo "== Initializing work tree"
+# We turn upload_address into a few variables, which can be used
+# by backends that must understand a subset of the SFTP commands
+upload_directory=
+upload_backend=
+case "$upload_address" in
+ *:* )
+ # Something with a colon is interpreted as the typical SCP
+ # location. We reinterpret that in our terms
+ upload_directory="${upload_address#*:}"
+ upload_address="${upload_address%%:*}"
+ upload_backend=sftp
+ ;;
+ *@* )
+ upload_backend=sftp
+ ;;
+ sftp://?*/* | sftp://?* )
+ # First, remove the URI scheme
+ upload_address="${upload_address#sftp://}"
+ # Now we know that we have a host, followed by a slash, followed by
+ # a directory spec. If there is no slash, there's no directory.
+ upload_directory="${upload_address#*/}"
+ if [ "$upload_directory" = "$upload_address" ]; then
+ # There was nothing with a slash to remove, so no directory.
+ upload_directory=
+ fi
+ upload_address="${upload_address%%/*}"
+ upload_backend=sftp
+ ;;
+ sftp:* )
+ echo >&2 "Invalid upload address $upload_address"
+ exit 1
+ ;;
+ * )
+ if $do_upload && ! [ -d "$upload_address" ]; then
+ echo >&2 "Not an existing directory: $upload_address"
+ exit 1
+ fi
+ upload_backend=file
+ ;;
+esac
-get_version
+# Initialize #########################################################
+
+$ECHO "== Initializing work tree"
# Generate a cloned directory name
release_clone="$orig_branch-release-tmp"
-echo "== Work tree will be in $release_clone"
+$ECHO "== Work tree will be in $release_clone"
# Make a clone in a subdirectory and move there
if ! [ -d "$release_clone" ]; then
# changes for the release, the update branch is where we make the post-
# release changes
update_branch="$orig_branch"
-release_branch="openssl-$SERIES"
+release_branch="$(std_branch_name)"
# among others, we only create a release branch if the patch number is zero
-if [ "$update_branch" = "$release_branch" ] || [ $PATCH -ne 0 ]; then
+if [ "$update_branch" = "$release_branch" ] \
+ || [ -z "$PATCH" ] \
+ || [ $PATCH -ne 0 ]; then
if $do_branch && $warn_branch; then
echo >&2 "Warning! We're already in a release branch; --branch ignored"
fi
$VERBOSE "== Creating a local release branch: $tmp_release_branch"
git checkout $git_quiet -b "$tmp_release_branch"
-echo "== Configuring OpenSSL for update and release. This may take a bit of time"
+$ECHO "== Configuring OpenSSL for update and release. This may take a bit of time"
./Configure cc >&42
# As long as we're doing an alpha release, we can have symbols without specific
# numbers assigned. In a beta or final release, all symbols MUST have an
# assigned number.
-if [ "$next_method" != 'alpha' ]; then
+if [ "$next_method" != 'alpha' ] && grep -q '^renumber *:' Makefile; then
make renumber >&42
fi
-make update-fips-checksums >&42
+if grep -q '^update-fips-checksums *:' Makefile; then
+ make update-fips-checksums >&42
+fi
-if [ -n "$(git status --porcelain)" ]; then
+if [ -n "$(git status --porcelain --untracked-files=no --ignore-submodules=all)" ]; then
$VERBOSE "== Committing updates"
git add -u
git commit $git_quiet -m $'make update\n\nRelease: yes'
# Write the version information we updated
set_version
+release="$FULL_VERSION"
if [ -n "$PRE_LABEL" ]; then
- release="$VERSION$_PRE_RELEASE_TAG$_BUILD_METADATA"
release_text="$SERIES$_BUILD_METADATA $PRE_LABEL $PRE_NUM"
announce_template=openssl-announce-pre-release.tmpl
else
- release="$VERSION$_BUILD_METADATA"
release_text="$release"
announce_template=openssl-announce-release.tmpl
fi
-tag="openssl-$release"
+tag="$(std_tag_name)"
$VERBOSE "== Updated version information to $release"
$VERBOSE "== Updating files with release date for $release : $RELEASE_DATE"
-for fixup in "$HERE/dev/release-aux"/fixup-*-release.pl; do
- file="$(basename "$fixup" | sed -e 's|^fixup-||' -e 's|-release\.pl$||')"
- $VERBOSE "> $file"
- RELEASE="$release" RELEASE_TEXT="$release_text" RELEASE_DATE="$RELEASE_DATE" \
- perl -pi $fixup $file
-done
+(
+ IFS=';'
+ for file in $RELEASE_FILES; do
+ fixup="$RELEASE_AUX/fixup-$(basename "$file")-release.pl"
+ $VERBOSE "> $file"
+ RELEASE="$release" RELEASE_TEXT="$release_text" RELEASE_DATE="$RELEASE_DATE" \
+ perl -pi $fixup $file
+ done
+)
$VERBOSE "== Committing updates and tagging"
git add -u
if [ -n "$reviewers" ]; then
addrev --release --nopr $reviewers
fi
-echo "Tagging release with tag $tag. You may need to enter a pass phrase"
+$ECHO "Tagging release with tag $tag. You may need to enter a pass phrase"
git tag$tagkey "$tag" -m "OpenSSL $release release tag"
tarfile=openssl-$release.tar
tgzfile=$tarfile.gz
announce=openssl-$release.txt
-echo "== Generating tar, hash and announcement files. This make take a bit of time"
+$ECHO "== Generating tar, hash and announcement files. This make take a bit of time"
$VERBOSE "== Making tarfile: $tgzfile"
-# Unfortunately, util/mktar.sh does verbose output on STDERR... for good
-# reason, but it means we don't display errors unless --verbose
-./util/mktar.sh --tarfile="../$tarfile" 2>&1 \
- | while read L; do $VERBOSE "> $L"; done
+
+# Unfortunately, some tarball generators do verbose output on STDERR... for
+# good reason, but it means we don't display errors unless --verbose
+(
+ if [ -f ./util/mktar.sh ]; then
+ ./util/mktar.sh --tarfile="../$tarfile" 2>&1
+ else
+ make DISTTARVARS=TARFILE="../$tarfile" dist 2>&1
+ fi
+) | while read L; do $VERBOSE "> $L"; done
if ! [ -f "../$tgzfile" ]; then
echo >&2 "Where did the tarball end up? (../$tgzfile)"
$VERBOSE "== Generating announcement text: $announce"
# Hack the announcement template
-cat "$HERE/dev/release-aux/$announce_template" \
+cat "$RELEASE_AUX/$announce_template" \
| sed -e "s|\\\$release_text|$release_text|g" \
-e "s|\\\$release|$release|g" \
-e "s|\\\$series|$SERIES|g" \
-e "s|\\\$length|$length|" \
-e "s|\\\$sha1hash|$sha1hash|" \
-e "s|\\\$sha256hash|$sha256hash|" \
- | perl -p "$HERE/dev/release-aux/fix-title.pl" \
+ | perl -p "$RELEASE_AUX/fix-title.pl" \
> "../$announce"
$VERBOSE "== Generating signatures: $tgzfile.asc $announce.asc"
rm -f "../$tgzfile.asc" "../$announce.asc"
-echo "Signing the release files. You may need to enter a pass phrase"
+$ECHO "Signing the release files. You may need to enter a pass phrase"
gpg$gpgkey --use-agent -sba "../$tgzfile"
gpg$gpgkey --use-agent -sta --clearsign "../$announce"
git push --follow-tags parent HEAD
if $do_upload; then
- (
- if [ "$VERBOSE" != ':' ]; then
- echo "progress"
- fi
- echo "put ../$tgzfile"
- echo "put ../$tgzfile.sha1"
- echo "put ../$tgzfile.sha256"
- echo "put ../$tgzfile.asc"
- echo "put ../$announce.asc"
- ) \
- | sftp "$upload_address"
+ $ECHO "== Upload tar, hash and announcement files"
fi
+(
+ # With sftp, the progress meter is enabled by default,
+ # so we turn it off unless --verbose was given
+ if [ "$VERBOSE" == ':' ]; then
+ echo "progress"
+ fi
+ if [ -n "$upload_directory" ]; then
+ echo "cd $upload_directory"
+ fi
+ echo "put ../$tgzfile"
+ echo "put ../$tgzfile.sha1"
+ echo "put ../$tgzfile.sha256"
+ echo "put ../$tgzfile.asc"
+ echo "put ../$announce.asc"
+) | upload_backend_$upload_backend "$upload_address" $do_upload
+
# Post-release #######################################################
+# Reset the files to their pre-release contents. This doesn't affect
+# HEAD, but simply set all the files in a state that 'git revert -n HEAD'
+# would have given, but without the artifacts that 'git revert' adds.
+#
+# This allows all the post-release fixup scripts to perform from the
+# same point as the release fixup scripts, hopefully making them easier
+# to write. This also makes the same post-release fixup scripts easier
+# to run when --branch has been used, as they will be run both on the
+# release branch and on the update branch, essentially from the same
+# state for affected files.
$VERBOSE "== Reset all files to their pre-release contents"
git reset $git_quiet HEAD^ -- .
git checkout -- .
next_release_state "$next_method2"
set_version
-release="$VERSION$_PRE_RELEASE_TAG$_BUILD_METADATA"
+release="$FULL_VERSION"
release_text="$VERSION$_BUILD_METADATA"
if [ -n "$PRE_LABEL" ]; then
release_text="$SERIES$_BUILD_METADATA $PRE_LABEL $PRE_NUM"
$VERBOSE "== Updated version information to $release"
$VERBOSE "== Updating files for $release :"
-for fixup in "$HERE/dev/release-aux"/fixup-*-postrelease.pl; do
- file="$(basename "$fixup" | sed -e 's|^fixup-||' -e 's|-postrelease\.pl$||')"
- $VERBOSE "> $file"
- RELEASE="$release" RELEASE_TEXT="$release_text" \
- PREV_RELEASE_TEXT="$prev_release_text" \
- PREV_RELEASE_DATE="$prev_release_date" \
- perl -pi $fixup $file
-done
+(
+ IFS=';'
+ for file in $RELEASE_FILES; do
+ fixup="$RELEASE_AUX/fixup-$(basename "$file")-postrelease.pl"
+ $VERBOSE "> $file"
+ RELEASE="$release" RELEASE_TEXT="$release_text" \
+ PREV_RELEASE_TEXT="$prev_release_text" \
+ PREV_RELEASE_DATE="$prev_release_date" \
+ perl -pi $fixup $file
+ done
+)
$VERBOSE "== Committing updates"
git add -u
next_release_state "minor"
set_version
- release="$VERSION$_PRE_RELEASE_TAG$_BUILD_METADATA"
+ release="$FULL_VERSION"
release_text="$SERIES$_BUILD_METADATA"
$VERBOSE "== Updated version information to $release"
$VERBOSE "== Updating files for $release :"
- for fixup in "$HERE/dev/release-aux"/fixup-*-postrelease.pl; do
- file="$(basename "$fixup" | sed -e 's|^fixup-||' -e 's|-postrelease\.pl$||')"
- $VERBOSE "> $file"
- RELEASE="$release" RELEASE_TEXT="$release_text" \
- perl -pi $fixup $file
- done
+ (
+ IFS=';'
+ for file in $RELEASE_FILES; do
+ fixup="$RELEASE_AUX/fixup-$(basename "$file")-postrelease.pl"
+ $VERBOSE "> $file"
+ RELEASE="$release" RELEASE_TEXT="$release_text" \
+ perl -pi $fixup $file
+ done
+ )
$VERBOSE "== Committing updates"
git add -u
fi
fi
-# Push everything to the parent repo
-$VERBOSE "== Push what we have to the parent repository"
-git push parent HEAD
+if ! $clean_worktree; then
+ # Push everything to the parent repo
+ $VERBOSE "== Push what we have to the parent repository"
+ git push parent HEAD
+fi
# Done ###############################################################
$VERBOSE "== Done"
cd $HERE
-cat <<EOF
+if $do_porcelain; then
+ echo "clone_directory='$release_clone'"
+ echo "update_branch='$tmp_update_branch'"
+ echo "final_update_branch='$update_branch'"
+ if [ "$tmp_release_branch" != "$tmp_update_branch" ]; then
+ echo "release_branch='$tmp_release_branch'"
+ echo "final_release_branch='$release_branch'"
+ fi
+ echo "release_tag='$tag'"
+else
+ cat <<EOF
======================================================================
The release is done, and involves a few files and commits for you to
EOF
-if $do_release; then
- cat <<EOF
+ if $do_release; then
+ cat <<EOF
The following files were uploaded to $upload_address, please ensure they
are dealt with appropriately:
$tgzfile.asc
$announce.asc
EOF
-fi
+ fi
-cat <<EOF
+ cat <<EOF
----------------------------------------------------------------------
EOF
-if $do_branch; then
- cat <<EOF
+ if $do_branch; then
+ cat <<EOF
You need to prepare the main repository with a new branch, '$release_branch'.
That is done directly in the server's bare repository like this:
git push git@github.openssl.org:openssl/openssl.git \\
$tag
EOF
-else
-cat <<EOF
+ else
+ cat <<EOF
One additional release branch has been added to your repository.
Push it to github, make a PR from it and have it approved:
git push git@github.openssl.org:openssl/openssl.git \\
$tag
EOF
-fi
+ fi
-cat <<EOF
+ cat <<EOF
----------------------------------------------------------------------
EOF
-cat <<EOF
+ cat <<EOF
When everything is done, or if something went wrong and you want to start
over, simply clean away temporary things left behind:
rm -rf $release_clone
EOF
-if $do_branch; then
- cat <<EOF
+ if $do_branch; then
+ cat <<EOF
The additional release branches:
git branch -D $tmp_release_branch
git branch -D $tmp_update_branch
EOF
-else
- cat <<EOF
+ else
+ cat <<EOF
The temporary release branch:
git branch -D $tmp_release_branch
EOF
+ fi
fi
exit 0
B<--branch> |
B<--local-user>=I<keyid> |
B<--reviewer>=I<id> |
+B<--upload-address>=I<address> |
B<--no-upload> |
B<--no-update> |
+B<--quiet> |
B<--verbose> |
B<--debug> |
+B<--porcelain> |
B<--help> |
B<--manual>
]
already exist, and switch to it. The exact branch name will be
C<< openssl-I<SERIES> >>.
+=item B<--upload-address>=I<address>
+
+The location that the release files are to be uploaded to. Supported values
+are:
+
+=over 4
+
+=item -
+
+an existing local directory
+
+=item -
+
+something that can be interpreted as an SCP/SFTP address. In this case,
+SFTP will always be used. Typical SCP remote file specs will be translated
+into something that makes sense for SFTP.
+
+=back
+
+The default upload address is C<upload@dev.openssl.org>.
+
=item B<--no-upload>
-Don't upload the produced files.
+Don't upload the release files.
=item B<--no-update>
Don't run C<make update> and C<make update-fips-checksums>.
+=item B<--quiet>
+
+Really quiet, only bare necessity output, which is the final instructions,
+or should the B<--porcelain> option be used, only that output.
+
+messages appearing on standard error will still be shown, but should be
+fairly minimal.
+
=item B<--verbose>
Verbose output.
Display extra debug output. Implies B<--no-upload>
+=item B<--porcelain>
+
+Give final output in an easy-to-parse format for scripts. The output comes
+in a form reminicent of shell variable assignments. Currently supported are:
+
+=over 4
+
+=item B<clone_directory>=I<dir>
+
+The directory for the clone that this script creates.
+
+=item B<update_branch>=I<branch>
+
+The temporary update branch
+
+=item B<final_update_branch>=I<branch>
+
+The final update branch that the temporary update branch should end up being
+merged into.
+
+=item B<release_branch>=I<branch>
+
+The temporary release branch, only given if it differs from the update branch
+(i.e. B<--branch> was given or implied).
+
+=item B<final_release_branch>=I<branch>
+
+The final release branch that the temporary release branch should end up being
+merged into. This is only given if it differs from the final update branch
+(i.e. B<--branch> was given or implied).
+
+=item B<release_tag>=I<tag>
+
+The release tag.
+
+=back
+
=item B<--local-user>=I<keyid>
Use I<keyid> as the local user for C<git tag> and for signing with C<gpg>.