Compare commits

..

12 commits

9 changed files with 314 additions and 81 deletions

View file

@ -1,10 +1,29 @@
{ lib, ... }:
{ config, lib, ... }:
let
inherit (lib)
mkOption
types
;
cfg = config.valhalla;
capitalize = (import ../text.nix { inherit lib; }).capitalize;
linuxOptions = {
defaultShell = mkOption {
type = types.nullOr types.str;
description = "The default shell of the user.";
default = null;
};
rclone = {
configurations = mkOption {
type = types.attrsOf syncType;
description = "The configurations of the rclone mounts.";
default = {};
};
};
};
syncType = types.submodule (
{ ... }: {
options = {
@ -21,44 +40,45 @@
};
});
userType = types.submodule (
{ ... }: {
options = {
displayName = mkOption {
type = types.nullOr types.str;
description = "The human-readable name of the user.";
default = null;
};
mailAddress = mkOption {
type = types.nullOr types.str;
description = "The mail address of the user.";
default = null;
};
groups = mkOption {
type = types.listOf types.str;
description = "The additional groups of the user.";
default = [];
};
defaultShell = mkOption {
type = types.nullOr types.str;
description = "The default shell of the user.";
default = null;
};
git = (import ./git/options.nix) { inherit lib; };
rclone = {
configurations = mkOption {
type = types.attrsOf syncType;
description = "The configurations of the rclone mounts.";
default = {};
mkUserType = { options }: (
types.submodule (
{ ... }: {
options = {
displayName = mkOption {
type = types.nullOr types.str;
description = "The human-readable name of the user.";
default = null;
};
};
mailAddress = mkOption {
type = types.nullOr types.str;
description = "The mail address of the user.";
default = null;
};
groups = mkOption {
type = types.listOf types.str;
description = "The additional groups of the user.";
default = [];
};
git = (import ./git/options.nix) { inherit lib; };
} // options;
}));
userType = mkUserType {
options = linuxOptions;
};
winUserType = mkUserType {
options = {
microsoftAccount = mkOption {
type = types.bool;
description = "A value indicating whether this user is a Microsoft Account.";
default = false;
};
});
};
};
in {
options = {
valhalla = {
@ -67,6 +87,27 @@
description = "The users to create on the machine.";
default = {};
};
windows.users = mkOption {
type = types.attrsOf winUserType;
description = "The users to create on the Windows machine.";
default = (lib.attrsets.concatMapAttrs (
name: options:
if builtins.elem name (builtins.attrNames linuxOptions)
then {}
else {
${capitalize name} = (lib.attrsets.concatMapAttrs (
name: value:
if builtins.elem name (builtins.attrNames linuxOptions)
then {}
else {
${name} = value;
}
) options) // {
groups = [];
};
}) cfg.users);
};
};
};
}

View file

