1
0
Fork 0

use logging in apply_patch2

+ possible fix for base commit

+ ph_ options to control commit behavior
This commit is contained in:
Mikhail Goncharov 2020-07-23 19:55:04 +02:00
parent ec2547a171
commit f99e98e434
5 changed files with 255 additions and 58 deletions

View file

@ -1 +1,98 @@
// Copyright 2019 Google LLC // // Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://llvm.org/LICENSE.txt // // 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. def success = true def failure_message = "" pipeline { agent { label 'linux' } parameters { string(name: 'DIFF_ID') string(name: 'PHID') string(name: 'REV_ID') } environment { CONDUIT_TOKEN = credentials('phabricator-conduit-token') PHABRICATOR_HOST = 'https://reviews.llvm.org' LLVM_DIR = "${WORKSPACE}/llvm-project" SCRIPT_DIR = "${WORKSPACE}/llvm-premerge-checks/scripts" RESULT_DIR = "${WORKSPACE}/results" PHAB_LOG = "${RESULT_DIR}/.phabricator-comment" MY_BUILD_ID = "${JOB_BASE_NAME}-${BUILD_NUMBER}" TARGET_DIR = "/mnt/nfs/results/${MY_BUILD_ID}" RESULT_URL = "http://results.llvm-merge-guard.org/${MY_BUILD_ID}" } options { timeout(time:10, unit:'MINUTES') } stages { stage("build info"){ steps { echo "Building diff ${DIFF_ID} with PHID ${PHID} for Revision ${REV_ID}" script { currentBuild.displayName += " D${REV_ID}" currentBuild.description = "<a href='https://reviews.llvm.org/D${REV_ID}'>D${REV_ID}</a>" } } } stage("git checkout"){ steps { dir("${LLVM_DIR}"){ git url: 'git@github.com:llvm-premerge-tests/llvm-project.git' } dir("llvm-premerge-checks") { git url: 'https://github.com/google/llvm-premerge-checks.git' } sh """ rm -rf ${RESULT_DIR} mkdir -p ${RESULT_DIR} """ } } stage('arc patch'){ steps { dir("${LLVM_DIR}"){ sh """ ${SCRIPT_DIR}/phabtalk/apply_patch2.py ${DIFF_ID} \ --token ${CONDUIT_TOKEN} \ --url ${PHABRICATOR_HOST} \ --comment-file ${PHAB_LOG} \ --push-branch """ } } } } post { always { dir("${RESULT_DIR}") { // copy console log to result folder sh "wget -qO console-log.txt http://jenkins-ui.jenkins.svc.cluster.local:8080/job/${JOB_BASE_NAME}/${BUILD_NUMBER}/consoleText" sh "mkdir ${TARGET_DIR}" sh "cp * ${TARGET_DIR}" } sh """${SCRIPT_DIR}/phabtalk/phabtalk.py "${PHID}" "${DIFF_ID}" \ --workspace "${LLVM_DIR}" \ --conduit-token "${CONDUIT_TOKEN}" \ --host "${PHABRICATOR_HOST}/api/" \ --buildresult ${currentBuild.result} \ --results-dir "${TARGET_DIR}" \ --results-url "${RESULT_URL}" \ --failures "Applying patch failed" \ --name "apply patch" """ } } }
// Copyright 2019 Google LLC
//
// Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://llvm.org/LICENSE.txt
//
// 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.
def success = true
def failure_message = ""
pipeline {
agent { label 'linux' }
parameters {
string(name: 'DIFF_ID')
string(name: 'PHID')
string(name: 'REV_ID')
}
environment {
CONDUIT_TOKEN = credentials('phabricator-conduit-token')
PHABRICATOR_HOST = 'https://reviews.llvm.org'
LLVM_DIR = "${WORKSPACE}/llvm-project"
SCRIPT_DIR = "${WORKSPACE}/llvm-premerge-checks/scripts"
RESULT_DIR = "${WORKSPACE}/results"
PHAB_LOG = "${RESULT_DIR}/.phabricator-comment"
MY_BUILD_ID = "${JOB_BASE_NAME}-${BUILD_NUMBER}"
TARGET_DIR = "/mnt/nfs/results/${MY_BUILD_ID}"
RESULT_URL = "http://results.llvm-merge-guard.org/${MY_BUILD_ID}"
}
options {
timeout(time:10, unit:'MINUTES')
}
stages {
stage("build info"){
steps {
echo "Building diff ${DIFF_ID} with PHID ${PHID} for Revision ${REV_ID}"
script {
currentBuild.displayName += " D${REV_ID}"
currentBuild.description = "<a href='https://reviews.llvm.org/D${REV_ID}'>D${REV_ID}</a>"
}
}
}
stage("git checkout"){
steps {
dir("${LLVM_DIR}"){
git url: 'git@github.com:llvm-premerge-tests/llvm-project.git'
}
dir("llvm-premerge-checks")
{
git url: 'https://github.com/google/llvm-premerge-checks.git'
}
sh """
rm -rf ${RESULT_DIR}
mkdir -p ${RESULT_DIR}
"""
}
}
stage('arc patch'){
steps {
dir("${LLVM_DIR}"){
sh """
${SCRIPT_DIR}/phabtalk/apply_patch2.py ${DIFF_ID} \
--token ${CONDUIT_TOKEN} \
--url ${PHABRICATOR_HOST} \
--push-branch
"""
}
}
}
}
post {
always {
dir("${RESULT_DIR}") {
// copy console log to result folder
sh "wget -qO console-log.txt http://jenkins-ui.jenkins.svc.cluster.local:8080/job/${JOB_BASE_NAME}/${BUILD_NUMBER}/consoleText"
sh "mkdir ${TARGET_DIR}"
sh "cp * ${TARGET_DIR}"
}
sh """${SCRIPT_DIR}/phabtalk/phabtalk.py "${PHID}" "${DIFF_ID}" \
--workspace "${LLVM_DIR}" \
--conduit-token "${CONDUIT_TOKEN}" \
--host "${PHABRICATOR_HOST}/api/" \
--buildresult ${currentBuild.result} \
--results-dir "${TARGET_DIR}" \
--results-url "${RESULT_URL}" \
--failures "Applying patch failed" \
--name "apply patch"
"""
}
}
}

