chore(scripts): group scripts into checks, codegen, maestro and release subdirectories

scripts/ had ~80 flat files. Entry points (ci_*.sh, codegen.sh, run_tests.sh, format_native.sh, setup_hooks.sh, upload-symbols.*) and the shared pubspec_version.py stay at the root; checkers, generators, maestro tooling and release tooling move into subdirectories with their tests. Updated every reference: workflow steps, guard-test glob, Docker COPY paths and .dockerignore whitelist, website audit path, dart test imports, and regenerated the five outputs whose headers embed generator paths.
This commit is contained in:
edde746
2026-08-17 01:40:54 +02:00
parent e47b4e0a73
commit f622ba8efe
86 changed files with 137 additions and 131 deletions
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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: |
+1 -1
View File
@@ -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/
+7 -7
View File
@@ -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 <adb-serial>` 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.
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 =
+1 -1
View File
@@ -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 = <int, String>{
+1 -1
View File
@@ -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;
@@ -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;
@@ -19,7 +19,7 @@ final _allowedDiagnostics = <AnalyzerDiagnostic>{
Future<void> 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 {
@@ -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:
@@ -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"
@@ -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"})
@@ -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")
@@ -17,11 +17,11 @@ void main(List<String> 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 <path>]');
stderr.writeln('Usage: dart run scripts/checks/check_icon_consistency.dart [--root <path>]');
exitCode = 64;
return;
}
@@ -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"
@@ -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:
@@ -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:
@@ -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] = []
@@ -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]")
@@ -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]")
@@ -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}$")
@@ -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}")
@@ -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"
@@ -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"
@@ -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"
@@ -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
@@ -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):
@@ -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 = (
@@ -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"
@@ -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)
@@ -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"
@@ -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"
@@ -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), [])
@@ -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"
@@ -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. "
@@ -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"
@@ -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",
@@ -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:
+4 -4
View File
@@ -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"
+13 -13
View File
@@ -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
+1 -1
View File
@@ -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
+5 -5
View File
@@ -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 "$@"
@@ -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<int> ordered, Map<int, int> 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<void> 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<void> generateDucetRanks({
Future<void> main(List<String> 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;
}
@@ -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<void> generateHidKeyLabels(
Future<void> main(List<String> 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;
}
@@ -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<void> generateIso639Data(
Future<void> main(List<String> 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;
}
@@ -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",
"",
@@ -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:
@@ -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",
@@ -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:
@@ -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)
@@ -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
@@ -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 <command>"
echo "Usage: ./scripts/release/release.sh <command>"
echo ""
echo "Commands:"
echo " changelog <file> Generate platform-specific changelogs from a notes file"
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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 "$@"
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 =
+3 -3
View File
@@ -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]);
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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 ''",