@ -1,4 +1,4 @@
{ lib, config, options, ... }:
{ lib, config, ... }:
let
inherit (lib)
mkOption
@ -7,16 +7,8 @@
types
;
users = config.valhalla.users;
setupUser = config.valhalla.setupUser.name;
capitalize = text:
let
chars = lib.strings.stringToCharacters text;
in lib.strings.concatStrings (
[(lib.strings.toUpper (builtins.elemAt chars 0))] ++
(lib.lists.drop 1 chars)
);
capitalize = (import ../text.nix { inherit lib; }).capitalize;
winType = types.submodule (
{ config, ... }: {
@ -27,15 +19,6 @@
default = capitalize setupUser;
};
users = mkOption {
type = options.valhalla.users.type;
description = "The users to add.";
default = lib.attrsets.concatMapAttrs (
name: options: {
${capitalize name} = options;
}) users;
};
dualboot = {
enable = mkEnableOption "dual boot";

9
lib/text.nix Normal file
View file

@ -0,0 +1,9 @@
{ lib, ... }: {
capitalize = text:
let
chars = lib.strings.stringToCharacters text;
in lib.strings.concatStrings (
[(lib.strings.toUpper (builtins.elemAt chars 0))] ++
(lib.lists.drop 1 chars)
);
}

View file

@ -10,6 +10,10 @@
enable = true;
linuxPercentage = 30;
};
users.Manuel = {
microsoftAccount = true;
};
};
partition.os.partitions = {

View file

@ -76,11 +76,16 @@ $null = New-Module {
$scriptPath = "$PSScriptRoot/../../Common/Scripts/config.fish";
function fish {
wsl --shell-type login -- nix --extra-experimental-features "nix-command flakes" run nixpkgs`#fish -- $args
}
if (-not $IsWindows) {
$escapedPath = (fish -c 'string escape $argv' "$scriptPath");
fish -c ". $escapedPath; $Script";
} else {
function fish {
wsl --shell-type login -- nix --extra-experimental-features "nix-command flakes" run nixpkgs`#fish -- $args
}
fish -c ". $(ConvertTo-LinuxPath $scriptPath); $Script" | ConvertFrom-Json;
fish -c ". $(ConvertTo-LinuxPath $scriptPath); $Script" | ConvertFrom-Json;
}
}
<#
@ -92,10 +97,51 @@ $null = New-Module {
#>
function Get-Config {
param(
[string] $Name,
[Parameter(ValueFromRemainingArguments)]
[string[]] $ArgumentList
)
Invoke-ConfigScript "getConfig $Name --json $ArgumentList";
}
<#
.SYNOPSIS
Gets a user configuration.
.PARAMETER UserName
The name of the user to get the configuration for.
.PARAMETER Name
The name of the configuration to get.
#>
function Get-UserConfig {
param(
[string] $UserName = $env:UserName,
[Parameter(Mandatory, Position = 0)]
[string] $Name
)
Invoke-ConfigScript "getConfig $Name --json";
if ((Get-Users) -contains $UserName) {
Get-Config "valhalla.windows.users.$UserName.$Name";
} else {
return $null;
}
}
<#
.SYNOPSIS
Gets the attributes of a configuration object.
.PARAMETER Name
The name of the configuration to get the attributes of.
#>
function Get-Attributes {
param(
[string] $Name
)
Invoke-ConfigScript "getAttributes $Name";
}
<#
@ -103,7 +149,7 @@ $null = New-Module {
Gets the names of the users to create.
#>
function Get-Users {
Invoke-ConfigScript "getUsers";
Get-Attributes "valhalla.windows.users";
}
<#

View file

@ -196,9 +196,9 @@ $null = New-Module {
function Start-SoftwareInstaller {
param(
[string] $Name,
[scriptblock] $Installer = { },
[scriptblock] $Configurator = { },
[scriptblock] $UserConfigurator = { },
[scriptblock] $Installer = $null,
[scriptblock] $Configurator = $null,
[scriptblock] $UserConfigurator = $null,
[Nullable[InstallerAction]] $Action,
[hashtable] $Arguments
)
@ -236,12 +236,16 @@ $null = New-Module {
};
if ($action -eq ([InstallerAction]::Install)) {
Write-Host "Installing $Name";
& $Installer @argumentList;
if ($Installer) {
Write-Host "Installing $Name";
& $Installer @argumentList;
}
# ToDo: Automatically configure after installation
} elseif ($Action -eq ([InstallerAction]::Configure)) {
Write-Host "Configuring $Name";
& $Configurator @argumentList;
if ($Configurator) {
Write-Host "Configuring $Name";
& $Configurator @argumentList;
}
if (-not (Test-SetupUser)) {
$argumentList.Add("action", [InstallerAction]::ConfigureUser);
@ -252,8 +256,10 @@ $null = New-Module {
$argumentList.Add($userArgument, ($env:UserName));
}
Write-Host "Configuring $Name for user ``$($Arguments[$userArgument])``";
& $UserConfigurator @argumentList;
if ($UserConfigurator) {
Write-Host "Configuring $Name for user ``$($Arguments[$userArgument])``";
& $UserConfigurator @argumentList;
}
}
};

View file

@ -8,8 +8,12 @@ function getConfig -S -a property
evalModule "$CONFIG_MODULE" "$property" $argv[2..]
end
function getAttributes -S -a property
getConfig "$property" --apply "builtins.attrNames" --json
end
function getUsers -S
getConfig valhalla.users --apply "builtins.attrNames" --json
getAttributes "valhalla.users"
end
function isSet -S -a property

View file

@ -9,6 +9,7 @@ $null = New-Module {
. "$PSScriptRoot/../Scripts/Hooks.ps1";
. "$PSScriptRoot/../Scripts/PowerManagement.ps1";
. "$PSScriptRoot/../Scripts/Update.ps1";
. "$PSScriptRoot/../Scripts/Users.ps1";
. "$PSScriptRoot/../../Common/Scripts/Config.ps1";
. "$PSScriptRoot/../../Common/Scripts/Operations.ps1";
. "$PSScriptRoot/../../Common/Scripts/Software.ps1";
@ -72,26 +73,25 @@ $null = New-Module {
continue;
}
if (-not (& { wsl --status; $?; })) {
if (-not (& { $null = wsl --status; $?; })) {
wsl --install --no-launch;
Restart-Intermediate;
return;
}
if (-not (wsl --shell-type login type -t nix)) {
if (-not (& { $null = wsl -l; $?; })) {
ubuntu install --root;
continue;
}
if (-not (wsl --shell-type login type -t nix)) {
wsl -- sh `<`(curl -L https://nixos.org/nix/install`) --daemon --yes;
wsl --shutdown;
continue;
}
Invoke-Hook "Install-PSModules" -Fallback {
Install-Module -AcceptLicense -Force PSWindowsUpdate;
Install-Module -AcceptLicense -Force PSScriptAnalyzer;
. "$PSScriptRoot/../Software/PinnedItem/Manage.ps1";
};
if (-not (Test-PSPackage Selenium.WebDriver)) {
Write-Host "Installing browser automation tools…";
Install-Module -AcceptLicense -Force NuGet;
Import-Module NuGet;
$null = Install-Package -Force Selenium.WebDriver -RequiredVersion 4.10.0 -SkipDependencies;
@ -99,6 +99,13 @@ $null = New-Module {
}
Install-ChocoPackage selenium-gecko-driver firefox;
Invoke-Hook "Install-PSModules" -Fallback {
Install-Module -AcceptLicense -Force PSWindowsUpdate;
Install-Module -AcceptLicense -Force PSScriptAnalyzer;
. "$PSScriptRoot/../Software/PinnedItem/Manage.ps1";
};
Set-Stage ([SetupStage]::Configure);
} else {
$null = Import-Module PSWindowsUpdate;
@ -411,6 +418,7 @@ $null = New-Module {
Set-Stage ([SetupStage]::CreateUser);
}
([SetupStage]::CreateUser) {
Start-ValhallaUserSetup;
Set-IsFinished $true;
}
}

View file

@ -0,0 +1,132 @@
using namespace System.Management.Automation.Host;
$null = New-Module {
. "$PSScriptRoot/../../Common/Scripts/Config.ps1";
[string] $userOption = "SetupUser";
[string] $userStageOption = "UserStage";
enum UserStage {
Create
ReEnableFeatures
}
<#
.SYNOPSIS
Gets the current stage of the user creation.
#>
function Get-UserStage {
Get-SetupOption -Name $userStageOption;
}
<#
.SYNOPSIS
Sets the current user creation stage.
.PARAMETER Value
The value to set the stage to.
#>
function Set-UserStage {
param(
[UserStage] $Value
)
Set-SetupOption -Name $userStageOption -Value $Value;
}
<#
.SYNOPSIS
Creates the configured users.
#>
function Start-ValhallaUserSetup {
[int] $current = Get-SetupOption $userOption;
[string[]] $users = Get-Users;
function Add-MicrosoftAccount {
param(
[string] $Name
)
$newUser = & {
while ($true) {
$currentUsers = Get-LocalUser | ForEach-Object { $_.Name };
Write-Host (
@(
"So… Windows is too dumb to create users which are bound to a Microsoft Account.",
"Thus, you have to do it by yourself.",
"So sorry…") -join "`n");
Write-Host "Create a user for ``$Name`` manually (because Windows is too stupid)…";
Read-Host "Hit enter once you're done";
$newUsers = @(Get-LocalUser | Where-Object { -not ($currentUsers -contains $_.Name) });
if ($newUsers.Count) {
if ($newUsers.Count -eq 1) {
$newUser = $newUsers[0];
Write-Host "Found new user ``$newUser``";
if (
$Host.UI.PromptForChoice(
"Confirm",
"Is ``$newUser`` your user?",
[ChoiceDescription[]]@(
[ChoiceDescription]::new("&No", "``$newUser`` is not your user"),
[ChoiceDescription]::new("&Yes", "``$newUser`` is your user")),
0) -eq 1) {
return $newUser;
}
} else {
$result = $Host.UI.PromptForChoice(
"Select your User",
"Which one is your user?",
[ChoiceDescription[]](
& {
[ChoiceDescription]::new("&None", "None of these users is yours");
for ($i = 0; $i -lt $newUsers.Count; $i++) {
$name = "$($newUsers[$i])";
[ChoiceDescription]::new("&$($i + 1) - ``$name``", "Your user is ``$name``");
}
}), 0);
if ($result -gt 0) {
return $newUsers[$result - 1];
}
}
Write-Host "Unable to determine the new user";
Write-Host "Retrying…";
}
}
};
Write-Host "Renaming the new user to ``$Name``";
Rename-LocalUser $newUser $Name;
}
for ($i = $current ?? 0; $i -lt $users.Count; $i++) {
Set-SetupOption $userOption $i;
$name = $users[$i];
Write-Host "Creating personal user ``$name``";
if (Get-UserConfig -UserName $name "microsoftAccount") {
Add-MicrosoftAccount $name;
} else {
$displayName = Get-UserConfig -UserName $name "displayName";
$userArguments = @{
name = $name;
};
if ($displayName) {
$userArguments.fullName = $displayName;
}
New-LocalUser -NoPassword @userArguments;
}
}
}
};