From c4a64b91c03dde47aed270b44d11c6602ce97019 Mon Sep 17 00:00:00 2001 From: Huang Hao Date: Mon, 12 Nov 2012 18:05:48 +0800 Subject: [PATCH] Change --spec to use only base file name Detail of discussion is in #485 * --spec accept only base file name * only check spec file in working copy if --include-all is given, otherwise check in commit * if gbs want to parse spec file, it must parse the correct version that in --commit(default HEAD) * unify --spec for all subcommands - ch: use the spec given first - rb: --include-all means to use work copy, otherwise checkout spec in some revision to parse - lb: check only one project is about to build and pass --spec directly to depanneur * add several helper functions - show_file_from_rev: cat file content from given rev - file_exists_in_rev: check whether file exist in given rev - glob_in_rev: glob file pattern in given rev Change-Id: I0621634b50ddedf65eab3c41db4d6f537194b345 --- gitbuildsys/cmd_build.py | 12 ++---- gitbuildsys/cmd_changelog.py | 12 +++--- gitbuildsys/cmd_export.py | 19 ++++----- gitbuildsys/cmd_remotebuild.py | 35 +++++++++------ gitbuildsys/parsing.py | 10 ++++- gitbuildsys/utils.py | 97 +++++++++++++++++++++++++++++++----------- tools/gbs | 31 +++++++------- 7 files changed, 139 insertions(+), 77 deletions(-) diff --git a/gitbuildsys/cmd_build.py b/gitbuildsys/cmd_build.py index 34704dd..f244fec 100644 --- a/gitbuildsys/cmd_build.py +++ b/gitbuildsys/cmd_build.py @@ -234,13 +234,9 @@ def main(args): repo = RpmGitRepository(workdir) workdir = repo.path except GitRepositoryError: - pass - - if args.spec: - try: - RpmGitRepository(os.path.dirname(args.spec)) - except GitRepositoryError: - msger.error('spec file should reside in git directory') + if args.spec: + msger.error("git project can't be found for --spec, " + "give it in argument or cd into it") hostarch = get_hostarch() if args.arch: @@ -317,7 +313,7 @@ def main(args): if args.define: cmd += [('--define="%s"' % i) for i in args.define] if args.spec: - cmd += [args.spec] + cmd += ['--spec=%s' % args.spec] msger.debug("running command: %s" % ' '.join(cmd)) retcode = os.system(' '.join(cmd)) diff --git a/gitbuildsys/cmd_changelog.py b/gitbuildsys/cmd_changelog.py index fbcab0f..8f0ee4e 100644 --- a/gitbuildsys/cmd_changelog.py +++ b/gitbuildsys/cmd_changelog.py @@ -94,15 +94,17 @@ def main(args): changes_file_list = glob.glob("%s/%s/*.changes" % (project_root_dir, packaging_dir)) - if changes_file_list: + if args.spec or not changes_file_list: + # Create .changes file with the same name as a spec + specfile = os.path.basename(utils.guess_spec(project_root_dir, + packaging_dir, args.spec)) + fn_changes = os.path.splitext(specfile)[0] + ".changes" + fn_changes = os.path.join(project_root_dir, packaging_dir, fn_changes) + else: fn_changes = changes_file_list[0] if len(changes_file_list) > 1: msger.warning("Found more than one changes files, %s is taken " \ % (changes_file_list[0])) - else: - # Create .changes file with the same name as a spec - specfile = utils.guess_spec(project_root_dir, packaging_dir, args.spec) - fn_changes = os.path.splitext(specfile)[0] + ".changes" # get the commit start from the args.since if args.since: diff --git a/gitbuildsys/cmd_export.py b/gitbuildsys/cmd_export.py index 0562971..349b570 100644 --- a/gitbuildsys/cmd_export.py +++ b/gitbuildsys/cmd_export.py @@ -186,7 +186,13 @@ def main(args): # Only guess spec filename here, parse later when we have the correct # spec file at hand - specfile = utils.guess_spec(workdir, packaging_dir, args.spec) + if args.commit: + commit = args.commit + elif args.include_all: + commit = 'WC.UNTRACKED' + else: + commit = 'HEAD' + relative_spec = utils.guess_spec(workdir, packaging_dir, args.spec, commit) outdir = "%s/packaging" % workdir if args.outdir: @@ -199,18 +205,11 @@ def main(args): export_dir = tempd.path with utils.Workdir(workdir): - if args.commit: - commit = args.commit - elif args.include_all: - commit = 'WC.UNTRACKED' - else: - commit = 'HEAD' - relative_spec = specfile.replace('%s/' % workdir, '') export_sources(repo, commit, export_dir, relative_spec, args) + specfile = os.path.basename(relative_spec) try: - spec = rpm.parse_spec(os.path.join(export_dir, - os.path.basename(specfile))) + spec = rpm.parse_spec(os.path.join(export_dir, specfile)) except GbpError, err: msger.error('%s' % err) diff --git a/gitbuildsys/cmd_remotebuild.py b/gitbuildsys/cmd_remotebuild.py index 9989378..d8d18fc 100644 --- a/gitbuildsys/cmd_remotebuild.py +++ b/gitbuildsys/cmd_remotebuild.py @@ -82,14 +82,32 @@ def main(args): # TODO: check ./packaging dir at first packaging_dir = get_packaging_dir(args) - specs = glob.glob('%s/%s/*.spec' % (workdir, packaging_dir)) - if not specs: - msger.error("no spec file found under './%s'" % packaging_dir) - specfile = utils.guess_spec(workdir, packaging_dir, args.spec) + if args.commit: + commit = args.commit + elif args.include_all: + commit = 'WC.UNTRACKED' + else: + commit = 'HEAD' + + relative_spec = utils.guess_spec(workdir, packaging_dir, args.spec, commit) + + if args.include_all: + # include_all means to use work copy, + # otherwise use the reversion in git history + spec_to_parse = os.path.join(workdir, relative_spec) + else: + content = utils.show_file_from_rev(workdir, relative_spec, commit) + if content is None: + msger.error('failed to checkout %s ' + 'from commit: %s' % (relative_spec, commit)) + + tmp_spec = utils.Temp(content=content) + spec_to_parse = tmp_spec.path + # get 'name' and 'version' from spec file try: - spec = gbp.rpm.parse_spec(specfile) + spec = gbp.rpm.parse_spec(spec_to_parse) except GbpError, err: msger.error('%s' % err) @@ -165,13 +183,6 @@ def main(args): msger.error(str(err)) with utils.Workdir(workdir): - if args.commit: - commit = args.commit - elif args.include_all: - commit = 'WC.UNTRACKED' - else: - commit = 'HEAD' - relative_spec = specfile.replace('%s/' % workdir, '') export_sources(repo, commit, exportdir, relative_spec, args) try: diff --git a/gitbuildsys/parsing.py b/gitbuildsys/parsing.py index 5c29da9..776d44c 100644 --- a/gitbuildsys/parsing.py +++ b/gitbuildsys/parsing.py @@ -18,10 +18,11 @@ """Local additions to commandline parsing.""" +import os import re import functools -from argparse import RawDescriptionHelpFormatter +from argparse import RawDescriptionHelpFormatter, ArgumentTypeError class GbsHelpFormatter(RawDescriptionHelpFormatter): """Changed default argparse help output by request from cmdln lovers.""" @@ -86,3 +87,10 @@ def subparser(func): subpar.set_defaults(module="cmd_%s" % name) return func(subpar) return wrapper + + +def basename_type(path): + '''validate function for base file name argument''' + if os.path.basename(path) != path: + raise ArgumentTypeError('should be a file name rather than a path') + return path diff --git a/gitbuildsys/utils.py b/gitbuildsys/utils.py index b2c5385..04a06ed 100644 --- a/gitbuildsys/utils.py +++ b/gitbuildsys/utils.py @@ -22,7 +22,9 @@ import tempfile import shutil import pycurl import hashlib +import fnmatch import signal +import subprocess import xml.etree.ElementTree as ET from collections import defaultdict @@ -43,31 +45,34 @@ class Workdir(object): def __exit__(self, _type, _value, _tb): os.chdir(self._cwd) -def guess_spec(workdir, packaging_dir, default_spec): - workdir = os.path.abspath(workdir) - git_project = os.path.basename(workdir) - - if default_spec: - default_spec = os.path.abspath(default_spec) - if not default_spec.startswith(workdir): - msger.error("spec file doesn't belong to the project %s: " - "%s" % (git_project, default_spec)) - if not os.path.exists(default_spec): - msger.error('%s does not exit' % default_spec) - return default_spec - - specfile = os.path.join(workdir, packaging_dir, '%s.spec' % git_project) - if not os.path.exists(specfile): - specs = glob.glob(os.path.join(workdir, packaging_dir, '*.spec')) - if not specs: - msger.error('no spec file found under %s/%s' % (workdir, - packaging_dir)) - - if len(specs) > 1: - msger.error("Can't decide which spec file to use.") - else: - specfile = specs[0] - return specfile +def guess_spec(git_path, packaging_dir, given_spec, commit_id='WC.UNTRACKED'): + '''guess spec file from project name if not given''' + git_path = os.path.abspath(git_path) + + if commit_id == 'WC.UNTRACKED': + check = lambda fname: os.path.exists(os.path.join(git_path, fname)) + glob_ = lambda pattern: [ name.replace(git_path+'/', '') + for name in glob.glob(os.path.join(git_path, pattern)) ] + msg = 'No such spec file %s' + else: + check = lambda fname: file_exists_in_rev(git_path, fname, commit_id) + glob_ = lambda pattern: glob_in_rev(git_path, pattern, commit_id) + msg = "No such spec file %%s in %s" % commit_id + + if given_spec: + spec = os.path.join(packaging_dir, given_spec) + if not check(spec): + msger.error(msg % spec) + return spec + + specs = glob_(os.path.join(packaging_dir, '*.spec')) + if not specs: + msger.error("can't find any spec file") + + project_name = os.path.basename(git_path) + spec = os.path.join(packaging_dir, '%s.spec' % project_name) + return spec if spec in specs else specs[0] + class Temp(object): """ @@ -460,3 +465,45 @@ def hexdigest(fhandle, block_size=4096): md5obj.update(data) return md5obj.hexdigest() + +def show_file_from_rev(git_path, relative_path, commit_id): + '''return a single file content from given rev.''' + + cmd = 'cd %s; git show %s:%s' % (git_path, commit_id, relative_path) + try: + return subprocess.check_output(cmd, shell=True) + except (subprocess.CalledProcessError, OSError), err: + msger.debug('failed to checkout %s from %s:%s' % (relative_path, + commit_id, str(err))) + return None + + +def file_exists_in_rev(git_path, relative_path, commit_id): + '''return True if file exists in given rev''' + + cmd = 'cd %s; git ls-tree --name-only %s %s' % ( + git_path, commit_id, relative_path) + + try: + output = subprocess.check_output(cmd, shell=True) + except (subprocess.CalledProcessError, OSError), err: + raise errors.GbsError('failed to check existence of %s in %s:%s' % ( + relative_path, commit_id, str(err))) + + return output != '' + + +def glob_in_rev(git_path, pattern, commit_id): + '''glob pattern in given rev''' + + path = os.path.dirname(pattern) + cmd = 'cd %s; git ls-tree --name-only %s %s/' % ( + git_path, commit_id, path) + + try: + output = subprocess.check_output(cmd, shell=True) + except (subprocess.CalledProcessError, OSError), err: + raise errors.GbsError('failed to glob %s in %s:%s' % ( + pattern, commit_id, str(err))) + + return fnmatch.filter(output.splitlines(), pattern) diff --git a/tools/gbs b/tools/gbs index 1faeb63..3ac2de9 100755 --- a/tools/gbs +++ b/tools/gbs @@ -21,19 +21,11 @@ import sys import os -from argparse import ArgumentParser, ArgumentTypeError +from argparse import ArgumentParser from gitbuildsys import __version__ from gitbuildsys import msger, errors -from gitbuildsys.parsing import subparser, GbsHelpFormatter - - -def abspath_arg(arg): - '''validate path argument and convert to absolute format''' - path = os.path.abspath(arg) - if not os.path.exists(path): - raise ArgumentTypeError('no such %s' % path) - return path +from gitbuildsys.parsing import subparser, GbsHelpFormatter, basename_type @subparser @@ -69,7 +61,7 @@ def import_parser(parser): def export_parser(parser): """export files and prepare for build Examples: - $ gbs export --spec packaging/my.spec --commit d64065c + $ gbs export --spec my.spec --commit d64065c $ gbs export --source-rpm -o /tmp/ $ gbs export --include-all """ @@ -79,7 +71,9 @@ def export_parser(parser): help='path to git repository') parser.add_argument('-o', '--outdir', help='output directory') - parser.add_argument('--spec', help='specify a spec file to use') + parser.add_argument('--spec', type=basename_type, + help='specify a spec file to use. It should be a file ' + 'name that GBS will find it in packaging dir') parser.add_argument('-c', '--commit', help='specify a commit ID to export') parser.add_argument('--include-all', action='store_true', help='uncommitted changes and untracked files ' @@ -152,8 +146,9 @@ def build_parser(parser): parser.add_argument('--skip-conf-repos', action="store_true", help='skip repositories mentioned in config file') parser.add_argument('-c', '--commit', help='specify a commit ID to build') - parser.add_argument('--spec', type=abspath_arg, - help='specify a spec file to use') + parser.add_argument('--spec', type=basename_type, + help='specify a spec file to use. It should be a file ' + 'name that GBS will find it in packaging dir') parser.add_argument('--extra-packs', help='specify extra packages to install to build root ' 'multiple packages can be separated by comma') @@ -244,7 +239,9 @@ def remotebuild_parser(parser): parser.add_argument('-P', '--profile', help='profile to be used for building, can be given ' 'without the "profile." prefix') - parser.add_argument('--spec', help='specify a spec file to use') + parser.add_argument('--spec', type=basename_type, + help='specify a spec file to use. It should be a file ' + 'name that GBS will find it in packaging dir') parser.add_argument('-c', '--commit', help='specify a commit ID to build') parser.add_argument('--buildlog', action='store_true', help='get buildlog from build sever') @@ -307,7 +304,9 @@ def changelog_parser(parser): default=os.getcwd(), help='path to git repository') - parser.add_argument('--spec', help='specify a spec file to use') + parser.add_argument('--spec', type=basename_type, + help='specify a spec file to use. It should be a file ' + 'name that GBS will find it in packaging dir') parser.add_argument('-s', '--since', help='commit to start from') parser.add_argument('-m', '--message', -- 2.7.4