diff --git a/.dockerignore b/.dockerignore index f50a8474e..43e281ae8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,5 +3,5 @@ !.maestro/jellyfin-demo/ !.maestro/jellyfin-demo/** !scripts/ -!scripts/maestro_fixtures.py -!scripts/maestro_real_jellyfin.py +!scripts/maestro/maestro_fixtures.py +!scripts/maestro/maestro_real_jellyfin.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a333277f6..ee47b4913 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: run: scripts/codegen.sh --check - name: Verify translation hygiene - run: python3 scripts/clean_translations.py --check --strict + run: python3 scripts/checks/clean_translations.py --check --strict - name: Verify workflow and script guards run: bash scripts/ci_guard_checks.sh @@ -75,10 +75,10 @@ jobs: xargs -0 -r dart format --output=none --set-exit-if-changed - name: Verify icon consistency - run: dart run scripts/check_icon_consistency.dart + run: dart run scripts/checks/check_icon_consistency.dart - name: Analyze code - run: dart run scripts/check_analyzer.dart + run: dart run scripts/checks/check_analyzer.dart - name: Check for unused code run: | diff --git a/.maestro/jellyfin-demo/Dockerfile b/.maestro/jellyfin-demo/Dockerfile index 4d88a3609..bd12c7038 100644 --- a/.maestro/jellyfin-demo/Dockerfile +++ b/.maestro/jellyfin-demo/Dockerfile @@ -10,7 +10,7 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* WORKDIR /opt/plezy-demo -COPY scripts/maestro_fixtures.py scripts/maestro_real_jellyfin.py scripts/prepare_maestro_media.py ./ +COPY scripts/maestro/maestro_fixtures.py scripts/maestro/maestro_real_jellyfin.py scripts/maestro/prepare_maestro_media.py ./ COPY .maestro/jellyfin-demo/seed.sh ./seed.sh ARG PLEZY_DEMO_MEDIA_BASE_URL=https://demo-files.plezy.app/media-samples/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d8072485..22d2798b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,22 +51,22 @@ Prerequisites: Java 17, Flutter and Android SDK/platform tools, a running Androi Run the suites from the repository root (`py -3` can replace `python3` on Windows): ```bash -python3 scripts/run_maestro.py basic # Basic user flows -python3 scripts/run_maestro.py catalog # Catalog and music flows -python3 scripts/run_maestro.py media # Codec playback and track selection +python3 scripts/maestro/run_maestro.py basic # Basic user flows +python3 scripts/maestro/run_maestro.py catalog # Catalog and music flows +python3 scripts/maestro/run_maestro.py media # Codec playback and track selection ``` Run one flow with `--flow`: ```bash -python3 scripts/run_maestro.py basic --flow .maestro/flows/04_search.yaml +python3 scripts/maestro/run_maestro.py basic --flow .maestro/flows/04_search.yaml ``` Use `--skip-build` to reuse the debug APK and `--skip-jellyfin-build` to reuse the Jellyfin image. Set `--device ` when multiple devices are connected; physical devices also require `--adb-reverse`. Top-level flows live in `.maestro/flows/`, shared setup in `.maestro/subflows/`, and focused regressions in -`.maestro/regression_flows/`. Automatic groups are declared in `scripts/run_maestro_ci.py::GROUPS`. Every top-level +`.maestro/regression_flows/`. Automatic groups are declared in `scripts/maestro/run_maestro_ci.py::GROUPS`. Every top-level regression flow must be registered either there or in `DESTRUCTIVE_MANUAL_TARGETS`; reusable subflows are not independent tests. A manual-only classification must state why the flow cannot run automatically. @@ -75,7 +75,7 @@ manual target rather than an automatic group. Run them only against the pre-seed emulator, using the required opt-in: ```bash -python3 scripts/run_maestro_ci.py profile-regressions --disposable-emulator +python3 scripts/maestro/run_maestro_ci.py profile-regressions --disposable-emulator ``` The target refuses to start without `--disposable-emulator`. Each profile flow writes to its own Jellyfin log and @@ -102,7 +102,7 @@ Update a production image only through a reviewed change: 4. Before changing the Bugs digest, exercise it with non-production configuration and a disposable volume. Review migrations, take a restorable `bugs_data` backup, then validate a cloned volume. A forward-only migration rolls back with the prior digest and pre-change backup, not by changing the image reference alone. -5. Run `python3 scripts/check_container_image_pins.py`, `python3 scripts/test_check_container_image_pins.py`, and +5. Run `python3 scripts/checks/check_container_image_pins.py`, `python3 scripts/checks/test_check_container_image_pins.py`, and `(cd server && go test ./...)`. Inspect the rendered Compose configuration and rebuilt images locally without exposing configuration values. Do not publish or deploy from a review checkout, and never fall back to `latest` when a digest is unavailable. diff --git a/README.md b/README.md index 117867209..44dc7988e 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ scripts/setup_hooks.sh End-to-end tests (Android emulator plus a Dockerized Jellyfin fixture): ```bash -python3 scripts/run_maestro.py basic +python3 scripts/maestro/run_maestro.py basic ``` ## Contributing diff --git a/lib/data/ducet_order.dart b/lib/data/ducet_order.dart index e4e8ca7ca..d0b5c3158 100644 --- a/lib/data/ducet_order.dart +++ b/lib/data/ducet_order.dart @@ -1,7 +1,7 @@ /// Dense BMP ranks per DUCET (Unicode 13.0) + pinned CLDR CJK radical-stroke. /// Each code unit stores rank + 1; zero means absent. /// Katakana sorts before hiragana (CLDR root tailoring). -/// Generated by scripts/generate_ducet_ranks.dart — do not edit. +/// Generated by scripts/codegen/generate_ducet_ranks.dart — do not edit. library; const String _ducetRanks = diff --git a/lib/data/hid_key_labels.dart b/lib/data/hid_key_labels.dart index 859206411..2bb8e1c47 100644 --- a/lib/data/hid_key_labels.dart +++ b/lib/data/hid_key_labels.dart @@ -1,4 +1,4 @@ -// Generated by dart run scripts/generate_hid_key_labels.dart from scripts/data/hid_key_labels.json; do not edit by hand. +// Generated by dart run scripts/codegen/generate_hid_key_labels.dart from scripts/codegen/data/hid_key_labels.json; do not edit by hand. /// Human-readable labels for physical keyboard keys, keyed by USB HID usage code. const hidKeyLabels = { diff --git a/lib/data/iso_639_data.dart b/lib/data/iso_639_data.dart index ec7babf48..e5fc9e0c2 100644 --- a/lib/data/iso_639_data.dart +++ b/lib/data/iso_639_data.dart @@ -1,4 +1,4 @@ -// Generated by dart run scripts/generate_iso_639_data.dart from scripts/data/iso_639_codes.json; do not edit by hand. +// Generated by dart run scripts/codegen/generate_iso_639_data.dart from scripts/codegen/data/iso_639_codes.json; do not edit by hand. class LanguageEntry { final String code1; diff --git a/lib/watch_together/services/relay_protocol.g.dart b/lib/watch_together/services/relay_protocol.g.dart index c733ec7a0..afce560fb 100644 --- a/lib/watch_together/services/relay_protocol.g.dart +++ b/lib/watch_together/services/relay_protocol.g.dart @@ -1,4 +1,4 @@ -// Generated by scripts/generate_relay_protocol.py. Do not edit. +// Generated by scripts/codegen/generate_relay_protocol.py. Do not edit. abstract final class RelayProtocol { static const int protocolVersion = 2; diff --git a/scripts/check_analyzer.dart b/scripts/checks/check_analyzer.dart similarity index 99% rename from scripts/check_analyzer.dart rename to scripts/checks/check_analyzer.dart index 4f96b065d..e1ce00fa2 100644 --- a/scripts/check_analyzer.dart +++ b/scripts/checks/check_analyzer.dart @@ -19,7 +19,7 @@ final _allowedDiagnostics = { Future main() async { final scriptDirectory = File.fromUri(Platform.script).parent; - final root = scriptDirectory.parent.path; + final root = scriptDirectory.parent.parent.path; late final Process process; try { diff --git a/scripts/check_apple_spm_locks.py b/scripts/checks/check_apple_spm_locks.py similarity index 99% rename from scripts/check_apple_spm_locks.py rename to scripts/checks/check_apple_spm_locks.py index a2c38c537..12e5c6a91 100755 --- a/scripts/check_apple_spm_locks.py +++ b/scripts/checks/check_apple_spm_locks.py @@ -147,7 +147,7 @@ def validate(root: Path) -> list[str]: def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser() - parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[1]) + parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2]) args = parser.parse_args(argv) errors = validate(args.root) if errors: diff --git a/scripts/check_build_workflow.py b/scripts/checks/check_build_workflow.py similarity index 99% rename from scripts/check_build_workflow.py rename to scripts/checks/check_build_workflow.py index 09bf81a1a..e181cb0bd 100644 --- a/scripts/check_build_workflow.py +++ b/scripts/checks/check_build_workflow.py @@ -8,7 +8,7 @@ import sys from workflow_yaml import iter_uses_references, job_block -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] DEFAULT_WORKFLOW = ROOT / ".github/workflows/build.yml" FLUTTER_VERSION = "3.44.0" FLUTTER_COMMIT = "559ffa3f75e7402d65a8def9c28389a9b2e6fe42" diff --git a/scripts/check_bun_audit.py b/scripts/checks/check_bun_audit.py similarity index 100% rename from scripts/check_bun_audit.py rename to scripts/checks/check_bun_audit.py diff --git a/scripts/check_codegen.py b/scripts/checks/check_codegen.py similarity index 100% rename from scripts/check_codegen.py rename to scripts/checks/check_codegen.py diff --git a/scripts/check_container_image_pins.py b/scripts/checks/check_container_image_pins.py similarity index 99% rename from scripts/check_container_image_pins.py rename to scripts/checks/check_container_image_pins.py index d2870e34f..1a01a61f9 100755 --- a/scripts/check_container_image_pins.py +++ b/scripts/checks/check_container_image_pins.py @@ -8,7 +8,7 @@ import re import sys from pathlib import Path -ROOT = Path(__file__).resolve().parent.parent +ROOT = Path(__file__).resolve().parents[2] PRODUCTION_DOCKERFILES = (ROOT / "server" / "Dockerfile",) PRODUCTION_COMPOSE_FILES = (ROOT / "server" / "docker-compose.yml",) SUPPORTED_PLATFORMS = frozenset({"linux/amd64", "linux/arm64"}) diff --git a/scripts/check_hardcoded_strings.py b/scripts/checks/check_hardcoded_strings.py similarity index 99% rename from scripts/check_hardcoded_strings.py rename to scripts/checks/check_hardcoded_strings.py index a98092b28..f05efce52 100755 --- a/scripts/check_hardcoded_strings.py +++ b/scripts/checks/check_hardcoded_strings.py @@ -23,7 +23,7 @@ import re from dataclasses import dataclass from pathlib import Path -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] LIB_DIR = ROOT / "lib" ALLOWLIST_PATH = Path(__file__).with_name("hardcoded_strings_allowlist.json") diff --git a/scripts/check_icon_consistency.dart b/scripts/checks/check_icon_consistency.dart similarity index 98% rename from scripts/check_icon_consistency.dart rename to scripts/checks/check_icon_consistency.dart index 0f0b7a986..ba51c879f 100644 --- a/scripts/check_icon_consistency.dart +++ b/scripts/checks/check_icon_consistency.dart @@ -17,11 +17,11 @@ void main(List arguments) { final scriptDirectory = File.fromUri(Platform.script).absolute.parent; late final Directory root; if (arguments.isEmpty) { - root = scriptDirectory.parent; + root = scriptDirectory.parent.parent; } else if (arguments.length == 2 && arguments.first == '--root') { root = Directory(arguments.last).absolute; } else { - stderr.writeln('Usage: dart run scripts/check_icon_consistency.dart [--root ]'); + stderr.writeln('Usage: dart run scripts/checks/check_icon_consistency.dart [--root ]'); exitCode = 64; return; } diff --git a/scripts/check_linux_package_deps.py b/scripts/checks/check_linux_package_deps.py similarity index 99% rename from scripts/check_linux_package_deps.py rename to scripts/checks/check_linux_package_deps.py index 40e990681..7ced09418 100644 --- a/scripts/check_linux_package_deps.py +++ b/scripts/checks/check_linux_package_deps.py @@ -12,7 +12,7 @@ import re import sys -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] if len(sys.argv) > 2: raise SystemExit(f"Usage: {Path(sys.argv[0]).name} [linux-dir]") LINUX = Path(sys.argv[1]).resolve() if len(sys.argv) == 2 else ROOT / "linux" diff --git a/scripts/check_shrinker_rules.py b/scripts/checks/check_shrinker_rules.py similarity index 99% rename from scripts/check_shrinker_rules.py rename to scripts/checks/check_shrinker_rules.py index 7324ba340..3ab751aa8 100755 --- a/scripts/check_shrinker_rules.py +++ b/scripts/checks/check_shrinker_rules.py @@ -179,7 +179,7 @@ def validate(root: Path) -> list[str]: def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser() - parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[1]) + parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2]) args = parser.parse_args(argv) errors = validate(args.root) if errors: diff --git a/scripts/check_tvos_test_wiring.py b/scripts/checks/check_tvos_test_wiring.py similarity index 99% rename from scripts/check_tvos_test_wiring.py rename to scripts/checks/check_tvos_test_wiring.py index 16d2152f3..efc50e068 100755 --- a/scripts/check_tvos_test_wiring.py +++ b/scripts/checks/check_tvos_test_wiring.py @@ -119,7 +119,7 @@ def validate(root: Path) -> list[str]: def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser() - parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[1]) + parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2]) args = parser.parse_args(argv) errors = validate(args.root) if errors: diff --git a/scripts/check_update_packages_workflow.py b/scripts/checks/check_update_packages_workflow.py similarity index 97% rename from scripts/check_update_packages_workflow.py rename to scripts/checks/check_update_packages_workflow.py index 651721302..9838270bf 100644 --- a/scripts/check_update_packages_workflow.py +++ b/scripts/checks/check_update_packages_workflow.py @@ -8,7 +8,7 @@ import sys from workflow_yaml import job_block -WORKFLOW = Path(__file__).resolve().parents[1] / ".github/workflows/update-packages.yml" +WORKFLOW = Path(__file__).resolve().parents[2] / ".github/workflows/update-packages.yml" text = WORKFLOW.read_text(encoding="utf-8") errors: list[str] = [] diff --git a/scripts/check_windows_installer.py b/scripts/checks/check_windows_installer.py similarity index 99% rename from scripts/check_windows_installer.py rename to scripts/checks/check_windows_installer.py index e3f9dec05..8530e57c1 100755 --- a/scripts/check_windows_installer.py +++ b/scripts/checks/check_windows_installer.py @@ -12,7 +12,7 @@ import re import sys -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] DEFAULT_SCRIPT = ROOT / "windows/build-installer.ps1" if len(sys.argv) > 2: raise SystemExit(f"Usage: {Path(sys.argv[0]).name} [build-installer-path]") diff --git a/scripts/check_windows_msix.py b/scripts/checks/check_windows_msix.py similarity index 99% rename from scripts/check_windows_msix.py rename to scripts/checks/check_windows_msix.py index e7d5741a9..34de33b78 100644 --- a/scripts/check_windows_msix.py +++ b/scripts/checks/check_windows_msix.py @@ -21,11 +21,13 @@ import re import sys from xml.etree import ElementTree +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + from pubspec_version import parse_pubspec_version from workflow_yaml import job_block -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] DEFAULT_SCRIPT = ROOT / "windows/build-msix.ps1" if len(sys.argv) > 2: raise SystemExit(f"Usage: {Path(sys.argv[0]).name} [build-msix-path]") diff --git a/scripts/check_workflow_action_pins.py b/scripts/checks/check_workflow_action_pins.py similarity index 97% rename from scripts/check_workflow_action_pins.py rename to scripts/checks/check_workflow_action_pins.py index 8cf669297..7db45720b 100755 --- a/scripts/check_workflow_action_pins.py +++ b/scripts/checks/check_workflow_action_pins.py @@ -9,7 +9,7 @@ from pathlib import Path import workflow_yaml -ROOT = Path(__file__).resolve().parent.parent +ROOT = Path(__file__).resolve().parents[2] WORKFLOWS = ROOT / ".github" / "workflows" ACTIONS = ROOT / ".github" / "actions" REMOTE_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_./-]+)?@[0-9a-fA-F]{40}$") diff --git a/scripts/check_workflow_security.py b/scripts/checks/check_workflow_security.py similarity index 99% rename from scripts/check_workflow_security.py rename to scripts/checks/check_workflow_security.py index cf6f9703d..10dbc8eda 100755 --- a/scripts/check_workflow_security.py +++ b/scripts/checks/check_workflow_security.py @@ -8,7 +8,7 @@ import sys from workflow_yaml import iter_uses_references, iter_workflow_files, scalar -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] WORKFLOWS = ROOT / ".github" / "workflows" CI_WORKFLOW = Path(".github/workflows/ci.yml") FULL_SHA = re.compile(r"[0-9a-f]{40}") diff --git a/scripts/clean_translations.py b/scripts/checks/clean_translations.py similarity index 95% rename from scripts/clean_translations.py rename to scripts/checks/clean_translations.py index e8cba38d1..2a717e19b 100755 --- a/scripts/clean_translations.py +++ b/scripts/checks/clean_translations.py @@ -3,11 +3,11 @@ Normalize lib/i18n/*.i18n.json to match en.i18n.json's structure/order/formatting and report en leaf keys that are never referenced from lib/**/*.dart. - python3 scripts/clean_translations.py # clean + unused report - python3 scripts/clean_translations.py --check # dry-run (no writes) - python3 scripts/clean_translations.py --clean # only normalize JSON - python3 scripts/clean_translations.py --unused # only unused-key scan - python3 scripts/clean_translations.py --strict # exit 1 if unused keys found + python3 scripts/checks/clean_translations.py # clean + unused report + python3 scripts/checks/clean_translations.py --check # dry-run (no writes) + python3 scripts/checks/clean_translations.py --clean # only normalize JSON + python3 scripts/checks/clean_translations.py --unused # only unused-key scan + python3 scripts/checks/clean_translations.py --strict # exit 1 if unused keys found Caveat: usage detection is static. Aliased access like `final _t = t; _t.a.b` would be missed. The repo does not currently use this pattern. @@ -20,7 +20,7 @@ import re import sys from pathlib import Path -ROOT = Path(__file__).resolve().parent.parent +ROOT = Path(__file__).resolve().parents[2] I18N_DIR = ROOT / "lib" / "i18n" LIB_DIR = ROOT / "lib" SOURCE_LOCALE = "en" diff --git a/scripts/hardcoded_strings_allowlist.json b/scripts/checks/hardcoded_strings_allowlist.json similarity index 100% rename from scripts/hardcoded_strings_allowlist.json rename to scripts/checks/hardcoded_strings_allowlist.json diff --git a/scripts/test_check_apple_spm_locks.py b/scripts/checks/test_check_apple_spm_locks.py similarity index 100% rename from scripts/test_check_apple_spm_locks.py rename to scripts/checks/test_check_apple_spm_locks.py diff --git a/scripts/test_check_build_workflow.py b/scripts/checks/test_check_build_workflow.py similarity index 98% rename from scripts/test_check_build_workflow.py rename to scripts/checks/test_check_build_workflow.py index 0d9b12d7a..aec613ec6 100755 --- a/scripts/test_check_build_workflow.py +++ b/scripts/checks/test_check_build_workflow.py @@ -8,8 +8,8 @@ import tempfile import unittest -ROOT = Path(__file__).resolve().parents[1] -CHECKER = ROOT / "scripts/check_build_workflow.py" +ROOT = Path(__file__).resolve().parents[2] +CHECKER = ROOT / "scripts/checks/check_build_workflow.py" WORKFLOW = ROOT / ".github/workflows/build.yml" SETUP_FLUTTER_GIT = ROOT / ".github/actions/setup-flutter-git/action.yml" diff --git a/scripts/test_check_bun_audit.py b/scripts/checks/test_check_bun_audit.py similarity index 100% rename from scripts/test_check_bun_audit.py rename to scripts/checks/test_check_bun_audit.py diff --git a/scripts/test_check_bundle_host_deps.py b/scripts/checks/test_check_bundle_host_deps.py similarity index 99% rename from scripts/test_check_bundle_host_deps.py rename to scripts/checks/test_check_bundle_host_deps.py index 3fb651d84..41037c940 100644 --- a/scripts/test_check_bundle_host_deps.py +++ b/scripts/checks/test_check_bundle_host_deps.py @@ -22,7 +22,7 @@ import tempfile import unittest -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] CHECKER = ROOT / "linux/packaging/check-bundle-host-deps.py" BUILD_PACKAGES = ROOT / "linux/packaging/build-packages.py" diff --git a/scripts/test_check_codegen.py b/scripts/checks/test_check_codegen.py similarity index 96% rename from scripts/test_check_codegen.py rename to scripts/checks/test_check_codegen.py index 8335f07ab..4e2bac05e 100644 --- a/scripts/test_check_codegen.py +++ b/scripts/checks/test_check_codegen.py @@ -38,12 +38,14 @@ class CodegenCheckTest(unittest.TestCase): subprocess.run(["git", "config", "user.name", "Fixture"], cwd=repository, check=True) subprocess.run(["git", "config", "core.autocrlf", "false"], cwd=repository, check=True) - shutil.copy2(SCRIPT_DIR.parent / ".gitattributes", repository / ".gitattributes") + shutil.copy2(SCRIPT_DIR.parents[1] / ".gitattributes", repository / ".gitattributes") (repository / "scripts").mkdir() - shutil.copy2(SCRIPT_DIR / "codegen.sh", repository / "scripts" / "codegen.sh") - shutil.copy2(SCRIPT_DIR / "check_codegen.py", repository / "scripts" / "check_codegen.py") - (repository / "scripts" / "generate_relay_protocol.py").write_text("fixture\n", encoding="utf-8") + (repository / "scripts" / "checks").mkdir() + (repository / "scripts" / "codegen").mkdir() + shutil.copy2(SCRIPT_DIR.parent / "codegen.sh", repository / "scripts" / "codegen.sh") + shutil.copy2(SCRIPT_DIR / "check_codegen.py", repository / "scripts" / "checks" / "check_codegen.py") + (repository / "scripts" / "codegen" / "generate_relay_protocol.py").write_text("fixture\n", encoding="utf-8") (repository / "source.txt").write_text("version one\n", encoding="utf-8") for relative in GENERATED_PATHS: path = repository / relative diff --git a/scripts/test_check_container_image_pins.py b/scripts/checks/test_check_container_image_pins.py similarity index 100% rename from scripts/test_check_container_image_pins.py rename to scripts/checks/test_check_container_image_pins.py diff --git a/scripts/test_check_hardcoded_strings.py b/scripts/checks/test_check_hardcoded_strings.py similarity index 100% rename from scripts/test_check_hardcoded_strings.py rename to scripts/checks/test_check_hardcoded_strings.py diff --git a/scripts/test_check_icon_consistency.py b/scripts/checks/test_check_icon_consistency.py similarity index 95% rename from scripts/test_check_icon_consistency.py rename to scripts/checks/test_check_icon_consistency.py index 5b4821147..63f72bed7 100755 --- a/scripts/test_check_icon_consistency.py +++ b/scripts/checks/test_check_icon_consistency.py @@ -6,8 +6,8 @@ import unittest from pathlib import Path -ROOT = Path(__file__).resolve().parent.parent -CHECKER = ROOT / "scripts" / "check_icon_consistency.dart" +ROOT = Path(__file__).resolve().parents[2] +CHECKER = ROOT / "scripts" / "checks" / "check_icon_consistency.dart" class IconConsistencyCheckerTest(unittest.TestCase): diff --git a/scripts/test_check_linux_package_deps.py b/scripts/checks/test_check_linux_package_deps.py similarity index 99% rename from scripts/test_check_linux_package_deps.py rename to scripts/checks/test_check_linux_package_deps.py index 6142c7166..9560e8bb8 100644 --- a/scripts/test_check_linux_package_deps.py +++ b/scripts/checks/test_check_linux_package_deps.py @@ -13,8 +13,8 @@ import tempfile import unittest -ROOT = Path(__file__).resolve().parents[1] -CHECKER = ROOT / "scripts/check_linux_package_deps.py" +ROOT = Path(__file__).resolve().parents[2] +CHECKER = ROOT / "scripts/checks/check_linux_package_deps.py" LINUX = ROOT / "linux" # Every file the checker reads, relative to the linux/ directory it is given. FIXTURE_FILES = ( diff --git a/scripts/test_check_package_deps.py b/scripts/checks/test_check_package_deps.py similarity index 98% rename from scripts/test_check_package_deps.py rename to scripts/checks/test_check_package_deps.py index 672248c87..8c21c89be 100644 --- a/scripts/test_check_package_deps.py +++ b/scripts/checks/test_check_package_deps.py @@ -24,10 +24,10 @@ import tempfile import unittest # The Windows-safe stub installer, rather than a second copy of its PATHEXT -# reasoning. scripts/ is sys.path[0] however this file is invoked. +# reasoning. scripts/checks/ is sys.path[0] however this file is invoked. from test_check_bundle_host_deps import install_stub -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] CHECKER = ROOT / "linux/packaging/check-package-deps.py" BUILD_PACKAGES = ROOT / "linux/packaging/build-packages.py" diff --git a/scripts/test_check_shrinker_rules.py b/scripts/checks/test_check_shrinker_rules.py similarity index 99% rename from scripts/test_check_shrinker_rules.py rename to scripts/checks/test_check_shrinker_rules.py index 633a788d2..130cdd35c 100755 --- a/scripts/test_check_shrinker_rules.py +++ b/scripts/checks/test_check_shrinker_rules.py @@ -65,7 +65,7 @@ class ShrinkerRulesCheckerTest(unittest.TestCase): return {"reflected namespace", "with FindClass", "from native code", "in the descriptor"} def test_repository_rules_cover_every_name_reached_class_and_member(self) -> None: - self.assertEqual([], CHECKER.validate(Path(__file__).resolve().parents[1])) + self.assertEqual([], CHECKER.validate(Path(__file__).resolve().parents[2])) def test_package_keep_plus_descriptor_keep_passes(self) -> None: self._write_rules(FULL_RULES) diff --git a/scripts/test_check_tvos_test_wiring.py b/scripts/checks/test_check_tvos_test_wiring.py similarity index 100% rename from scripts/test_check_tvos_test_wiring.py rename to scripts/checks/test_check_tvos_test_wiring.py diff --git a/scripts/test_check_windows_installer.py b/scripts/checks/test_check_windows_installer.py similarity index 97% rename from scripts/test_check_windows_installer.py rename to scripts/checks/test_check_windows_installer.py index 32f481f86..13c3d0796 100755 --- a/scripts/test_check_windows_installer.py +++ b/scripts/checks/test_check_windows_installer.py @@ -8,8 +8,8 @@ import tempfile import unittest -ROOT = Path(__file__).resolve().parents[1] -CHECKER = ROOT / "scripts/check_windows_installer.py" +ROOT = Path(__file__).resolve().parents[2] +CHECKER = ROOT / "scripts/checks/check_windows_installer.py" SCRIPT = ROOT / "windows/build-installer.ps1" diff --git a/scripts/test_check_windows_msix.py b/scripts/checks/test_check_windows_msix.py similarity index 98% rename from scripts/test_check_windows_msix.py rename to scripts/checks/test_check_windows_msix.py index dee286cc6..c0e530140 100644 --- a/scripts/test_check_windows_msix.py +++ b/scripts/checks/test_check_windows_msix.py @@ -8,8 +8,8 @@ import tempfile import unittest -ROOT = Path(__file__).resolve().parents[1] -CHECKER = ROOT / "scripts/check_windows_msix.py" +ROOT = Path(__file__).resolve().parents[2] +CHECKER = ROOT / "scripts/checks/check_windows_msix.py" SCRIPT = ROOT / "windows/build-msix.ps1" diff --git a/scripts/test_check_workflow_action_pins.py b/scripts/checks/test_check_workflow_action_pins.py similarity index 100% rename from scripts/test_check_workflow_action_pins.py rename to scripts/checks/test_check_workflow_action_pins.py diff --git a/scripts/test_check_workflow_security.py b/scripts/checks/test_check_workflow_security.py similarity index 97% rename from scripts/test_check_workflow_security.py rename to scripts/checks/test_check_workflow_security.py index c96a55e78..3cbe1a6f4 100755 --- a/scripts/test_check_workflow_security.py +++ b/scripts/checks/test_check_workflow_security.py @@ -7,7 +7,7 @@ from check_workflow_security import check_workflow SAFE_SHA = "a" * 40 -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] CI_PATH = Path(".github/workflows/ci.yml") @@ -40,7 +40,7 @@ jobs: .replace("name: Code Analysis", "name: Repository checks", 1) .replace("runs-on: ubuntu-latest", "runs-on: internal-linux", 1) .replace("- sanitizer: address", "- sanitizer: memory", 1) - .replace("dart run scripts/check_analyzer.dart", "dart run tool/check.dart", 1) + .replace("dart run scripts/checks/check_analyzer.dart", "dart run tool/check.dart", 1) ) self.assertEqual(self.check(changed, CI_PATH), []) diff --git a/scripts/test_clean_translations.py b/scripts/checks/test_clean_translations.py similarity index 100% rename from scripts/test_clean_translations.py rename to scripts/checks/test_clean_translations.py diff --git a/scripts/test_fetch_tvos_engine.py b/scripts/checks/test_fetch_tvos_engine.py similarity index 99% rename from scripts/test_fetch_tvos_engine.py rename to scripts/checks/test_fetch_tvos_engine.py index cf07e6d8a..d558de4da 100755 --- a/scripts/test_fetch_tvos_engine.py +++ b/scripts/checks/test_fetch_tvos_engine.py @@ -14,7 +14,7 @@ import unittest from pathlib import Path -ROOT = Path(__file__).resolve().parents[1] +ROOT = Path(__file__).resolve().parents[2] FETCH_ENGINE = ROOT / "tvos/scripts/fetch_engine.sh" diff --git a/scripts/test_format_native.py b/scripts/checks/test_format_native.py similarity index 99% rename from scripts/test_format_native.py rename to scripts/checks/test_format_native.py index dc0a48bfc..3c23c8631 100644 --- a/scripts/test_format_native.py +++ b/scripts/checks/test_format_native.py @@ -6,7 +6,7 @@ import tempfile import unittest from pathlib import Path -SCRIPT = Path(__file__).resolve().parent / "format_native.sh" +SCRIPT = Path(__file__).resolve().parents[1] / "format_native.sh" DIGEST = "a16be01dcc480aab2f55f444b620142152f66e31564b3b9376506d624c28a2ad" JAVA_DIAGNOSTIC = ( "A working JDK 17+ is required for Kotlin formatting. " diff --git a/scripts/test_run_tests.py b/scripts/checks/test_run_tests.py similarity index 99% rename from scripts/test_run_tests.py rename to scripts/checks/test_run_tests.py index d55c078c4..0702c0528 100755 --- a/scripts/test_run_tests.py +++ b/scripts/checks/test_run_tests.py @@ -17,7 +17,7 @@ import tempfile import unittest -ROOT_DIR = Path(__file__).resolve().parents[1] +ROOT_DIR = Path(__file__).resolve().parents[2] RUN_TESTS = ROOT_DIR / "scripts" / "run_tests.sh" # Resolved up front: one test empties PATH, which would otherwise hide bash too. BASH = shutil.which("bash") or "/bin/bash" diff --git a/scripts/test_verify_runtime_inputs.py b/scripts/checks/test_verify_runtime_inputs.py similarity index 99% rename from scripts/test_verify_runtime_inputs.py rename to scripts/checks/test_verify_runtime_inputs.py index 8f5f65975..3aa9c4be3 100755 --- a/scripts/test_verify_runtime_inputs.py +++ b/scripts/checks/test_verify_runtime_inputs.py @@ -15,7 +15,7 @@ SPEC = importlib.util.spec_from_file_location("verify_runtime_inputs", SCRIPT) CHECKER = importlib.util.module_from_spec(SPEC) assert SPEC.loader is not None SPEC.loader.exec_module(CHECKER) -REPOSITORY = Path(__file__).resolve().parents[1] +REPOSITORY = Path(__file__).resolve().parents[2] FIXTURES = ( "pubspec.lock", diff --git a/scripts/verify_runtime_inputs.py b/scripts/checks/verify_runtime_inputs.py similarity index 99% rename from scripts/verify_runtime_inputs.py rename to scripts/checks/verify_runtime_inputs.py index 464834cf5..4471f556e 100755 --- a/scripts/verify_runtime_inputs.py +++ b/scripts/checks/verify_runtime_inputs.py @@ -251,7 +251,7 @@ def validate(root: Path) -> list[str]: def main() -> int: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[1]) + parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2]) arguments = parser.parse_args() errors = validate(arguments.root.resolve()) if errors: diff --git a/scripts/workflow_yaml.py b/scripts/checks/workflow_yaml.py similarity index 100% rename from scripts/workflow_yaml.py rename to scripts/checks/workflow_yaml.py diff --git a/scripts/ci_checks.sh b/scripts/ci_checks.sh index 3cc00cda7..4caeaac0c 100755 --- a/scripts/ci_checks.sh +++ b/scripts/ci_checks.sh @@ -66,7 +66,7 @@ fi rm -f "$out" section "translation hygiene" -if python3 scripts/clean_translations.py --check --strict; then +if python3 scripts/checks/clean_translations.py --check --strict; then ok "locale files normalized and no unused keys found" else fail "translation files need cleanup or contain unused keys" @@ -74,7 +74,7 @@ else fi section "hardcoded UI strings" -if python3 scripts/check_hardcoded_strings.py; then +if python3 scripts/checks/check_hardcoded_strings.py; then ok "user-facing strings use the translation layer" else fail "hardcoded user-facing English strings found" @@ -90,7 +90,7 @@ else fi section "icon consistency" -if dart run scripts/check_icon_consistency.dart; then +if dart run scripts/checks/check_icon_consistency.dart; then ok "production icons use AppIcon and rounded Symbols" else fail "icon consistency violations found" @@ -109,7 +109,7 @@ fi rm -f "$out" section "Dart analyzer" -if dart run scripts/check_analyzer.dart; then +if dart run scripts/checks/check_analyzer.dart; then ok "no unapproved diagnostics" else fail "analyzer errors, warnings, unexpected infos, or tool failure" diff --git a/scripts/ci_guard_checks.sh b/scripts/ci_guard_checks.sh index 0399584bd..4f3bf443d 100644 --- a/scripts/ci_guard_checks.sh +++ b/scripts/ci_guard_checks.sh @@ -10,21 +10,21 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" for checker in \ - scripts/check_build_workflow.py \ - scripts/check_apple_spm_locks.py \ - scripts/check_tvos_test_wiring.py \ - scripts/check_shrinker_rules.py \ - scripts/verify_runtime_inputs.py \ - scripts/check_workflow_security.py \ - scripts/check_workflow_action_pins.py \ - scripts/check_container_image_pins.py \ - scripts/check_update_packages_workflow.py \ - scripts/check_linux_package_deps.py \ - scripts/check_windows_installer.py \ - scripts/check_windows_msix.py; do + scripts/checks/check_build_workflow.py \ + scripts/checks/check_apple_spm_locks.py \ + scripts/checks/check_tvos_test_wiring.py \ + scripts/checks/check_shrinker_rules.py \ + scripts/checks/verify_runtime_inputs.py \ + scripts/checks/check_workflow_security.py \ + scripts/checks/check_workflow_action_pins.py \ + scripts/checks/check_container_image_pins.py \ + scripts/checks/check_update_packages_workflow.py \ + scripts/checks/check_linux_package_deps.py \ + scripts/checks/check_windows_installer.py \ + scripts/checks/check_windows_msix.py; do python3 "$checker" done -for guard_test in scripts/test_*.py; do +for guard_test in scripts/test_*.py scripts/*/test_*.py; do python3 "$guard_test" done diff --git a/scripts/ci_website_checks.sh b/scripts/ci_website_checks.sh index 56e8ca114..63b8aa64b 100755 --- a/scripts/ci_website_checks.sh +++ b/scripts/ci_website_checks.sh @@ -8,5 +8,5 @@ bun install --frozen-lockfile bun run test bun run check bun run build -python3 "$ROOT_DIR/scripts/test_check_bun_audit.py" +python3 "$ROOT_DIR/scripts/checks/test_check_bun_audit.py" bun run audit diff --git a/scripts/codegen.sh b/scripts/codegen.sh index 5e876c34f..0a0e2c917 100755 --- a/scripts/codegen.sh +++ b/scripts/codegen.sh @@ -4,12 +4,12 @@ cd "$(dirname "${BASH_SOURCE[0]}")/.." if [[ "${1:-}" == "--check" ]]; then shift - exec python3 scripts/check_codegen.py "$@" + exec python3 scripts/checks/check_codegen.py "$@" fi -dart run scripts/generate_ducet_ranks.dart -dart run scripts/generate_hid_key_labels.dart -dart run scripts/generate_iso_639_data.dart -python3 scripts/generate_relay_protocol.py +dart run scripts/codegen/generate_ducet_ranks.dart +dart run scripts/codegen/generate_hid_key_labels.dart +dart run scripts/codegen/generate_iso_639_data.dart +python3 scripts/codegen/generate_relay_protocol.py dart run slang dart run build_runner build "$@" diff --git a/scripts/data/FractionalUCA-651afecf9ccf1541a49306993e8210fa2209aa0b.txt.gz b/scripts/codegen/data/FractionalUCA-651afecf9ccf1541a49306993e8210fa2209aa0b.txt.gz similarity index 100% rename from scripts/data/FractionalUCA-651afecf9ccf1541a49306993e8210fa2209aa0b.txt.gz rename to scripts/codegen/data/FractionalUCA-651afecf9ccf1541a49306993e8210fa2209aa0b.txt.gz diff --git a/scripts/data/allkeys-13.0.0.txt.gz b/scripts/codegen/data/allkeys-13.0.0.txt.gz similarity index 100% rename from scripts/data/allkeys-13.0.0.txt.gz rename to scripts/codegen/data/allkeys-13.0.0.txt.gz diff --git a/scripts/data/hid_key_labels.json b/scripts/codegen/data/hid_key_labels.json similarity index 100% rename from scripts/data/hid_key_labels.json rename to scripts/codegen/data/hid_key_labels.json diff --git a/scripts/data/iso_639_codes.json b/scripts/codegen/data/iso_639_codes.json similarity index 100% rename from scripts/data/iso_639_codes.json rename to scripts/codegen/data/iso_639_codes.json diff --git a/scripts/generate_android_icons.sh b/scripts/codegen/generate_android_icons.sh similarity index 100% rename from scripts/generate_android_icons.sh rename to scripts/codegen/generate_android_icons.sh diff --git a/scripts/generate_ducet_ranks.dart b/scripts/codegen/generate_ducet_ranks.dart similarity index 98% rename from scripts/generate_ducet_ranks.dart rename to scripts/codegen/generate_ducet_ranks.dart index 620aa39ce..5cd822517 100644 --- a/scripts/generate_ducet_ranks.dart +++ b/scripts/codegen/generate_ducet_ranks.dart @@ -1,7 +1,7 @@ /// Generates lib/data/ducet_order.dart from pinned Unicode and CLDR data. /// /// Usage: -/// dart run scripts/generate_ducet_ranks.dart [allkeys.txt] [FractionalUCA.txt] +/// dart run scripts/codegen/generate_ducet_ranks.dart [allkeys.txt] [FractionalUCA.txt] /// /// Default generation uses tracked deterministic gzip copies of the pinned source bytes. /// Unicode data is redistributed unmodified under the Unicode License v3. @@ -388,7 +388,7 @@ String renderDucetOrder(List ordered, Map kangxiDecomp) { ..writeln('/// Dense BMP ranks per DUCET (Unicode 13.0) + pinned CLDR CJK radical-stroke.') ..writeln('/// Each code unit stores rank + 1; zero means absent.') ..writeln('/// Katakana sorts before hiragana (CLDR root tailoring).') - ..writeln('/// Generated by scripts/generate_ducet_ranks.dart — do not edit.') + ..writeln('/// Generated by scripts/codegen/generate_ducet_ranks.dart — do not edit.') ..writeln('library;') ..writeln() ..writeln('const String _ducetRanks ='); @@ -460,7 +460,7 @@ Future generateDucetRanks({ final sources = useBundledSources ? await loadBundledSources( bundledSourceDirectory: - bundledSourceDirectory ?? Directory.fromUri(Directory.current.uri.resolve('scripts/data/')), + bundledSourceDirectory ?? Directory.fromUri(Directory.current.uri.resolve('scripts/codegen/data/')), allKeysDescriptor: allKeysDescriptor, fractionalUcaDescriptor: fractionalUcaDescriptor, ) @@ -486,7 +486,7 @@ Future generateDucetRanks({ Future main(List args) async { if (args.length > 2) { - stderr.writeln('Usage: dart run scripts/generate_ducet_ranks.dart [allkeys.txt] [FractionalUCA.txt]'); + stderr.writeln('Usage: dart run scripts/codegen/generate_ducet_ranks.dart [allkeys.txt] [FractionalUCA.txt]'); exitCode = 64; return; } diff --git a/scripts/generate_hid_key_labels.dart b/scripts/codegen/generate_hid_key_labels.dart similarity index 93% rename from scripts/generate_hid_key_labels.dart rename to scripts/codegen/generate_hid_key_labels.dart index 63ac589c9..44ebae16c 100644 --- a/scripts/generate_hid_key_labels.dart +++ b/scripts/codegen/generate_hid_key_labels.dart @@ -1,13 +1,13 @@ /// Generates lib/data/hid_key_labels.dart from the curated offline HID catalog. /// /// Usage: -/// dart run scripts/generate_hid_key_labels.dart [input.json] [output.dart] +/// dart run scripts/codegen/generate_hid_key_labels.dart [input.json] [output.dart] library; import 'dart:convert'; import 'dart:io'; -const defaultHidKeyLabelsInput = 'scripts/data/hid_key_labels.json'; +const defaultHidKeyLabelsInput = 'scripts/codegen/data/hid_key_labels.json'; const defaultHidKeyLabelsOutput = 'lib/data/hid_key_labels.dart'; final class HidKeyLabel { @@ -99,8 +99,8 @@ HidKeyCatalog parseHidKeyLabelsCatalog(String source) { String renderHidKeyLabels(HidKeyCatalog catalog) { final output = StringBuffer() ..writeln( - '// Generated by dart run scripts/generate_hid_key_labels.dart from ' - 'scripts/data/hid_key_labels.json; do not edit by hand.', + '// Generated by dart run scripts/codegen/generate_hid_key_labels.dart from ' + 'scripts/codegen/data/hid_key_labels.json; do not edit by hand.', ) ..writeln() ..writeln('/// Human-readable labels for physical keyboard keys, keyed by USB HID usage code.') @@ -144,7 +144,7 @@ Future generateHidKeyLabels( Future main(List arguments) async { if (arguments.length > 2) { - stderr.writeln('Usage: dart run scripts/generate_hid_key_labels.dart [input.json] [output.dart]'); + stderr.writeln('Usage: dart run scripts/codegen/generate_hid_key_labels.dart [input.json] [output.dart]'); exitCode = 64; return; } diff --git a/scripts/generate_iso_639_data.dart b/scripts/codegen/generate_iso_639_data.dart similarity index 94% rename from scripts/generate_iso_639_data.dart rename to scripts/codegen/generate_iso_639_data.dart index 30b48f75e..d9bd1b3bd 100644 --- a/scripts/generate_iso_639_data.dart +++ b/scripts/codegen/generate_iso_639_data.dart @@ -1,13 +1,13 @@ /// Generates lib/data/iso_639_data.dart from the curated offline ISO 639 catalog. /// /// Usage: -/// dart run scripts/generate_iso_639_data.dart [input.json] [output.dart] +/// dart run scripts/codegen/generate_iso_639_data.dart [input.json] [output.dart] library; import 'dart:convert'; import 'dart:io'; -const defaultIso639Input = 'scripts/data/iso_639_codes.json'; +const defaultIso639Input = 'scripts/codegen/data/iso_639_codes.json'; const defaultIso639Output = 'lib/data/iso_639_data.dart'; final class Iso639CatalogEntry { @@ -110,8 +110,8 @@ Iso639Catalog parseIso639Catalog(String source) { String renderIso639Data(Iso639Catalog catalog) { final output = StringBuffer() ..writeln( - '// Generated by dart run scripts/generate_iso_639_data.dart from ' - 'scripts/data/iso_639_codes.json; do not edit by hand.', + '// Generated by dart run scripts/codegen/generate_iso_639_data.dart from ' + 'scripts/codegen/data/iso_639_codes.json; do not edit by hand.', ) ..writeln() ..writeln('class LanguageEntry {') @@ -181,7 +181,7 @@ Future generateIso639Data( Future main(List arguments) async { if (arguments.length > 2) { - stderr.writeln('Usage: dart run scripts/generate_iso_639_data.dart [input.json] [output.dart]'); + stderr.writeln('Usage: dart run scripts/codegen/generate_iso_639_data.dart [input.json] [output.dart]'); exitCode = 64; return; } diff --git a/scripts/generate_relay_protocol.py b/scripts/codegen/generate_relay_protocol.py similarity index 95% rename from scripts/generate_relay_protocol.py rename to scripts/codegen/generate_relay_protocol.py index d15ca6fc1..e748425d4 100755 --- a/scripts/generate_relay_protocol.py +++ b/scripts/codegen/generate_relay_protocol.py @@ -6,7 +6,7 @@ from __future__ import annotations import json from pathlib import Path -ROOT = Path(__file__).resolve().parent.parent +ROOT = Path(__file__).resolve().parents[2] SPEC_PATH = ROOT / "relay_protocol.json" DART_PATH = ROOT / "lib/watch_together/services/relay_protocol.g.dart" GO_PATH = ROOT / "server/relay_protocol_gen.go" @@ -35,7 +35,7 @@ def validated_id_pattern(spec: dict) -> str: def dart_source(spec: dict) -> str: id_pattern = validated_id_pattern(spec) lines = [ - "// Generated by scripts/generate_relay_protocol.py. Do not edit.", + "// Generated by scripts/codegen/generate_relay_protocol.py. Do not edit.", "", "abstract final class RelayProtocol {", ] @@ -74,7 +74,7 @@ def dart_source(spec: dict) -> str: def go_source(spec: dict) -> str: validated_id_pattern(spec) lines = [ - "// Code generated by scripts/generate_relay_protocol.py. DO NOT EDIT.", + "// Code generated by scripts/codegen/generate_relay_protocol.py. DO NOT EDIT.", "", "package main", "", diff --git a/scripts/test_generate_relay_protocol.py b/scripts/codegen/test_generate_relay_protocol.py similarity index 100% rename from scripts/test_generate_relay_protocol.py rename to scripts/codegen/test_generate_relay_protocol.py diff --git a/scripts/maestro_fixtures.py b/scripts/maestro/maestro_fixtures.py similarity index 100% rename from scripts/maestro_fixtures.py rename to scripts/maestro/maestro_fixtures.py diff --git a/scripts/maestro_jellyfin_proxy.py b/scripts/maestro/maestro_jellyfin_proxy.py similarity index 100% rename from scripts/maestro_jellyfin_proxy.py rename to scripts/maestro/maestro_jellyfin_proxy.py diff --git a/scripts/maestro_real_jellyfin.py b/scripts/maestro/maestro_real_jellyfin.py similarity index 99% rename from scripts/maestro_real_jellyfin.py rename to scripts/maestro/maestro_real_jellyfin.py index 2b0ae2494..1fd2cbff5 100755 --- a/scripts/maestro_real_jellyfin.py +++ b/scripts/maestro/maestro_real_jellyfin.py @@ -141,12 +141,12 @@ def _reset_managed_directory(path: Path) -> None: marker = path / _MANAGED_MARKER if not path.exists(): path.mkdir(parents=True) - marker.write_text("Managed by scripts/maestro_real_jellyfin.py\n", encoding="utf-8") + marker.write_text("Managed by scripts/maestro/maestro_real_jellyfin.py\n", encoding="utf-8") return if not marker.is_file(): if any(path.iterdir()): raise ValueError(f"Refusing to clear unmanaged media staging directory: {path}") - marker.write_text("Managed by scripts/maestro_real_jellyfin.py\n", encoding="utf-8") + marker.write_text("Managed by scripts/maestro/maestro_real_jellyfin.py\n", encoding="utf-8") return for child in path.iterdir(): if child == marker: diff --git a/scripts/prepare_maestro_media.py b/scripts/maestro/prepare_maestro_media.py similarity index 100% rename from scripts/prepare_maestro_media.py rename to scripts/maestro/prepare_maestro_media.py diff --git a/scripts/run_maestro.py b/scripts/maestro/run_maestro.py similarity index 99% rename from scripts/run_maestro.py rename to scripts/maestro/run_maestro.py index 4504c44de..523982943 100755 --- a/scripts/run_maestro.py +++ b/scripts/maestro/run_maestro.py @@ -18,7 +18,7 @@ import urllib.error import urllib.request -ROOT_DIR = Path(__file__).resolve().parent.parent +ROOT_DIR = Path(__file__).resolve().parents[2] APP_ID = "com.edde746.plezy" FAULTS = ("music-failure", "offline", "recovery") ANIMATION_SCALES = ( @@ -482,7 +482,7 @@ class MaestroRunner: self.proxy_process = subprocess.Popen( ( sys.executable, - str(ROOT_DIR / "scripts/maestro_jellyfin_proxy.py"), + str(ROOT_DIR / "scripts/maestro/maestro_jellyfin_proxy.py"), "--host", self.config.jellyfin_host, "--port", diff --git a/scripts/run_maestro_ci.py b/scripts/maestro/run_maestro_ci.py similarity index 100% rename from scripts/run_maestro_ci.py rename to scripts/maestro/run_maestro_ci.py diff --git a/scripts/test_maestro_flow_contracts.py b/scripts/maestro/test_maestro_flow_contracts.py similarity index 99% rename from scripts/test_maestro_flow_contracts.py rename to scripts/maestro/test_maestro_flow_contracts.py index 20ccfa6c0..fd8846548 100755 --- a/scripts/test_maestro_flow_contracts.py +++ b/scripts/maestro/test_maestro_flow_contracts.py @@ -9,7 +9,7 @@ import unittest from typing import Any -ROOT_DIR = Path(__file__).resolve().parents[1] +ROOT_DIR = Path(__file__).resolve().parents[2] def _mapping_separator(value: str) -> int | None: diff --git a/scripts/test_maestro_jellyfin_proxy.py b/scripts/maestro/test_maestro_jellyfin_proxy.py similarity index 100% rename from scripts/test_maestro_jellyfin_proxy.py rename to scripts/maestro/test_maestro_jellyfin_proxy.py diff --git a/scripts/test_maestro_real_jellyfin.py b/scripts/maestro/test_maestro_real_jellyfin.py similarity index 100% rename from scripts/test_maestro_real_jellyfin.py rename to scripts/maestro/test_maestro_real_jellyfin.py diff --git a/scripts/test_run_maestro.py b/scripts/maestro/test_run_maestro.py similarity index 96% rename from scripts/test_run_maestro.py rename to scripts/maestro/test_run_maestro.py index 4fbc332ed..59367342b 100755 --- a/scripts/test_run_maestro.py +++ b/scripts/maestro/test_run_maestro.py @@ -20,12 +20,13 @@ import run_maestro # noqa: E402 import run_maestro_ci # noqa: E402 -ROOT_DIR = Path(__file__).resolve().parent.parent +ROOT_DIR = Path(__file__).resolve().parents[2] SCRIPTS_DIR = ROOT_DIR / "scripts" LOCAL_SCRIPT_TEST_DISPATCHER = SCRIPTS_DIR / "ci_checks.sh" CI_SCRIPT_TEST_DISPATCHER = ROOT_DIR / ".github/workflows/ci.yml" # The guard roster both dispatchers above delegate to. It discovers the script -# tests by glob, so a new scripts/test_*.py is picked up without being listed. +# tests by glob, so a new test_*.py under scripts/ or scripts/*/ is picked up +# without being listed. GUARD_SCRIPT_TEST_DISPATCHER = SCRIPTS_DIR / "ci_guard_checks.sh" SCRIPT_TEST_DISPATCHERS = ( LOCAL_SCRIPT_TEST_DISPATCHER, @@ -61,7 +62,7 @@ def _is_main_guard(expression: ast.expr) -> bool: def _executable_script_tests() -> set[str]: executable = set() - for path in SCRIPTS_DIR.glob("test_*.py"): + for path in (*SCRIPTS_DIR.glob("test_*.py"), *SCRIPTS_DIR.glob("*/test_*.py")): module = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) if any(isinstance(node, ast.If) and _is_main_guard(node.test) for node in module.body): executable.add(path.name) @@ -72,10 +73,11 @@ def _dispatched_script_tests(path: Path) -> list[str]: dispatched = [] for line in path.read_text(encoding="utf-8").splitlines(): stripped = line.strip() - # `for guard_test in scripts/test_*.py; do` dispatches the whole roster. - loop = re.match(r"for\s+\w+\s+in\s+(scripts/test_[^;\s]*\.py)\s*;?\s*(?:do)?$", stripped) + # `for guard_test in scripts/test_*.py scripts/*/test_*.py; do` dispatches the whole roster. + loop = re.match(r"for\s+\w+\s+in\s+(scripts/test_\*\.py scripts/\*/test_\*\.py)\s*;?\s*(?:do)?$", stripped) if loop: - dispatched.extend(sorted(match.name for match in ROOT_DIR.glob(loop.group(1)))) + for pattern in loop.group(1).split(): + dispatched.extend(sorted(match.name for match in ROOT_DIR.glob(pattern))) continue try: command = shlex.split(stripped, comments=True) diff --git a/scripts/generate_windows_msix_assets.ps1 b/scripts/release/generate_windows_msix_assets.ps1 similarity index 97% rename from scripts/generate_windows_msix_assets.ps1 rename to scripts/release/generate_windows_msix_assets.ps1 index d96a8430e..59eef5734 100644 --- a/scripts/generate_windows_msix_assets.ps1 +++ b/scripts/release/generate_windows_msix_assets.ps1 @@ -4,7 +4,7 @@ $ErrorActionPreference = "Stop" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -$ProjectRoot = Split-Path -Parent $ScriptDir +$ProjectRoot = Split-Path -Parent (Split-Path -Parent $ScriptDir) Set-Location $ProjectRoot Add-Type -AssemblyName System.Drawing diff --git a/scripts/release.sh b/scripts/release/release.sh similarity index 93% rename from scripts/release.sh rename to scripts/release/release.sh index 34a89d80e..a88f28a31 100755 --- a/scripts/release.sh +++ b/scripts/release/release.sh @@ -3,7 +3,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$SCRIPT_DIR/.." +PROJECT_ROOT="$SCRIPT_DIR/../.." if [ -f "$PROJECT_ROOT/.env" ]; then set -a @@ -22,7 +22,7 @@ create_changelogs() { fi local version_code - version_code=$(python3 "$SCRIPT_DIR/pubspec_version.py" --build-number "$PROJECT_ROOT/pubspec.yaml") + version_code=$(python3 "$SCRIPT_DIR/../pubspec_version.py" --build-number "$PROJECT_ROOT/pubspec.yaml") local prompt="Below is a changelog for a cross-platform Flutter app (iOS, Android, macOS, Linux, Windows). Return ONLY the entries relevant to the given platform. Keep the same format (section headers + bullet points). If a section has no relevant entries, omit it entirely. If an entry is not platform-specific, include it. You MUST stay under the character limit. Aggressively drop less important entries and consolidate similar ones to fit. Count your output characters before responding. Output nothing else." local notes @@ -77,7 +77,7 @@ case "${1:-help}" in all) release_android && release_ios ;; clean) clean ;; help|--help|-h) - echo "Usage: ./scripts/release.sh " + echo "Usage: ./scripts/release/release.sh " echo "" echo "Commands:" echo " changelog Generate platform-specific changelogs from a notes file" diff --git a/scripts/upload_symbols.dart b/scripts/release/upload_symbols.dart similarity index 100% rename from scripts/upload_symbols.dart rename to scripts/release/upload_symbols.dart diff --git a/scripts/upload-symbols.ps1 b/scripts/upload-symbols.ps1 index ce40ba1c9..dde3381cc 100755 --- a/scripts/upload-symbols.ps1 +++ b/scripts/upload-symbols.ps1 @@ -16,10 +16,10 @@ $Root = Split-Path -Parent $ScriptDir Set-Location $Root if ([string]::IsNullOrEmpty($SourceRoot)) { - & dart run scripts/upload_symbols.dart $Platform + & dart run scripts/release/upload_symbols.dart $Platform } else { - & dart run scripts/upload_symbols.dart $Platform $SourceRoot + & dart run scripts/release/upload_symbols.dart $Platform $SourceRoot } if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE diff --git a/scripts/upload-symbols.sh b/scripts/upload-symbols.sh index c6ee2a55d..4bc7a6ca9 100755 --- a/scripts/upload-symbols.sh +++ b/scripts/upload-symbols.sh @@ -10,4 +10,4 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$ROOT" -exec dart run scripts/upload_symbols.dart "$@" +exec dart run scripts/release/upload_symbols.dart "$@" diff --git a/server/relay_protocol_gen.go b/server/relay_protocol_gen.go index b41836f05..c4814c690 100644 --- a/server/relay_protocol_gen.go +++ b/server/relay_protocol_gen.go @@ -1,4 +1,4 @@ -// Code generated by scripts/generate_relay_protocol.py. DO NOT EDIT. +// Code generated by scripts/codegen/generate_relay_protocol.py. DO NOT EDIT. package main diff --git a/test/scripts/check_analyzer_test.dart b/test/scripts/check_analyzer_test.dart index 9567f6410..15017a99c 100644 --- a/test/scripts/check_analyzer_test.dart +++ b/test/scripts/check_analyzer_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; -import '../../scripts/check_analyzer.dart'; +import '../../scripts/checks/check_analyzer.dart'; const _root = r'C:\repo'; const _allowedInfo = diff --git a/test/scripts/generate_ducet_ranks_test.dart b/test/scripts/generate_ducet_ranks_test.dart index e5dbf95d4..388e6c401 100644 --- a/test/scripts/generate_ducet_ranks_test.dart +++ b/test/scripts/generate_ducet_ranks_test.dart @@ -5,7 +5,7 @@ import 'package:crypto/crypto.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as path; -import '../../scripts/generate_ducet_ranks.dart'; +import '../../scripts/codegen/generate_ducet_ranks.dart'; void main() { late Directory temporaryDirectory; @@ -21,8 +21,8 @@ void main() { }); test('bundled gzip inputs have deterministic normalized headers and bytes', () { - final allKeys = File('scripts/data/${allKeysSource.bundledFileName}').readAsBytesSync(); - final fractional = File('scripts/data/${fractionalUcaSource.bundledFileName}').readAsBytesSync(); + final allKeys = File('scripts/codegen/data/${allKeysSource.bundledFileName}').readAsBytesSync(); + final fractional = File('scripts/codegen/data/${fractionalUcaSource.bundledFileName}').readAsBytesSync(); expect(allKeys.take(8), [0x1F, 0x8B, 0x08, 0, 0, 0, 0, 0]); expect(fractional.take(8), [0x1F, 0x8B, 0x08, 0, 0, 0, 0, 0]); diff --git a/test/scripts/generate_hid_key_labels_test.dart b/test/scripts/generate_hid_key_labels_test.dart index 1031f5d4e..70fc5489e 100644 --- a/test/scripts/generate_hid_key_labels_test.dart +++ b/test/scripts/generate_hid_key_labels_test.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; -import '../../scripts/generate_hid_key_labels.dart'; +import '../../scripts/codegen/generate_hid_key_labels.dart'; void main() { late String validSource; diff --git a/test/scripts/generate_iso_639_data_test.dart b/test/scripts/generate_iso_639_data_test.dart index a6a90b224..bae7326da 100644 --- a/test/scripts/generate_iso_639_data_test.dart +++ b/test/scripts/generate_iso_639_data_test.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; -import '../../scripts/generate_iso_639_data.dart'; +import '../../scripts/codegen/generate_iso_639_data.dart'; void main() { late String validSource; diff --git a/test/scripts/upload_symbols_test.dart b/test/scripts/upload_symbols_test.dart index a39564dc7..294afb490 100644 --- a/test/scripts/upload_symbols_test.dart +++ b/test/scripts/upload_symbols_test.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as path; -import '../../scripts/upload_symbols.dart'; +import '../../scripts/release/upload_symbols.dart'; void main() { late Directory repository; diff --git a/website/package.json b/website/package.json index ad3d02c08..69bb09a6c 100644 --- a/website/package.json +++ b/website/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "vite dev", "build": "vite build", - "audit": "python3 ../scripts/check_bun_audit.py --project . --baseline bun_audit_baseline.json", + "audit": "python3 ../scripts/checks/check_bun_audit.py --project . --baseline bun_audit_baseline.json", "preview": "vite preview", "test": "bun test", "prepare": "svelte-kit sync || echo ''",