Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ The repository supports two installation modes:
- **Light mode** (servers, CI, codespaces): Essential tools only
- **Full mode** (dev servers, workstations): Full development tooling including Task and mise

On Windows, full mode also applies opinionated workstation settings. On
Microsoft Surface Laptop models, the hardware power button is set to **Do
nothing** for both AC and battery power.

The mode is auto-detected based on:
- Hostname patterns (SVLDEV* = full, SVL* = light)
- Environment (codespaces, devcontainer, CI = light)
Expand Down
5 changes: 5 additions & 0 deletions docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ The repository supports two installation modes:
- **Full mode** (dev servers, workstations) — Full development tooling
including Task and mise.

On Windows, full mode also applies opinionated workstation settings. Microsoft
Surface Laptop models have the hardware power button set to **Do nothing** for
both AC and battery power, preventing an accidental press next to Delete from
suspending or shutting down the computer.

The mode is auto-detected based on:

- Hostname patterns (`SVL*DEV*` = full, `SVL*` = light).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# {{ if and (eq .chezmoi.os "windows") (eq .installType "full") }}
#!/usr/bin/env pwsh
# Disable the hardware power button on Microsoft Surface Laptop models.
# Runs in the opinionated full profile and is idempotent.

[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
"PSAvoidUsingWriteHost",
"",
Justification = "Matches existing chezmoi setup script progress output."
)]
param(
[switch]$SkipApply
)

$ErrorActionPreference = "Stop"

function Test-SurfaceLaptop {
param(
[AllowNull()]
[object]$ComputerSystem
)

if ($null -eq $ComputerSystem) {
return $false
}

$manufacturer = [string]$ComputerSystem.Manufacturer
$model = [string]$ComputerSystem.Model

return $manufacturer.Trim() -eq "Microsoft Corporation" -and
$model.Trim() -match "^Surface Laptop(?:\s|$)"
}

function Invoke-PowerCfg {
param(
[Parameter(Mandatory = $true)]
[string[]]$ArgumentList
)

$powerCfgPath = Join-Path $env:SystemRoot "System32\powercfg.exe"
if (-not (Test-Path -LiteralPath $powerCfgPath -PathType Leaf)) {
throw "powercfg.exe was not found at '$powerCfgPath'."
}

$output = & $powerCfgPath @ArgumentList 2>&1
if ($LASTEXITCODE -ne 0) {
$details = ($output | Out-String).Trim()
throw "powercfg.exe failed with exit code $LASTEXITCODE for '$($ArgumentList -join ' ')': $details"
}
}

function Disable-SurfaceLaptopPowerButton {
[CmdletBinding(SupportsShouldProcess)]
param(
[scriptblock]$GetComputerSystem = {
Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
},

[scriptblock]$InvokePowerCfg = {
param([string[]]$ArgumentList)
Invoke-PowerCfg -ArgumentList $ArgumentList
}
)

$computerSystem = & $GetComputerSystem
if (-not (Test-SurfaceLaptop -ComputerSystem $computerSystem)) {
Write-Host "[SKIP] Power button unchanged: this device is not a Microsoft Surface Laptop." -ForegroundColor Yellow
return [pscustomobject]@{
Status = "NotSurfaceLaptop"
Changed = $false
Model = [string]$computerSystem.Model
}
}

$powerButtonSetting = "7648efa3-dd9c-4e3e-b566-50f929386280"
$buttonSubgroup = "4f971e89-eebd-4455-a8de-9e59040e7347"
$commands = @(
[pscustomobject]@{
ArgumentList = @("/SETACVALUEINDEX", "SCHEME_CURRENT", $buttonSubgroup, $powerButtonSetting, "0")
}
[pscustomobject]@{
ArgumentList = @("/SETDCVALUEINDEX", "SCHEME_CURRENT", $buttonSubgroup, $powerButtonSetting, "0")
}
[pscustomobject]@{
ArgumentList = @("/SETACTIVE", "SCHEME_CURRENT")
}
)

$model = [string]$computerSystem.Model
if (-not $PSCmdlet.ShouldProcess($model, "Set the AC and battery power button action to Do nothing")) {
return [pscustomobject]@{
Status = "WhatIf"
Changed = $false
Model = $model
}
}

foreach ($command in $commands) {
& $InvokePowerCfg -ArgumentList $command.ArgumentList
}

Write-Host "[OK] Disabled the power button on $model for AC and battery power." -ForegroundColor Green
return [pscustomobject]@{
Status = "Disabled"
Changed = $true
Model = $model
}
}

if (-not $SkipApply) {
Disable-SurfaceLaptopPowerButton | Out-Null
}
# {{ end }}
151 changes: 151 additions & 0 deletions tests/powershell/SurfaceLaptopPowerButton.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#Requires -Version 7.0
<#
.SYNOPSIS
Tests the Surface Laptop power-button configuration.
#>

