diff --git a/.gitattributes b/.gitattributes index 905a8ead7..10cf9919c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ src/InfiniFrame.Native/Dependencies/* linguist-vendored -src/InfiniFrame.Native/Dependencies/**/* linguist-vendored \ No newline at end of file +src/InfiniFrame.Native/Dependencies/**/* linguist-vendored + +*.sh text eol=lf \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 49bbe5474..56c3e2bb5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -20,3 +20,7 @@ updates: directory: "/" schedule: interval: "weekly" + commit-message: + prefix: "Chore(deps): " + include: "scope" + diff --git a/.github/scripts/bump_version.py b/.github/scripts/bump_version.py index 0dc70227e..526ae5b27 100644 --- a/.github/scripts/bump_version.py +++ b/.github/scripts/bump_version.py @@ -6,6 +6,7 @@ import xml.etree.ElementTree as Et from pathlib import Path from typing import Final, Literal, Never +import json # Resolve paths from the repository root: .github/scripts -> repo root is three levels up. REPO_ROOT: Final[Path] = Path(__file__).parent.parent.parent @@ -14,7 +15,6 @@ VERSION_PATTERN: Final[re.Pattern[str]] = re.compile(r"^\d+\.\d+\.\d+(-preview\.\d+)?$") BumpPart = Literal["major", "minor", "patch", "preview"] - def fail(message: str) -> Never: print(message) raise SystemExit(1) @@ -82,6 +82,60 @@ def update_cmake_version(cmake_path: Path, new_version: str) -> None: fail("Error: Could not find InfiniFrame.Native version in CMakeLists.txt") cmake_path.write_text(updated, encoding="utf-8") +def update_package_json_version(pkg_path: Path, new_version: str) -> None: + """ + Updates: + - package.json "version" + - optionally replaces version placeholders inside scripts + """ + data = json.loads(pkg_path.read_text(encoding="utf-8")) + + # Update top-level version + if "version" in data: + data["version"] = new_version + + # Optional: update version strings inside scripts + scripts = data.get("scripts", {}) + for key, value in scripts.items(): + if isinstance(value, str): + scripts[key] = _replace_version_in_string(value, new_version) + + data["scripts"] = scripts + + pkg_path.write_text( + json.dumps(data, indent=2, ensure_ascii=False) + "\n", + encoding="utf-8", + ) + +def _replace_version_in_string(text: str, new_version: str) -> str: + """ + Replace common version patterns inside arbitrary strings. + Extend this as needed per repo conventions. + """ + # Example patterns you might have in scripts: + # - --version 1.2.3 + # - v1.2.3 + # - 1.2.3-preview.4 + + text = re.sub( + r"\d+\.\d+\.\d+(?:-preview\.\d+)?", + new_version, + text, + ) + return text + +def update_all_package_json_files(repo_root: Path, new_version: str) -> None: + """ + Recursively updates all package.json files in the repository. + """ + for pkg_path in repo_root.rglob("package.json"): + # skip certain folders just in case + if "node_modules" in pkg_path.parts: + continue + if "InfiniFrame.Native" in pkg_path.parts: + continue + + update_package_json_version(pkg_path, new_version) def main() -> int: if len(sys.argv) < 2: @@ -122,6 +176,7 @@ def main() -> int: version_elem.text = new_version tree.write(FILE, encoding="utf-8", xml_declaration=True) update_cmake_version(CMAKE_FILE, new_version) + update_all_package_json_files(REPO_ROOT, new_version) print(f"Bumped version: {old_version} -> {new_version}") print(new_version) # Output for GitHub Actions to capture diff --git a/.github/scripts/test_bump_version.py b/.github/scripts/test_bump_version.py index bd3317721..d60098b58 100644 --- a/.github/scripts/test_bump_version.py +++ b/.github/scripts/test_bump_version.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 from __future__ import annotations +import json import sys +import tempfile from pathlib import Path import pytest @@ -10,7 +12,12 @@ if str(SCRIPT_DIR) not in sys.path: sys.path.insert(0, str(SCRIPT_DIR)) -from bump_version import bump, validate_version +from bump_version import ( + bump, + validate_version, + update_package_json_version, + _replace_version_in_string, +) @pytest.mark.parametrize( @@ -49,3 +56,37 @@ def test_bump_unknown_part_raises_value_error() -> None: with pytest.raises(ValueError): # noinspection PyTypeChecker bump("1.2.3", "banana") + +def test_replace_version_in_string() -> None: + assert _replace_version_in_string( + "build --version 1.2.3", + "9.8.7", + ) == "build --version 9.8.7" + + assert _replace_version_in_string( + "tool v1.2.3-preview.4 run", + "0.0.1", + ) == "tool v0.0.1 run" + +def test_update_package_json_version_updates_version_and_scripts() -> None: + with tempfile.TemporaryDirectory() as tmp: + pkg_path = Path(tmp) / "package.json" + + original = { + "name": "test", + "version": "1.0.0", + "scripts": { + "build": "echo 1.0.0", + "deploy": "echo deploying v1.0.0-preview.3", + }, + } + + pkg_path.write_text(json.dumps(original), encoding="utf-8") + + update_package_json_version(pkg_path, "2.3.4") + + updated = json.loads(pkg_path.read_text(encoding="utf-8")) + + assert updated["version"] == "2.3.4" + assert updated["scripts"]["build"] == "echo 2.3.4" + assert updated["scripts"]["deploy"] == "echo deploying v2.3.4" diff --git a/.github/scripts/update_native_vendor_deps.py b/.github/scripts/update_native_vendor_deps.py index 113a55ccd..471733ee1 100644 --- a/.github/scripts/update_native_vendor_deps.py +++ b/.github/scripts/update_native_vendor_deps.py @@ -5,6 +5,7 @@ import json import os import urllib.request +import urllib.error from pathlib import Path from typing import Any @@ -39,27 +40,31 @@ def download_file(url: str, destination: Path, token: str) -> None: with urllib.request.urlopen(request, timeout=120) as response: destination.write_bytes(response.read()) - -def get_latest_release(repo: str, token: str) -> tuple[str, dict[str, str]]: +def get_latest_release(repo: str, token: str) -> tuple[str | None, dict[str, str]]: url = f"https://api.github.com/repos/{repo}/releases/latest" - payload = request_json(url, token) + try: + payload = request_json(url, token) + except urllib.error.HTTPError as e: + if e.code == 404: + # No releases → fallback mode + return None, {} + raise tag = payload.get("tag_name") if not isinstance(tag, str) or not tag: - raise RuntimeError(f"Missing tag_name for {repo}") + return None, {} assets_payload = payload.get("assets", []) - if not isinstance(assets_payload, list): - raise RuntimeError(f"Unexpected assets payload for {repo}") - assets: dict[str, str] = {} - for asset in assets_payload: - if not isinstance(asset, dict): - continue - name = asset.get("name") - download_url = asset.get("browser_download_url") - if isinstance(name, str) and isinstance(download_url, str): - assets[name] = download_url + + if isinstance(assets_payload, list): + for asset in assets_payload: + if not isinstance(asset, dict): + continue + name = asset.get("name") + download_url = asset.get("browser_download_url") + if isinstance(name, str) and isinstance(download_url, str): + assets[name] = download_url return tag, assets @@ -92,6 +97,10 @@ def update_manifest(manifest_path: Path, check_only: bool) -> int: latest_tag, latest_assets = get_latest_release(repo, token) + # If no releases, stick to manifest tag (or empty) + if latest_tag is None: + latest_tag = current_tag or "" + has_missing_files = False for entries in (assets, source_files, licenses): for asset_entry in entries: @@ -104,7 +113,8 @@ def update_manifest(manifest_path: Path, check_only: bool) -> int: if has_missing_files: break - needs_update = latest_tag != current_tag + has_release = latest_tag is not None and latest_tag != "" + needs_update = has_release and latest_tag != current_tag needs_download = needs_update or has_missing_files if not needs_download: @@ -117,22 +127,23 @@ def update_manifest(manifest_path: Path, check_only: bool) -> int: if check_only: continue - for asset_entry in assets: - if not isinstance(asset_entry, dict): - raise RuntimeError(f"Library {name} has invalid asset entry") - - asset_name = asset_entry.get("asset") - destination = asset_entry.get("destination") - if not isinstance(asset_name, str) or not isinstance(destination, str): - raise RuntimeError(f"Library {name} has invalid asset definition") - - download_url = latest_assets.get(asset_name) - if not download_url: - raise RuntimeError(f"Asset '{asset_name}' not found in latest release for {repo}") - - destination_path = REPO_ROOT / destination - print(f" downloading {asset_name} -> {destination}") - download_file(download_url, destination_path, token) + if latest_assets: + for asset_entry in assets: + if not isinstance(asset_entry, dict): + raise RuntimeError(f"Library {name} has invalid asset entry") + + asset_name = asset_entry.get("asset") + destination = asset_entry.get("destination") + if not isinstance(asset_name, str) or not isinstance(destination, str): + raise RuntimeError(f"Library {name} has invalid asset definition") + + download_url = latest_assets.get(asset_name) + if not download_url: + raise RuntimeError(f"Asset '{asset_name}' not found in latest release for {repo}") + + destination_path = REPO_ROOT / destination + print(f" downloading {asset_name} -> {destination}") + download_file(download_url, destination_path, token) for source_entry in source_files: if not isinstance(source_entry, dict): diff --git a/.github/workflows/shared-testing-windows-playwright.yml b/.github/workflows/shared-testing-windows-playwright.yml index a816e964c..cbcb96fd7 100644 --- a/.github/workflows/shared-testing-windows-playwright.yml +++ b/.github/workflows/shared-testing-windows-playwright.yml @@ -1,4 +1,5 @@ name: "Shared: Windows Playwright Tests" + on: workflow_call: inputs: @@ -16,24 +17,74 @@ on: type: string required: true +permissions: + contents: read + checks: write + statuses: write + jobs: + windows-playwright-pending: + name: CI Testing - Windows Playwright (Pending) + runs-on: windows-latest + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ inputs.checkout_ref != '' && inputs.checkout_ref || (inputs.pr_number != '' && format('refs/pull/{0}/merge', inputs.pr_number) || github.ref) }} + fetch-depth: 1 + + - name: Set Playwright Check Pending + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # noinspection UndefinedAction + uses: ./.github/actions/sync-check + # noinspection UndefinedParamsPresent + with: + repo: ${{ github.repository }} + sha: ${{ inputs.target_sha }} + context: CI Testing - Windows Playwright + state: pending + status-description: Windows Playwright tests in progress + target-url: ${{ inputs.workflow_url }} + allow-status-422: 'true' + windows-playwright: - name: CI Testing - Windows Playwright + name: CI Testing - Windows Playwright ${{ matrix.project.name }} runs-on: windows-latest timeout-minutes: 75 + + needs: windows-playwright-pending + + strategy: + fail-fast: false + matrix: + project: + - name: WebApp Vue + path: InfiniFrameTests.Playwright.WebApp.Vue + sourceFolder: tests/InfiniFrameTests.Playwright.WebApp.Vue/Sources/infiniframe-playwright-vue + - name: WebApp React + path: InfiniFrameTests.Playwright.WebApp.React + sourceFolder: tests/InfiniFrameTests.Playwright.WebApp.React/Sources/infiniframe-playwright-react + - name: Blazor MudBlazor + path: InfiniFrameTests.Playwright.BlazorWebView.MudBlazor + sourceFolder: "" + env: TARGET_SHA: ${{ inputs.target_sha }} WORKFLOW_URL: ${{ inputs.workflow_url }} + permissions: contents: read statuses: write checks: write - + steps: - name: Checkout uses: actions/checkout@v6 with: - ref: ${{ inputs.checkout_ref != '' && inputs.checkout_ref || (inputs.pr_number != '' && inputs.pr_number != null && format('refs/pull/{0}/merge', inputs.pr_number) || github.ref) }} + ref: ${{ inputs.checkout_ref != '' && inputs.checkout_ref || (inputs.pr_number != '' && format('refs/pull/{0}/merge', inputs.pr_number) || github.ref) }} fetch-depth: 1 - name: Set Playwright Check Pending @@ -45,9 +96,9 @@ jobs: with: repo: ${{ github.repository }} sha: ${{ inputs.target_sha }} - context: CI Testing - Windows Playwright + context: CI Testing - Windows Playwright ${{ matrix.project.name }} state: pending - status-description: Windows Playwright tests in progress (net8.0, net9.0, net10.0) + status-description: Windows Playwright tests in progress target-url: ${{ inputs.workflow_url }} allow-status-422: 'true' @@ -59,33 +110,31 @@ jobs: 9.x 10.x - - name: Cache NuGet - uses: actions/cache@v5 - with: - path: ~\.nuget\packages - key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/Directory.Packages.props') }} - restore-keys: | - ${{ runner.os }}-nuget- - - name: Setup Native dependencies # noinspection UndefinedAction uses: ./.github/actions/setup-dependencies-native # noinspection UndefinedParamsPresent with: apt-cache-version: 1.0 - brew-cache-key: ${{ matrix.os }}-${{ matrix.arch }}-brew-native-${{ hashFiles('.github/actions/setup-dependencies-native/action.yml', '.github/workflows/shared-testing-windows-playwright.yml') }} - brew-restore-key: ${{ matrix.os }}-${{ matrix.arch }}-brew-native- + brew-cache-key: ${{ runner.os }}-brew-native-${{ hashFiles('.github/actions/setup-dependencies-native/action.yml') }} + brew-restore-key: ${{ runner.os }}-brew-native- + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: 20 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' - name: Prepare WebView2 Cache Directory shell: pwsh run: New-Item -ItemType Directory -Path ".cache\webview2" -Force | Out-Null - name: Cache WebView2 - id: cache-webview2 uses: actions/cache@v5 with: path: .cache\webview2 - key: webview2-installer-${{ runner.os }} + key: webview2-installer-${{ runner.os }}-v1 - name: Install WebView2 Runtime shell: pwsh @@ -95,66 +144,79 @@ jobs: Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile $installer } Start-Process -FilePath $installer -ArgumentList "/silent", "/install" -Wait + + - name: NPM Install + if: ${{ matrix.project.sourceFolder != '' }} + shell: pwsh + run: | + $dir = "${{ matrix.project.sourceFolder }}" + + if ([string]::IsNullOrWhiteSpace($dir)) { + throw "sourceFolder is empty for ${{ matrix.project.name }}" + } + + Write-Host "Installing npm dependencies in $dir" + + if (!(Test-Path $dir)) { + throw "Directory not found: $dir" + } + + Push-Location $dir + npm ci + Pop-Location - name: Restore - run: dotnet restore InfiniFrame.GitHubActions.Testing.Playwright.slnf /p:NoWarn=NU1503 + run: | + dotnet restore InfiniFrame.GitHubActions.Testing.Playwright.slnf ` + /p:NoWarn=NU1503 - - name: Build Release + - name: Build Native run: | dotnet build src/InfiniFrame.Native/InfiniFrame.Native.proj ` --configuration Release ` --no-restore ` /p:SolutionDir=$GITHUB_WORKSPACE/ - dotnet build InfiniFrame.GitHubActions.Testing.Playwright.slnf ` + - name: Build Tests + run: | + dotnet build tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj ` --configuration Release ` --no-restore ` + --framework net8.0 ` + /p:SolutionDir=$GITHUB_WORKSPACE/ + + dotnet build tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj ` + --configuration Release ` + --no-restore ` + --framework net9.0 ` + /p:SolutionDir=$GITHUB_WORKSPACE/ + + dotnet build tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj ` + --configuration Release ` + --no-restore ` + --framework net10.0 ` /p:SolutionDir=$GITHUB_WORKSPACE/ - - - name: Cache Playwright Browsers - id: cache-playwright - uses: actions/cache@v5 - with: - path: ~\AppData\Local\ms-playwright - key: playwright-${{ runner.os }}-${{ hashFiles('tests/InfiniFrameTests.Playwright.WebApp.React/InfiniFrameTests.Playwright.WebApp.React.csproj', 'tests/InfiniFrameTests.Playwright.WebApp.Vue/InfiniFrameTests.Playwright.WebApp.Vue.csproj', 'tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor.csproj') }} - restore-keys: | - playwright-${{ runner.os }}- - name: Install Playwright Browsers - if: steps.cache-playwright.outputs.cache-hit != 'true' - working-directory: tests/InfiniFrameTests.Playwright.WebApp.Vue shell: pwsh run: | - $scripts = @( - "bin/Release/net10.0/playwright.ps1", - "bin/Release/net9.0/playwright.ps1", - "bin/Release/net8.0/playwright.ps1" - ) - $script = $scripts | Where-Object { Test-Path $_ } | Select-Object -First 1 - if (-Not $script) { - throw "Playwright install script not found in bin/Release/(net8.0|net9.0|net10.0)" + $script = "tests/${{ matrix.project.path }}/bin/Release/net10.0/playwright.ps1" + + if (-not (Test-Path $script)) { + throw "Playwright script not found: $script" } - + pwsh $script install - name: Verify Native Binaries shell: pwsh run: | - $frameworks = @("net8.0", "net9.0", "net10.0") - $projects = @( - "tests/InfiniFrameTests.Playwright.WebApp.React", - "tests/InfiniFrameTests.Playwright.WebApp.Vue", - "tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor" - ) - foreach ($framework in $frameworks) { - foreach ($project in $projects) { - $binPath = "$project/bin/Release/$framework" - foreach ($dll in @("InfiniFrame.Native.dll", "WebView2Loader.dll")) { - $fullPath = Join-Path $binPath $dll - if (-Not (Test-Path $fullPath)) { - throw "$dll missing in $binPath" - } - } + $binPath = "tests/${{ matrix.project.path }}/bin/Release/net10.0" + + foreach ($dll in @("InfiniFrame.Native.dll", "WebView2Loader.dll")) { + $fullPath = Join-Path $binPath $dll + if (-Not (Test-Path $fullPath)) { + throw "$dll missing in $binPath" } } @@ -165,73 +227,78 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env['ACTIONS_RUNTIME_TOKEN']); core.exportVariable('ACTIONS_RESULTS_URL', process.env['ACTIONS_RESULTS_URL']); - - name: Run Playwright tests (all frameworks/projects) - id: playwright_all - continue-on-error: true + - name: Run Playwright tests NET 8.0 shell: pwsh run: | - $frameworks = @( - "net8.0", - "net9.0", - "net10.0" - ) - - $projects = @( - "tests/InfiniFrameTests.Playwright.WebApp.Vue", - "tests/InfiniFrameTests.Playwright.WebApp.React", - "tests/InfiniFrameTests.Playwright.BlazorWebView.MudBlazor" - ) - - $failed = @() - - foreach ($framework in $frameworks) { - foreach ($project in $projects) { - Write-Host "" - Write-Host "▶ Running $project ($framework)" - - dotnet test --project $project ` - --configuration Release ` - --no-build ` - --no-restore ` - --framework $framework - - if ($LASTEXITCODE -ne 0) { - Write-Host "❌ Failed: $project ($framework)" - $failed += "$project ($framework)" - } else { - Write-Host "✅ Passed: $project ($framework)" - } - } - } - - if ($failed.Count -gt 0) { - $msg = "Windows Playwright tests failed: $($failed -join ', ')" - "failure_message=$msg" >> $env:GITHUB_OUTPUT - "success_message=" >> $env:GITHUB_OUTPUT - - Write-Host "❌ $msg" - exit 1 - } - - $msg = "Windows Playwright tests passed (all frameworks/projects)" - "success_message=$msg" >> $env:GITHUB_OUTPUT - "failure_message=" >> $env:GITHUB_OUTPUT - - Write-Host "✅ All tests passed" - + dotnet test ` + --project tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj ` + --configuration Release ` + --no-build ` + --no-restore ` + --framework net8.0 + + - name: Run Playwright tests NET 9.0 + shell: pwsh + run: | + dotnet test ` + --project tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj ` + --configuration Release ` + --no-build ` + --no-restore ` + --framework net9.0 + + - name: Run Playwright tests NET 10.0 + shell: pwsh + run: | + dotnet test ` + --project tests/${{ matrix.project.path }}/${{ matrix.project.path }}.csproj ` + --configuration Release ` + --no-build ` + --no-restore ` + --framework net10.0 - name: Update Playwright Check if: always() env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # noinspection UndefinedAction, UndefinedParamsPresent + # noinspection UndefinedAction uses: ./.github/actions/sync-check-finalize # noinspection UndefinedParamsPresent with: repo: ${{ github.repository }} sha: ${{ inputs.target_sha }} - context: CI Testing - Windows Playwright + context: CI Testing - Windows Playwright ${{ matrix.project.name }} target-url: ${{ inputs.workflow_url }} job-status: ${{ job.status }} - success-description: ${{ steps.playwright_all.outputs.success_message }} - failure-description: ${{ steps.playwright_all.outputs.failure_message }} + success-description: Windows Playwright tests passed + failure-description: Windows Playwright tests failed + + windows-playwright-finalize: + name: CI Testing - Windows Playwright (Finalize) + runs-on: windows-latest + timeout-minutes: 10 + + needs: windows-playwright + if: always() + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ inputs.checkout_ref != '' && inputs.checkout_ref || (inputs.pr_number != '' && format('refs/pull/{0}/merge', inputs.pr_number) || github.ref) }} + fetch-depth: 1 + + - name: Set Playwright Check Final Status + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # noinspection UndefinedAction + uses: ./.github/actions/sync-check-finalize + # noinspection UndefinedParamsPresent + with: + repo: ${{ github.repository }} + sha: ${{ inputs.target_sha }} + context: CI Testing - Windows Playwright + target-url: ${{ inputs.workflow_url }} + job-status: ${{ needs.windows-playwright.result == 'success' && 'success' || 'failure' }} + success-description: Windows Playwright tests passed + failure-description: Windows Playwright tests failed diff --git a/.gitignore b/.gitignore index fe2ddda26..041f3c111 100644 --- a/.gitignore +++ b/.gitignore @@ -348,6 +348,7 @@ healthchecksdb /src/InfiniFrame.Native/cmake-build-debug/ /src/InfiniFrame.Native/cmake-build-debug-linux/ /src/InfiniFrame.Native/cmake-build-debug-windows/ +/src/InfiniFrame.Native/Embedded/InfiniFrameHostJs/InfiniFrameHostJs.cpp # wwwroot folders from js web based projects /examples/InfiniFrameExample.WebApp.React/wwwroot/ diff --git a/.run/docker-compose-linux.sh.run.xml b/.run/docker-compose-linux.sh.run.xml index 51e1c02f0..2cc12f377 100644 --- a/.run/docker-compose-linux.sh.run.xml +++ b/.run/docker-compose-linux.sh.run.xml @@ -1,6 +1,6 @@  - - + \ No newline at end of file diff --git a/.run/docker-linux-run-blazorwebview.sh.run.xml b/.run/docker-linux-run-blazorwebview.sh.run.xml new file mode 100644 index 000000000..4749efb00 --- /dev/null +++ b/.run/docker-linux-run-blazorwebview.sh.run.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.run/docker-run-linux-playwright.sh.run.xml b/.run/docker-linux-run-playwright.sh.run.xml similarity index 78% rename from .run/docker-run-linux-playwright.sh.run.xml rename to .run/docker-linux-run-playwright.sh.run.xml index 2e99f6b49..759f838ef 100644 --- a/.run/docker-run-linux-playwright.sh.run.xml +++ b/.run/docker-linux-run-playwright.sh.run.xml @@ -1,6 +1,6 @@  - -