Skip to content

Instantly share code, notes, and snippets.

@fmoessbauer
Last active January 10, 2025 17:09
Show Gist options
  • Select an option

  • Save fmoessbauer/3447e5eee62f874f2ceb59da384d4c4c to your computer and use it in GitHub Desktop.

Select an option

Save fmoessbauer/3447e5eee62f874f2ceb59da384d4c4c to your computer and use it in GitHub Desktop.
# SPDX-License-Identifier: MIT
# SPDX-Copyright-Notice: Copyright 2024 Siemens AG
#
# Author: Felix Moessbauer <felix.moessbauer@siemens.com>
#
# Convert a repo manifest XML file to a kas YAML file
# Example:
# repoManifest2kasYml.py default.xml > kas.yml
# kas checkout kas.yml
import xml.etree.ElementTree as ET
import yaml
import sys
import re
# repo manifest spec
# https://gerrit.googlesource.com/git-repo/+/master/docs/manifest-format.md#Element-default
# TODO: this requires a kas schema addition
LINKFILES_SUPPRTED = False
def get_defaults(el):
defaults = {
'remote': None,
'revision': None,
'upstream': None
}
if len(el) > 1:
raise ValueError('Too many default elements')
for child in el:
if child.tag == 'default':
defaults['remote'] = child.attrib.get('remote')
defaults['revision'] = child.attrib.get('revision')
defaults['upstream'] = child.attrib.get('upstream')
return defaults
def get_remotes(el):
remotes = {}
for child in el:
if child.tag == 'remote':
remotes[child.attrib.get('name')] = {
'fetch': child.attrib.get('fetch'),
'revision': child.attrib.get('revision')
}
return remotes
def get_projects(el):
projects = {}
for child in el:
if child.tag == 'project':
name = child.attrib.get('name')
projects[name] = {
'path': child.attrib.get('path'),
'remote': child.attrib.get('remote'),
'revision': child.attrib.get('revision'),
'upstream': child.attrib.get('upstream'),
}
for po in child:
if po.tag == 'linkfile':
if 'linkfiles' not in projects[name]:
projects[name]['linkfiles'] = []
projects[name]['linkfiles'].append(
{'src': po.attrib.get('src'),
'dest': po.attrib.get('dest')})
continue
print(f'warning: {po.tag} on "{name}" ignored',
file=sys.stderr)
return projects
def _triage_revision(rev, upstream):
if rev and re.match(r'^[0-9a-f]{40}|[0-9a-f]{64}$', rev):
return (rev, None, upstream)
if rev and rev.startswith('refs/tags/'):
return (None, rev[11:], upstream)
return (None, None, rev)
def get_kas_defaults(defaults):
_, tag, branch = _triage_revision(defaults.get('revision'),
defaults.get('upstream'))
defaults = {}
if tag:
defaults['repos'] = {'tag': tag}
if branch:
defaults['repos'] = {'branch': branch}
return defaults
def get_kas_repos(defaults, remotes, projects):
repos = {}
for name, project in projects.items():
remote = project.get('remote') or defaults.get('remote')
if not remote:
raise ValueError(f'No remote specified for project {name}')
remote_url = remotes[remote]['fetch']
# only branches can be defaulted, but we don't know if the revision
# is a branch or a commit (yet)
revision = project.get('revision') or defaults.get('revision')
upstream = project.get('upstream')
if not revision:
raise ValueError(f'No revision specified for project {name}')
commit, tag, branch = _triage_revision(revision, upstream)
repos[name] = {
'url': f'{remote_url}/{name}',
}
path = project.get('path')
if path:
repos[name]['path'] = path
if commit:
repos[name]['commit'] = commit
repos[name]['branch'] = None
repos[name]['tag'] = None
if tag:
repos[name]['tag'] = tag
repos[name]['branch'] = None
if branch:
repos[name]['branch'] = branch
repos[name]['tag'] = None
# disable importing layers
repos[name]['layers'] = {'.': False}
if 'linkfiles' in project and LINKFILES_SUPPRTED:
repos[name]['linkfiles'] = project['linkfiles']
return repos
def parseManifestXML(xmlfile):
'''
Parse a repo manifest XML file and return a dictionary of the manifest
with all relevant information.
'''
tree = ET.parse(xmlfile)
root = tree.getroot()
default = root.findall('default')
remotes = root.findall('remote')
projects = root.findall('project')
# TODO: include manifest support
manifest = {
'defaults': get_defaults(default),
'remotes': get_remotes(remotes),
'projects': get_projects(projects)
}
return manifest
def get_kas_representation(manifest):
defaults = manifest['defaults']
remotes = manifest['remotes']
projects = manifest['projects']
spec = {
'header': {'version': 18},
'defaults': get_kas_defaults(defaults),
'repos': get_kas_repos(defaults, remotes, projects)
}
return spec
if __name__ == '__main__':
if (len(sys.argv) != 2):
print('Usage: ' + sys.argv[0] + ' infile')
sys.exit(0)
infile = sys.argv[1]
manifest = parseManifestXML(infile)
kas = get_kas_representation(manifest)
yaml.dump(kas, sys.stdout, sort_keys=False)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment