Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package obs-scm-bridge for openSUSE:Factory checked in at 2025-05-26 18:34:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/obs-scm-bridge (Old) and /work/SRC/openSUSE:Factory/.obs-scm-bridge.new.2732 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "obs-scm-bridge" Mon May 26 18:34:25 2025 rev:19 rq:1279572 version:0.7.0 Changes: -------- --- /work/SRC/openSUSE:Factory/obs-scm-bridge/obs-scm-bridge.changes 2025-05-13 20:08:35.902718292 +0200 +++ /work/SRC/openSUSE:Factory/.obs-scm-bridge.new.2732/obs-scm-bridge.changes 2025-05-26 18:36:08.828964436 +0200 @@ -1,0 +2,8 @@ +Fri May 23 14:30:39 UTC 2025 - Adrian Schröter <adr...@suse.de> + +- 0.7.0 + * supporting _manifest file as successor of _subdirs + * record configured branch of submodules in package scmsync url + * stay on the configured branch of a submodule on checkout + +------------------------------------------------------------------- Old: ---- obs-scm-bridge-0.6.3.obscpio New: ---- obs-scm-bridge-0.7.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ obs-scm-bridge.spec ++++++ --- /var/tmp/diff_new_pack.sSfnvH/_old 2025-05-26 18:36:09.689000690 +0200 +++ /var/tmp/diff_new_pack.sSfnvH/_new 2025-05-26 18:36:09.693000859 +0200 @@ -22,7 +22,7 @@ %define build_pkg_name build %endif Name: obs-scm-bridge -Version: 0.6.3 +Version: 0.7.0 Release: 0 Summary: A help service to work with git repositories in OBS License: GPL-2.0-or-later ++++++ _service ++++++ --- /var/tmp/diff_new_pack.sSfnvH/_old 2025-05-26 18:36:09.721002039 +0200 +++ /var/tmp/diff_new_pack.sSfnvH/_new 2025-05-26 18:36:09.725002208 +0200 @@ -2,8 +2,8 @@ <service name="obs_scm" mode="manual"> <param name="url">https://github.com/openSUSE/obs-scm-bridge</param> <param name="scm">git</param> - <param name="revision">0.6.3</param> - <param name="version">0.6.3</param> + <param name="revision">0.7.0</param> + <param name="version">0.7.0</param> </service> <service mode="manual" name="set_version" /> ++++++ obs-scm-bridge-0.6.3.obscpio -> obs-scm-bridge-0.7.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-scm-bridge-0.6.3/README.md new/obs-scm-bridge-0.7.0/README.md --- old/obs-scm-bridge-0.6.3/README.md 2025-05-13 14:39:58.000000000 +0200 +++ new/obs-scm-bridge-0.7.0/README.md 2025-05-23 13:03:23.000000000 +0200 @@ -66,6 +66,9 @@ * noobsinfo=1 do not write a `_scmsync.obsinfo` file + * trackingbranch=BRANCH may be used to clone the branch instead of a revision. + information is taken from .gitmodules if available. + Special directives for entire projects ====================================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/obs-scm-bridge-0.6.3/obs_scm_bridge new/obs-scm-bridge-0.7.0/obs_scm_bridge --- old/obs-scm-bridge-0.6.3/obs_scm_bridge 2025-05-13 14:39:58.000000000 +0200 +++ new/obs-scm-bridge-0.7.0/obs_scm_bridge 2025-05-23 13:03:23.000000000 +0200 @@ -30,6 +30,7 @@ credentials_config = "/etc/obs/services/scm-bridge/credentials" export_debian_orig_from_git = '/usr/lib/build/export_debian_orig_from_git' pack_directories = False +follow_tracking_branch = False get_assets = False shallow_clone = True create_obsinfo = False @@ -50,6 +51,7 @@ shallow_clone = False create_obsinfo = False rewrite_url_to_ssh = True + follow_tracking_branch = True os.environ['LANG'] = "C" class ObsGit(object): @@ -60,6 +62,7 @@ self.outdir = outdir self.clonedir = None self.revision = None + self.trackingbranch = None self.subdir = None self.projectscmsync = projectscmsync self.keep_meta = False @@ -77,6 +80,8 @@ self.add_noobsinfo = False #: git submodule config, if present self.gsmconfig: Optional[configparser.ConfigParser] = None + self.gsmpath = {} + self.gsmrevisions = {} query = urllib.parse.parse_qs(self.url[4], keep_blank_values=True) if "subdir" in query: @@ -109,6 +114,10 @@ self.create_obsinfo = False del query['noobsinfo'] self.url[4] = urllib.parse.urlencode(query, doseq=True) + if 'trackingbranch' in query: + self.trackingbranch = query['trackingbranch'][0] + del query['trackingbranch'] + self.url[4] = urllib.parse.urlencode(query, doseq=True) if self.url[5]: self.revision = self.url[5] self.url[5] = '' @@ -234,12 +243,12 @@ def verify_branch(self, branch:str) -> None: if branch.startswith('-'): - self.die(f"illegel branch/commit '{branch}'") + self.die(f"illegal branch/commit '{branch}'") sys.exit(1) def verify_subdir(self, subdir:str) -> None: if subdir.startswith('-'): - self.die(f"illegel sub-directory '{subdir}'") + self.die(f"illegal sub-directory '{subdir}'") def verify_scmurl(self, scmurl:str) -> None: if testcase_mode and scmurl.startswith('file://'): @@ -296,28 +305,42 @@ self.verify_scmurl(self.scmtoolurl) if self.revision: self.verify_branch(self.revision) + if self.trackingbranch: + self.verify_branch(self.trackingbranch) if self.subdir: self.verify_subdir(self.subdir) + branch = self.revision + reset_to_commit = None if self.revision and re.match(r"^[0-9a-fA-F]{40,}$", self.revision): - self.do_clone_commit(outdir, include_submodules=include_submodules) - return + if follow_tracking_branch: + branch = self.trackingbranch + reset_to_commit = self.revision + else: + self.do_clone_commit(outdir, include_submodules=include_submodules) + return cmd = [ 'git', 'clone', self.scmtoolurl, outdir ] if include_submodules: if self.subdir: cmd += [ "--recurse-submodules=" + self.subdir ] else: cmd += [ '--recurse-submodules' ] - if self.shallow_clone: + if self.shallow_clone and not reset_to_commit: cmd += [ '--depth', '1' ] if self.subdir: - cmd += [ '--filter=blob:none', '--no-checkout' ] - if self.revision: + cmd += [ '--filter=blob:none' ] + if self.subdir or reset_to_commit: + cmd += [ '--no-checkout' ] + if branch: cmd.insert(2, '-b') - cmd.insert(3, self.revision) + cmd.insert(3, branch) env = {"GIT_LFS_SKIP_SMUDGE": "1", **os.environ} if self.no_lfs else None self.run_cmd(cmd, fatal="git clone", env=env) + if reset_to_commit: + cmd = [ 'git', '-C', outdir, 'reset', '--soft', reset_to_commit ] + self.run_cmd(cmd, fatal="git reset", env=env) if self.subdir: self.do_set_sparse_checkout(outdir) + if self.subdir or reset_to_commit: self.do_checkout(outdir, 'HEAD', include_submodules=include_submodules) # the _scmsync.obsinfo file might become obsolete again when we store entire @@ -537,17 +560,36 @@ revisions[lstree[3]] = lstree[2] return revisions - def process_package_submodule(self, gsmsection: configparser.SectionProxy, name: str, revision: str) -> None: + def process_package_submodule(self, name: str, subdir: str) -> None: if not self._REGEXP.match(name): logging.warn("submodule name contains invalid char: %s", name) return - path = gsmsection['path'] + section = self.gsmpath[subdir + name] + if not section: + logging.warn("submodule not configured for %s", subdir + name) + return + gsmsection = self.gsmconfig[section] + urlstr = gsmsection['url'] + if not urlstr: + self.die(f"url not defined for submodule {section}") + + revisions = self.gsmrevisions.get(subdir) + if not revisions: + revisions = self.gsmrevisions[subdir] = self.list_submodule_revisions(subdir) + + revision = revisions.get(gsmsection['path']) + if not revision: + self.die(f"could not determine revision of submodule for {gsmsection['path']}") # write xml file and register the module url = list(urllib.parse.urlparse(urlstr)) url[5] = revision + if 'branch' in gsmsection: + query = urllib.parse.parse_qs(url[4], keep_blank_values=True); + query['trackingbranch'] = [gsmsection['branch']] + url[4] = urllib.parse.urlencode(query, doseq=True) if self.arch: query = urllib.parse.parse_qs(url[4], keep_blank_values=True); query['arch'] = self.arch @@ -609,6 +651,22 @@ return self.write_package_xml_local_link(name, target) + def parse_gsmconfig(self): + self.gsmconfig = configparser.ConfigParser() + self.gsmconfig.read(self.clonedir + '/.gitmodules') + gsmpath = {} + for section in self.gsmconfig.sections(): + gsmconfig = self.gsmconfig[section] + if not 'path' in gsmconfig: + logging.warn("path not defined for git submodule " + section) + continue + path = gsmconfig['path'] + if path in gsmpath: + logging.warn("multiple definitions of %s path in git submodule config", path) + continue + gsmpath[path] = section + self.gsmpath = gsmpath + def generate_project_files(self) -> None: clonedir = tempfile.mkdtemp(prefix="obs-scm-bridge") self.clonedir = clonedir @@ -622,37 +680,46 @@ os.chdir(self.outdir) subdir = self.subdir + '/' if self.subdir else '' if os.path.isfile(clonedir + '/.gitmodules'): - self.gsmconfig = configparser.ConfigParser() - self.gsmconfig.read(clonedir + '/.gitmodules') + self.parse_gsmconfig() self.generate_package_xml_files_of_directory(subdir) shutil.rmtree(clonedir) self.clonedir = None - def generate_package_xml_files_of_directory_submodules(self, subdir) -> None: - revisions = None - for section in self.gsmconfig.sections(): - gsmconfig = self.gsmconfig[section] - if not 'path' in gsmconfig: - logging.warn("path not defined for git submodule " + section) - continue - name = gsmconfig['path'] - if subdir: - if not name.startswith(subdir): - continue - name = name.removeprefix(subdir) - if '/' in name: - continue - if name in self.processed: - continue # already handled - if not 'url' in gsmconfig: - self.die(f"url not defined for submodule {section}") - if not revisions: - revisions = self.list_submodule_revisions(subdir) - revision = revisions.get(gsmconfig['path'], None) - if not revision: - self.die("could not determine revision of submodule for {gsmconfig['path']}") - self.process_package_submodule(gsmconfig, name, revision) - self.processed[name] = True + def read_project_manifest(self, filename): + packages = None + subdirectories = [] + manifest_yml = None + with open(filename) as stream: + manifest_yml = yaml.safe_load(stream) + if 'packages' in manifest_yml: + packages = [] + if manifest_yml.get('packages'): + for name in manifest_yml['packages']: + if not name or name.startswith('.') or name.startswith('/'): + logging.warn("illegal packages entry '%s'", name) + continue + if '/' in name or '*' in name: # for now + logging.warn("packages entry with '/' or '*' not implemented yet") + continue + packages.append(name) + if manifest_yml.get('subdirectories'): + for newsubdir in manifest_yml['subdirectories']: + if newsubdir: + subdirectories.append(newsubdir) + return packages, subdirectories + + def read_project_subdirs(self, filename): + packages = None + subdirectories = [] + subdir_yml = None + with open(filename) as stream: + subdir_yml = yaml.safe_load(stream) + for newsubdir in subdir_yml['subdirs']: + if newsubdir: + subdirectories.append(newsubdir) + if 'toplevel' not in subdir_yml or subdir_yml['toplevel'] != 'include': + packages = [] + return packages, subdirectories def generate_package_xml_files_of_directory(self, subdir) -> None: if subdir: @@ -665,28 +732,27 @@ shutil.move(directory + '/_config', '.') self.processed['_config'] = True - if os.path.isfile(directory + '/_subdirs'): - subdir_yml = None - with open(directory + '/_subdirs') as stream: - subdir_yml = yaml.safe_load(stream) - for newsubdir in subdir_yml['subdirs']: - if not newsubdir: - continue - if (subdir + newsubdir + '/') in self.processed: - continue - self.processed[subdir + newsubdir + '/'] = True - self.generate_package_xml_files_of_directory(subdir + newsubdir + '/') - if 'toplevel' not in subdir_yml or subdir_yml['toplevel'] != 'include': - return + packages = None + subdirectories = [] - # process all submodules in this directory - if self.gsmconfig: - self.generate_package_xml_files_of_directory_submodules(subdir) + if os.path.isfile(directory + '/_manifest'): + (packages, subdirectories) = self.read_project_manifest(directory + '/_manifest') + elif os.path.isfile(directory + '/_subdirs'): + (packages, subdirectories) = self.read_project_subdirs(directory + '/_subdirs') + + # handle all subdirectories + for newsubdir in subdirectories: + if (subdir + newsubdir + '/') in self.processed: + continue + self.processed[subdir + newsubdir + '/'] = True + self.generate_package_xml_files_of_directory(subdir + newsubdir + '/') + + if packages is None: + logging.debug("walk via %s", directory) + packages = sorted(os.listdir(directory)) # handle plain files and directories - logging.debug("walk via %s", directory) - listing = sorted(os.listdir(directory)) - for name in listing: + for name in packages: if name in self.processed: continue # already handled fname = directory + '/' + name @@ -697,7 +763,7 @@ elif os.path.islink(fname): target = os.readlink(fname).rstrip('/') # this is no recursive lookup, but is there a usecase? if not target or '/' in target or target.startswith('.'): - logging.warn("only local links are supported, skipping: " + name) + logging.warn("only local links are supported, skipping: %s -> %s", name, target) continue if not os.path.isdir(directory + '/' + target): logging.debug("skipping dangling symlink %s -> %s", name, target) @@ -707,7 +773,10 @@ elif os.path.isdir(fname): if (subdir + name + '/') in self.processed: continue # already handled in _subdir loop - self.process_package_subdirectory(name, subdir) + if (subdir + name) in self.gsmpath: + self.process_package_submodule(name, subdir) + else: + self.process_package_subdirectory(name, subdir) self.processed[name] = True if __name__ == '__main__': ++++++ obs-scm-bridge.obsinfo ++++++ --- /var/tmp/diff_new_pack.sSfnvH/_old 2025-05-26 18:36:09.881008785 +0200 +++ /var/tmp/diff_new_pack.sSfnvH/_new 2025-05-26 18:36:09.889009122 +0200 @@ -1,5 +1,5 @@ name: obs-scm-bridge -version: 0.6.3 -mtime: 1747139998 -commit: f51c5818e939ae854b41f66d7cd38ddccc4a6791 +version: 0.7.0 +mtime: 1747998203 +commit: 0a33be1d0808ab7e3a358a31fd2541fa3b128cbb