View file

@ -1 +1,98 @@
// Copyright 2019 Google LLC // // Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://llvm.org/LICENSE.txt // // 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. def success = true def failure_message = "" pipeline { agent { label 'linux' } parameters { string(name: 'DIFF_ID') string(name: 'PHID') string(name: 'REV_ID') } environment { CONDUIT_TOKEN = credentials('phabricator-conduit-token') PHABRICATOR_HOST = 'https://reviews.llvm.org' LLVM_DIR = "${WORKSPACE}/llvm-project" SCRIPT_DIR = "${WORKSPACE}/llvm-premerge-checks/scripts" RESULT_DIR = "${WORKSPACE}/results" PHAB_LOG = "${RESULT_DIR}/.phabricator-comment" MY_BUILD_ID = "${JOB_BASE_NAME}-${BUILD_NUMBER}" TARGET_DIR = "/mnt/nfs/results/${MY_BUILD_ID}" RESULT_URL = "http://results.llvm-merge-guard.org/${MY_BUILD_ID}" } options { timeout(time:10, unit:'MINUTES') } stages { stage("build info"){ steps { echo "Building diff ${DIFF_ID} with PHID ${PHID} for Revision ${REV_ID}" script { currentBuild.displayName += " D${REV_ID}" currentBuild.description = "<a href='https://reviews.llvm.org/D${REV_ID}'>D${REV_ID}</a>" } } } stage("git checkout"){ steps { dir("${LLVM_DIR}"){ git url: 'git@github.com:llvm-premerge-tests/llvm-project.git' } dir("llvm-premerge-checks") { git url: 'https://github.com/google/llvm-premerge-checks.git' } sh """ rm -rf ${RESULT_DIR} mkdir -p ${RESULT_DIR} """ } } stage('arc patch'){ steps { dir("${LLVM_DIR}"){ sh """ ${SCRIPT_DIR}/phabtalk/apply_patch2.py ${DIFF_ID} \ --token ${CONDUIT_TOKEN} \ --url ${PHABRICATOR_HOST} \ --comment-file ${PHAB_LOG} \ --push-branch """ } } } } post { always { dir("${RESULT_DIR}") { // copy console log to result folder sh "wget -qO console-log.txt http://jenkins-ui.jenkins.svc.cluster.local:8080/job/${JOB_BASE_NAME}/${BUILD_NUMBER}/consoleText" sh "mkdir ${TARGET_DIR}" sh "cp * ${TARGET_DIR}" } sh """${SCRIPT_DIR}/phabtalk/phabtalk.py "${PHID}" "${DIFF_ID}" \ --workspace "${LLVM_DIR}" \ --conduit-token "${CONDUIT_TOKEN}" \ --host "${PHABRICATOR_HOST}/api/" \ --buildresult ${currentBuild.result} \ --results-dir "${TARGET_DIR}" \ --results-url "${RESULT_URL}" \ --failures "Applying patch failed" \ --name "apply patch" """ } } }
// Copyright 2019 Google LLC
//
// Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://llvm.org/LICENSE.txt
//
// 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.
def success = true
def failure_message = ""
pipeline {
agent { label 'linux' }
parameters {
string(name: 'DIFF_ID')
string(name: 'PHID')
string(name: 'REV_ID')
}
environment {
CONDUIT_TOKEN = credentials('phabricator-conduit-token')
PHABRICATOR_HOST = 'https://reviews.llvm.org'
LLVM_DIR = "${WORKSPACE}/llvm-project"
SCRIPT_DIR = "${WORKSPACE}/llvm-premerge-checks/scripts"
RESULT_DIR = "${WORKSPACE}/results"
PHAB_LOG = "${RESULT_DIR}/.phabricator-comment"
MY_BUILD_ID = "${JOB_BASE_NAME}-${BUILD_NUMBER}"
TARGET_DIR = "/mnt/nfs/results/${MY_BUILD_ID}"
RESULT_URL = "http://results.llvm-merge-guard.org/${MY_BUILD_ID}"
}
options {
timeout(time:10, unit:'MINUTES')
}
stages {
stage("build info"){
steps {
echo "Building diff ${DIFF_ID} with PHID ${PHID} for Revision ${REV_ID}"
script {
currentBuild.displayName += " D${REV_ID}"
currentBuild.description = "<a href='https://reviews.llvm.org/D${REV_ID}'>D${REV_ID}</a>"
}
}
}
stage("git checkout"){
steps {
dir("${LLVM_DIR}"){
git url: 'git@github.com:llvm-premerge-tests/llvm-project.git'
}
dir("llvm-premerge-checks")
{
git url: 'https://github.com/google/llvm-premerge-checks.git'
}
sh """
rm -rf ${RESULT_DIR}
mkdir -p ${RESULT_DIR}
"""
}
}
stage('arc patch'){
steps {
dir("${LLVM_DIR}"){
sh """
${SCRIPT_DIR}/phabtalk/apply_patch2.py ${DIFF_ID} \
--token ${CONDUIT_TOKEN} \
--url ${PHABRICATOR_HOST} \
--push-branch
"""
}
}
}
}
post {
always {
dir("${RESULT_DIR}") {
// copy console log to result folder
sh "wget -qO console-log.txt http://jenkins-ui.jenkins.svc.cluster.local:8080/job/${JOB_BASE_NAME}/${BUILD_NUMBER}/consoleText"
sh "mkdir ${TARGET_DIR}"
sh "cp * ${TARGET_DIR}"
}
sh """${SCRIPT_DIR}/phabtalk/phabtalk.py "${PHID}" "${DIFF_ID}" \
--workspace "${LLVM_DIR}" \
--conduit-token "${CONDUIT_TOKEN}" \
--host "${PHABRICATOR_HOST}/api/" \
--buildresult ${currentBuild.result} \
--results-dir "${TARGET_DIR}" \
--results-url "${RESULT_URL}" \
--failures "Applying patch failed" \
--name "apply patch"
"""
}
}
}