BeforeAll {
$script:RepoRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
$script:ScriptPath = Join-Path $script:RepoRoot `
"home\.chezmoiscripts\windows\run_onchange_disable-surface-laptop-power-button.ps1.tmpl"

$scriptContent = Get-Content -LiteralPath $script:ScriptPath -Raw
. ([scriptblock]::Create($scriptContent)) -SkipApply
}

Describe "Surface Laptop power button script" -Tag "Unit" {
It "exists and has valid PowerShell syntax" {
$script:ScriptPath | Should -Exist

$errors = $null
[System.Management.Automation.Language.Parser]::ParseFile(
$script:ScriptPath,
[ref]$null,
[ref]$errors
) | Out-Null

$errors | Should -BeNullOrEmpty
}

It "is limited to the Windows full profile" {
$content = Get-Content -LiteralPath $script:ScriptPath -Raw

$content | Should -Match 'eq \.chezmoi\.os "windows"'
$content | Should -Match 'eq \.installType "full"'
}

It "recognizes Microsoft Surface Laptop models" {
Test-SurfaceLaptop -ComputerSystem ([pscustomobject]@{
Manufacturer = "Microsoft Corporation"
Model = "Surface Laptop 7th Edition"
}) | Should -BeTrue
}

It "recognizes Surface Laptop Studio models" {
Test-SurfaceLaptop -ComputerSystem ([pscustomobject]@{
Manufacturer = "Microsoft Corporation"
Model = "Surface Laptop Studio 2"
}) | Should -BeTrue
}

It "does not match other Surface form factors" {
Test-SurfaceLaptop -ComputerSystem ([pscustomobject]@{
Manufacturer = "Microsoft Corporation"
Model = "Surface Pro 11th Edition"
}) | Should -BeFalse
}

It "requires Microsoft as the manufacturer" {
Test-SurfaceLaptop -ComputerSystem ([pscustomobject]@{
Manufacturer = "Contoso"
Model = "Surface Laptop 7"
}) | Should -BeFalse
}

It "does not invoke powercfg on non-Surface hardware" {
$script:PowerCfgCalls = @()

$result = Disable-SurfaceLaptopPowerButton `
-GetComputerSystem {
[pscustomobject]@{
Manufacturer = "Dell Inc."
Model = "XPS 13"
}
} `
-InvokePowerCfg {
param([string[]]$ArgumentList)
$script:PowerCfgCalls += , $ArgumentList
}

$result.Status | Should -Be "NotSurfaceLaptop"
$result.Changed | Should -BeFalse
$script:PowerCfgCalls | Should -BeNullOrEmpty
}

It "disables the current plan power button action for AC and battery power" {
$script:PowerCfgCalls = @()

$result = Disable-SurfaceLaptopPowerButton `
-GetComputerSystem {
[pscustomobject]@{
Manufacturer = "Microsoft Corporation"
Model = "Surface Laptop 7"
}
} `
-InvokePowerCfg {
param([string[]]$ArgumentList)
$script:PowerCfgCalls += , $ArgumentList
}

$result.Status | Should -Be "Disabled"
$result.Changed | Should -BeTrue
$script:PowerCfgCalls.Count | Should -Be 3
$script:PowerCfgCalls[0] -join " " | Should -Be (
"/SETACVALUEINDEX SCHEME_CURRENT " +
"4f971e89-eebd-4455-a8de-9e59040e7347 " +
"7648efa3-dd9c-4e3e-b566-50f929386280 0"
)
$script:PowerCfgCalls[1] -join " " | Should -Be (
"/SETDCVALUEINDEX SCHEME_CURRENT " +
"4f971e89-eebd-4455-a8de-9e59040e7347 " +
"7648efa3-dd9c-4e3e-b566-50f929386280 0"
)
$script:PowerCfgCalls[2] -join " " | Should -Be "/SETACTIVE SCHEME_CURRENT"
}

It "does not invoke powercfg under WhatIf" {
$script:PowerCfgCalls = @()

$result = Disable-SurfaceLaptopPowerButton `
-GetComputerSystem {
[pscustomobject]@{
Manufacturer = "Microsoft Corporation"
Model = "Surface Laptop 7"
}
} `
-InvokePowerCfg {
param([string[]]$ArgumentList)
$script:PowerCfgCalls += , $ArgumentList
} `
-WhatIf

$result.Status | Should -Be "WhatIf"
$result.Changed | Should -BeFalse
$script:PowerCfgCalls | Should -BeNullOrEmpty
}

It "propagates powercfg failures" {
{
Disable-SurfaceLaptopPowerButton `
-GetComputerSystem {
[pscustomobject]@{
Manufacturer = "Microsoft Corporation"
Model = "Surface Laptop 7"
}
} `
-InvokePowerCfg {
throw "powercfg failed"
}
} | Should -Throw "*powercfg failed*"
}
}