Package: approvals-validator
Approval ValidatorA CLI to validate that sufficient approvals have been received for a changeset
in the context of a project.Table of ContentsApproval ValidatorInstallationUsageRequirementsDependenciesTestsDesign NotesChangeSet,ChangedDirectoryfile_utilsexceptionsPerformanceInstallationThe quickest way to use the tool in anger is to install it usingpip:pip install approvals-validator.Alternatively, run the executable from the project root. Install dependencies
withbin/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.RequirementsPython >= 3.8.0 (forfunctools.cached_property)A.tool-versionsfile is included forasdfusers.DependenciesclickpytestThe test-runner script (./test) will attempt to install dependencies in a
virtualenv at project root named./env.For reference,bin/setupusage 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 outputTestsA test runner script is included to run the entire test suite and display code
coverage metrics. Pass the--dockerflag to (re-)build a Docker image and
run tests with Docker.Acceptances tests are written in Bash script, unit and integration tests in
Python with pytest.% ./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 NotesThe script entrypoint is the CLI function in the executablevalidate_approvals.Theapproval_validator.cli_utilsmodule defines how arguments are parsed.ChangeSet,ChangedDirectoryThe main classes areChangeSetandChangedDirectory.The former models an entire changeset (i.e., all the files passed via the--changed-filesflag), the latter each individual entry in the list of files
passed to--changed_files.# approval_validator/changed_directory.py L24-37@cached_propertydefaffected_directories(self)->Tuple[Path,...]:returnutil.find_dependent_dirs(self.directory)@cached_propertydefapproved(self)->bool:"""Return true if sufficient approval has been received for thisChangedDirectory."""forimpacted_dirinself.impacted_directories:ifnotself.__change_approved(impacted_dir):returnFalsereturnTruefile_utilsFile-parsing and directory-traversal logic is housed in thefile_utilsmodule.exceptionsDefinesApprovalValidatorError, the base class for library-specific
exceptions, andProjectRootNotFoundError, which is raised when a project root
can't be found.# approval_validator/exceptions.py L8-20classProjectRootNotFoundError(ApprovalValidatorError):"""Raised when a project root can't be found."""def__init__(self,start_dir):self.start_dir=start_dirdef__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 ofPROJECT_ROOT_FILES. (see: approval_validator/file_utils.py)"""returnf"\n\n{cleandoc(message)}"PerformanceCaching improved running time by ~20%. The following facilities are used:functools.cached_propertyfunctools.lru_cache