Add temporary compiler-driven migration script #22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: .NET 10 migration build | ||
| on: | ||
| push: | ||
| branches: | ||
| - agent/net10-mudblazor-migration | ||
| pull_request: | ||
| branches: | ||
| - magiccodingman/net10 | ||
| workflow_dispatch: | ||
| permissions: | ||
| contents: write | ||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repair branch | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: agent/net10-mudblazor-migration | ||
| - name: Apply compiler-driven migration fixes | ||
| shell: bash | ||
| run: | | ||
| python3 <<'PY' | ||
| from pathlib import Path | ||
| def replace(path: str, old: str, new: str) -> None: | ||
| file = Path(path) | ||
| text = file.read_text(encoding="utf-8-sig") | ||
| if old in text: | ||
| file.write_text(text.replace(old, new), encoding="utf-8") | ||
| dashboard = "TruthGate-Web/TruthGate-Web/Components/Shared/DashboardMetrics.razor" | ||
| replace(dashboard, " MatchBoundsToSize = true,\n", "") | ||
| replace(dashboard, ".AsChartDataSet()", "") | ||
| api_keys = "TruthGate-Web/TruthGate-Web/Components/Pages/Settings/ApiKeys.razor" | ||
| replace(api_keys, | ||
| 'Dialogs.ShowAsync("Add API Key", p,', | ||
| 'Dialogs.ShowAsync<AddApiKeyDialog>("Add API Key", p,') | ||
| replace(api_keys, | ||
| "if (result.Canceled || result.Data is not AddApiKeyDialog.AddApiKeyResult r) return;", | ||
| "if (result is null || result.Canceled || result.Data is not AddApiKeyDialog.AddApiKeyResult r) return;") | ||
| replace(api_keys, | ||
| "if (result.Canceled || result.Data is not bool yes || !yes) return;", | ||
| "if (result is null || result.Canceled || result.Data is not bool yes || !yes) return;") | ||
| ip_bans = "TruthGate-Web/TruthGate-Web/Components/Pages/Settings/IpBansPage.razor" | ||
| replace(ip_bans, | ||
| "LoadBansGridAsync(GridState<BanDto> state)", | ||
| "LoadBansGridAsync(GridState<BanDto> state, CancellationToken cancellationToken)") | ||
| replace(ip_bans, | ||
| "LoadWhitelistIpGridAsync(GridState<Whitelist> state)", | ||
| "LoadWhitelistIpGridAsync(GridState<Whitelist> state, CancellationToken cancellationToken)") | ||
| replace(ip_bans, | ||
| "LoadWhitelistPrefixGridAsync(GridState<Whitelist> state)", | ||
| "LoadWhitelistPrefixGridAsync(GridState<Whitelist> state, CancellationToken cancellationToken)") | ||
| replace(ip_bans, "Dialogs.ShowMessageBox(", "Dialogs.ShowMessageBoxAsync(") | ||
| replace(ip_bans, 'Sortable="false"', 'SortMode="SortMode.None"') | ||
| replace(ip_bans, | ||
| 'await Dialogs.ShowAsync<SearchIpDialog>("Search IP", parameters, new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small }).Result;', | ||
| 'var dialog = await Dialogs.ShowAsync<SearchIpDialog>("Search IP", parameters, new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small });\n await dialog.Result;') | ||
| replace(ip_bans, | ||
| 'await Dialogs.ShowAsync<AddBanDialog>("Add Ban", new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small }).Result;', | ||
| 'var dialog = await Dialogs.ShowAsync<AddBanDialog>("Add Ban", new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small });\n await dialog.Result;') | ||
| replace(ip_bans, | ||
| "DbFactory.CreateDbContextAsync();", | ||
| "DbFactory.CreateDbContextAsync(cancellationToken);") | ||
| replace(ip_bans, "q.CountAsync();", "q.CountAsync(cancellationToken);") | ||
| replace(ip_bans, ".ToListAsync();", ".ToListAsync(cancellationToken);") | ||
| simple_prompt = "TruthGate-Web/TruthGate-Web/Components/Shared/SimplePromptDialog.razor" | ||
| replace(simple_prompt, '<MudForm @ref="_form" Validation="Validate">', '<MudForm @ref="_form">') | ||
| replace(simple_prompt, | ||
| 'Required="@Required"\n Lines=', | ||
| 'Required="@Required"\n Validation="@(new Func<string?, string?>(ValidateValue))"\n Lines=') | ||
| replace(simple_prompt, | ||
| ''' private IEnumerable<string> Validate() | ||
| { | ||
| _error = null; | ||
| if (Required && string.IsNullOrWhiteSpace(_value)) | ||
| { | ||
| _error = "Please enter a value."; | ||
| yield return _error; | ||
| } | ||
| } | ||
| ''', | ||
| ''' private string? ValidateValue(string? value) | ||
| { | ||
| _error = null; | ||
| if (Required && string.IsNullOrWhiteSpace(value)) | ||
| return _error = "Please enter a value."; | ||
| return null; | ||
| } | ||
| ''') | ||
| replace(simple_prompt, "await _form.Validate();", "await _form.ValidateAsync();") | ||
| add_pinned = "TruthGate-Web/TruthGate-Web/Components/Shared/AddPinnedItemDialog.razor" | ||
| replace(add_pinned, | ||
| '<MudForm @ref="_form" Model="_model" Validation="Validate">', | ||
| '<MudForm @ref="_form" Model="_model">') | ||
| add_ipns = "TruthGate-Web/TruthGate-Web/Components/Shared/AddOrEditIpnsDialog.razor" | ||
| replace(add_ipns, | ||
| '<MudForm @ref="_form" Model="_model" Validation="Validate">', | ||
| '<MudForm @ref="_form" Model="_model">') | ||
| replace(add_ipns, | ||
| 'Required="true"\n Disabled="_isEdit" />', | ||
| 'Required="true"\n Validation="@(new Func<string?, string?>(ValidateName))"\n Disabled="_isEdit" />', | ||
| ) | ||
| # The second required/disabled field is the IPNS key; correct its validator after the global replacement. | ||
| replace(add_ipns, | ||
| 'Label="IPNS Key (k51… or /ipns/k51…)"\n @bind-Value="_model.Key"\n Immediate="true"\n Required="true"\n Validation="@(new Func<string?, string?>(ValidateName))"', | ||
| 'Label="IPNS Key (k51… or /ipns/k51…)"\n @bind-Value="_model.Key"\n Immediate="true"\n Required="true"\n Validation="@(new Func<string?, string?>(ValidateKey))"') | ||
| replace(add_ipns, | ||
| ''' private IEnumerable<string> Validate() | ||
| { | ||
| _error = null; | ||
| if (string.IsNullOrWhiteSpace(_model.Name)) | ||
| yield return "Name is required."; | ||
| if (string.IsNullOrWhiteSpace(_model.Key)) | ||
| yield return "IPNS key is required."; | ||
| var leaf = IpfsGateway.ToSafeLeaf(_model.Name); | ||
| if (leaf is null) | ||
| yield return "Invalid name."; | ||
| if (!_model.Key.StartsWith("k51") && !_model.Key.StartsWith("/ipns/")) | ||
| yield return "IPNS Key should look like k51… or /ipns/k51…"; | ||
| } | ||
| ''', | ||
| ''' private string? ValidateName(string? value) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(value)) | ||
| return "Name is required."; | ||
| return IpfsGateway.ToSafeLeaf(value) is null ? "Invalid name." : null; | ||
| } | ||
| private static string? ValidateKey(string? value) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(value)) | ||
| return "IPNS key is required."; | ||
| return value.StartsWith("k51", StringComparison.OrdinalIgnoreCase) || | ||
| value.StartsWith("/ipns/k51", StringComparison.OrdinalIgnoreCase) | ||
| ? null | ||
| : "IPNS Key should look like k51… or /ipns/k51…"; | ||
| } | ||
| ''') | ||
| replace(add_ipns, "private async void Submit()", "private async Task Submit()") | ||
| replace(add_ipns, "await _form.Validate();", "await _form.ValidateAsync();") | ||
| replace(add_ipns, | ||
| '<MudGrid Spacing="2" AlignItems="AlignItems.Center">', | ||
| '<MudGrid Spacing="2" Style="align-items:center;">') | ||
| search_ip = "TruthGate-Web/TruthGate-Web/Components/Pages/Settings/Shared/SearchIpDialog.razor" | ||
| replace(search_ip, '<MudForm @ref="_form" Validation="Validate">', '<MudForm @ref="_form">') | ||
| replace(search_ip, | ||
| 'Required="true"\n @onkeydown=', | ||
| 'Required="true"\n Validation="@(new Func<string?, string?>(ValidateIp))"\n @onkeydown=') | ||
| replace(search_ip, | ||
| ''' private IEnumerable<string> Validate() | ||
| { | ||
| _error = null; | ||
| if (string.IsNullOrWhiteSpace(_ip)) | ||
| { | ||
| _error = "Please enter an IP address."; | ||
| yield return _error; | ||
| yield break; | ||
| } | ||
| if (!IPAddress.TryParse(_ip, out _)) | ||
| { | ||
| _error = "Invalid IP format (v4 or v6 required)."; | ||
| yield return _error; | ||
| } | ||
| } | ||
| ''', | ||
| ''' private string? ValidateIp(string? value) | ||
| { | ||
| _error = null; | ||
| if (string.IsNullOrWhiteSpace(value)) | ||
| return _error = "Please enter an IP address."; | ||
| if (!IPAddress.TryParse(value, out _)) | ||
| return _error = "Invalid IP format (v4 or v6 required)."; | ||
| return null; | ||
| } | ||
| ''') | ||
| replace(search_ip, "await _form.Validate();", "await _form.ValidateAsync();") | ||
| replace(search_ip, 'Sortable="false"', 'SortMode="SortMode.None"') | ||
| domains = "TruthGate-Web/TruthGate-Web/Components/Pages/Settings/Domains.razor" | ||
| replace(domains, 'Class="truncate-text" Title="@context.RedirectUrl"', 'Class="truncate-text" title="@context.RedirectUrl"') | ||
| replace(domains, | ||
| 'Color="Color.Primary" Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.Link"', | ||
| 'Color="Color.Primary" Variant="Variant.Outlined" Icon="@Icons.Material.Filled.Link"') | ||
| replace(domains, | ||
| 'Color="Color.Primary" Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.Fingerprint"', | ||
| 'Color="Color.Primary" Variant="Variant.Outlined" Icon="@Icons.Material.Filled.Fingerprint"') | ||
| PY | ||
| if ! git diff --quiet; then | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git add TruthGate-Web/TruthGate-Web/Components/Shared/DashboardMetrics.razor \ | ||
| TruthGate-Web/TruthGate-Web/Components/Pages/Settings/ApiKeys.razor \ | ||
| TruthGate-Web/TruthGate-Web/Components/Pages/Settings/IpBansPage.razor \ | ||
| TruthGate-Web/TruthGate-Web/Components/Shared/SimplePromptDialog.razor \ | ||
| TruthGate-Web/TruthGate-Web/Components/Shared/AddPinnedItemDialog.razor \ | ||
| TruthGate-Web/TruthGate-Web/Components/Shared/AddOrEditIpnsDialog.razor \ | ||
| TruthGate-Web/TruthGate-Web/Components/Pages/Settings/Shared/SearchIpDialog.razor \ | ||
| TruthGate-Web/TruthGate-Web/Components/Pages/Settings/Domains.razor | ||
| git commit -m "Finish MudBlazor 9 form and component migrations" | ||
| git push origin HEAD:agent/net10-mudblazor-migration | ||
| fi | ||
| - name: Setup .NET 10 | ||
| uses: actions/setup-dotnet@v4 | ||
| with: | ||
| dotnet-version: 10.0.x | ||
| - name: Restore | ||
| run: dotnet restore TruthGate-IPFS.sln | ||
| - name: Build | ||
| id: build | ||
| shell: bash | ||
| run: | | ||
| set +e | ||
| dotnet build TruthGate-IPFS.sln --configuration Release --no-restore > net10-build.log 2>&1 | ||
| status=$? | ||
| echo "status=$status" >> "$GITHUB_OUTPUT" | ||
| tail -n 200 net10-build.log | ||
| exit 0 | ||
| - name: Upload build diagnostics | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: net10-build-log | ||
| path: net10-build.log | ||
| if-no-files-found: error | ||
| - name: Report build failure | ||
| if: steps.build.outputs.status != '0' | ||
| run: exit 1 | ||