Skip to content

Commit

Permalink
validate-tags: allow ignoring certain collections (#491)
Browse files Browse the repository at this point in the history
* validate-tags: allow ignoring certain collections

This adds a `-I` / `--ignore` flag to `antsibull-build validate-tags`
and `antsibull-build validate-tags-file` to ignore errors for specific
collections. `--ignores-file` can be specified separately or
simultaneously to ignore collections from a file with a newline
separated list of names.

Useless ignores are treated as errors by default, but this can be
changed with `--no-warn-useless-ignores`.

`ignores` and `warn_useless_ignores` arguments were added to
`antsibull.tagging.validate_tags()` to support this functionality.

* correct changelog frag syntax and rm invalid entry

Co-authored-by: Felix Fontein <felix@fontein.de>

* fix argparse help message grammar

Co-authored-by: Felix Fontein <felix@fontein.de>

* s/warn_useless_ignores/error_on_useless_ignores/

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
  • Loading branch information
gotmax23 and felixfontein committed Mar 26, 2023
1 parent 768a2e8 commit 015f935
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 12 deletions.
6 changes: 6 additions & 0 deletions changelogs/fragments/491.yaml
@@ -0,0 +1,6 @@
---
minor_changes:
- Add a ``-I`` / ``--ignore`` and a ``--ignores-file`` flag to the
``antsibull-build validate-tags`` and ``antsibull-build validate-tags-file``
subcommands to ignore errors for certain collections
(https://github.com/ansible-community/antsibull/pull/491).
26 changes: 25 additions & 1 deletion src/antsibull/cli/antsibull_build.py
Expand Up @@ -25,6 +25,10 @@
InvalidArgumentError, get_toplevel_parser, normalize_toplevel_options
)
from antsibull_core.config import ConfigError, load_config # noqa: E402
from antsibull_core.vendored._argparse_booleanoptionalaction import ( # noqa: E402
BooleanOptionalAction
)


from ..build_collection import build_collection_command # noqa: E402
from ..build_ansible_commands import ( # noqa: E402
Expand Down Expand Up @@ -370,10 +374,29 @@ def parse_args(program_name: str, args: list[str]) -> argparse.Namespace:
validate_deps.add_argument('collection_root',
help='Path to a ansible_collections directory containing a'
' collection tree to check.')
validate_tags_shared = argparse.ArgumentParser(add_help=False)
validate_tags_shared.add_argument(
'-I', '--ignore',
action='append', help='Ignore these collections when reporting errors.'
)
validate_tags_shared.add_argument(
'--ignores-file',
help='Path to a file with newline separated list of collections to ignore',
type=argparse.FileType('r'),
)
validate_tags_shared.add_argument(
'-E', '--error-on-useless-ignores',
action=BooleanOptionalAction,
dest='error_on_useless_ignores',
default=True,
help='By default, useless ignores (e.g. passing'
' `--ignore collection.collection` when that collection is'
' properly tagged) will be considered an error.'
)

validate_tags = subparsers.add_parser(
'validate-tags',
parents=[build_parser],
parents=[build_parser, validate_tags_shared],
description="Ensure that collection versions in an Ansible release are tagged"
" in collections' respective git repositories."
)
Expand All @@ -393,6 +416,7 @@ def parse_args(program_name: str, args: list[str]) -> argparse.Namespace:

validate_tags_file = subparsers.add_parser(
'validate-tags-file',
parents=[validate_tags_shared],
description="Ensure that collection versions in an Ansible release are tagged"
" in collections' respective git repositories."
" This validates the tags file generated by"
Expand Down
63 changes: 52 additions & 11 deletions src/antsibull/tagging.py
Expand Up @@ -13,7 +13,8 @@
import os
import re
import sys
from collections.abc import AsyncGenerator
from collections.abc import AsyncGenerator, Collection
from typing import TextIO

import asyncio_pool # type: ignore[import]

Expand All @@ -37,8 +38,13 @@ def validate_tags_file_command() -> int:
generated by the 'validate-tags' subcommand.
"""
app_ctx = app_context.app_ctx.get()
ignores = _get_ignores(app_ctx.extra['ignore'], app_ctx.extra['ignores_file'])
tag_data = load_yaml_file(app_ctx.extra['tags_file'])
return _print_validation_errors(tag_data)
return _print_validation_errors(
tag_data,
ignores,
app_ctx.extra['error_on_useless_ignores'],
)


def validate_tags_command() -> int:
Expand All @@ -50,53 +56,88 @@ def validate_tags_command() -> int:
and then verifies the data.
"""
app_ctx = app_context.app_ctx.get()
ignores = _get_ignores(app_ctx.extra['ignore'], app_ctx.extra['ignores_file'])
tag_data = asyncio.run(
get_collections_tags(
app_ctx.extra['data_dir'], app_ctx.extra['deps_file']
)
)
if app_ctx.extra['output']:
store_yaml_file(app_ctx.extra['output'], tag_data)
return _print_validation_errors(tag_data)
return _print_validation_errors(
tag_data,
ignores,
app_ctx.extra['error_on_useless_ignores'],
)


def _print_validation_errors(
tag_data: dict[str, dict[str, str | None]]
tag_data: dict[str, dict[str, str | None]],
ignores: Collection[str] = (),
error_on_useless_ignores: bool = True,
) -> int:
"""
This takes the tag_data and prints any validation errors to stderr.
"""
errors = validate_tags(tag_data)
errors = validate_tags(tag_data, ignores, error_on_useless_ignores)
if not errors:
return 0
for error in errors:
print(error, file=sys.stderr)
return 1


def _get_ignores(
ignores: Collection[str], ignore_fp: TextIO | None
) -> set[str]:
ignores = set(ignores)
if ignore_fp:
ignores.update(
line.strip() for line in ignore_fp if not line.startswith('#')
)
return ignores


##############
# Library code
##############


def validate_tags(
tag_data: dict[str, dict[str, str | None]]
tag_data: dict[str, dict[str, str | None]],
ignores: Collection[str] = (),
error_on_useless_ignores: bool = True,
) -> list[str]:
"""
Validate that each collection in tag_data has a repository and a tag
associated with it. Return a list of validation errors.
:param tag_data: A tag data dictionary as returned by `get_collections_tags`
:param ignores: A list of collection names for which to ignore errors
:param error_on_useless_ignores: Whether to error for useless ignores
"""
errors = []
ignore_set = set(ignores)
for name, data in tag_data.items():
if not data['repository']:
version = data['version']
if name in ignore_set:
ignore_set.remove(name)
if data['repository'] and data['tag'] and error_on_useless_ignores:
errors.append(
f'useless ignore {name!r}: {name} {version} is properly tagged'
)
elif not data['repository']:
errors.append(
f"{name}'s repository is not specified at all in collection-meta.yaml"
)
continue
if not data['tag']:
elif not data['tag']:
errors.append(
f'{name} {version} is not tagged in {data["repository"]}'
)
if ignore_set and error_on_useless_ignores:
for name in ignore_set:
errors.append(
f"{name} {data['version']} is not tagged in "
f"{data['repository']}"
f'invalid ignore {name!r}: {name} does not match any collection'
)
return errors

Expand Down

0 comments on commit 015f935

Please sign in to comment.