Add support for git project handling

Automatically switches over from OBS api calls to git
when specifing a git repository on CLI
This commit is contained in:
Adrian Schröter 2024-02-13 16:17:05 +01:00
parent 9b449f91cb
commit cf36ceb5b8
No known key found for this signature in database
GPG Key ID: 918D8C954C08DB67
3 changed files with 101 additions and 48 deletions

View File

@ -45,6 +45,7 @@ class CommandLineInterface(ToolBase.CommandLineInterface):
@cmdln.option('-f', '--force', action='store_true', help='continue even if build is in progress') @cmdln.option('-f', '--force', action='store_true', help='continue even if build is in progress')
@cmdln.option('-d', '--dry', help='no modifications uploaded') @cmdln.option('-d', '--dry', help='no modifications uploaded')
@cmdln.option('-p', '--project', help='target project') @cmdln.option('-p', '--project', help='target project')
@cmdln.option('-g', '--git-url', help='git repository for target project')
@cmdln.option('-s', '--scope', help='scope on which to operate ({}, staging:$letter)'.format(', '.join(SCOPES))) @cmdln.option('-s', '--scope', help='scope on which to operate ({}, staging:$letter)'.format(', '.join(SCOPES)))
@cmdln.option('--no-checkout', action='store_true', help='reuse checkout in cache') @cmdln.option('--no-checkout', action='store_true', help='reuse checkout in cache')
@cmdln.option('--stop-after-solve', action='store_true', help='only create group files') @cmdln.option('--stop-after-solve', action='store_true', help='only create group files')
@ -93,13 +94,13 @@ class CommandLineInterface(ToolBase.CommandLineInterface):
self.tool.reset() self.tool.reset()
self.tool.dry_run = self.options.dry self.tool.dry_run = self.options.dry
return self.tool.update_and_solve_target(api, target_project, target_config, main_repo, return self.tool.update_and_solve_target(api, target_project, target_config, main_repo,
project=project, scope=scope, force=opts.force, git_url=opts.git_url, project=project, scope=scope,
no_checkout=opts.no_checkout, force=opts.force, no_checkout=opts.no_checkout,
only_release_packages=opts.only_release_packages, only_release_packages=opts.only_release_packages,
stop_after_solve=opts.stop_after_solve, stop_after_solve=opts.stop_after_solve,
custom_cache_tag=opts.custom_cache_tag) custom_cache_tag=opts.custom_cache_tag)
except MismatchedRepoException: except MismatchedRepoException:
logging.error("Failed to create weakremovers.inc due to mismatch in repos - project most likey started building again.") logging.error("Failed to create weakremovers.inc due to mismatch in repos - project most likey started building again!")
# for stagings we have to be strict on the exit value # for stagings we have to be strict on the exit value
if scope == 'staging': if scope == 'staging':
return 1 return 1

View File

@ -330,11 +330,15 @@ class Group(object):
continue continue
if name in missing: if name in missing:
if ignore_broken and name not in self.required: if ignore_broken and name not in self.required:
content += prefix + "#- " + name + " is missing\n" msg = ' {} not found on {}'.format(name, ','.join(sorted(missing[name])))
content += prefix + "#- " + msg + "\n"
self.logger.error(msg)
continue continue
if name in unresolvable: if name in unresolvable:
if ignore_broken and name not in self.required: if ignore_broken and name not in self.required:
content += prefix + "#- " + name + " is unresolvable: " + unresolvable[name] + "\n" msg = ' {} uninstallable: {}'.format(name, unresolvable[name])
content += prefix + "#- " + msg + "\n"
self.logger.error(msg)
continue continue
content += prefix + "- " + name content += prefix + "- " + name
if comment: if comment:

View File

