Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support platform requirements on gzdev projects #83

Merged
merged 4 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,43 @@ stages.

`gzdev repository enable --project=ignition-math6`

## The repository.yaml configuration file

The repository module uses a [yaml configuration](plugins/config/repository.yaml) file to specify
the following information used by the code.

* {} squares used to refer to keys that are variable names. Otherwise literals are expected as keys.
* All fields are mandatory unless noted otherwise.

### repositories metadata

* `repositories` with the following format:
* `name:` short name used in the command line or used as an id of the repository
* `key:` fingerprint of the signing key of the repository. Used for verification proposes.
* `key_url:` signing key file (binary format) of the repository to install into the system
* `linux_distro:` name of the Linux distribution for this configuration
* `types:` flavours or types inside the Linux distribution to apply
* `name:` id to name the type inside the Linux distribution (i.e stable)
* `url:` repository url for the Linux distribution type

### projects metadata

The `--project` argument allows gzdev to configure group of the repositories to use and/or
add some extra requirements.

**Note:** project entries in the yaml are processed by order. The first one matching the
project name and other requirements is the only one executed.

* `projects` with the following format:
* `name:` short name used as an id for the project
* `repositories:` list of repositories to configure into the system
* `name:` name of the repository to install as named inside `repositories:` section
* `type:` name of the type of repository to install matching the `repositories:` section
* `requirements:` [optional] section to list requirements to met for repositories installations
* `distributions:` [optional] subsection for requirements on the platform being run
* `{distribution_name}:` distribution name (i.e ubuntu) as in `distro.id()` module call
* `{version_name}:` release name inside the distribution (i.e jammy) as in `distro.codename()` module call

# Support/Contribute
* [GitHub Issue Tracker](https://github.com/gazebo-tooling/gzdev/issues) - gzdev specific questions
* [Gazebo Answers](http://answers.gazebosim.org) - Gazebo specific questions
Expand Down
14 changes: 12 additions & 2 deletions plugins/config/_test_repository.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,21 @@ projects:
- name: osrf
type: stable
- name: ignition-transport7
requirements:
distributions:
ubuntu:
- no-existing-distro
repositories:
- name: osrf
type: stable
type: broken
- name: ignition-transport7
requirements:
distributions:
ubuntu:
- jammy
repositories:
- name: osrf
type: prerelease
type: stable
- name: ignition-.*
repositories:
- name: osrf
Expand Down
56 changes: 44 additions & 12 deletions plugins/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,33 @@ def load_config_file(config_file_path='config/repository.yaml'):
exit(-1)


def load_project(project, config):
ret = []
def get_first_valid_project_config(project, config, linux_distro):
"""Returns the project configuration from yaml that correspond
to the first match while searching starting from top to bottom
"""
for p in config['projects']:
pattern = re.compile(p['name'])

if pattern.search(project):
ret = p['repositories']
break
# project name found, check that requirements are met
try:
requirements = p['requirements']
if linux_distro in requirements['distributions'][distro.id()]:
return p
except KeyError as kerror:
# 0. No requirments set
if 'requirements' in str(kerror):
return p
# 1. No disitribution requirement set
if 'distributions' in str(kerror):
return p
assert f'Unexpected keyerror: #{str(kerror)}'

if not ret:
error('Unknown project: ' + project)
return None

return ret

def get_repositories_config(project_config):
return project_config['repositories']


def get_linux_distro():
Expand Down Expand Up @@ -159,8 +174,8 @@ def run_apt_update():
_check_call(['apt-get', 'update'])


def install_repos(project_list, config, linux_distro, gpg_check):
for p in project_list:
def install_repos(repos_list, config, linux_distro, gpg_check):
for p in repos_list:
install_repo(p['name'], p['type'], config, linux_distro, gpg_check)


Expand Down Expand Up @@ -207,7 +222,7 @@ def normalize_args(args):
if force_linux_distro:
linux_distro = force_linux_distro
else:
linux_distro = None
linux_distro = distro.codename()
if '--keyserver' in args:
warn('--keyserver option is deprecated. It is safe to remove it')
return action, repo_name, repo_type, project, linux_distro, gpg_check
Expand All @@ -220,14 +235,31 @@ def validate_input(args):
error('Unknown action: ' + args.action)


def process_project_install(project, config, linux_distro, gpg_check,
dry_run=False):
project_config = get_first_valid_project_config(project, config, linux_distro)
if not project_config:
error('Unknown project: ' + project)

if not dry_run: # useful for tests
install_repos(get_repositories_config(project_config),
config,
linux_distro,
gpg_check)


def process_input(args, config):
action, repo_name, repo_type, project, linux_distro, gpg_check = args

if (action == 'enable'):
if project:
project_list = load_project(project, config)
install_repos(project_list, config, linux_distro, gpg_check)
# project dependant installation
process_project_install(project,
config,
linux_distro,
gpg_check)
else:
# generic repository installation
install_repo(repo_name,
repo_type,
config,
Expand Down
54 changes: 48 additions & 6 deletions plugins/repository_TEST.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,63 @@ def test_no_type(self):
class TestProjectNameResolution(TestBase):

def test_direct_match(self):
projects = repository.load_project('ignition-math6', self.config)
for p in projects:
project_config = \
repository.get_first_valid_project_config('ignition-math6',
self.config,
'jammy')
for p in repository.get_repositories_config(project_config):
self.assertEqual(p['name'], 'osrf')
self.assertEqual(p['type'], 'stable')

def test_non_exist(self):
with self.assertRaises(SystemExit):
repository.load_project('fooooo', self.config)
self.assertIsNone(
repository.get_first_valid_project_config('fooooo',
self.config,
'jammy'))

def test_regexp(self):
projects = repository.load_project('ignition-plugin', self.config)
for p in projects:
project_config = \
repository.get_first_valid_project_config('ignition-plugin',
self.config,
'jammy')
for p in repository.get_repositories_config(project_config):
self.assertEqual(p['name'], 'osrf')
self.assertEqual(p['type'], 'regexp')

def test_precedence(self):
project_config = \
repository.get_first_valid_project_config('ignition-transport7',
self.config,
'jammy')
for p in repository.get_repositories_config(project_config):
self.assertEqual(p['name'], 'osrf')
self.assertEqual(p['type'], 'stable')


class TestProjectInstall(TestBase):

def test_no_distributions(self):
repository.process_project_install('ignition-math6',
self.config,
'jammy',
gpg_check=False,
dry_run=True)

def test_distributions(self):
repository.process_project_install('ignition-transport7',
self.config,
'jammy',
gpg_check=False,
dry_run=True)

def test_non_exist(self):
with self.assertRaises(SystemExit):
repository.process_project_install('fooooo',
self.config,
'jammy',
gpg_check=False,
dry_run=True)


if __name__ == '__main__':
unittest.main()
Loading