A CLI tool to validate changeset approvals.
Project description
Approval Validator
A CLI to validate that sufficient approvals have been received for a changeset in the context of a project.
Table of Contents
Installation
The quickest way to use the tool in anger is to install it using pip
:
pip install approvals-validator
.
Alternatively, run the executable from the project root or symlink it into someplace in PATH and run it from another project root on your system.
Usage
% ./validate_approvals --help
Usage: validate_approvals REQUIRED_FLAGS
Validate that the correct approvals have been received to approve changes
to the given files.
Note: Multiple approvers and/or changed files can be passed as CSV strings.
Example:
./validate_approvals --approvers alovelace,eclarke --changed-files src/com/twitter/follow/Follow.java
Options:
-a, --approvers USERNAMES Username(s) of approvals. [required]
-c, --changed-files FILE_PATHS File paths. [required]
-h, --help Show this message and exit.
Requirements
- Python >= 3.8.0 (for
functools.cached_property
)
A .tool-versions
file is included for asdf users.
Dependencies
Running the executable (./validate_approvals
) or tests (./test
) will attempt
to install dependencies in a virtualenv at project root named ./env
.
For reference, bin/setup
usage instructions:
Usage:
./bin/setup [OPTIONS] ENV
Install dependencies for `validate_approvals` in a virtualenv at project root.
Available environments:
dev Install all dependencies
prod Install minimal dependencies for running `validate_approvals`
test Install minimal and test dependencies
Available options:
--silent Run without verbose output
Tests
Acceptances tests are written in Bash script, unit and integration tests in
Python with pytest. To run all of them and display code coverage info, issue
./test
from the project root.
% ./test
Running acceptance tests...
./validate_approvals -c data/minimal/y/file -a B
./validate_approvals -c data/minimal/y/file -a A,C
./validate_approvals -c data/minimal/y/file -a D
./validate_approvals --approvers alovelace,ghopper --changed-files data/repo/src/com/twitter/follow/Follow.java,data/repo/src/com/twitter/user/User.java
./validate_approvals --approvers alovelace --changed-files data/repo/src/com/twitter/follow/Follow.java
./validate_approvals --approvers eclarke --changed-files data/repo/src/com/twitter/follow/Follow.java
./validate_approvals --approvers alovelace,eclarke --changed-files data/repo/src/com/twitter/follow/Follow.java
./validate_approvals --approvers mfox --changed-files data/repo/src/com/twitter/tweet/Tweet.java
Running pytest tests...
Running mypy on 12 files... done with status 0
Success: no issues found in 12 source files
................................ [100%]
---------- coverage: platform darwin, python 3.8.0-final-0 -----------
Name Stmts Miss Cover
------------------------------------------------------------------------
approval_validator/__init__.py 3 0 100%
approval_validator/change_set.py 14 14 0%
approval_validator/changed_directory.py 30 0 100%
approval_validator/cli.py 14 14 0%
approval_validator/cli_utils.py 14 14 0%
approval_validator/exceptions.py 9 2 78%
approval_validator/file_utils.py 74 0 100%
approval_validator/tests/__init__.py 0 0 100%
approval_validator/tests/changed_directory_test.py 37 0 100%
approval_validator/tests/file_utils_test.py 59 4 93%
------------------------------------------------------------------------
TOTAL 254 48 81%
Design Notes
The entrypoint is approvals_validator/cli.py
, which defines the CLI.
The cli_utils
module defines how arguments are parsed.
ChangeSet
, ChangedDirectory
The main classes are ChangeSet
and ChangedDirectory
.
The former models an entire changeset (i.e., all the files passed via the
--changed-files
flag), the latter each individual entry in the list of files
passed to --changed_files
.
# approval_validator/changed_directory.py L24-37
@cached_property
def affected_directories(self) -> Tuple[Path, ...]:
return util.find_dependent_dirs(self.directory)
@cached_property
def approved(self) -> bool:
"""
Return true if sufficient approval has been received for this
ChangedDirectory.
"""
for impacted_dir in self.impacted_directories:
if not self.__change_approved(impacted_dir):
return False
return True
file_utils
File-parsing and directory-traversal logic is housed in the file_utils
module.
exceptions
Defines ApprovalValidatorError
, the base class for library-specific
exceptions, and ProjectRootNotFoundError
, which is raised when a project root
can't be found.
# approval_validator/exceptions.py L8-20
class ProjectRootNotFoundError(ApprovalValidatorError):
"""Raised when a project root can't be found."""
def __init__(self, start_dir):
self.start_dir = start_dir
def __str__(self):
message = f"""
Project root search failed. Started from: {self.start_dir}
Note: We detect the presence of a project root using the entries of
PROJECT_ROOT_FILES. (see: approval_validator/file_utils.py)
"""
return f"\n\n{cleandoc(message)}"
Performance
Caching improved running time by ~20%. The following facilities are used:
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Hashes for approvals_validator-0.0.2-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 844b6ecfdf8ce185135d19711ff4134e41ce860a3f027721921b44b4f85abd16 |
|
MD5 | 363392ba3ea3b352c21075ea6344c4e2 |
|
BLAKE2b-256 | a60ed0e793d582c99cc5793acd0a6da90a5f05e6e94c1ffd1df54a5ce59739ea |