Fix ignore rule parsing and loading edge cases #2840
Workflow file for this run
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: Build and Test | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths-ignore: | |
| - '**.md' | |
| - 'CLAUDE.md' | |
| - 'LICENSE' | |
| pull_request: | |
| branches: | |
| - main | |
| paths-ignore: | |
| - '**.md' | |
| - 'CLAUDE.md' | |
| - 'LICENSE' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| build: | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| test-framework: [net8.0, net9.0] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| # The Windows lane is still dominated by temp-heavy test I/O even after | |
| # the Defender exclusions from #394. Pinning TMP/TEMP to RUNNER_TEMP | |
| # moves Path.GetTempPath()-driven fixtures onto the same faster, already | |
| # CI-owned volume as the checkout workspace instead of the default user | |
| # profile temp under C:\Users\runneradmin\AppData\Local\Temp. | |
| # GITHUB_ENV is written before the Defender-exclusion step so the later | |
| # verification sees the actual effective TMP/TEMP/GetTempPath values. | |
| # Windows lane は #394 の Defender 除外後も temp-heavy なテスト I/O が | |
| # 支配的なため、TMP/TEMP を RUNNER_TEMP 配下へ固定する。これにより | |
| # Path.GetTempPath() を使う fixture 群が、既定の | |
| # C:\Users\runneradmin\AppData\Local\Temp ではなく checkout と同じ | |
| # CI 管理下の高速ボリュームを使う。Defender 除外 step より前に | |
| # GITHUB_ENV へ書き込むことで、後続の検証が実際の TMP/TEMP / | |
| # GetTempPath 値をそのまま確認できるようにする。 | |
| - name: Pin Windows TMP/TEMP to runner temp | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $tempRoot = Join-Path $env:RUNNER_TEMP "cdidx-temp" | |
| New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null | |
| "TMP=$tempRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| "TEMP=$tempRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| $env:TMP = $tempRoot | |
| $env:TEMP = $tempRoot | |
| Write-Host "Pinned Windows TMP/TEMP to: $tempRoot" | |
| Write-Host ".NET Path.GetTempPath() now resolves to: $([System.IO.Path]::GetTempPath())" | |
| # Windows Defender real-time scanning of every temp file created by the | |
| # test suite (temp projects, SQLite DBs, .cdidx directories, synthetic | |
| # source files) dominates the Windows lane of this matrix; see issue #394. | |
| # Excluding the workspace plus the effective temp roots before build/test | |
| # and the immutable NuGet global package cache keeps Defender from | |
| # repeatedly scanning the restore/build inputs that dotnet touches on | |
| # Windows. This also prevents silent regression if the runner image or | |
| # TMP/TEMP wiring changes again in the future. | |
| # We rely on -ExclusionPath only: -ExclusionProcess expects full image | |
| # paths, so bare filenames like 'dotnet.exe' would silently fail to take | |
| # effect. We include $env:TMP and the live [IO.Path]::GetTempPath() value | |
| # in addition to $env:TEMP / RUNNER_TEMP because .NET's Path.GetTempPath() | |
| # consults TMP before TEMP and may resolve to a path that differs from | |
| # $env:TEMP in pwsh. Paths are trimmed of trailing separators so the | |
| # Get-MpPreference -notcontains check matches the stored form exactly. | |
| # Every Add-MpPreference call uses -ErrorAction Stop, and a final | |
| # Get-MpPreference verification throws if any exclusion did not register | |
| # so the step fails loudly instead of degrading silently. The list of | |
| # effective exclusions is also echoed to the log so future investigations | |
| # of Windows lane variance can see exactly which paths were covered. | |
| # テストスイートが大量に作る一時ファイル(temp project / SQLite DB / | |
| # .cdidx / 合成ソース)を Windows Defender が都度スキャンする影響で | |
| # Windows lane だけ極端に遅くなる(issue #394)。build/test 前に | |
| # workspace と実効 temp root 群に加えて変更されない NuGet global package | |
| # cache も除外し、Windows で dotnet が繰り返し触る restore/build 入力を | |
| # Defender に再スキャンさせない。これで runner image や TMP/TEMP の | |
| # 配線が今後変わっても silent regression しないようにする。 | |
| # -ExclusionProcess はフルパスを要求する仕様のため、bare filename では | |
| # 黙って no-op になる。ここでは -ExclusionPath だけに寄せる。.NET の | |
| # Path.GetTempPath() は TMP → TEMP → USERPROFILE の順で参照し、pwsh の | |
| # $env:TEMP と異なるパスへ解決されうるため、$env:TMP と実行時の | |
| # [IO.Path]::GetTempPath() も追加でカバーする。末尾区切り文字を揃え、 | |
| # Get-MpPreference -notcontains の照合を保存形式と完全一致させる。 | |
| # 各呼び出しを -ErrorAction Stop で明示失敗させ、最後に Get-MpPreference | |
| # で登録を検証し、silent degradation を起こさないようにする。最終的に | |
| # 適用された除外パス一覧はログにも出力し、今後 Windows lane のばらつき | |
| # を調査するときにどのパスが対象になったかを追えるようにする。 | |
| - name: Exclude workspace and temp paths from Windows Defender (Windows only) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $candidates = @( | |
| "${{ github.workspace }}", | |
| $env:RUNNER_TEMP, | |
| $env:TEMP, | |
| $env:TMP, | |
| [System.IO.Path]::GetTempPath(), | |
| $env:NUGET_PACKAGES, | |
| (Join-Path $env:USERPROFILE ".nuget\packages"), | |
| (Join-Path $env:LOCALAPPDATA "NuGet\packages") | |
| ) | |
| $paths = $candidates | | |
| Where-Object { $_ } | | |
| ForEach-Object { $_.TrimEnd('\','/') } | | |
| Where-Object { $_ } | | |
| Select-Object -Unique | |
| Write-Host "Windows Defender exclusion candidates:" | |
| foreach ($path in $paths) { | |
| Write-Host " $path" | |
| } | |
| foreach ($path in $paths) { | |
| Add-MpPreference -ExclusionPath $path -ErrorAction Stop | |
| } | |
| $prefs = Get-MpPreference | |
| foreach ($path in $paths) { | |
| if ($prefs.ExclusionPath -notcontains $path) { | |
| throw "Windows Defender exclusion was not applied: $path" | |
| } | |
| } | |
| - name: Set up .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: | | |
| 8.0.x | |
| 9.0.x | |
| - name: Cache NuGet packages | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: | | |
| ~/.nuget/packages | |
| ~\AppData\Local\NuGet\packages | |
| key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json', '**/*.csproj') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nuget- | |
| # --locked-mode requires every resolved package to match the committed | |
| # packages.lock.json so an unexpected transitive bump (including silent | |
| # native swaps under SQLitePCLRaw.bundle_e_sqlite3) fails the build | |
| # instead of slipping into CI/release artifacts. See issue #1556. | |
| # --locked-mode は packages.lock.json と完全一致する解決を強制するため、 | |
| # SQLitePCLRaw.bundle_e_sqlite3 の native を含む推移依存の予期せぬ | |
| # bump が CI / release アーティファクトに紛れ込まずビルド失敗で気付ける。 | |
| # 詳細は issue #1556 参照。 | |
| - name: Restore dependencies | |
| run: dotnet restore CodeIndex.sln --locked-mode | |
| - name: Audit NuGet package vulnerabilities | |
| shell: pwsh | |
| run: | | |
| $auditOutput = dotnet list src/CodeIndex/CodeIndex.csproj package --vulnerable --include-transitive --no-restore 2>&1 | |
| $auditExitCode = $LASTEXITCODE | |
| $auditOutput | ForEach-Object { Write-Host $_ } | |
| if ($auditExitCode -ne 0) { | |
| exit $auditExitCode | |
| } | |
| if ($auditOutput -match '\b(High|Critical)\b') { | |
| throw "High or critical NuGet vulnerability detected." | |
| } | |
| - name: Build | |
| run: dotnet build tests/CodeIndex.Tests/CodeIndex.Tests.csproj --configuration Release --framework ${{ matrix.test-framework }} --no-restore | |
| - name: Test | |
| run: dotnet test tests/CodeIndex.Tests/CodeIndex.Tests.csproj --configuration Release --framework ${{ matrix.test-framework }} --no-build --nologo --logger "trx;LogFileName=test_results.trx" --results-directory ./TestResults | |
| - name: Summarize TRX telemetry | |
| if: always() | |
| run: dotnet run --project tools/CodeIndex.TestTelemetry -- summarize --results-directory ./TestResults --top 10 | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: TestResults-${{ matrix.os }}-${{ matrix.test-framework }} | |
| if-no-files-found: warn | |
| path: TestResults/**/*.trx | |
| - name: Publish | |
| if: matrix.os == 'ubuntu-latest' && matrix.test-framework == 'net8.0' | |
| run: dotnet publish src/CodeIndex/CodeIndex.csproj --configuration Release --no-build --output publish | |
| - name: Upload build artifact | |
| if: matrix.os == 'ubuntu-latest' && matrix.test-framework == 'net8.0' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: CodeIndex | |
| path: publish/** |