@ -37,6 +37,7 @@ from pkglistgen.group import Group
SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__)) SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__))
PRODUCT_SERVICE = '/usr/lib/obs/service/create_single_product' PRODUCT_SERVICE = '/usr/lib/obs/service/create_single_product'
PRODUCT_COMPOSE_SEPERATOR_LINE = '# The following is generated by openSUSE-release-tools'
# share header cache with repochecker # share header cache with repochecker
CACHEDIR = CacheManager.directory('repository-meta') CACHEDIR = CacheManager.directory('repository-meta')
@ -141,7 +142,7 @@ class PkgListGen(ToolBase.ToolBase):
archs = ['*'] + self.all_architectures archs = ['*'] + self.all_architectures
# a single file covering all builds via multibuild flavors # a single file covering all builds via multibuild flavors
with open(os.path.join(self.productcompose_dir, 'default.productcompose'), 'a') as opfh: with open(os.path.join(self.productcompose_dir, 'default.productcompose'), 'a') as opfh:
opfh.write("# The following is generated by openSUSE-release-tools\n") opfh.write(PRODUCT_COMPOSE_SEPERATOR_LINE + "\n")
for name in self.groups: for name in self.groups:
group = self.groups[name] group = self.groups[name]
if not group.solved: if not group.solved:
@ -656,6 +657,7 @@ class PkgListGen(ToolBase.ToolBase):
target_project: str, target_project: str,
target_config: Mapping[str, Any], target_config: Mapping[str, Any],
main_repo: str, main_repo: str,
git_url: str,
project: str, project: str,
scope: str, scope: str,
force: bool, force: bool,
@ -675,6 +677,37 @@ class PkgListGen(ToolBase.ToolBase):
release = target_config.get('pkglistgen-release', '000release-packages') release = target_config.get('pkglistgen-release', '000release-packages')
oldrepos = target_config.get('pkglistgen-repos', '000update-repos') oldrepos = target_config.get('pkglistgen-repos', '000update-repos')
# Cache dir specific to hostname and project.
host = urlparse(api.apiurl).hostname
prefix_dir = 'pkglistgen'
if custom_cache_tag:
prefix_dir += '-' + custom_cache_tag
cache_dir = CacheManager.directory(prefix_dir, host, project)
drop_list = []
checkout_list = [group, product, release]
if not force:
root = ET.fromstringlist(show_results_meta(api.apiurl, project, product,
repository=[main_repo], multibuild=True))
if len(root.xpath('result[@state="building"]')) or len(root.xpath('result[@state="dirty"]')):
logging.info('{}/{} build in progress'.format(project, product))
return
if git_url:
if os.path.exists(cache_dir + "/.git"):
# reset and update existing clone
logging.debug(subprocess.check_output(
['git', 'reset', '--hard'], cwd=cache_dir, encoding='utf-8'))
logging.debug(subprocess.check_output(
['git', 'pull', '--rebase'], cwd=cache_dir, encoding='utf-8'))
else:
if os.path.exists(cache_dir):
shutil.rmtree(cache_dir)
logging.debug(subprocess.check_output(
['git', 'clone', '--recurse-submodules', git_url, cache_dir], encoding='utf-8'))
if os.path.exists(cache_dir + '/' + '000update-repos'):
logging.error('No support for 000update-repos in git projects atm')
return
else:
url = api.makeurl(['source', project]) url = api.makeurl(['source', project])
packages = ET.parse(http_GET(url)).getroot() packages = ET.parse(http_GET(url)).getroot()
if packages.find('entry[@name="{}"]'.format(product)) is None: if packages.find('entry[@name="{}"]'.format(product)) is None:
@ -683,15 +716,8 @@ class PkgListGen(ToolBase.ToolBase):
# TODO disable build. # TODO disable build.
logging.info('{} undeleted, skip dvd until next cycle'.format(product)) logging.info('{} undeleted, skip dvd until next cycle'.format(product))
return return
elif not force:
root = ET.fromstringlist(show_results_meta(api.apiurl, project, product,
repository=[main_repo], multibuild=True))
if len(root.xpath('result[@state="building"]')) or len(root.xpath('result[@state="dirty"]')):
logging.info('{}/{} build in progress'.format(project, product))
return
drop_list = api.item_exists(project, oldrepos) drop_list = api.item_exists(project, oldrepos)
checkout_list = [group, product, release]
if drop_list and not only_release_packages: if drop_list and not only_release_packages:
checkout_list.append(oldrepos) checkout_list.append(oldrepos)
@ -701,13 +727,6 @@ class PkgListGen(ToolBase.ToolBase):
logging.info('{} undeleted, skip dvd until next cycle'.format(release)) logging.info('{} undeleted, skip dvd until next cycle'.format(release))
return return
# Cache dir specific to hostname and project.
host = urlparse(api.apiurl).hostname
prefix_dir = 'pkglistgen'
if custom_cache_tag:
prefix_dir += '-' + custom_cache_tag
cache_dir = CacheManager.directory(prefix_dir, host, project)
if not no_checkout: if not no_checkout:
if os.path.exists(cache_dir): if os.path.exists(cache_dir):
shutil.rmtree(cache_dir) shutil.rmtree(cache_dir)
@ -722,36 +741,41 @@ class PkgListGen(ToolBase.ToolBase):
self.input_dir = group_dir self.input_dir = group_dir
self.output_dir = product_dir self.output_dir = product_dir
if not no_checkout and not git_url:
logging.debug('Skipping checkout of {}'.format(project))
for package in checkout_list: for package in checkout_list:
if no_checkout:
logging.debug('Skipping checkout of {}/{}'.format(project, package))
continue
checkout_package(api.apiurl, project, package, expand_link=True, checkout_package(api.apiurl, project, package, expand_link=True,
prj_dir=cache_dir, outdir=os.path.join(cache_dir, package)) prj_dir=cache_dir, outdir=os.path.join(cache_dir, package))
file_utils.unlink_all_except(release_dir, ['weakremovers.inc', '*.changes']) file_utils.unlink_all_except(release_dir, ['weakremovers.inc', '*.changes'])
if not only_release_packages: if not only_release_packages:
file_utils.unlink_all_except(product_dir) file_utils.unlink_all_except(product_dir)
ignore_list = ['supportstatus.txt', 'summary-staging.txt', 'package-groups.changes'] ignore_list = ['supportstatus.txt', 'summary-staging.txt', 'package-groups.changes',
'default.productcompose.in']
ignore_list += self.group_input_files() ignore_list += self.group_input_files()
file_utils.copy_directory_contents(group_dir, product_dir, ignore_list) file_utils.copy_directory_contents(group_dir, product_dir, ignore_list)
file_utils.change_extension(product_dir, '.spec.in', '.spec') file_utils.change_extension(product_dir, '.spec.in', '.spec')
file_utils.change_extension(product_dir, '.product.in', '.product') file_utils.change_extension(product_dir, '.product.in', '.product')
fn = os.path.join(group_dir, 'default.productcompose') fn = os.path.join(group_dir, 'default.productcompose.in')
if os.path.isfile(fn): if os.path.isfile(fn):
if not os.path.isdir(self.productcompose_dir): if not os.path.isdir(self.productcompose_dir):
os.mkdir(self.productcompose_dir) # currently always creating it local at least raise Exception('default.productcompose.in exists, but output directory is missing in git!')
lines = open(fn).readlines() lines = open(fn).readlines()
new_lines = [] new_lines = []
for line in lines: for line in lines:
if line.startswith('# The following is generated by openSUSE-release-tools'): if line.startswith(PRODUCT_COMPOSE_SEPERATOR_LINE):
break break
new_lines.append(line) new_lines.append(line)
open(os.path.join(self.productcompose_dir, 'default.productcompose'), 'w').write(''.join(new_lines)) open(os.path.join(self.productcompose_dir, 'default.productcompose'), 'w').write(''.join(new_lines))
<<<<<<< HEAD
if git_url: if git_url:
logging.debug(subprocess.check_output( logging.debug(subprocess.check_output(
['git', 'add', self.productcompose_dir, f"{self.productcompose_dir}/default.productcompose"], ['git', 'add', self.productcompose_dir, f"{self.productcompose_dir}/default.productcompose"],
cwd=cache_dir, encoding='utf-8')) cwd=cache_dir, encoding='utf-8'))
=======
if os.path.isfile(os.path.join(group_dir, 'supportstatus.txt')):
shutil.copy(os.path.join(group_dir, 'supportstatus.txt'), self.productcompose_dir)
>>>>>>> 8c7c16b8 (git cleanup)
self.skip_productcompose = False self.skip_productcompose = False
else: else:
# No template file, so we don't create one either # No template file, so we don't create one either
@ -834,6 +858,10 @@ class PkgListGen(ToolBase.ToolBase):
"- automatically generated by openSUSE-release-tools/pkglistgen\n\n" "- automatically generated by openSUSE-release-tools/pkglistgen\n\n"
) )
if git_url:
logging.debug(subprocess.check_output(
'git add *.spec', cwd=release_dir, shell=True, encoding='utf-8'))
else:
self.commit_package(release_dir) self.commit_package(release_dir)
if only_release_packages: if only_release_packages:
@ -854,6 +882,26 @@ class PkgListGen(ToolBase.ToolBase):
for line in sorted(output): for line in sorted(output):
f.write(line + '\n') f.write(line + '\n')
if git_url:
<<<<<<< HEAD
if os.path.isfile(f"{productcompose_dir}/default.productcompose"):
logging.debug(subprocess.check_output(
['git', 'add', productcompose_dir, f"{productcompose_dir}/default.productcompose"],
cwd=cache_dir, encoding='utf-8'))
if os.path.isdir(product_dir):
=======
if product_dir and os.path.isdir(product_dir):
>>>>>>> 8c7c16b8 (git cleanup)
logging.debug(subprocess.check_output(
['git', 'add', productcompose_dir, product_dir],
cwd=cache_dir, encoding='utf-8'))
logging.debug(subprocess.check_output(
'git commit -m "Update by pkglistgen of openSUSE-release-tool"', cwd=cache_dir, shell=True, encoding='utf-8'))
if not self.dry_run:
logging.debug(subprocess.check_output(
'git push', cwd=cache_dir, shell=True, encoding='utf-8'))
elif not self.dry_run:
self.commit_package(product_dir) self.commit_package(product_dir)
if os.path.isfile(reference_summary): if os.path.isfile(reference_summary):