blob: 74082829cde02a65b443cd9c30c017e39a6f1af1 [file]
# SPDX-License-Identifier: Apache-2.0
#
# Modifications by Apache Solr contributors; see git log for details.
# Licensed under the Apache License, Version 2.0.
#
# The OpenSearch Contributors require contributions made to
# this file be licensed under the Apache-2.0 license or a
# compatible open source license.
# Modifications Copyright OpenSearch Contributors. See
# GitHub history for details.
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you 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.
import os
import re
from solrorbit import exceptions
from solrorbit.utils import io, process
MIN_REQUIRED_MAJOR_VERSION = 2
VERSION_REGEX = r'.* ([0-9]+)\.([0-9]+)\..*'
def probed(f):
def probe(src, *args, **kwargs):
try:
out, _, status = process.run_subprocess_with_out_and_err("git --version")
except FileNotFoundError:
status = 1
if status != 0:
raise exceptions.SystemSetupError("Error invoking 'git', please install (or re-install).")
match = re.search(VERSION_REGEX, out)
if not match or int(match.group(1)) < MIN_REQUIRED_MAJOR_VERSION:
raise exceptions.SystemSetupError("solr-orbit requires at least version 2 of git. "
f"You have {out}. Please update git.")
return f(src, *args, **kwargs)
return probe
def is_working_copy(src):
"""
Checks whether the given directory is a git working copy.
:param src: A directory. May or may not exist.
:return: True iff the given directory is a git working copy.
"""
return os.path.exists(src) and os.path.exists(os.path.join(src, ".git"))
@probed
def clone(src, remote):
io.ensure_dir(src)
# Don't swallow subprocess output, user might need to enter credentials...
if process.run_subprocess_with_logging("git clone %s %s" % (remote, io.escape_path(src))):
raise exceptions.SupplyError("Could not clone from [%s] to [%s]" % (remote, src))
@probed
def fetch(src, remote="origin"):
if process.run_subprocess_with_logging("git -C {0} fetch --prune --tags {1}".format(io.escape_path(src), remote)):
raise exceptions.SupplyError("Could not fetch source tree from [%s]" % remote)
@probed
def checkout(src_dir, branch="main"):
if process.run_subprocess_with_logging("git -C {0} checkout {1}".format(io.escape_path(src_dir), branch)):
raise exceptions.SupplyError("Could not checkout [%s]. Do you have uncommitted changes?" % branch)
@probed
def rebase(src_dir, remote="origin", branch="main"):
checkout(src_dir, branch)
if process.run_subprocess_with_logging("git -C {0} rebase {1}/{2}".format(io.escape_path(src_dir), remote, branch)):
raise exceptions.SupplyError("Could not rebase on branch [%s]" % branch)
@probed
def pull(src_dir, remote="origin", branch="main"):
fetch(src_dir, remote)
rebase(src_dir, remote, branch)
@probed
def pull_ts(src_dir, ts):
fetch(src_dir)
clean_src = io.escape_path(src_dir)
revision = process.run_subprocess_with_output(
"git -C {0} rev-list -n 1 --before=\"{1}\" --date=iso8601 origin/main".format(clean_src, ts))[0].strip()
if process.run_subprocess_with_logging("git -C {0} checkout {1}".format(clean_src, revision)):
raise exceptions.SupplyError("Could not checkout source tree for timestamped revision [%s]" % ts)
@probed
def pull_revision(src_dir, revision):
fetch(src_dir)
if process.run_subprocess_with_logging("git -C {0} checkout {1}".format(io.escape_path(src_dir), revision)):
raise exceptions.SupplyError("Could not checkout source tree for revision [%s]" % revision)
@probed
def head_revision(src_dir):
return process.run_subprocess_with_output("git -C {0} rev-parse --short HEAD".format(
io.escape_path(src_dir)))[0].strip()
@probed
def current_branch(src_dir):
return process.run_subprocess_with_output("git -C {0} rev-parse --abbrev-ref HEAD".format(
io.escape_path(src_dir)))[0].strip()
@probed
def branches(src_dir, remote=True):
clean_src = io.escape_path(src_dir)
if remote:
# Because compatability issues with Git 2.40.0+, updated --format='%(refname:short)' to --format='%(refname)'
return _cleanup_remote_branch_names(process.run_subprocess_with_output(
"git -C {src} for-each-ref refs/remotes/ --format='%(refname)'".format(src=clean_src)))
else:
return _cleanup_local_branch_names(
process.run_subprocess_with_output(
"git -C {src} for-each-ref refs/heads/ --format='%(refname:short)'".format(src=clean_src)))
@probed
def tags(src_dir):
return _cleanup_tag_names(process.run_subprocess_with_output("git -C {0} tag".format(io.escape_path(src_dir))))
def _cleanup_remote_branch_names(branch_names):
return [(b[b.rindex("/") + 1:]).strip() for b in branch_names if not b.endswith("/HEAD")]
def _cleanup_local_branch_names(branch_names):
return [b.strip() for b in branch_names if not b.endswith("HEAD")]
def _cleanup_tag_names(tag_names):
return [t.strip() for t in tag_names]