Add DistroLauncher
@@ -1,11 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Setup MSBuild 🧰
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
with:
|
||||
vs-version: '[17.2,'
|
||||
@@ -0,0 +1,21 @@
|
||||
outputs:
|
||||
version:
|
||||
description: 'The generated version number'
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Generate/Set Version 🏷️
|
||||
id: version
|
||||
shell: pwsh
|
||||
run: |
|
||||
$LATEST_TAG = git describe --tags --abbrev=0 # Get the latest tag name
|
||||
$COMMIT_COUNT = git rev-list --use-bitmap-index --count "$(git rev-list --tags --no-walk --max-count=1)..HEAD" # Count all commits since the last tag
|
||||
$NIXOS_WSL_VERSION = "$($LATEST_TAG -replace '(.+)\.(.+)\.(.+)\..+', '$1.$2.$3').${COMMIT_COUNT}" # Compose the NixOS-WSL version number
|
||||
Write-Output "version=${NIXOS_WSL_VERSION}" >> $env:GITHUB_OUTPUT
|
||||
|
||||
Write-Output $NIXOS_WSL_VERSION > ./VERSION
|
||||
Write-Output $(git rev-parse HEAD) >> ./VERSION
|
||||
((Get-Content -path ./Launcher/Launcher/Launcher.csproj -Raw) -replace '1.3.3.7',$NIXOS_WSL_VERSION) | Set-Content -Path ./Launcher/Launcher/Launcher.csproj
|
||||
((Get-Content -path ./Launcher/Launcher-Appx/Package.appxmanifest -Raw) -replace '1.3.3.7',$NIXOS_WSL_VERSION) | Set-Content -Path ./Launcher/Launcher-Appx/Package.appxmanifest
|
||||
@@ -2,14 +2,23 @@ name: "Push"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [main, distro-launcher]
|
||||
pull_request: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Tarballs 🛠️
|
||||
name: Build 🛠️
|
||||
uses: ./.github/workflows/run_build.yml
|
||||
|
||||
package:
|
||||
name: Package 📦
|
||||
uses: ./.github/workflows/run_package.yml
|
||||
needs:
|
||||
- build
|
||||
secrets:
|
||||
APPX_SIGNING_CERT: ${{ secrets.APPX_SIGNING_CERT }}
|
||||
CERT_ALGORITHM: ${{ secrets.CERT_ALGORITHM }}
|
||||
|
||||
checks:
|
||||
name: Flake Checks 📋
|
||||
uses: ./.github/workflows/run_checks.yml
|
||||
|
||||
@@ -6,23 +6,41 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Tarballs 🛠️
|
||||
name: Build 🛠️
|
||||
uses: ./.github/workflows/run_build.yml
|
||||
|
||||
package:
|
||||
name: Package 📦
|
||||
uses: ./.github/workflows/run_package.yml
|
||||
needs:
|
||||
- build
|
||||
secrets:
|
||||
APPX_SIGNING_CERT: ${{ secrets.APPX_SIGNING_CERT }}
|
||||
CERT_ALGORITHM: ${{ secrets.CERT_ALGORITHM }}
|
||||
|
||||
release:
|
||||
needs:
|
||||
- build
|
||||
- package
|
||||
name: Create Release 📢
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Tarball 📥
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Download appx 📥
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: appx
|
||||
|
||||
- name: Rename appx ✏️
|
||||
run: mv */*.msixbundle NixOS-WSL.msixbundle
|
||||
|
||||
- name: Generate Checksums 🔑
|
||||
run: |
|
||||
mv tarball-modern/nixos-wsl.tar.gz nixos-wsl.tar.gz
|
||||
mv tarball-legacy/nixos-wsl.tar.gz nixos-wsl-legacy.tar.gz
|
||||
for x in *.tar.gz; do
|
||||
for x in *.{tar.gz,msixbundle}; do
|
||||
sha256sum $x > ${x}.sha256
|
||||
done
|
||||
|
||||
@@ -34,5 +52,7 @@ jobs:
|
||||
nixos-wsl.tar.gz.sha256
|
||||
nixos-wsl-legacy.tar.gz
|
||||
nixos-wsl-legacy.tar.gz.sha256
|
||||
NixOS-WSL.msixbundle
|
||||
NixOS-WSL.msixbundle.sha256
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
name: Build Tarballs
|
||||
name: Build Tarball
|
||||
|
||||
on:
|
||||
workflow_call: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build 🛠️
|
||||
tar:
|
||||
name: Tarball 🛠️
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -22,17 +22,12 @@ jobs:
|
||||
- name: Install Nix ❄️
|
||||
uses: ./.github/actions/install-nix
|
||||
|
||||
- name: Generate Version 🏷️
|
||||
- name: Set Version 🏷️
|
||||
uses: ./.github/actions/version
|
||||
id: version
|
||||
shell: pwsh
|
||||
run: |
|
||||
$LATEST_TAG = git describe --tags --abbrev=0 # Get the latest tag name
|
||||
$COMMIT_COUNT = git rev-list --use-bitmap-index --count "$(git rev-list --tags --no-walk --max-count=1)..HEAD" # Count all commits since the last tag
|
||||
$NIXOS_WSL_VERSION = "$($LATEST_TAG -replace '(.+)\.(.+)\.(.+)\..+', '$1.$2.$3').${COMMIT_COUNT}" # Compose the NixOS-WSL version number
|
||||
echo "version=$NIXOS_WSL_VERSION" >> $env:GITHUB_OUTPUT
|
||||
|
||||
echo $NIXOS_WSL_VERSION > ./VERSION
|
||||
echo $(git rev-parse HEAD) >> ./VERSION
|
||||
- name: Summary Headline 📝
|
||||
run: echo '## NixOS-WSL ${{ steps.version.outputs.version }}' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Build Tarball 🛠️
|
||||
uses: ./.github/actions/build-wsl-tarball
|
||||
@@ -45,3 +40,31 @@ jobs:
|
||||
with:
|
||||
name: tarball-${{ matrix.config }}
|
||||
path: nixos-wsl.tar.gz
|
||||
|
||||
launcher:
|
||||
name: Launcher 🛠️
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup MSBuild 🧰
|
||||
uses: ./.github/actions/setup-msbuild
|
||||
|
||||
- name: Set Versions 🏷️
|
||||
uses: ./.github/actions/version
|
||||
|
||||
- name: Build Launcher
|
||||
run: |
|
||||
cd Launcher
|
||||
dotnet publish --self-contained -r win-x64 -c Release -p:ContinuousIntegrationBuild=true -p:Deterministic=true --output Launcher\pkg Launcher
|
||||
|
||||
- name: Upload Launcher 📤
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-bin
|
||||
path: |
|
||||
Launcher/Launcher/obj
|
||||
Launcher/Launcher/pkg
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
name: Build Tarball
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
APPX_SIGNING_CERT: {}
|
||||
CERT_ALGORITHM: {}
|
||||
|
||||
jobs:
|
||||
launcher-bundle:
|
||||
name: ZIP 📦
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download tarball 📥
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tarball-modern
|
||||
|
||||
- name: Download launcher binaries 📥
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: launcher-bin
|
||||
path: Launcher/Launcher/
|
||||
|
||||
- name: Bundle tarball with launcher 📦
|
||||
run: |
|
||||
Copy-Item nixos-wsl.tar.gz Launcher\Launcher\pkg\
|
||||
|
||||
- name: Upload Launcher 📤
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-bundle
|
||||
path: Launcher/Launcher/pkg
|
||||
|
||||
launcher-msix:
|
||||
name: MSIX 📦
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download tarball 📥
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tarball-modern
|
||||
|
||||
- name: Download launcher binaries 📥
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: launcher-bin
|
||||
path: Launcher/Launcher/
|
||||
|
||||
- name: Bundle tarball with launcher 📦
|
||||
run: |
|
||||
Copy-Item nixos-wsl.tar.gz Launcher\Launcher-Appx\
|
||||
|
||||
- name: Setup MSBuild 🧰
|
||||
uses: ./.github/actions/setup-msbuild
|
||||
|
||||
- name: Set Versions 🏷️
|
||||
uses: ./.github/actions/version
|
||||
|
||||
- name: Build MSIX package 📦
|
||||
run: |
|
||||
cd Launcher
|
||||
nuget restore
|
||||
msbuild Launcher-Appx\Launcher-Appx.wapproj /p:Configuration=Release /p:Platform=x64 /p:ContinuousIntegrationBuild=true /p:Deterministic=true
|
||||
|
||||
- name: Sign package ✒️
|
||||
env:
|
||||
APPX_SIGNING_CERT: ${{ secrets.APPX_SIGNING_CERT }}
|
||||
CERT_ALGORITHM: ${{ secrets.CERT_ALGORITHM }}
|
||||
if: env.APPX_SIGNING_CERT != ''
|
||||
run: |
|
||||
Write-Output $env:APPX_SIGNING_CERT > appx-cert.b64
|
||||
certutil -decode appx-cert.b64 appx-cert.pfx
|
||||
&"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64\signtool.exe" sign /fd $env:CERT_ALGORITHM /a /f appx-cert.pfx $(Get-ChildItem -Recurse -Filter "*.msixbundle")[0].FullName
|
||||
Get-PfxCertificate appx-cert.pfx | Export-Certificate -FilePath "$($(Get-ChildItem -Recurse -Filter "*.msixbundle")[0].Directory.FullName)\certificate.cer" -Type CERT
|
||||
|
||||
- name: Upload MSIX 📤
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-msix
|
||||
path: Launcher/Launcher-Appx/AppPackages/
|
||||
@@ -0,0 +1,8 @@
|
||||
[*.cs]
|
||||
indent_size = 4
|
||||
csharp_new_line_before_open_brace = none
|
||||
csharp_style_var_when_type_is_apparent = true
|
||||
csharp_style_var_for_built_in_types = true
|
||||
csharp_new_line_before_else = false
|
||||
csharp_new_line_before_catch = false
|
||||
csharp_new_line_before_finally = false
|
||||
@@ -0,0 +1,12 @@
|
||||
bin/
|
||||
*.dll
|
||||
*.exe
|
||||
*.appx
|
||||
*.cache
|
||||
.vs/
|
||||
.idea/
|
||||
*.DotSettings.user
|
||||
obj/
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
*.pfx
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '15.0'">
|
||||
<VisualStudioVersion>15.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<WapProjPath Condition="'$(WapProjPath)'==''">$(MSBuildExtensionsPath)\Microsoft\DesktopBridge\</WapProjPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>b2533ea5-9dd5-41bb-bf00-a1d0fdba58b7</ProjectGuid>
|
||||
<TargetPlatformVersion>10.0.22000.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
|
||||
<DefaultLanguage>de-DE</DefaultLanguage>
|
||||
<AppxPackageSigningEnabled>False</AppxPackageSigningEnabled>
|
||||
<NoWarn>$(NoWarn);NU1702</NoWarn>
|
||||
<PackageCertificateKeyFile>Launcher-Appx_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
|
||||
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
|
||||
<GenerateTestArtifacts>True</GenerateTestArtifacts>
|
||||
<AppxBundlePlatforms>x64</AppxBundlePlatforms>
|
||||
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
|
||||
<EntryPointProjectUniqueName>..\Launcher\Launcher.csproj</EntryPointProjectUniqueName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Images\SplashScreen.scale-200.png" />
|
||||
<Content Include="Images\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Images\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Images\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Images\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Images\StoreLogo.png" />
|
||||
<Content Include="Images\Wide310x150Logo.scale-200.png" />
|
||||
<Content Include="nixos-wsl.tar.gz" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22000.197" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Launcher\Launcher.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebuggerFlavor>AppHostLocalDebugger</DebuggerFlavor>
|
||||
<RemoteDebugEnabled>False</RemoteDebugEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebuggerFlavor>AppHostLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<UapAppxPackageBuildMode>SideloadOnly</UapAppxPackageBuildMode>
|
||||
<AppxShowAllApps>False</AppxShowAllApps>
|
||||
<AppxBuildConfigurationSelection>x64</AppxBuildConfigurationSelection>
|
||||
<PackageOptionalProjectsInIdeBuilds>False</PackageOptionalProjectsInIdeBuilds>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebuggerFlavor>AppHostLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebuggerFlavor>AppHostLocalDebugger</DebuggerFlavor>
|
||||
<RemoteDebugEnabled>False</RemoteDebugEnabled>
|
||||
<DebuggerType>Mixed</DebuggerType>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap mp uap2 uap3 rescap desktop">
|
||||
|
||||
<Identity
|
||||
Name="NixOS"
|
||||
Publisher="CN=nzbr"
|
||||
Version="1.3.3.7"
|
||||
ProcessorArchitecture="x64"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>NixOS</DisplayName>
|
||||
<PublisherDisplayName>nzbr</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0"/>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" MaxVersionTested="10.0.14393.0"/>
|
||||
</Dependencies>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust"/>
|
||||
</Capabilities>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="NixOS"
|
||||
Executable="Launcher\NixOS.exe"
|
||||
EntryPoint="Windows.FullTrustApplication">
|
||||
<uap:VisualElements
|
||||
Description="NixOS WSL Distro Launcher"
|
||||
BackgroundColor="#FFFFFF"
|
||||
Square150x150Logo="Images\Square150x150Logo.png"
|
||||
Square44x44Logo="Images\Square44x44Logo.png" DisplayName="NixOS">
|
||||
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png"/>
|
||||
<uap:SplashScreen Image="Images\SplashScreen.png"/>
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap3:Extension Category="windows.appExecutionAlias" Executable="Launcher\NixOS.exe"
|
||||
EntryPoint="Windows.FullTrustApplication">
|
||||
<uap3:AppExecutionAlias>
|
||||
<desktop:ExecutionAlias Alias="NixOS.exe"/>
|
||||
</uap3:AppExecutionAlias>
|
||||
</uap3:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
</Package>
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32407.343
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Launcher", "Launcher\Launcher.csproj", "{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}"
|
||||
EndProject
|
||||
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Launcher-Appx", "Launcher-Appx\Launcher-Appx.wapproj", "{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{86D47DB8-0919-4929-9841-EC9B8FCB282F}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.gitignore = .gitignore
|
||||
default.nix = default.nix
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{8EB5612C-BFFA-43EF-AAC6-68AB01DA30CA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Debug|ARM64.ActiveCfg = Release|ARM64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Debug|ARM64.Build.0 = Release|ARM64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Debug|ARM64.Deploy.0 = Release|ARM64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Debug|x64.ActiveCfg = Release|x64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Debug|x64.Build.0 = Release|x64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Debug|x64.Deploy.0 = Release|x64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Release|x64.ActiveCfg = Release|x64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Release|x64.Build.0 = Release|x64
|
||||
{B2533EA5-9DD5-41BB-BF00-A1D0FDBA58B7}.Release|x64.Deploy.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6515FF51-7BEF-41B2-A3ED-1D5025A3BFA4}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,8 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_CATCH_ON_NEW_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ELSE_ON_NEW_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FINALLY_ON_NEW_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<AssemblyName>NixOS</AssemblyName>
|
||||
<Version>1.3.3.7</Version>
|
||||
<ApplicationIcon>nixos.ico</ApplicationIcon>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="nixos.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta3.22114.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
After Width: | Height: | Size: 67 KiB |
@@ -0,0 +1,30 @@
|
||||
using System.CommandLine;
|
||||
using Launcher.Helpers;
|
||||
|
||||
namespace Launcher.Commands;
|
||||
|
||||
public static class Install {
|
||||
public static Command GetCommand() {
|
||||
var command = new Command("install") {
|
||||
Description = $"Install {DistributionInfo.DisplayName} if it has not been installed already"
|
||||
};
|
||||
var reinstallOpt = new Option<bool>("--reinstall") {
|
||||
Description = "Delete the existing installation and install a fresh copy"
|
||||
};
|
||||
command.Add(reinstallOpt);
|
||||
|
||||
command.SetHandler((bool reinstall) => {
|
||||
if (reinstall) {
|
||||
var result = InstallationHelper.Uninstall();
|
||||
if (result != 0) {
|
||||
Program.result = result;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Program.result = InstallationHelper.Install();
|
||||
}, reinstallOpt);
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.CommandLine;
|
||||
using WSL;
|
||||
|
||||
namespace Launcher.Commands;
|
||||
|
||||
public static class Registered {
|
||||
public static Command GetCommand() {
|
||||
var command = new Command("registered") {
|
||||
Description = "Check whether or not the distribution is registered"
|
||||
};
|
||||
var quietOpt = new Option<bool>(new[] { "--quiet", "-q" }) {
|
||||
Description = "Only return the appropriate exit code, dont write to the console"
|
||||
};
|
||||
command.Add(quietOpt);
|
||||
|
||||
command.SetHandler((bool quiet) => {
|
||||
var registered = WslApiLoader.WslIsDistributionRegistered(DistributionInfo.Name);
|
||||
|
||||
if (!quiet) {
|
||||
if (registered)
|
||||
Console.WriteLine($"{DistributionInfo.Name} is registered");
|
||||
else
|
||||
Console.WriteLine($"{DistributionInfo.Name} is not registered");
|
||||
}
|
||||
|
||||
Program.result = registered ? 0 : 1;
|
||||
}, quietOpt);
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.CommandLine;
|
||||
using Launcher.Helpers;
|
||||
using WSL;
|
||||
|
||||
namespace Launcher.Commands;
|
||||
|
||||
public static class Run {
|
||||
public static Command GetCommand() {
|
||||
var command = new Command("run") {
|
||||
Description =
|
||||
"Run a command in the current directory. If no command is specified, the default shell is launched"
|
||||
};
|
||||
var argCmd = new Argument<string>("command") {
|
||||
Arity = ArgumentArity.ZeroOrOne
|
||||
};
|
||||
command.AddArgument(argCmd);
|
||||
|
||||
command.SetHandler((string? cmd) => {
|
||||
uint exitCode;
|
||||
|
||||
if (!WslApiLoader.WslIsDistributionRegistered(DistributionInfo.Name)) {
|
||||
Console.Error.WriteLine($"{DistributionInfo.DisplayName} is not installed!");
|
||||
Program.result = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
WslApiLoader.WslLaunchInteractive(DistributionInfo.Name, cmd, true, out exitCode);
|
||||
} catch (WslApiException e) {
|
||||
Console.Error.WriteLine("An error occured when starting the command!");
|
||||
Program.result = e.HResult;
|
||||
return;
|
||||
}
|
||||
|
||||
Program.result = (int)exitCode;
|
||||
}, argCmd);
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.CommandLine;
|
||||
using Launcher.Helpers;
|
||||
|
||||
namespace Launcher.Commands;
|
||||
|
||||
public static class Uninstall {
|
||||
public static Command GetCommand() {
|
||||
var command = new Command("uninstall") {
|
||||
Description = $"Uninstall {DistributionInfo.DisplayName}"
|
||||
};
|
||||
|
||||
command.SetHandler(() => { Program.result = InstallationHelper.Uninstall(); });
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Launcher;
|
||||
|
||||
internal static class DistributionInfo {
|
||||
public const string DisplayName = "NixOS";
|
||||
public const string WindowTitle = "NixOS";
|
||||
|
||||
// Name that the distro is registered as in WSL. Can be overridden with --distro-name
|
||||
public static string Name = "NixOS";
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.Reflection;
|
||||
using WSL;
|
||||
|
||||
namespace Launcher.Helpers;
|
||||
|
||||
public static class InstallationHelper {
|
||||
/// <summary>
|
||||
/// Registers the distribution and runs first-time setup
|
||||
/// </summary>
|
||||
/// <returns>0 on success, an error code otherwise</returns>
|
||||
public static int Install() {
|
||||
Console.WriteLine($"Registering {DistributionInfo.DisplayName}...");
|
||||
|
||||
if (WslApiLoader.WslIsDistributionRegistered(DistributionInfo.Name)) {
|
||||
Console.Error.WriteLine($"{DistributionInfo.DisplayName} is already installed!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determine tarball location
|
||||
var tarPath = FindTarball();
|
||||
if (tarPath == null) {
|
||||
Console.Error.WriteLine("Could not find distro tarball");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
WslApiLoader.WslRegisterDistribution(
|
||||
DistributionInfo.Name,
|
||||
tarPath
|
||||
);
|
||||
} catch (WslApiException e) {
|
||||
Console.Error.WriteLine("There was an error registering the distribution");
|
||||
return e.HResult;
|
||||
}
|
||||
|
||||
Console.WriteLine("Installation finished successfully");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister the distribution
|
||||
/// </summary>
|
||||
/// <returns>0 on success and an error code otherwise</returns>
|
||||
public static int Uninstall() {
|
||||
Console.WriteLine($"Uninstalling {DistributionInfo.DisplayName}...");
|
||||
if (!WslApiLoader.WslIsDistributionRegistered(DistributionInfo.Name)) {
|
||||
Console.Error.WriteLine($"{DistributionInfo.DisplayName} is not installed!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
WslApiLoader.WslUnregisterDistribution(DistributionInfo.Name);
|
||||
} catch (WslApiException e) {
|
||||
Console.Error.WriteLine("An error occured when unregistering the distribution!");
|
||||
return e.HResult;
|
||||
}
|
||||
|
||||
Console.WriteLine("Uninstall completed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the path of the installer tarball
|
||||
/// </summary>
|
||||
/// <returns>the full path to the tarball or null</returns>
|
||||
public static string? FindTarball() {
|
||||
const string tarFileName = "nixos-wsl.tar.gz";
|
||||
|
||||
// Accept a tarball in the current directory when running a debug build
|
||||
#if (DEBUG)
|
||||
var pwd = Directory.GetCurrentDirectory();
|
||||
var debugTarPath = Path.Combine(pwd, tarFileName);
|
||||
if (File.Exists(debugTarPath)) return debugTarPath;
|
||||
#endif
|
||||
|
||||
// Look for the tarball in the same directory as the executable
|
||||
var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
if (assemblyPath == null) return null;
|
||||
|
||||
var tarPath = Path.Combine(assemblyPath, tarFileName);
|
||||
if (File.Exists(tarPath)) return tarPath;
|
||||
|
||||
// In the APPX package, the tarball is in the parent directory
|
||||
var parentDirectory = Directory.GetParent(assemblyPath)?.FullName;
|
||||
if (parentDirectory == null) return null;
|
||||
|
||||
tarPath = Path.Combine(parentDirectory, tarFileName);
|
||||
return File.Exists(tarPath)
|
||||
? tarPath
|
||||
: null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using WSL;
|
||||
|
||||
namespace Launcher.Helpers;
|
||||
|
||||
public static class StartupHelper {
|
||||
private static bool booted;
|
||||
|
||||
public static bool BootDistro() {
|
||||
if (booted) return true;
|
||||
try {
|
||||
WslApiLoader.WslLaunchInteractive(
|
||||
DistributionInfo.Name,
|
||||
"/bin/sh -c \"exit\"", // Don't run anything, exit after we get a shell
|
||||
false,
|
||||
out var exitCode
|
||||
);
|
||||
if (exitCode != 0) {
|
||||
Console.Error.WriteLine("An error occured during the distro's startup process");
|
||||
return false;
|
||||
}
|
||||
} catch (WslApiException) {
|
||||
Console.Error.WriteLine("An error occured while trying to run a command in the distro");
|
||||
return false;
|
||||
}
|
||||
|
||||
booted = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using WSL;
|
||||
|
||||
namespace Launcher.Helpers;
|
||||
|
||||
public static class VersionHelper {
|
||||
public static Version? LauncherVersion => typeof(VersionHelper).Assembly.GetName().Version;
|
||||
|
||||
public static NixosWslVersion? InstalledVersion {
|
||||
get {
|
||||
try {
|
||||
if (StartupHelper.BootDistro()) {
|
||||
var output = WslApiLoader.WslLaunchGetOutput(
|
||||
DistributionInfo.Name,
|
||||
"/bin/sh --login -c \"nixos-wsl-version --json\"",
|
||||
false,
|
||||
out var exitCode,
|
||||
true
|
||||
).Trim();
|
||||
|
||||
var json = JsonSerializer.Deserialize<Dictionary<string, string>>(output);
|
||||
var version = json!["release"];
|
||||
|
||||
if (exitCode == 0) return new NixosWslVersion(version);
|
||||
}
|
||||
} catch (Exception) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class NixosWslVersion {
|
||||
private readonly Version _version;
|
||||
private readonly string _versionString;
|
||||
|
||||
internal NixosWslVersion(string versionString) {
|
||||
_versionString = versionString;
|
||||
|
||||
if (Version.TryParse(versionString, out var ver)) {
|
||||
_version = ver;
|
||||
return;
|
||||
}
|
||||
|
||||
_version = versionString == "DEV_BUILD"
|
||||
? new Version(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)
|
||||
: new Version(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public Version AsVersion() {
|
||||
return _version;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return _versionString;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Builder;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.IO;
|
||||
using System.CommandLine.Parsing;
|
||||
using Launcher.Commands;
|
||||
using Launcher.Helpers;
|
||||
using WSL;
|
||||
|
||||
namespace Launcher;
|
||||
|
||||
internal static class Program {
|
||||
public static int result;
|
||||
|
||||
public static async Task<int> Main(string[] args) {
|
||||
// Set title of the console
|
||||
Console.Title = DistributionInfo.WindowTitle;
|
||||
|
||||
// Argparse docs: https://github.com/dotnet/command-line-api
|
||||
|
||||
var rootCommand = new RootCommand(
|
||||
$"Manage the {DistributionInfo.DisplayName} WSL distribution"
|
||||
) {
|
||||
Run.GetCommand(),
|
||||
Install.GetCommand(),
|
||||
Registered.GetCommand(),
|
||||
Uninstall.GetCommand(),
|
||||
};
|
||||
|
||||
var distroNameOption = new Option<string>("--distro-name") {
|
||||
Description = "WSL distribution name",
|
||||
Arity = ArgumentArity.ZeroOrOne
|
||||
};
|
||||
distroNameOption.SetDefaultValue(DistributionInfo.Name);
|
||||
|
||||
var versionOption = new Option<bool>("--version") {
|
||||
Description = "Show version information"
|
||||
};
|
||||
|
||||
// Add the global options to the root command and all subcommands
|
||||
rootCommand.AddOption(distroNameOption);
|
||||
rootCommand.AddOption(versionOption);
|
||||
foreach (var subcommand in rootCommand.Subcommands) {
|
||||
subcommand.AddOption(distroNameOption);
|
||||
subcommand.AddOption(versionOption);
|
||||
}
|
||||
|
||||
rootCommand.SetHandler(() => {
|
||||
if (!WslApiLoader.WslIsDistributionRegistered(DistributionInfo.Name)) {
|
||||
result = InstallationHelper.Install();
|
||||
if (result != 0) return;
|
||||
}
|
||||
|
||||
try {
|
||||
WslApiLoader.WslLaunchInteractive(DistributionInfo.Name, null, true, out var exitCode);
|
||||
result = (int)exitCode;
|
||||
} catch (WslApiException e) {
|
||||
Console.Error.WriteLine("An error occured when starting the shell");
|
||||
result = e.HResult;
|
||||
}
|
||||
});
|
||||
|
||||
var commandLineBuilder = new CommandLineBuilder(rootCommand)
|
||||
.UseHelp()
|
||||
.UseEnvironmentVariableDirective()
|
||||
.UseParseDirective()
|
||||
.UseSuggestDirective()
|
||||
.RegisterWithDotnetSuggest()
|
||||
.UseTypoCorrections()
|
||||
.UseParseErrorReporting()
|
||||
.UseExceptionHandler()
|
||||
.CancelOnProcessTermination();
|
||||
|
||||
// Implement --distro-name option
|
||||
commandLineBuilder.AddMiddleware(async (context, next) => {
|
||||
var distroNameResult = context.ParseResult.FindResultFor(distroNameOption);
|
||||
|
||||
if (distroNameResult is { Tokens.Count: > 0 }) DistributionInfo.Name = distroNameResult.Tokens[0].ToString();
|
||||
|
||||
await next(context);
|
||||
}, (MiddlewareOrder)(-1300)); // Run before --version
|
||||
|
||||
// Implement --version option
|
||||
commandLineBuilder.AddMiddleware(async (context, next) => {
|
||||
var versionResult = context.ParseResult.FindResultFor(versionOption);
|
||||
|
||||
if (versionResult != null) {
|
||||
var vl = VersionHelper.LauncherVersion?.ToString();
|
||||
var vi = VersionHelper.InstalledVersion?.ToString() ?? "UNKNOWN";
|
||||
|
||||
context.Console.Out.WriteLine($"Launcher: {vl}");
|
||||
context.Console.Out.WriteLine($"Module: {vi}");
|
||||
} else {
|
||||
await next(context);
|
||||
}
|
||||
}, (MiddlewareOrder)(-1200)); // Internal value for the builtin version option
|
||||
|
||||
await commandLineBuilder.Build().InvokeAsync(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace WSL.Kernel32;
|
||||
|
||||
public static partial class Kernel32Loader {
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL.Kernel32;
|
||||
|
||||
public static partial class Kernel32Loader {
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool CreatePipe(
|
||||
out IntPtr hReadPipe,
|
||||
out IntPtr hWritePipe,
|
||||
ref SECURITY_ATTRIBUTES lpPipeAttributes,
|
||||
uint nSize
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL.Kernel32;
|
||||
|
||||
public static partial class Kernel32Loader {
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL.Kernel32;
|
||||
|
||||
public static partial class Kernel32Loader {
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern int GetProcessId(IntPtr handle);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL.Kernel32;
|
||||
|
||||
public static partial class Kernel32Loader {
|
||||
public static int STD_INPUT_HANDLE = -10;
|
||||
public static int STD_OUTPUT_HANDLE = -11;
|
||||
public static int STD_ERROR_HANDLE = -12;
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetStdHandle(int nStdHandle);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL.Kernel32;
|
||||
|
||||
public static partial class Kernel32Loader {
|
||||
[DllImport(@"kernel32.dll", SetLastError = true)]
|
||||
public static extern unsafe bool ReadFile(
|
||||
IntPtr hFile,
|
||||
IntPtr pBuffer,
|
||||
int NumberOfBytesToRead,
|
||||
out int pNumberOfBytesRead,
|
||||
IntPtr lpOverlapped
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL.Kernel32;
|
||||
|
||||
public static partial class Kernel32Loader {
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SECURITY_ATTRIBUTES {
|
||||
public int nLength;
|
||||
public IntPtr lpSecurityDescriptor;
|
||||
public bool bInheritHandle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL.Kernel32;
|
||||
|
||||
public static partial class Kernel32Loader {
|
||||
public const UInt32 INFINITE = 0xFFFFFFFF;
|
||||
const UInt32 WAIT_ABANDONED = 0x00000080;
|
||||
const UInt32 WAIT_OBJECT_0 = 0x00000000;
|
||||
const UInt32 WAIT_TIMEOUT = 0x00000102;
|
||||
|
||||
public static void WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds) {
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
// ReSharper disable once LocalFunctionHidesMethod
|
||||
static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
|
||||
|
||||
var result = WaitForSingleObject(hHandle, dwMilliseconds);
|
||||
if (result == WAIT_OBJECT_0) {
|
||||
return;
|
||||
}
|
||||
if (result == WAIT_TIMEOUT) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
if (result == WAIT_ABANDONED) {
|
||||
throw new AbandonedMutexException();
|
||||
}
|
||||
throw new Exception("WaitForSingleObject failed");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace WSL;
|
||||
|
||||
public class WslApiException : Exception {
|
||||
|
||||
private long hresult;
|
||||
WslApiException(long hresult) {
|
||||
this.hresult = hresult;
|
||||
}
|
||||
|
||||
public static void checkResult(long hresult) {
|
||||
if (hresult != 0) {
|
||||
throw new WslApiException(hresult);
|
||||
}
|
||||
}
|
||||
|
||||
public override string Message => $"WSL API Error {hresult}";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
public static void WslConfigureDistribution(
|
||||
string distributionName,
|
||||
ulong defaultUID,
|
||||
WSL_DISTRIBUTION_FLAGS wslDistributionFlags
|
||||
) {
|
||||
[DllImport("wslapi.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
|
||||
// ReSharper disable once LocalFunctionHidesMethod
|
||||
static extern long WslConfigureDistribution(
|
||||
string distributionName,
|
||||
ulong defaultUID,
|
||||
WSL_DISTRIBUTION_FLAGS wslDistributionFlags
|
||||
);
|
||||
WslApiException.checkResult(WslConfigureDistribution(distributionName, defaultUID, wslDistributionFlags));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
[Flags]
|
||||
public enum WSL_DISTRIBUTION_FLAGS {
|
||||
WSL_DISTRIBUTION_FLAGS_NONE = 0x0,
|
||||
WSL_DISTRIBUTION_FLAGS_ENABLE_INTEROP = 0x1,
|
||||
WSL_DISTRIBUTION_FLAGS_APPEND_NT_PATH = 0x2,
|
||||
WSL_DISTRIBUTION_FLAGS_ENABLE_DRIVE_MOUNTING = 0x4
|
||||
}
|
||||
|
||||
public static WSL_DISTRIBUTION_FLAGS WSL_DISTRIBUTION_FLAGS_VALID =
|
||||
WSL_DISTRIBUTION_FLAGS.WSL_DISTRIBUTION_FLAGS_ENABLE_INTEROP |
|
||||
WSL_DISTRIBUTION_FLAGS.WSL_DISTRIBUTION_FLAGS_APPEND_NT_PATH |
|
||||
WSL_DISTRIBUTION_FLAGS.WSL_DISTRIBUTION_FLAGS_ENABLE_DRIVE_MOUNTING;
|
||||
|
||||
public static WSL_DISTRIBUTION_FLAGS WSL_DISTRIBUTION_FLAGS_DEFAULT =
|
||||
WSL_DISTRIBUTION_FLAGS.WSL_DISTRIBUTION_FLAGS_ENABLE_INTEROP |
|
||||
WSL_DISTRIBUTION_FLAGS.WSL_DISTRIBUTION_FLAGS_APPEND_NT_PATH |
|
||||
WSL_DISTRIBUTION_FLAGS.WSL_DISTRIBUTION_FLAGS_ENABLE_DRIVE_MOUNTING;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Data;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
// Wrap this method, so that we can get a managed string array out of it instead of char***
|
||||
public static unsafe void WslGetDistributionConfiguration(
|
||||
string distributionName,
|
||||
out ulong distributionVersion,
|
||||
out ulong defaultUID,
|
||||
out WslApiLoader.WSL_DISTRIBUTION_FLAGS wslDistributionFlags,
|
||||
out string[] defaultEnvironmentVariables
|
||||
) {
|
||||
[DllImport("wslapi.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
|
||||
// ReSharper disable once LocalFunctionHidesMethod
|
||||
static extern long WslGetDistributionConfiguration(
|
||||
string distributionName,
|
||||
out ulong distributionVersion,
|
||||
out ulong defaultUID,
|
||||
out WslApiLoader.WSL_DISTRIBUTION_FLAGS wslDistributionFlags,
|
||||
out byte** defaultEnvironmentVariables,
|
||||
out ulong defaultEnvironmentVariableCount
|
||||
);
|
||||
|
||||
byte** envv;
|
||||
ulong envc;
|
||||
|
||||
WslApiException.checkResult(
|
||||
WslGetDistributionConfiguration(
|
||||
distributionName,
|
||||
out distributionVersion,
|
||||
out defaultUID,
|
||||
out wslDistributionFlags,
|
||||
out envv,
|
||||
out envc)
|
||||
);
|
||||
|
||||
defaultEnvironmentVariables = new string[envc];
|
||||
|
||||
var anyNull = false;
|
||||
for (ulong i = 0; i < envc; i++) {
|
||||
var ptr = new IntPtr(*(envv + i)); // Get the pointer to the string at offset i and dereference it
|
||||
var str = Marshal
|
||||
.PtrToStringAnsi(ptr); // Create a managed string from the char* pointer (_needs_ to be ANSI)
|
||||
|
||||
if (str == null) {
|
||||
anyNull = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(ptr); // Free the string
|
||||
|
||||
defaultEnvironmentVariables[i] = str;
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(new IntPtr(envv)); // Free the array
|
||||
|
||||
if (anyNull)
|
||||
throw new NoNullAllowedException(
|
||||
"One of the strings in the environment array returned by wslapi.dll is null");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
[DllImport("wslapi.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
|
||||
public static extern bool WslIsDistributionRegistered(string distributionName);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using WSL.Kernel32;
|
||||
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
public static void WslLaunch(
|
||||
string distributionName,
|
||||
string? command,
|
||||
bool useCurrentWorkingDirectory,
|
||||
IntPtr stdIn,
|
||||
IntPtr stdOut,
|
||||
IntPtr stdErr,
|
||||
out IntPtr process
|
||||
) {
|
||||
[DllImport("wslapi.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
|
||||
// ReSharper disable once LocalFunctionHidesMethod
|
||||
static extern long WslLaunch(
|
||||
string distributionName,
|
||||
string? command,
|
||||
bool useCurrentWorkingDirectory,
|
||||
IntPtr stdIn,
|
||||
IntPtr stdOut,
|
||||
IntPtr stdErr,
|
||||
out IntPtr process
|
||||
);
|
||||
|
||||
WslApiException.checkResult(
|
||||
WslLaunch(
|
||||
distributionName,
|
||||
command,
|
||||
useCurrentWorkingDirectory,
|
||||
stdIn,
|
||||
stdOut,
|
||||
stdErr,
|
||||
out process
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using WSL.Kernel32;
|
||||
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
|
||||
// Contains code from https://github.com/wslhub/wsl-sdk-dotnet/blob/892e8b5564170eae9944649cf8ab424ce0fbce52/src/Wslhub.Sdk/Wsl.cs#L364
|
||||
public static unsafe string WslLaunchGetOutput(
|
||||
string distributionName,
|
||||
string command,
|
||||
bool useCurrentWorkingDirectory,
|
||||
out uint exitCode,
|
||||
bool noStderr = false
|
||||
) {
|
||||
var stdin = Kernel32Loader.GetStdHandle(Kernel32Loader.STD_INPUT_HANDLE);
|
||||
var stderr = Kernel32Loader.GetStdHandle(Kernel32Loader.STD_ERROR_HANDLE);
|
||||
|
||||
var attributes = new Kernel32Loader.SECURITY_ATTRIBUTES {
|
||||
lpSecurityDescriptor = IntPtr.Zero,
|
||||
bInheritHandle = true,
|
||||
};
|
||||
attributes.nLength = Marshal.SizeOf(attributes);
|
||||
|
||||
if (!Kernel32Loader.CreatePipe(out IntPtr readPipe, out IntPtr writePipe, ref attributes, 0))
|
||||
throw new Exception("Cannot create stdout pipe");
|
||||
|
||||
var stderrRead = IntPtr.Zero;
|
||||
var stderrWrite = IntPtr.Zero;
|
||||
if (noStderr) {
|
||||
if (!Kernel32Loader.CreatePipe(out stderrRead, out stderrWrite, ref attributes, 0))
|
||||
throw new Exception("Cannot create stderr pipe");
|
||||
stderr = stderrWrite;
|
||||
}
|
||||
|
||||
try {
|
||||
WslLaunch(distributionName, command, useCurrentWorkingDirectory, stdin, writePipe, stderr,
|
||||
out var hProcess);
|
||||
Kernel32Loader.WaitForSingleObject(hProcess, Kernel32Loader.INFINITE);
|
||||
|
||||
if (!Kernel32Loader.GetExitCodeProcess(hProcess, out exitCode)) {
|
||||
Kernel32Loader.CloseHandle(hProcess);
|
||||
throw new Exception("Could not get exit code of WSL process");
|
||||
}
|
||||
|
||||
Kernel32Loader.CloseHandle(hProcess);
|
||||
Kernel32Loader.CloseHandle(writePipe); // Make sure the pipe is closed if the spawned process has not done that
|
||||
|
||||
const int bufferLength = 65536;
|
||||
var bufferPointer = Marshal.AllocHGlobal(bufferLength);
|
||||
var outputContents = new StringBuilder();
|
||||
var encoding = new UTF8Encoding(false);
|
||||
var read = 0;
|
||||
|
||||
do {
|
||||
if (!Kernel32Loader.ReadFile(readPipe, bufferPointer, bufferLength, out read, IntPtr.Zero)) {
|
||||
var lastError = Marshal.GetLastWin32Error();
|
||||
|
||||
if (lastError != 0) {
|
||||
Marshal.FreeHGlobal(bufferPointer);
|
||||
throw new Exception("Could not read from pipe");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
outputContents.Append(encoding.GetString((byte*)bufferPointer.ToPointer(), read));
|
||||
} while (read == bufferLength);
|
||||
|
||||
Marshal.FreeHGlobal(bufferPointer);
|
||||
|
||||
return outputContents.ToString();
|
||||
} finally {
|
||||
Kernel32Loader.CloseHandle(readPipe);
|
||||
Kernel32Loader.CloseHandle(writePipe);
|
||||
if (noStderr) {
|
||||
Kernel32Loader.CloseHandle(stderrRead);
|
||||
Kernel32Loader.CloseHandle(stderrWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
public static unsafe void WslLaunchInteractive(
|
||||
string distributionName,
|
||||
string? command,
|
||||
bool useCurrentWorkingDirectory,
|
||||
out uint exitCode
|
||||
) {
|
||||
[DllImport("wslapi.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = true)]
|
||||
// ReSharper disable once LocalFunctionHidesMethod
|
||||
static extern long WslLaunchInteractive(
|
||||
[In] string distributionName,
|
||||
[In] string? command,
|
||||
[In] bool useCurrentWorkingDirectory,
|
||||
[Out, MarshalAs(UnmanagedType.U4)] out uint exitCode
|
||||
);
|
||||
|
||||
WslApiException.checkResult(
|
||||
WslLaunchInteractive(
|
||||
distributionName,
|
||||
command,
|
||||
useCurrentWorkingDirectory,
|
||||
out exitCode
|
||||
)
|
||||
);
|
||||
Console.WriteLine(exitCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
public static void WslRegisterDistribution(
|
||||
string distributionName,
|
||||
string tarGzFilename
|
||||
) {
|
||||
[DllImport("wslapi.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
|
||||
// ReSharper disable once LocalFunctionHidesMethod
|
||||
static extern long WslRegisterDistribution(
|
||||
string distributionName,
|
||||
string tarGzFilename
|
||||
);
|
||||
|
||||
WslApiException.checkResult(
|
||||
WslRegisterDistribution(
|
||||
distributionName,
|
||||
tarGzFilename
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WSL;
|
||||
|
||||
public static partial class WslApiLoader {
|
||||
public static void WslUnregisterDistribution(
|
||||
string distributionName
|
||||
) {
|
||||
[DllImport("wslapi.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
|
||||
// ReSharper disable once LocalFunctionHidesMethod
|
||||
static extern long WslUnregisterDistribution(string distributionName);
|
||||
|
||||
WslApiException.checkResult(
|
||||
WslUnregisterDistribution(
|
||||
distributionName
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
{ pkgs ? import <nixpkgs> { }
|
||||
, stdenv ? pkgs.stdenv
|
||||
, lib ? pkgs.lib
|
||||
, dotnet-sdk ? pkgs.dotnet-sdk
|
||||
, dotnetPackages ? pkgs.dotnetPackages
|
||||
, fetchurl ? pkgs.fetchurl
|
||||
, linkFarmFromDrvs ? pkgs.linkFarmFromDrvs
|
||||
}:
|
||||
|
||||
let
|
||||
fetchNuGet = { pname, version, hash }: fetchurl {
|
||||
name = "${pname}-${version}.nupkg";
|
||||
url = "https://www.nuget.org/api/v2/package/${pname}/${version}";
|
||||
inherit hash;
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation rec {
|
||||
name = "NixOS-WSL-Launcher";
|
||||
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = [
|
||||
dotnet-sdk
|
||||
dotnetPackages.Nuget
|
||||
];
|
||||
|
||||
nugetDeps = linkFarmFromDrvs "${name}-nuget-deps" (map fetchNuGet [
|
||||
{ pname = "Microsoft.AspNetCore.App.Runtime.win-x64"; version = "6.0.25"; hash = "sha256-njOrSWL+YJJvnywJ0clMzYdaANYl3rcVaZeZAki1erk="; }
|
||||
{ pname = "Microsoft.AspNetCore.App.Runtime.win-arm64"; version = "6.0.25"; hash = "sha256-Jnt9WIxGvvejtBW5mjWCXlYmsUj6fxVI4Xs3WWsr8YE="; }
|
||||
{ pname = "Microsoft.NETCore.App.Host.win-x64"; version = "6.0.25"; hash = "sha256-ATiO6e8xcDPrRngmT9k//7Z2Mx07hJhxgmdwUvMYJA0="; }
|
||||
{ pname = "Microsoft.NETCore.App.Host.win-arm64"; version = "6.0.25"; hash = "sha256-xvj8CZHY4PyXKroniWj0JIoksKf4pcbberfMf5XZYaE="; }
|
||||
{ pname = "Microsoft.NETCore.App.Runtime.win-x64"; version = "6.0.25"; hash = "sha256-Lk6cn0cSQVp4QVf6IznE7UnZ9tYxZmRMYrsTIHq9Vg8="; }
|
||||
{ pname = "Microsoft.NETCore.App.Runtime.win-arm64"; version = "6.0.25"; hash = "sha256-eCoXKCHkrteypFVOUctV3ReQk/lFjJ7cI/CVk2tNrZY="; }
|
||||
{ pname = "System.CommandLine"; version = "2.0.0-beta3.22114.1"; hash = "sha256-lcqMVesbt2E189e6KfG8HVmJc3hjo2ht48WnH1UaMkY="; }
|
||||
]);
|
||||
|
||||
unpackPhase = ''
|
||||
cp -r --no-preserve=mode $src/. .
|
||||
'';
|
||||
|
||||
configurePhase = ''
|
||||
runHook preConfigure
|
||||
|
||||
mkdir .home
|
||||
export HOME=$PWD/.home
|
||||
|
||||
export DOTNET_NOLOGO=1
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
|
||||
nuget sources Add -Name nix -Source "$PWD/nix"
|
||||
nuget init "$nugetDeps" "$PWD/nix"
|
||||
dotnet nuget disable source nuget.org
|
||||
|
||||
mkdir -p $HOME/.nuget/NuGet
|
||||
cp $HOME/.config/NuGet/NuGet.Config $HOME/.nuget/NuGet
|
||||
|
||||
dotnet restore --ignore-failed-sources
|
||||
|
||||
runHook postConfigure
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
mkdir -p $out
|
||||
|
||||
${lib.concatStringsSep "\n" (
|
||||
map (runtime: ''
|
||||
dotnet publish \
|
||||
--self-contained \
|
||||
-p:ContinuousIntegrationBuild=true \
|
||||
-p:Deterministic=true \
|
||||
--packages "$HOME/nuget_pkgs" \
|
||||
-r ${runtime} \
|
||||
-c Release \
|
||||
--output $out/${runtime} \
|
||||
Launcher
|
||||
'') [ "win-x64" "win-arm64" ]
|
||||
)}
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
dontInstall = true;
|
||||
|
||||
dontStrip = true; # strip breaks the assemblies
|
||||
|
||||
meta = with lib; {
|
||||
description = "WSL Distribution Launcher for NixOS-WSL";
|
||||
homepage = "https://github.com/nix-community/NixOS-WSL";
|
||||
license = licenses.asl20;
|
||||
maintainers = with maintainers; [ nzbr ];
|
||||
platforms = [ "x86_64-linux" "x86_64-windows" ];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{ runCommand
|
||||
, dotnet-sdk
|
||||
, ...
|
||||
}:
|
||||
runCommand "check-dotnet-format" { nativeBuildInputs = [ dotnet-sdk ]; } ''
|
||||
cd "${./../Launcher}"
|
||||
dotnet format --verbosity detailed --no-restore --verify-no-changes && touch $out
|
||||
''
|
||||
@@ -89,6 +89,7 @@
|
||||
args = { inherit inputs; };
|
||||
in
|
||||
{
|
||||
dotnet-format = pkgs.callPackage ./checks/dotnet-format.nix args;
|
||||
nixpkgs-fmt = pkgs.callPackage ./checks/nixpkgs-fmt.nix args;
|
||||
shfmt = pkgs.callPackage ./checks/shfmt.nix args;
|
||||
rustfmt = pkgs.callPackage ./checks/rustfmt.nix args;
|
||||
|
||||
@@ -63,7 +63,7 @@ with lib;
|
||||
'') config.wsl.version
|
||||
)}
|
||||
--json)
|
||||
echo '${generators.toJSON {} config.wsl.version}' | ${pkgs.jq}/bin/jq # Use jq to pretty-print the JSON
|
||||
echo '${generators.toJSON {} config.wsl.version}' | ${pkgs.jq}/bin/jq -M # Use jq to pretty-print the JSON
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
|
||||