Newer
Older
# Copyright © 2012-2016 Intel Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# Authors:
# Daniel Vetter <daniel.vetter@ffwll.ch>
# Jani Nikula <jani.nikula@intel.com>
# drm-intel-next maintainer script
# fail on any goof-up
# User configuration. Global DIM_ prefixed uppercase variables. Set in
# environment or configuration file. See dimrc.sample for an example.
#
# dim configuration file
DIM_CONFIG=${DIM_CONFIG:-$HOME/.dimrc}
if [ -r $DIM_CONFIG ]; then
# shellcheck source=/dev/null
. $DIM_CONFIG
fi
# prefix for repo directories
DIM_PREFIX=${DIM_PREFIX:-$HOME/linux}
# main maintainer repo under $DIM_PREFIX
# mail user agent. must support a subset of mutt(1) command line options:
# usage: $DIM_MUA [-s subject] [-i file] [-c cc-addr] to-addr [...]
DIM_MUA=${DIM_MUA:-mutt}
# make options (not used for C=1)
DIM_MAKE_OPTIONS=${DIM_MAKE_OPTIONS:--j20}
Ander Conselvan de Oliveira
committed
# command to run after dim apply
DIM_POST_APPLY_ACTION=${DIM_POST_APPLY_ACTION:-}
# greetings pull request template
DIM_TEMPLATE_HELLO=${DIM_TEMPLATE_HELLO:-$HOME/.dim.template.hello}
# signature pull request template
DIM_TEMPLATE_SIGNATURE=${DIM_TEMPLATE_SIGNATURE:-$HOME/.dim.template.signature}
# GPG key id for signing tags. If unset, don't sign.
DIM_GPG_KEYID=${DIM_GPG_KEYID:+-u $DIM_GPG_KEYID}
# Internal configuration. Global dim_ prefixed variables.
dim=$(basename $0)
dim_timestamp="$(date --utc +%Yy-%mm-%dd-%Hh-%Mm-%Ss) UTC"
# Recipients for all dim based pull requests.
# Add To: lines to the end, Cc: lines in the beginning with -c.
dim_pull_request_recipients=(
-c "Daniel Vetter <daniel.vetter@ffwll.ch>"
-c "Jani Nikula <jani.nikula@linux.intel.com>"
-c "Joonas Lahtinen <joonas.lahtinen@linux.intel.com>"
-c "Rodrigo Vivi <rodrigo.vivi@intel.com>"
-c "Sean Paul <seanpaul@chromium.org>"
-c "Gustavo Padovan <gustavo@padovan.org>"
-c "dri-devel@lists.freedesktop.org"
-c "intel-gfx@lists.freedesktop.org"
-c "dim-tools@lists.freedesktop.org"
"Dave Airlie <airlied@gmail.com>"
)
# Recipients for drm-intel-testing updates.
dim_test_request_recipients=(
-c "Jani Nikula <jani.nikula@linux.intel.com>"
-c "Joonas Lahtinen <joonas.lahtinen@linux.intel.com>"
-c "Rodrigo Vivi <rodrigo.vivi@intel.com>"
-c "intel-gfx@lists.freedesktop.org"
"Jari Tahvanainen <jari.tahvanainen@intel.com>"
)
dim_integration_config=nightly.conf
function read_integration_config
{
# clear everything first to allow configuration reload
unset drm_tip_repos drm_tip_config
declare -g -A drm_tip_repos
declare -g -a drm_tip_config
if [ -r $DIM_PREFIX/drm-rerere/$dim_integration_config ]; then
# shellcheck source=/dev/null
source $DIM_PREFIX/drm-rerere/$dim_integration_config
if [[ "${#drm_tip_repos[@]}" = "0" ]] || [[ "${#drm_tip_config[@]}" = "0" ]]; then
echoerr "$dim_integration_config not set up correctly, please fix manually"
echoerr "$dim_integration_config is missing, please check your configuration and/or run dim setup"
fi
dim_branches=
for conf in "${drm_tip_config[@]}"; do
local repo branch override
if [[ "$repo" = "drm-intel" || "$repo" = "drm-misc" ]]; then
dim_branches="$dim_branches $branch"
fi
done
}
function warn_or_fail
{
if [[ $FORCE ]] ; then
echoerr "WARNING: $1, but continuing"
elif [[ $DRY ]] ; then
echoerr "WARNING: $1, but continuing dry-run"
echoerr "ERROR: $1, aborting"
function pause
{
read -rsp "Press any key to continue..." -n1 key2
echo
}
#
# Only function and alias definitions until the subcommand handling at the end.
#
#
# Variable naming convetion:
#
# repo:
# symbolic git repository name from $dim_integration_config
# remote:
# local remote name in the git repository for the current path
# branch:
# git branch name - dim assumes that the remote and local name match
# url:
# url to a repo, using ssh:// protocol
# git_url:
# url to a repo, but using anonymous git:// protocol
#
# The below functions map between these.
#
function url_to_remote # url [url ...]
local url remote
if [[ "$#" = "0" ]]; then
echoerr "url_to_remote without URLs"
return 1
for url; do
remote=$(git remote -v | grep -m 1 "$url" | cut -f 1)
if [[ -n "$remote" ]]; then
echo "$remote"
return 0
fi
done
echoerr "No git remote for any of the URLs $* found in $(pwd)"
url=$1
remote=${url%.git}
remote=${remote##*/}
read -r -i "$remote" -e -p "Enter a name to auto-add this remote, leave blank to abort: " remote
if [[ -z "$remote" ]]; then
echoerr "Please set it up yourself using:"
echoerr " $ git remote add <name> $url"
echoerr "with a name of your choice."
git remote add $remote $url
echo $remote
return 0
function pick_protocol_url # (git|ssh|https|whatever) url [url ...]
{
local url protocol protocol_url
if [[ "$#" -lt "2" ]]; then
echoerr "pick_protocol_url without protocol or URLs"
return 1
fi
protocol=$1
shift
# Find the URL that has given protocol
for url; do
case $url in
${protocol}://*)
protocol_url=$url
break
;;
esac
done
if [[ -z "$protocol_url" ]]; then
echoerr "No $protocol URL in any of the URLs: $*"
return 1
fi
echo $protocol_url
}
function branch_to_remote # branch
{
branch=$1
remote=$(git rev-parse --abbrev-ref --symbolic-full-name "$branch@{upstream}")
remote=${remote%%/*}
echo $remote
}
local repo url_list
repo=$1
url_list=${drm_tip_repos[$repo]}
if [[ -z "$url_list" ]]; then
echoerr "unknown repo $repo"
return 1
fi
url_to_remote $url_list
function branch_to_repo # branch
{
for conf in "${drm_tip_config[@]}"; do
local repo branch override
if [[ "$branch" == "$1" ]] ; then
echo $repo
fi
done
echo ""
}
local using
using="${BASH_SOURCE[0]}"
if [[ ! -e "$using" ]]; then
echoerr "could not figure out the version being used ($using)."
fi
if [[ ! -e "$DIM_PREFIX/maintainer-tools/.git" ]]; then
echoerr "could not find the upstream repo for $dim."
if ! git --git-dir=$DIM_PREFIX/maintainer-tools/.git show "@{upstream}:dim" |\
diff "$using" - >& /dev/null; then
echoerr "not running upstream version of the script."
function check_for_updates
{
local stamp stampfile
stampfile=$HOME/.dim-update-check-timestamp
# daily check for updates based on file timestamp
stamp=$(stat --printf=%Y $stampfile 2>/dev/null || echo -n 0)
if [[ $((stamp + 24*60*60)) -lt $(date +%s) ]]; then
dim_uptodate || true
touch $stampfile
fi
}
function git_fetch_helper # remote
{
local remote
remote=$1
# old git versions returned 128 if there was nothing to fetch
if [[ $? -ne "128" ]] ; then
echoerr "Failed to fetch $remote"
return 1
fi
fi
}
function git_current_branch
{
git symbolic-ref -q --short HEAD
}
function git_is_current_branch # branch
{
test "$(git_current_branch)" = "$1"
function git_branch_exists # branch
{
if [[ "$(git branch --list $1)" == "" ]] ; then
false
else
true
fi
}
function git_committer_email
{
if ! committer_email=$(git config --get user.email) ; then
fi
echo $committer_email
}
function git_version_check
{
local min_version="git version 2.8"
if ! echo -e "$min_version\n$(git version)" | sort -VC; then
echoerr "WARNING: recommended minimum $min_version, you have $(git version)"
fi
}
# get message id from file
# $1 = file
message_get_id ()
{
python <<EOF
from email.parser import Parser
headers = Parser().parse(open('$1', 'r'))
message_id = headers['message-id']
if message_id is not None:
print(message_id.strip('<>'))
EOF
}
message_print_body ()
{
python2 <<EOF
import email
def print_part(part):
mtype = part.get_content_maintype()
if mtype == 'text':
print(part.get_payload(decode=True))
def print_msg(file):
msg = email.message_from_file(file)
if msg.is_multipart():
for part in msg.get_payload():
print_part(part)
else:
print_part(msg)
print_msg(open('$1', 'r'))
EOF
}
# append all arguments as tags at the end of the commit message of HEAD
function dim_commit_add_tag
for arg; do
# the first sed deletes all trailing blank lines at the end
git log -1 --pretty=%B | \
sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' | \
sed "\$a${arg}" | \
git commit --amend -F-
done
# $1: branch [optional]
function git_find_tip
{
git log $1 -1 --format=%H --grep="^drm-tip: .* integration manifest$"
}
# $1: branch [optional]
function dim_retip
{
local branch upstream new_upstream
if [ -n "$branch" ] ; then
shift
else
branch=$(git symbolic-ref --short HEAD)
fi
if repo_to_remote drm-tip &> /dev/null ; then
new_upstream=$(repo_to_remote drm-tip)/drm-tip
else
new_upstream=$(git branch -r | grep '\/drm-tip$')
upstream=$(git_find_tip "$branch")
if [[ -z "$upstream" ]]; then
echoerr "$branch is not based on drm-tip"
return 1
fi
git rebase --onto $new_upstream $upstream $branch "$@"
# update for-linux-next and for-linux-next-fixes branches
function update_linux_next # branch next next-fixes fixes
local branch linux_next linux_next_fixes linux_fixes repo remote
branch=$1
linux_next=$2
linux_next_fixes=$3
linux_fixes=$4
if [[ $repo != $(branch_to_repo $linux_next) ]] ; then
# always update drm-intel-fixes
echo -n "Pushing $linux_fixes to for-linux-next-fixes... "
git push $DRY_RUN $remote +$remote/$linux_fixes:for-linux-next-fixes # >& /dev/null
if git merge-base --is-ancestor $remote/$linux_next_fixes $remote/$linux_fixes ; then
# -fixes has caught up to dinf, i.e. we're out of the merge
# window. Push the next queue.
echo -n "Out of merge window. Pushing $linux_next to for-linux-next... "
git push $DRY_RUN $remote +$remote/$linux_next:for-linux-next >& /dev/null
# dinf is ahead of -fixes, i.e. drm-next has already closed for
# the next merge window and we've started to gather new fixes
# for the current -next cycle. Push dinf
echo -n "Pushing $linux_next_fixes to for-linux-next... "
git push $DRY_RUN $remote +$remote/$linux_next_fixes:for-linux-next >& /dev/null
function check_conflicts # tree
if git diff | grep -q '\(<<<<<<<\|=======\|>>>>>>>\||||||||\)' ; then
# we need an empty line to make it look pretty
echoerr ""
echoerr "FAILURE: Could not merge $1"
echoerr "See the section \"Resolving Conflicts when Rebuilding drm-tip\""
echoerr "in the drm-intel.rst documentation for how to handle this situation."
echo $DIM_PREFIX/drm-tip/.git/rr-cache
if ! git_is_current_branch rerere-cache; then
echo "Fail: Branch setup for the rerere-cache is borked."
exit 1
fi
if ! git pull -q ; then
# We raced with someone else hitting the same conflict, or the
# erratic git gc for rr-cache entries nuke a few entries we
# still want to keep. Clean up and try, but only when the
# initial pull fails since otherwise there's no way to keep new
# resolutions around.
echo "Conflict in the rr-cache, cleaning up"
git clean -fdx rr-cache/
git checkout -f ':(glob)rr-cache/**'
if ! git pull -q ; then
echoerr "Failed to update the rerere cache."
echoerr "Please manually run"
echoerr " $ cd $DIM_PREFIX/drm-rerere ; git pull"
echoerr "and fixup any issues."
cd - > /dev/null
}
function update_rerere_cache
{
echo -n "Updating rerere cache... "
pull_rerere_cache
if [ -d $(rr_cache_dir) ] ; then
rm -Rf $(rr_cache_dir)
fi
ln -s "$DIM_PREFIX/drm-rerere/rr-cache" $(dirname $(rr_cache_dir))
fi
echo "Done."
}
function commit_rerere_cache
{
local remote file commit_message
cd $DIM_PREFIX/drm-rerere/
remote=$(branch_to_remote rerere-cache)
git add ./*.patch >& /dev/null || true
git add fixups/*.patch >& /dev/null || true
for file in $(git ls-files -- rr-cache); do
if ! git log --since="60 days ago" --name-only -- $file | grep $file &> /dev/null; then
git rm $file &> /dev/null || true
done
find rr-cache/ -mtime -1 -type f -not -name "thisimage*" -print0 | xargs -0 git add > /dev/null || true
git rm rr-cache/rr-cache &> /dev/null || true
commit_message=$(mktemp)
cat > $commit_message <<-EOF
$dim_timestamp: $integration_branch rerere cache update
$(git --version)
EOF
if git commit -F $commit_message >& /dev/null; then
echo -n "Pushing rerere cache... "
git push $DRY_RUN $remote HEAD >& /dev/null && echo "Done."
function fetch_all
{
for repo in "${!drm_tip_repos[@]}"; do
remote=$(repo_to_remote $repo)
echo -n "Fetching $repo (local remote $remote)... "
git_fetch_helper $remote
echo "Done."
done
}
function find_fixup_file # repo branch
{
local file_paths repo branch rerere
repo=$1
branch=$2
rerere=$DIM_PREFIX/drm-rerere
file_paths="$rerere/${repo}-${branch//\//-}-fixup.patch
$rerere/fixups/${branch//\//-}.patch"
for file_path in $file_paths; do
[ -f "$file_path" ] && break
done
echo "$file_path"
}
local integration_branch specfile first rerere repo remote
integration_branch=drm-tip
first=1
rerere=$DIM_PREFIX/drm-rerere
if git status --porcelain | grep -v "^.. rr-cache" | grep -q -v "^[ ?][ ?]"; then
warn_or_fail "integration configuration file $dim_integration_config not committed"
echo -n "Reloading $dim_integration_config... "
read_integration_config
cd $DIM_PREFIX/$integration_branch
if ! git_is_current_branch $integration_branch ; then
echo "Branch setup for the integration repo is borked"
exit 1
fi
# rerere uses sha1 of the diff to identify conflicts, we must ensure
# that they look the same for everyone
git config merge.conflictstyle merge
for conf in "${drm_tip_config[@]}"; do
local branch override sha1 fixup_file
remote=$(repo_to_remote $repo)
sha1=$remote/$branch
echo -n "Merging $repo (local remote $remote) $branch... "
if [[ -n "$override" ]]; then
sha1=$override
echo -n "Using override sha1: $sha1... "
git reset --hard $sha1 &> /dev/null
elif git merge --rerere-autoupdate --ff-only $sha1 >& /dev/null ; then
# nothing to do if just fast-forward
fixup_file=$(find_fixup_file $repo $branch)
echo $branch > .fixup_branch
git merge --rerere-autoupdate --no-commit $sha1 >& /dev/null || true
# normalize conflict markers
if git grep -l '^>>>>>>> ' &> /dev/null ; then
git grep -l '^>>>>>>> ' | xargs sed -e "s|^>>>>>>> .*$|>>>>>>> $repo\/$branch|" -i
echo -n "Applying manual fixup patch for $integration_branch merge... "
check_conflicts "$repo/$branch"
# because we filter out fast-forward merges there will
# always be something to commit
git commit --no-edit --quiet
echo "Done."
echo -e "$repo $branch $(git rev-parse $sha1)\n\t$(git log -1 $sha1 --pretty=format:%s)" >> $specfile
echo -n "Adding integration manifest $integration_branch: $dim_timestamp... "
mv $specfile integration-manifest
git add integration-manifest
git commit --quiet -m "$integration_branch: $dim_timestamp integration manifest"
remote=$(repo_to_remote drm-tip)
echo -n "Pushing $integration_branch... "
git push $DRY_RUN $remote +HEAD >& /dev/null && echo "Done."
# additional patch checks before pushing, e.g. for r-b tags
function checkpatch_commit_push
{
# use real names for people with many different email addresses
author=$(git show -s $sha1 --format="format:%an")
committer=$(git show -s $sha1 --format="format:%cn")
# outlook mangles mails into "Last, First"
author_outlook=$(git show -s $sha1 --format="format:%an" | sed -e 's/\([^ ]*\) \(.*\)/\2, \1/')
# check for author sign-off
if ! git show -s $sha1 | grep -qi "S.*-by:.*\\($author\\|$author_outlook\\)" ; then
warn_or_fail "$sha1 is lacking author of sign-off"
fi
# check for committer sign-off
if ! git show -s $sha1 | grep -qi "S.*-by:.*$committer" ; then
warn_or_fail "$sha1 is lacking committer of sign-off"
fi
# check for Link tag
if [[ "$managed_branch" = "1" ]] && ! git show -s $sha1 | grep -qi 'Link:' ; then
warn_or_fail "$sha1 is lacking of link tag"
fi
# check for a-b/r-b tag
if git show -s $sha1 | grep -qi '\(reviewed\|acked\)\S*-by:' ; then
return
fi
# check for committer != author
return
fi
warn_or_fail "$sha1 is lacking mandatory review"
}
# push branch $1, rebuild drm-tip. the rest of the arguments are passed to git
shift
assert_branch $branch
committer_email=$(git_committer_email)
for sha1 in $(git rev-list "$branch@{u}..$branch" --committer="$committer_email" --no-merges) ; do
checkpatch_commit_push $sha1
done
git push $DRY_RUN $remote $branch "$@"
update_linux_next $branch drm-intel-next-queued drm-intel-next-fixes drm-intel-fixes
update_linux_next $branch drm-misc-next drm-misc-next-fixes drm-misc-fixes
update_linux_next $branch drm-amd-next drm-amd-next-fixes drm-amd-fixes
dim_alias_pq=push-queued
function dim_push_queued
{
dim_push_branch drm-intel-next-queued "$@"
}
dim_alias_pnf=push-next-fixes
function dim_push_next_fixes
{
dim_push_branch drm-intel-next-fixes "$@"
}
dim_alias_pf=push-fixes
function dim_push_fixes
{
dim_push_branch drm-intel-fixes "$@"
}
function dim_push
{
dim_push_branch $(git_current_branch) "$@"
}
function apply_patch #patch_file
local patch message_id committer_email patch_from sob rv
message_id=$(message_get_id $patch)
patch_from=$(grep "From:" "$patch" | head -1)
if [[ "$patch_from" != *"$committer_email"* ]] ; then
git am --scissors -3 $sob "$@" $patch
if [ -n "$message_id" ]; then
dim_commit_add_tag "Link: https://patchwork.freedesktop.org/patch/msgid/$message_id"
echoerr "WARNING: No message-id found in the patch file."
rv=1
if ! checkpatch_commit HEAD branch; then
if ! check_maintainer $branch HEAD; then
rv=1
fi
Ander Conselvan de Oliveira
committed
eval $DRY $DIM_POST_APPLY_ACTION
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
return $rv
}
# ensure we're on branch $1, and apply patches. the rest of the arguments are
# passed to git am.
dim_alias_ab=apply-branch
dim_alias_sob=apply-branch
function dim_apply_branch
{
local branch file rv
branch=${1:?$usage}
shift
file=$(mktemp)
dir=$(mktemp -d)
assert_branch $branch
assert_repo_clean
cat > $file
git mailsplit -b -o$dir $file > /dev/null
for patch in $dir/*; do
if ! apply_patch $patch "$@"; then
rv=1
fi
done
rm -rf $file $dir
function dim_apply_pull
{
local branch file message_id pull_branch rv
branch=${1:?$usage}
file=$(mktemp)
assert_branch $branch
assert_repo_clean
cat > $file
pull_branch=$(sed -e '0,/[gG]it repository at:$/d' $file | head -n 2 | tail -n 1)
git fetch $pull_branch
for sha1 in $(git rev-list "HEAD..FETCH_HEAD" --no-merges) ; do
checkpatch_commit_push $sha1 0
done
$DRY git pull $pull_branch
dim_commit_add_tag "Link: https://patchwork.freedesktop.org/patch/msgid/$message_id"
else
echoerr "WARNING: No message-id found in the patch file."
rv=1
fi
eval $DRY $DIM_POST_APPLY_ACTION
function dim_backmerge
{
local branch upstream patch_file
branch=${1:?$usage}
upstream=${2:?$usage}
if ! dim_list_upstreams | grep -q "^$upstream\$"; then
warn_or_fail "$upstream is not an upstream branch"
fi
tip_remote=$(repo_to_remote drm-tip)
git fetch -q $tip_remote || true
if ! git merge-base --is-ancestor $upstream $tip_remote/drm-tip ; then
echoerr "Upstream $upstream not merged into drm-tip, aborting."
echoerr "Please make sure any backmerge is tested in drm-tip,"
echoerr "to give all the CI bots some time to find bugs."
exit 1
fi
assert_branch $branch
assert_repo_clean
git merge --rerere-autoupdate --no-commit $upstream >& /dev/null || true
if [[ -d .git ]]; then
patch_file=".git"
else
patch_file=$(cut -d ' ' -f 2 .git)
fi
patch_file=$patch_file/MERGE_MSG
cat > $patch_file <<-HERE
Merge $upstream into $branch
*** DETAILED BACKMERGE RATIONALE HERE ***
HERE
if git diff | grep -q '\(<<<<<<<\|=======\|>>>>>>>\||||||||\)' ; then
echoerr "Conflicts found while merging $upstream into $branch."
echoerr "This should only happen when git rerere gets confused"
echoerr "or if there's a manual fixup patch in drm-rerere."
echoerr "Please proceed with extreme caution."
echoerr "Once the conflict is resolved, commit it with"
echoerr " git commit -a"
fi
git add -u
git commit -s
}
function dim_add_link
{
local branch file message_id
shift
file=$(mktemp)
assert_branch $branch
assert_repo_clean
cat > $file
message_id=$(message_get_id $file)
rm -f $file
dim_commit_add_tag "Link: https://patchwork.freedesktop.org/patch/msgid/$message_id"