Script 'mail_helper' called by obssrc Hello community,
here is the log from the commit of package osc for openSUSE:Factory checked in at 2022-07-29 16:48:20 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/osc (Old) and /work/SRC/openSUSE:Factory/.osc.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "osc"
Fri Jul 29 16:48:20 2022 rev:164 rq:991675 version:0.181.0
Changes: -------- --- /work/SRC/openSUSE:Factory/osc/osc.changes 2022-06-25 10:24:51.170724349 +0200 +++ /work/SRC/openSUSE:Factory/.osc.new.1533/osc.changes 2022-07-29 16:48:25.962741777 +0200 @@ -1,0 +2,16 @@ +Tue Jul 26 19:37:46 UTC 2022 - Daniel Mach daniel.mach@suse.com + +- 0.181.0 + - fix crash when 'pass' is not set in the config file + - add missing attributes to Package when scm_url is set + - fix failure to create config in current dir + - update list of considered file names for ssh key autodetection + - allow users to prefer ssh key over password auth + - ssh: recognize gpg keys (yubikey usage) + - fix operating on _project meta + - revert "interpretation of string literals in messages" that broke unicode handling + - fix product build rpm caching + - enable md5 revisions in osc log + - parseRevisionOption(): raise an exception on invalid revisions + +-------------------------------------------------------------------
Old: ---- osc-0.180.0.tar.gz
New: ---- osc-0.181.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences: ------------------ ++++++ osc.spec ++++++ --- /var/tmp/diff_new_pack.d2ztT7/_old 2022-07-29 16:48:26.782744057 +0200 +++ /var/tmp/diff_new_pack.d2ztT7/_new 2022-07-29 16:48:26.786744068 +0200 @@ -27,7 +27,7 @@ %define use_python python %endif
-%define version_unconverted 0.180.0 +%define version_unconverted 0.181.0 %define osc_plugin_dir %{_prefix}/lib/osc-plugins %define macros_file macros.osc %if ! %{defined _rpmmacrodir} @@ -35,7 +35,7 @@ %endif
Name: osc -Version: 0.180.0 +Version: 0.181.0 Release: 0 Summary: Open Build Service Commander License: GPL-2.0-or-later
++++++ PKGBUILD ++++++ --- /var/tmp/diff_new_pack.d2ztT7/_old 2022-07-29 16:48:26.826744180 +0200 +++ /var/tmp/diff_new_pack.d2ztT7/_new 2022-07-29 16:48:26.830744191 +0200 @@ -1,5 +1,5 @@ pkgname=osc -pkgver=0.180.0 +pkgver=0.181.0 pkgrel=0 pkgdesc="Open Build Service client" arch=('x86_64')
++++++ _service ++++++ --- /var/tmp/diff_new_pack.d2ztT7/_old 2022-07-29 16:48:26.850744247 +0200 +++ /var/tmp/diff_new_pack.d2ztT7/_new 2022-07-29 16:48:26.866744291 +0200 @@ -1,7 +1,7 @@ <services> <service name="tar_scm" mode="disabled"> - <param name="version">0.180.0</param> - <param name="revision">0.180.0</param> + <param name="version">0.181.0</param> + <param name="revision">0.181.0</param> <param name="url">https://github.com/openSUSE/osc.git</param> <param name="scm">git</param> </service>
++++++ debian.changelog ++++++ --- /var/tmp/diff_new_pack.d2ztT7/_old 2022-07-29 16:48:26.906744402 +0200 +++ /var/tmp/diff_new_pack.d2ztT7/_new 2022-07-29 16:48:26.910744413 +0200 @@ -1,4 +1,4 @@ -osc (0.180.0-0) unstable; urgency=low +osc (0.181.0-0) unstable; urgency=low - Update to 0.174.0: - fix password deletion via "osc config -d <apiurl> pass" - support changing the password store via "osc config <apiurl>
++++++ osc-0.180.0.tar.gz -> osc-0.181.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/NEWS new/osc-0.181.0/NEWS --- old/osc-0.180.0/NEWS 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/NEWS 2022-07-26 21:34:07.000000000 +0200 @@ -1,3 +1,16 @@ +0.181.0 + - fix crash when 'pass' is not set in the config file + - add missing attributes to Package when scm_url is set + - fix failure to create config in current dir + - update list of considered file names for ssh key autodetection + - allow users to prefer ssh key over password auth + - ssh: recognize gpg keys (yubikey usage) + - fix operating on _project meta + - revert "interpretation of string literals in messages" that broke unicode handling + - fix product build rpm caching + - enable md5 revisions in osc log + - parseRevisionOption(): raise an exception on invalid revisions + 0.180.0 - warn when trying to commit a prj/pac managed in scm - fix crash on "osc up" for git based package/projects diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/osc/build.py new/osc-0.181.0/osc/build.py --- old/osc-0.180.0/osc/build.py 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/osc/build.py 2022-07-26 21:34:07.000000000 +0200 @@ -293,7 +293,7 @@
def makeurls(self, cachedir, urllist): - self.localdir = '%s/%s/%s/%s' % (cachedir, self.project, self.repository, self.arch) + self.localdir = '%s/%s/%s/%s' % (cachedir, self.project, self.repository, self.repoarch) self.fullfilename = os.path.join(self.localdir, self.canonname) self.urllist = [url % self.mp for url in urllist]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/osc/commandline.py new/osc-0.181.0/osc/commandline.py --- old/osc-0.180.0/osc/commandline.py 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/osc/commandline.py 2022-07-26 21:34:07.000000000 +0200 @@ -983,10 +983,6 @@ # If project or package arguments missing, assume to work # with project and/or package in current local directory. attributepath = [] - - if opts.message: - opts.message = str(opts.message.encode().decode('unicode_escape')) - if cmd in ['prj', 'prjconf']: if len(args) < 1: apiurl = store_read_apiurl(os.curdir) @@ -1291,7 +1287,7 @@ src_update = "noupdate"
if opts.message: - opts.message = str(opts.message.encode().decode('unicode_escape')) + pass elif opts.file: if opts.file == '-': opts.message = sys.stdin.read() @@ -1962,8 +1958,6 @@
if not opts.message: opts.message = edit_message() - else: - opts.message = str(opts.message.encode().decode('unicode_escape'))
xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \ (actionsxml, _html_escape(opts.message or "")) @@ -2041,8 +2035,6 @@ raise oscerr.WrongOptions('invalid '--role': either specify 'maintainer' or 'bugowner'') if not opts.message: opts.message = edit_message() - else: - opts.message = str(opts.message.encode().decode('unicode_escape'))
r = Request() if user.startswith('group:'): @@ -2121,8 +2113,6 @@ footer = textwrap.TextWrapper(width = 66).fill( 'please explain why you like to delete project %s' % project) opts.message = edit_message(footer) - else: - opts.message = str(opts.message.encode().decode('unicode_escape'))
r = Request() r.add_action('delete', tgt_project=project, tgt_package=package, tgt_repository=repository) @@ -2170,8 +2160,6 @@ 'please explain why you like to change the devel project of %s/%s to %s/%s' % (project, package, devel_project, devel_package)) opts.message = edit_message(footer) - else: - opts.message = str(opts.message.encode().decode('unicode_escape'))
r = Request() r.add_action('change_devel', src_project=devel_project, src_package=devel_package, @@ -2341,9 +2329,6 @@ if opts.incoming: conf.config['include_request_from_project'] = False
- if opts.message: - opts.message = str(opts.message.encode().decode('unicode_escape')) - if args[0] == 'help': return self.do_help(['help', 'request'])
@@ -2895,8 +2880,7 @@ raise oscerr.WrongArgs('Too many arguments (required none or two)') else: raise oscerr.WrongArgs('Too few arguments (required none or two)') - if opts.message: - opts.message = str(opts.message.encode().decode('unicode_escape')) + try: copy_pac(apiurl, project, package, apiurl, project, package, expand=True, comment=opts.message) except HTTPError as e: @@ -3108,7 +3092,7 @@ rev, dummy = parseRevisionOption(opts.revision)
if opts.message: - comment = str(opts.message.encode().decode('unicode_escape')) + comment = opts.message else: if not rev: rev = show_upstream_rev(src_apiurl, src_project, src_package) @@ -3255,8 +3239,6 @@
if not opts.message: opts.message = edit_message() - else: - opts.message = str(opts.message.encode().decode('unicode_escape'))
if 'kind' in root.attrib and root.attrib['kind'] == 'maintenance_incident': r = create_release_request(apiurl, source_project, opts.message) @@ -3300,8 +3282,6 @@ maintenance_attribute = conf.config['maintenance_attribute'] if opts.attribute: maintenance_attribute = opts.attribute - if opts.message: - opts.message = str(opts.message.encode().decode('unicode_escape'))
source_project = target_project = None
@@ -3440,8 +3420,6 @@
if not opts.message: opts.message = edit_message() - else: - opts.message = str(opts.message.encode().decode('unicode_escape'))
supersede_existing = False reqs = [] @@ -3665,9 +3643,6 @@ if len(args) >= 4: tpackage = args[3]
- if opts.message: - opts.message = str(opts.message.encode().decode('unicode_escape')) - try: exists, targetprj, targetpkg, srcprj, srcpkg = \ branch_pkg(apiurl, args[0], args[1], @@ -3761,7 +3736,7 @@
msg = '' if opts.message: - msg = str(opts.message.encode().decode('unicode_escape')) + msg = opts.message else: msg = edit_message()
@@ -3808,7 +3783,7 @@
msg = '' if opts.message: - msg = str(opts.message.encode().decode('unicode_escape')) + msg = opts.message else: msg = edit_message()
@@ -3854,8 +3829,6 @@ ${cmd_option_list} """ apiurl = self.get_api_url() - if opts.message: - opts.message = str(opts.message.encode().decode('unicode_escape')) kind = 'prj' path_args = (project,) if package is not None: @@ -3896,7 +3869,7 @@
msg = '' if opts.message: - msg = str(opts.message.encode().decode('unicode_escape')) + msg = opts.message else: msg = edit_message()
@@ -5003,7 +4976,7 @@
msg = '' if opts.message: - msg = str(opts.message.encode().decode('unicode_escape')) + msg = opts.message elif opts.file: if opts.file == '-': msg = sys.stdin.read() @@ -7120,7 +7093,7 @@ project = args[0] package = args[1]
- rev, rev_upper = parseRevisionOption(opts.revision, allow_md5=False) + rev, rev_upper = parseRevisionOption(opts.revision) if rev and not checkRevision(project, package, rev, apiurl, opts.meta): print('Revision '%s' does not exist' % rev, file=sys.stderr) sys.exit(1) @@ -8538,7 +8511,7 @@
if requestactionsxml != "": if opts.message: - message = str(opts.message.encode().decode('unicode_escape')) + message = opts.message else: message = edit_message()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/osc/conf.py new/osc-0.181.0/osc/conf.py --- old/osc-0.180.0/osc/conf.py 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/osc/conf.py 2022-07-26 21:34:07.000000000 +0200 @@ -540,14 +540,26 @@ for authreq in headers.get_all('www-authenticate', []): scheme = authreq.split()[0].lower() authreqs[scheme] = authreq - if 'signature' in authreqs and self.signatureauthhandler and \ - (self.signatureauthhandler.sshkey_known() or 'basic' not in authreqs): + + if 'signature' in authreqs \ + and self.signatureauthhandler \ + and ( + # sshkey explicitly set in the config file, use it instead of doing basic auth + self.signatureauthhandler.sshkey_known() + or ( + # can't fall-back to basic auth, because server doesn't support it + 'basic' not in authreqs + # can't fall-back to basic auth, because there's no password provided + or not self.passwd.find_user_password(None, apiurl)[1] + )): del headers['www-authenticate'] headers['www-authenticate'] = authreqs['signature'] return self.signatureauthhandler.http_error_401(req, fp, code, msg, headers) + if 'basic' in authreqs: del headers['www-authenticate'] headers['www-authenticate'] = authreqs['basic'] + response = super(self.__class__, self).http_error_401(req, fp, code, msg, headers) # workaround for http://bugs.python.org/issue9639 if hasattr(self, 'retried'): @@ -565,7 +577,7 @@ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, _ = proc.communicate() if proc.returncode == 0 and stdout.strip(): - return stdout.splitlines() + return [self.get_fingerprint(line) for line in stdout.splitlines()] else: return []
@@ -584,21 +596,43 @@ return True return False
+ def is_ssh_public_keyfile(self, keyfile_path): + if not os.path.isfile(keyfile_path): + return False + return keyfile_path.endswith(".pub") + + @staticmethod + def get_fingerprint(line): + parts = line.strip().split(b" ") + if len(parts) < 2: + raise ValueError("Unable to retrieve ssh key fingerprint from line: {}".format(line)) + return parts[1] + def list_ssh_dir_keys(self): sshdir = os.path.expanduser('~/.ssh') keys_in_home_ssh = {} for keyfile in os.listdir(sshdir): - if keyfile.endswith(".pub"): + if keyfile.startswith(("agent-", "authorized_keys", "config", "known_hosts")): + # skip files that definitely don't contain keys continue + keyfile_path = os.path.join(sshdir, keyfile) - if not self.is_ssh_private_keyfile(keyfile_path): + # public key alone may be sufficient because the private key + # can get loaded into ssh-agent from gpg (yubikey works this way) + is_public = self.is_ssh_public_keyfile(keyfile_path) + # skip private detection if we think the key is a public one already + is_private = False if is_public else self.is_ssh_private_keyfile(keyfile_path) + + if not is_public and not is_private: continue + cmd = ["ssh-keygen", "-lf", keyfile_path] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, _ = proc.communicate() if proc.returncode == 0: - fingerprint = stdout.strip() - if fingerprint: + fingerprint = self.get_fingerprint(stdout) + if fingerprint and (fingerprint not in keys_in_home_ssh or is_private): + # prefer path to a private key keys_in_home_ssh[fingerprint] = keyfile_path return keys_in_home_ssh
@@ -610,7 +644,7 @@ if fingerprint in keys_in_home_ssh: return keys_in_home_ssh[fingerprint] sshdir = os.path.expanduser('~/.ssh') - keyfiles = ('id_ed25519', 'id_rsa') + keyfiles = ('id_ed25519', 'id_ed25519_sk', 'id_rsa', 'id_ecdsa', 'id_ecdsa_sk', 'id_dsa') for keyfile in keyfiles: keyfile_path = os.path.join(sshdir, keyfile) if os.path.isfile(keyfile_path): @@ -827,9 +861,10 @@ fname = os.readlink(fname)
# create directories to the config file (if they don't exist already) - if not os.path.exists(os.path.dirname(fname)): + fdir = os.path.dirname(fname) + if fdir: try: - os.makedirs(os.path.dirname(fname), mode=0o700) + os.makedirs(fdir, mode=0o700) except OSError as e: if e.errno != errno.EEXIST: raise diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/osc/core.py new/osc-0.181.0/osc/core.py --- old/osc-0.180.0/osc/core.py 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/osc/core.py 2022-07-26 21:34:07.000000000 +0200 @@ -7,7 +7,7 @@
from .util import git_version -__version__ = git_version.get_version('0.180.0') +__version__ = git_version.get_version('0.181.0')
# __store_version__ is to be incremented when the format of the working copy @@ -1800,6 +1800,10 @@ self.srcmd5 = None self.linkinfo = None self.serviceinfo = None + self.size_limit = None + self.meta = None + self.excluded = [] + self.filenamelist_unvers = [] return
files_tree = read_filemeta(self.dir) @@ -3612,10 +3616,6 @@ query['view'] = "blame" query['meta'] = 1
- # The fake packages _project has no _meta file - if pac.startswith('_project'): - return "" - url = makeurl(apiurl, ['source', prj, pac, '_meta'], query) try: f = http_GET(url) @@ -6851,12 +6851,15 @@ revisions = [None, None] if string: parts = string.split(':') - for i, revision in enumerate(parts[0:2], 0): + + if len(parts) > 2: + raise oscerr.OscInvalidRevision(string) + + for i, revision in enumerate(parts, 0): if revision.isdigit() or (allow_md5 and revision.isalnum() and len(revision) == 32): revisions[i] = revision elif revision != '' and revision != 'latest': - print('your revision '%s' will be ignored' % string, file=sys.stderr) - return None, None + raise oscerr.OscInvalidRevision(string)
return tuple(revisions)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/osc/credentials.py new/osc-0.181.0/osc/credentials.py --- old/osc-0.180.0/osc/credentials.py 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/osc/credentials.py 2022-07-26 21:34:07.000000000 +0200 @@ -166,6 +166,9 @@
@classmethod def decode_password(cls, password): + if password is None: + # avoid crash on encoding None when 'pass' is not specified in the config + return None compressed_pw = base64.b64decode(password.encode("ascii")) return bz2.decompress(compressed_pw).decode("ascii")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/osc/fetch.py new/osc-0.181.0/osc/fetch.py --- old/osc-0.180.0/osc/fetch.py 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/osc/fetch.py 2022-07-26 21:34:07.000000000 +0200 @@ -265,8 +265,8 @@ if not hdrmd5 or hdrmd5 != i.hdrmd5: print('%s/%s: attempting download from api, since the hdrmd5 did not match - %s != %s' % (i.project, i.name, hdrmd5, i.hdrmd5)) - os.unlink(i.fullfilename) - self.__add_cpio(i) + os.unlink(i.fullfilename) + self.__add_cpio(i)
except KeyboardInterrupt: print('Cancelled by user (ctrl-c)') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/osc/oscerr.py new/osc-0.181.0/osc/oscerr.py --- old/osc-0.180.0/osc/oscerr.py 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/osc/oscerr.py 2022-07-26 21:34:07.000000000 +0200 @@ -121,6 +121,22 @@ self.e = e self.msg = msg
+ +class OscValueError(OscBaseError): + """ + Invalid argument value (of correct type). + """ + pass + + +class OscInvalidRevision(OscValueError): + """ + Invalid revision value. + """ + def __str__(self): + return "Invalid revision value: {}".format("".join(self.args)) + + class PackageNotInstalled(OscBaseError): """ Exception raised when a package is not installed on local system diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/osc/util/git_version.py new/osc-0.181.0/osc/util/git_version.py --- old/osc-0.180.0/osc/util/git_version.py 2022-06-24 15:23:26.000000000 +0200 +++ new/osc-0.181.0/osc/util/git_version.py 2022-07-26 21:34:07.000000000 +0200 @@ -10,8 +10,9 @@ # the `version` variable contents get substituted during `git archive` # it requires adding this to .gitattributes: <path to this file> export-subst version = "$Format:%(describe:tags=true)$" - if version.startswith("$"): - # version hasn't been substituted during `git archive` + if version.startswith(("$", "%")): + # "$": version hasn't been substituted during `git archive` + # "%": "Format:" and "$" characters get removed from the version string (a GitHub bug?) return None return version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.180.0/tests/test_core.py new/osc-0.181.0/tests/test_core.py --- old/osc-0.180.0/tests/test_core.py 1970-01-01 01:00:00.000000000 +0100 +++ new/osc-0.181.0/tests/test_core.py 2022-07-26 21:34:07.000000000 +0200 @@ -0,0 +1,52 @@ +from osc.core import parseRevisionOption +from osc.oscerr import OscInvalidRevision + +import unittest + + +class TestParseRevisionOption(unittest.TestCase): + def test_empty(self): + expected = None, None + actual = parseRevisionOption("") + self.assertEqual(expected, actual) + + def test_colon(self): + expected = None, None + actual = parseRevisionOption(":") + # your revision ':' will be ignored + self.assertEqual(expected, actual) + + def test_invalid_multiple_colons(self): + self.assertRaises(OscInvalidRevision, parseRevisionOption, ":::::") + + def test_one_number(self): + expected = ("1", None) + actual = parseRevisionOption("1") + self.assertEqual(expected, actual) + + def test_two_numbers(self): + expected = ("1", "2") + actual = parseRevisionOption("1:2") + self.assertEqual(expected, actual) + + def test_invalid_multiple_numbers(self): + self.assertRaises(OscInvalidRevision, parseRevisionOption, "1:2:3:4:5") + + def test_one_hash(self): + expected = "c4ca4238a0b923820dcc509a6f75849b", None + actual = parseRevisionOption("c4ca4238a0b923820dcc509a6f75849b") + self.assertEqual(expected, actual) + + def test_two_hashes(self): + expected = ("d41d8cd98f00b204e9800998ecf8427e", "c4ca4238a0b923820dcc509a6f75849b") + actual = parseRevisionOption("d41d8cd98f00b204e9800998ecf8427e:c4ca4238a0b923820dcc509a6f75849b") + self.assertEqual(expected, actual) + + def test_invalid_multiple_hashes(self): + rev = "d41d8cd98f00b204e9800998ecf8427e:c4ca4238a0b923820dcc509a6f75849b:c81e728d9d4c2f636f067f89cc14862c" + self.assertRaises(OscInvalidRevision, parseRevisionOption, rev) + + +if __name__ == "__main__": + import unittest + unittest.main()
++++++ osc.dsc ++++++ --- /var/tmp/diff_new_pack.d2ztT7/_old 2022-07-29 16:48:27.302745503 +0200 +++ /var/tmp/diff_new_pack.d2ztT7/_new 2022-07-29 16:48:27.306745514 +0200 @@ -1,6 +1,6 @@ Format: 1.0 Source: osc -Version: 0.180.0-0 +Version: 0.181.0-0 Binary: osc Maintainer: Adrian Schroeter adrian@suse.de Architecture: any