diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 7bb2739..34e0d3d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,10 +3,16 @@ "isRoot": true, "tools": { "fantomas": { - "version": "6.0.5", + "version": "7.0.3", "commands": [ "fantomas" ] + }, + "fsharp-analyzers": { + "version": "0.33.1", + "commands": [ + "fsharp-analyzers" + ] } } } diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..e75d028 --- /dev/null +++ b/.envrc @@ -0,0 +1,23 @@ +use flake +DOTNET_PATH=$(readlink "$(which dotnet)") +SETTINGS_FILE=$(find . -maxdepth 1 -type f -name '*.sln.DotSettings.user') +MSBUILD=$(realpath "$(find "$(dirname "$DOTNET_PATH")/../share/dotnet/sdk" -maxdepth 2 -type f -name MSBuild.dll)") +if [ -f "$SETTINGS_FILE" ] ; then + xmlstarlet ed --inplace \ + -N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \ + -N x="http://schemas.microsoft.com/winfx/2006/xaml" \ + -N s="clr-namespace:System;assembly=mscorlib" \ + -N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \ + --update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue']" \ + --value "$(realpath "$(dirname "$DOTNET_PATH")/../share/dotnet/dotnet")" \ + "$SETTINGS_FILE" + + xmlstarlet ed --inplace \ + -N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \ + -N x="http://schemas.microsoft.com/winfx/2006/xaml" \ + -N s="clr-namespace:System;assembly=mscorlib" \ + -N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \ + --update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue']" \ + --value "$MSBUILD" \ + "$SETTINGS_FILE" +fi diff --git a/.github/workflows/dotnet-core.yaml b/.github/workflows/dotnet-core.yaml deleted file mode 100644 index cda12f2..0000000 --- a/.github/workflows/dotnet-core.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: .NET Core - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 6.0.100 - - name: Install dependencies - run: dotnet restore FicroKanSharp.sln - - name: Build - run: dotnet build FicroKanSharp.sln --configuration Release --no-restore - - name: Test - run: dotnet test FicroKanSharp.sln --no-restore --verbosity normal - - name: Install Fantomas - run: dotnet tool restore - - name: Run Fantomas - run: dotnet tool run fantomas --check . diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml new file mode 100644 index 0000000..f9832b4 --- /dev/null +++ b/.github/workflows/dotnet.yaml @@ -0,0 +1,148 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json +name: .NET + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + DOTNET_NOLOGO: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + NUGET_XMLDOC_MODE: '' + DOTNET_MULTILEVEL_LOOKUP: 0 + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # so that NerdBank.GitVersioning has access to history + - name: Install Nix + uses: cachix/install-nix-action@v30 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Restore dependencies + run: nix develop --command dotnet restore + - name: Build + run: nix develop --command dotnet build --no-restore --configuration Release + - name: Test + run: nix develop --command dotnet test + + build-nix: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v30 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Build + run: nix build + - name: Reproducibility check + run: nix build --rebuild + + check-dotnet-format: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v30 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Run Fantomas + run: nix run .#fantomas -- --check . + + check-nix-format: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v30 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Run Alejandra + run: nix develop --command alejandra --check . + + flake-check: + name: Check flake + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Install Nix + uses: cachix/install-nix-action@v30 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Flake check + run: nix flake check + + linkcheck: + name: Check links + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Install Nix + uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Run link checker + run: nix develop --command markdown-link-check README.md + + nuget-pack: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # so that NerdBank.GitVersioning has access to history + - name: Install Nix + uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Restore dependencies + run: nix develop --command dotnet restore + - name: Build + run: nix develop --command dotnet build --no-restore --configuration Release + - name: Pack + run: nix develop --command dotnet pack --configuration Release + - name: Upload NuGet artifact + uses: actions/upload-artifact@v4 + with: + name: nuget-package + path: FicroKanSharp/bin/Release/FicroKanSharp.*.nupkg + + expected-pack: + needs: [nuget-pack] + runs-on: ubuntu-latest + steps: + - name: Download NuGet artifact + uses: actions/download-artifact@v4 + with: + name: nuget-package + path: packed + - name: Check NuGet contents + # Verify that there is exactly one nupkg in the artifact that would be NuGet published + run: if [[ $(find packed -maxdepth 1 -name 'FicroKanSharp.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi + + all-required-checks-complete: + if: ${{ always() }} + needs: [check-dotnet-format, check-nix-format, build, build-nix, flake-check, nuget-pack, expected-pack] + runs-on: ubuntu-latest + steps: + - uses: G-Research/common-actions/check-required-lite@2b7dc49cb14f3344fbe6019c14a31165e258c059 + with: + needs-context: ${{ toJSON(needs) }} + diff --git a/.gitignore b/.gitignore index 9d2addd..8dd3643 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ riderModule.iml *.user *.DotSettings .DS_Store +.direnv/ +result diff --git a/FicroKanSharp.Test/Arithmetic.fs b/FicroKanSharp.Test/Arithmetic.fs index 11f0c8f..efd88e7 100644 --- a/FicroKanSharp.Test/Arithmetic.fs +++ b/FicroKanSharp.Test/Arithmetic.fs @@ -2,11 +2,12 @@ namespace FicroKanSharp.Test open FicroKanSharp open FsUnitTyped -open Xunit +open NUnit.Framework +[] module Arithmetic = - [] + [] let ``Arithmetic example, untyped`` () = let zero : Term = Term.Symbol ("zero", []) @@ -76,7 +77,7 @@ module Arithmetic = | Zero | Succ of TypedTerm - [] + [] let ``Arithmetic example, typed`` () = let rec ofInt (n : int) : TypedTerm = if n = 0 then diff --git a/FicroKanSharp.Test/FicroKanSharp.Test.fsproj b/FicroKanSharp.Test/FicroKanSharp.Test.fsproj index fa1fcbf..dee0ad6 100644 --- a/FicroKanSharp.Test/FicroKanSharp.Test.fsproj +++ b/FicroKanSharp.Test/FicroKanSharp.Test.fsproj @@ -1,7 +1,7 @@ - net6.0 + net9.0 @@ -14,10 +14,10 @@ - - - - + + + + diff --git a/FicroKanSharp.Test/Geometry.fs b/FicroKanSharp.Test/Geometry.fs index c9a52b8..4a71ae8 100644 --- a/FicroKanSharp.Test/Geometry.fs +++ b/FicroKanSharp.Test/Geometry.fs @@ -4,8 +4,9 @@ namespace FicroKanSharp.Test open FsUnitTyped open FicroKanSharp -open Xunit +open NUnit.Framework +[] module Geometry = type Point<'a> = Point of TypedTerm<'a> * TypedTerm<'a> @@ -34,7 +35,7 @@ module Geometry = |> TypedTerm.Goal.callFresh |> TypedTerm.Goal.callFresh - [] + [] let ``Geometry example from Learn Prolog Now`` () = Line ( TypedTerm.Literal (Point (TypedTerm.Literal 1, TypedTerm.Literal 1)), diff --git a/FicroKanSharp.Test/Recursive.fs b/FicroKanSharp.Test/Recursive.fs index 7ad5697..9df200e 100644 --- a/FicroKanSharp.Test/Recursive.fs +++ b/FicroKanSharp.Test/Recursive.fs @@ -1,9 +1,10 @@ namespace FicroKanSharp.Test -open Xunit +open NUnit.Framework open FsUnitTyped open FicroKanSharp +[] module Recursive = type Entity = @@ -13,7 +14,7 @@ module Recursive = | Blood of TypedTerm | Name of TypedTerm - [] + [] let ``Recursive definitions, example 1`` () : unit = let justAte (t1 : TypedTerm) (t2 : TypedTerm) : Goal = Goal.disj @@ -58,7 +59,7 @@ module Recursive = type Human = Human of string - [] + [] let ``Recursive definitions, example 2`` () : unit = let children = [ diff --git a/FicroKanSharp.Test/TestCustomUnification.fs b/FicroKanSharp.Test/TestCustomUnification.fs index 5a35af6..3354513 100644 --- a/FicroKanSharp.Test/TestCustomUnification.fs +++ b/FicroKanSharp.Test/TestCustomUnification.fs @@ -2,8 +2,9 @@ namespace FicroKanSharp.Test open FicroKanSharp open FsUnitTyped -open Xunit +open NUnit.Framework +[] module TestCustomUnification = [] @@ -27,7 +28,7 @@ module TestCustomUnification = = Some state - [] + [] let ``Type with custom unification`` () = Goal.equiv (Term.Symbol (Int.Case1, [])) (Term.Symbol (Int.Case2, [])) |> Goal.evaluate @@ -94,7 +95,7 @@ module TestCustomUnification = else Term.Symbol (Peano.Succ, [ toTerm (n - 1) ]) - [] + [] let ``A custom augmented Peano naturals type`` () = Goal.equiv (Term.Symbol (Peano.Pure 5, [])) (toTerm 5) |> Goal.evaluate diff --git a/FicroKanSharp.Test/TestExamples.fs b/FicroKanSharp.Test/TestExamples.fs index 64035ca..16d2672 100644 --- a/FicroKanSharp.Test/TestExamples.fs +++ b/FicroKanSharp.Test/TestExamples.fs @@ -1,12 +1,13 @@ namespace FicroKanSharp.Test open FicroKanSharp -open Xunit open FsUnitTyped +open NUnit.Framework +[] module TestThing = - [] + [] let ``Example from the docs`` () : unit = let aAndB = Goal.conj @@ -47,7 +48,7 @@ module TestThing = | None -> () | Some s -> failwith $"{s}" - [] + [] let ``Another example`` () = let aAndB = (Goal.callFresh (fun x -> Goal.equiv (Term.Variable x) (Term.Symbol (5, [])))) @@ -64,7 +65,7 @@ module TestThing = | None -> () | Some s -> failwithf $"{s}" - [] + [] let ``Recursive example`` () = let rec fives (x : Variable) = (Goal.disj (Goal.equiv (Term.Variable x) (Term.Symbol (5, []))) (Goal.delay (fun () -> fives x))) @@ -78,7 +79,7 @@ module TestThing = Map.ofList [ Variable.VariableCount 0, Term.Symbol (5, []) ] ] - [] + [] let ``Another recursive example`` () = let rec fives (x : Variable) = (Goal.disj (Goal.equiv (Term.Variable x) (Term.Symbol (5, []))) (Goal.delay (fun () -> fives x))) @@ -115,7 +116,7 @@ module TestThing = s |> Map.toList |> shouldEqual [ Variable.VariableCount 0, Term.Symbol (6, []) ] /// This arose because x0 unified to x1, x1 unified to 1, but x0 didn't get reduced to 1 by `walk`. - [] + [] let ``Unification works transitively`` () = Goal.callFresh (fun twon -> // 0 diff --git a/FicroKanSharp.Test/TypedArithmetic.fs b/FicroKanSharp.Test/TypedArithmetic.fs index 7b8c60f..311919b 100644 --- a/FicroKanSharp.Test/TypedArithmetic.fs +++ b/FicroKanSharp.Test/TypedArithmetic.fs @@ -1,9 +1,10 @@ namespace FicroKanSharp.Test open FicroKanSharp -open Xunit +open NUnit.Framework open FsUnitTyped +[] module TypedArithmetic = type Nat = @@ -73,7 +74,7 @@ module TypedArithmetic = Goal.disj zeroCase succCase - [] + [] let ``Arithmetic example using literals`` () = TypedTerm.Goal.callFresh (fun n -> // should be 1 TypedTerm.Goal.callFresh (fun m -> // should be 3 @@ -139,7 +140,7 @@ module TypedArithmetic = |> Reify.satisfiableExactlyOnce |> shouldEqual true - [] + [] let ``Test times`` () : unit = // Evaluate 3 * 3 diff --git a/analyzers/analyzers.fsproj b/analyzers/analyzers.fsproj new file mode 100644 index 0000000..e7d28fd --- /dev/null +++ b/analyzers/analyzers.fsproj @@ -0,0 +1,17 @@ + + + + false + false + ../.analyzerpackages/ + net6.0 + true + false + + + + + + + + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..e9e2af1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1761236834, + "narHash": "sha256-+pthv6hrL5VLW2UqPdISGuLiUZ6SnAXdd2DdUE+fV2Q=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d5faa84122bc0a1fd5d378492efce4e289f8eac1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..1e96479 --- /dev/null +++ b/flake.nix @@ -0,0 +1,72 @@ +{ + description = "Toy microkanren implementation"; + + inputs = { + flake-utils.url = "github:numtide/flake-utils"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { + nixpkgs, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages.${system}; + pname = "FicroKanSharp"; + dotnet-sdk = pkgs.dotnetCorePackages.sdk_9_0; + dotnet-runtime = pkgs.dotnetCorePackages.runtime_9_0; + version = "0.1"; + dotnetTool = dllOverride: toolName: toolVersion: hash: + pkgs.stdenvNoCC.mkDerivation rec { + name = toolName; + version = toolVersion; + nativeBuildInputs = [pkgs.makeWrapper]; + src = pkgs.fetchNuGet { + pname = name; + version = version; + hash = hash; + installPhase = ''mkdir -p $out/bin && cp -r tools/net*/any/* $out/bin''; + }; + installPhase = let + dll = + if isNull dllOverride + then name + else dllOverride; + in + # fsharp-analyzers requires the .NET SDK at runtime, so we use that instead of dotnet-runtime. + '' + runHook preInstall + mkdir -p "$out/lib" + cp -r ./bin/* "$out/lib" + makeWrapper "${dotnet-sdk}/bin/dotnet" "$out/bin/${name}" --set DOTNET_HOST_PATH "${dotnet-sdk}/bin/dotnet" --add-flags "$out/lib/${dll}.dll" + runHook postInstall + ''; + }; + in { + packages = let + deps = builtins.fromJSON (builtins.readFile ./nix/deps.json); + in { + fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") deps)).hash; + fsharp-analyzers = dotnetTool "FSharp.Analyzers.Cli" "fsharp-analyzers" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fsharp-analyzers.version (builtins.head (builtins.filter (elem: elem.pname == "fsharp-analyzers") deps)).hash; + default = pkgs.buildDotnetModule { + inherit pname version dotnet-sdk dotnet-runtime; + name = "FicroKanSharp"; + src = ./.; + projectFile = "./FicroKanSharp/FicroKanSharp.fsproj"; + testProjectFile = "./FicroKanSharp.Test/FicroKanSharp.Test.fsproj"; + nugetDeps = ./nix/deps.json; # `nix build .#default.fetch-deps && ./result nix/deps.json` + doCheck = true; + }; + }; + devShells.default = pkgs.mkShell { + buildInputs = [dotnet-sdk]; + packages = [ + pkgs.alejandra + pkgs.nodePackages.markdown-link-check + pkgs.shellcheck + pkgs.xmlstarlet + ]; + }; + }); +} diff --git a/nix/deps.json b/nix/deps.json new file mode 100644 index 0000000..e796470 --- /dev/null +++ b/nix/deps.json @@ -0,0 +1,177 @@ +[ + { + "pname": "fantomas", + "version": "7.0.3", + "hash": "sha256-0XlfV7SxXPDnk/CjkUesJSaH0cxlNHJ+Jj86zNUhkNA=" + }, + { + "pname": "fsharp-analyzers", + "version": "0.33.1", + "hash": "sha256-vYXvqnf3en487svFv3CmNl24SolwMYzu6zKKGXNxSu8=" + }, + { + "pname": "FsUnit", + "version": "7.1.1", + "hash": "sha256-UMCEGKxQ4ytjmPuVpiNaAPbi3RQH9gqa61JJIUS/6hg=" + }, + { + "pname": "Microsoft.ApplicationInsights", + "version": "2.23.0", + "hash": "sha256-5sf3bg7CZZjHseK+F3foOchEhmVeioePxMZVvS6Rjb0=" + }, + { + "pname": "Microsoft.AspNetCore.App.Ref", + "version": "6.0.36", + "hash": "sha256-9jDkWbjw/nd8yqdzVTagCuqr6owJ/DUMi4BlUZT4hWU=" + }, + { + "pname": "Microsoft.AspNetCore.App.Runtime.linux-arm64", + "version": "6.0.36", + "hash": "sha256-JQULJyF0ivLoUU1JaFfK/HHg+/qzpN7V2RR2Cc+WlQ4=" + }, + { + "pname": "Microsoft.AspNetCore.App.Runtime.linux-x64", + "version": "6.0.36", + "hash": "sha256-zUsVIpV481vMLAXaLEEUpEMA9/f1HGOnvaQnaWdzlyY=" + }, + { + "pname": "Microsoft.AspNetCore.App.Runtime.osx-arm64", + "version": "6.0.36", + "hash": "sha256-2seqZcz0JeUjkzh3QcGa9TcJ4LUafpFjTRk+Nm8T6T0=" + }, + { + "pname": "Microsoft.AspNetCore.App.Runtime.osx-x64", + "version": "6.0.36", + "hash": "sha256-yxLafxiBKkvfkDggPk0P9YZIHBkDJOsFTO7/V9mEHuU=" + }, + { + "pname": "Microsoft.CodeCoverage", + "version": "18.0.0", + "hash": "sha256-1RNxheCYASMusDC48BXtaO3MhnInw15JVfjfLM1VMGA=" + }, + { + "pname": "Microsoft.NET.Test.Sdk", + "version": "18.0.0", + "hash": "sha256-9iW+9mvMeZgDXwSoR08bnvRNsN4jT8OVWcjq3lcE+cs=" + }, + { + "pname": "Microsoft.NETCore.App.Host.linux-arm64", + "version": "6.0.36", + "hash": "sha256-9lC/LYnthYhjkWWz2kkFCvlA5LJOv11jdt59SDnpdy0=" + }, + { + "pname": "Microsoft.NETCore.App.Host.linux-x64", + "version": "6.0.36", + "hash": "sha256-VFRDzx7LJuvI5yzKdGmw/31NYVbwHWPKQvueQt5xc10=" + }, + { + "pname": "Microsoft.NETCore.App.Host.osx-arm64", + "version": "6.0.36", + "hash": "sha256-DaSWwYACJGolEBuMhzDVCj/rQTdDt061xCVi+gyQnuo=" + }, + { + "pname": "Microsoft.NETCore.App.Host.osx-x64", + "version": "6.0.36", + "hash": "sha256-FrRny9EI6HKCKQbu6mcLj5w4ooSRrODD4Vj2ZMGnMd4=" + }, + { + "pname": "Microsoft.NETCore.App.Ref", + "version": "6.0.36", + "hash": "sha256-9LZgVoIFF8qNyUu8kdJrYGLutMF/cL2K82HN2ywwlx8=" + }, + { + "pname": "Microsoft.NETCore.App.Runtime.linux-arm64", + "version": "6.0.36", + "hash": "sha256-k3rxvUhCEU0pVH8KgEMtkPiSOibn+nBh+0zT2xIfId8=" + }, + { + "pname": "Microsoft.NETCore.App.Runtime.linux-x64", + "version": "6.0.36", + "hash": "sha256-U8wJ2snSDFqeAgDVLXjnniidC7Cr5aJ1/h/BMSlyu0c=" + }, + { + "pname": "Microsoft.NETCore.App.Runtime.osx-arm64", + "version": "6.0.36", + "hash": "sha256-UfLcrL2Gj/OLz0s92Oo+OCJeDpZFAcQLPLiSNND8D5Y=" + }, + { + "pname": "Microsoft.NETCore.App.Runtime.osx-x64", + "version": "6.0.36", + "hash": "sha256-0xIJYFzxdMcnCj3wzkFRQZSnQcPHzPHMzePRIOA3oJs=" + }, + { + "pname": "Microsoft.Testing.Extensions.Telemetry", + "version": "1.9.0", + "hash": "sha256-JT91ThKLEyoRS/8ZJqZwlSTT7ofC2QhNqPFI3pYmMaw=" + }, + { + "pname": "Microsoft.Testing.Extensions.TrxReport.Abstractions", + "version": "1.9.0", + "hash": "sha256-oscZOEKw7gM6eRdDrOS3x+CwqIvXWRmfmi0ugCxBRw0=" + }, + { + "pname": "Microsoft.Testing.Extensions.VSTestBridge", + "version": "1.9.0", + "hash": "sha256-CadXLWD093sUDaWhnppzD9LvpxSRqqt93ZEOFiIAPyw=" + }, + { + "pname": "Microsoft.Testing.Platform", + "version": "1.9.0", + "hash": "sha256-6nzjoYbJOh7v/GB7d+TDuM0l/xglCshFX6KWjg7+cFI=" + }, + { + "pname": "Microsoft.Testing.Platform.MSBuild", + "version": "1.9.0", + "hash": "sha256-/bileP4b+9RZp8yjgS6eynXwc2mohyyzf6p/0LZJd8I=" + }, + { + "pname": "Microsoft.TestPlatform.AdapterUtilities", + "version": "17.13.0", + "hash": "sha256-Vr+3Tad/h/nk7f/5HMExn3HvCGFCarehFAzJSfCBaOc=" + }, + { + "pname": "Microsoft.TestPlatform.ObjectModel", + "version": "17.13.0", + "hash": "sha256-6S0fjfj8vA+h6dJVNwLi6oZhYDO/I/6hBZaq2VTW+Uk=" + }, + { + "pname": "Microsoft.TestPlatform.ObjectModel", + "version": "18.0.0", + "hash": "sha256-O/ivHdoIO+q1n0byJ9OZO4quOqACOD3K3Qm00wfhuZk=" + }, + { + "pname": "Microsoft.TestPlatform.TestHost", + "version": "18.0.0", + "hash": "sha256-qAIX2Rqxrnh1xaYRjCbkkvvMm407oyKihqyVjURX5wY=" + }, + { + "pname": "Newtonsoft.Json", + "version": "13.0.3", + "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + }, + { + "pname": "NUnit", + "version": "4.4.0", + "hash": "sha256-5geF5QOF+X/WkuCEgkPVKH4AdKx4U0olpU07S8+G3nU=" + }, + { + "pname": "NUnit3TestAdapter", + "version": "5.2.0", + "hash": "sha256-ybTutL4VkX/fq61mS+O3Ruh+adic4fpv+MKgQ0IZvGg=" + }, + { + "pname": "System.Collections.Immutable", + "version": "8.0.0", + "hash": "sha256-F7OVjKNwpqbUh8lTidbqJWYi476nsq9n+6k0+QVRo3w=" + }, + { + "pname": "System.Diagnostics.DiagnosticSource", + "version": "5.0.0", + "hash": "sha256-6mW3N6FvcdNH/pB58pl+pFSCGWgyaP4hfVtC/SMWDV4=" + }, + { + "pname": "System.Reflection.Metadata", + "version": "8.0.0", + "hash": "sha256-dQGC30JauIDWNWXMrSNOJncVa1umR1sijazYwUDdSIE=" + } +]