From bb49460fa0bf40ece2ff2489acbe0ac718498114 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Apr 04 2016 01:14:38 +0000 Subject: Document default behaviour and signature verification algorithm help2man --no-info ./spectool -o spectool.1 generates a fairly nice man page. --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae7d65a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/__pycache__/ +/spectool.1 diff --git a/README.rst b/README.rst index 964123d..eec6891 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,7 @@ -This is a rewrite of spectool in Python (3.3). +spectool +======== + +This is a rewrite of spectool in Python (3.3+). The initial goal is for it to be completely compatible with the Perl version, including having identical non-debug non-help output. @@ -6,3 +9,76 @@ including having identical non-debug non-help output. Further goals are to use the Python rpm and curl bindings to avoid shelling out to curl and rpm, and to eventually handle more advanced situations like automatically generating git checkouts and validating signatures. + +Source URL and destination file name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default ``spectool`` uses the component of the URL (anything after the last +slash) as the destination file name. It is often desirable to specify a custom +destination file name. This is possible by appending a pseudo fragment identifier +that includes a slash. + +Examples:: + + https://example.net/path/to/file.tar.gz → file.tar.gz + https://example.net/path/to/file.tar.gz#/project-file.tar.gz → project-file.tar.gz + +In particular this works for github/gitlab/bitbucket archive URLs:: + + Source0: https://github.com/OWNER/PROJECT/archive/GIT-TAG.tar.gz#/PROJECT-VERSION.tar.gz + Source1: https://gitlab.com/OWNER/NAME/repository/archive.tar.gz?ref=GIT-TAG#/NAME-VERSION.tar.gz + Source2: https://bitbucket.org/OWNER/NAME/get/GIT-TAG.tar.gz#/NAME-VERSION.tar.gz + +Common usage +~~~~~~~~~~~~ + +If the specfile is not specified, and current directory contains just +one specfile, that specfile will be used by default. + +When invoked without arguments:: + + $ spectool + +all Sources and Patches specified in the specfile will be listed. + +To download all Sources and Patches:: + + $ spectool -g + +Signatures will be verified if they are listed among the Sources. + +Signature verification +~~~~~~~~~~~~~~~~~~~~~~ + +``spectool`` currently uses ``gpgv2`` to verify signatures. Currently +only detached signatures are supported. + +The following algorithm is used: + +1. For all Sources, if the destination file name ends in ``.gpg`` or + ``.sig``, it is considered a signature. +2. For all Sources and Patches, it is checked if a matching signature + is specified. The name of the signature file must be the same but + with ``.gpg`` or ``.sig`` appended. +3. If a signature file is specified, the Source or Patch is checked. +4. Signatures are checked against a keyring which must be specified in + a file. It can be specified using ``--keyring=N``, in which case + SourceN is used as the keyring, or ``--keyring=FILE``, in which + case FILE is used as the keyring. If the keyring is not specified + explicitly, if there's just one Source with ``.kbx`` extension, it + will be used as the keyring, and otherwise, if there's a Source + with the ``.gpg`` extension that does match not another Source or + Patch as specified in point 2., it will be used as the keyring. If + the ``--keyring`` option is not used and the keyring cannot be + guessed, verification fails. +5. Verification is performed automatically after download, and also + when ``--verify`` option is specified. + +Example (based on the youtube-dl package):: + + Source0: https://yt-dl.org/downloads/2016.03.06/youtube-dl-2016.03.06.tar.gz + Source1: https://yt-dl.org/downloads/2016.03.06/youtube-dl-2016.03.06.tar.gz.sig + Source2: gpgkey-7D33D762FD6C35130481347FDB4B54CBA4826A18.gpg + +In this case Source2 is used as the keyring, Source1 as the signature, +and Source0 is the file that is verified. diff --git a/spectool b/spectool index 1bc4d03..f92bf28 100755 --- a/spectool +++ b/spectool @@ -9,14 +9,30 @@ import os.path from subprocess import CalledProcessError, PIPE, Popen, TimeoutExpired from urllib import request -# Python conversion of spectool. +__doc__ = ''' +A tool to download sources and patches from specfiles. -VERSION = '2.0' -CURLRC = '/etc/rpmdevrools/curlrc' -PROTOCOLS = ['ftp', 'http', 'https'] +If the specfile is not specified, and current directory contains just +one specfile, that specfile will be used by default. + +When invoked without arguments: + +$ spectool + +all Sources and Patches specified in the specfile will be listed. + +To download all Sources and Patches: + +$ spectool -g + +Signatures will be verified if they are listed among the Sources. +''' + +__version__ = '2.0' +PROTOCOLS = {'http', 'https', 'ftp'} dbprint = None -USER_AGENT = 'spectool/' + VERSION +USER_AGENT = 'spectool/' + __version__ # Sure wish I had Python 3.5's subprocess.run(), so here's a hacked one. @@ -171,7 +187,6 @@ class Selections(object): else: raise SelectionError('No patch item {}.'.format(patch)) - def parseopts(): def flatten_commas(items): """Turn a bunch of comma-separated lists into one flat list.""" @@ -185,10 +200,9 @@ def parseopts(): return out parser = argparse.ArgumentParser( - description='A tool to download sources and patches from spectiles.', - usage='%(prog)s [options] ', - epilog="Files:\n/etc/rpmdevrools/curlrc\n optional curl(1) configuration", + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, + allow_abbrev=False, add_help=False) parser.add_argument('spec', help='The specfile to be parsed', nargs='?') @@ -202,6 +216,7 @@ def parseopts(): help='verify the signatures on files') mode.add_argument('-h', '--help', action='help', help="display this help screen") + mode1.add_argument('--version', action='version', version='spectool ' + __version__) select = parser.add_argument_group('Files on which to operate') select.add_argument('-A', '--all', action='store_true', default=True, @@ -211,9 +226,9 @@ def parseopts(): select.add_argument('-P', '--patches', action='store_true', dest='allpatches', help='all patches') select.add_argument('-s', '--source', action='append', dest='sourcelist', - help='specified sources', metavar='x[,y[,...]]') + help='specified source numbers', metavar='x[,y[,...]]') select.add_argument('-p', '--patch', action='append', dest='patchlist', - help='specified patches', metavar='a[,b[,...]]') + help='specified patch numbers', metavar='a[,b[,...]]') misc = parser.add_argument_group('Miscellaneous') misc.add_argument('-d', '--define', action='append', dest='defines', @@ -221,8 +236,7 @@ def parseopts(): help="defines RPM macro 'macro' to be 'value'") misc1 = misc.add_mutually_exclusive_group() - misc1.add_argument('-C', '--directory', action='store', dest='downloaddir', - metavar='dir', + misc1.add_argument('-C', '--directory', dest='downloaddir', metavar='dir', help="download into specified directory (default '.')") misc1.add_argument('-R', '--sourcedir', action='store_true', dest='downloadtosourcedir', help="download into RPM's %%{_sourcedir}") @@ -232,7 +246,7 @@ def parseopts(): misc.add_argument('-f', '--force', action='store_true', help="try to unlink and download if target files exist") - misc.add_argument('--keyring', + misc.add_argument('--keyring', metavar='FILE-OR-NUMBER', help="path to file or Source number for the keyring with trusted key") misc.add_argument('--no-verify', action='store_false', dest='verify', help='skip signatures verification') @@ -337,7 +351,7 @@ def list_files(spec, opts, selected): def is_downloadable(url): """Check that string is a valid URL of a protocol which we can handle.""" - return url.split('://')[0] in {'http', 'https', 'ftp'} + return url.split('://')[0] in PROTOCOLS def is_signature(url): """Check that path looks like a signature."""