From d06b9aa7639ad0267ed89234465551c12c0d0f9e Mon Sep 17 00:00:00 2001 From: Scott Little Date: Wed, 25 Sep 2019 14:27:40 -0400 Subject: [PATCH] Branch and tag tools converted from wrgit to repo Update branching and tagging tools, used by patching, to do things the 'repo' way. Repo has replaced wrgit. This is complicated by ... 1) Not all repos are under our control, and so can't be branched or tagged by us. The tools now accepts arguments that list 'remotes' or 'projects' to which branches or tags are to be applied 2) The manifest requires an update to reference the new branch or tag as it applies to affected projects. Non-affected projects can optionally be locked down by converting the revision to a sha. The tools must now try to generate that manifest. It might be sub-optimal. i.e. a revision is explicitly added to each project, rather than using the 'default' mechanism. Perhaps something to address in a future update. ORIGINATING_BRANCH=master ORIGINATING_BUILD=20200220T023000Z NEW_BRANCH=r/stx.4.0 STX_REMOTES="starlingx" repo init -u https://opendev.org/starlingx/manifest -b $ORIGINATING_BRANCH repo sync --force-sync curl http://mirror.starlingx.cengn.ca/mirror/starlingx/${ORIGINATING_BRANCH}/centos/${ORIGINATING_BUILD}/outputs/CONTEXT.sh > ${ORIGINATING_BUILD}.context source ${ORIGINATING_BUILD}.context create_branches_and_tags.sh --branch=$NEW_BRANCH --remotes=$STX_REMOTES --manifest --lockdown push_branches_tags.sh --branch=$NEW_BRANCH --remotes=$STX_REMOTES --manifest ORIGINATING_BRANCH=r/stx.4.0 ORIGINATING_BUILD=20200220T023000Z NEW_STX_TAG=4.0.0 NEW_GIT_HUB_TAG=stx.4.0.0 STX_REMOTES="starlingx" GIT_HUB_REMOTES="stx-staging" repo init -u https://opendev.org/starlingx/manifest -b $ORIGINATING_BRANCH repo sync --force-sync curl http://mirror.starlingx.cengn.ca/mirror/starlingx/${ORIGINATING_BRANCH}/centos/${ORIGINATING_BUILD}/outputs/CONTEXT.sh > ${ORIGINATING_BUILD}.context source ${ORIGINATING_BUILD}.context TAG=TEST_19.10_PATCH_0000 create_tags.sh --tag=$NEW_STX_TAG --remotes=$STX_REMOTES,$GIT_HUB_REMOTES --manifest --manifest-prefix=stx. --lock-down create_tags.sh --tag=$NEW_GIT_HUB_TAG --remotes=$GIT_HUB_REMOTES push_tags.sh --tag=$NEW_STX_TAG --remotes=$STX_REMOTES push_tags.sh --tag=$NEW_GIT_HUB_TAG --remotes=$STX_REMOTES cd .repo/manifests git mv $TAG-default.xml stx.$TAG.xml vi stx.$TAG.xml # set revision for all stx-staging projects to refs/tags/stx.4.0.0 # delete 'revision' for all starlingx projects git add stx.$TAG.xml git commit -s --amend # set title to Locked down manifest for StarlingX $TAG git tag -d $TAG git review git tag -s -m "Tag $TAG" $TAG git push gerrit $TAG Change-Id: I5ac0c3e44ffda262edb416e877d46c9036cd6e92 Signed-off-by: Scott Little --- build-tools/branching/branch_and_tag.sh | 76 ---- .../branching/create_branches_and_tags.sh | 287 +++++++++++++-- build-tools/branching/create_tags.sh | 237 ++++++++++++- build-tools/branching/push_branches_tags.sh | 257 ++++++++++++-- build-tools/branching/push_tags.sh | 241 ++++++++++++- build-tools/git-repo-utils.sh | 229 ++++++++++++ build-tools/git-utils.sh | 333 ++++++++++++++++++ build-tools/repo-utils.sh | 237 +++++++++++++ 8 files changed, 1739 insertions(+), 158 deletions(-) delete mode 100755 build-tools/branching/branch_and_tag.sh create mode 100644 build-tools/git-repo-utils.sh create mode 100644 build-tools/repo-utils.sh diff --git a/build-tools/branching/branch_and_tag.sh b/build-tools/branching/branch_and_tag.sh deleted file mode 100755 index dd9e3077..00000000 --- a/build-tools/branching/branch_and_tag.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash - -# The purpose of this script is to create branches and tags that follow a convention -# If the desired branch already exists, it is skipped. -# If the desired tag already exists, it is skipped. - -OLD_TAG=vCGCS_DEV_0018 -NEW_TAG=vCGCS_DEV_0019 - -OLD_BRANCH=CGCS_DEV_0018 -NEW_BRANCH=CGCS_DEV_0019 - -if [ -z "$MY_REPO" ]; then - echo "MY_REPO is unset" - exit 1 -else - echo "MY_REPO is set to '$MY_REPO'" -fi - -if [ -d "$MY_REPO" ]; then - cd $MY_REPO - echo "checking out and pulling old branch" - wrgit checkout $OLD_BRANCH - if [ $? -ne 0 ]; then - echo "ERROR: wrgit checkout $OLD_BRANCH" - exit 1 - fi - - wrgit pull - if [ $? -ne 0 ]; then - echo "ERROR: wrgit pull" - exit 1 - fi -else - echo "Could not change to diectory '$MY_REPO'" - exit 1 -fi - -echo "Finding subgits" -SUBGITS=`find . -type d -name ".git" | sed "s%/\.git$%%"` - -# Go through all subgits and create the NEW_BRANCH if it does not already exist -# Go through all subgits and create the NEW_TAG if it does not already exist -for subgit in $SUBGITS; do - echo "" - echo "" - pushd $subgit > /dev/null - git fetch - git fetch --tags - # check if destination branch already exists - echo "$subgit" - branch_check=`git branch -a --list $NEW_BRANCH` - if [ -z "$branch_check" ]; then - echo "Creating $NEW_BRANCH" - git checkout $OLD_BRANCH - git checkout -b $NEW_BRANCH - git push origin $NEW_BRANCH:$NEW_BRANCH - else - echo "$NEW_BRANCH already exists" - fi - tag_check=`git tag -l $NEW_TAG` - if [ -z "$tag_check" ]; then - echo "Creating $NEW_TAG" - # create tag - git checkout $NEW_BRANCH - git pull origin - git tag $NEW_TAG - git push origin $NEW_TAG - else - echo "$NEW_TAG already exists" - fi - - popd > /dev/null -done - -echo "All done. branches and tags are pushed" diff --git a/build-tools/branching/create_branches_and_tags.sh b/build-tools/branching/create_branches_and_tags.sh index 01556387..a371cd13 100755 --- a/build-tools/branching/create_branches_and_tags.sh +++ b/build-tools/branching/create_branches_and_tags.sh @@ -1,54 +1,287 @@ #!/bin/bash -if [ x"$1" = x ] ; then - echo "ERROR: You must specify a name to create branches and tags" +# +# Copyright (c) 2020 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# Create a git branch from the current heads on a subset of git projects +# within a manifest. A tag is also created to mark the commit where +# the branch was forked from. The subset of projects affected can be +# identified by project name or remote name. +# +# Optionally a new manifest (.xml) can be created that selects +# the new branch for the affected projects. As a further option, +# projects that are not branched can me 'locked down' within the new +# manifest by setting the sha of the current head as the revision. +# +# See also: push_branches_tags.sh + +CREATE_BRANCHES_AND_TAGS_SH_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}" )" )" + +source "${CREATE_BRANCHES_AND_TAGS_SH_DIR}/../git-repo-utils.sh" + +usage () { + echo "create_branches_and_tags.sh --branch= [--tag=] [ --remotes= ] [ --projects= ] [ --manifest [ --lock-down ]]" + echo "" + echo "Create a branch and a tag in all listed projects, and all" + echo "projects hosted by all listed remotes. Lists are comma separated." + echo "" + echo "The branch name must be provided. The tag name can also be provided." + echo "If the tag is omitted, one is automativally generate by adding the" + echo "prefix 'v' to the branch name." + echo "" + echo "If a manifest is requested, it will recieve the name '.xml' and" + echo "it will specify the branch as the revision for all tagged projects." + echo "If lockdown is requested, all other projects get the current" + echo "HEAD's sha set as the revision." +} + +TEMP=$(getopt -o h --long remotes:,projects:,branch:,tag:,manifest,lock-down,help -n 'create_branches_and_tags.sh' -- "$@") +if [ $? -ne 0 ]; then + usage exit 1 fi -branch=$1 -tag="v$branch" +eval set -- "$TEMP" + +HELP=0 +MANIFEST=0 +LOCK_DOWN=0 +remotes="" +projects="" +branch="" +tag="" +manifest="" +new_manifest="" +repo_root_dir="" + +while true ; do + case "$1" in + -h|--help) HELP=1 ; shift ;; + --remotes) remotes+=$(echo "$2 " | tr ',' ' '); shift 2;; + --projects) projects+=$(echo "$2 " | tr ',' ' '); shift 2;; + --branch) branch=$2; shift 2;; + --tag) tag=$2; shift 2;; + --manifest) MANIFEST=1 ; shift ;; + --lock-down) LOCK_DOWN=1 ; shift ;; + --) shift ; break ;; + *) usage; exit 1 ;; + esac +done + +if [ $HELP -eq 1 ]; then + usage + exit 0 +fi + +if [ "$branch" == "" ] ; then + echo_stderr "ERROR: You must specify a branch" + usage + exit 1 +fi + +repo_root_dir=$(repo_root) +if [ $? -ne 0 ]; then + echo_stderr "Current directory is not managed by repo." + exit 1 +fi + +if [ $MANIFEST -eq 1 ]; then + manifest=$(repo_manifest $repo_root_dir) + if [ $? -ne 0 ]; then + echo_stderr "failed to find current manifest." + exit 1 + fi + + if [ ! -f $manifest ]; then + echo_stderr "manifest file missing '$manifest'." + exit 1 + fi + + new_manifest="$(dirname $manifest)/${branch}-$(basename $manifest)" + if [ -f $new_manifest ]; then + echo_stderr "branched manifest file already present '$new_manifest'." + exit 1 + fi +fi + +for project in $projects; do + if ! repo_is_project $project; then + echo_stderr "Invalid project: $project" + echo_stderr "Valid projects are: $(repo_project_list | tr '\n' ' ')" + exit 1 + fi +done + +for remote in $remotes; do + if ! repo_is_remote $remote; then + echo_stderr "Invalid remote: $remote" + echo_stderr "Valid remotes are: $(repo_remote_list | tr '\n' ' ')" + exit 1 + fi +done + +# Add projects from listed remotes +if [ "$remotes" != "" ]; then + projects+="$(repo_project_list $remotes | tr '\n' ' ')" +fi + +# If no projects or remotes specified, process ALL projects +if [ "$projects" == "" ] && [ "$remotes" == "" ]; then + projects="$(repo_project_list)" +fi + +if [ "$projects" == "" ]; then + echo_stderr "No projects found" + exit 1 +fi + +# Provide a default tag name if not otherwise provided +if [ "$tag" == "" ]; then + tag="v$branch" +fi echo "Finding subgits" -SUBGITS=`find . -type d -name ".git" | sed "s%/\.git$%%"` +SUBGITS=$(repo forall $projects -c 'echo '"$repo_root_dir"'/$REPO_PATH') # Go through all subgits and create the branch and tag if they does not already exist +echo "Applying branched and tags" +( for subgit in $SUBGITS; do - echo "" - echo "" - pushd $subgit > /dev/null + ( + cd $subgit - # check if destination branch already exists - echo "$subgit" - branch_check=`git branch -a --list $branch` - if [ -z "$branch_check" ]; then - echo "Creating branch $branch" - git checkout -b $branch + review_method=$(git_repo_review_method) + if [ -f .gitreview ] && [ "${review_method}" == "gerrit" ] ; then + git review -s > /dev/null if [ $? != 0 ] ; then - echo "ERROR: Could not exec: git checkout -b $branch" - popd > /dev/null + echo_stderr "ERROR: failed to setup git review in ${subgit}" + exit 1 + fi + fi + + remote=$(git_repo_remote) + if [ "${remote}" == "" ]; then + remote=$(git_remote) + if [ "${remote}" == "" ]; then + echo_stderr "ERROR: Failed to determine remote in ${subgit}" + exit 1 + fi + fi + + remote_branch=$(git_repo_remote_branch) + if [ "${remote_branch}" == "" ]; then + remote_branch=$(git_remote_branch) + if [ "${remote_branch}" == "" ]; then + echo_stderr "ERROR: failed to determine remote branch in ${subgit}" + exit 1 + fi + fi + + # check if destination branch already exists + branch_check=$(git branch -a --list $branch) + if [ -z "$branch_check" ]; then + echo "Creating branch $branch in $subgit" + echo " git checkout -t ${remote}/${remote_branch} -b $branch" + git checkout -t ${remote}/${remote_branch} -b $branch + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to create branch '$branch' in ${subgit}" exit 1 fi - # git push origin $branch:$branch else - echo "Branch $branch already exists" + echo "Branch $branch already exists in $subgit" git checkout $branch fi - tag_check=`git tag -l $tag` + tag_check=$(git tag -l $tag) if [ -z "$tag_check" ]; then - echo "Creating tag $tag" - git tag $tag + echo "Creating tag $tag in ${subgit}" + git tag -s -m "Branch $branch" $tag if [ $? != 0 ] ; then - echo "ERROR: Could not exec: git tag $tag" - popd > /dev/null + echo "ERROR: failed to create tag '$tag' in ${subgit}" exit 1 fi - # git push origin $tag else - echo "Tag $tag already exists" + echo "Tag '$tag' already exists in ${subgit}" + fi + ) || exit 1 +done +) || exit 1 + +if [ $MANIFEST -eq 1 ]; then + ( + new_manifest_name=$(basename "${new_manifest}") + new_manifest_dir=$(dirname "${new_manifest}") + manifest_name=$(basename "${manifest}") + manifest_dir=$(dirname "${manifest}") + + cd "${new_manifest_dir}" || exit 1 + + review_method=$(git_review_method) + if [ -f .gitreview ] && [ "${review_method}" == "gerrit" ] ; then + git review -s > /dev/null + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to setup git review in ${new_manifest_dir}" + exit 1 + fi fi - popd > /dev/null -done + branch_check=$(git branch -a --list $branch) + if [ ! -z "$branch_check" ]; then + echo "Branch $branch already exists in ${new_manifest_dir}" + exit 1 + fi + tag_check=$(git tag -l $tag) + if [ ! -z "$tag_check" ]; then + echo "Tag '$tag' already exists in ${new_manifest_dir}" + exit 1 + fi + + remote=$(git_remote) + if [ "${remote}" == "" ]; then + echo_stderr "ERROR: Failed to determine remote in ${new_manifest_dir}" + exit 1 + fi + + remote_branch=$(git_remote_branch) + if [ "${remote_branch}" == "" ]; then + echo_stderr "ERROR: failed to determine remote branch in ${new_manifest_dir}" + exit 1 + fi + + echo "Creating branch '$branch' in ${new_manifest_dir}" + git checkout -t ${remote}/${remote_branch} -b $branch + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to create branch '$branch' in ${new_manifest_dir}" + exit 1 + fi + + echo "Creating tag '$tag' in ${new_manifest_dir}" + git tag -s -m "Branch $branch" $tag + if [ $? != 0 ] ; then + echo "ERROR: failed to create tag '$tag' in ${new_manifest_dir}" + exit 1 + fi + + echo "Creating manifest ${new_manifest_name}" + manifest_set_revision "${manifest}" "${new_manifest}" "$branch" ${LOCK_DOWN} $projects || exit 1 + + echo "Move manifest ${new_manifest_name}, overwriting ${manifest_name}" + \cp -f "${manifest}" "${manifest}.save" + \mv -f "${new_manifest}" "${manifest}" + + echo "Committing ${manifest_name} in ${manifest_dir}" + git add ${manifest_name} || exit 1 + git commit -s -m "Modified manifest ${manifest_name} for branch ${branch}" + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to commit new manifest ${manifest_name} in ${manifest_dir}" + exit 1 + fi + + ) || exit 1 +fi diff --git a/build-tools/branching/create_tags.sh b/build-tools/branching/create_tags.sh index 0a16eac3..0716ae65 100755 --- a/build-tools/branching/create_tags.sh +++ b/build-tools/branching/create_tags.sh @@ -1,35 +1,238 @@ #!/bin/bash -if [ x"$1" = x ] ; then - echo "ERROR: You must specify a name to create tags" +# +# Copyright (c) 2020 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# Create a git tag from the current heads on a subset of git projects +# within a manifest. The subset of projects affected can be +# identified by project name or remote name. +# +# Optionally a new manifest (.xml) can be created that selects +# the new tag for the affected projects. As a further option, +# projects that are not branched can me 'locked down' within the new +# manifest by setting the sha of the current head as the revision. +# +# See also: push_tags.sh + +CREATE_TAGS_SH_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}" )" )" + +source "${CREATE_TAGS_SH_DIR}/../git-repo-utils.sh" + +usage () { + echo "create_tags.sh --tag= [ --remotes= ] [ --projects= ] [ --manifest [ --manifest-prefix ] [ --lock-down ]]" + echo " " + echo "Create a git tag in all listed projects, and all projects" + echo "hosted by all listed remotes. Lists are comma separated." + echo "" + echo "If a manifest is requested, it will recieve the name '.xml' and" + echo "it will specify the tag as the revision for all tagged projects." + echo "If lockdown is requested, all non-tagged projects get the current" + echo "HEAD's sha set as the revision." +} + +TEMP=$(getopt -o h --long remotes:,projects:,tag:,manifest,manifest-prefix:,lock-down,help -n 'create_tags.sh' -- "$@") +if [ $? -ne 0 ]; then + usage + exit 1 +fi +eval set -- "$TEMP" + +HELP=0 +MANIFEST=0 +LOCK_DOWN=0 +remotes="" +projects="" +tag="" +manifest="" +new_manifest="" +manifest_prefix="" +repo_root_dir="" + +while true ; do + case "$1" in + -h|--help) HELP=1 ; shift ;; + --remotes) remotes+=$(echo "$2 " | tr ',' ' '); shift 2;; + --projects) projects+=$(echo "$2 " | tr ',' ' '); shift 2;; + --tag) tag=$2; shift 2;; + --manifest) MANIFEST=1 ; shift ;; + --manifest-prefix) manifest_prefix=$2; shift 2;; + --lock-down) LOCK_DOWN=1 ; shift ;; + --) shift ; break ;; + *) usage; exit 1 ;; + esac +done + +if [ $HELP -eq 1 ]; then + usage + exit 0 +fi + +if [ "$tag" == "" ] ; then + echo_stderr "ERROR: You must specify a tag" + usage + exit 1 +fi + +repo_root_dir=$(repo_root) +if [ $? -ne 0 ]; then + echo_stderr "Current directory is not managed by repo." + exit 1 +fi + +if [ $MANIFEST -eq 1 ]; then + manifest=$(repo_manifest $repo_root_dir) + if [ $? -ne 0 ]; then + echo_stderr "failed to find current manifest." + exit 1 + fi + + if [ ! -f $manifest ]; then + echo_stderr "manifest file missing '$manifest'." + exit 1 + fi + + new_manifest="$(dirname $manifest)/${manifest_prefix}${tag}-$(basename $manifest)" + if [ -f $new_manifest ]; then + echo_stderr "tagged manifest file already present '$new_manifest'." + exit 1 + fi +fi + +for project in $projects; do + if ! repo_is_project $project; then + echo_stderr "Invalid project: $project" + echo_stderr "Valid projects are: $(repo_project_list | tr '\n' ' ')" + exit 1 + fi +done + +for remote in $remotes; do + if ! repo_is_remote $remote; then + echo_stderr "Invalid remote: $remote" + echo_stderr "Valid remotes are: $(repo_remote_list | tr '\n' ' ')" + exit 1 + fi +done + +# Add projects from listed remotes +if [ "$remotes" != "" ]; then + projects+="$(repo_project_list $remotes | tr '\n' ' ')" +fi + +# If no projects or remotes specified, process ALL projects +if [ "$projects" == "" ] && [ "$remotes" == "" ]; then + projects="$(repo_project_list)" +fi + +if [ "$projects" == "" ]; then + echo_stderr "No projects found" exit 1 fi -tag=$1 echo "Finding subgits" -SUBGITS=`find . -type d -name ".git" | sed "s%/\.git$%%"` +SUBGITS=$(repo forall $projects -c 'echo '"$repo_root_dir"'/$REPO_PATH') # Go through all subgits and create the tag if it does not already exist +echo "Applying branched and tags" +( for subgit in $SUBGITS; do - echo "" - echo "" - pushd $subgit > /dev/null + ( + cd $subgit - tag_check=`git tag -l $tag` - if [ -z "$tag_check" ]; then - echo "Creating tag $tag" - git tag $tag + review_method=$(git_repo_review_method) + if [ -f .gitreview ] && [ "${review_method}" == "gerrit" ] ; then + git review -s > /dev/null if [ $? != 0 ] ; then - echo "ERROR: Could not exec: git tag $tag" - popd > /dev/null + echo_stderr "ERROR: failed to setup git review in ${subgit}" exit 1 fi - # git push origin $tag - else - echo "Tag $tag already exists" fi - popd > /dev/null + tag_check=$(git tag -l $tag) + if [ -z "$tag_check" ]; then + echo "Creating tag '$tag' in ${subgit}" + git tag -s -m "Tag $tag" $tag + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to create tag '$tag' in ${subgit}" + exit 1 + fi + else + echo "Tag '$tag' already exists in ${subgit}" + fi + ) || exit 1 done +) || exit 1 +if [ $MANIFEST -eq 1 ]; then + ( + new_manifest_name=$(basename "${new_manifest}") + new_manifest_dir=$(dirname "${new_manifest}") + + cd "${new_manifest_dir}" || exit 1 + + review_method=$(git_review_method) + if [ -f .gitreview ] && [ "${review_method}" == "gerrit" ] ; then + git review -s > /dev/null + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to setup git review in ${new_manifest_dir}" + exit 1 + fi + fi + + tag_check=$(git tag -l $tag) + if [ ! -z "$tag_check" ]; then + echo "Tag '$tag' already exists in ${new_manifest_dir}" + exit 1 + fi + + remote=$(git_remote) + if [ "${remote}" == "" ]; then + echo_stderr "ERROR: Failed to determine remote in ${new_manifest_dir}" + exit 1 + fi + + remote_branch=$(git_remote_branch) + if [ "${remote_branch}" == "" ]; then + echo_stderr "ERROR: failed to determine remote branch in ${new_manifest_dir}" + exit 1 + fi + + new_branch="create_manifest_for_tag_${tag}" + # check if destination branch already exists + branch_check=$(git branch -a --list $new_branch) + if [ -z "$branch_check" ]; then + echo "Creating branch ${new_branch} in ${new_manifest_dir}" + git checkout -t ${remote}/${remote_branch} -b "${new_branch}" + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to create branch '${new_branch}' in ${new_manifest_dir}" + exit 1 + fi + else + echo "Branch ${branch} already exists in $subgit" + git checkout ${new_branch} + fi + + echo "Creating manifest ${new_manifest_name}" + manifest_set_revision "${manifest}" "${new_manifest}" "refs/tags/$tag" ${LOCK_DOWN} $projects || exit 1 + + echo "Committing ${new_manifest_name} in ${new_manifest_dir}" + git add ${new_manifest_name} || exit 1 + git commit -s -m "Create new manifest ${new_manifest_name}" + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to commit new manifest ${new_manifest_name} in ${new_manifest_dir}" + exit 1 + fi + + echo "Creating tag '$tag' in ${new_manifest_dir}" + git tag -s -m "Tag $tag" $tag + if [ $? != 0 ] ; then + echo_stderr "ERROR: failed to create tag '$tag' in ${new_manifest_dir}" + exit 1 + fi + ) || exit 1 +fi diff --git a/build-tools/branching/push_branches_tags.sh b/build-tools/branching/push_branches_tags.sh index 82a7914f..1f94cc42 100755 --- a/build-tools/branching/push_branches_tags.sh +++ b/build-tools/branching/push_branches_tags.sh @@ -1,42 +1,255 @@ - #!/bin/bash -if [ x"$1" = x ] ; then - echo "ERROR: You must specify a name to create branches and tags" +# +# Copyright (c) 2020 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# A tool to push the branches, tags, and optional manifest created by +# create_branches_and_tags.sh to the upstream source. +# +# Arguemens should match those passed to create_branches_and_tags.sh +# with the exception of '--lockdown'. +# + +PUSH_BRANCHES_TAGS_SH_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}" )" )" + +source "${PUSH_BRANCHES_TAGS_SH_DIR}/../git-repo-utils.sh" + +usage () { + echo "push_branches_tags.sh --branch= [--tag=] [ --remotes= ] [ --projects= ] [ --manifest ]" + echo "" + echo "Push a pre-existing branch and tag into all listed projects, and all" + echo "projects hosted by all listed remotes. Lists are comma separated." + echo "" + echo "The branch name must be provided. The tag name can also be provided." + echo "If the tag is omitted, one is automativally generate by adding the" + echo "prefix 'v' to the branch name." + echo "" + echo "A manifest push can also be requested.vision." +} + +TEMP=$(getopt -o h --long remotes:,projects:,branch:,tag:,manifest,help -n 'push_branches_tags.sh' -- "$@") +if [ $? -ne 0 ]; then + usage exit 1 fi -branch=$1 -tag="v$branch" +eval set -- "$TEMP" + +HELP=0 +MANIFEST=0 +remotes="" +projects="" +branch="" +tag="" +manifest="" +repo_root_dir="" + +while true ; do + case "$1" in + -h|--help) HELP=1 ; shift ;; + --remotes) remotes+=$(echo "$2 " | tr ',' ' '); shift 2;; + --projects) projects+=$(echo "$2 " | tr ',' ' '); shift 2;; + --branch) branch=$2; shift 2;; + --tag) tag=$2; shift 2;; + --manifest) MANIFEST=1 ; shift ;; + --) shift ; break ;; + *) usage; exit 1 ;; + esac +done + +if [ $HELP -eq 1 ]; then + usage + exit 0 +fi + +if [ "$branch" == "" ] ; then + echo_stderr "ERROR: You must specify a branch" + usage + exit 1 +fi + +repo_root_dir=$(repo_root) +if [ $? -ne 0 ]; then + echo_stderr "Current directory is not managed by repo." + exit 1 +fi + +if [ $MANIFEST -eq 1 ]; then + manifest=$(repo_manifest $repo_root_dir) + if [ $? -ne 0 ]; then + echo_stderr "failed to find current manifest." + exit 1 + fi + + if [ ! -f "${manifest}" ]; then + echo_stderr "manifest file missing '${manifest}'." + exit 1 + fi + + if [ ! -f "${manifest}.save" ]; then + echo_stderr "manifest file missing '${manifest}.save'." + exit 1 + fi + + # The new manifest referes to branches that are not yet available on the remotes. + # This will break some repo commands, e.g repo forall'. + # + # To get arround this we swap in the old manifest until we get passed the + # problematic commands. + \cp -f "${manifest}" "${manifest}.new" + \cp -f "${manifest}.save" "${manifest}" +fi + +for project in $projects; do + if ! repo_is_project $project; then + echo_stderr "Invalid project: $project" + echo_stderr "Valid projects are: $(repo_project_list | tr '\n' ' ')" + exit 1 + fi +done + +for remote in $remotes; do + if ! repo_is_remote $remote; then + echo_stderr "Invalid remote: $remote" + echo_stderr "Valid remotes are: $(repo_remote_list | tr '\n' ' ')" + exit 1 + fi +done + +# Add projects from listed remotes +if [ "$remotes" != "" ]; then + projects+="$(repo_project_list $remotes | tr '\n' ' ')" +fi + +# If no projects or remotes specified, process ALL projects +if [ "$projects" == "" ] && [ "$remotes" == "" ]; then + projects="$(repo_project_list)" +fi + +if [ "$projects" == "" ]; then + echo_stderr "No projects found" + exit 1 +fi + +# Provide a default tag name if not otherwise provided +if [ "$tag" == "" ]; then + tag="v$branch" +fi + echo "Finding subgits" -SUBGITS=`find . -type d -name ".git" | sed "s%/\.git$%%"` +SUBGITS=$(repo forall $projects -c 'echo '"$repo_root_dir"'/$REPO_PATH') # Go through all subgits and create the branch and tag if they does not already exist +( for subgit in $SUBGITS; do - echo "" - echo "" - pushd $subgit > /dev/null + ( + cd $subgit - # check if destination branch already exists - echo "$subgit" - echo "Pushing branch $branch" - git push origin $branch:$branch - if [ $? != 0 ] ; then - echo "ERROR: Could not exec: git push origin $branch:$branch" - popd > /dev/null + branch_check=$(git branch -a --list $branch) + if [ -z "$branch_check" ]; then + echo_stderr "ERROR: Expected branch '$branch' to exist in ${subgit}" exit 1 fi - echo "Pushing tag $tag" - git push origin $tag - if [ $? != 0 ] ; then - echo "ERROR: Could not exec: git push origin $tag" - popd > /dev/null + tag_check=$(git tag -l $tag) + if [ "${tag_check}" == "" ]; then + echo_stderr "ERROR: Expected tag '$tag' to exist in ${subgit}" exit 1 fi - popd > /dev/null + review_method=$(git_repo_review_method) + if [ "${review_method}" == "" ]; then + echo_stderr "ERROR: Failed to determine review method in ${subgit}" + exit 1 + fi + + if [ "${review_method}" == "gerrit" ]; then + remote=$(git_repo_review_remote) + else + remote=$(git_repo_remote) + fi + + if [ "${remote}" == "" ]; then + echo_stderr "ERROR: Failed to determine remote in ${subgit}" + exit 1 + fi + + echo "Pushing branch $branch in ${subgit}" + if [ "${review_method}" == "gerrit" ]; then + echo "git push --tags ${remote} ${branch}" + git push --tags ${remote} ${branch} + else + echo "git push --tags --set-upstream ${remote} ${branch}" + git push --tags --set-upstream ${remote} ${branch} + fi + + if [ $? != 0 ] ; then + echo_stderr "ERROR: Failed to push branch '${branch}' to remote '${remote}' in ${subgit}" + exit 1 + fi + ) done +) || exit 1 +if [ $MANIFEST -eq 1 ]; then + # restore manifest + \cp -f "${manifest}.new" "${manifest}" +fi + +if [ $MANIFEST -eq 1 ]; then + ( + manifest_name=$(basename "${manifest}") + manifest_dir=$(dirname "${manifest}") + + cd "${manifest_dir}" || exit 1 + + if [ ! -f ${manifest_name} ]; then + echo_stderr "ERROR: Expected file '${manifest_name} to exist in ${manifest_dir}" + exit 1 + fi + + branch_check=$(git branch -a --list $branch) + if [ -z "$branch_check" ]; then + echo_stderr "ERROR: Expected branch '$branch' to exist in ${manifest_dir}" + exit 1 + fi + + tag_check=$(git tag -l $tag) + if [ "${tag_check}" == "" ]; then + echo_stderr "ERROR: Expected tag '$tag' to exist in ${manifest_dir}" + exit 1 + fi + + review_method=$(git_review_method) + if [ "${review_method}" == "" ]; then + echo_stderr "ERROR: Failed to determine review method in ${manifest_dir}" + exit 1 + fi + + + remote=$(git_review_remote) + if [ "${remote}" == "" ]; then + echo_stderr "ERROR: Failed to determine remote in ${manifest_dir}" + exit 1 + fi + + echo "Pushing branch $branch in ${manifest_dir}" + if [ "${review_method}" == "gerrit" ]; then + # Is a reviewless push possible as part of creating a new branch in gerrit? + git push --tags ${remote} ${branch} + else + git push --tags --set-upstream ${remote} ${branch} + fi + + if [ $? != 0 ] ; then + echo_stderr "ERROR: Failed to push tag '${tag}' to remote '${remote}' in ${manifest_dir}" + exit 1 + fi + ) || exit 1 +fi diff --git a/build-tools/branching/push_tags.sh b/build-tools/branching/push_tags.sh index 8f1bf801..ef4edd90 100755 --- a/build-tools/branching/push_tags.sh +++ b/build-tools/branching/push_tags.sh @@ -1,29 +1,238 @@ #!/bin/bash -if [ x"$1" = x ] ; then - echo "ERROR: You must specify a name to push tags" +# +# Copyright (c) 2020 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# A tool to push the tags, and optional manifest created by +# create_tags.sh to the upstream source. +# +# Arguemens should match those passed to create_tags.sh +# with the exception of '--lockdown'. +# + +PUSH_TAGS_SH_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}" )" )" + +source "${PUSH_TAGS_SH_DIR}/../git-repo-utils.sh" + +usage () { + echo "push_tags.sh --tag= [ --remotes= ] [ --projects= ] [ --manifest [--manifest-prefix ]]" + echo " " + echo "Push a pre-existing git tag into all listed projects, and all projects" + echo "hosted by all listed remotes. Lists are comma separated." + echo "" + echo "A manifest push can also be requested." +} + +TEMP=$(getopt -o h --long remotes:,projects:,tag:,manifest,manifest-prefix:,help -n 'push_tags.sh' -- "$@") +if [ $? -ne 0 ]; then + usage exit 1 fi -tag=$1 +eval set -- "$TEMP" +HELP=0 +MANIFEST=0 +remotes="" +projects="" +tag="" +manifest="" +manifest_prefix="" +new_manifest="" +repo_root_dir="" -echo "Finding subgits" -SUBGITS=`find . -type d -name ".git" | sed "s%/\.git$%%"` +while true ; do + case "$1" in + -h|--help) HELP=1 ; shift ;; + --remotes) remotes+=$(echo "$2 " | tr ',' ' '); shift 2;; + --projects) projects+=$(echo "$2 " | tr ',' ' '); shift 2;; + --tag) tag=$2; shift 2;; + --manifest) MANIFEST=1 ; shift ;; + --manifest-prefix) manifest_prefix=$2; shift 2;; + --) shift ; break ;; + *) usage; exit 1 ;; + esac +done -# Go through all subgits and create the tag if it does not already exist -for subgit in $SUBGITS; do - echo "" - echo "" - pushd $subgit > /dev/null +if [ $HELP -eq 1 ]; then + usage + exit 0 +fi - echo "Creating tag $tag" - git push origin $tag - if [ $? != 0 ] ; then - echo "ERROR: Could not exec: git push origin $tag" - popd > /dev/null +if [ "$tag" == "" ] ; then + echo_stderr "ERROR: You must specify a tags" + usage + exit 1 +fi + +repo_root_dir=$(repo_root) +if [ $? -ne 0 ]; then + echo_stderr "Current directory is not managed by repo." + exit 1 +fi + +if [ $MANIFEST -eq 1 ]; then + manifest=$(repo_manifest $repo_root_dir) + if [ $? -ne 0 ]; then + echo_stderr "failed to find current manifest." exit 1 fi - popd > /dev/null + if [ ! -f $manifest ]; then + echo_stderr "manifest file missing '$manifest'." + exit 1 + fi + + new_manifest="$(dirname $manifest)/${manifest_prefix}${tag}-$(basename $manifest)" + if [ ! -f $new_manifest ]; then + echo_stderr "Expected a tagged manifest file already present '$new_manifest'." + exit 1 + fi +fi + +for project in $projects; do + if ! repo_is_project $project; then + echo_stderr "Invalid project: $project" + echo_stderr "Valid projects are: $(repo_project_list | tr '\n' ' ')" + exit 1 + fi done +for remote in $remotes; do + if ! repo_is_remote $remote; then + echo_stderr "Invalid remote: $remote" + echo_stderr "Valid remotes are: $(repo_remote_list | tr '\n' ' ')" + exit 1 + fi +done + +# Add projects from listed remotes +if [ "$remotes" != "" ]; then + projects+="$(repo_project_list $remotes | tr '\n' ' ')" +fi + +# If no projects or remotes specified, process ALL projects +if [ "$projects" == "" ] && [ "$remotes" == "" ]; then + projects="$(repo_project_list)" +fi + +if [ "$projects" == "" ]; then + echo_stderr "No projects found" + exit 1 +fi + + +echo "Finding subgits" +SUBGITS=$(repo forall $projects -c 'echo '"$repo_root_dir"'/$REPO_PATH') + +# Go through all subgits and create the tag if it does not already exist +( +for subgit in $SUBGITS; do + ( + cd $subgit + tag_check=$(git tag -l $tag) + if [ "${tag_check}" == "" ]; then + echo_stderr "ERROR: Expected tag '$tag' to exist in ${subgit}" + exit 1 + fi + + review_method=$(git_repo_review_method) + if [ "${review_method}" == "" ]; then + echo_stderr "ERROR: Failed to determine review method in ${subgit}" + exit 1 + fi + + if [ "${review_method}" == "gerrit" ]; then + remote=$(git_repo_review_remote) + else + remote=$(git_repo_remote) + fi + + if [ "${remote}" == "" ]; then + echo_stderr "ERROR: Failed to determine remote in ${subgit}" + exit 1 + fi + + echo "Pushing tag $tag in ${subgit}" + if [ "${review_method}" == "gerrit" ]; then + echo "git push ${remote} ${tag}" + git push ${remote} ${tag} + else + echo "git push ${remote} ${tag}" + git push ${remote} ${tag} + fi + + if [ $? != 0 ] ; then + echo_stderr "ERROR: Failed to push tag '${tag}' to remote '${remote}' in ${subgit}" + exit 1 + fi + ) +done +) || exit 1 + +if [ $MANIFEST -eq 1 ]; then + ( + new_manifest_name=$(basename "${new_manifest}") + new_manifest_dir=$(dirname "${new_manifest}") + + cd "${new_manifest_dir}" || exit 1 + + local_branch=$(git_local_branch) + if [ "${local_branch}" == "" ]; then + echo_stderr "ERROR: failed to determine local branch in ${new_manifest_dir}" + exit 1 + fi + + remote_branch=$(git_remote_branch) + if [ "${remote_branch}" == "" ]; then + echo_stderr "ERROR: failed to determine remote branch in ${new_manifest_dir}" + exit 1 + fi + + if [ ! -f ${new_manifest_name} ]; then + echo_stderr "ERROR: Expected file '${new_manifest_name}' to exist in ${new_manifest_dir}" + exit 1 + fi + + tag_check=$(git tag -l $tag) + if [ "${tag_check}" == "" ]; then + echo_stderr "ERROR: Expected tag '$tag' to exist in ${new_manifest_dir}" + exit 1 + fi + + review_method=$(git_review_method) + if [ "${review_method}" == "" ]; then + echo_stderr "ERROR: Failed to determine review method in ${new_manifest_dir}" + exit 1 + fi + + remote=$(git_review_remote) + if [ "${remote}" == "" ]; then + echo_stderr "ERROR: Failed to determine remote in ${new_manifest_dir}" + exit 1 + fi + + echo "Pushing tag $tag in ${new_manifest_dir}" + if [ "${review_method}" == "gerrit" ]; then + git review + if [ $? != 0 ] ; then + echo_stderr "ERROR: Failed to create git review from ${new_manifest_dir}" + exit 1 + fi + echo "When review is merged: please run ..." + echo " cd ${new_manifest_dir}" + echo " git push ${remote} ${tag}" + else + git push ${remote} ${local_branch}:${remote_branch} + git push ${remote} ${tag}:${tag} + fi + + if [ $? != 0 ] ; then + echo_stderr "ERROR: Failed to push tag '${tag}' to branch ${remote_branch} on remote '${remote}' from ${new_manifest_dir}" + exit 1 + fi + ) || exit 1 +fi diff --git a/build-tools/git-repo-utils.sh b/build-tools/git-repo-utils.sh new file mode 100644 index 00000000..fc4acf85 --- /dev/null +++ b/build-tools/git-repo-utils.sh @@ -0,0 +1,229 @@ +#!/bin/bash + +# +# Copyright (c) 2020 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# A collection of utilities that stradle the divide between +# between git and repo. +# +# These utilites are often the most reliable to use. They will +# try to get the answer from repo, and will fall back to git +# of repo isn't providing a satisfactory answer. A prime example +# is the repo's manifest, which isn't fully managed by repo, +# but isn't a fully independent git either. +# + + +GIT_REPO_UTILS_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}" )" )" + +source ${GIT_REPO_UTILS_DIR}/repo-utils.sh +source ${GIT_REPO_UTILS_DIR}/git-utils.sh + + +# +# git_repo_rel_dir []: +# Return the relative directory of a git within a repo. +# +git_repo_rel_dir () { + local DIR="${1:-${PWD}}" + + local GIT_DIR="" + local REPO_DIR="" + local GIT_RELATIVE_DIR="" + + GIT_DIR=$(git_root ${DIR}) + REPO_DIR=$(readlink -f $(repo_root ${DIR})) + GIT_RELATIVE_DIR=${GIT_DIR#${REPO_DIR}/} + echo ${GIT_RELATIVE_DIR} +} + +# +# git_repo_project []: +# Return the repo 'project' of a git. +# + +git_repo_project() { + local DIR="${1:-${PWD}}" + + ( + cd ${DIR} + + GIT_RELATIVE_DIR=$(git_repo_rel_dir) + repo forall -c "if [ \$REPO_PATH == ${GIT_RELATIVE_DIR} ]; then echo \$REPO_PROJECT; fi" + ) +} + +# +# git_repo_remote []: +# Return the repo 'remote' of a git. +# + +git_repo_remote() { + local DIR="${1:-${PWD}}" + + ( + cd ${DIR} + + GIT_RELATIVE_DIR=$(git_repo_rel_dir) + repo forall -c "if [ \$REPO_PATH == ${GIT_RELATIVE_DIR} ]; then echo \$REPO_REMOTE; fi" + ) +} + + +# +# git_repo_remote_branch []: +# Return the repo 'remote branch' of a git. +# + +git_repo_remote_branch() { + local DIR="${1:-${PWD}}" + + ( + cd ${DIR} + + GIT_RELATIVE_DIR=$(git_repo_rel_dir) + REF=$(repo forall -c "if [ \$REPO_PATH == ${GIT_RELATIVE_DIR} ]; then echo \$REPO_RREV; fi") + if git_is_branch ${REF} ; then + echo ${REF} + else + return 1 + fi + ) +} + +# +# git_repo_remote_ref []: +# Return the repo 'remote branch' of a git. +# + +git_repo_remote_ref() { + local DIR="${1:-${PWD}}" + + ( + cd ${DIR} + + GIT_RELATIVE_DIR=$(git_repo_rel_dir) + repo forall -c "if [ \$REPO_PATH == ${GIT_RELATIVE_DIR} ]; then echo \$REPO_RREV; fi" + ) +} + +git_repo_remote_url () { + local remote="" + remote=$(git_repo_remote) || return 1 + git config remote.$remote.url +} + +url_to_host () { + local URL="${1}" + + # Strip protocol, path, user/pwd, port + echo "${URL}" | sed -e 's#^[^:]*://##' -e 's#/.*$##' -e 's#^[^@]*@##' -e 's#:.*$##' +} + +host_to_domain () { + local host="${1}" + local elements=0 + + elements=$(echo "${host}" | sed 's#[^.]##g' | wc --chars) + if [ $elements -gt 2 ]; then + # strip lead element + echo "${host}" | sed 's#^[^.]*.##' + else + echo "${host}" + fi +} + +git_repo_review_method () { + local DIR="${1:-${PWD}}" + local GIT_DIR="" + local remote_url="" + local review_host="" + local remote_host="" + + GIT_DIR=$(git_root ${DIR}) || return 1 + + if [ ! -f ${GIT_DIR}/.gitreview ]; then + # No .gitreview file + echo 'default' + return 0 + fi + + if ! grep -q '\[gerrit\]' ${GIT_DIR}/.gitreview; then + # .gitreview file has no gerrit entry + echo 'default' + return 0 + fi + + review_host="$(grep host= ${GIT_DIR}/.gitreview | sed 's#^host=##' | head -n 1)" + remote_url="$(git_repo_remote_url)" || return 1 + remote_host="$(url_to_host "${remote_url}")" + if [ "${review_host}" == "{remote_host}" ]; then + # Will review against same host as we pulled from. All is well + echo 'gerrit' + return 0 + else + review_domain="$(host_to_domain "${review_host}")" + remote_domain="$(host_to_domain "${remote_host}")" + if [ "${review_domain}" == "${remote_domain}" ]; then + # Will review and remote hosts share a commom domain. Close enough + + echo 'gerrit' + return 0 + else + # Domains don't match. Not close enough to say gerrit is safe. + # Did someone forget to update .gitreview? + # Are we not pulling from the authoritative source? + + echo 'default' + return 0 + fi + fi + + # Shouldn't get here + return 1 +} + +git_repo_review_remote () { + local method="" + method=$(git_repo_review_method) + if [ "${method}" == "gerrit" ]; then + git config remote.gerrit.url > /dev/null + if [ $? -ne 0 ]; then + # Perhaps we need to run git review -s' and try again + + # echo a blank line into git in case it prompts for a username + echo | git review -s > /dev/null || return 1 + git config remote.gerrit.url > /dev/null || return 1 + fi + echo "gerrit" + else + git_repo_remote + fi +} + +git_repo_review_url () { + local DIR="${1:-${PWD}}" + local GIT_DIR="" + + GIT_DIR=$(git_root ${DIR}) + + local method="" + method=$(git_repo_review_method) + if [ "${method}" == "gerrit" ]; then + git config remote.gerrit.url + if [ $? -ne 0 ]; then + # Perhaps we need to run git review -s' and try again + + # echo a blank line into git in case it prompts for a username + echo | git review -s > /dev/null || return 1 + git config remote.gerrit.url || return 1 + fi + else + return 1 + fi +} + diff --git a/build-tools/git-utils.sh b/build-tools/git-utils.sh index 47e156b2..8e27001e 100755 --- a/build-tools/git-utils.sh +++ b/build-tools/git-utils.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # # Copyright (c) 2018 Wind River Systems, Inc. # @@ -9,6 +11,11 @@ # by repo manifests. # +echo_stderr () +{ + echo "$@" >&2 +} + git_ctx_root_dir () { dirname "${MY_REPO}" } @@ -80,6 +87,243 @@ git_list_containing_tag () { } +# +# git_root []: +# Return the root directory of a git +# Note: symlinks are fully expanded. +# + +git_root () { + local DIR="${1:-${PWD}}" + + if [ ! -d "${DIR}" ]; then + echo_stderr "No such directory: ${DIR}" + return 1 + fi + + ( + cd "${DIR}" + ROOT_DIR="$(git rev-parse --show-toplevel)" || exit 1 + readlink -f "${ROOT_DIR}" + ) +} + +# +# git_list_tags []: +# Return a list of all git tags. +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_list_tags () { + local DIR="${1:-${PWD}}" + + ( + cd "$DIR" + git tag + ) +} + + +# +# git_list_branches []: +# Return a list of all git branches. +# Non-local branches will be prefixed by 'remote/' +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_list_branches () { + local DIR="${1:-${PWD}}" + + ( + cd "$DIR" + git branch --list --all | sed 's#^..##' + ) +} + + +# +# git_list_remote_branches []: +# Return a list of all git branches defined for . +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_list_remote_branches () { + local REMOTE="${1}" + local DIR="${2:-${PWD}}" + + ( + cd "$DIR" + git branch --list --all "${REMOTE}/*" | sed "s#^.*/${REMOTE}/##" + ) +} + + +# +# git_is_tag []: +# Test if a is defined within a git. +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_is_tag () { + local TAG="${1}" + local DIR="${2:-${PWD}}" + + # remove a trailing ^0 if present + TAG=${TAG%^0} + + if [ "$TAG" == "" ]; then + return 1; + fi + + ( + cd "$DIR" + git show-ref ${TAG} | cut -d ' ' -f 2 | grep -q '^refs/tags/' + ) +} + + +# +# git_is_local_branch []: +# Test if a is defined locally within a git. +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_is_local_branch () { + local BRANCH="${1}" + local DIR="${2:-${PWD}}" + + if [ "$BRANCH" == "" ]; then + return 1; + fi + + ( + cd "$DIR" + git show-ref ${BRANCH} | cut -d ' ' -f 2 | grep -q '^refs/heads/' + ) +} + + +# +# git_is_remote_branch []: +# Test if a is defined in any of the remotes of the git. +# The branche does NOT need to be prefixed by the remore name. +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_is_remote_branch () { + local BRANCH="${1}" + local DIR="${2:-${PWD}}" + + if [ "$BRANCH" == "" ]; then + return 1; + fi + + ( + cd "$DIR" + git show-ref ${BRANCH} | cut -d ' ' -f 2 | grep -q '^refs/remotes/' + ) +} + + +# +# git_is_branch []: +# Test if a is defined in the git. +# The branch can be local or remote. +# Remote branches do NOT need to be prefixed by the remore name. +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_is_branch () { + local BRANCH="${1}" + local DIR="${2:-${PWD}}" + + if [ "$BRANCH" == "" ]; then + return 1; + fi + + git_is_local_branch ${BRANCH} "${DIR}" || git_is_remote_branch ${BRANCH} "${DIR}" +} + + +# +# git_is_ref []: +# Test if a is a valid name for a commit. +# The reference can be a sha, tag, or branch. +# Remote branches must be prefixed by the remore name, +# as in / . +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_is_ref () { + local REF="${1}" + local DIR="${2:-${PWD}}" + + if [ "$REF" == "" ]; then + return 1; + fi + + # test "$(git cat-file -t ${REF})" == "commit" + local TYPE="" + TYPE="$(git cat-file -t ${REF} 2> /dev/null)" && test "${TYPE}" == "commit" +} + + +# +# git_is_sha []: +# Test if a is defined in the git. The sha can be abreviated. +# Either specify a directory in the git, +# or use current directory if unspecified. +# + +git_is_sha () { + local SHA="${1}" + local DIR="${2:-${PWD}}" + + if [ "$SHA" == "" ]; then + return 1; + fi + + git_is_ref ${SHA} "${DIR}" && ! ( git_is_branch ${SHA} "${DIR}" || git_is_tag ${SHA} "${DIR}") +} + + +# +# git_ref_type []: +# Determine the type of the git reference . +# The result, via stdout, will be one of ("sha", "tag", "branch" or "invalid") +# Remote branches do NOT need to be prefixed by the remore name. +# Either specify a directory in the git, +# or use current directory if unspecified. + +git_ref_type () { + local REF="${1}" + local DIR="${2:-${PWD}}" + + if git_is_branch ${REF} ${DIR}; then + echo 'branch' + return 0 + fi + if git_is_tag ${REF} ${DIR}; then + echo 'tag' + return 0 + fi + if git_is_sha ${REF} ${DIR}; then + echo 'sha' + return 0 + fi + echo 'invalid' + return 1 +} + +# # # git_context: # Returns a bash script that can be used to recreate the current git context, @@ -146,3 +390,92 @@ git_test_context () { return 1 } + +git_local_branch () { + local result="" + result=$(git name-rev --name-only HEAD) + if [ "$result" == "" ] || [ "$result" == "undefined" ]; then + return 1 + fi + + # handle the case where a tag is returned by looking at the parent. + # This weird case when a local commit is tagged and we were in + # detached head state, or on 'default' branch. + while git_is_tag $result; do + result=$(git name-rev --name-only $result^1 ) + if [ "$result" == "" ] || [ "$result" == "undefined" ]; then + return 1 + fi + done + + echo $result +} + +git_list_remotes () { + git remote | grep -v gerrit +} + +git_remote () { + local DIR="${1:-${PWD}}" + + ( + cd ${DIR} + local_branch=$(git_local_branch) || return 1 + + # Return remote of current local branch, else default remote. + git config branch.${local_branch}.remote || git_list_remotes + ) +} + +git_remote_url () { + local remote="" + remote=$(git_remote) || return 1 + git config remote.$remote.url +} + +git_remote_branch () { + local local_branch="" + local_branch=$(git_local_branch) || return 1 + git config branch.${local_branch}.merge | sed 's#^refs/heads/##' +} + +git_review_method () { + local url="" + url=$(git_remote_url) || exit 1 + if [[ "${url}" =~ "/git.starlingx.io/" || "${url}" =~ "/opendev.org/" ]]; then + echo 'gerrit' + else + echo 'default' + fi +} + +git_review_url () { + local method="" + method=$(git_review_method) + if [ "${method}" == "gerrit" ]; then + git config remote.gerrit.url + if [ $? -ne 0 ]; then + # Perhaps we need to run git review -s' and try again + git review -s > /dev/null || return 1 + git config remote.gerrit.url + fi + else + git_remote_url + fi +} + +git_review_remote () { + local method="" + method=$(git_review_method) + if [ "${method}" == "gerrit" ]; then + git config remote.gerrit.url > /dev/null + if [ $? -ne 0 ]; then + # Perhaps we need to run git review -s' and try again + git review -s > /dev/null || return 1 + git config remote.gerrit.url > /dev/null || return 1 + fi + echo "gerrit" + else + git_remote + fi +} diff --git a/build-tools/repo-utils.sh b/build-tools/repo-utils.sh new file mode 100644 index 00000000..b8db2ee1 --- /dev/null +++ b/build-tools/repo-utils.sh @@ -0,0 +1,237 @@ +#!/bin/bash + +# +# Copyright (c) 2020 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# A collection of utilities relating to 'repo' +# + + +# +# Echo to stderr +# echo_stderr [any text you want] +# + +echo_stderr () +{ + echo "$@" >&2 +} + +# +# Get the root dir of a repo managed repository +# repo_root [] +# + +repo_root () { + local query_dir="${1:-${PWD}}" + local work_dir + + if [ ! -d "${query_dir}" ]; then + echo_stderr "not a valid directory: ${query_dir}" + return 1 + fi + + if [ "${query_dir:0:1}" != "/" ]; then + query_dir=$(readlink -f ${query_dir}) + if [ $? -ne 0 ]; then + return 1 + fi + fi + + work_dir="${query_dir}" + while true; do + if [ -d "$work_dir/.repo/manifests" ]; then + echo $work_dir + return 0 + fi + + if [ "${work_dir}" == "/" ]; then + break + fi + + work_dir="$(dirname "${work_dir}")" + done + + echo_stderr "directory is not controlled by repo: ${query_dir}" + return 1 +} + +# +# Get the active manifest file of a repo managed repository +# repo_manifest [] +# + +repo_manifest () { + local query_dir="${1:-${PWD}}" + local root_dir="" + local repo_manifest="" + + root_dir="$(repo_root "${query_dir}")" + if [ $? -ne 0 ]; then + return 1 + fi + + repo_manifest="${root_dir}/.repo/manifest.xml" + + # Depending on repo version, ${repo_manifest} is either a symlink to + # the real manifest, or a wrapper manifest that includes the real manifest + if [ -L "${repo_manifest}" ]; then + readlink -f "${repo_manifest}" + else + grep " +# +# old_manifest = Path to original manifest. +# new_manifest = Path to modified manifest. It will not overwrite an +# existing file. +# revision = A branch, tag ,or sha. Branch and SHA can be used +# directly, but repo requires that a tag be in the form +# "refs/tags/". +# lock_down = 0 or 1. If 1, set a revision on all other non-listed +# projects to equal the SHA of the current git head. +# project-list = A space seperated list of projects. Listed projects +# will have their revision set to the provided revision +# value. +# +manifest_set_revision () { + local old_manifest="${1}" + local new_manifest="${2}" + local revision="${3}" + local lock_down="${4}" + shift 4 + local projects="${@}" + + local repo_root_dir="" + local line="" + local FOUND=0 + local path="" + local project="" + local rev="" + + repo_root_dir=$(repo_root) + if [ $? -ne 0 ]; then + echo_stderr "Current directory is not managed by repo." + return 1 + fi + + if [ ! -f "${old_manifest}" ]; then + echo_stderr "Old manifest file is missing '${old_manifest}'." + return 1 + fi + + if [ -f "${new_manifest}" ]; then + echo_stderr "New manifest file already present '${new_manifest}'." + return 1 + fi + + mkdir -p "$(dirname "${new_manifest}")" + if [ $? -ne 0 ]; then + echo_stderr "Failed to create directory '$(dirname "${new_manifest}")'" + return 1 + fi + + while IFS= read -r line; do + echo "${line}" | grep -q '