Compare commits

...

68 Commits

Author SHA1 Message Date
patrick-conscriptus[bot]
c380f95ea5 Automated commit (#125)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-10-05 01:48:03 +00:00
Patrick Stevens
1895f28c7b Switch to trusted publishing (#124) 2025-10-03 09:40:27 +00:00
patrick-conscriptus[bot]
f6ff717135 Automated commit (#123)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-09-28 01:49:32 +00:00
patrick-conscriptus[bot]
2111177d4f Automated commit (#122)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-09-21 01:51:25 +00:00
Patrick Stevens
cf249fd556 Case-insensitive roll-forward parsing (#121) 2025-09-16 21:23:15 +00:00
patrick-conscriptus[bot]
4704c471bc Automated commit (#120)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-09-14 01:47:04 +00:00
Patrick Stevens
0f6ab4345c Bump ApiSurface (#119) 2025-09-08 21:33:32 +00:00
dependabot[bot]
04caa77a2d Bump actions/setup-dotnet from 4 to 5 (#118)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4 to 5.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:28:55 +01:00
patrick-conscriptus[bot]
2fd85399f4 Automated commit (#117)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-09-07 01:45:36 +00:00
dependabot[bot]
7bccc10aab Bump actions/attest-build-provenance from 2.4.0 to 3.0.0 (#116)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2.4.0 to 3.0.0.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](e8998f9491...977bb373ed)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-01 08:27:18 +01:00
patrick-conscriptus[bot]
3492eec8d1 Automated commit (#115)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-31 01:47:29 +00:00
patrick-conscriptus[bot]
17a179309d Automated commit (#114)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-24 01:55:18 +00:00
dependabot[bot]
72ba1a73f2 Bump actions/checkout from 4 to 5 (#113) 2025-08-18 08:13:49 +01:00
patrick-conscriptus[bot]
d7425bd959 Automated commit (#112)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-17 02:00:07 +00:00
dependabot[bot]
c6c8da38b1 Bump actions/download-artifact from 4 to 5 (#111) 2025-08-11 07:09:05 +01:00
patrick-conscriptus[bot]
8c47fb5d0c Automated commit (#110)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-10 02:07:09 +00:00
patrick-conscriptus[bot]
1f4691d040 Automated commit (#109)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-03 02:10:02 +00:00
patrick-conscriptus[bot]
746389cbb5 Automated commit (#108)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-07-27 02:08:33 +00:00
patrick-conscriptus[bot]
9f0013e771 Automated commit (#107)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-07-20 02:07:26 +00:00
patrick-conscriptus[bot]
6dbc283237 Automated commit (#106)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-07-13 02:06:12 +00:00
patrick-conscriptus[bot]
548a8f9d6a Automated commit (#105)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-07-06 02:01:19 +00:00
patrick-conscriptus[bot]
6f24a09daf Automated commit (#104)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-06-29 02:04:51 +00:00
patrick-conscriptus[bot]
9668acb5ff Automated commit (#103)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-06-22 02:00:02 +00:00
dependabot[bot]
b4fa714249 Bump actions/attest-build-provenance from 2.3.0 to 2.4.0 (#102) 2025-06-16 08:58:43 +01:00
patrick-conscriptus[bot]
75cfe6d55e Automated commit (#101)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-06-15 02:01:18 +00:00
patrick-conscriptus[bot]
8ae289665c Automated commit (#100)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-06-08 01:58:45 +00:00
patrick-conscriptus[bot]
23158bc07d Automated commit (#99)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-06-01 02:07:07 +00:00
patrick-conscriptus[bot]
785b1f28be Automated commit (#98)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-05-25 01:56:49 +00:00
patrick-conscriptus[bot]
5dcd188f40 Automated commit (#97)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-05-18 01:56:04 +00:00
Patrick Stevens
b1cae0acb7 Permit overriding dotnet path (#96) 2025-05-16 19:54:57 +00:00
Patrick Stevens
5a7eb63590 MIT licence (#95) 2025-05-16 20:27:58 +01:00
Patrick Stevens
1b534018e9 Add runtime lookup (#94) 2025-05-16 20:25:40 +01:00
Patrick Stevens
a013fc415e Add runtimeconfig types (#93) 2025-05-16 14:04:33 +00:00
Patrick Stevens
bbdabd8206 Augment the envrc (#92) 2025-05-16 13:52:37 +00:00
patrick-conscriptus[bot]
787ebffc21 Automated commit (#91)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-05-11 01:54:09 +00:00
dependabot[bot]
ffbc46dad5 Bump actions/attest-build-provenance from 2.2.3 to 2.3.0 (#90) 2025-05-05 09:27:36 +01:00
patrick-conscriptus[bot]
e67c1f2f4e Automated commit (#89)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-05-04 01:55:26 +00:00
patrick-conscriptus[bot]
4dc5d06082 Automated commit (#88)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-04-27 01:50:56 +00:00
patrick-conscriptus[bot]
16c8999b50 Automated commit (#87)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-04-20 01:51:28 +00:00
patrick-conscriptus[bot]
074fee4cd6 Automated commit (#86)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-04-13 03:10:52 +00:00
patrick-conscriptus[bot]
1b4f69df96 Automated commit (#85)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-04-07 06:45:58 +00:00
dependabot[bot]
4a498a9729 Bump actions/create-github-app-token from 1 to 2 (#84) 2025-04-07 07:40:28 +01:00
patrick-conscriptus[bot]
84feacc7aa Automated commit (#83)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-04-06 01:48:10 +00:00
patrick-conscriptus[bot]
d317109c93 Automated commit (#82)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-03-30 01:49:00 +00:00
patrick-conscriptus[bot]
91873c8d0e Automated commit (#81)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-03-23 01:46:04 +00:00
dependabot[bot]
b41d221acf Bump cachix/install-nix-action from 30 to 31 (#80)
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 30 to 31.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v30...v31)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Patrick Stevens <3138005+Smaug123@users.noreply.github.com>
2025-03-21 14:35:27 +00:00
dependabot[bot]
a968e43ea5 Bump actions/attest-build-provenance from 2.2.0 to 2.2.3 (#79)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2.2.0 to 2.2.3.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](520d128f16...c074443f1a)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Patrick Stevens <3138005+Smaug123@users.noreply.github.com>
2025-03-21 14:31:01 +00:00
patrick-conscriptus[bot]
ea34dd0447 Upgrade Nix flake and deps (#78)
* Automated commit

* Bump versions

---------

Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-03-21 14:26:36 +00:00
patrick-conscriptus[bot]
bc5cf83a23 Automated commit (#75)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-02-23 01:41:54 +00:00
patrick-conscriptus[bot]
a5718e9816 Automated commit (#74)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-02-16 01:50:04 +00:00
patrick-conscriptus[bot]
59e9866a4c Automated commit (#73)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-02-09 01:31:46 +00:00
patrick-conscriptus[bot]
ea8db204c2 Automated commit (#72)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-02-02 01:31:11 +00:00
dependabot[bot]
4cafe1268d Bump actions/attest-build-provenance from 2.1.0 to 2.2.0 (#71) 2025-01-27 08:09:40 +00:00
patrick-conscriptus[bot]
cb9ca1dab9 Automated commit (#70)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-01-26 01:28:56 +00:00
patrick-conscriptus[bot]
3e6f9a0e14 Automated commit (#69)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-01-19 01:39:20 +00:00
Patrick Stevens
71d222962a Use GR's release action (#68) 2025-01-13 09:43:49 +00:00
patrick-conscriptus[bot]
9dc3abcff4 Automated commit (#67)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-01-12 01:43:03 +00:00
patrick-conscriptus[bot]
3a4339043d Automated commit (#66)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-01-05 01:41:50 +00:00
patrick-conscriptus[bot]
8e59918cee Automated commit (#65)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-12-29 01:42:33 +00:00
Patrick Stevens
8adcc54d4d Fix flake update after recent nixpkgs bump (#64) 2024-12-24 19:56:52 +00:00
Patrick Stevens
57a6708318 Use non-deprecated nixpkgs methods (#63) 2024-12-24 19:52:55 +00:00
patrick-conscriptus[bot]
76d466cb16 Automated commit (#62)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-12-22 01:42:00 +00:00
dependabot[bot]
c2bdaf0623 Bump actions/attest-build-provenance from 2.0.1 to 2.1.0 (#61)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](c4fbc64884...7668571508)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-16 09:41:56 +00:00
patrick-conscriptus[bot]
71640cf4ca Automated commit (#60)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-12-15 01:49:22 +00:00
dependabot[bot]
b598a51b29 Bump actions/attest-build-provenance from 1.4.4 to 2.0.1 (#59)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 1.4.4 to 2.0.1.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](ef244123eb...c4fbc64884)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 08:29:05 +00:00
patrick-conscriptus[bot]
afca66ef4f Automated commit (#58)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-12-08 01:50:13 +00:00
patrick-conscriptus[bot]
64f8913624 Automated commit (#57)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-12-01 01:55:20 +00:00
Patrick Stevens
c75ec6aff9 Fix path filters (#55) 2024-11-24 09:11:50 +00:00
26 changed files with 998 additions and 444 deletions

22
.envrc
View File

@@ -1 +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

View File

@@ -18,10 +18,10 @@ jobs:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- uses: actions/setup-dotnet@v4
- uses: actions/setup-dotnet@v5
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
@@ -31,7 +31,7 @@ jobs:
- name: Publish
run: dotnet publish Example
- name: Run example
run: ".\\Example\\bin\\Release\\net8.0\\win-x64\\Example.exe"
run: ".\\Example\\bin\\Release\\net8.0\\Example.exe"
build:
strategy:
@@ -43,11 +43,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -57,18 +57,22 @@ jobs:
run: nix develop --command dotnet build --no-restore --configuration ${{matrix.config}}
- name: Test
run: nix develop --command dotnet test --no-build --verbosity normal --configuration ${{matrix.config}}
- name: Publish example
run: nix develop --command dotnet publish --no-build --verbosity normal --configuration ${{matrix.config}} Example
- name: Publish example self-contained
run: nix develop --command dotnet publish --self-contained --runtime linux-x64 --verbosity normal --configuration ${{matrix.config}} Example
- name: Run example self-contained
run: "./Example/bin/${{matrix.config}}/*/*/Example"
run: "./Example/bin/${{matrix.config}}/net*/*-*/Example"
- name: Publish example non-self-contained
run: nix develop --command dotnet publish --verbosity normal --configuration ${{matrix.config}} Example
- name: Run example non-self-contained
run: "./Example/bin/${{matrix.config}}/net*/Example"
build-nix:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -81,9 +85,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -94,9 +98,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -109,7 +113,7 @@ jobs:
steps:
- uses: actions/checkout@master
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -122,7 +126,7 @@ jobs:
steps:
- uses: actions/checkout@master
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -132,11 +136,11 @@ jobs:
nuget-pack:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -157,7 +161,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download NuGet artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: nuget-package
path: packed
@@ -166,19 +170,31 @@ jobs:
run: if [[ $(find packed -maxdepth 1 -name 'WoofWare.DotnetRuntimeLocator.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
github-release-dry-run:
needs: [nuget-pack]
runs-on: ubuntu-latest
needs: [nuget-pack]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Download NuGet artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: nuget-package
- name: Tag and release
- name: Compute package path
id: compute-path
run: |
find . -maxdepth 1 -type f -name 'WoofWare.DotnetRuntimeLocator.*.nupkg' -exec sh -c 'echo "output=$(basename "$1")" >> $GITHUB_OUTPUT' shell {} \;
- name: Compute tag name
id: compute-tag
env:
DRY_RUN: 1
GITHUB_TOKEN: mock-token
run: sh .github/workflows/tag.sh
NUPKG_PATH: ${{ steps.compute-path.outputs.output }}
run: echo "output=$(basename "$NUPKG_PATH" .nupkg)" >> $GITHUB_OUTPUT
- name: Tag and release
uses: G-Research/common-actions/github-release@19d7281a0f9f83e13c78f99a610dbc80fc59ba3b
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
target-commitish: ${{ github.sha }}
tag: ${{ steps.compute-tag.outputs.output }}
binary-contents: ${{ steps.compute-path.outputs.output }}
dry-run: true
all-required-checks-complete:
if: ${{ always() }}
@@ -199,12 +215,12 @@ jobs:
contents: read
steps:
- name: Download NuGet artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: nuget-package
path: packed
- name: Attest Build Provenance
uses: actions/attest-build-provenance@ef244123eb79f2f7a7e75d99086184180e6d0018 # v1.4.4
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with:
subject-path: "packed/WoofWare.DotnetRuntimeLocator.*.nupkg"
@@ -218,25 +234,30 @@ jobs:
attestations: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Download NuGet artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: nuget-package
path: packed
- name: Identify .NET
id: identify-dotnet
run: nix develop --command bash -c "echo dotnet=$(which dotnet) >> $GITHUB_OUTPUT"
- name: Obtain NuGet key
uses: NuGet/login@d22cc5f58ff5b88bf9bd452535b4335137e24544
id: login
with:
user: ${{ secrets.NUGET_USER }}
- name: Publish NuGet package
uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
with:
package-name: WoofWare.DotnetRuntimeLocator
nuget-key: ${{ secrets.NUGET_API_KEY }}
nuget-key: ${{ steps.login.outputs.NUGET_API_KEY }}
nupkg-dir: packed/
dotnet: ${{ steps.identify-dotnet.outputs.dotnet }}
@@ -248,12 +269,24 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Download NuGet artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: nuget-package
- name: Tag and release
- name: Compute package path
id: compute-path
run: |
find . -maxdepth 1 -type f -name 'WoofWare.DotnetRuntimeLocator.*.nupkg' -exec sh -c 'echo "output=$(basename "$1")" >> $GITHUB_OUTPUT' shell {} \;
- name: Compute tag name
id: compute-tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: sh .github/workflows/tag.sh
NUPKG_PATH: ${{ steps.compute-path.outputs.output }}
run: echo "output=$(basename "$NUPKG_PATH" .nupkg)" >> $GITHUB_OUTPUT
- name: Tag and release
uses: G-Research/common-actions/github-release@19d7281a0f9f83e13c78f99a610dbc80fc59ba3b
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
target-commitish: ${{ github.sha }}
tag: ${{ steps.compute-tag.outputs.output }}
binary-contents: ${{ steps.compute-path.outputs.output }}

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
@@ -21,18 +21,18 @@ jobs:
- name: Update Nix flake
run: 'nix flake update'
- name: Build passthru
run: 'nix build ".#default.passthru.fetch-deps"'
- name: Build fetch-deps
run: 'nix build ".#default.fetch-deps"'
- name: Run passthru
run: ./result nix/deps.nix
- name: Run fetch-deps
run: ./result nix/deps.json
- name: Format
run: 'nix develop --command alejandra .'
- name: Create token
id: generate-token
uses: actions/create-github-app-token@v1
uses: actions/create-github-app-token@v2
with:
# https://github.com/actions/create-github-app-token/issues/136
app-id: ${{ secrets.APP_ID }}

View File

@@ -1,120 +0,0 @@
#!/bin/bash
echo "Dry-run? $DRY_RUN!"
find . -maxdepth 1 -type f ! -name "$(printf "*\n*")" -name '*.nupkg' | while IFS= read -r file
do
tag=$(basename "$file" .nupkg)
git tag "$tag"
${DRY_RUN:+echo} git push origin "$tag"
done
export TAG
TAG=$(find . -maxdepth 1 -type f -name 'WoofWare.DotnetRuntimeLocator.*.nupkg' -exec sh -c 'basename "$1" .nupkg' shell {} \; | grep -v Attributes)
case "$TAG" in
*"
"*)
echo "Error: TAG contains a newline; multiple plugins found."
exit 1
;;
esac
# target_commitish empty indicates the repo default branch
curl_body='{"tag_name":"'"$TAG"'","target_commitish":"","name":"'"$TAG"'","draft":false,"prerelease":false,"generate_release_notes":false}'
echo "cURL body: $curl_body"
failed_output=$(cat <<'EOF'
{
"message": "Validation Failed",
"errors": [
{
"resource": "Release",
"code": "already_exists",
"field": "tag_name"
}
],
"documentation_url": "https://docs.github.com/rest/releases/releases#create-a-release"
}
EOF
)
success_output=$(cat <<'EOF'
{
"url": "https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/releases/158152116",
"assets_url": "https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/releases/158152116/assets",
"upload_url": "https://uploads.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/releases/158152116/assets{?name,label}",
"html_url": "https://github.com/Smaug123/WoofWare.DotnetRuntimeLocator/releases/tag/WoofWare.DotnetRuntimeLocator.2.1.30",
"id": 158152116,
"author": {
"login": "github-actions[bot]",
"id": 41898282,
"node_id": "MDM6Qm90NDE4OTgyODI=",
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
"html_url": "https://github.com/apps/github-actions",
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
"type": "Bot",
"site_admin": false
},
"node_id": "RE_kwDOJfksgc4JbTW0",
"tag_name": "WoofWare.DotnetRuntimeLocator.2.1.30",
"target_commitish": "main",
"name": "WoofWare.DotnetRuntimeLocator.2.1.30",
"draft": false,
"prerelease": false,
"created_at": "2024-05-30T11:00:55Z",
"published_at": "2024-05-30T11:03:02Z",
"assets": [
],
"tarball_url": "https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/tarball/WoofWare.DotnetRuntimeLocator.2.1.30",
"zipball_url": "https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/zipball/WoofWare.DotnetRuntimeLocator.2.1.30",
"body": null
}
EOF
)
HANDLE_OUTPUT=''
handle_error() {
ERROR_OUTPUT="$1"
exit_message=$(echo "$ERROR_OUTPUT" | jq -r --exit-status 'if .errors | length == 1 then .errors[0].code else null end')
if [ "$exit_message" = "already_exists" ] ; then
HANDLE_OUTPUT="Did not create GitHub release because it already exists at this version."
else
echo "Unexpected error output from curl: $(cat curl_output.json)"
echo "JQ output: $(exit_message)"
exit 2
fi
}
run_tests() {
handle_error "$failed_output"
if [ "$HANDLE_OUTPUT" != "Did not create GitHub release because it already exists at this version." ]; then
echo "Bad output from handler: $HANDLE_OUTPUT"
exit 3
fi
HANDLE_OUTPUT=''
echo "Tests passed."
}
run_tests
if [ "$DRY_RUN" != 1 ] ; then
if curl --fail-with-body -L -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/releases -d "$curl_body" > curl_output.json; then
echo "Curl succeeded."
else
handle_error "$(cat curl_output.json)"
echo "$HANDLE_OUTPUT"
fi
fi

View File

@@ -10,7 +10,7 @@
<WarnOn>FS3388,FS3559</WarnOn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" PrivateAssets="all"/>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.8.38-alpha" PrivateAssets="all"/>
<SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/>
</ItemGroup>
<PropertyGroup Condition="'$(GITHUB_ACTION)' != ''">

View File

@@ -1,17 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<SelfContained>true</SelfContained>
</PropertyGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs"/>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.fs"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WoofWare.DotnetRuntimeLocator\WoofWare.DotnetRuntimeLocator.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WoofWare.DotnetRuntimeLocator\WoofWare.DotnetRuntimeLocator.csproj"/>
</ItemGroup>
</Project>

View File

@@ -1,6 +1,8 @@
namespace Example
namespace Example
open System
open System.IO
open System.Reflection
open WoofWare.DotnetRuntimeLocator
module Program =
@@ -18,4 +20,16 @@ module Program =
for f in info.Frameworks do
Console.WriteLine $"Framework: %O{f}"
// Identify the runtime which would execute this DLL
let self = Assembly.GetExecutingAssembly().Location
let runtimeSearchDirs = DotnetRuntime.SelectForDll self
// For example, the System.Text.Json.dll which this DLL would load:
runtimeSearchDirs
|> Seq.tryPick (fun dir ->
let attempt = Path.Combine (dir, "System.Text.Json.dll")
if File.Exists attempt then Some attempt else None
)
|> Option.get
|> fun s -> Console.WriteLine $"System.Text.Json location: %s{s}"
0

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Patrick Stevens
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -11,6 +11,11 @@ See [the example](Example/Program.fs).
let info = DotnetEnvironmentInfo.Get ()
// or, if you already know a path to the `dotnet` executable...
let info = DotnetEnvironmentInfo.GetSpecific "/path/to/dotnet"
// identify the directories containing the framework which would execute a given DLL
let dirsToSearch : string seq = DotnetRuntime.SelectForDll "/path/to/dll.dll"
// or, if you would be running with a specific `/path/to/dotnet exec /path/to/dll.dll`:
let dirsToSearch : string seq = DotnetRuntime.SelectForDll ("/path/to/dll.dll", "/path/to/dotnet")
```
## Troubleshooting
@@ -20,4 +25,10 @@ The easiest way to make sure we can find a `dotnet` is to have one on your PATH.
If you have a *very* strange setup, we may be unable to locate the `libhostfxr` library we use to find the runtimes.
In that case, you can supply the environment variable `WOOFWARE_DOTNET_LOCATOR_LIBHOSTFXR`,
which should be a full path to a `libhostfxr` DLL on your system.
(Normally this is in `/usr/share/dotnet/host/fxr/{runtime}/libhostfxr.so`; you must make sure your version is from runtime 6 or greater, because the required symbols were not added until then.)
(Normally this is in `/usr/share/dotnet/host/fxr/{runtime}/libhostfxr.so`;
you must make sure your version is from runtime 6 or greater,
because the required symbols were not added until then.)
# Licence
WoofWare.DotnetRuntimeLocator is licensed to you under the MIT licence, a copy of which can be found at [LICENSE](./LICENSE).

View File

@@ -117,7 +117,10 @@ public record DotnetEnvironmentInfo(
/// <summary>
/// Get the environment information that is available to the specified `dotnet` executable.
/// </summary>
/// <param name="dotnetExe">A `dotnet` (or `dotnet.exe`) executable, e.g. one from /usr/bin/dotnet. Set this to null if you want us to just do our best.</param>
/// <param name="dotnetExe">
/// A `dotnet` (or `dotnet.exe`) executable, e.g. one from /usr/bin/dotnet. Set this to null if you
/// want us to just do our best.
/// </param>
/// <returns>Information about the environment available to the given executable.</returns>
/// <exception cref="Exception">Throws on any failure; handles nothing gracefully.</exception>
public static DotnetEnvironmentInfo GetSpecific(FileInfo? dotnetExe)
@@ -140,6 +143,7 @@ public record DotnetEnvironmentInfo(
dotnetParent = parent.FullName;
}
}
return CallDelegate(dotnetParent, f);
}
finally
@@ -169,10 +173,7 @@ public record DotnetEnvironmentInfo(
foreach (var component in path.Split(':'))
{
var dotnet = Path.Combine(component, "dotnet");
if (File.Exists(dotnet))
{
return new FileInfo(dotnet);
}
if (File.Exists(dotnet)) return new FileInfo(dotnet);
}
}
@@ -187,7 +188,11 @@ public record DotnetEnvironmentInfo(
/// <exception cref="Exception">Throws on any failure; handles nothing gracefully.</exception>
public static DotnetEnvironmentInfo Get()
{
var dotnetExe = LocateDotnetExe();
var dotnetExe = Environment.GetEnvironmentVariable("WOOFWARE_DOTNET_LOCATOR_DOTNET_EXE") switch
{
null => LocateDotnetExe(),
var s => new FileInfo(s)
};
// `null` can happen! Maybe we're self-contained.
return GetSpecific(dotnetExe);

View File

@@ -0,0 +1,319 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace WoofWare.DotnetRuntimeLocator;
/// <summary>
/// The result of a call to `DotnetRuntime.Select`.
/// This is `type DotnetRuntimeSelection = | Framework of DotnetEnvironmentFrameworkInfo | Sdk of
/// DotnetEnvironmentSdkInfo | Absent`.
/// </summary>
internal class DotnetRuntimeSelection
{
private readonly int _discriminator;
private readonly DotnetEnvironmentFrameworkInfo? _framework;
private readonly DotnetEnvironmentSdkInfo? _sdk;
/// <summary>
/// The constructor which means "We found the right runtime, and it's from this framework".
/// </summary>
/// <param name="framework">For example, </param>
public DotnetRuntimeSelection(DotnetEnvironmentFrameworkInfo framework)
{
_discriminator = 1;
_framework = framework;
}
/// <summary>
/// The constructor which means "We found the right runtime, and it's from this SDK".
/// </summary>
/// <param name="sdk">For example, </param>
public DotnetRuntimeSelection(DotnetEnvironmentSdkInfo sdk)
{
_discriminator = 2;
_sdk = sdk;
}
/// <summary>
/// The constructor which means "We were unable to find an appropriate runtime".
/// </summary>
public DotnetRuntimeSelection()
{
_discriminator = 3;
}
/// <summary>
/// Exhaustive match on this discriminated union.
/// </summary>
/// <param name="withFramework">If `this` is a `Framework`, call this continuation with its value.</param>
/// <param name="withSdk">If `this` is a `Sdk`, call this continuation with its value.</param>
/// <param name="withNone">If `this` represents the absence of a result, call this continuation.</param>
/// <returns>The result of the continuation which was called.</returns>
public TRet Visit<TRet>(Func<DotnetEnvironmentFrameworkInfo, TRet> withFramework,
Func<DotnetEnvironmentSdkInfo, TRet> withSdk,
Func<TRet> withNone)
{
return _discriminator switch
{
1 => withFramework.Invoke(_framework!),
2 => withSdk.Invoke(_sdk!),
3 => withNone.Invoke(),
_ => throw new InvalidOperationException($"unrecognised union discriminator %i{_discriminator}")
};
}
}
/// <summary>
/// Module to hold methods for automatically identifying a .NET runtime.
/// </summary>
public static class DotnetRuntime
{
/// <returns>For each requested runtime in the RuntimeOptions, the resolved place in which to find that runtime.</returns>
private static IReadOnlyDictionary<string, DotnetRuntimeSelection> SelectRuntime(RuntimeOptions options,
DotnetEnvironmentInfo env)
{
var rollForwardEnvVar = Environment.GetEnvironmentVariable("DOTNET_ROLL_FORWARD");
RollForward rollForward;
if (rollForwardEnvVar == null)
{
rollForward = options.RollForward ?? RollForward.Minor;
}
else
{
if (!Enum.TryParse(rollForwardEnvVar, out rollForward))
throw new ArgumentException(
$"Unable to parse the value of environment variable DOTNET_ROLL_FORWARD, which was: {rollForwardEnvVar}");
}
IReadOnlyDictionary<string, Version> desiredVersions;
if (options.IncludedFrameworks == null)
{
if (options.Framework == null)
{
if (options.Frameworks == null)
throw new InvalidDataException(
"Expected runtimeconfig.json file to have either a framework or frameworks entry, but it had neither");
desiredVersions = options.Frameworks.Select(x => (x.Name, new Version(x.Version))).GroupBy(x => x.Name)
.Select(data =>
{
var versions = (IReadOnlyList<Version>)data.Select(datum => datum.Item2).ToList();
if (versions.Count != 1)
{
var description = string.Join(", ", versions.Select(x => x.ToString()));
throw new InvalidDataException(
$"Unexpectedly had not-exactly-one version desired for framework {data.Key}: {description}");
}
return (data.Key, versions[0]);
})
.ToDictionary();
}
else
{
var result = new Dictionary<string, Version>
{ { options.Framework.Name, new Version(options.Framework.Version) } };
desiredVersions = result;
}
}
else
{
desiredVersions = options.IncludedFrameworks.Select(x => (x.Name, new Version(x.Version)))
.GroupBy(x => x.Name)
.Select(data =>
{
var versions = (IReadOnlyList<Version>)data.Select(datum => datum.Item2).ToList();
if (versions.Count != 1)
{
var description = string.Join(", ", versions.Select(x => x.ToString()));
throw new InvalidDataException(
$"Unexpectedly had not-exactly-one version desired for framework {data.Key}: {description}");
}
return (data.Key, versions[0]);
})
.ToDictionary();
}
IReadOnlyDictionary<string, IReadOnlyList<RuntimeOnDisk>> availableRuntimes = env
.Frameworks.SelectMany(availableFramework =>
{
var availableVersion = new Version(availableFramework.Version);
if (!desiredVersions.TryGetValue(availableFramework.Name, out var desiredVersion))
{
// we don't desire this framework at any version; skip it
return [];
}
if (availableVersion < desiredVersion)
{
// It's never desired to roll *backward*.
return [];
}
return new List<(string, DotnetEnvironmentFrameworkInfo)>
{ (availableFramework.Name, availableFramework) };
}).GroupBy(x => x.Item1)
.Select(group =>
{
var grouping = group.Select(x => new RuntimeOnDisk(x.Item2, new Version(x.Item2.Version))).ToList();
return (group.Key, (IReadOnlyList<RuntimeOnDisk>)grouping);
})
.ToDictionary();
switch (rollForward)
{
case RollForward.Minor:
{
return desiredVersions.Select(desired =>
{
if (!availableRuntimes.TryGetValue(desired.Key, out var available))
{
return (desired.Key, new DotnetRuntimeSelection());
}
if (ReferenceEquals(available, null))
{
throw new NullReferenceException("logic error: contents of non-nullable dict can't be null");
}
// If there's a correct major and minor version, take the latest patch.
var correctMajorAndMinorVersion =
available.Where(data =>
data.InstalledVersion.Major == desired.Value.Major &&
data.InstalledVersion.Minor == desired.Value.Minor).ToList();
if (correctMajorAndMinorVersion.Count > 0)
{
return (desired.Key, new DotnetRuntimeSelection(correctMajorAndMinorVersion.MaxBy(v => v.InstalledVersion)!.Installed));
}
// Otherwise roll forward to lowest higher minor version
var candidate = available.Where(data => data.InstalledVersion.Major == desired.Value.Major)
.MinBy(v => (v.InstalledVersion.Minor, -v.InstalledVersion.Build));
return (desired.Key, candidate == null ? new DotnetRuntimeSelection() : new DotnetRuntimeSelection(candidate.Installed));
}).ToDictionary();
}
case RollForward.Major:
{
throw new NotImplementedException();
}
case RollForward.LatestPatch:
{
return desiredVersions.Select(desired =>
{
var matches = availableRuntimes[desired.Key]
.Where(data =>
data.InstalledVersion.Minor == desired.Value.Minor &&
data.InstalledVersion.Major == desired.Value.Major).MaxBy(data => data.InstalledVersion);
return matches == null
? (desired.Key, new DotnetRuntimeSelection())
: (desired.Key, new DotnetRuntimeSelection(matches.Installed));
}).ToDictionary();
}
case RollForward.LatestMinor:
{
return desiredVersions.Select(desired =>
{
var matches = availableRuntimes[desired.Key]
.Where(data =>
data.InstalledVersion.Major == desired.Value.Major).MaxBy(data => data.InstalledVersion);
return matches == null
? (desired.Key, new DotnetRuntimeSelection())
: (desired.Key, new DotnetRuntimeSelection(matches.Installed));
}).ToDictionary();
}
case RollForward.LatestMajor:
{
return desiredVersions.Select(desired =>
{
var match = availableRuntimes[desired.Key].MaxBy(data => data.InstalledVersion);
return match == null ? (desired.Key, new DotnetRuntimeSelection()) : (desired.Key, new DotnetRuntimeSelection(match.Installed));
}).ToDictionary();
}
case RollForward.Disable:
{
return desiredVersions.Select(desired =>
{
var exactMatch = availableRuntimes[desired.Key]
.FirstOrDefault(available => available.InstalledVersion == desired.Value);
if (exactMatch != null)
{
return (desired.Key, new DotnetRuntimeSelection(exactMatch.Installed));
}
else
{
return (desired.Key, new DotnetRuntimeSelection());
}
}
).ToDictionary();
}
default:
{
throw new ArgumentOutOfRangeException();
}
}
}
private static JsonSerializerOptions _options = new() {PropertyNameCaseInsensitive = true, Converters = { new JsonStringEnumConverter<RollForward>() }};
/// <summary>
/// Parse a runtimeconfig.json file.
/// </summary>
/// <param name="contents">Contents of the runtimeconfig.json file to parse.</param>
/// <exception cref="NullReferenceException">I think this can't happen, but the docs suggest that deserialization might return null.</exception>
public static RuntimeConfig? DeserializeRuntimeConfig(string contents)
{
return JsonSerializer.Deserialize<RuntimeConfig>(contents, _options);
}
/// <summary>
/// Given a .NET executable DLL, identify the most appropriate .NET runtime to run it.
/// This is pretty half-baked at the moment; test this yourself to make sure it does what you want it to!
/// </summary>
/// <param name="dllPath">Path to an OutputType=Exe .dll file.</param>
/// <param name="dotnet">
/// Path to the `dotnet` binary which you would use e.g. in `dotnet exec` to run the DLL specified by
/// `dllPath`.
/// </param>
/// <returns>
/// An ordered collection of folder paths. When resolving any particular DLL during the execution of the input
/// DLL, search these folders; if a DLL name appears in multiple of these folders, the earliest is correct for that
/// DLL.
/// </returns>
public static IReadOnlyList<string> SelectForDll(string dllPath, string? dotnet = null)
{
if (!dllPath.EndsWith(".dll", StringComparison.Ordinal))
throw new ArgumentException(
$"SelectForDll requires the input DLL to have the extension '.dll'; provided: {dllPath}");
var dll = new FileInfo(dllPath);
var dllParentDir = dll.Directory ?? throw new ArgumentException($"dll path {dllPath} had no parent");
var name = dll.Name.Substring(0, dll.Name.Length - ".dll".Length);
var configFilePath = Path.Combine(dllParentDir.FullName, $"{name}.runtimeconfig.json");
// It appears to be undocumented why this returns a nullable, and the Rider decompiler doesn't suggest there are
// any code paths where it can return null?
var contents = File.ReadAllText(configFilePath);
var runtimeConfig = DeserializeRuntimeConfig(contents) ?? throw new NullReferenceException($"Failed to parse contents of file {configFilePath} as a runtime config");
var availableRuntimes = dotnet == null
? DotnetEnvironmentInfo.Get()
: DotnetEnvironmentInfo.GetSpecific(new FileInfo(dotnet));
var runtimes = SelectRuntime(runtimeConfig.RuntimeOptions, availableRuntimes);
return runtimes.SelectMany(runtime => runtime.Value.Visit(framework => new[] { $"{framework.Path}/{framework.Version}" },
sdk => [sdk.Path],
() => []
)).Prepend(dllParentDir.FullName).ToList();
}
private record RuntimeOnDisk(
DotnetEnvironmentFrameworkInfo Installed,
Version InstalledVersion);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.InteropServices;
namespace WoofWare.DotnetRuntimeLocator;

View File

@@ -0,0 +1,120 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace WoofWare.DotnetRuntimeLocator;
/// <summary>
/// The type of a "framework" entry in the "runtimeOptions" setting of a runtimeconfig.json file.
/// </summary>
public record RuntimeConfigFramework
{
/// <summary>
/// For example, "Microsoft.NETCore.App".
/// </summary>
[JsonPropertyName("name")]
public required string Name { get; init; }
/// <summary>
/// For example, "9.0.0".
/// </summary>
[JsonPropertyName("version")]
public required string Version { get; init; }
}
/// <summary>
/// The value of e.g. `--roll-forward` or DOTNET_ROLL_FORWARD.
/// </summary>
public enum RollForward
{
/// <summary>
/// If the requested version is missing, roll forward to the lowest available minor version higher than requested.
/// If the requested version is available, silently use the LatestPatch policy.
/// Minor is the default if unspecified.
/// </summary>
Minor,
/// <summary>
/// If the requested version is missing, roll forward to the lowest available major version higher than requested,
/// at "lowest minor version" (the docs are unclear whether this means "lowest *available*", or "0").
/// If the requested version is available, silently use the Minor policy.
/// </summary>
Major,
/// <summary>
/// Roll forward to the highest patch version at exactly the requested major and minor versions.
/// </summary>
LatestPatch,
/// <summary>
/// Roll forward to the highest minor version, even if the requested minor version is available.
/// </summary>
LatestMinor,
/// <summary>
/// Roll forward to the highest available major version and highest available minor version at that major version,
/// even if the requested version is available.
/// </summary>
LatestMajor,
/// <summary>
/// Suppress all rolling forward: use only the exact specified version.
/// </summary>
Disable
}
/// <summary>
/// The contents of the "runtimeOptions" key in a runtimeconfig.json file.
/// </summary>
public record RuntimeOptions
{
/// Target framework moniker, such as "net9.0".
[JsonPropertyName("tfm")]
public required string Tfm { get; init; }
/// <summary>
/// The .NET runtime which this executable expects.
/// This is optional, because you can instead specify multiple Frameworks, in which case any of the frameworks
/// is acceptable (according to Claude; the MS docs are impenetrable as ever).
/// </summary>
[JsonPropertyName("framework")]
public RuntimeConfigFramework? Framework { get; init; }
/// <summary>
/// Any of these runtimes by itself would be enough to run this executable.
/// It's much more normal to see a single `framework` instead of this.
/// </summary>
[JsonPropertyName("frameworks")]
public IReadOnlyList<RuntimeConfigFramework>? Frameworks { get; init; }
/// <summary>
/// This is a self-contained executable which has these framework entirely contained next to it.
/// </summary>
[JsonPropertyName("includedFrameworks")]
public IReadOnlyList<RuntimeConfigFramework>? IncludedFrameworks { get; init; }
/// <summary>
/// This application advertises that it's fine with running under this roll-forward.
/// </summary>
[JsonPropertyName("rollForward")]
public RollForward? RollForward { get; init; }
}
/// <summary>
/// The contents of a runtimeconfig.json file.
/// Note that this record doesn't capture everything: for example, "configProperties" might be present in the file,
/// but is not represented in this type.
/// </summary>
public record RuntimeConfig
{
/// <summary>
/// The contents of the file.
/// </summary>
[JsonPropertyName("runtimeOptions")]
public required RuntimeOptions RuntimeOptions { get; init; }
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(RuntimeConfig))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}

View File

@@ -44,4 +44,54 @@ WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.op_Inequality [static met
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.Path [property]: string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.set_Path [method]: string -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.set_Version [method]: string -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.Version [property]: string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.Version [property]: string
WoofWare.DotnetRuntimeLocator.DotnetRuntime inherit obj
WoofWare.DotnetRuntimeLocator.DotnetRuntime.DeserializeRuntimeConfig [static method]: string -> WoofWare.DotnetRuntimeLocator.RuntimeConfig
WoofWare.DotnetRuntimeLocator.DotnetRuntime.SelectForDll [static method]: (string, string) -> string System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.RollForward inherit System.Enum
WoofWare.DotnetRuntimeLocator.RollForward.Disable [static field]: WoofWare.DotnetRuntimeLocator.RollForward = Disable
WoofWare.DotnetRuntimeLocator.RollForward.LatestMajor [static field]: WoofWare.DotnetRuntimeLocator.RollForward = LatestMajor
WoofWare.DotnetRuntimeLocator.RollForward.LatestMinor [static field]: WoofWare.DotnetRuntimeLocator.RollForward = LatestMinor
WoofWare.DotnetRuntimeLocator.RollForward.LatestPatch [static field]: WoofWare.DotnetRuntimeLocator.RollForward = LatestPatch
WoofWare.DotnetRuntimeLocator.RollForward.Major [static field]: WoofWare.DotnetRuntimeLocator.RollForward = Major
WoofWare.DotnetRuntimeLocator.RollForward.Minor [static field]: WoofWare.DotnetRuntimeLocator.RollForward = Minor
WoofWare.DotnetRuntimeLocator.RollForward.value__ [field]: int
WoofWare.DotnetRuntimeLocator.RuntimeConfig inherit obj, implements WoofWare.DotnetRuntimeLocator.RuntimeConfig System.IEquatable
WoofWare.DotnetRuntimeLocator.RuntimeConfig..ctor [constructor]: unit
WoofWare.DotnetRuntimeLocator.RuntimeConfig.<Clone>$ [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfig
WoofWare.DotnetRuntimeLocator.RuntimeConfig.get_RuntimeOptions [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeOptions
WoofWare.DotnetRuntimeLocator.RuntimeConfig.op_Equality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeConfig, WoofWare.DotnetRuntimeLocator.RuntimeConfig) -> bool
WoofWare.DotnetRuntimeLocator.RuntimeConfig.op_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeConfig, WoofWare.DotnetRuntimeLocator.RuntimeConfig) -> bool
WoofWare.DotnetRuntimeLocator.RuntimeConfig.RuntimeOptions [property]: WoofWare.DotnetRuntimeLocator.RuntimeOptions
WoofWare.DotnetRuntimeLocator.RuntimeConfig.set_RuntimeOptions [method]: WoofWare.DotnetRuntimeLocator.RuntimeOptions -> unit
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework inherit obj, implements WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.IEquatable
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework..ctor [constructor]: unit
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.<Clone>$ [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.get_Name [method]: unit -> string
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.get_Version [method]: unit -> string
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.Name [property]: string
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.op_Equality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework, WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework) -> bool
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.op_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework, WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework) -> bool
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.set_Name [method]: string -> unit
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.set_Version [method]: string -> unit
WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework.Version [property]: string
WoofWare.DotnetRuntimeLocator.RuntimeOptions inherit obj, implements WoofWare.DotnetRuntimeLocator.RuntimeOptions System.IEquatable
WoofWare.DotnetRuntimeLocator.RuntimeOptions..ctor [constructor]: unit
WoofWare.DotnetRuntimeLocator.RuntimeOptions.<Clone>$ [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeOptions
WoofWare.DotnetRuntimeLocator.RuntimeOptions.Framework [property]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework
WoofWare.DotnetRuntimeLocator.RuntimeOptions.Frameworks [property]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Framework [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Frameworks [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_IncludedFrameworks [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_RollForward [method]: unit -> WoofWare.DotnetRuntimeLocator.RollForward System.Nullable
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Tfm [method]: unit -> string
WoofWare.DotnetRuntimeLocator.RuntimeOptions.IncludedFrameworks [property]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.RuntimeOptions.op_Equality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeOptions, WoofWare.DotnetRuntimeLocator.RuntimeOptions) -> bool
WoofWare.DotnetRuntimeLocator.RuntimeOptions.op_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeOptions, WoofWare.DotnetRuntimeLocator.RuntimeOptions) -> bool
WoofWare.DotnetRuntimeLocator.RuntimeOptions.RollForward [property]: WoofWare.DotnetRuntimeLocator.RollForward System.Nullable
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Framework [method]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework -> unit
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Frameworks [method]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList -> unit
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_IncludedFrameworks [method]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList -> unit
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_RollForward [method]: WoofWare.DotnetRuntimeLocator.RollForward System.Nullable -> unit
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Tfm [method]: string -> unit
WoofWare.DotnetRuntimeLocator.RuntimeOptions.Tfm [property]: string

View File

@@ -0,0 +1,21 @@
namespace WoofWare.DotnetRuntimeLocator.Test
open System
open System.IO
open System.Reflection
[<RequireQualifiedAccess>]
module Assembly =
let getEmbeddedResource (assembly : Assembly) (name : string) : string =
let names = assembly.GetManifestResourceNames ()
let names =
names |> Seq.filter (fun s -> s.EndsWith (name, StringComparison.Ordinal))
use s =
names
|> Seq.exactlyOne
|> assembly.GetManifestResourceStream
|> fun s -> new StreamReader (s)
s.ReadToEnd ()

View File

@@ -1,27 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<NuGetAudit>false</NuGetAudit>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<NuGetAudit>false</NuGetAudit>
</PropertyGroup>
<ItemGroup>
<Compile Include="TestSurface.fs" />
<Compile Include="TestDotnetEnvironmentInfo.fs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Assembly.fs" />
<Compile Include="TestDotnetRuntime.fs" />
<Compile Include="TestRuntimeConfigParse.fs"/>
<Compile Include="TestSurface.fs"/>
<Compile Include="TestDotnetEnvironmentInfo.fs"/>
<EmbeddedResource Include="runtimeconfig1.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.40" />
<PackageReference Include="FsUnit" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="NUnit" Version="4.1.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="ApiSurface" Version="5.0.1"/>
<PackageReference Include="FsUnit" Version="7.1.1"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
<PackageReference Include="NUnit" Version="4.4.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="5.1.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WoofWare.DotnetRuntimeLocator.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WoofWare.DotnetRuntimeLocator.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,36 @@
namespace WoofWare.DotnetRuntimeLocator.Test
open System.IO
open System.Reflection
open NUnit.Framework
open WoofWare.DotnetRuntimeLocator
[<TestFixture>]
module TestDotnetRuntime =
let inline shouldBeSome (x : 'a option) : unit =
match x with
| None -> failwith "option was None"
| Some _ -> ()
let inline shouldBeNone (x : 'a option) : unit =
match x with
| Some x -> failwith $"expectd None, but option was Some %O{x}"
| None -> ()
[<Test>]
let ``Test DotnetRuntime`` () =
let assy = Assembly.GetExecutingAssembly ()
let selectedRuntime = DotnetRuntime.SelectForDll assy.Location
let existsDll (name : string) =
selectedRuntime
|> Seq.tryPick (fun dir ->
let attempt = Path.Combine (dir, name)
if File.Exists attempt then Some attempt else None
)
existsDll "System.Private.CoreLib.dll" |> shouldBeSome
existsDll "System.Text.Json.dll" |> shouldBeSome
existsDll "Test.dll" |> shouldBeSome
existsDll "blah-de-blah.dll" |> shouldBeNone

View File

@@ -0,0 +1,49 @@
namespace WoofWare.DotnetRuntimeLocator.Test
open System.IO
open System.Reflection
open System.Text.Json
open FsUnitTyped
open NUnit.Framework
open WoofWare.DotnetRuntimeLocator
[<TestFixture>]
module TestRuntimeConfigParse =
[<Test>]
let ``Can parse our own runtime config`` () =
let assy = Assembly.GetExecutingAssembly ()
let runtimeConfig =
Path.Combine (FileInfo(assy.Location).Directory.FullName, $"%s{assy.GetName().Name}.runtimeconfig.json")
|> File.ReadAllText
let actual = JsonSerializer.Deserialize<RuntimeConfig> runtimeConfig
let expected =
RuntimeConfig (
RuntimeOptions =
RuntimeOptions (
Tfm = "net8.0",
Framework = RuntimeConfigFramework (Name = "Microsoft.NETCore.App", Version = "8.0.0")
)
)
actual |> shouldEqual expected
[<Test>]
let ``Example 1`` () =
let content =
Assembly.getEmbeddedResource (Assembly.GetExecutingAssembly ()) "runtimeconfig1.json"
let expected =
RuntimeConfig (
RuntimeOptions =
RuntimeOptions (
Tfm = "net8.0",
RollForward = RollForward.Major,
Framework = RuntimeConfigFramework (Name = "Microsoft.NETCore.App", Version = "8.0.0")
)
)
DotnetRuntime.DeserializeRuntimeConfig content |> shouldEqual expected

View File

@@ -14,10 +14,11 @@ module TestSurface =
let ``Update API surface`` () =
ApiSurface.writeAssemblyBaseline assembly
[<Test>]
[<Test ; Explicit "Bug in ApiSurface: https://github.com/G-Research/ApiSurface/pull/111">]
let ``Ensure public API is fully documented`` () =
DocCoverage.assertFullyDocumented assembly
[<Test ; Explicit "Not yet published">]
let ``Ensure version is monotonic`` () =
[<Test>]
// https://github.com/nunit/nunit3-vs-adapter/issues/876
let ``EnsureVersionIsMonotonic`` () =
MonotonicVersion.validate assembly "WoofWare.DotnetRuntimeLocator"

View File

@@ -0,0 +1,13 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"rollForward": "Major",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
"configProperties": {
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<EnableDefaultItems>false</EnableDefaultItems>
@@ -23,8 +23,10 @@
<Compile Include="DotnetEnvironmentFrameworkInfo.cs"/>
<Compile Include="DotnetEnvironmentInfo.cs"/>
<Compile Include="DotnetEnvironmentSdkInfo.cs"/>
<Compile Include="DotnetRuntime.cs" />
<Compile Include="InteropStructs.cs"/>
<Compile Include="Boilerplate.cs"/>
<Compile Include="RuntimeConfigOptions.cs"/>
<EmbeddedResource Include="SurfaceBaseline.txt"/>
<EmbeddedResource Include="version.json"/>
<None Include="..\README.md">

View File

@@ -1,10 +1,12 @@
{
"version": "0.1",
"publicReleaseRefSpec": ["^refs/heads/main$"],
"version": "0.4",
"publicReleaseRefSpec": [
"^refs/heads/main$"
],
"pathFilters": [
"^Test/",
":!Test/",
":/WoofWare.DotnetRuntimeLocator",
":/Directory.Build.props",
":/README.md"
]
}
}

6
flake.lock generated
View File

@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1731890469,
"narHash": "sha256-D1FNZ70NmQEwNxpSSdTXCSklBH1z2isPR84J6DQrJGs=",
"lastModified": 1759536663,
"narHash": "sha256-hhM8SUI6kQMei5TImFdNQy9EDT8g2hAD161DUtbfAy0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5083ec887760adfe12af64830a66807423a859a7",
"rev": "27ac93958969b5f3dccd654b402599cf3de633ac",
"type": "github"
},
"original": {

View File

@@ -42,15 +42,18 @@
'';
};
in {
packages = {
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") ((import ./nix/deps.nix) {fetchNuGet = x: x;}))).hash;
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;
default = pkgs.buildDotnetModule {
inherit pname version dotnet-sdk dotnet-runtime;
name = "WoofWare.Myriad.Plugins";
name = "WoofWare.DotnetRuntimeLocator";
src = ./.;
projectFile = "./WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj";
testProjectFile = "./WoofWare.DotnetRuntimeLocator/Test/Test.fsproj";
nugetDeps = ./nix/deps.nix; # `nix build .#default.passthru.fetch-deps && ./result nix/deps.nix`
disabledTests = ["WoofWare.DotnetRuntimeLocator.Test.TestSurface.EnsureVersionIsMonotonic"];
nugetDeps = ./nix/deps.json; # `nix build .#default.fetch-deps && ./result nix/deps.json`
doCheck = true;
};
};
@@ -60,6 +63,7 @@
pkgs.alejandra
pkgs.nodePackages.markdown-link-check
pkgs.shellcheck
pkgs.xmlstarlet
];
};
});

172
nix/deps.json Normal file
View File

@@ -0,0 +1,172 @@
[
{
"pname": "ApiSurface",
"version": "5.0.1",
"hash": "sha256-0GMXEMFgWbbE2OGxW+6h4zGgQHg+IZy1aI13Dn97xSU="
},
{
"pname": "fantomas",
"version": "6.3.8",
"hash": "sha256-dNbs5+gzjjMw2fl0ZRB9qzj9d+rz1o3Wi/qUvBDoz2E="
},
{
"pname": "FSharp.Core",
"version": "8.0.403",
"hash": "sha256-3XSQp7JUOU5T6gvSQXNfBF4t4gaX4J4xushH+cfM9mE="
},
{
"pname": "FsUnit",
"version": "7.1.1",
"hash": "sha256-UMCEGKxQ4ytjmPuVpiNaAPbi3RQH9gqa61JJIUS/6hg="
},
{
"pname": "Microsoft.ApplicationInsights",
"version": "2.23.0",
"hash": "sha256-5sf3bg7CZZjHseK+F3foOchEhmVeioePxMZVvS6Rjb0="
},
{
"pname": "Microsoft.CodeCoverage",
"version": "17.14.1",
"hash": "sha256-f8QytG8GvRoP47rO2KEmnDLxIpyesaq26TFjDdW40Gs="
},
{
"pname": "Microsoft.NET.Test.Sdk",
"version": "17.14.1",
"hash": "sha256-mZUzDFvFp7x1nKrcnRd0hhbNu5g8EQYt8SKnRgdhT/A="
},
{
"pname": "Microsoft.Testing.Extensions.Telemetry",
"version": "1.7.3",
"hash": "sha256-Z6WsY2FCUbNnT5HJd7IOrfOvqknVXp6PWzTVeb0idVg="
},
{
"pname": "Microsoft.Testing.Extensions.TrxReport.Abstractions",
"version": "1.7.3",
"hash": "sha256-PTee04FHyTHx/gF5NLckXuVje807G51MzkPrZ1gkgCw="
},
{
"pname": "Microsoft.Testing.Extensions.VSTestBridge",
"version": "1.7.3",
"hash": "sha256-8d+wZmucfSO7PsviHjVxYB4q6NcjgxvnCUpLePq35sM="
},
{
"pname": "Microsoft.Testing.Platform",
"version": "1.7.3",
"hash": "sha256-cavX11P5o9rooqC3ZHw5h002OKRg2ZNR/VaRwpNTQYA="
},
{
"pname": "Microsoft.Testing.Platform.MSBuild",
"version": "1.7.3",
"hash": "sha256-cREl529UQ/c5atT8KimMgrgNdy6MrAd0sBGT8sXRRPM="
},
{
"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": "17.14.1",
"hash": "sha256-QMf6O+w0IT+16Mrzo7wn+N20f3L1/mDhs/qjmEo1rYs="
},
{
"pname": "Microsoft.TestPlatform.TestHost",
"version": "17.14.1",
"hash": "sha256-1cxHWcvHRD7orQ3EEEPPxVGEkTpxom1/zoICC9SInJs="
},
{
"pname": "Nerdbank.GitVersioning",
"version": "3.8.38-alpha",
"hash": "sha256-gPMrVbjOZxXoofczF/pn6eVkLhjVSJIyQrLO2oljrDc="
},
{
"pname": "Newtonsoft.Json",
"version": "13.0.3",
"hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc="
},
{
"pname": "NuGet.Common",
"version": "6.14.0",
"hash": "sha256-jDOwt3veI1GSG8CfBnf2+dJxD3E/Nmlc+vHtD4J76Ms="
},
{
"pname": "NuGet.Configuration",
"version": "6.14.0",
"hash": "sha256-1PN9s6fhCw3wd2260U6hQ4vG3jIvcG8GIn1oQgxMXA0="
},
{
"pname": "NuGet.Frameworks",
"version": "6.14.0",
"hash": "sha256-3ViM3R1ucQMEL2hQYsivT86kI6veMQK2xDsiAcFcVQk="
},
{
"pname": "NuGet.Packaging",
"version": "6.14.0",
"hash": "sha256-Yafbnxs3maj55bJ1oKQiZ0QkntFUzXdhorL94YEUOhY="
},
{
"pname": "NuGet.Protocol",
"version": "6.14.0",
"hash": "sha256-uLDKfs+QN1MdnuQtTES8qfNzzsmYKM6XB9pwJc4G+eo="
},
{
"pname": "NuGet.Versioning",
"version": "6.14.0",
"hash": "sha256-DqdOJgsphKxSvqB8b60zNPCaiLfbiu3WnUJ/90feLrY="
},
{
"pname": "NUnit",
"version": "4.4.0",
"hash": "sha256-5geF5QOF+X/WkuCEgkPVKH4AdKx4U0olpU07S8+G3nU="
},
{
"pname": "NUnit3TestAdapter",
"version": "5.1.0",
"hash": "sha256-5z470sFjV67wGHaw8KfmSloIAYe81Dokp0f8I6zXsDc="
},
{
"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.Formats.Asn1",
"version": "6.0.0",
"hash": "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8="
},
{
"pname": "System.Reflection.Metadata",
"version": "1.6.0",
"hash": "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E="
},
{
"pname": "System.Reflection.Metadata",
"version": "8.0.0",
"hash": "sha256-dQGC30JauIDWNWXMrSNOJncVa1umR1sijazYwUDdSIE="
},
{
"pname": "System.Security.Cryptography.Pkcs",
"version": "6.0.4",
"hash": "sha256-2e0aRybote+OR66bHaNiYpF//4fCiaO3zbR2e9GABUI="
},
{
"pname": "System.Security.Cryptography.ProtectedData",
"version": "4.4.0",
"hash": "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE="
},
{
"pname": "System.Text.Json",
"version": "8.0.5",
"hash": "sha256-yKxo54w5odWT6nPruUVsaX53oPRe+gKzGvLnnxtwP68="
}
]

View File

@@ -1,224 +0,0 @@
# This file was automatically generated by passthru.fetch-deps.
# Please dont edit it manually, your changes might get overwritten!
{fetchNuGet}: [
(fetchNuGet {
pname = "ApiSurface";
version = "4.0.40";
hash = "sha256-oO9V30T9+QN/DKSTqS6jRH7ES/NkV5n5jZnaWM0CP7E=";
})
(fetchNuGet {
pname = "fantomas";
version = "6.3.8";
hash = "sha256-dNbs5+gzjjMw2fl0ZRB9qzj9d+rz1o3Wi/qUvBDoz2E=";
})
(fetchNuGet {
pname = "FSharp.Core";
version = "8.0.401";
hash = "sha256-+tp7/Ssr5lb55ZBTOjTuuH0rLCGfhe5Yjq4jvU5KML0=";
})
(fetchNuGet {
pname = "FsUnit";
version = "6.0.0";
hash = "sha256-q87WQf6MqGhzvaQ7WkkUlCdoE94DY0CD5PaXEj64A6M=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Ref";
version = "6.0.35";
hash = "sha256-BxvIeZIaBdC0wyDQqKW0E5axSRSrtQk3oEPsT287014=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
version = "6.0.35";
hash = "sha256-jM/HzLumZvI939DrNb8LHnEr/in1Lws0j/FAfdXSzbk=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
version = "6.0.35";
hash = "sha256-2eUqoTcqTU3ebv53IV6yvN9EhkOqnyBRd2tz74HuSsE=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
version = "6.0.35";
hash = "sha256-6mY2uBhvKCpEFJLYX9+f1mpYrWdN69i+14DPjO4U8eo=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
version = "6.0.35";
hash = "sha256-ljEkMgkgfEeqzRnmTubjSK2dzkph0cSQ7+2J986F7HI=";
})
(fetchNuGet {
pname = "Microsoft.CodeCoverage";
version = "17.10.0";
hash = "sha256-yQFwqVChRtIRpbtkJr92JH2i+O7xn91NGbYgnKs8G2g=";
})
(fetchNuGet {
pname = "Microsoft.NET.Test.Sdk";
version = "17.10.0";
hash = "sha256-rkHIqB2mquNXF89XBTFpUL2z5msjTBsOcyjSBCh36I0=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-arm64";
version = "6.0.35";
hash = "sha256-yrtPCYD8skaWnfIoaUdQ1dns0YrypxDocskS2WGxF6g=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-x64";
version = "6.0.35";
hash = "sha256-maNzxJQ5oCd86VI4ROzl4RqOV1RNXn3qWjrAfBjr2Y0=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-arm64";
version = "6.0.35";
hash = "sha256-cBcfv7tnZa2xO5T5VOx3/7EvJ5u4/C4dFnV1Jj6VFPU=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-x64";
version = "6.0.35";
hash = "sha256-05wMp5+etiV/vgktqGo8+4XB7FNYxwCUKpJsW48tgvQ=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Ref";
version = "6.0.35";
hash = "sha256-IcpSbsSHgYBbNVvbcXfmRRM9bdx3pogLncO4RuXEab0=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
version = "6.0.35";
hash = "sha256-jPUhSrzqnH1GNi/c7dSnZSQhFNVGdmlAQkDLdXVWBBc=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
version = "6.0.35";
hash = "sha256-Gf3e0EdBEgq8GcZttTHbKGupFlDyB80nhYpBN0X9Kro=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
version = "6.0.35";
hash = "sha256-IGArFhlq3UzZY93lJ+WrB+zmuu/2o8lVwT7MJKpz6DE=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
version = "6.0.35";
hash = "sha256-EtFBg8yBNhAEQlL97oVGiu05rPMSKLd0wE44zTBT7FI=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.Platforms";
version = "2.0.0";
hash = "sha256-IEvBk6wUXSdyCnkj6tHahOJv290tVVT8tyemYcR0Yro=";
})
(fetchNuGet {
pname = "Microsoft.TestPlatform.ObjectModel";
version = "17.10.0";
hash = "sha256-3YjVGK2zEObksBGYg8b/CqoJgLQ1jUv4GCWNjDhLRh4=";
})
(fetchNuGet {
pname = "Microsoft.TestPlatform.TestHost";
version = "17.10.0";
hash = "sha256-+yzP3FY6WoOosSpYnB7duZLhOPUZMQYy8zJ1d3Q4hK4=";
})
(fetchNuGet {
pname = "Nerdbank.GitVersioning";
version = "3.6.133";
hash = "sha256-AEnBQaGGPMBmZJjZrdWARq/jY4SluuPIsKBbvfNEvLE=";
})
(fetchNuGet {
pname = "Newtonsoft.Json";
version = "13.0.1";
hash = "sha256-K2tSVW4n4beRPzPu3rlVaBEMdGvWSv/3Q1fxaDh4Mjo=";
})
(fetchNuGet {
pname = "Newtonsoft.Json";
version = "13.0.3";
hash = "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=";
})
(fetchNuGet {
pname = "NuGet.Common";
version = "6.10.0";
hash = "sha256-cM3Q7srAykk6KA/FThZkDjs+MIIjiUJSXZjRSqPNP1o=";
})
(fetchNuGet {
pname = "NuGet.Configuration";
version = "6.10.0";
hash = "sha256-H/qaaKbyp3oo/w2dBvaXEhG5+Tgcp75tJR1brpSdCqs=";
})
(fetchNuGet {
pname = "NuGet.Frameworks";
version = "6.10.0";
hash = "sha256-E88PUsK5J0b9c0sN+xNZU+X1MOCfJaIvByr1H4ZHLUM=";
})
(fetchNuGet {
pname = "NuGet.Packaging";
version = "6.10.0";
hash = "sha256-RK18XuOdmXPXhmBLRGjw00QsLiKuYawqjDQUlzcbRaM=";
})
(fetchNuGet {
pname = "NuGet.Protocol";
version = "6.10.0";
hash = "sha256-TPWWce4FhI3VaDhAR6mUYydFA6AjrPtcJSMmPQEmu0I=";
})
(fetchNuGet {
pname = "NuGet.Versioning";
version = "6.10.0";
hash = "sha256-r8w4/bin+ZQK6AzkuFXhWbteQlzRFY++S1yDTrq0KfQ=";
})
(fetchNuGet {
pname = "NUnit";
version = "4.1.0";
hash = "sha256-srzj0lf2ReKw41TnigZwf8rqKKNzGRRVrgN3hR/vRjo=";
})
(fetchNuGet {
pname = "NUnit3TestAdapter";
version = "4.5.0";
hash = "sha256-ER3ogl0L5FYyc6pVVPY1ch+AQxG/WgFcnWECnYQJPes=";
})
(fetchNuGet {
pname = "System.Formats.Asn1";
version = "6.0.0";
hash = "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8=";
})
(fetchNuGet {
pname = "System.IO.Abstractions";
version = "4.2.13";
hash = "sha256-nkC/PiqE6+c1HJ2yTwg3x+qdBh844Z8n3ERWDW8k6Gg=";
})
(fetchNuGet {
pname = "System.IO.FileSystem.AccessControl";
version = "4.5.0";
hash = "sha256-ck44YBQ0M+2Im5dw0VjBgFD1s0XuY54cujrodjjSBL8=";
})
(fetchNuGet {
pname = "System.Reflection.Metadata";
version = "1.6.0";
hash = "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E=";
})
(fetchNuGet {
pname = "System.Security.AccessControl";
version = "4.5.0";
hash = "sha256-AFsKPb/nTk2/mqH/PYpaoI8PLsiKKimaXf+7Mb5VfPM=";
})
(fetchNuGet {
pname = "System.Security.Cryptography.Pkcs";
version = "6.0.4";
hash = "sha256-2e0aRybote+OR66bHaNiYpF//4fCiaO3zbR2e9GABUI=";
})
(fetchNuGet {
pname = "System.Security.Cryptography.ProtectedData";
version = "4.4.0";
hash = "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE=";
})
(fetchNuGet {
pname = "System.Security.Principal.Windows";
version = "4.5.0";
hash = "sha256-BkUYNguz0e4NJp1kkW7aJBn3dyH9STwB5N8XqnlCsmY=";
})
(fetchNuGet {
pname = "System.Text.Encodings.Web";
version = "7.0.0";
hash = "sha256-tF8qt9GZh/nPy0mEnj6nKLG4Lldpoi/D8xM5lv2CoYQ=";
})
(fetchNuGet {
pname = "System.Text.Json";
version = "7.0.3";
hash = "sha256-aSJZ17MjqaZNQkprfxm/09LaCoFtpdWmqU9BTROzWX4=";
})
]