From 5d140104ea701482cffa3f7ef0f03c97fe4d0507 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:29:29 -0400 Subject: [PATCH 1/6] workstation-v0: add keyboard policy validation helper --- .../bin/check-keyboard-policy.sh | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh diff --git a/profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh b/profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh new file mode 100644 index 0000000..6145bd0 --- /dev/null +++ b/profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Validate workstation-v0 keyboard/remap policy without requiring root access. +# Emits key=value lines for doctor/status or CI consumption. + +have(){ command -v "$1" >/dev/null 2>&1; } + +profile_dir(){ + cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd +} + +backend(){ + printf '%s\n' "${SOURCEOS_REMAP_BACKEND:-input-remapper}" +} + +template_path(){ + printf '%s/sourceos/input/xremap-macos-compat.yml\n' "${XDG_CONFIG_HOME:-$HOME/.config}" +} + +main(){ + local pdir b tmpl + pdir="$(profile_dir)" + b="$(backend)" + tmpl="$(template_path)" + + printf 'profile_dir=%s\n' "$pdir" + printf 'selected_backend=%s\n' "$b" + + case "$b" in + input-remapper|xremap|kinto) + printf 'backend_valid=yes\n' + ;; + *) + printf 'backend_valid=no\n' + ;; + esac + + if have input-remapper-control; then + printf 'input_remapper=present\n' + else + printf 'input_remapper=missing\n' + fi + + if have xremap; then + printf 'xremap=present\n' + else + printf 'xremap=missing\n' + fi + + if have xkeysnail; then + printf 'xkeysnail=present\n' + else + printf 'xkeysnail=missing\n' + fi + + if [[ -f "$tmpl" ]]; then + printf 'xremap_template=present\n' + else + printf 'xremap_template=missing\n' + fi + printf 'xremap_template_path=%s\n' "$tmpl" + + if [[ -f "$pdir/gnome/input-install.sh" ]]; then + printf 'input_installer=present\n' + else + printf 'input_installer=missing\n' + fi +} + +main "$@" From f259eacdc98446f82939cda586128f92ef9d88af Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:30:44 -0400 Subject: [PATCH 2/6] ci(workstation): add keyboard policy smoke workflow --- .../workflows/workstation-keyboard-policy.yml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/workstation-keyboard-policy.yml diff --git a/.github/workflows/workstation-keyboard-policy.yml b/.github/workflows/workstation-keyboard-policy.yml new file mode 100644 index 0000000..59a5318 --- /dev/null +++ b/.github/workflows/workstation-keyboard-policy.yml @@ -0,0 +1,59 @@ +name: workstation-keyboard-policy + +on: + pull_request: + paths: + - 'profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh' + - 'profiles/linux-dev/workstation-v0/gnome/input-install.sh' + - 'profiles/linux-dev/workstation-v0/gnome/README.md' + - 'profiles/linux-dev/workstation-v0/manifest.yaml' + - '.github/workflows/workstation-keyboard-policy.yml' + push: + branches: + - main + paths: + - 'profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh' + - 'profiles/linux-dev/workstation-v0/gnome/input-install.sh' + - 'profiles/linux-dev/workstation-v0/gnome/README.md' + - 'profiles/linux-dev/workstation-v0/manifest.yaml' + - '.github/workflows/workstation-keyboard-policy.yml' + +jobs: + keyboard-policy-smoke: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Syntax check keyboard policy scripts + run: | + set -euo pipefail + bash -n profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh + bash -n profiles/linux-dev/workstation-v0/gnome/input-install.sh + + - name: Smoke: keyboard policy helper default backend + run: | + set -euo pipefail + helper='profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh' + tmp_home=$(mktemp -d) + out=$(HOME="$tmp_home" XDG_CONFIG_HOME="$tmp_home/.config" bash "$helper") + grep -F 'selected_backend=input-remapper' <<<"$out" >/dev/null + grep -F 'backend_valid=yes' <<<"$out" >/dev/null + grep -F 'input_installer=present' <<<"$out" >/dev/null + + - name: Smoke: keyboard policy helper rejects invalid backend + run: | + set -euo pipefail + helper='profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh' + tmp_home=$(mktemp -d) + out=$(HOME="$tmp_home" XDG_CONFIG_HOME="$tmp_home/.config" SOURCEOS_REMAP_BACKEND=invalid bash "$helper") + grep -F 'selected_backend=invalid' <<<"$out" >/dev/null + grep -F 'backend_valid=no' <<<"$out" >/dev/null + + - name: Smoke: input installer writes xremap template in compatibility mode + run: | + set -euo pipefail + installer='profiles/linux-dev/workstation-v0/gnome/input-install.sh' + tmp_home=$(mktemp -d) + HOME="$tmp_home" XDG_CONFIG_HOME="$tmp_home/.config" XDG_CURRENT_DESKTOP=GNOME SOURCEOS_REMAP_BACKEND=xremap bash "$installer" + test -s "$tmp_home/.config/sourceos/input/xremap-macos-compat.yml" From 481d9cf7d24d60de0f480efb32b6ebded951493f Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:31:34 -0400 Subject: [PATCH 3/6] docs(gnome): document keyboard remap policy lane --- profiles/linux-dev/workstation-v0/gnome/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/profiles/linux-dev/workstation-v0/gnome/README.md b/profiles/linux-dev/workstation-v0/gnome/README.md index 16650b8..422dbb0 100644 --- a/profiles/linux-dev/workstation-v0/gnome/README.md +++ b/profiles/linux-dev/workstation-v0/gnome/README.md @@ -17,6 +17,7 @@ Principles: ./appearance-apply.sh ./files-sidebar.sh ./mac-defaults.sh +./input-install.sh ``` ## Current baseline (v0) @@ -49,6 +50,15 @@ This lane adds bounded visual/workflow polish without replacing GNOME Shell or l - `files-sidebar.sh` seeds GTK/Nautilus bookmarks for Desktop, Documents, Downloads, Pictures, Screenshots, Music, Videos, and Public. - The workstation installer runs both helpers best-effort after the extension pinset and before input/gesture setup. +## Keyboard/remap policy v1 + +The workstation keeps keyboard remapping explicit and policy-gated: + +- `input-remapper` remains the default Fedora/GNOME backend because it is packaged and works at the evdev layer. +- `xremap` is the advanced compatibility lane; the installer writes a template at `$XDG_CONFIG_HOME/sourceos/input/xremap-macos-compat.yml`. +- Kinto remains an explicit compatibility lane for X11/xkeysnail-style workflows and is not auto-installed in the Wayland-first profile. +- `check-keyboard-policy.sh` emits key=value status for CI and future doctor/status integration. + ## Follow-on work - Wayland-safe keyboard remap lane expansion From 94bdcdca79d6bc08cf224670377010e226e2e689 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Fri, 24 Apr 2026 20:41:08 -0400 Subject: [PATCH 4/6] workstation-v0: enrich keyboard policy check output --- .../bin/check-keyboard-policy.sh | 65 +++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh b/profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh index 6145bd0..d9e95ad 100644 --- a/profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh +++ b/profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh @@ -18,41 +18,61 @@ template_path(){ printf '%s/sourceos/input/xremap-macos-compat.yml\n' "${XDG_CONFIG_HOME:-$HOME/.config}" } +present_or_missing(){ + if have "$1"; then + printf 'present\n' + else + printf 'missing\n' + fi +} + main(){ - local pdir b tmpl + local pdir b tmpl backend_valid input_remapper xremap_bin xkeysnail selected_available policy_ok pdir="$(profile_dir)" b="$(backend)" tmpl="$(template_path)" + backend_valid=no + selected_available=unknown + policy_ok=no printf 'profile_dir=%s\n' "$pdir" + printf 'default_backend=input-remapper\n' + printf 'primary_backend=input-remapper\n' + printf 'compatibility_backends=xremap,kinto\n' + printf 'wayland_first=yes\n' + printf 'kinto_auto_install=no\n' printf 'selected_backend=%s\n' "$b" case "$b" in input-remapper|xremap|kinto) - printf 'backend_valid=yes\n' - ;; - *) - printf 'backend_valid=no\n' + backend_valid=yes ;; esac + printf 'backend_valid=%s\n' "$backend_valid" - if have input-remapper-control; then - printf 'input_remapper=present\n' - else - printf 'input_remapper=missing\n' - fi + input_remapper="$(present_or_missing input-remapper-control)" + xremap_bin="$(present_or_missing xremap)" + xkeysnail="$(present_or_missing xkeysnail)" - if have xremap; then - printf 'xremap=present\n' - else - printf 'xremap=missing\n' - fi + printf 'input_remapper=%s\n' "$input_remapper" + printf 'xremap=%s\n' "$xremap_bin" + printf 'xkeysnail=%s\n' "$xkeysnail" - if have xkeysnail; then - printf 'xkeysnail=present\n' - else - printf 'xkeysnail=missing\n' - fi + case "$b" in + input-remapper) + selected_available="$input_remapper" + ;; + xremap) + selected_available="$xremap_bin" + ;; + kinto) + selected_available="$xkeysnail" + ;; + *) + selected_available=invalid + ;; + esac + printf 'selected_backend_available=%s\n' "$selected_available" if [[ -f "$tmpl" ]]; then printf 'xremap_template=present\n' @@ -66,6 +86,11 @@ main(){ else printf 'input_installer=missing\n' fi + + if [[ "$backend_valid" == "yes" ]]; then + policy_ok=yes + fi + printf 'policy_ok=%s\n' "$policy_ok" } main "$@" From ad107cfd4b828f94eaf20ccda02a7d8e08fc7f30 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Fri, 24 Apr 2026 22:08:41 -0400 Subject: [PATCH 5/6] ci(workstation): strengthen keyboard policy assertions --- .../workflows/workstation-keyboard-policy.yml | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/.github/workflows/workstation-keyboard-policy.yml b/.github/workflows/workstation-keyboard-policy.yml index 59a5318..e9caa9b 100644 --- a/.github/workflows/workstation-keyboard-policy.yml +++ b/.github/workflows/workstation-keyboard-policy.yml @@ -37,9 +37,32 @@ jobs: helper='profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh' tmp_home=$(mktemp -d) out=$(HOME="$tmp_home" XDG_CONFIG_HOME="$tmp_home/.config" bash "$helper") + grep -F 'default_backend=input-remapper' <<<"$out" >/dev/null + grep -F 'primary_backend=input-remapper' <<<"$out" >/dev/null + grep -F 'compatibility_backends=xremap,kinto' <<<"$out" >/dev/null + grep -F 'wayland_first=yes' <<<"$out" >/dev/null + grep -F 'kinto_auto_install=no' <<<"$out" >/dev/null grep -F 'selected_backend=input-remapper' <<<"$out" >/dev/null grep -F 'backend_valid=yes' <<<"$out" >/dev/null + grep -F 'selected_backend_available=missing' <<<"$out" >/dev/null grep -F 'input_installer=present' <<<"$out" >/dev/null + grep -F 'policy_ok=yes' <<<"$out" >/dev/null + + - name: Smoke: keyboard policy helper detects selected backend availability + run: | + set -euo pipefail + helper='profiles/linux-dev/workstation-v0/bin/check-keyboard-policy.sh' + tmp_home=$(mktemp -d) + stub_bin=$(mktemp -d) + cat > "$stub_bin/input-remapper-control" <<'EOF' + #!/usr/bin/env bash + exit 0 + EOF + chmod +x "$stub_bin/input-remapper-control" + out=$(HOME="$tmp_home" XDG_CONFIG_HOME="$tmp_home/.config" PATH="$stub_bin:$PATH" bash "$helper") + grep -F 'input_remapper=present' <<<"$out" >/dev/null + grep -F 'selected_backend_available=present' <<<"$out" >/dev/null + grep -F 'policy_ok=yes' <<<"$out" >/dev/null - name: Smoke: keyboard policy helper rejects invalid backend run: | @@ -49,6 +72,8 @@ jobs: out=$(HOME="$tmp_home" XDG_CONFIG_HOME="$tmp_home/.config" SOURCEOS_REMAP_BACKEND=invalid bash "$helper") grep -F 'selected_backend=invalid' <<<"$out" >/dev/null grep -F 'backend_valid=no' <<<"$out" >/dev/null + grep -F 'selected_backend_available=invalid' <<<"$out" >/dev/null + grep -F 'policy_ok=no' <<<"$out" >/dev/null - name: Smoke: input installer writes xremap template in compatibility mode run: | @@ -56,4 +81,9 @@ jobs: installer='profiles/linux-dev/workstation-v0/gnome/input-install.sh' tmp_home=$(mktemp -d) HOME="$tmp_home" XDG_CONFIG_HOME="$tmp_home/.config" XDG_CURRENT_DESKTOP=GNOME SOURCEOS_REMAP_BACKEND=xremap bash "$installer" - test -s "$tmp_home/.config/sourceos/input/xremap-macos-compat.yml" + tmpl="$tmp_home/.config/sourceos/input/xremap-macos-compat.yml" + test -s "$tmpl" + grep -F 'CapsLock: Esc' "$tmpl" >/dev/null + grep -F 'Control_L: Super_L' "$tmpl" >/dev/null + grep -F 'Super_L: Alt_L' "$tmpl" >/dev/null + grep -F 'Alt_L: Control_L' "$tmpl" >/dev/null From 590d9ce1e2faea1ddbe567c87afa759a5905b5bc Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Fri, 24 Apr 2026 22:12:26 -0400 Subject: [PATCH 6/6] ci(workstation): assert full xremap modifier template --- .github/workflows/workstation-keyboard-policy.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/workstation-keyboard-policy.yml b/.github/workflows/workstation-keyboard-policy.yml index e9caa9b..7fab53c 100644 --- a/.github/workflows/workstation-keyboard-policy.yml +++ b/.github/workflows/workstation-keyboard-policy.yml @@ -87,3 +87,6 @@ jobs: grep -F 'Control_L: Super_L' "$tmpl" >/dev/null grep -F 'Super_L: Alt_L' "$tmpl" >/dev/null grep -F 'Alt_L: Control_L' "$tmpl" >/dev/null + grep -F 'Control_R: Super_R' "$tmpl" >/dev/null + grep -F 'Super_R: Alt_R' "$tmpl" >/dev/null + grep -F 'Alt_R: Control_R' "$tmpl" >/dev/null