diff --git a/.github/workflows/nightly-fix-finder.lock.yml b/.github/workflows/nightly-fix-finder.lock.yml index 1cfa01c5a2a..7205b1ac9f2 100644 --- a/.github/workflows/nightly-fix-finder.lock.yml +++ b/.github/workflows/nightly-fix-finder.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"78def8a16b90fd0d251d2c6798504c9a328d8d06b3ae8d44314cc7b17bfaf307","body_hash":"3a02a1154cfbc9b2a905016f3dc26298740948ad85fea1ea99a8ceb738a05430","compiler_version":"v0.86.2","strict":true,"agent_id":"copilot","agent_model":"gpt-5.6-sol","engine_versions":{"copilot":"1.0.79"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"0852b7327a990b7893686d406b9530eebd44134779216ce97ae737195aee974d","body_hash":"13962e8be95c0d0ab375b3f20cbe01d27a5597d54ac307588678d031ef4f0ab8","compiler_version":"v0.86.2","strict":true,"agent_id":"copilot","agent_model":"gpt-5.6-sol","engine_versions":{"copilot":"1.0.79"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"6aab9e5b5c91c615506061f09bedd81a23babe3c","version":"v0.86.2"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.44","digest":"sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.44@sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44","digest":"sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44@sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.44","digest":"sha256:c064d15974f7c933ec7d3f7b4038f4fd203547b3154bdc821afd379144887eff","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.44@sha256:c064d15974f7c933ec7d3f7b4038f4fd203547b3154bdc821afd379144887eff"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.44","digest":"sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.44@sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.9","digest":"sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196"},{"image":"ghcr.io/github/github-mcp-server:v1.9.0","digest":"sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e","pinned_image":"ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e"}],"has_pull_request":true} # This file was automatically generated by gh-aw (v0.86.2). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -88,8 +88,8 @@ on: - 03-region-directives - 04-missing-xml-docs - 05-general-mistakes - - 06-unused-using-directives - - 07-asynctask-log-property + - 06-completely-dead-code + - 07-stale-xamarin-references - 08-string-literal-error-messages required: false type: choice @@ -512,9 +512,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_934a23c591d74812_EOF' - {"create_pull_request":{"allowed_base_branches":["main"],"allowed_files":["src/**","tests/**","Documentation/**"],"auto_close_issue":false,"draft":false,"fallback_as_issue":false,"labels":["automated","code-quality"],"max":1,"max_patch_files":20,"max_patch_size":4096,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md"],"protected_files_policy":"request_review","title_prefix":"[fix-finder] "},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_934a23c591d74812_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_122171cf9e615f0b_EOF' + {"create_pull_request":{"allowed_base_branches":["main"],"allowed_files":["src/**","tests/**","Documentation/**","README.md"],"auto_close_issue":false,"draft":false,"fallback_as_issue":false,"labels":["automated","code-quality"],"max":1,"max_patch_files":20,"max_patch_size":4096,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md"],"protected_files_policy":"request_review","title_prefix":"[fix-finder] "},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_122171cf9e615f0b_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -1750,7 +1750,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.android.com,*.azure.com,*.githubusercontent.com,*.google.com,*.googleapis.com,*.gradle-enterprise.cloud,*.gvt1.com,*.microsoft.com,*.vsassets.io,*.vsblob.vsassets.io,adoptium.net,aka.ms,android.googlesource.com,api.adoptium.net,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.foojay.io,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.apache.org,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,cdn.azul.com,central.sonatype.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,develocity.apache.org,dist.nuget.org,dl.google.com,dlcdn.apache.org,docs.github.com,docs.oracle.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.java.net,download.oracle.com,downloads.gradle-dn.com,ge.spockframework.org,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,gradle.org,gstatic.com,host.docker.internal,httpbin.org,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,maven-central.storage-download.googleapis.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,pkgs.dev.azure.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,repo.gradle.org,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo1.maven.org,repository.apache.org,s.symcb.com,s.symcd.com,scans-in.gradle.com,security.ubuntu.com,services.gradle.org,source.dot.net,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.java.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"allowed_files\":[\"src/**\",\"tests/**\",\"Documentation/**\"],\"auto_close_issue\":false,\"draft\":false,\"fallback_as_issue\":false,\"labels\":[\"automated\",\"code-quality\"],\"max\":1,\"max_patch_files\":20,\"max_patch_size\":4096,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"[fix-finder] \"},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"allowed_files\":[\"src/**\",\"tests/**\",\"Documentation/**\",\"README.md\"],\"auto_close_issue\":false,\"draft\":false,\"fallback_as_issue\":false,\"labels\":[\"automated\",\"code-quality\"],\"max\":1,\"max_patch_files\":20,\"max_patch_size\":4096,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"[fix-finder] \"},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/nightly-fix-finder.md b/.github/workflows/nightly-fix-finder.md index 5fad24ad0ee..2f72264a3df 100644 --- a/.github/workflows/nightly-fix-finder.md +++ b/.github/workflows/nightly-fix-finder.md @@ -21,8 +21,8 @@ on: - "03-region-directives" - "04-missing-xml-docs" - "05-general-mistakes" - - "06-unused-using-directives" - - "07-asynctask-log-property" + - "06-completely-dead-code" + - "07-stale-xamarin-references" - "08-string-literal-error-messages" required: false type: choice @@ -63,6 +63,7 @@ safe-outputs: - src/** - tests/** - Documentation/** + - README.md auto-close-issue: false draft: false fallback-as-issue: false @@ -205,6 +206,12 @@ Before changing files, score the proposed fix on a 0–30 scale across three dim **Threshold: ≥ 22 / 30 to implement.** Additionally, **safety must be ≥ 6** — any fix scoring lower on safety must be declined regardless of total. The SkiaSharp project that pioneered this rubric confirmed it correctly stops risky behavior-change fixes that otherwise look attractive. +For `08-string-literal-error-messages`, a change that only moves an existing +diagnostic into the English resource system, preserving its meaning, arguments, +severity, and control flow, scores at least 8 for safety and 8 for scope. Treat +the required resource, checked-in resource accessor, source call site, and XA +message documentation as one cohesive fix. + If the proposal scores below either bar, call `noop` with a message that includes the score breakdown and why you declined. Do not modify files. ## Phase 4: Implement and Validate @@ -212,7 +219,7 @@ If the proposal scores below either bar, call `noop` with a message that include Implement the fix yourself: 1. Make the smallest complete change that resolves the verified problem. -2. Follow all repository instructions and existing style. Never modify generated files, non-English localization files, or unrelated code. +2. Follow all repository instructions and existing style. Never modify generated files, non-English localization files, or unrelated code. The only generated-file exception is a checked-in `Resources.Designer.cs` accessor required by `08-string-literal-error-messages`; change only the accessor for the new English resource. 3. Add or update a focused test when behavior changes or a regression test is practical. 4. Bootstrap the repository toolchain with `./build.sh Prepare` before validation. This installs the repository-pinned .NET 11 SDK under the active `bin/{Debug|Release}/dotnet` configuration and prepares generated build prerequisites. Then run the smallest targeted build or test command through `./dotnet-local.sh`; never use the runner's system `dotnet` or lower `DotNetTargetFrameworkVersion` to work around missing tooling. A PR requires successful validation; if the fix cannot be validated in this environment, revert only your own changes and call `noop`. 5. Review `git diff` for accidental or unrelated edits. diff --git a/.github/workflows/nightly-fix-finder/06-completely-dead-code.sh b/.github/workflows/nightly-fix-finder/06-completely-dead-code.sh new file mode 100644 index 00000000000..7d4861c43e2 --- /dev/null +++ b/.github/workflows/nightly-fix-finder/06-completely-dead-code.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Category: Completely Dead Code + +cat << 'GUIDANCE' +## Category: Completely Dead Code + +### What to look for +Code under `src/` that can be proven to have no callers and no execution path: +- Private members or nested types whose identifier has no references +- Branches that are unconditionally unreachable +- Disabled `#if false` blocks with no documented reason to retain them + +The proof must account for every target framework and build configuration in +the owning project. "Probably unused" is not sufficient. + +### How to fix +Delete the smallest complete dead member or block, then remove imports or helper +code that became unused as a direct result. Add or update a focused test only +when removal changes a code path that can be exercised. + +Before deleting a private member, search the full repository for its identifier +and inspect attributes, interfaces, partial declarations, build targets, and +generated-code inputs that could reference it indirectly. + +### What NOT to flag +- Public, protected, or internal API +- JNI callbacks, `[Register]` members, Java peer members, or native entry points +- MSBuild task types or properties, serialization members, reflection targets, + dependency-injection entry points, or members referenced by name +- Platform-, runtime-, ABI-, or TFM-specific code that is live in another build +- Compatibility code retained for older Android or .NET versions +- Generated files, submodules under `external/`, or test code +- Code that is merely redundant, inefficient, or currently untested +GUIDANCE + +echo "" +echo "## Scan Data" +echo "### Explicitly unreachable or disabled constructs" +OBVIOUS=$(grep -rnP '^\s*(#if\s+false\b|if\s*\(\s*false\s*\)|while\s*\(\s*false\s*\))' \ + --include='*.cs' \ + --exclude-dir=obj --exclude-dir=bin \ + --exclude-dir=Tests --exclude-dir=Test --exclude-dir=tests \ + --exclude='*.generated.cs' --exclude='*.Designer.cs' --exclude='*.g.cs' \ + src/ 2>/dev/null \ + | shuf -n 20) +if [ -n "$OBVIOUS" ]; then + echo "$OBVIOUS" +else + echo "None found" +fi + +echo "" +echo "### Private declarations whose identifier appears only once in repository C# source" +SINGLETON_TOKENS=$(mktemp) +PRIVATE_DECLARATIONS=$(mktemp) +trap 'rm -f "$SINGLETON_TOKENS" "$PRIVATE_DECLARATIONS"' EXIT + +grep -rhoP '\b[A-Za-z_][A-Za-z0-9_]*\b' \ + --include='*.cs' \ + --exclude-dir=obj --exclude-dir=bin \ + src/ 2>/dev/null \ + | sort | uniq -c \ + | awk '$1 == 1 { print $2 }' > "$SINGLETON_TOKENS" + +grep -rnPo '^\s*private\s+(?:(?:static|readonly|const|async|unsafe|partial|volatile|new|sealed)\s+)*(?:[A-Za-z_][A-Za-z0-9_.<>,?\[\]]*\s+)+\K[A-Za-z_][A-Za-z0-9_]*(?=\s*(?:\(|\{|=>|=|;))' \ + --include='*.cs' \ + --exclude-dir=obj --exclude-dir=bin \ + --exclude-dir=Tests --exclude-dir=Test --exclude-dir=tests \ + --exclude='*.generated.cs' --exclude='*.Designer.cs' --exclude='*.g.cs' \ + src/ 2>/dev/null > "$PRIVATE_DECLARATIONS" + +CANDIDATES=$(awk -F: ' + NR == FNR { + singleton [$1] = 1 + next + } + length ($NF) >= 4 && singleton [$NF] { + print + } +' "$SINGLETON_TOKENS" "$PRIVATE_DECLARATIONS" | shuf -n 20) + +if [ -n "$CANDIDATES" ]; then + echo "$CANDIDATES" +else + echo "None found" +fi diff --git a/.github/workflows/nightly-fix-finder/06-unused-using-directives.sh b/.github/workflows/nightly-fix-finder/06-unused-using-directives.sh deleted file mode 100644 index 23ae089b3a2..00000000000 --- a/.github/workflows/nightly-fix-finder/06-unused-using-directives.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# Category: Unused Using Directives - -cat << 'GUIDANCE' -## Category: Unused Using Directives - -### What to look for -Files in `src/` with >10 `using` directives that likely contain unused ones. -A high count alone isn't a bug — the agent MUST open the file and confirm -specific directives are unused before changing it. - -### How to fix -Remove the unused `using` directives. Keep `global using`s and conditional -ones (`#if NETxx`) intact. - -### What NOT to flag -- Files where every `using` is genuinely referenced -- Generated files (`*.generated.cs`, `*.Designer.cs`) -- Test files where `using` directives are often intentional setup -GUIDANCE - -echo "" -echo "## Scan Data" -echo "### Files with many using directives in shipped code (potential cleanup, sample)" -for f in $(find src -name '*.cs' -type f \ - ! -path '*/obj/*' ! -path '*/bin/*' \ - ! -path '*/Tests/*' ! -path '*/Test/*' ! -path '*/tests/*' \ - ! -name '*.generated.cs' ! -name '*.Designer.cs' \ - ! -name '*Test.cs' ! -name '*Tests.cs' \ - 2>/dev/null | shuf | head -30); do - count=$(grep -c '^using ' "$f" 2>/dev/null || true) - if [ "${count:-0}" -gt 10 ]; then - echo " $f: $count using directives" - fi -done diff --git a/.github/workflows/nightly-fix-finder/07-asynctask-log-property.sh b/.github/workflows/nightly-fix-finder/07-asynctask-log-property.sh deleted file mode 100644 index ce00cdf51f2..00000000000 --- a/.github/workflows/nightly-fix-finder/07-asynctask-log-property.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash -# Category: AsyncTask Log Property Misuse - -cat << 'GUIDANCE' -## Category: AsyncTask Log Property Misuse - -### What to look for -Direct uses of the `Log` property (`Log.LogMessage`, `Log.LogError`, -`Log.LogWarning`, `LogErrorFromException`, etc.) inside MSBuild task -classes that derive from `AsyncTask`. The `Log` property is marked -`[Obsolete]` on `AsyncTask` because calling it from a background thread -can hang Visual Studio. - -### How to fix -Swap the call to the matching thread-safe helper on `AsyncTask`: - -| Direct (forbidden) | Thread-safe replacement | -|---------------------------------|--------------------------------------| -| `Log.LogMessage (...)` | `LogMessage (...)` | -| `Log.LogError (...)` | `LogCodedError ("XA####", Properties.Resources.XA####, ...)` | -| `Log.LogWarning (...)` | `LogCodedWarning ("XA####", Properties.Resources.XA####, ...)` | -| `Log.LogErrorFromException (e)` | `LogCodedError ("XA####", Properties.Resources.XA####, ..., e.Message)` -- fold the exception detail into the localized error, or `throw;` to propagate -| `Log.LogDebugMessage (...)` | `LogDebugMessage (...)` | - -Error / warning messages MUST come from `Properties.Resources` (e.g. -`Properties.Resources.XA0143`) with a stable `XA####` code — never log a -raw `ex.Message` as the user-facing string. If no suitable resource -entry exists yet, that means a new `XA####` code is needed in -`Resources.resx` and `Resources.Designer.cs`; in that case prefer a -`noop` over inventing an inline English string. - -### What NOT to flag -- Classes that derive from `Task` or `AndroidTask` directly (not - `AsyncTask`) — `Log` is fine on those. -- Generated files (`*.generated.cs`, `*.Designer.cs`, `*.g.cs`). -- Test code under `Tests/` / `tests/`. -- The `AsyncTask` base class itself, and the `AsyncTaskExtensions` file. -- The agent MUST open the file and verify the enclosing class actually - derives (directly or transitively) from `AsyncTask` before changing it. -GUIDANCE - -echo "" -echo "## Scan Data" -echo "### Files deriving from AsyncTask" -echo "(Note: this regex matches DIRECT derivation only. Some tasks reach" -echo " AsyncTask through an intermediate base -- e.g. Aapt2Compile / Aapt2Link" -echo " extend \`Aapt2 : AsyncTask\` -- and are not enumerated here. That is" -echo " acceptable for a one-fix-per-night sampler. The agent MUST still verify" -echo " transitive AsyncTask derivation when reading the candidate file.)" -ASYNC_FILES=$(grep -rlP ':\s*AsyncTask\b' \ - --include='*.cs' \ - --exclude-dir=obj --exclude-dir=bin \ - --exclude-dir=Tests --exclude-dir=Test --exclude-dir=tests \ - --exclude='*.generated.cs' --exclude='*.Designer.cs' --exclude='*.g.cs' \ - src/ 2>/dev/null || true) - -if [ -z "$ASYNC_FILES" ]; then - echo " (none found — scan cannot proceed)" - exit 0 -fi - -echo "$ASYNC_FILES" | sed 's/^/ /' - -echo "" -echo "### Direct \`Log.Log*\` calls inside AsyncTask-derived files (sample)" -echo "$ASYNC_FILES" \ - | xargs grep -nHP '\bLog\.(LogMessage|LogError|LogWarning|LogErrorFromException|LogDebugMessage|LogCodedError|LogCodedWarning)\b' 2>/dev/null \ - | shuf | head -20 \ - || echo "None found" diff --git a/.github/workflows/nightly-fix-finder/07-stale-xamarin-references.sh b/.github/workflows/nightly-fix-finder/07-stale-xamarin-references.sh new file mode 100644 index 00000000000..24cc9d79163 --- /dev/null +++ b/.github/workflows/nightly-fix-finder/07-stale-xamarin-references.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# Category: Stale Xamarin and Mono References + +cat << 'GUIDANCE' +## Category: Stale Xamarin and Mono References + +### What to look for +Comments in source code and current documentation that still describe the +product as Xamarin or Xamarin.Android, or link to the archived +`xamarin/xamarin-android` or `mono/mono` repositories, when the text clearly +predates the .NET 6 transition. + +Good candidates state current behavior, ownership, setup, or contribution +instructions using obsolete names or locations. Verify that the replacement is +accurate before editing: +- Current product name: `.NET for Android` +- Current repository: `dotnet/android` +- Runtime source or issues formerly in `mono/mono`: locate the exact equivalent + in `dotnet/runtime`; do not mechanically rewrite the repository name + +### How to fix +Update only the stale prose or link. Preserve the original technical meaning, +verify replacement URLs exist, and keep the diff focused. If no accurate modern +equivalent can be established, call `noop`. + +### What NOT to flag +- Historical release notes, previous-release documentation, migration guides, + or text intentionally describing Xamarin-era behavior +- Namespaces, assembly names, package IDs, MSBuild properties, paths, API names, + compatibility switches, test fixtures, or literal values containing Xamarin +- Copyright, attribution, license, third-party notice, or provenance text +- References to the Mono runtime that remain technically current +- Old repository links retained to identify the original issue, commit, or + source provenance when no equivalent moved to the current repository +- Generated files or submodules under `external/` +GUIDANCE + +echo "" +echo "## Scan Data" +if [ ! -d Documentation ] || [ ! -d src ] || [ ! -f README.md ]; then + echo "Required scan paths are missing." >&2 + exit 1 +fi + +echo "### Links to the pre-.NET 6 mono/mono repository" +MONO_LINKS=$(grep -rnEi 'github\.com/mono/mono' \ + --include='*.md' --include='*.cs' --include='*.cpp' --include='*.cc' \ + --include='*.h' --include='*.hh' --include='*.java' \ + --exclude='previous-releases.md' --exclude='AssemblyInfo.cs' \ + --exclude-dir=release-notes \ + --exclude-dir=obj --exclude-dir=bin \ + --exclude-dir=Tests --exclude-dir=Test --exclude-dir=tests \ + Documentation/ src/ README.md 2>/dev/null \ + | grep -vEi 'github\.com/mono/mono/blob/2019-10/mcs/tools/aprofutil/README\.md' \ + | shuf -n 20) +if [ -n "$MONO_LINKS" ]; then + echo "$MONO_LINKS" +else + echo "None found" +fi + +echo "" +echo "### Stale Xamarin documentation and repository links" +DOC_MATCHES=$(grep -rnEi \ + 'https?://(docs\.microsoft\.com/xamarin/android|developer\.xamarin\.com/(guides/)?android|github\.com/xamarin/xamarin-android/wiki)' \ + --include='*.md' \ + --exclude='previous-releases.md' \ + --exclude-dir=release-notes \ + --exclude-dir=obj --exclude-dir=bin \ + Documentation/ README.md 2>/dev/null \ + | shuf -n 20) +if [ -n "$DOC_MATCHES" ]; then + echo "$DOC_MATCHES" +else + echo "None found" +fi + +echo "" +echo "### Current prose that still describes the product as Xamarin" +PROSE_MATCHES=$(grep -rnEi \ + 'Xamarin(\.Android| Android)? (project|application|app|build system|product|tooling|runtime|SDK|provides|supports|uses|requires|inserts|will|can|does|is|was|source tree)' \ + --include='*.md' \ + --exclude='previous-releases.md' \ + --exclude-dir=release-notes \ + Documentation/ 2>/dev/null \ + | grep -vEi 'Xamarin\.Android\.(slnx|Build|Tools|Runtime)|Xamarin\.(AndroidX|Forms|Kotlin|Google)|legacy|classic|Added in|Removed in|support ended|upgrade|migration' \ + | shuf -n 10) + +SOURCE_PROSE=$(grep -rnEi \ + 'Xamarin(\.Android| Android)? (project|application|app|build system|product|tooling|runtime|SDK|provides|supports|uses|requires|inserts|will|can|does|is|was|source tree)' \ + --include='*.cs' --include='*.cpp' --include='*.cc' \ + --include='*.h' --include='*.hh' --include='*.java' \ + --exclude-dir=obj --exclude-dir=bin \ + --exclude-dir=Tests --exclude-dir=Test --exclude-dir=tests \ + --exclude='*.generated.cs' --exclude='*.Designer.cs' --exclude='*.g.cs' \ + --exclude='AssemblyInfo.cs' \ + src/ 2>/dev/null \ + | grep -E ':[0-9]+:.*(//|/\*|\*)' \ + | grep -vEi 'Xamarin\.Android\.(slnx|Build|Tools|Runtime)|Xamarin\.(AndroidX|Forms|Kotlin|Google)|legacy|classic|Copyright|@xamarin\.com|/dev/null \ - | grep '"' \ - | grep -v 'Properties\.Resources' \ - | shuf | head -20 || echo "None found" +echo "### Hardcoded error strings in resource-backed shipped projects (sample)" +RESOURCE_ROOTS=$(find src -path '*/Properties/Resources.resx' -type f 2>/dev/null \ + | sed 's#/Properties/Resources\.resx$##') + +if [ -z "$RESOURCE_ROOTS" ]; then + echo "None found" + exit 0 +fi + +MATCHES=$( + while IFS= read -r root; do + grep -rnP '(?/dev/null + grep -rnP 'Log(?:CodedError|CodedWarning)\s*\(\s*"[^"]+"\s*,\s*\$?"' \ + --include='*.cs' \ + --exclude-dir=obj --exclude-dir=bin \ + --exclude-dir=Tests --exclude-dir=Test --exclude-dir=tests \ + --exclude='*.generated.cs' --exclude='*.Designer.cs' \ + --exclude='*Test.cs' --exclude='*Tests.cs' \ + "$root" 2>/dev/null + done <<< "$RESOURCE_ROOTS" \ + | grep -vE '(^|[^A-Za-z0-9_])([A-Za-z_][A-Za-z0-9_.]*\.)?Resources\.' \ + | grep -vE 'LogError[[:space:]]*\([[:space:]]*\$?"XA(\{|[0-9])' \ + | grep -vE 'Log\.Log(Error|Warning)[[:space:]]*\([[:space:]]*""[[:space:]]*,[[:space:]]*"XA[0-9]+"' \ + | grep -vE '\$"\{[^}]+\}"' \ + | grep -vP '\$"(?:\{[^}]+\}[ .,:;-]*)+"' \ + | grep -vE '\$?"\{(message|error|text|output|ex|rex)(\.|})' \ + | grep -vE '"\{[0-9]+\}([ :.-]*\{[0-9]+\})+"' \ + | grep -v '"{0}"' \ + | shuf -n 20 +) + +if [ -n "$MATCHES" ]; then + echo "$MATCHES" +else + echo "None found" +fi