View file

@ -22,7 +22,8 @@ scripts/phabtalk/apply_patch2.py $ph_buildable_diff \
--path "${BUILDKITE_BUILD_PATH}"/llvm-project-fork \
--token $CONDUIT_TOKEN \
--url $PHABRICATOR_HOST \
--comment-file apply_patch.txt \
--log-level $LOG_LEVEL \
--commit $BASE_COMMIT \
--push-branch
EXIT_STATUS=$?

View file

@ -19,6 +19,9 @@ import yaml
if __name__ == '__main__':
queue_prefix = os.getenv("ph_queue_prefix", "")
diff_id = os.getenv("ph_buildable_diff")
log_level = os.getenv('ph_log_level', 'INFO')
base_commit = os.getenv('ph_base_commit', 'auto')
run_build = os.getenv('ph_skip_build') is None
steps = []
create_branch_step = {
'label': 'create branch',
@ -26,6 +29,10 @@ if __name__ == '__main__':
'commands': ['scripts/buildkite/apply_patch.sh'],
'agents': {'queue': f'{queue_prefix}linux'},
'timeout_in_minutes': 20,
'env': {
'LOG_LEVEL': log_level,
'BASE_COMMIT': base_commit,
}
}
build_linux_step = {
'trigger': 'premerge-checks',
@ -41,5 +48,6 @@ if __name__ == '__main__':
if e.startswith('ph_'):
build_linux_step['build']['env'][e] = os.getenv(e)
steps.append(create_branch_step)
steps.append(build_linux_step)
if run_build:
steps.append(build_linux_step)
print(yaml.dump({'steps': steps}))

View file

@ -16,10 +16,12 @@
import argparse
import datetime
import json
import logging
import os
import re
import socket
import subprocess
import sys
import time
from typing import List, Optional, Tuple
@ -53,9 +55,8 @@ class ApplyPatch:
https://github.com/llvm/llvm-project or given a path to clone into.
"""
def __init__(self, path: str, diff_id: int, comment_file_path: str, token: str, url: str, git_hash: str,
def __init__(self, path: str, diff_id: int, token: str, url: str, git_hash: str,
push_branch: bool = False):
self.comment_file_path = comment_file_path
self.push_branch = push_branch # type: bool
self.conduit_token = token # type: Optional[str]
self.host = url # type: Optional[str]
@ -64,18 +65,17 @@ class ApplyPatch:
if not self.host.endswith('/api/'):
self.host += '/api/'
self.phab = self._create_phab()
self.base_revision = git_hash # type: Optional[str]
self.msg = [] # type: List[str]
self.base_revision = git_hash # type: str
if not os.path.isdir(path):
print(f'{path} does not exist, cloning repository')
logging.info(f'{path} does not exist, cloning repository')
# TODO: progress of clonning
self.repo = Repo.clone_from(FORK_REMOTE_URL, path)
else:
print('repository exist, will reuse')
logging.info('repository exist, will reuse')
self.repo = Repo(path) # type: Repo
os.chdir(path)
print('working dir', os.getcwd())
logging.info(f'working dir {os.getcwd()}')
@property
def branch_name(self):
@ -86,7 +86,7 @@ class ApplyPatch:
"""Load arc configuration from file if not set."""
if self.conduit_token is not None or self.host is not None:
return
print('Loading configuration from ~/.arcrc file')
logging.info('Loading configuration from ~/.arcrc file')
with open(os.path.expanduser('~/.arcrc'), 'r') as arcrc_file:
arcrc = json.load(arcrc_file)
# use the first host configured in the file
@ -95,24 +95,27 @@ class ApplyPatch:
def run(self):
"""try to apply the patch from phabricator
Write to `self.comment_file` for showing error messages on Phabricator.
"""
try:
self._refresh_master()
revision_id, dependencies, base_revision = self._get_dependencies(self.diff_id)
dependencies.reverse() # Arrange deps in chronological order.
if self.base_revision != 'auto':
logging.info('Using base revision provided by command line\n{} instead of resolved\n{}'.format(
self.base_revision, base_revision))
base_revision = self.base_revision
self._create_branch(base_revision)
print('git reset, git cleanup...')
logging.info('git reset, git cleanup...')
self.repo.git.reset('--hard')
self.repo.git.clean('-fdx')
print('Analyzing {}'.format(diff_to_str(revision_id)))
logging.info('Analyzing {}'.format(diff_to_str(revision_id)))
if len(dependencies) > 0:
print('This diff depends on: {}'.format(diff_list_to_str(dependencies)))
logging.info('This diff depends on: {}'.format(diff_list_to_str(dependencies)))
missing, landed = self._get_missing_landed_dependencies(dependencies)
print(' Already landed: {}'.format(diff_list_to_str(landed)))
print(' Will be applied: {}'.format(diff_list_to_str(missing)))
logging.info(' Already landed: {}'.format(diff_list_to_str(landed)))
logging.info(' Will be applied: {}'.format(diff_list_to_str(missing)))
if missing:
for revision in missing:
self._apply_revision(revision)
@ -120,15 +123,17 @@ class ApplyPatch:
self.repo.config_writer().set_value("user", "name", "myusername").release()
self.repo.config_writer().set_value("user", "email", "myemail@example.com").release()
self.repo.git.commit('-a', '-m', 'dependencies')
print('All depended diffs are applied')
logging.info('All depended diffs are applied')
logging.info('applying original diff')
self._apply_diff(self.diff_id, revision_id)
if self.push_branch:
self._commit_and_push()
else:
self.repo.git.add('-u', '.')
print('done.')
finally:
self._write_error_message()
return 0
except Exception as e:
logging.error(f'exception: {e}')
return 1
def _refresh_master(self):
"""Update local git repo and origin.
@ -138,7 +143,7 @@ class ApplyPatch:
if not self.push_branch:
return
print('Syncing local, origin and upstream...')
logging.info('Syncing local, origin and upstream...')
self.repo.git.fetch('--all')
self.repo.git.checkout('master')
self.repo.git.reset('--hard')
@ -150,27 +155,24 @@ class ApplyPatch:
self.repo.git.pull('upstream', 'master')
try:
self.repo.git.push('origin', 'master')
print('refresh of master branch completed')
logging.info('refresh of master branch completed')
except GitCommandError as e:
print('Info: Could not push to origin master.')
logging.info('Info: Could not push to origin master.')
def _create_branch(self, base_revision: Optional[str]):
self.repo.git.fetch('--all')
if self.branch_name in self.repo.heads:
self.repo.delete_head('--force', self.branch_name)
if self.base_revision is not None:
print('Using base revision provided by command line\n{} instead of resolved\n{}'.format(
self.base_revision, base_revision))
base_revision = self.base_revision
try:
commit = self.repo.commit()
commit = self.repo.commit(base_revision)
except BadName:
print('Revision {} not found in upstream repository, using master instead.'.format(base_revision))
logging.info('Revision {} not found in upstream repository, using master instead.'.format(base_revision))
commit = self.repo.heads['master']
logging.info(f'creating branch {self.branch_name} at {commit.hexsha}')
new_branch = self.repo.create_head(self.branch_name, commit.hexsha)
self.repo.head.reference = new_branch
self.repo.head.reset(index=True, working_tree=True)
print('Base revision is {}'.format(self.repo.head.commit.hexsha))
logging.info('Base branch revision is {}'.format(self.repo.head.commit.hexsha))
def _commit_and_push(self):
"""Commit the patch and push it to origin."""
@ -181,7 +183,7 @@ class ApplyPatch:
self.repo.index.commit(message='applying Diff {}'.format(
self.diff_id))
self.repo.git.push('--force', 'origin', self.branch_name)
print('Branch {} pushed to origin'.format(self.branch_name))
logging.info('Branch {} pushed to origin'.format(self.branch_name))
pass
def _create_phab(self):
@ -211,10 +213,12 @@ class ApplyPatch:
"""Get all dependencies for the diff.
They are listed in reverse chronological order - from most recent to least recent."""
print('Getting dependencies of {}'.format(diff_id))
logging.info('Getting dependencies of {}'.format(diff_id))
diff = self._get_diff(diff_id)
logging.debug(f'diff object: {diff}')
revision_id = int(diff.revisionID)
revision = self._get_revision(revision_id)
logging.debug(f'revision object: {revision}')
base_revision = diff['sourceControlBaseRevision']
if base_revision is None or len(base_revision) == 0:
base_revision = 'master'
@ -231,9 +235,9 @@ class ApplyPatch:
def _apply_diff(self, diff_id: int, revision_id: int):
"""Download and apply a diff to the local working copy."""
print('Applying diff {} for revision {}...'.format(diff_id, diff_to_str(revision_id)))
# TODO: print diff or URL to it
logging.info('Applying diff {} for revision {}...'.format(diff_id, diff_to_str(revision_id)))
diff = try_call(lambda: self.phab.differential.getrawdiff(diffID=str(diff_id)).response)
logging.debug(f'diff {diff_id}:\n{diff}')
proc = subprocess.run('git apply -', input=diff, shell=True, text=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if proc.returncode != 0:
@ -244,20 +248,9 @@ class ApplyPatch:
revision = self._get_revision(revision_id)
# take the diff_id with the highest number, this should be latest one
diff_id = max(revision['diffs'])
logging.info(f'picking diff from revision {revision_id}: {diff_id} from {revision["diffs"]}')
self._apply_diff(diff_id, revision_id)
def _write_error_message(self):
"""Write the log message to a file."""
if self.comment_file_path is None:
return
if len(self.msg) == 0:
return
print('writing error message to {}'.format(self.comment_file_path))
with open(self.comment_file_path, 'a') as comment_file:
text = '\n\n'.join(self.msg)
comment_file.write(text)
def _get_landed_revisions(self):
"""Get list of landed revisions from current git branch."""
diff_regex = re.compile(r'^Differential Revision: https://reviews\.llvm\.org/(.*)$', re.MULTILINE)
@ -274,7 +267,8 @@ class ApplyPatch:
if result is not None:
yield result.group(1)
if earliest_commit is not None:
print('Earliest analyzed commit in history', earliest_commit.hexsha, earliest_commit.committed_datetime)
logging.info(f'Earliest analyzed commit in history {earliest_commit.hexsha}, '
f'{earliest_commit.committed_datetime}')
return
def _get_missing_landed_dependencies(self, dependencies: List[int]) -> Tuple[List[int], List[int]]:
@ -308,24 +302,24 @@ def try_call(call):
except socket.timeout as e:
c += 1
if c > 5:
print('Connection to Pharicator failed, giving up: {}'.format(e))
logging.error('Connection to Pharicator failed, giving up: {}'.format(e))
raise
print('Connection to Pharicator failed, retrying: {}'.format(e))
logging.error('Connection to Pharicator failed, retrying: {}'.format(e))
time.sleep(c * 10)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Apply Phabricator patch to working directory.')
parser.add_argument('diff_id', type=int)
# TODO: instead of --comment-file use stdout / stderr.
parser.add_argument('--path', type=str, help='repository path', default=os.getcwd())
parser.add_argument('--comment-file', type=str, dest='comment_file_path', default=None)
parser.add_argument('--token', type=str, default=None, help='Conduit API token')
parser.add_argument('--url', type=str, default=None, help='Phabricator URL')
parser.add_argument('--commit', dest='commit', type=str, default=None,
help='Use this commit as a base. By default tool tries to pick the base commit itself')
parser.add_argument('--commit', dest='commit', type=str, default='auto',
help='Use this commit as a base. For "auto" tool tries to pick the base commit itself')
parser.add_argument('--push-branch', action='store_true', dest='push_branch',
help='choose if branch shall be pushed to origin')
parser.add_argument('--log-level', type=str, default='INFO')
args = parser.parse_args()
patcher = ApplyPatch(args.path, args.diff_id, args.comment_file_path, args.token, args.url, args.commit, args.push_branch)
patcher.run()
logging.basicConfig(level=args.log_level, format='%(levelname)-7s %(message)s')
patcher = ApplyPatch(args.path, args.diff_id, args.token, args.url, args.commit, args.push_branch)
sys.exit(patcher.run())