Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Astro | e9447929b6 |
|
@ -6,7 +6,7 @@ WFVM
|
||||||
A Nix library to create and manage virtual machines running Windows, a medieval operating system found on most computers in 2020. The F stands for "Functional" or a four-letter word of your choice.
|
A Nix library to create and manage virtual machines running Windows, a medieval operating system found on most computers in 2020. The F stands for "Functional" or a four-letter word of your choice.
|
||||||
|
|
||||||
* Reproducible - everything runs in the Nix sandbox with no tricks.
|
* Reproducible - everything runs in the Nix sandbox with no tricks.
|
||||||
* Fully automatic, parameterizable Windows 11 installation.
|
* Fully automatic, parameterizable Windows 10 installation.
|
||||||
* Uses QEMU with KVM.
|
* Uses QEMU with KVM.
|
||||||
* Supports incremental installation (using "layers") of additional software via QEMU copy-on-write backing chains. For example, ``wfvm.makeWindowsImage { installCommands = [ wfvm.layers.anaconda3 ]; };`` gives you a VM image with Anaconda3 installed, and ``wfvm.makeWindowsImage { installCommands = [ wfvm.layers.anaconda3 wfvm.layers.msys2 ]; };`` gives you one with both Anaconda3 and MSYS2 installed. The base Windows installation and the Anaconda3 data are shared between both images, and only the MSYS2 installation is performed when building the second image after the first one has been built.
|
* Supports incremental installation (using "layers") of additional software via QEMU copy-on-write backing chains. For example, ``wfvm.makeWindowsImage { installCommands = [ wfvm.layers.anaconda3 ]; };`` gives you a VM image with Anaconda3 installed, and ``wfvm.makeWindowsImage { installCommands = [ wfvm.layers.anaconda3 wfvm.layers.msys2 ]; };`` gives you one with both Anaconda3 and MSYS2 installed. The base Windows installation and the Anaconda3 data are shared between both images, and only the MSYS2 installation is performed when building the second image after the first one has been built.
|
||||||
* Included layers: Anaconda3, a software installer chock full of bugs that pretends to be a package manager, Visual Studio, a spamming system for Microsoft accounts that includes a compiler, and MSYS2, which is the only sane component in the whole lot.
|
* Included layers: Anaconda3, a software installer chock full of bugs that pretends to be a package manager, Visual Studio, a spamming system for Microsoft accounts that includes a compiler, and MSYS2, which is the only sane component in the whole lot.
|
||||||
|
|
12
flake.lock
12
flake.lock
|
@ -2,16 +2,16 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1685004253,
|
"lastModified": 1665449268,
|
||||||
"narHash": "sha256-AbVL1nN/TDicUQ5wXZ8xdLERxz/eJr7+o8lqkIOVuaE=",
|
"narHash": "sha256-cw4xrQIAZUyJGj58Dp5VLICI0rscd+uap83afiFzlcA=",
|
||||||
"owner": "NixOS",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "3e01645c40b92d29f3ae76344a6d654986a91a91",
|
"rev": "285e77efe87df64105ec14b204de6636fb0a7a27",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "nixos",
|
||||||
"ref": "nixos-23.05",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
21
flake.nix
21
flake.nix
|
@ -2,7 +2,7 @@
|
||||||
description = "WFVM: Windows Functional Virtual Machine";
|
description = "WFVM: Windows Functional Virtual Machine";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs }:
|
outputs = { self, nixpkgs }:
|
||||||
|
@ -12,26 +12,13 @@
|
||||||
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
in rec {
|
in {
|
||||||
lib = import ./wfvm {
|
lib = import ./wfvm {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
packages.${system} = rec {
|
packages.${system}.demoImage = import ./wfvm/demo-image.nix {
|
||||||
demoImage = import ./wfvm/demo-image.nix {
|
inherit self;
|
||||||
inherit self;
|
|
||||||
};
|
|
||||||
|
|
||||||
default = lib.utils.wfvm-run {
|
|
||||||
name = "demo";
|
|
||||||
image = demoImage;
|
|
||||||
script =
|
|
||||||
''
|
|
||||||
echo "Windows booted. Press Enter to terminate VM."
|
|
||||||
read
|
|
||||||
'';
|
|
||||||
display = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
, impureShellCommands ? []
|
, impureShellCommands ? []
|
||||||
, driveLetter ? "D:"
|
, driveLetter ? "D:"
|
||||||
, efi ? true
|
, efi ? true
|
||||||
, imageSelection ? "Windows 11 Pro N"
|
, imageSelection ? "Windows 10 Pro"
|
||||||
, enableTpm
|
|
||||||
, ...
|
, ...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
|
@ -59,16 +58,18 @@ let
|
||||||
assertCommand = c: builtins.typeOf c == "string" || builtins.typeOf c == "set" && builtins.hasAttr "Path" c && builtins.hasAttr "Description" c;
|
assertCommand = c: builtins.typeOf c == "string" || builtins.typeOf c == "set" && builtins.hasAttr "Path" c && builtins.hasAttr "Description" c;
|
||||||
|
|
||||||
commands = builtins.map (x: assert assertCommand x; if builtins.typeOf x == "string" then { Path = x; Description = x; } else x) (
|
commands = builtins.map (x: assert assertCommand x; if builtins.typeOf x == "string" then { Path = x; Description = x; } else x) (
|
||||||
[ {
|
[
|
||||||
Path = "powershell.exe Set-ExecutionPolicy -Force Unrestricted";
|
{
|
||||||
Description = "Allow unsigned powershell scripts.";
|
Path = "powershell.exe Set-ExecutionPolicy -Force Unrestricted";
|
||||||
} {
|
Description = "Allow unsigned powershell scripts.";
|
||||||
Path = ''powershell.exe ${driveLetter}\win-bundle-installer.exe'';
|
}
|
||||||
Description = "Install any declared packages.";
|
]
|
||||||
} {
|
++ [
|
||||||
Path = "net accounts /maxpwage:unlimited";
|
{
|
||||||
Description = "Disable forced password expiry.";
|
Path = ''powershell.exe ${driveLetter}\win-bundle-installer.exe'';
|
||||||
} ]
|
Description = "Install any declared packages.";
|
||||||
|
}
|
||||||
|
]
|
||||||
++ setupCommands
|
++ setupCommands
|
||||||
++ [
|
++ [
|
||||||
{
|
{
|
||||||
|
@ -147,14 +148,6 @@ let
|
||||||
</DriverPaths>
|
</DriverPaths>
|
||||||
</component>
|
</component>
|
||||||
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
${lib.optionalString (!enableTpm) ''
|
|
||||||
<RunSynchronous>
|
|
||||||
<RunSynchronousCommand wcm:action="add">
|
|
||||||
<Order>1</Order>
|
|
||||||
<Path>reg add HKLM\System\Setup\LabConfig /v BypassTPMCheck /t reg_dword /d 0x00000001 /f</Path>
|
|
||||||
</RunSynchronousCommand>
|
|
||||||
</RunSynchronous>
|
|
||||||
''}
|
|
||||||
|
|
||||||
<DiskConfiguration>
|
<DiskConfiguration>
|
||||||
<Disk wcm:action="add">
|
<Disk wcm:action="add">
|
||||||
|
@ -206,7 +199,6 @@ let
|
||||||
<PartitionID>3</PartitionID>
|
<PartitionID>3</PartitionID>
|
||||||
</InstallTo>
|
</InstallTo>
|
||||||
<InstallFrom>
|
<InstallFrom>
|
||||||
<Path>\install.swm</Path>
|
|
||||||
<MetaData wcm:action="add">
|
<MetaData wcm:action="add">
|
||||||
<Key>/IMAGE/NAME</Key>
|
<Key>/IMAGE/NAME</Key>
|
||||||
<Value>${imageSelection}</Value>
|
<Value>${imageSelection}</Value>
|
||||||
|
@ -217,7 +209,7 @@ let
|
||||||
|
|
||||||
<UserData>
|
<UserData>
|
||||||
<ProductKey>
|
<ProductKey>
|
||||||
${if productKey != null then "<Key>${productKey}</Key>" else "<Key/>"}
|
${if productKey != null then "<Key>${productKey}</Key>" else ""}
|
||||||
<WillShowUI>OnError</WillShowUI>
|
<WillShowUI>OnError</WillShowUI>
|
||||||
</ProductKey>
|
</ProductKey>
|
||||||
<AcceptEula>true</AcceptEula>
|
<AcceptEula>true</AcceptEula>
|
||||||
|
@ -307,7 +299,7 @@ let
|
||||||
</component>
|
</component>
|
||||||
</settings>
|
</settings>
|
||||||
|
|
||||||
<cpi:offlineImage cpi:source="wim:c:/wim/windows-11/install.wim#${imageSelection}" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
|
<cpi:offlineImage cpi:source="wim:c:/wim/windows-10/install.wim#${imageSelection}" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
|
||||||
</unattend>
|
</unattend>
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
makeWindowsImage = attrs: import ./win.nix ({ inherit pkgs; } // attrs);
|
makeWindowsImage = attrs: import ./win.nix ({ inherit pkgs; } // attrs);
|
||||||
layers = import ./layers { inherit pkgs; };
|
layers = (import ./layers { inherit pkgs; });
|
||||||
utils = import ./utils.nix { inherit pkgs; };
|
utils = (import ./utils.nix { inherit pkgs; });
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@ wfvm.makeWindowsImage {
|
||||||
|
|
||||||
# Custom base iso
|
# Custom base iso
|
||||||
# windowsImage = pkgs.requireFile rec {
|
# windowsImage = pkgs.requireFile rec {
|
||||||
# name = "Win11_22H2_English_x64v1.iso";
|
# name = "Win10_21H1_English_x64.iso";
|
||||||
# sha256 = "08mbppsm1naf73z8fjyqkf975nbls7xj9n4fq0yp802dv1rz3whd";
|
# sha256 = "1sl51lnx4r6ckh5fii7m2hi15zh8fh7cf7rjgjq9kacg8hwyh4b9";
|
||||||
# message = "Get disk image ${name} from https://www.microsoft.com/en-us/software-download/windows11/";
|
# message = "Get ${name} from https://www.microsoft.com/en-us/software-download/windows10ISO";
|
||||||
# };
|
# };
|
||||||
|
|
||||||
# impureShellCommands = [
|
# impureShellCommands = [
|
||||||
|
@ -57,7 +57,7 @@ wfvm.makeWindowsImage {
|
||||||
disable-autolock
|
disable-autolock
|
||||||
disable-firewall
|
disable-firewall
|
||||||
])
|
])
|
||||||
anaconda3 msys2
|
anaconda3 msys2 msvc msvc-ide-unbreak
|
||||||
];
|
];
|
||||||
|
|
||||||
# services = {
|
# services = {
|
||||||
|
@ -70,7 +70,7 @@ wfvm.makeWindowsImage {
|
||||||
|
|
||||||
# License key (required)
|
# License key (required)
|
||||||
# productKey = throw "Search the f* web"
|
# productKey = throw "Search the f* web"
|
||||||
imageSelection = "Windows 11 Pro N";
|
imageSelection = "Windows 10 Pro";
|
||||||
|
|
||||||
|
|
||||||
# Locales
|
# Locales
|
||||||
|
|
|
@ -72,7 +72,7 @@ in
|
||||||
bootstrapper = pkgs.fetchurl {
|
bootstrapper = pkgs.fetchurl {
|
||||||
name = "RESTRICTDIST-vs_Community.exe";
|
name = "RESTRICTDIST-vs_Community.exe";
|
||||||
url = "https://aka.ms/vs/16/release/vs_community.exe";
|
url = "https://aka.ms/vs/16/release/vs_community.exe";
|
||||||
sha256 = "sha256-l4ZKFZTgHf3BmD0eFWyGwsvb4lqB/LiQYizAABOs3gg=";
|
sha256 = "sha256-4X8NhdcNyfHkN6eKkNz8Unvv49wRZE4CQ1vf6P1R2ic=";
|
||||||
};
|
};
|
||||||
# This touchy-feely "community" piece of trash seems deliberately crafted to break Wine, so we use the VM to run it.
|
# This touchy-feely "community" piece of trash seems deliberately crafted to break Wine, so we use the VM to run it.
|
||||||
download-vs = wfvm.utils.wfvm-run {
|
download-vs = wfvm.utils.wfvm-run {
|
||||||
|
@ -93,7 +93,7 @@ in
|
||||||
|
|
||||||
outputHashAlgo = "sha256";
|
outputHashAlgo = "sha256";
|
||||||
outputHashMode = "recursive";
|
outputHashMode = "recursive";
|
||||||
outputHash = "sha256-GoOKzln8DXVMx52jWGEjwkOFkpSW+wEffAVmBVugIyk=";
|
outputHash = "0ic3jvslp2y9v8yv9mfr2mafkvj2q5frmcyhmlbxj71si1x3kpag";
|
||||||
|
|
||||||
phases = [ "buildPhase" ];
|
phases = [ "buildPhase" ];
|
||||||
buildInputs = [ download-vs ];
|
buildInputs = [ download-vs ];
|
||||||
|
|
|
@ -1,45 +1,23 @@
|
||||||
{ pkgs
|
{ pkgs, baseRtc ? "2022-10-10T10:10:10", cores ? "4", qemuMem ? "4G", efi ? true }:
|
||||||
, baseRtc ? "2022-10-10T10:10:10"
|
|
||||||
, cores ? "4"
|
|
||||||
, qemuMem ? "4G"
|
|
||||||
, efi ? true
|
|
||||||
, enableTpm ? false
|
|
||||||
, ...
|
|
||||||
}:
|
|
||||||
|
|
||||||
rec {
|
rec {
|
||||||
# qemu_test is a smaller closure only building for a single system arch
|
# qemu_test is a smaller closure only building for a single system arch
|
||||||
qemu = pkgs.qemu;
|
qemu = pkgs.qemu;
|
||||||
|
|
||||||
OVMF = pkgs.OVMF.override {
|
|
||||||
secureBoot = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
mkQemuFlags = extraFlags: [
|
mkQemuFlags = extraFlags: [
|
||||||
"-enable-kvm"
|
"-enable-kvm"
|
||||||
"-cpu host"
|
"-cpu host"
|
||||||
"-smp ${cores}"
|
"-smp ${cores}"
|
||||||
"-m ${qemuMem}"
|
"-m ${qemuMem}"
|
||||||
"-M q35,smm=on"
|
"-M q35"
|
||||||
"-vga qxl"
|
"-vga qxl"
|
||||||
"-rtc base=${baseRtc}"
|
"-rtc base=${baseRtc}"
|
||||||
"-device qemu-xhci"
|
"-device qemu-xhci"
|
||||||
"-device virtio-net-pci,netdev=n1"
|
"-device virtio-net-pci,netdev=n1"
|
||||||
] ++ pkgs.lib.optionals efi [
|
] ++ pkgs.lib.optionals efi [
|
||||||
"-bios ${OVMF.fd}/FV/OVMF.fd"
|
"-bios ${pkgs.OVMF.fd}/FV/OVMF.fd"
|
||||||
] ++ pkgs.lib.optionals enableTpm [
|
|
||||||
"-chardev" "socket,id=chrtpm,path=tpm.sock"
|
|
||||||
"-tpmdev" "emulator,id=tpm0,chardev=chrtpm"
|
|
||||||
"-device" "tpm-tis,tpmdev=tpm0"
|
|
||||||
] ++ extraFlags;
|
] ++ extraFlags;
|
||||||
|
|
||||||
tpmStartCommands = pkgs.lib.optionalString enableTpm ''
|
|
||||||
mkdir -p tpmstate
|
|
||||||
${pkgs.swtpm}/bin/swtpm socket \
|
|
||||||
--tpmstate dir=tpmstate \
|
|
||||||
--ctrl type=unixio,path=tpm.sock &
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Pass empty config file to prevent ssh from failing to create ~/.ssh
|
# Pass empty config file to prevent ssh from failing to create ~/.ssh
|
||||||
sshOpts = "-F /dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=1";
|
sshOpts = "-F /dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=1";
|
||||||
win-exec = pkgs.writeShellScriptBin "win-exec" ''
|
win-exec = pkgs.writeShellScriptBin "win-exec" ''
|
||||||
|
@ -58,6 +36,7 @@ rec {
|
||||||
# Wait for VM to be accessible
|
# Wait for VM to be accessible
|
||||||
sleep 20
|
sleep 20
|
||||||
echo "Waiting for SSH..."
|
echo "Waiting for SSH..."
|
||||||
|
TEXT=""
|
||||||
while true; do
|
while true; do
|
||||||
if test "$timeout" -eq 0; then
|
if test "$timeout" -eq 0; then
|
||||||
echo "SSH connection timed out"
|
echo "SSH connection timed out"
|
||||||
|
@ -69,6 +48,13 @@ rec {
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
${pkgs.vncdo}/bin/vncdo rcapture cap.png 0 0 1024 768
|
||||||
|
${pkgs.imagemagick}/bin/mogrify -density 70x70 -units PixelsPerInch cap.png
|
||||||
|
NEW_TEXT="$(${pkgs.tesseract}/bin/tesseract cap.png stdout)"
|
||||||
|
if [ "$TEXT" != "$NEW_TEXT" ]; then
|
||||||
|
echo "$NEW_TEXT"
|
||||||
|
TEXT="$NEW_TEXT"
|
||||||
|
fi
|
||||||
echo "Retrying in 1 second, timing out in $timeout seconds"
|
echo "Retrying in 1 second, timing out in $timeout seconds"
|
||||||
|
|
||||||
((timeout=$timeout-1))
|
((timeout=$timeout-1))
|
||||||
|
@ -107,7 +93,7 @@ rec {
|
||||||
(map ({ listenAddr, targetAddr, port }:
|
(map ({ listenAddr, targetAddr, port }:
|
||||||
",guestfwd=tcp:${listenAddr}:${toString port}-cmd:${pkgs.socat}/bin/socat\\ -\\ tcp:${targetAddr}:${toString port}"
|
",guestfwd=tcp:${listenAddr}:${toString port}-cmd:${pkgs.socat}/bin/socat\\ -\\ tcp:${targetAddr}:${toString port}"
|
||||||
) forwardedPorts);
|
) forwardedPorts);
|
||||||
qemuParams = mkQemuFlags (pkgs.lib.optional (!display) "-display none" ++ pkgs.lib.optional (!fakeRtc) "-rtc base=localtime" ++ [
|
qemuParams = mkQemuFlags (pkgs.lib.optional (!display) "-vnc 127.0.0.1:0" ++ pkgs.lib.optional (!fakeRtc) "-rtc base=localtime" ++ [
|
||||||
"-drive"
|
"-drive"
|
||||||
"file=${image},index=0,media=disk,cache=unsafe"
|
"file=${image},index=0,media=disk,cache=unsafe"
|
||||||
"-snapshot"
|
"-snapshot"
|
||||||
|
@ -115,7 +101,6 @@ rec {
|
||||||
]);
|
]);
|
||||||
in pkgs.writeShellScriptBin "wfvm-run-${name}" ''
|
in pkgs.writeShellScriptBin "wfvm-run-${name}" ''
|
||||||
set -e -m
|
set -e -m
|
||||||
${tpmStartCommands}
|
|
||||||
${qemu}/bin/qemu-system-x86_64 ${pkgs.lib.concatStringsSep " " qemuParams} &
|
${qemu}/bin/qemu-system-x86_64 ${pkgs.lib.concatStringsSep " " qemuParams} &
|
||||||
|
|
||||||
${win-wait}/bin/win-wait
|
${win-wait}/bin/win-wait
|
||||||
|
|
40
wfvm/win.nix
40
wfvm/win.nix
|
@ -5,16 +5,15 @@
|
||||||
, impureMode ? false
|
, impureMode ? false
|
||||||
, installCommands ? []
|
, installCommands ? []
|
||||||
, users ? {}
|
, users ? {}
|
||||||
, enableTpm ? true
|
|
||||||
# autounattend always installs index 1, so this default is backward-compatible
|
# autounattend always installs index 1, so this default is backward-compatible
|
||||||
, imageSelection ? "Windows 11 Pro N"
|
, imageSelection ? "Windows 10 Pro"
|
||||||
, efi ? true
|
, efi ? true
|
||||||
, ...
|
, ...
|
||||||
}@attrs:
|
}@attrs:
|
||||||
|
|
||||||
let
|
let
|
||||||
lib = pkgs.lib;
|
lib = pkgs.lib;
|
||||||
utils = import ./utils.nix ({ inherit pkgs efi enableTpm; } // attrs);
|
utils = import ./utils.nix { inherit pkgs efi; };
|
||||||
inherit (pkgs) guestfs-tools;
|
inherit (pkgs) guestfs-tools;
|
||||||
|
|
||||||
# p7zip on >20.03 has known vulns but we have no better option
|
# p7zip on >20.03 has known vulns but we have no better option
|
||||||
|
@ -37,14 +36,15 @@ let
|
||||||
);
|
);
|
||||||
|
|
||||||
windowsIso = if windowsImage != null then windowsImage else pkgs.requireFile rec {
|
windowsIso = if windowsImage != null then windowsImage else pkgs.requireFile rec {
|
||||||
name = "Win11_22H2_English_x64v2.iso";
|
name = "Win10_21H2_English_x64.iso";
|
||||||
sha256 = "0xhhxy47yaf1jsfmskym5f65hljw8q0aqs70my86m402i6dsjnc0";
|
sha256 = "0kr3m0bjy086whcbssagsshdxj6lffcz7wmvbh50zhrkxgq3hrbz";
|
||||||
message = "Get disk image ${name} from https://www.microsoft.com/en-us/software-download/windows11/";
|
message = "Get ${name} from https://www.microsoft.com/en-us/software-download/windows10ISO";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# stable as of 2021-04-08
|
||||||
virtioWinIso = pkgs.fetchurl {
|
virtioWinIso = pkgs.fetchurl {
|
||||||
url = "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.229-1/virtio-win.iso";
|
url = "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.185-2/virtio-win-0.1.185.iso";
|
||||||
sha256 = "1q5vrcd70kya4nhlbpxmj7mwmwra1hm3x7w8rzkawpk06kg0v2n8";
|
sha256 = "11n3kjyawiwacmi3jmfmn311g9xvfn6m0ccdwnjxw1brzb4kqaxg";
|
||||||
};
|
};
|
||||||
|
|
||||||
openSshServerPackage = pkgs.fetchurl {
|
openSshServerPackage = pkgs.fetchurl {
|
||||||
|
@ -54,7 +54,7 @@ let
|
||||||
|
|
||||||
autounattend = import ./autounattend.nix (
|
autounattend = import ./autounattend.nix (
|
||||||
attrs // {
|
attrs // {
|
||||||
inherit pkgs enableTpm;
|
inherit pkgs;
|
||||||
users = users // {
|
users = users // {
|
||||||
wfvm = {
|
wfvm = {
|
||||||
password = "1234";
|
password = "1234";
|
||||||
|
@ -88,7 +88,7 @@ let
|
||||||
|
|
||||||
installScript = pkgs.writeScript "windows-install-script" (
|
installScript = pkgs.writeScript "windows-install-script" (
|
||||||
let
|
let
|
||||||
qemuParams = utils.mkQemuFlags (lib.optional (!impureMode) "-display none" ++ [
|
qemuParams = utils.mkQemuFlags (lib.optional (!impureMode) "-vnc 127.0.0.1:0" ++ [
|
||||||
# "CD" drive with bootstrap pkgs
|
# "CD" drive with bootstrap pkgs
|
||||||
"-drive"
|
"-drive"
|
||||||
"id=virtio-win,file=${bootstrapPkgs},if=none,format=raw,readonly=on"
|
"id=virtio-win,file=${bootstrapPkgs},if=none,format=raw,readonly=on"
|
||||||
|
@ -108,7 +108,7 @@ let
|
||||||
in
|
in
|
||||||
''
|
''
|
||||||
#!${pkgs.runtimeShell}
|
#!${pkgs.runtimeShell}
|
||||||
set -euxo pipefail
|
set -euo pipefail
|
||||||
export PATH=${lib.makeBinPath [ p7zip utils.qemu guestfs-tools pkgs.wimlib ]}:$PATH
|
export PATH=${lib.makeBinPath [ p7zip utils.qemu guestfs-tools pkgs.wimlib ]}:$PATH
|
||||||
|
|
||||||
# Create a bootable "USB" image
|
# Create a bootable "USB" image
|
||||||
|
@ -132,11 +132,21 @@ let
|
||||||
''}
|
''}
|
||||||
rm -rf win
|
rm -rf win
|
||||||
|
|
||||||
${utils.tpmStartCommands}
|
|
||||||
|
|
||||||
# Qemu requires files to be rw
|
# Qemu requires files to be rw
|
||||||
qemu-img create -f qcow2 c.img ${diskImageSize}
|
qemu-img create -f qcow2 c.img ${diskImageSize}
|
||||||
qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams}
|
qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams} &
|
||||||
|
|
||||||
|
TEXT=""
|
||||||
|
while [ -n "$(jobs)" ]; do
|
||||||
|
${pkgs.vncdo}/bin/vncdo rcapture cap.png 0 0 1024 768
|
||||||
|
${pkgs.imagemagick}/bin/mogrify -density 70x70 -units PixelsPerInch cap.png
|
||||||
|
NEW_TEXT="$(${pkgs.tesseract5}/bin/tesseract cap.png stdout)"
|
||||||
|
if [ "$TEXT" != "$NEW_TEXT" ]; then
|
||||||
|
echo "$NEW_TEXT"
|
||||||
|
TEXT="$NEW_TEXT"
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -161,8 +171,6 @@ let
|
||||||
|
|
||||||
in ''
|
in ''
|
||||||
set -x
|
set -x
|
||||||
${utils.tpmStartCommands}
|
|
||||||
|
|
||||||
# Create an image referencing the previous image in the chain
|
# Create an image referencing the previous image in the chain
|
||||||
qemu-img create -F qcow2 -f qcow2 -b ${acc} c.img
|
qemu-img create -F qcow2 -f qcow2 -b ${acc} c.img
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue