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. Install dependencies
with bin/setup
.
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
The test-runner script (./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 11 files... done with status 0
Success: no issues found in 11 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_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 240 34 86%
Design Notes
The script entrypoint is the CLI function in the executable
validate_approvals
.
The approval_validator.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 Distribution
Built Distribution
Hashes for approvals_validator-0.2.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 983bf6bfea2edf91943b8765e65b0e4a56f8e75d69161c21078165e6b4a82a83 |
|
MD5 | e4f792d402604297248da370712f07c6 |
|
BLAKE2b-256 | b90dc49c70240d39b55d5175355d7d1516013dbb114dbc3927e6caaef9eb95ea |
Hashes for approvals_validator-0.2.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 556669f77b1ff845e5da595c90de1d98a429d236fb002bf89ac57bee23864719 |
|
MD5 | ea0f7a308b98873f4fd1b7e8cb2907fc |
|
BLAKE2b-256 | e0191f6287b373a0ee60a1de4144e3b3275ce957219a81eff92b954aee5bb3f8 |