CLI: Add support for listing repository types

Similar to listing sites for a repository, this adds functionality
for listing available types.

Change-Id: I9e6399b56f0986003b61f1db0839ed21c6237cec
This commit is contained in:
Felipe Monteiro 2018-10-03 15:51:21 -05:00
parent 57a6c6a84e
commit ab3228f2ed
5 changed files with 158 additions and 22 deletions

View File

@ -29,7 +29,7 @@ CONTEXT_SETTINGS = {
'help_option_names': ['-h', '--help'],
}
REPOSITORY_OPTION = click.option(
MAIN_REPOSITORY_OPTION = click.option(
'-r',
'--site-repository',
'site_repository',
@ -49,6 +49,22 @@ EXTRA_REPOSITORY_OPTION = click.option(
'site-definition for the site will be leveraged but can be overridden '
'using -e global=/opt/global@revision.')
REPOSITORY_KEY_OPTION = click.option(
'-k',
'--repo-key',
'repo_key',
help='The SSH public key to use when cloning remote authenticated '
'repositories.')
REPOSITORY_USERNAME_OPTION = click.option(
'-u',
'--repo-username',
'repo_username',
help=
'The SSH username to use when cloning remote authenticated repositories '
'specified in the site-definition file. Any occurrences of REPO_USERNAME '
'will be replaced with this value.')
ALLOW_MISSING_SUBSTITUTIONS_OPTION = click.option(
'-f',
'--fail-on-missing-sub-src',
@ -99,8 +115,12 @@ def main(*, verbose):
@main.group(help='Commands related to repositories')
@REPOSITORY_OPTION
def repo(*, site_repository):
@MAIN_REPOSITORY_OPTION
# TODO(felipemonteiro): Support EXTRA_REPOSITORY_OPTION as well to be
# able to lint multiple repos together.
@REPOSITORY_USERNAME_OPTION
@REPOSITORY_KEY_OPTION
def repo(*, site_repository, repo_key, repo_username):
"""Group for repo-level actions, which include:
* lint: lint all sites across the repository
@ -108,6 +128,8 @@ def repo(*, site_repository):
"""
config.set_site_repo(site_repository)
config.set_repo_key(repo_key)
config.set_repo_username(repo_username)
def _lint_helper(*,
@ -145,22 +167,10 @@ def lint_repo(*, fail_on_missing_sub_src, exclude_lint, warn_lint):
@main.group(help='Commands related to sites')
@REPOSITORY_OPTION
@MAIN_REPOSITORY_OPTION
@EXTRA_REPOSITORY_OPTION
@click.option(
'-k',
'--repo-key',
'repo_key',
help='The SSH public key to use when cloning remote authenticated '
'repositories.')
@click.option(
'-u',
'--repo-username',
'repo_username',
help=
'The SSH username to use when cloning remote authenticated repositories '
'specified in the site-definition file. Any occurrences of REPO_USERNAME '
'will be replaced with this value.')
@REPOSITORY_USERNAME_OPTION
@REPOSITORY_KEY_OPTION
def site(*, site_repository, extra_repositories, repo_key, repo_username):
"""Group for site-level actions, which include:
@ -238,7 +248,7 @@ def collect(*, save_location, validate, exclude_lint, warn_lint, site_name):
'output_stream',
type=click.File(mode='w'),
default=sys.stdout,
help='Where to output')
help='Where to output. Defaults to sys.stdout.')
def list_(*, output_stream):
engine.repository.process_site_repository(update_config=True)
engine.site.list_(output_stream)
@ -251,7 +261,7 @@ def list_(*, output_stream):
'output_stream',
type=click.File(mode='w'),
default=sys.stdout,
help='Where to output')
help='Where to output. Defaults to sys.stdout.')
@click.argument('site_name')
def show(*, output_stream, site_name):
engine.repository.process_repositories(site_name)
@ -265,7 +275,7 @@ def show(*, output_stream, site_name):
'output_stream',
type=click.File(mode='w'),
default=sys.stdout,
help='Where to output')
help='Where to output. Defaults to sys.stdout.')
@click.argument('site_name')
def render(*, output_stream, site_name):
engine.repository.process_repositories(site_name)
@ -287,3 +297,34 @@ def lint_site(*, fail_on_missing_sub_src, exclude_lint, warn_lint, site_name):
fail_on_missing_sub_src=fail_on_missing_sub_src,
exclude_lint=exclude_lint,
warn_lint=warn_lint)
@main.group(help='Commands related to types')
@MAIN_REPOSITORY_OPTION
@EXTRA_REPOSITORY_OPTION
@REPOSITORY_USERNAME_OPTION
@REPOSITORY_KEY_OPTION
def type(*, site_repository, extra_repositories, repo_key, repo_username):
"""Group for repo-level actions, which include:
* list: list all types across the repository
"""
config.set_site_repo(site_repository)
config.set_extra_repo_store(extra_repositories or [])
config.set_repo_key(repo_key)
config.set_repo_username(repo_username)
@type.command('list', help='List known types')
@click.option(
'-o',
'--output',
'output_stream',
type=click.File(mode='w'),
default=sys.stdout,
help='Where to output. Defaults to sys.stdout.')
def list_types(*, output_stream):
"""List type names for a given repository."""
engine.repository.process_site_repository(update_config=True)
engine.type.list_types(output_stream)

View File

@ -19,7 +19,8 @@ except NameError:
GLOBAL_CONTEXT = {
'site_repo': './',
'extra_repos': [],
'site_path': 'site'
'site_path': 'site',
'type_path': 'type'
}
@ -85,3 +86,12 @@ def get_rel_site_path():
def set_rel_site_path(p):
p = p or 'site'
GLOBAL_CONTEXT['site_path'] = p
def get_rel_type_path():
return GLOBAL_CONTEXT.get('type_path', 'type')
def set_rel_type_path(p):
p = p or 'type'
GLOBAL_CONTEXT['type_path'] = p

34
pegleg/engine/type.py Normal file
View File

@ -0,0 +1,34 @@
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import csv
import logging
from pegleg.engine import util
__all__ = ('list_types', )
LOG = logging.getLogger(__name__)
def list_types(output_stream):
"""List type names for a given repository."""
# TODO(felipemonteiro): This should output a formatted table, not rows of
# data without delimited columns.
fieldnames = ['type_name']
writer = csv.DictWriter(
output_stream, fieldnames=fieldnames, delimiter=' ')
for type_name in util.files.list_types():
writer.writerow({'type_name': type_name})

View File

@ -180,6 +180,18 @@ def list_sites(primary_repo_base=None):
yield path
def list_types(primary_repo_base=None):
"""Get a list of type directories in the primary repo."""
if not primary_repo_base:
primary_repo_base = config.get_site_repo()
full_type_path = os.path.join(primary_repo_base,
config.get_rel_type_path())
for path in os.listdir(full_type_path):
joined_path = os.path.join(full_type_path, path)
if os.path.isdir(joined_path):
yield path
def directory_for(*, path):
for r in config.all_repos():
if path.startswith(r):

View File

@ -244,3 +244,42 @@ class TestRepoCliActions(BaseCLIActionTest):
# A successful result (while setting lint checks to exclude) should
# output nothing.
assert not result.output
class TestTypeCliActions(BaseCLIActionTest):
"""Tests type-level CLI actions."""
def test_list_types_using_remote_repo_url(self):
"""Validates list types action using remote repo URL."""
# Scenario:
#
# 1) List types (should clone repo automatically)
repo_url = 'https://github.com/openstack/%s@%s' % (self.repo_name,
self.repo_rev)
# NOTE(felipemonteiro): Pegleg currently doesn't dump a table to stdout
# for this CLI call so mock out the csv DictWriter to determine output.
with mock.patch('pegleg.engine.type.csv.DictWriter') as mock_writer:
result = self.runner.invoke(cli.type, ['-r', repo_url, 'list'])
assert result.exit_code == 0
m_writer = mock_writer.return_value
m_writer.writerow.assert_any_call({'type_name': 'foundry'})
def test_list_types_using_local_repo_path(self):
"""Validates list types action using local repo path."""
# Scenario:
#
# 1) List types for local repo path
repo_path = self.treasuremap_path
# NOTE(felipemonteiro): Pegleg currently doesn't dump a table to stdout
# for this CLI call so mock out the csv DictWriter to determine output.
with mock.patch('pegleg.engine.type.csv.DictWriter') as mock_writer:
result = self.runner.invoke(cli.type, ['-r', repo_path, 'list'])
assert result.exit_code == 0
m_writer = mock_writer.return_value
m_writer.writerow.assert_any_call({'type_name': 'foundry'})