mirror of
https://github.com/material-components/material-components-ios.git
synced 2026-01-09 07:11:20 +08:00
864 lines
26 KiB
Bash
Executable File
864 lines
26 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Copyright 2017-present The Material Motion and Material Components for
|
|
# iOS Authors. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# Abort if any command returns an error
|
|
set -e
|
|
|
|
parentcmd=$(basename "${BASH_SOURCE[1]}")
|
|
cmd=$(basename "${BASH_SOURCE[0]}")
|
|
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
|
|
usage() {
|
|
"$dir/readme_to_console" "$dir/README-release.md"
|
|
}
|
|
|
|
# HELPER METHODS
|
|
|
|
current_branch() {
|
|
git rev-parse --abbrev-ref HEAD
|
|
}
|
|
|
|
enforce_clean_state() {
|
|
if [[ $(git status --porcelain) ]]; then
|
|
echo "${B}Your git repo is not in a clean state.${N}"
|
|
echo "Please revert or commit all changes before cutting a release."
|
|
git status
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
enforce_release_cut() {
|
|
if [ ! $(git rev-parse --verify release-candidate 2> /dev/null) ]; then
|
|
echo "No release in progress."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
enforce_changelog_version() {
|
|
changelog_version=$(awk '/# (.+)/ { print $2; exit}' "$rootdir/CHANGELOG.md")
|
|
changelog_version=$(version_for_platform $changelog_version)
|
|
if [ "$changelog_version" == "#develop#" ]; then
|
|
echo "${B}You haven't updated CHANGELOG.md with the current version yet.${N}"
|
|
echo "Please run '$parentcmd $cmd bump [version]' before running this command again."
|
|
exit 1
|
|
elif [ "$version" != "$changelog_version" ]; then
|
|
echo "Mismatch in CHANGELOG.md's latest version."
|
|
echo
|
|
echo " CHANGELOG.md latest version: $changelog_version"
|
|
echo " Desired version: $version"
|
|
echo
|
|
echo "Please edit CHANGELOG.md or change your version number."
|
|
echo
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
get_latest_branch() {
|
|
if [ ! $(git rev-parse --verify release-candidate 2> /dev/null) ]; then
|
|
echo "HEAD"
|
|
else
|
|
echo "release-candidate"
|
|
fi
|
|
}
|
|
|
|
version_for_platform() {
|
|
user_version="$1"
|
|
|
|
if [ -f "$rootdir/Podfile" ]; then
|
|
echo "v${user_version#v}"
|
|
elif [ -f "$rootdir/build.gradle" ]; then
|
|
echo "${user_version#v}"
|
|
else
|
|
echo "v${user_version#v}"
|
|
fi
|
|
}
|
|
|
|
# COMMAND METHODS
|
|
|
|
# Creates a release-candidate branch based off the latest origin/develop
|
|
cut_release() {
|
|
isHotFix=false;
|
|
|
|
while test $# -gt 0; do
|
|
case "$1" in
|
|
--hotfix)
|
|
isHotFix=true;
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ $(git rev-parse --verify release-candidate 2> /dev/null) ]; then
|
|
echo "${B}Release already cut.${N}"
|
|
echo "Consider deleting your existing release-candidate branch."
|
|
exit 1
|
|
fi
|
|
|
|
git fetch
|
|
|
|
git show develop >> /dev/null 2>&1 || { git checkout -b develop origin/develop; }
|
|
|
|
deviance=$(git log develop..origin/develop --oneline | wc -l)
|
|
if [ $deviance -ne 0 ]; then
|
|
echo
|
|
echo " Your local develop branch is behind origin/develop."
|
|
echo " Refusing to continue until you've rebased off of origin/develop."
|
|
echo
|
|
echo " git checkout develop"
|
|
echo " git rebase origin/develop"
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
deviance=$(git log origin/develop..develop --oneline | wc -l)
|
|
if [ $deviance -ne "0" ]; then
|
|
echo
|
|
echo " Your local develop branch is ahead of origin/develop."
|
|
echo " Refusing to continue until you've landed your local changes into origin/develop."
|
|
echo
|
|
exit 1 # TODO: Revert this line so we bail out.
|
|
fi
|
|
|
|
if $isHotFix; then
|
|
branch=origin/stable
|
|
branch_message="This is a hotfix... branching off $branch"
|
|
else
|
|
branch=origin/develop
|
|
branch_message="This is a normal release... branching off $branch"
|
|
fi
|
|
echo $branch_message
|
|
git checkout -b release-candidate $branch
|
|
git checkout origin/stable -- .gitattributes
|
|
|
|
touch "$rootdir/CHANGELOG.md"
|
|
if ! grep "# #develop#" "$rootdir/CHANGELOG.md" >> /dev/null; then
|
|
echo "Generating API diff..."
|
|
CHANGELOG_TMP_PATH=$(mktemp -d)
|
|
generate_release_apidiff | tee "$CHANGELOG_TMP_PATH/api_diff"
|
|
|
|
CHANGELOG_PATH=$(cat "$CHANGELOG_TMP_PATH/api_diff" | grep "Changelog=" | cut -d'=' -f2)
|
|
|
|
# Add the new changelog contents in reverse order:
|
|
echo -e "\n---\n" | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
generate_release_notes | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "\n## Component changes" | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
cat "$CHANGELOG_PATH" | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "## API changes" | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "Replace this text with example code for each new feature." | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "## New features\n" | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "Replace this text with links to deprecation guides." | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "## New deprecations\n" | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "Replace this explanations for how to resolve the breaking changes." | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "## Breaking changes\n" | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "Replace this text with a summarized description of this release's contents." | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
echo -e "# #develop#\n" | cat - "$rootdir/CHANGELOG.md" > /tmp/out && mv /tmp/out "$rootdir/CHANGELOG.md"
|
|
fi
|
|
git add "$rootdir/CHANGELOG.md"
|
|
git commit -m "Automatic changelog preparation for release."
|
|
git push origin release-candidate -u
|
|
|
|
RELEASE_SHA=$(git merge-base --fork-point release-candidate $branch)
|
|
PULL_REQUEST_URL="https://github.com/material-components/material-components-ios/compare/stable...release-candidate"
|
|
echo "${B}You can now start the release-candidate pull request:${N}"
|
|
echo
|
|
echo " $PULL_REQUEST_URL"
|
|
echo
|
|
echo "This will initiate public testing of the release candidate."
|
|
echo
|
|
echo "${B}You can now kick off internal testing.${N}"
|
|
}
|
|
|
|
abort_release() {
|
|
enforce_release_cut
|
|
|
|
echo "${B}About to abort the release candidate.${N}"
|
|
echo "${B}${U}This action is not easily reversible.${N}"
|
|
echo
|
|
echo -n "Press enter to continue..."
|
|
read
|
|
|
|
git checkout origin/develop
|
|
git branch -D release-candidate
|
|
git push origin :release-candidate
|
|
}
|
|
|
|
test_release() {
|
|
"$dir/prep_all"
|
|
"$dir/build_all" --verbose
|
|
"$dir/test_all"
|
|
"$dir/build_codelabs"
|
|
}
|
|
|
|
bump_release() {
|
|
if [ -z "$1" ]; then
|
|
echo "Missing desired version."
|
|
echo
|
|
echo "Usage: $parentcmd $cmd bump <desired version> [<old version>]"
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
enforce_release_cut
|
|
|
|
new_version="$1"
|
|
new_version=${new_version#v}
|
|
|
|
if [ -z "$2" ]; then
|
|
last_version=$(git describe --tags $(git rev-list --tags --max-count=1))
|
|
else
|
|
last_version="$2"
|
|
fi
|
|
last_version=${last_version#v}
|
|
|
|
grep -FIlr "$last_version" . 2>/dev/null | grep -v -f "$dir/versionignore" | while read line; do
|
|
sed -i.bak "s:$last_version:$new_version:g" "$line"
|
|
rm "$line.bak"
|
|
done
|
|
|
|
grep -FIlr "#develop#" . 2>/dev/null | grep -v -f "$dir/versionignore" | while read line; do
|
|
sed -i.bak "s:#develop#:$new_version:g" "$line"
|
|
rm "$line.bak"
|
|
done
|
|
}
|
|
|
|
merge_release() {
|
|
enforce_release_cut
|
|
|
|
version=$(version_for_platform $1)
|
|
if [ -z "$version" ]; then
|
|
echo "Must provide a ${U}version${N} argument."
|
|
exit 1
|
|
fi
|
|
|
|
deviance=$(git log develop..origin/develop --oneline | wc -l)
|
|
if [ $deviance -ne 0 ]; then
|
|
echo
|
|
echo " Your local develop branch is behind origin/develop."
|
|
echo " Refusing to continue until you've rebased off of origin/develop."
|
|
echo
|
|
echo " git checkout develop"
|
|
echo " git rebase origin/develop"
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
deviance=$(git log origin/develop..develop --oneline | wc -l)
|
|
if [ $deviance -ne "0" ]; then
|
|
echo
|
|
echo " Your local develop branch is ahead of origin/develop."
|
|
echo " Refusing to continue until you've landed your local changes into origin/develop."
|
|
echo
|
|
exit 1 # TODO: Revert this line so we bail out.
|
|
fi
|
|
|
|
current_branch=$(current_branch)
|
|
if [ "$current_branch" != "release-candidate" ]; then
|
|
echo "Checking out the release-candidate branch..."
|
|
git checkout release-candidate
|
|
fi
|
|
|
|
enforce_changelog_version
|
|
|
|
if [ $(git rev-list --tags --max-count=1 2> /dev/null) ]; then
|
|
last_version=$(git describe --tags $(git rev-list --tags --max-count=1))
|
|
last_version=${last_version#v}
|
|
last_version=$(echo "$last_version" | sed "s:\.:\\\\.:g")
|
|
if grep -Ilr "$last_version" . | grep -v -f "$dir/versionignore"; then
|
|
echo "Old version $last_version found in the files above."
|
|
read -r -p "Continue? [y/N] " response
|
|
case $response in
|
|
[yY][eE][sS]|[yY]) ;;
|
|
*)
|
|
echo "Aborting release merge. Please run $parentcmd $cmd bump"
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
fi
|
|
|
|
echo "Merging release-candidate into stable and develop..."
|
|
|
|
# Legend of commits.
|
|
# Each numbered commit corresponds to a set of commands below, with all `o` symbols having been
|
|
# committed by earlier steps in the release process.
|
|
#
|
|
# develop o-o----------3
|
|
# \ /
|
|
# release-candidate o--o---2
|
|
# \ /
|
|
# stable o--------1
|
|
|
|
# Commit #1: Create the releasable merge commit in stable.
|
|
git fetch
|
|
if [ ! $(git rev-parse --verify stable 2> /dev/null) ]; then
|
|
git checkout -b stable origin/stable
|
|
else
|
|
git checkout stable
|
|
fi
|
|
git rebase origin/stable
|
|
git merge --no-ff release-candidate --no-edit
|
|
|
|
# Commit #2: Connect stable to develop's ancestry by merging stable back into the
|
|
# release-candidate.
|
|
git checkout release-candidate
|
|
git merge --no-ff stable --no-edit
|
|
|
|
# Commit #3: Merge the release-candidate into develop.
|
|
if [ ! $(git rev-parse --verify develop 2> /dev/null) ]; then
|
|
git checkout -b develop origin/develop
|
|
else
|
|
git checkout develop
|
|
fi
|
|
git rebase origin/develop
|
|
# The following lines will only create one commit thanks to the --no-commit argument here.
|
|
git merge --no-ff --no-commit release-candidate --no-edit
|
|
git reset HEAD .gitattributes
|
|
git checkout -- .gitattributes
|
|
git commit -m "merged release-candidate"
|
|
git branch -D release-candidate
|
|
|
|
git checkout stable
|
|
}
|
|
|
|
publish_release() {
|
|
version=$(version_for_platform $1)
|
|
if [ -z "$version" ]; then
|
|
echo "Must provide a ${U}version${N} argument."
|
|
exit 1
|
|
fi
|
|
|
|
current_branch=$(current_branch)
|
|
if [ "$current_branch" != "stable" ]; then
|
|
echo "This command must be run from the ${B}stable${N} branch."
|
|
echo
|
|
echo "Your current branch: $current_branch"
|
|
exit 1
|
|
fi
|
|
|
|
enforce_changelog_version
|
|
|
|
ghtoken="$(cut -d " " -f 2 <<< $(cat ~/.config/gh/hosts.yml | grep "oauth_token: "))"
|
|
if [ -z "$ghtoken" ]; then
|
|
echo "Error: Unable to find a token in ~/.config/gh/hosts.yml."
|
|
echo
|
|
echo "You must authenticate with the official GitHub command line interface tool:"
|
|
echo "https://github.com/cli/cli"
|
|
echo
|
|
echo "Not to be confused with the following unofficial (and no longer maintained) GitHub command line tool that this script used to use:"
|
|
echo "https://github.com/node-gh/gh"
|
|
echo
|
|
echo "To proceed, do the following:"
|
|
echo "1. Install the official command line tool"
|
|
echo "2. Authenticate using 'gh auth login'"
|
|
echo " a. When it asks 'What account do you want to log into?', choose 'GitHub.com'"
|
|
echo " b. When it asks 'How would you like to authenticate?', choose 'Login with a web browser'"
|
|
echo "3. Re-run the scripts/release command"
|
|
echo
|
|
echo "You will probably have to delete or move the old gh to make way for the new (official) one."
|
|
exit 1
|
|
fi
|
|
|
|
curl -sH "Authorization: token $ghtoken" \
|
|
"https://api.github.com/repos/material-components/material-components-ios/releases/tags/$version" \
|
|
| grep -q 'message": "Not Found'
|
|
|
|
if [ $? -ne 0 ]; then # Found the release
|
|
echo "Release already cut."
|
|
echo
|
|
echo " Release URL: https://github.com/material-components/material-components-ios/releases/tag/$version"
|
|
open "https://github.com/material-components/material-components-ios/releases/tag/$version"
|
|
exit 0
|
|
fi
|
|
|
|
git push origin stable develop
|
|
|
|
tmp_path=$(mktemp -d)
|
|
|
|
curl -s \
|
|
-H "Authorization: token $ghtoken" \
|
|
-H "Content-Type: application/json" \
|
|
-X POST \
|
|
-d '{"tag_name":"'$version'","target_commitish":"stable","name":"'$version'","draft":true}' \
|
|
"https://api.github.com/repos/material-components/material-components-ios/releases" > "$tmp_path/release"
|
|
|
|
if [ $? -ne 0 ]; then # Found the release
|
|
echo "Failed to draft the release. Check that it doesn't already exist before continuing."
|
|
echo "https://api.github.com/repos/material-components/material-components-ios/releases"
|
|
exit 1
|
|
fi
|
|
|
|
htmlurl=$(cat "$tmp_path/release" | grep '^ "html_url' | cut -d'"' -f4)
|
|
htmlurl=$(echo $htmlurl | sed "s:/tag/:/edit/:")
|
|
|
|
echo "A draft release has been made."
|
|
echo
|
|
echo " Edit the draft: $htmlurl"
|
|
echo
|
|
echo "Update the release's description with the following:${B}"
|
|
awk '/# / { print $0; while(getline > 0) {if (/^# /) exit; print $0 }}' "$rootdir/CHANGELOG.md" | tail -n +2
|
|
echo ${N}
|
|
|
|
echo "Deleting remote release-candidate..."
|
|
git push origin :release-candidate
|
|
|
|
git checkout develop
|
|
|
|
echo "Press enter to open the release draft url in your browser:"
|
|
read
|
|
|
|
if [ "$(uname)" == "Darwin" ]; then
|
|
open $htmlurl
|
|
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
|
|
xdg-open $htmlurl
|
|
fi
|
|
}
|
|
|
|
publish_podspec() {
|
|
cat <<EOL
|
|
|\\___/|
|
|
(,\\ /,)\\
|
|
/ / \\ DRAGON SAYS HALT:
|
|
(@_^_@)/ \\ HAS THE INTERNAL CL LANDED?
|
|
W//W_/ \\ DO NOT PUBLISH THIS RELEASE UNTIL
|
|
(//) | \\ IT HAS LANDED.
|
|
(/ /) _|_ / ) \\
|
|
(// /) '/,_ _ _/ (~^-.
|
|
(( // )) ,-{ _ \`.
|
|
(( /// )) '/\\ / \\
|
|
(( ///)) \`. { } \\
|
|
((/ )) .----~-.\\ \\-' ~-__
|
|
///.----..> \\ \\_ ~--____
|
|
///-._ _ _ _} ~--------------
|
|
EOL
|
|
echo "Press enter to confirm that the internal CL has landed."
|
|
read
|
|
|
|
cd "$rootdir"
|
|
echo "Verifying Cocoapod trunk permissions"
|
|
pod trunk me > cocoapods_permissions.log
|
|
if grep -Fq "MaterialComponents" cocoapods_permissions.log; then
|
|
echo "Running a pod trunk push from stable..."
|
|
git checkout stable
|
|
pod trunk push MaterialComponents.podspec
|
|
rm cocoapods_permissions.log
|
|
else
|
|
echo "You do not have the right permissions to push the pod via the CocoaPods API. See cocoapods_permissions.log for more info."
|
|
fi
|
|
}
|
|
|
|
# Generators
|
|
|
|
generate_release_apidiff() {
|
|
CHANGELOG_TMP_PATH=$(mktemp -d)
|
|
|
|
validate_commit() {
|
|
git cat-file -t $1 >> /dev/null 2> /dev/null || { echo "$1 is not a valid commit."; exit 1; }
|
|
}
|
|
|
|
# Verify commits
|
|
if [ -n "$1" ]; then
|
|
old_commit=$(git rev-list -n 1 $1)
|
|
else
|
|
old_commit=$(git rev-list -n 1 origin/stable)
|
|
fi
|
|
new_commit=$(git rev-list -n 1 $(get_latest_branch))
|
|
|
|
if [[ -z "$old_commit" || -z "$new_commit" ]]; then
|
|
echo "Unable to get commit shas."
|
|
exit 1
|
|
fi
|
|
|
|
validate_commit $old_commit
|
|
validate_commit $new_commit
|
|
|
|
TMP_PATH=$(mktemp -d)
|
|
OLD_ROOT_PATH="$TMP_PATH/old"
|
|
NEW_ROOT_PATH="$TMP_PATH/new"
|
|
clean_clones() {
|
|
if [ ! -z "$OLD_ROOT_PATH" ]; then
|
|
rm -rf "$OLD_ROOT_PATH"
|
|
fi
|
|
if [ ! -z "$NEW_ROOT_PATH" ]; then
|
|
rm -rf "$NEW_ROOT_PATH"
|
|
fi
|
|
}
|
|
trap clean_clones EXIT
|
|
|
|
# We do not need LFS for the temporary clones; they are used solely to do API diffs.
|
|
# See https://github.com/git-lfs/git-lfs/issues/2406 for where this flag came from.
|
|
export GIT_LFS_SKIP_SMUDGE=1
|
|
|
|
"$dir/temporary_clone_at_ref" "$OLD_ROOT_PATH" $old_commit
|
|
"$dir/temporary_clone_at_ref" "$NEW_ROOT_PATH" $new_commit
|
|
|
|
unset GIT_LFS_SKIP_SMUDGE
|
|
|
|
# Find command in all component src directories and grab search path for "Material$component.h"
|
|
old_header_search_paths=""
|
|
new_header_search_paths=""
|
|
for d in $NEW_ROOT_PATH/components/*/src; do
|
|
folder=$(dirname $d)
|
|
component=$(basename $folder)
|
|
old_header_search_paths="$old_header_search_paths --oldargs -I$OLD_ROOT_PATH/components/$component/src/ "
|
|
new_header_search_paths="$new_header_search_paths --newargs -I$NEW_ROOT_PATH/components/$component/src/ "
|
|
done
|
|
|
|
if [ ! -f "$dir/external/material-motion-apidiff/src/pathapidiff" ]; then
|
|
git submodule update --init --recursive
|
|
fi
|
|
|
|
ALL_CHANGELOG_PATH="$TMP_PATH/changelog"
|
|
ALL_ERROR_LOG_PATH="$TMP_PATH/errlog"
|
|
|
|
echo "Changelog=$ALL_CHANGELOG_PATH"
|
|
echo "Errors=$ALL_ERROR_LOG_PATH"
|
|
|
|
# Run new pathdiff script on each umbrella header in array
|
|
for umbrella_header_path in $(generate_release_umbrella_headers "$old_commit" | grep -v "private"); do
|
|
umbrella_header=$(basename "$umbrella_header_path")
|
|
component=$(echo "$umbrella_header" | cut -d'.' -f1 | sed 's:^Material::')
|
|
component_path=$(dirname "$umbrella_header_path")
|
|
|
|
echo -n "Diffing $component ($component_path)..."
|
|
|
|
if [ ! -f "$OLD_ROOT_PATH/$umbrella_header_path" ]; then
|
|
echo >> $ALL_CHANGELOG_PATH
|
|
echo "### $component" >> $ALL_CHANGELOG_PATH
|
|
echo >> $ALL_CHANGELOG_PATH
|
|
if [[ "$component" = *"+"* ]]; then
|
|
echo "**New extension.**" >> $ALL_CHANGELOG_PATH
|
|
else
|
|
echo "**New component.**" >> $ALL_CHANGELOG_PATH
|
|
fi
|
|
|
|
echo "New!"
|
|
continue
|
|
fi
|
|
|
|
CHANGES_PATH="$TMP_PATH/${component}changes"
|
|
ERROR_PATH="$TMP_PATH/${component}errlog"
|
|
|
|
"$dir/external/material-motion-apidiff/src/pathapidiff" \
|
|
"$OLD_ROOT_PATH" "$NEW_ROOT_PATH" objc "/$umbrella_header_path" \
|
|
>> "$CHANGES_PATH" \
|
|
2>> "$ERROR_PATH"
|
|
|
|
if [ -s "$CHANGES_PATH" ]; then
|
|
echo >> $ALL_CHANGELOG_PATH
|
|
echo "### $component" >> $ALL_CHANGELOG_PATH
|
|
cat "$CHANGES_PATH" >> $ALL_CHANGELOG_PATH
|
|
|
|
echo -n " Changes detected."
|
|
fi
|
|
|
|
if [ -s "$ERROR_PATH" ]; then
|
|
echo "### $component" >> "$ALL_ERROR_LOG_PATH"
|
|
cat "$ERROR_PATH" >> "$ALL_ERROR_LOG_PATH"
|
|
fi
|
|
|
|
echo
|
|
done
|
|
}
|
|
|
|
generate_release_authors() {
|
|
git log origin/stable...$(get_latest_branch) --format="%ae" | sort | uniq
|
|
}
|
|
|
|
generate_release_components() {
|
|
if [ -n "$1" ]; then
|
|
old_commit="$1"
|
|
else
|
|
old_commit=origin/stable
|
|
fi
|
|
git diff --name-only "$old_commit"..$(get_latest_branch) components/ \
|
|
| grep "src/" | cut -d'/' -f2- | rev | cut -d'/' -f3- | rev | sed 's|/src||' | sort | uniq
|
|
}
|
|
|
|
generate_release_files() {
|
|
if [ -n "$1" ]; then
|
|
old_commit="$1"
|
|
else
|
|
old_commit=origin/stable
|
|
fi
|
|
git diff --name-only "$old_commit"..$(get_latest_branch) components/
|
|
}
|
|
|
|
generate_release_umbrella_headers() {
|
|
for file in $(generate_release_files "$@"); do
|
|
file_dir=$(dirname $file)
|
|
if ls "$file_dir"/Material*.h 1> /dev/null 2>&1; then
|
|
ls "$file_dir"/Material*.h | grep -v "_table"
|
|
fi
|
|
done | sort | uniq
|
|
}
|
|
|
|
generate_release_headers() {
|
|
git diff --name-only origin/stable..$(get_latest_branch) components/ \
|
|
| grep -i -e "components\/.*\/src\/.*\.h" \
|
|
| grep -v -i -e "\/private\/"
|
|
}
|
|
|
|
generate_release_log() {
|
|
git --no-pager log origin/stable..$(get_latest_branch) "$@"
|
|
}
|
|
|
|
generate_release_diff() {
|
|
git_diff=diff
|
|
if [ "$1" == "--use_diff_tool" ]; then
|
|
git_diff=difftool
|
|
shift 1
|
|
fi
|
|
|
|
git $git_diff origin/stable..$(get_latest_branch) "$@"
|
|
}
|
|
|
|
generate_release_notes() {
|
|
# Echoes the SHA's title as a CHANGELOG.md entry.
|
|
sha_to_changelog_entry() {
|
|
sha="$1"
|
|
git log \
|
|
-1 \
|
|
--pretty="* [%s](https://github.com/material-components/material-components-ios/commit/%H) (%an)" \
|
|
--no-merges \
|
|
"$sha"
|
|
}
|
|
# Returns a non-zero value if the given SHA affects any paths other than the given path.
|
|
# Ignores modifications to MaterialComponents.podspec.
|
|
does_commit_affect_other_paths() {
|
|
sha="$1"
|
|
path="$2"
|
|
git diff-tree --no-commit-id --name-only -r "$sha" \
|
|
| grep -v "$path" \
|
|
| grep -v "MaterialComponents.podspec" \
|
|
| grep -v "MaterialComponentsBeta.podspec" \
|
|
| grep -v "catalog/Podfile" \
|
|
| grep -v "snapshot_test_goldens"
|
|
}
|
|
# Echoes a list of changelog entries that affect a given path and only that path.
|
|
changes_for_path() {
|
|
path="${1:-components/}"
|
|
git log \
|
|
--pretty="%H" \
|
|
--no-merges \
|
|
origin/stable..$(get_latest_branch) "$path" | while read sha; do
|
|
if [[ ! $(does_commit_affect_other_paths "$sha" "$path") ]]; then
|
|
sha_to_changelog_entry "$sha"
|
|
fi
|
|
done
|
|
}
|
|
# Echoes a list of changes that affect the given path AND other paths.
|
|
changes_for_multiple_paths() {
|
|
path="${1:-components/}"
|
|
git log \
|
|
--pretty="%H" \
|
|
--no-merges \
|
|
origin/stable..$(get_latest_branch) "$path" | while read sha; do
|
|
if [[ $(does_commit_affect_other_paths "$sha" "$path") ]]; then
|
|
sha_to_changelog_entry "$sha"
|
|
fi
|
|
done
|
|
}
|
|
# Echoes a list of breaking changelog entries for a given path.
|
|
filter_breaking_changes() {
|
|
cat - | grep "\[.*\]\!" | perl -pe "s|\* \[\[.+?\]!|* [**Breaking**:|" | sort
|
|
}
|
|
# Echoes a list of non-breaking changelog entries for a given path.
|
|
filter_non_breaking_changes() {
|
|
cat - | grep -v "\[.*\]\!" | perl -pe "s|\* \[\[.+?\] |* [|" | sort
|
|
}
|
|
|
|
has_breaking_changes=false
|
|
has_non_breaking_changes=false
|
|
|
|
# Output breaking changes that affect specific components.
|
|
|
|
find components -type d -name 'src' | sort | while read path; do
|
|
folder=$(dirname $path)
|
|
|
|
if [[ $(changes_for_path "$folder" | filter_breaking_changes) ]]; then
|
|
component=$(echo $folder | cut -d'/' -f2-)
|
|
|
|
if [ "$has_breaking_changes" = false ]; then
|
|
has_breaking_changes=true
|
|
echo
|
|
echo "## Breaking changes"
|
|
fi
|
|
echo
|
|
echo "### $component"
|
|
echo
|
|
|
|
changes_for_path "$folder" | filter_breaking_changes
|
|
fi
|
|
done
|
|
|
|
# Output breaking changes that affect multiple components.
|
|
|
|
all_breaking_changes_for_multiple_paths() {
|
|
find components -type d -name 'src' | while read path; do
|
|
folder=$(dirname $path)
|
|
changes_for_multiple_paths "$folder" | filter_breaking_changes
|
|
done | sort | uniq
|
|
}
|
|
if [[ $(all_breaking_changes_for_multiple_paths) ]]; then
|
|
has_breaking_changes=true
|
|
echo
|
|
echo "## Multi-component breaking changes"
|
|
echo
|
|
all_breaking_changes_for_multiple_paths
|
|
fi
|
|
|
|
# Output changes that affect specific components.
|
|
|
|
find components -type d -name 'src' | sort | while read path; do
|
|
folder=$(dirname $path)
|
|
|
|
if [[ $(changes_for_path "$folder" | filter_non_breaking_changes) ]]; then
|
|
component=$(echo $folder | cut -d'/' -f2-)
|
|
|
|
echo
|
|
echo "### $component"
|
|
echo
|
|
|
|
changes_for_path "$folder" | filter_non_breaking_changes
|
|
fi
|
|
done
|
|
|
|
# Output changes that affect multiple components.
|
|
|
|
all_non_breaking_changes_for_multiple_paths() {
|
|
find components -type d -name 'src' | while read path; do
|
|
folder=$(dirname $path)
|
|
changes_for_multiple_paths "$folder" | filter_non_breaking_changes
|
|
done | sort | uniq
|
|
}
|
|
if [[ $(all_non_breaking_changes_for_multiple_paths) ]]; then
|
|
has_non_breaking_changes=true
|
|
echo
|
|
echo "## Multi-component changes"
|
|
echo
|
|
all_non_breaking_changes_for_multiple_paths
|
|
fi
|
|
}
|
|
|
|
other_thing() {
|
|
find components -type d -name 'src' | sort | while read path; do
|
|
folder=$(dirname $path)
|
|
component=$(echo $folder | cut -d'/' -f2-)
|
|
|
|
if [[ $component == private* ]]; then
|
|
continue;
|
|
fi
|
|
|
|
if [ $(git log --pretty=oneline --no-merges origin/stable..$(get_latest_branch) $folder \
|
|
| wc -l) == "0" ]; then
|
|
continue
|
|
fi
|
|
|
|
componentdiff() {
|
|
git log \
|
|
--pretty="* [%s](https://github.com/material-components/material-components-ios/commit/%H) (%an)" \
|
|
--no-merges \
|
|
origin/stable..$(get_latest_branch) \
|
|
$folder | grep -v "\[automated\]"
|
|
}
|
|
|
|
if [[ $(componentdiff) ]]; then
|
|
echo
|
|
echo "### $component"
|
|
|
|
if [[ $(componentdiff | grep "\[$component\]\!") ]]; then
|
|
echo
|
|
echo "#### Breaking changes"
|
|
echo
|
|
|
|
componentdiff \
|
|
| grep "\[$component\]\!" \
|
|
| sed "s|\[$component\]!|**Breaking**: |" \
|
|
| sort
|
|
fi
|
|
|
|
if [[ $(componentdiff | grep -v "\[$component\]\!") ]]; then
|
|
echo
|
|
|
|
componentdiff \
|
|
| grep -v "\[$component\]!" \
|
|
| sed "s|\[$component\] ||" \
|
|
| sort
|
|
fi
|
|
fi
|
|
|
|
done
|
|
}
|
|
|
|
generate_release_source() {
|
|
git diff --name-only origin/stable..$(get_latest_branch) components/ \
|
|
| grep "src/"
|
|
}
|
|
|
|
generate_release_stories() {
|
|
generate_release_log | grep -i pivotal
|
|
}
|
|
|
|
if [ $# -eq 0 ]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! $(git rev-parse --is-inside-work-tree -q 2> /dev/null) ]; then
|
|
echo "Must be run from a git directory."
|
|
exit 1
|
|
fi
|
|
|
|
if [ -t 1 ] ; then # We're writing directly to terminal
|
|
readonly B=$(tput bold)
|
|
readonly U=$(tput smul)
|
|
readonly N=$(tput sgr0)
|
|
else # We're in a pipe
|
|
readonly B=""
|
|
readonly U=""
|
|
readonly N=""
|
|
fi
|
|
|
|
rootdir=$( cd "$(dirname $(git rev-parse --git-dir))" && pwd )
|
|
|
|
#enforce_clean_state
|
|
|
|
case "$1" in
|
|
cut) cut_release ${@:2} ;;
|
|
test) test_release ${@:2} ;;
|
|
bump) bump_release ${@:2} ;;
|
|
merge) merge_release ${@:2} ;;
|
|
publish) publish_release ${@:2} ;;
|
|
podspec) publish_podspec ${@:2} ;;
|
|
|
|
apidiff) generate_release_apidiff ${@:2} ;; # args: [base sha]
|
|
authors) generate_release_authors ${@:2} ;;
|
|
components) generate_release_components ${@:2} ;; # args: [base sha]
|
|
diff) generate_release_diff ${@:2} ;;
|
|
files) generate_release_files ${@:2} ;; # args: [base sha]
|
|
headers) generate_release_headers ${@:2} ;;
|
|
log) generate_release_log ${@:2} ;;
|
|
notes) generate_release_notes ${@:2} ;;
|
|
source) generate_release_source ${@:2} ;;
|
|
stories) generate_release_stories ${@:2} ;;
|
|
umbrellas) generate_release_umbrella_headers ${@:2} ;; # args: [base sha]
|
|
|
|
abort) abort_release ${@:2} ;;
|
|
|
|
*) usage ;;
|
|
esac
|