blob: 3119f3789f22c5a29c47a3db728c7516f0cfb6c8 [file] [log] [blame]
#!/usr/bin/env bash
# SPDX-License-Identifier: Apache-2.0 OR MIT
set -eEuo pipefail
IFS=$'\n\t'
cd "$(dirname "$0")"/..
# shellcheck disable=SC2154
trap 's=$?; echo >&2 "$0: error on line "${LINENO}": ${BASH_COMMAND}"; exit ${s}' ERR
# Publish a new release.
#
# USAGE:
# ./tools/publish.sh <VERSION>
#
# Note: This script requires the following tools:
# - parse-changelog <https://github.com/taiki-e/parse-changelog>
# - cargo-workspaces <https://github.com/pksunkara/cargo-workspaces>
x() {
local cmd="$1"
shift
(
set -x
"${cmd}" "$@"
)
}
bail() {
echo >&2 "error: $*"
exit 1
}
version="${1:?}"
version="${version#v}"
tag_prefix="v"
tag="${tag_prefix}${version}"
changelog="CHANGELOG.md"
if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z\.-]+)?(\+[0-9A-Za-z\.-]+)?$ ]]; then
bail "invalid version format '${version}'"
fi
if [[ $# -gt 1 ]]; then
bail "invalid argument '$2'"
fi
# Make sure there is no uncommitted change.
git diff --exit-code
git diff --exit-code --staged
# Make sure the same release has not been created in the past.
if gh release view "${tag}" &>/dev/null; then
bail "tag '${tag}' has already been created and pushed"
fi
# Make sure that the release was created from an allowed branch.
if ! git branch | grep -q '\* main$'; then
bail "current branch is not 'main'"
fi
release_date=$(date -u '+%Y-%m-%d')
tags=$(git --no-pager tag | (grep -E "^${tag_prefix}[0-9]+" || true))
if [[ -n "${tags}" ]]; then
# Make sure the same release does not exist in changelog.
if grep -Eq "^## \\[${version//./\\.}\\]" "${changelog}"; then
bail "release ${version} already exist in ${changelog}"
fi
if grep -Eq "^\\[${version//./\\.}\\]: " "${changelog}"; then
bail "link to ${version} already exist in ${changelog}"
fi
# Update changelog.
remote_url=$(grep -E '^\[Unreleased\]: https://' "${changelog}" | sed 's/^\[Unreleased\]: //; s/\.\.\.HEAD$//')
before_tag="${remote_url#*/compare/}"
remote_url="${remote_url%/compare/*}"
sed -i "s/^## \\[Unreleased\\]/## [Unreleased]\\n\\n## [${version}] - ${release_date}/" "${changelog}"
sed -i "s#^\[Unreleased\]: https://.*#[Unreleased]: ${remote_url}/compare/${tag}...HEAD\\n[${version}]: ${remote_url}/compare/${before_tag}...${tag}#" "${changelog}"
if ! grep -Eq "^## \\[${version//./\\.}\\] - ${release_date}$" "${changelog}"; then
bail "failed to update ${changelog}"
fi
if ! grep -Eq "^\\[${version//./\\.}\\]: " "${changelog}"; then
bail "failed to update ${changelog}"
fi
else
# Make sure the release exists in changelog.
if ! grep -Eq "^## \\[${version//./\\.}\\] - ${release_date}$" "${changelog}"; then
bail "release ${version} does not exist in ${changelog} or has wrong release date"
fi
if ! grep -Eq "^\\[${version//./\\.}\\]: " "${changelog}"; then
bail "link to ${version} does not exist in ${changelog}"
fi
fi
# Make sure that a valid release note for this version exists.
# https://github.com/taiki-e/parse-changelog
changes=$(parse-changelog "${changelog}" "${version}")
if [[ -z "${changes}" ]]; then
bail "changelog for ${version} has no body"
fi
echo "============== CHANGELOG =============="
echo "${changes}"
echo "======================================="
metadata=$(cargo metadata --format-version=1 --all-features --no-deps)
prev_version=''
manifest_paths=()
for id in $(jq <<<"${metadata}" '.workspace_members[]'); do
pkg=$(jq <<<"${metadata}" ".packages[] | select(.id == ${id})")
publish=$(jq <<<"${pkg}" -r '.publish')
# Publishing is unrestricted if null, and forbidden if an empty array.
if [[ "${publish}" == "[]" ]]; then
continue
fi
actual_version=$(jq <<<"${pkg}" -r '.version')
if [[ -z "${prev_version:-}" ]]; then
prev_version="${actual_version}"
fi
# Make sure that the version number of all publishable workspace members matches.
if [[ "${actual_version}" != "${prev_version}" ]]; then
name=$(jq <<<"${pkg}" -r '.name')
bail "publishable workspace members must be version '${prev_version}', but package '${name}' is version '${actual_version}'"
fi
manifest_path=$(jq <<<"${pkg}" -r '.manifest_path')
manifest_paths+=("${manifest_path}")
done
# Update version.
x cargo workspaces version --force '*' --no-git-commit --exact -y custom "${version}"
if [[ -n "${tags}" ]]; then
# Create a release commit.
x git add "${changelog}" "${manifest_paths[@]}"
x git commit -m "Release ${version}"
fi
x git tag "${tag}"
x git push origin main
x git push origin --tags