diff --git a/.appveyor.yml b/.appveyor.yml index 41d1f222..a88d9714 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,6 +14,18 @@ environment: CF_BUCKET_NAME: flet-simple matrix: + # - job_name: 'Android: websockets' + # job_group: build_android + # FORGE_ARCH: android + # FORGE_PACKAGES: >- + # websockets:12.0 + + # - job_name: 'iOS: websockets' + # job_group: build_ios + # FORGE_ARCH: iOS + # FORGE_PACKAGES: >- + # websockets:12.0 + - job_name: 'Android arm64-v8a: opencv-python' job_group: build_android FORGE_ARCH: 'android:arm64-v8a' @@ -49,7 +61,7 @@ environment: FORGE_ARCH: 'iphonesimulator:x86_64' FORGE_PACKAGES: numpy:2.0.0 opencv-python:4.10.0.84 - - job_name: 'Android: pydantic-core, pillow, lru-dict, contourpy, kiwisolver, aiohttp, bitarray, argon2-cffi-binding, bcrypt, cryptography, brotli' + - job_name: 'Android: pydantic-core, pillow, lru-dict, contourpy, kiwisolver, aiohttp, bitarray, argon2-cffi-binding, bcrypt, cryptography, brotli, websockets' job_group: build_android FORGE_ARCH: android FORGE_PACKAGES: >- @@ -69,6 +81,7 @@ environment: cryptography:42.0.7 brotli:1.1.0 pydantic-core:2.18.4 + websockets:12.0 - job_name: 'Android: numpy, matplotlib, pandas, blis' job_group: build_android @@ -80,7 +93,7 @@ environment: pandas:2.2.2 blis:0.9.1 - - job_name: 'iOS: pillow, lru-dict, yarl, contourpy, kiwisolver, aiohttp, bitarray' + - job_name: 'iOS: pillow, lru-dict, yarl, contourpy, kiwisolver, aiohttp, bitarray, websockets' job_group: build_ios FORGE_ARCH: iOS FORGE_PACKAGES: >- @@ -94,6 +107,7 @@ environment: kiwisolver:1.4.5 aiohttp:3.9.5 bitarray:2.9.2 + websockets:12.0 - job_name: 'iOS: cffi, argon2-cffi-bindings, bcrypt, cryptography, brotli' job_group: build_ios diff --git a/.ci/publish-wheels.py b/.ci/publish-wheels.py index 8760e874..8845a765 100644 --- a/.ci/publish-wheels.py +++ b/.ci/publish-wheels.py @@ -1,24 +1,30 @@ +import glob import hashlib import os +import zipfile from argparse import ArgumentParser import boto3 -def get_sha256(filepath): - """Calculates the SHA256 hash of a file.""" +def get_content_sha256(content): + hasher = hashlib.sha256() + hasher.update(content) + return hasher.hexdigest() + + +def get_file_sha256(filepath): with open(filepath, "rb") as f: - hasher = hashlib.sha256() - hasher.update(f.read()) - return hasher.hexdigest() + return get_content_sha256(f.read()) -def upload_file(s3_client, bucket_name, local_file_path, remote_file_path): +def upload_file( + s3_client, bucket_name, local_file_path, remote_file_path, wheel_hash, metadata_hash +): """Uploads a file to Cloudflare S2 storage with SHA256 hash in metadata.""" try: with open(local_file_path, "rb") as f: - sha256_hash = get_sha256(local_file_path) - metadata = {"sha256": sha256_hash} + metadata = {"wheel_hash": wheel_hash, "metadata_hash": metadata_hash} s3_client.upload_fileobj( f, bucket_name, @@ -72,12 +78,32 @@ def main(): ) # Loop through files in the directory - for file in os.listdir(dist_dir): - local_file_path = os.path.join(dist_dir, file) - remote_file_path = ( - file # Upload with the same filename in the bucket (can be modified) + for wheel in glob.glob(f"{dist_dir}/*.whl"): + remote_wheel_path = os.path.basename(wheel) + + # extract and upload metadata + with zipfile.ZipFile(wheel) as z: + metadata_filename = next( + filter(lambda f: f.endswith(".dist-info/METADATA"), z.namelist()) + ) + with z.open(metadata_filename) as f: + metadata = f.read() + s3_client.put_object( + Key=f"{remote_wheel_path}.metadata", + Body=metadata, + Bucket=cf_bucket_name, + ContentType="application/octet-stream", + ) + + # upload wheel + upload_file( + s3_client, + cf_bucket_name, + wheel, + remote_wheel_path, + wheel_hash=get_file_sha256(wheel), + metadata_hash=get_content_sha256(metadata), ) - upload_file(s3_client, cf_bucket_name, local_file_path, remote_file_path) print("All files uploaded!") diff --git a/.ci/rebuild-simple-index.py b/.ci/rebuild-simple-index.py index 33c81666..03d450c5 100644 --- a/.ci/rebuild-simple-index.py +++ b/.ci/rebuild-simple-index.py @@ -5,7 +5,7 @@ html_header = "\n" html_root_anchor = '{0}
\n' -html_package_anchor = '{key}
\n' +html_package_anchor = '{key}
\n' html_footer = "\n" @@ -56,7 +56,7 @@ def normalize(name): package_name = parts[1] if not package_name in index: index[package_name] = [] - elif not key.endswith("index.html"): + elif key.endswith(".whl"): print(key) package_name = normalize(key.split("-")[0]) wheels = index.get(package_name, None) @@ -64,7 +64,13 @@ def normalize(name): wheels = [] index[package_name] = wheels metadata = s3_client.head_object(Bucket=cf_bucket_name, Key=obj["Key"]) - wheels.append({"key": key, "sha256": metadata["Metadata"]["sha256"]}) + wheels.append( + { + "key": key, + "wheel_hash": metadata["Metadata"].get("wheel_hash", ""), + "metadata_hash": metadata["Metadata"].get("metadata_hash", ""), + } + ) print("Writing root index") packages = [ @@ -76,13 +82,19 @@ def normalize(name): Key="simple/index.html", Body="\n".join(lines).encode("utf8"), Bucket=cf_bucket_name, + ContentType="text/html", ) print("Updating package indexes") for package_name, files in index.items(): files.sort(key=lambda f: f["key"]) versions = [ - html_package_anchor.format(key=f["key"], sha256=f["sha256"]) for f in files + html_package_anchor.format( + key=f["key"], + wheel_hash=f["wheel_hash"], + metadata_hash=f["metadata_hash"], + ) + for f in files ] key = f"simple/{package_name}/index.html" if len(versions) == 0: @@ -95,6 +107,7 @@ def normalize(name): Key=key, Body="\n".join(lines).encode("utf8"), Bucket=cf_bucket_name, + ContentType="text/html", ) diff --git a/recipes/websockets/meta.yaml b/recipes/websockets/meta.yaml new file mode 100644 index 00000000..7e331d03 --- /dev/null +++ b/recipes/websockets/meta.yaml @@ -0,0 +1,3 @@ +package: + name: websockets + version: '12.0' \ No newline at end of file