Files
dosh/tests/release_scripts.rs
T
DuProcess 1ca8a2e3ce
ci / test (push) Canceled after 0s
ci / fuzz-smoke (push) Canceled after 0s
ci / macos-client (macos-aarch64, macos-14) (push) Canceled after 0s
ci / macos-client (macos-x86_64, macos-13) (push) Canceled after 0s
ci / windows-client (push) Canceled after 0s
ci / package-release (linux-x86_64, ubuntu-latest, , , ) (push) Canceled after 0s
ci / package-release (macos-aarch64, macos-14, , , ) (push) Canceled after 0s
ci / package-release (macos-x86_64, macos-13, , , ) (push) Canceled after 0s
ci / package-release (windows-aarch64, windows-latest, aarch64, windows, aarch64-pc-windows-msvc) (push) Canceled after 0s
ci / package-release (windows-x86_64, windows-latest, , , ) (push) Canceled after 0s
ci / remote-bench (push) Canceled after 0s
ci / publish-gitea-release (push) Canceled after 0s
Add cmd installer entrypoint for Windows
2026-07-16 22:12:09 -04:00

565 lines
23 KiB
Rust

#[test]
fn quiet_update_keeps_prebuilt_enabled_by_default() {
let install = include_str!("../install.sh");
assert!(install.contains("use_prebuilt=\"${DOSH_USE_PREBUILT:-1}\""));
assert!(
!install.contains("use_prebuilt=0"),
"quiet updates must not force source builds"
);
assert!(install.contains("[ \"$quiet\" != \"1\" ] && echo \"Trying Dosh prebuilt"));
assert!(install.contains("[ \"$quiet\" != \"1\" ] && echo \"Building Dosh $role\""));
assert!(
!install.contains("[ \"$quiet\" = \"1\" ] && echo"),
"quiet updates must suppress informational installer output"
);
}
#[test]
fn release_scripts_skip_stale_artifacts_when_auto_discovering() {
let upload = include_str!("../scripts/upload-gitea-release.sh");
assert!(upload.contains("skipping $artifact: version"));
assert!(upload.contains("expected_version"));
let publish = include_str!("../scripts/publish-gitea-release.sh");
assert!(publish.contains("skipping stale artifact"));
assert!(publish.contains("actual=\"$(artifact_version \"$artifact\" || true)\""));
}
#[test]
fn release_upload_refuses_partial_platform_sets_by_default() {
let upload = include_str!("../scripts/upload-gitea-release.sh");
assert!(upload.contains("require_complete_release_artifacts \"$@\""));
assert!(upload.contains("DOSH_RELEASE_ALLOW_PARTIAL=1"));
assert!(upload.contains("refusing partial release upload"));
for name in [
"dosh-linux-x86_64.tar.gz",
"dosh-macos-aarch64.tar.gz",
"dosh-macos-x86_64.tar.gz",
"dosh-windows-x86_64.zip",
"dosh-windows-aarch64.zip",
] {
assert!(
upload.contains(name),
"release upload completeness check must require {name}"
);
}
}
#[test]
fn release_upload_marks_prerelease_tags_as_prereleases() {
let upload = include_str!("../scripts/upload-gitea-release.sh");
assert!(upload.contains("release_prerelease=false"));
assert!(upload.contains("*-*) release_prerelease=true"));
assert!(upload.contains("\"prerelease\":%s"));
assert!(upload.contains("metadata_payload="));
assert!(
upload.contains("-X PATCH"),
"existing Gitea releases must be corrected when prerelease metadata changes"
);
}
#[test]
fn release_gates_test_all_targets() {
let makefile = include_str!("../Makefile");
let ci = include_str!("../.github/workflows/ci.yml");
assert!(makefile.contains("test:\n\tcargo test --all-targets"));
assert!(makefile.contains("cargo test --all-targets\n\t$(MAKE) tui-harness"));
assert!(ci.contains("run: sh -n install.sh scripts/*.sh"));
assert!(ci.contains("run: cargo clippy --all-targets -- -D warnings"));
assert!(ci.contains("run: cargo test --all-targets"));
assert!(ci.contains("run: sh scripts/tui-harness.sh"));
let macos_client = ci
.split("macos-client:")
.nth(1)
.and_then(|tail| tail.split("\n windows-client:").next())
.expect("macos-client job");
assert!(macos_client.contains("name: macos-aarch64"));
assert!(macos_client.contains("name: macos-x86_64"));
assert!(macos_client.contains("os: macos-14"));
assert!(macos_client.contains("os: macos-13"));
assert!(macos_client.contains("name: Test macOS client"));
assert!(
macos_client.contains("run: cargo test --all-targets"),
"macOS CI must run Rust tests, not only release packaging"
);
let windows_client = ci
.split("windows-client:")
.nth(1)
.and_then(|tail| tail.split("\n package-release:").next())
.expect("windows-client job");
assert!(windows_client.contains("runs-on: windows-latest"));
assert!(windows_client.contains("name: Test Windows client"));
assert!(
windows_client.contains("run: cargo test --all-targets"),
"Windows CI must run Rust tests, not only build/package"
);
}
#[test]
fn one_dot_zero_gates_default_to_full_length_soaks() {
let makefile = include_str!("../Makefile");
let reconnect_check = makefile
.split("reconnect-check:")
.nth(1)
.and_then(|tail| tail.split("\nhostile-network-check:").next())
.expect("reconnect-check target");
assert!(reconnect_check.contains("DOSH_SOAK_SECONDS=$${DOSH_SOAK_SECONDS:-1800}"));
assert!(!reconnect_check.contains("DOSH_SOAK_SECONDS=$${DOSH_SOAK_SECONDS:-300}"));
let hostile_check = makefile
.split("hostile-network-check:")
.nth(1)
.and_then(|tail| tail.split("\npersistence-check:").next())
.expect("hostile-network-check target");
assert!(hostile_check.contains("DOSH_BADNET_SOAK_SECONDS=$${DOSH_BADNET_SOAK_SECONDS:-1800}"));
assert!(!hostile_check.contains("DOSH_BADNET_SOAK_SECONDS=$${DOSH_BADNET_SOAK_SECONDS:-300}"));
}
#[test]
fn installers_skip_stale_latest_release_prebuilts() {
let install = include_str!("../install.sh");
assert!(install.contains("latest_release_is_stale"));
assert!(install.contains("current_source_version"));
assert!(install.contains("latest release $latest is older than source $current"));
assert!(install.contains("version_prerelease"));
assert!(install.contains("prerelease_less_than"));
assert!(
install.contains("if latest_release_is_stale; then"),
"unix installer must skip stale latest before downloading a prebuilt"
);
let ps1 = include_str!("../install.ps1");
assert!(ps1.contains("Latest-ReleaseIsStale"));
assert!(ps1.contains("Current-SourceVersion"));
assert!(ps1.contains("latest release $latestVersion is older than source $current"));
assert!(ps1.contains("Version-Prerelease"));
assert!(ps1.contains("Compare-Prerelease"));
assert!(
ps1.contains("if (Latest-ReleaseIsStale)"),
"windows installer must skip stale latest before downloading a prebuilt"
);
}
#[test]
fn installers_refuse_prebuilts_without_checksums() {
let install = include_str!("../install.sh");
assert!(install.contains("verify_archive_checksum"));
assert!(install.contains("refusing unverified binary for $url"));
assert!(
!install.contains("continuing without sidecar verification"),
"unix installer must not silently install an unverifiable prebuilt"
);
let ps1 = include_str!("../install.ps1");
assert!(ps1.contains("Verify-ArchiveChecksum"));
assert!(ps1.contains("refusing unverified binary for $Url"));
assert!(
!ps1.contains("continuing without sidecar verification"),
"windows installer must not silently install an unverifiable prebuilt"
);
}
#[test]
fn windows_installer_reuses_persistent_source_update_cache() {
let ps1 = include_str!("../install.ps1");
assert!(ps1.contains("DOSH_UPDATE_CACHE"));
assert!(ps1.contains("elseif ($env:LOCALAPPDATA)"));
assert!(ps1.contains("Join-Path $env:LOCALAPPDATA \"dosh\\source\""));
assert!(ps1.contains("Join-Path $HOME \".cache\\dosh\\source\""));
assert!(ps1.contains("Assert-NoRelativePathSegments($Path)"));
assert!(ps1.contains("Assert-NoRelativePathSegments $Path"));
assert!(ps1.contains("$segment -eq \".\" -or $segment -eq \"..\""));
assert!(ps1.contains("$localAppData = Normalize-PathForCompare $env:LOCALAPPDATA"));
assert!(ps1.contains(
"$localAppDataDosh = Normalize-PathForCompare (Join-Path $env:LOCALAPPDATA \"dosh\")"
));
assert!(ps1.contains("$full -eq $localAppData -or $full -eq $localAppDataDosh"));
assert!(ps1.contains("$sourceCache = Assert-SafeUpdateCache $UpdateCache"));
assert!(ps1.contains(
"$fetchArgs = @(\"-C\", $sourceCache, \"fetch\", \"--depth\", \"1\", \"origin\", \"main\")"
));
assert!(ps1.contains("$fetchArgs = @(\"-C\", $sourceCache, \"fetch\", \"-q\", \"--depth\", \"1\", \"origin\", \"main\")"));
assert!(ps1.contains("git @fetchArgs"));
assert!(ps1.contains("git -C $sourceCache checkout -q -B main FETCH_HEAD"));
assert!(ps1.contains(
"$cloneArgs = @(\"clone\", \"--depth\", \"1\", \"--branch\", \"main\", $Repo, $sourceCache)"
));
assert!(ps1.contains("$cloneArgs = @(\"clone\", \"-q\", \"--depth\", \"1\", \"--branch\", \"main\", $Repo, $sourceCache)"));
assert!(ps1.contains("git @cloneArgs | Out-Null"));
assert!(
!ps1.contains("git clone --depth 1 $Repo $tmp"),
"Windows source updates should reuse a persistent cache instead of recloning into temp"
);
}
#[test]
fn windows_quiet_source_updates_silence_git_like_unix() {
let install = include_str!("../install.sh");
let ps1 = include_str!("../install.ps1");
assert!(install.contains("git -C \"$update_cache\" fetch -q --depth 1 origin main"));
assert!(install.contains("git clone -q --depth 1 --branch main \"$repo\" \"$update_cache\""));
assert!(ps1.contains("if ($Quiet)"));
assert!(ps1.contains("\"fetch\", \"-q\", \"--depth\""));
assert!(ps1.contains("\"clone\", \"-q\", \"--depth\""));
}
#[test]
fn windows_installer_bootstraps_rust_for_source_fallback_like_unix() {
let install = include_str!("../install.sh");
let ps1 = include_str!("../install.ps1");
assert!(install.contains("ensure_cargo()"));
assert!(install.contains("cargo not found; installing Rust toolchain with rustup"));
assert!(ps1.contains("function Ensure-Cargo"));
assert!(ps1.contains("cargo not found; installing Rust toolchain with winget/rustup"));
assert!(ps1.contains("winget install --id Rustlang.Rustup"));
assert!(ps1.contains("--accept-package-agreements --accept-source-agreements"));
assert!(ps1.contains("PathList-Contains $env:Path $cargoBin"));
assert!(ps1.contains("$env:Path = \"$cargoBin;$env:Path\""));
assert!(ps1.contains("Ensure-Cargo"));
assert!(!ps1.contains("Require-Command cargo"));
}
#[test]
fn windows_installer_bootstraps_git_for_source_fallback_like_unix() {
let ps1 = include_str!("../install.ps1");
assert!(ps1.contains("function Ensure-Git"));
assert!(ps1.contains("git not found; installing Git with winget"));
assert!(ps1.contains("winget install --id Git.Git"));
assert!(ps1.contains("--accept-package-agreements --accept-source-agreements"));
assert!(ps1.contains("Join-Path $env:ProgramFiles \"Git\\cmd\""));
assert!(ps1.contains("Join-Path ${env:ProgramFiles(x86)} \"Git\\cmd\""));
assert!(ps1.contains("Join-Path $env:LOCALAPPDATA \"Programs\\Git\\cmd\""));
assert!(ps1.contains("PathList-Contains $env:Path $gitBin"));
assert!(ps1.contains("$env:Path = \"$gitBin;$env:Path\""));
assert!(ps1.contains("Ensure-Git"));
assert!(!ps1.contains("Require-Command git"));
}
#[test]
fn windows_cmd_installer_wraps_native_powershell_installer() {
let cmd = include_str!("../install.cmd");
let readme = include_str!("../README.md");
assert!(cmd.contains("@echo off"));
assert!(cmd.contains("https://git.palav.dev/Palav/dosh/raw/branch/main/install.ps1"));
assert!(cmd.contains("for %%P in (powershell.exe pwsh.exe powershell pwsh) do"));
assert!(cmd.contains("Invoke-WebRequest -UseBasicParsing"));
assert!(cmd.contains("-ExecutionPolicy Bypass -File \"%TMP_PS%\" %*"));
assert!(cmd.contains("exit /b %RC%"));
assert!(readme.contains("Windows client from `cmd.exe`:"));
assert!(readme.contains("install.cmd"));
}
#[test]
fn windows_installer_honors_quiet_update_mode() {
let ps1 = include_str!("../install.ps1");
assert!(ps1.contains("$Quiet = $env:DOSH_UPDATE_QUIET"));
assert!(ps1.contains("function Write-Info($Message)"));
assert!(ps1.contains("if (-not $Quiet)"));
assert!(ps1.contains("Write-Info \"Trying Dosh prebuilt"));
assert!(ps1.contains("Write-Info \"Falling back to source build\""));
assert!(ps1.contains("Write-Info \"Installed Dosh client to $bindir\""));
assert!(
!ps1.contains("Write-Host \"Trying Dosh prebuilt"),
"Windows quiet update must not print prebuilt progress directly"
);
assert!(
!ps1.contains("Write-Host \"Installed Dosh client"),
"Windows quiet update must not print install summary directly"
);
}
#[test]
fn windows_source_fallback_builds_client_only_like_unix() {
let install = include_str!("../install.sh");
let ps1 = include_str!("../install.ps1");
assert!(install.contains("cargo build -q --release --bin dosh-client"));
assert!(install.contains("cargo build --release --bin dosh-client"));
assert!(ps1.contains("$buildArgs = @(\"build\", \"--release\", \"--bin\", \"dosh-client\")"));
assert!(
ps1.contains(
"$buildArgs = @(\"build\", \"-q\", \"--release\", \"--bin\", \"dosh-client\")"
),
"Windows quiet source fallback should use cargo -q like Unix"
);
assert!(ps1.contains("cargo @buildArgs"));
assert!(
!ps1.contains("cargo build --release --bin dosh-client --bin dosh-bench"),
"Windows client source fallback must not compile dosh-bench on every update"
);
assert!(ps1.contains("if (Test-Path \"target\\release\\dosh-bench.exe\")"));
}
#[test]
fn windows_prebuilt_install_requires_client_but_not_bench() {
let install = include_str!("../install.sh");
let ps1 = include_str!("../install.ps1");
assert!(install.contains("install_extracted_binary \"$tmpdir/extract\" dosh-client"));
assert!(install.contains("if found_bench=\"$(find_extracted_binary"));
assert!(ps1.contains("throw \"prebuilt archive missing dosh-client.exe\""));
assert!(ps1.contains("-Filter \"dosh-bench.exe\""));
assert!(ps1.contains("if ($bench)"));
assert!(
!ps1.contains("throw \"prebuilt archive missing dosh-bench.exe\""),
"Windows prebuilt install should not fall back to source just because dosh-bench is absent"
);
}
#[test]
fn windows_installer_adds_user_path_idempotently() {
let ps1 = include_str!("../install.ps1");
assert!(ps1.contains("function Add-UserPath($PathToAdd)"));
assert!(ps1.contains("function PathList-Contains($PathValue, $PathToFind)"));
assert!(ps1.contains("$wanted = Normalize-PathForCompare $PathToFind"));
assert!(ps1.contains("foreach ($entry in ($PathValue -split ';' | Where-Object { $_ }))"));
assert!(ps1.contains("$wanted = Normalize-PathForCompare $PathToAdd"));
assert!(ps1.contains("-ieq $wanted"));
assert!(ps1.contains("(@($entries) + $PathToAdd) -join ';'"));
assert!(ps1.contains("PathList-Contains $env:Path $PathToAdd"));
assert!(ps1.contains("$env:Path = \"$PathToAdd;$env:Path\""));
assert!(
ps1.find("PathList-Contains $env:Path $PathToAdd").unwrap()
< ps1.find("foreach ($entry in $entries)").unwrap(),
"Windows installer must refresh the current shell PATH even when user PATH already contains Dosh"
);
assert!(
!ps1.contains("\"$userPath;$bindir\""),
"Windows installer must not write a leading semicolon when user PATH is empty"
);
assert!(ps1.contains("Add-UserPath $bindir"));
}
#[test]
fn windows_installer_prefers_native_wow64_arch_for_prebuilts() {
let ps1 = include_str!("../install.ps1");
assert!(ps1.contains("PROCESSOR_ARCHITEW6432"));
assert!(ps1.contains("$arch = if ($env:PROCESSOR_ARCHITEW6432)"));
assert!(ps1.contains("\"ARM64\" { \"aarch64\"; break }"));
assert!(ps1.contains("\"dosh-windows-$(Normalize-Arch).zip\""));
}
#[test]
fn unix_installer_rejects_unsafe_source_update_cache_paths() {
let install = include_str!("../install.sh");
assert!(install.contains("normalize_update_cache_path()"));
assert!(install.contains("safe_update_cache_path()"));
assert!(install.contains("update_cache=\"$(safe_update_cache_path \"$update_cache\")\""));
for pattern in [
"\"\"|\"/\"|\"$home_norm\"|\"$home_cache_norm\"",
"\".\"|\"..\"|./*|../*|*/.|*/..|*/./*|*/../*",
] {
assert!(
install.contains(pattern),
"Unix installer must reject unsafe update cache pattern {pattern}"
);
}
assert!(
install.contains("rm -rf \"$update_cache\""),
"source cache replacement remains the destructive operation guarded by safe_update_cache_path"
);
}
#[test]
fn installers_generate_same_native_auth_client_defaults() {
let install = include_str!("../install.sh");
let ps1 = include_str!("../install.ps1");
for setting in [
"auth_preference = \"native,ssh\"",
"trust_on_first_use = false",
"native_auth_timeout_ms = 700",
"known_hosts = \"~/.config/dosh/known_hosts\"",
"identity_files = [\"~/.ssh/id_ed25519\"]",
"use_ssh_agent = true",
"forward_agent = false",
] {
assert!(
install.contains(setting),
"Unix installer missing client default {setting}"
);
assert!(
ps1.contains(setting),
"Windows installer missing client default {setting}"
);
}
}
#[test]
fn windows_installer_uses_native_credential_cache_path() {
let install = include_str!("../install.sh");
let ps1 = include_str!("../install.ps1");
assert!(install.contains("credential_cache = \"~/.local/share/dosh/credentials\""));
assert!(ps1.contains("$credentialCache = if ($env:LOCALAPPDATA)"));
assert!(ps1.contains("Join-Path $env:LOCALAPPDATA \"dosh\\credentials\""));
assert!(ps1.contains(".Replace('\\', '/')"));
assert!(ps1.contains("credential_cache = \"$credentialCache\""));
}
#[test]
fn windows_installer_generates_hosts_config_like_unix() {
let install = include_str!("../install.sh");
let ps1 = include_str!("../install.ps1");
for setting in [
"hosts.toml",
"# default_command = \"tm\"",
"[default]",
"predict = true",
] {
assert!(
install.contains(setting),
"Unix installer missing host config setting {setting}"
);
assert!(
ps1.contains(setting),
"Windows installer missing host config setting {setting}"
);
}
assert!(install.contains("host_udp_line=\"# dosh_host = \\\"server.example.com\\\"\""));
assert!(
ps1.contains("$hostUdpLine = if ($DoshHost)")
&& ps1.contains("# dosh_host = `\"server.example.com`\""),
"Windows installer should not bake an SSH target into dosh_host"
);
assert!(
ps1.contains("Write-Utf8NoBom $hostsConfig $hostsToml"),
"Windows installer should write TOML as UTF-8 without BOM"
);
assert!(
!ps1.contains("Set-Content -NoNewline -Encoding utf8 $hostsConfig"),
"PowerShell Set-Content -Encoding utf8 can write a BOM on older hosts"
);
}
#[test]
fn package_check_verifies_only_artifacts_it_builds() {
let makefile = include_str!("../Makefile");
let verify = include_str!("../scripts/verify-release-artifacts.sh");
assert!(
verify.contains("if [ \"$#\" -gt 0 ]; then"),
"release verifier must accept an explicit artifact list"
);
let package_check = makefile
.split("package-check:")
.nth(1)
.and_then(|tail| tail.split("\ninstall:").next())
.expect("package-check target");
assert!(package_check.contains("package-release-linux"));
assert!(package_check.contains("package-release-windows"));
assert!(package_check.contains("package-release-windows-arm64"));
assert!(package_check.contains("aarch64-w64-mingw32-clang"));
assert!(package_check.contains("aarch64-pc-windows-gnullvm"));
assert!(package_check.contains("dosh-linux-x86_64.tar.gz"));
assert!(package_check.contains("dosh-windows-x86_64.zip"));
assert!(
!package_check.contains("dosh-macos-aarch64.tar.gz"),
"Linux package-check does not build macOS artifacts, so it must not depend on one"
);
}
#[test]
fn complete_release_verifier_requires_all_desktop_architectures() {
let verify = include_str!("../scripts/verify-release-artifacts.sh");
assert!(verify.contains("dosh-linux-x86_64.tar.gz"));
assert!(verify.contains("dosh-macos-aarch64.tar.gz"));
assert!(verify.contains("dosh-macos-x86_64.tar.gz"));
assert!(verify.contains("dosh-windows-x86_64.zip"));
assert!(verify.contains("dosh-windows-aarch64.zip"));
}
#[test]
fn package_release_workflow_builds_both_windows_architectures() {
let ci = include_str!("../.github/workflows/ci.yml");
assert!(ci.contains("name: windows-x86_64"));
assert!(ci.contains("name: windows-aarch64"));
assert!(ci.contains("DOSH_PACKAGE_ARCH: ${{ matrix.package_arch }}"));
assert!(ci.contains("aarch64-pc-windows-msvc"));
}
#[test]
fn release_verifier_checks_checksums_and_expected_binaries() {
let verify = include_str!("../scripts/verify-release-artifacts.sh");
assert!(verify.contains("verify_checksum()"));
assert!(verify.contains("release checksum mismatch"));
for member in [
"dosh/bin/dosh",
"dosh/bin/dosh-client",
"dosh/bin/dosh-server",
"dosh/bin/dosh-auth",
"dosh/bin/dosh-bench",
"dosh/bin/dosh.exe",
"dosh/bin/dosh-client.exe",
"dosh/bin/dosh-bench.exe",
] {
assert!(
verify.contains(member),
"release verifier must require archive member {member}"
);
}
assert!(verify.contains("artifact missing expected member"));
}
#[test]
fn package_release_stamps_git_build_info() {
let package = include_str!("../scripts/package-release.sh");
let build = include_str!("../build.rs");
for name in [
"DOSH_BUILD_GIT_HASH",
"DOSH_BUILD_COMMIT_DATE",
"DOSH_BUILD_GIT_DIRTY",
] {
assert!(package.contains(name), "package script must export {name}");
assert!(build.contains(name), "build.rs must consume {name}");
}
assert!(package.contains("git rev-parse --short=12 HEAD"));
assert!(package.contains("git show -s --format=%cs HEAD"));
assert!(build.contains("cargo:rerun-if-env-changed=DOSH_BUILD_GIT_HASH"));
}
#[test]
fn package_release_maps_windows_cross_arch_targets() {
let package = include_str!("../scripts/package-release.sh");
assert!(package.contains("host_arch=\"$(normalize_arch)\""));
assert!(package.contains("elif [ \"$arch\" != \"$host_arch\" ]; then"));
assert!(package.contains("x86_64-pc-windows-msvc"));
assert!(package.contains("aarch64-pc-windows-msvc"));
}
#[test]
fn package_release_windows_zip_tries_classic_and_core_powershell() {
let package = include_str!("../scripts/package-release.sh");
assert!(package.contains("powershell_compress_archive()"));
assert!(package.contains("for ps in powershell.exe powershell pwsh.exe pwsh; do"));
assert!(package.contains("\"$ps\" -NoProfile -Command"));
assert!(package.contains("windows packaging requires PowerShell or zip"));
}
#[test]
fn systemd_service_does_not_sandbox_remote_shells() {
let service = include_str!("../packaging/systemd/dosh-server.service");
let install = include_str!("../install.sh");
for raw in [service, install] {
assert!(raw.contains("KillMode=process"));
assert!(
raw.contains("Restart=always"),
"dosh-server should come back after accidental SIGTERM while preserving child shells"
);
for directive in [
"NoNewPrivileges=",
"PrivateTmp=",
"ProtectSystem=",
"ProtectHome=",
"RestrictAddressFamilies=",
"RestrictRealtime=",
"LockPersonality=",
"MemoryDenyWriteExecute=",
] {
assert!(
!raw.contains(directive),
"dosh sessions are user shells; systemd sandbox directive {directive} changes terminal/app behavior"
);
}
assert!(
!raw.contains("ReadWritePaths="),
"remote shells must not be restricted to dosh config/data paths"
);
}
}