Compare commits
70 Commits
dev
...
screenshot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9588895282 | ||
|
|
6bc1ddbac6 | ||
|
|
4201c70874 | ||
|
|
6be95ea336 | ||
|
|
c6c22a176d | ||
|
|
c98e16d5df | ||
|
|
2638c437ba | ||
|
|
beb8981454 | ||
|
|
9f4605d876 | ||
|
|
5a076ae854 | ||
|
|
7362485714 | ||
|
|
b4e8074916 | ||
|
|
ec39d4cc6c | ||
|
|
9a6b1c1fdc | ||
|
|
8856345353 | ||
|
|
cc8cce41a4 | ||
|
|
84054bd54c | ||
|
|
95c08ee850 | ||
|
|
2532e2fe45 | ||
|
|
6eae46c6de | ||
|
|
5ea63c3403 | ||
|
|
1da10668b5 | ||
|
|
5b4e13637d | ||
|
|
f97bb3cb4f | ||
|
|
dfcc21a506 | ||
|
|
735d85c892 | ||
|
|
4d6ef64f8a | ||
|
|
e3c03974d5 | ||
|
|
7c11b5a47a | ||
|
|
f9238e1934 | ||
|
|
1298dc1ddd | ||
|
|
c3f876e8dd | ||
|
|
ee679a8019 | ||
|
|
e3a0215869 | ||
|
|
f913825ba6 | ||
|
|
34cd9c832a | ||
|
|
7af1edfd42 | ||
|
|
8c053cb76c | ||
|
|
2230aa4e75 | ||
|
|
1b1ff87dff | ||
|
|
4a9861f129 | ||
|
|
3a0603c154 | ||
|
|
7f3f649247 | ||
|
|
d2fb00d580 | ||
|
|
09482030da | ||
|
|
cf949d2af6 | ||
|
|
6adc17b962 | ||
|
|
febcb16b06 | ||
|
|
f4bcdbcfaf | ||
|
|
32e74d037c | ||
|
|
ffc5cca16e | ||
|
|
2e8bb1d00c | ||
|
|
d1546d0ee3 | ||
|
|
78e59f1ff4 | ||
|
|
990197dc87 | ||
|
|
0a1f06a587 | ||
|
|
d1dd257fd4 | ||
|
|
58304ea073 | ||
|
|
a762233a8c | ||
|
|
ef18dd8e01 | ||
|
|
48d070723d | ||
|
|
09a6d27107 | ||
|
|
6f74857578 | ||
|
|
d5035fe13e | ||
|
|
462bd34299 | ||
|
|
554278c8ec | ||
|
|
bf3611a937 | ||
|
|
0ec6f42d14 | ||
|
|
983f3fdde6 | ||
|
|
2e1f94e6ec |
137
.github/diff_report/diff_screenshots.py
vendored
Normal file
137
.github/diff_report/diff_screenshots.py
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
"""
|
||||
Utility to compare screenshots before and after a change and generate a report of the
|
||||
differences.
|
||||
|
||||
Expected usage in a GitHub Actions workflow; compare `dev` with the `$BRANCH_NAME` in the
|
||||
associated PR that triggered the CI run:
|
||||
python src/seedsigner/resources/seedsigner-translations/.github/diff_report/diff_screenshots.py ./artifacts/dev ./artifacts/$BRANCH_NAME ./artifacts/diff
|
||||
"""
|
||||
import argparse
|
||||
import glob
|
||||
import hashlib
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(prog=__name__)
|
||||
|
||||
parser.add_argument("before_dir", type=str, help="Directory containing screenshots before the proposed changes")
|
||||
parser.add_argument("after_dir", type=str, help="Directory containing screenshots after the proposed changes")
|
||||
parser.add_argument("output_dir", type=str, help="Directory to save the screenshots diff report")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# "before" and "after" directories are named: artifacts/$TARGET_BRANCH and artifacts/$BRANCH_NAME
|
||||
before_branch_name = args.before_dir.split(os.path.sep)[-1]
|
||||
after_branch_name = args.after_dir.split(os.path.sep)[-1]
|
||||
|
||||
def list_files_recursively(path: str) -> list[str]:
|
||||
""" Return a list of paths to all png files in the directory tree """
|
||||
return glob.glob(path + "/**/*.png", recursive=True)
|
||||
|
||||
|
||||
def compute_file_hash(file_path: str) -> str:
|
||||
""" Return the file hash using sha256 """
|
||||
hash_func = hashlib.new('sha256')
|
||||
|
||||
with open(file_path, 'rb') as file:
|
||||
while chunk := file.read(8192): # Read the file in chunks of 8192 bytes
|
||||
hash_func.update(chunk)
|
||||
|
||||
return hash_func.hexdigest()
|
||||
|
||||
|
||||
def get_pathname_fragment(path:str) -> str:
|
||||
""" Extract the last 3 parts of the path:
|
||||
en/tools_views/ToolsCalcFinalWordDoneView.png
|
||||
|
||||
These paths will be the same in the "before" and "after" directories.
|
||||
"""
|
||||
parts = path.split(os.path.sep)
|
||||
if len(parts) < 3:
|
||||
raise ValueError(f"Path should have at least 3 parts: {path}")
|
||||
return os.path.sep.join(parts[-3:])
|
||||
|
||||
|
||||
def get_locale_and_screenshot_name(path: str) -> tuple[str, str]:
|
||||
""" Parse the path to extract the locale and the screenshot name.
|
||||
|
||||
Assumes we're working with a path like:
|
||||
en/tools_views/ToolsCalcFinalWordDoneView.png
|
||||
"""
|
||||
parts = path.split(os.path.sep)
|
||||
if len(parts) != 3:
|
||||
raise ValueError(f"Path should have 3 parts: {path}")
|
||||
return parts[0], parts[-1].split(".")[0]
|
||||
|
||||
|
||||
# Recursively list and hash all png files in the "before" directory
|
||||
before_screenshots = {}
|
||||
paths_before = []
|
||||
for file in list_files_recursively(args.before_dir):
|
||||
screenshot_path = get_pathname_fragment(file)
|
||||
before_screenshots[screenshot_path] = compute_file_hash(file)
|
||||
paths_before.append(screenshot_path)
|
||||
|
||||
# Do the same for the "after" directory, but do the diff while we're here
|
||||
only_in_after = []
|
||||
diffs: list[str] = []
|
||||
paths_after = []
|
||||
for file in list_files_recursively(args.after_dir):
|
||||
screenshot_path = get_pathname_fragment(file)
|
||||
if screenshot_path not in before_screenshots:
|
||||
only_in_after.append(screenshot_path)
|
||||
|
||||
elif before_screenshots[screenshot_path] != compute_file_hash(file):
|
||||
diffs.append(screenshot_path)
|
||||
|
||||
paths_after.append(screenshot_path)
|
||||
|
||||
only_in_before = set(paths_before) - set(paths_after)
|
||||
|
||||
html_content = "<h1>Screenshots diff report</h1>"
|
||||
html_content += f"""<p>Comparing {before_branch_name} to {after_branch_name}</p>"""
|
||||
output_dir_before = os.path.join(args.output_dir, "before")
|
||||
output_dir_after = os.path.join(args.output_dir, "after")
|
||||
os.makedirs(output_dir_before, exist_ok=True)
|
||||
os.makedirs(output_dir_after, exist_ok=True)
|
||||
|
||||
for screenshot_path in only_in_before:
|
||||
locale, screenshot_name = get_locale_and_screenshot_name(screenshot_path)
|
||||
print(f"Screenshot only in before: {locale}: {screenshot_name}")
|
||||
os.makedirs(os.path.join(output_dir_before, os.path.dirname(screenshot_path)), exist_ok=True)
|
||||
shutil.copy(os.path.join(args.before_dir, screenshot_path), os.path.join(output_dir_before, screenshot_path))
|
||||
html_content += f"<p>{locale}: REMOVED {screenshot_name}</br><img src='{os.path.join('before', screenshot_path)}'></p></br></br>"
|
||||
|
||||
for screenshot_path in only_in_after:
|
||||
locale, screenshot_name = get_locale_and_screenshot_name(screenshot_path)
|
||||
print(f"Screenshot only in after: {locale}: {screenshot_name}")
|
||||
os.makedirs(os.path.join(output_dir_after, os.path.dirname(screenshot_path)), exist_ok=True)
|
||||
shutil.copy(os.path.join(args.after_dir, screenshot_path), os.path.join(output_dir_after, screenshot_path))
|
||||
html_content += f"<p>{locale}: ADDED {screenshot_name}</br><img src='{os.path.join('after', screenshot_path)}'></p></br></br>"
|
||||
|
||||
for screenshot_path in diffs:
|
||||
locale, screenshot_name = get_locale_and_screenshot_name(screenshot_path)
|
||||
print(f"Screenshot different: {locale}: {screenshot_name}")
|
||||
# Copy both screenshots to the output dir
|
||||
os.makedirs(os.path.join(output_dir_before, os.path.dirname(screenshot_path)), exist_ok=True)
|
||||
os.makedirs(os.path.join(output_dir_after, os.path.dirname(screenshot_path)), exist_ok=True)
|
||||
shutil.copy(os.path.join(args.before_dir, screenshot_path), os.path.join(output_dir_before, screenshot_path))
|
||||
shutil.copy(os.path.join(args.after_dir, screenshot_path), os.path.join(output_dir_after, screenshot_path))
|
||||
html_content += f"<p>{locale}: {screenshot_name}</br><img src='{os.path.join('before', screenshot_path)}'> <img src='{os.path.join('after', screenshot_path)}'></p></br></br>"
|
||||
|
||||
if not only_in_after and not only_in_before and not diffs:
|
||||
print("No differences found")
|
||||
html_content += "<h1>No differences found</h1>"
|
||||
|
||||
script_dir = pathlib.Path(__file__).parent.resolve()
|
||||
html_output = ""
|
||||
with open(os.path.join(script_dir, "index.html"), "r") as f:
|
||||
html_output = f.read().replace("{{ content }}", html_content)
|
||||
|
||||
with open(os.path.join(args.output_dir, "index.html"), "w") as f:
|
||||
f.write(html_output)
|
||||
|
||||
# Also copy the css file; source: https://github.com/picocss/pico
|
||||
shutil.copy(os.path.join(script_dir, "pico.min.css"), os.path.join(args.output_dir, "pico.min.css"))
|
||||
15
.github/diff_report/index.html
vendored
Normal file
15
.github/diff_report/index.html
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<title>SeedSigner screenshot diffs</title>
|
||||
<link rel="stylesheet" href="pico.min.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
{{ content }}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
4
.github/diff_report/pico.min.css
vendored
Normal file
4
.github/diff_report/pico.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
89
.github/workflows/tests.yml
vendored
Normal file
89
.github/workflows/tests.yml
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
# Concurrency group that uses the workflow name and PR number if available
|
||||
# or commit SHA as a fallback. If a new build is triggered under that
|
||||
# concurrency group while a previous build is running it will be canceled.
|
||||
# Repeated pushes to a PR will cancel all previous builds, while multiple
|
||||
# merges to main will not cancel.
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
steps:
|
||||
- name: Checkout main repo 'dev'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'SeedSigner/seedsigner'
|
||||
ref: dev
|
||||
submodules: true
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libzbar0
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt -r tests/requirements.txt -r l10n/requirements-l10n.txt
|
||||
pip install -e .
|
||||
- name: Generate current 'dev' screenshots
|
||||
run: |
|
||||
mkdir -p artifacts/dev
|
||||
python -m pytest tests/screenshot_generator/generator.py
|
||||
sleep 10
|
||||
mv ./seedsigner-screenshots/* ./artifacts/dev/
|
||||
- name: Checkout updated translations (PR)
|
||||
uses: actions/checkout@v4
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
with:
|
||||
path: src/seedsigner/resources/seedsigner-translations
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Checkout updated translations (Push)
|
||||
uses: actions/checkout@v4
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
with:
|
||||
path: src/seedsigner/resources/seedsigner-translations
|
||||
ref: ${{ github.sha }}
|
||||
- name: Compile updated translations catalogs
|
||||
run: |
|
||||
python setup.py compile_catalog
|
||||
cd src/seedsigner/resources/seedsigner-translations
|
||||
git status
|
||||
- name: Generate latest screenshots
|
||||
run: |
|
||||
rm -rf seedsigner-screenshots
|
||||
mkdir -p artifacts/$BRANCH_NAME
|
||||
python -m pytest tests/screenshot_generator/generator.py
|
||||
sleep 10
|
||||
mv ./seedsigner-screenshots/* ./artifacts/$BRANCH_NAME/
|
||||
- name: Diff screenshots
|
||||
run: |
|
||||
mkdir -p artifacts/diff
|
||||
python src/seedsigner/resources/seedsigner-translations/.github/diff_report/diff_screenshots.py ./artifacts/dev ./artifacts/$BRANCH_NAME ./artifacts/diff
|
||||
- name: Clean up artifacts
|
||||
run: |
|
||||
rm -rf ./artifacts/$BRANCH_NAME
|
||||
rm -rf ./artifacts/dev
|
||||
mv ./artifacts/diff/* ./artifacts
|
||||
rmdir ./artifacts/diff
|
||||
- name: Archive CI Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ci-artifacts
|
||||
path: artifacts/**
|
||||
retention-days: 60
|
||||
# Upload also when tests fail. The workflow result (red/green) will
|
||||
# be not effected by this.
|
||||
if: always()
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user