| # 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" |
| |
| # Update shallow copies if needed |
| local unshallow= |
| if [ -f .git/shallow ] ; then |
| log_i "Removing shallow clone for ${REPO_PROJECT}" |
| unshallow="--unshallow" |
| 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 |
| # Try fetching first. In case of failures, use `git ls-remote` to check for |
| # details. Don't always call it; otherwise upstream will block connections |
| # because of too many requests. |
| ( |
| # _git_fetch exits in case of errors, so run it in a sub shell. |
| _git_fetch "${from_ref}:${to_ref}" |
| ) |
| |
| local result=$? |
| if [ "${result}" -ne 0 ]; then |
| # Try getting some details on what went wrong: |
| _remote_ref_exists "${from_ref}" || true |
| 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() |
| { |
| _git_push "${REPO_LREV}:${REPO_RREV}" |
| } |
| |
| [ -n "${DEBUG}" ] && set -x || true |