Skip to content

Commit

Permalink
Test workflows (#16)
Browse files Browse the repository at this point in the history
* src/entry_points/diff_fixtures: Create base CLI and functions for fixture diffing.

* src/entry_points/diff_fixtures: Create fixture json hash tree function.

* src/entry_points/diff_fixtures|workflows: Update main workflow to create fixture json diff.
  • Loading branch information
spencer-tb authored Feb 14, 2024
1 parent 04f9b1b commit 9b99fe4
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 3 deletions.
14 changes: 11 additions & 3 deletions .github/workflows/fixtures.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,16 @@ jobs:
wget -O $GITHUB_WORKSPACE/bin/solc https://binaries.soliditylang.org/${PLATFORM}/$RELEASE_NAME
chmod a+x $GITHUB_WORKSPACE/bin/solc
echo $GITHUB_WORKSPACE/bin >> $GITHUB_PATH
- name: Run fixtures fill
- name: Run fixtures fill and create fixtures tree hash file
shell: bash
run: |
pip install --upgrade pip
python -m venv env
source env/bin/activate
pip install -e .
fill ${{ matrix.fill-params }}
dfx --generate-fixtures-tree-only
mv fixtures_tree.json ${{ matrix.name }}_hash_tree.json
- name: Create fixtures info file
shell: bash
run: |
Expand All @@ -63,8 +65,10 @@ jobs:
tar -czvf ${{ matrix.name }}.tar.gz ./fixtures
- uses: actions/upload-artifact@v3
with:
name: ${{ matrix.name }}
path: ${{ matrix.name }}.tar.gz
name: ${{ matrix.name }}-artifacts
path: |
${{ matrix.name }}.tar.gz
${{ matrix.name }}_hash_tree.json
release:
runs-on: ubuntu-latest
needs: build
Expand All @@ -74,6 +78,10 @@ jobs:
uses: actions/download-artifact@v3
with:
path: .
- name: Remove fixtures hash tree files from artifacts
shell: bash
run: |
rm *_hash_tree.json
- name: Draft Release
uses: softprops/action-gh-release@v1
with:
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ evm_transition_tool =
console_scripts =
fill = entry_points.fill:main
tf = entry_points.tf:main
dfx = entry_points.diff_fixtures:main
order_fixtures = entry_points.order_fixtures:main
pyspelling_soft_fail = entry_points.pyspelling_soft_fail:main
markdownlintcli2_soft_fail = entry_points.markdownlintcli2_soft_fail:main
Expand Down
149 changes: 149 additions & 0 deletions src/entry_points/diff_fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""
Functions and CLI interface for identifying differences in fixture content based on SHA256 hashes.
Features include generating SHA256 hash maps for fixture files, excluding the '_info' key for
consistency, and calculating a cumulative hash across all files for quick detection of any changes.
The CLI interface allows users to detect changes locally during development.
Example CLI Usage:
```
python diff_fixtures.py --input ./fixtures --develop
# or via CLI entry point after package installation
dfx --input ./fixtures
```
CI/CD utilizes the functions to create a json fixture hash map file for the main branch during the
fixture artifact build process, and within the PR branch that a user is developing on. These are
then compared within the PR workflow during each commit to flag any changes in the fixture files.
"""

import argparse
import hashlib
import json
from pathlib import Path
from typing import Dict, List, Tuple


def compute_fixture_hash(fixture_path: Path) -> str:
"""
Generates a sha256 hash of a fixture json files.
The hash for each fixture is calculated without the `_info` key.
"""
with open(fixture_path, "r") as file:
data = json.load(file)
data.pop("_info", None)
return hashlib.sha256(json.dumps(data, sort_keys=True).encode()).hexdigest()


def compute_cumulative_hash(hashes: List[str]) -> str:
"""
Creates cumulative sha256 hash from a list of hashes.
"""
return hashlib.sha256("".join(sorted(hashes)).encode()).hexdigest()


def generate_fixture_tree_json(fixtures_directory: Path, output_file: str, parent_path="") -> None:
"""
Generates a JSON file containing a tree structure of the fixtures directory calculating
cumulative hashes at each folder and file. The tree structure is a nested dictionary used to
compare fixture differences, using the cumulative hash as a quick comparison metric.
"""

def build_tree(directory: Path, parent_path) -> Tuple[Dict, List[str]]:
"""
Recursively builds a tree structure for fixture directories and files,
calculating cumulative hashes at each sub tree.
"""
directory_contents = {}
all_hashes = []

for item in directory.iterdir():
relative_path = f"{parent_path}/{item.name}" if parent_path else item.name

if item.is_dir():
sub_tree, sub_tree_hashes = build_tree(item, relative_path)
directory_contents[item.name] = [
{
"path": relative_path,
"hash": compute_cumulative_hash(sub_tree_hashes),
"contents": sub_tree,
}
]
all_hashes.extend(sub_tree_hashes)
elif item.suffix == ".json":
file_hash = compute_fixture_hash(item)
directory_contents[item.name] = [
{
"path": relative_path,
"hash": file_hash,
}
]
all_hashes.append(file_hash)
return directory_contents, all_hashes

tree_contents, tree_hashes = build_tree(fixtures_directory, parent_path)
fixtures_tree = {
"fixtures": {
"cumulative_hash": compute_cumulative_hash(tree_hashes),
"contents": tree_contents,
}
}
with open(output_file, "w") as file:
json.dump(fixtures_tree, file, indent=4)


def main():
"""
CLI interface for comparing fixture differences between the input directory and the main
branch fixture artifacts.
"""
parser = argparse.ArgumentParser(
description=(
"Determines if a diff exists between an input fixtures directory and the most recent "
"built fixtures from the main branch git workflow. "
"Does not provide detailed file changes. "
"If no input directory is provided, `./fixtures` is used as the default. "
"Compares only the non-development fixtures by default."
)
)
parser.add_argument(
"--input",
type=str,
default="./fixtures",
help="Input path for the fixtures directory",
)
parser.add_argument(
"--develop",
action="store_true",
default=False,
help="Compares all fixtures including the development fixtures.",
)
parser.add_argument(
"--generate-fixtures-tree-only",
action="store_true",
default=False,
help="Generates a fixture tree json file without comparing the input fixtures.",

)
parser.add_argument( # To be implemented
"--commit",
type=str,
default=None,
help="The commit hash to compare the input fixtures against.",
)
args = parser.parse_args()

input_path = Path(args.input)
if not input_path.exists() or not input_path.is_dir():
raise FileNotFoundError(
f"Error: The input or default fixtures directory does not exist: {args.input}"
)

if args.generate_fixtures_tree_only:
generate_fixture_tree_json(fixtures_directory=input_path, output_file="fixtures_tree.json")
return


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ delitem
Dencun
dev
devnet
dfx
difficulty
dir
dirname
Expand Down Expand Up @@ -195,6 +196,7 @@ pdb
petersburg
png
Pomerantz
posix
ppa
ppas
pre
Expand Down

0 comments on commit 9b99fe4

Please sign in to comment.