blob: 35e1163cf4eb01ebd6aaa7b3050902c0d091aaab [file] [log] [blame]
# Copyright 2018-2020 Fairphone B.V.
#
# 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.
#
# repo forall -c environment:
# pwd is the project's working directory. If the current client is a
# mirror client, then pwd is the Git repository.
#
# REPO_PROJECT is set to the unique name of the project.
#
# REPO_PATH is the path relative the root of the client.
#
# REPO_REMOTE is the name of the remote system from the manifest.
#
# REPO_LREV is the name of the revision from the manifest, translated to a
# local tracking branch. If you need to pass the manifest revision to a
# locally executed git command, use REPO_LREV.
#
# REPO_RREV is the name of the revision from the manifest, exactly as
# written in the manifest.
#
# REPO_COUNT is the total number of projects being iterated.
#
# REPO_I is the current (1-based) iteration count. Can be used in
# conjunction with REPO_COUNT to add a simple progress indicator to your
# command.
#
# REPO__* are any extra environment variables, specified by the
# "annotation" element under any project element. This can be useful for
# differentiating trees based on user-specific criteria, or simply
# annotating tree details.#
#
#
# Globals:
# SHELL_LIBS
set -e
# Includes:
. ${SHELL_LIBS}/utils.sh
# Environment variables:
VERBOSE="${VERBOSE:-}"
DEBUG="${DEBUG:-}"
DRY_RUN="${DRY_RUN:-}" #'--dry-run'. If set, run ssh/git commands without modify remote.
PUSH_SKIP_VALIDATION="${PUSH_SKIP_VALIDATION:-false}"
TARGET_GERRIT_NAME="${TARGET_GERRIT_NAME:-fairphone}"
TARGET_GERRIT_URL="${TARGET_GERRIT_URL:-review.fairphone.software}"
TARGET_GERRIT_PORT="${TARGET_GERRIT_PORT:-29418}"
TARGET_GERRIT_PARENT_PROJECT="${TARGET_GERRIT_PARENT_PROJECT:-Marshmallow}"
# Check if a reference exists on project remote.
#
# Globals:
# REPO_REMOTE
# Arguments:
# ref The reference to check.
# Returns:
# 0 if found, otherwise the exit code for 'git ls-remote'
_remote_ref_exists()
{
local ref="$1"
local result=0
git ls-remote --exit-code ${REPO_REMOTE} ${ref} > /dev/null 2>&1 \
|| result=$?
case $result in
0)
;;
2)
log_e "${ref} does not exist on remote '${REPO_REMOTE}'"
;;
128)
log_e "${REPO_REMOTE} is not a valid remote"
;;
*)
log_e "[$result]: Unable to 'git ls-remote ${REPO_REMOTE} ${ref}'"
;;
esac
return "${result}"
}
# Check if a reference exists.
#
# Globals:
# None
# Arguments:
# ref The explicit reference path to check.
# Returns:
# Exit code of 'git show-ref'
_local_ref_exists()
{
local ref="$1"
local result=0
git show-ref --quiet --verify ${ref} || result=$?
return "${result}"
}
# Fetch a reference from a remote.
#
# If we have a shallow copy of the project, remove this condition.
#
# Globals:
# REPO_PROJECT
# REPO_REMOTE
# VERBOSE
# Arguments:
# ref_spec Specifies which refs to fetch and which local refs to update.
# Returns:
# None
_git_fetch()
{
local ref_spec="$1"
local unshallow_only=false
if [ $# -eq 2 ] ; then
if [ "$2" = "--unshallow_only" ]; then
unshallow_only=true
else
log_e "Error in _git_fetch: Invalid second parameter: $2"
fi
fi
# Update shallow copies if needed
local unshallow=
if [ -f .git/shallow ] ; then
log_i "Removing shallow clone for ${REPO_PROJECT}"
unshallow="--unshallow"
elif [ "${unshallow_only}" = true ]; then
return 0
fi
local result=0
git fetch --no-tags ${unshallow} ${VERBOSE} "${REPO_REMOTE}" \
"${ref_spec}" || result=$?
case $result in
0)
;;
*)
log_e "[$result]: Error fetching ${ref_spec} from ${REPO_REMOTE}."
;;
esac
}
# Push a reference to the remote gerrit.
#
# Globals:
# TARGET_GERRIT_NAME
# DRY_RUN
# PUSH_SKIP_VALIDATION
# Arguments:
# ref_spec Specify what destination ref to update with what source object.
# Returns:
# None
_git_push()
{
local ref_spec="$1"
local param_skip_validation=""
if [ "${PUSH_SKIP_VALIDATION}" = true ]; then
param_skip_validation="-o skip-validation"
fi
local result=0
git push ${DRY_RUN} ${param_skip_validation} "${TARGET_GERRIT_NAME}" "${ref_spec}" || result=$?
case $result in
0)
;;
128)
remote_url="$(git config --get "remote.${TARGET_GERRIT_NAME}.url")"
log_e "${remote_url} is not a valid remote."
;;
*)
log_e "[$result]: Error pushing ${ref_spec} to \
${TARGET_GERRIT_NAME}."
;;
esac
}
# Run a gerrit command on the remote gerrit server.
#
# Globals:
# TARGET_GERRIT_PORT
# TARGET_GERRIT_URL
# Arguments:
# cmd The gerrit command
# Returns:
# Result of the gerrit command
_ssh_gerrit()
{
local result=0
ssh -p "${TARGET_GERRIT_PORT}" "${TARGET_GERRIT_URL}" gerrit $@ \
|| result=$?
case $result in
0)
;;
*)
log_e "[$result]: Error running 'ssh gerrit $@'"
;;
esac
return "${result}"
}
# Grep all git logs for patterns.
#
# Globals:
# REPO_PROJECT
# REPO_REMOTE
# Arguments:
# List of space separated patterns to grep
# Returns:
# None
git_grep () {
local greps=
for pattern in $@ ; do
greps="${greps} --grep ${pattern}"
done
local oneline="--oneline"
if [ -n "${VERBOSE}" ] ; then
oneline=
fi
log_bold "[${REPO_REMOTE}] ${REPO_PROJECT}:"
git --no-pager log ${oneline} --all ${greps}
}
# Add remote gerrit to local git project if missing.
#
# Globals:
# TARGET_GERRIT_NAME
# TARGET_GERRIT_PORT
# TARGET_GERRIT_URL
# REPO_PROJECT
# Arguments:
# None
# Returns:
# None
add_git_remote()
{
if [ -z "$(git config --get "remote.${TARGET_GERRIT_NAME}.url")" ] ; then
log_i "Adding remote '${TARGET_GERRIT_NAME}' to ${REPO_PROJECT}"
local gerrit="ssh://${TARGET_GERRIT_URL}:${TARGET_GERRIT_PORT}"
git remote add "${TARGET_GERRIT_NAME}" "${gerrit}/${REPO_PROJECT}"
fi
}
# Add missing project to remote gerrit if missing.
#
# Globals:
# TARGET_GERRIT_URL
# REPO_PROJECT
# DRY_RUN
# Arguments:
# None
# Returns:
# None
add_project_to_gerrit()
{
# Does project exist on remote?
[ -n "$(_ssh_gerrit ls-projects -r ^${REPO_PROJECT}$)" ] && return
if [ -z "${DRY_RUN}" ] ; then
local result=0
_ssh_gerrit create-project "${REPO_PROJECT}" \
-p "${TARGET_GERRIT_PARENT_PROJECT}" || result=$?
if [ $result -eq 0 ] ; then
log_i " ${REPO_PROJECT} added to ${TARGET_GERRIT_URL}"
fi
else
# No dry-run options exists for 'gerrit create-project'
log_i "ssh gerrit create-project ${REPO_PROJECT} -p \
${TARGET_GERRIT_PARENT_PROJECT}"
fi
}
# Fetch and save a reference from a remote.
#
# If we have a shallow copy, remove this condition.
#
# Globals:
# REPO_REMOTE
# Arguments:
# from_ref The explicit ref path of the ref to fetch.
# to_ref The explicit ref path of where to save the ref.
# Returns:
# None
fetch_remote_ref()
{
local from_ref=$1
local to_ref=$2
local result=0
_remote_ref_exists "${from_ref}" || result=$?
if [ $result -eq 0 ] ; then
_git_fetch "${from_ref}:${to_ref}"
fi
}
# Fetch a branch from a remote.
#
# Globals:
# REPO_REMOTE
# Arguments:
# branch
# Returns:
# None
fetch_remote_branch()
{
local branch="$1"
[ -n "${branch}" ] || log_e "Missing branch to fetch"
local from_branch="refs/heads/${branch}"
local to_branch="refs/remotes/${REPO_REMOTE}/${branch}"
fetch_remote_ref "${from_branch}" "${to_branch}"
}
# Fetch a tag from a remote.
#
# Globals:
# None
# Arguments:
# tag
# Returns:
# None
fetch_remote_tag()
{
local tag="$1"
[ -n "${tag}" ] || log_e "Missing tag to fetch"
fetch_remote_ref "refs/tags/${tag}" "refs/tags/${tag}"
}
# Globals:
# REPO_RREV
# Arguments:
# None
# Returns:
# None
remove_shallow_clone()
{
# Update shallow copies if needed
if [ -f .git/shallow ] ; then
_git_fetch "${REPO_RREV}:${REPO_RREV}"
fi
}
# Push a reference to the remote gerrit.
#
# If the branch exists both locally and on remote, we default to pushing the
# local branch.
#
# Globals:
# TARGET_GERRIT_NAME
# REPO_REMOTE
# Arguments:
# branch_src Name of the local or remote branch to push.
# branch_target Name of the target branch to push to.
# Returns:
# None
push_branch_to_gerrit()
{
local branch_src="$1"
local branch_target="$2"
[ -n "${branch_src}" ] || log_e 'Missing branch parameter'
[ -n "${branch_target}" ] || log_e 'Missing target branch parameter'
local from_ref=
local to_ref="refs/heads/${branch_target}"
local result=0
_local_ref_exists "refs/heads/${branch_src}" || result=$?
if [ $result -eq 0 ] ; then
from_ref="refs/heads/${branch_src}"
else
result=0
_local_ref_exists "refs/remotes/${REPO_REMOTE}/${branch_src}" \
|| result=$?
if [ $result -eq 0 ] ; then
from_ref="refs/remotes/${REPO_REMOTE}/${branch_src}"
else
log_e "Unable to find local branch '${branch_src}' to push."
fi
fi
if [ -n "${from_ref}" ] ; then
_git_push "${from_ref}:${to_ref}"
fi
}
# Push a tag to the remote gerrit.
#
# Globals:
# TARGET_GERRIT_NAME
# Arguments:
# Tag Name of the tag to push.
# Returns:
# None
push_tag_to_gerrit()
{
local tag="refs/tags/$1"
[ -n "${tag}" ] || log_e 'Missing tag parameter'
local result=0
_local_ref_exists "${tag}" || result=$?
if [ $result -eq 0 ] ; then
_git_push "${tag}:${tag}"
fi
}
# Push the remote revision that the manifest is currently pointing to.
#
# Globals:
# TARGET_GERRIT_NAME
# Arguments:
# None
# Returns:
# None
push_current_remote_revision()
{
# Fetch again to ensure we unshallow if needed.
_git_fetch "${REPO_RREV}" --unshallow_only
# Push from the current local to the remote revision on the target Gerrit.
_git_push "${REPO_LREV}:${REPO_RREV}"
}
[ -n "${DEBUG}" ] && set -x || true