Split workstation profiles for Linux and Windows WSL

This commit is contained in:
Fabio Scotto di Santolo
2026-04-01 13:54:07 +02:00
parent d7659b9c21
commit 9065261bff
18 changed files with 606 additions and 241 deletions

View File

@@ -91,6 +91,17 @@
+ (ubuntu_packages_base | default([]))
+ (ubuntu_docker_packages | default([]))
+ (profile_packages | default([]))
+ (workstation_dev_packages | default([]))
+ (
(workstation_host_linux_packages | default([]))
if 'workstation_host_linux' in group_names
else []
)
+ (
(workstation_dev_wsl_packages | default([]))
if 'workstation_dev_wsl' in group_names
else []
)
+ (desktop_common_packages | default([]))
+ (
(desktop_i3_packages | default([]))
@@ -103,7 +114,13 @@
else []
)
+ (host_packages | default([]))
) | unique
)
| difference(
(workstation_dev_wsl_excluded_packages | default([]))
if 'workstation_dev_wsl' in group_names
else []
)
| unique
}}
state: present

View File

@@ -0,0 +1,124 @@
---
- name: Ensure workstation user directories exist
tags: [dotfiles, dotfiles:workstation]
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop: "{{ workstation_user_directories | default([]) }}"
loop_control:
label: "{{ item.path }}"
- name: Copy workstation dotfiles
tags: [dotfiles, dotfiles:workstation]
ansible.builtin.copy:
src: "{{ playbook_dir }}/../dotfiles/workstation/{{ item.src }}"
dest: "{{ user_home }}/{{ item.dest }}"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop: "{{ workstation_dotfiles | default([]) }}"
loop_control:
label: "{{ item.dest }}"
- name: Render workstation templates
tags: [dotfiles, dotfiles:workstation]
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ user_home }}/{{ item.dest }}"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop: "{{ workstation_templates | default([]) }}"
loop_control:
label: "{{ item.dest }}"
- name: Set workstation external tool release metadata
tags: [packages]
ansible.builtin.set_fact:
workstation_tools_tmp_dir: /tmp/workstation-tools
opencode_asset_name: >-
{{
'opencode-linux-x64-baseline.tar.gz' if ansible_facts['architecture'] == 'x86_64'
else 'opencode-linux-arm64.tar.gz' if ansible_facts['architecture'] in ['aarch64', 'arm64']
else ''
}}
when: workstation_manage_opencode | default(false)
- name: Ensure architecture is supported for OpenCode binary
tags: [packages]
ansible.builtin.fail:
msg: "Unsupported architecture {{ ansible_facts['architecture'] }} for OpenCode release binary"
when:
- workstation_manage_opencode | default(false)
- opencode_asset_name == ''
- name: Ensure temporary directory exists for workstation external tools
tags: [packages]
ansible.builtin.file:
path: "{{ workstation_tools_tmp_dir }}"
state: directory
mode: "0755"
when: workstation_manage_opencode | default(false)
- name: Fetch latest OpenCode release metadata
tags: [packages]
ansible.builtin.uri:
url: https://api.github.com/repos/anomalyco/opencode/releases/latest
headers:
Accept: application/vnd.github+json
return_content: true
register: opencode_latest_release
changed_when: false
when: workstation_manage_opencode | default(false)
- name: Set OpenCode release asset metadata
tags: [packages]
ansible.builtin.set_fact:
opencode_version: "{{ opencode_latest_release.json.tag_name }}"
opencode_asset: >-
{{
opencode_latest_release.json.assets
| selectattr('name', 'equalto', opencode_asset_name)
| first
| default({})
}}
when: workstation_manage_opencode | default(false)
- name: Ensure latest OpenCode asset metadata is available
tags: [packages]
ansible.builtin.fail:
msg: "Could not find OpenCode asset {{ opencode_asset_name }} in release {{ opencode_version }}"
when:
- workstation_manage_opencode | default(false)
- opencode_asset == {}
- name: Download OpenCode release archive
tags: [packages]
ansible.builtin.get_url:
url: "{{ opencode_asset.browser_download_url }}"
dest: "{{ workstation_tools_tmp_dir }}/{{ opencode_asset.name }}"
checksum: "{{ opencode_asset.digest | default(omit) }}"
mode: "0644"
when: workstation_manage_opencode | default(false)
- name: Extract OpenCode release archive
tags: [packages]
ansible.builtin.unarchive:
src: "{{ workstation_tools_tmp_dir }}/{{ opencode_asset.name }}"
dest: "{{ workstation_tools_tmp_dir }}"
remote_src: true
when: workstation_manage_opencode | default(false)
- name: Install OpenCode binary
tags: [packages]
ansible.builtin.copy:
src: "{{ workstation_tools_tmp_dir }}/opencode"
dest: /usr/local/bin/opencode
remote_src: true
owner: root
group: root
mode: "0755"
when: workstation_manage_opencode | default(false)

View File

@@ -0,0 +1,30 @@
---
- name: Ensure WSL boot configuration file exists
tags: [packages, services]
ansible.builtin.file:
path: /etc/wsl.conf
state: touch
owner: root
group: root
mode: "0644"
when: workstation_wsl_systemd_enabled | default(false)
- name: Enable systemd in WSL
tags: [packages, services]
community.general.ini_file:
path: /etc/wsl.conf
section: boot
option: systemd
value: 'true'
mode: "0644"
register: workstation_wsl_systemd_config
when: workstation_wsl_systemd_enabled | default(false)
- name: Note when WSL must be restarted
tags: [packages, services]
ansible.builtin.debug:
msg: "Restart the WSL distro with 'wsl --shutdown' from Windows to apply /etc/wsl.conf changes."
changed_when: false
when:
- workstation_wsl_systemd_enabled | default(false)
- workstation_wsl_systemd_config is changed

View File

@@ -1,117 +1,4 @@
---
- name: Ensure workstation user directories exist
tags: [dotfiles, dotfiles:workstation]
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop: "{{ workstation_user_directories | default([]) }}"
loop_control:
label: "{{ item.path }}"
- name: Copy workstation dotfiles
tags: [dotfiles, dotfiles:workstation]
ansible.builtin.copy:
src: "{{ playbook_dir }}/../dotfiles/workstation/{{ item.src }}"
dest: "{{ user_home }}/{{ item.dest }}"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop: "{{ workstation_dotfiles | default([]) }}"
loop_control:
label: "{{ item.dest }}"
- name: Render workstation templates
tags: [dotfiles, dotfiles:workstation]
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ user_home }}/{{ item.dest }}"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop: "{{ workstation_templates | default([]) }}"
loop_control:
label: "{{ item.dest }}"
- name: Set workstation external tool release metadata
tags: [packages]
ansible.builtin.set_fact:
workstation_tools_tmp_dir: /tmp/workstation-tools
opencode_asset_name: >-
{{
'opencode-linux-x64-baseline.tar.gz' if ansible_facts['architecture'] == 'x86_64'
else 'opencode-linux-arm64.tar.gz' if ansible_facts['architecture'] in ['aarch64', 'arm64']
else ''
}}
- name: Ensure architecture is supported for OpenCode binary
tags: [packages]
ansible.builtin.fail:
msg: "Unsupported architecture {{ ansible_facts['architecture'] }} for OpenCode release binary"
when: opencode_asset_name == ''
- name: Ensure temporary directory exists for workstation external tools
tags: [packages]
ansible.builtin.file:
path: "{{ workstation_tools_tmp_dir }}"
state: directory
mode: "0755"
- name: Fetch latest OpenCode release metadata
tags: [packages]
ansible.builtin.uri:
url: https://api.github.com/repos/anomalyco/opencode/releases/latest
headers:
Accept: application/vnd.github+json
return_content: true
register: opencode_latest_release
changed_when: false
- name: Set OpenCode release asset metadata
tags: [packages]
ansible.builtin.set_fact:
opencode_version: "{{ opencode_latest_release.json.tag_name }}"
opencode_asset: >-
{{
opencode_latest_release.json.assets
| selectattr('name', 'equalto', opencode_asset_name)
| first
| default({})
}}
- name: Ensure latest OpenCode asset metadata is available
tags: [packages]
ansible.builtin.fail:
msg: "Could not find OpenCode asset {{ opencode_asset_name }} in release {{ opencode_version }}"
when: opencode_asset == {}
- name: Download OpenCode release archive
tags: [packages]
ansible.builtin.get_url:
url: "{{ opencode_asset.browser_download_url }}"
dest: "{{ workstation_tools_tmp_dir }}/{{ opencode_asset.name }}"
checksum: "{{ opencode_asset.digest | default(omit) }}"
mode: "0644"
- name: Extract OpenCode release archive
tags: [packages]
ansible.builtin.unarchive:
src: "{{ workstation_tools_tmp_dir }}/{{ opencode_asset.name }}"
dest: "{{ workstation_tools_tmp_dir }}"
remote_src: true
- name: Install OpenCode binary
tags: [packages]
ansible.builtin.copy:
src: "{{ workstation_tools_tmp_dir }}/opencode"
dest: /usr/local/bin/opencode
remote_src: true
owner: root
group: root
mode: "0755"
- name: Ensure GNOME extension directories exist
tags: [packages, gnome]
ansible.builtin.file:

View File

@@ -0,0 +1,108 @@
---
- name: Enable required Windows features for WSL
tags: [packages, services, wsl]
ansible.windows.win_optional_feature:
name:
- Microsoft-Windows-Subsystem-Linux
- VirtualMachinePlatform
state: present
include_parent: true
- name: Ensure winget is available on Windows host
tags: [packages]
ansible.windows.win_powershell:
script: |
$winget = Get-Command winget.exe -ErrorAction SilentlyContinue
if ($null -eq $winget) {
throw 'winget.exe is not available on the Windows host. Install App Installer or rerun the bootstrap script.'
}
$Ansible.Changed = $false
- name: Ensure WSL 2 is the default backend
tags: [packages, services, wsl]
ansible.windows.win_powershell:
script: |
$status = & wsl --status 2>$null
if ($LASTEXITCODE -eq 0 -and $status -match 'Default Version:\s*2') {
$Ansible.Changed = $false
return
}
& wsl --set-default-version 2
if ($LASTEXITCODE -ne 0) {
throw 'Failed to set WSL default version to 2.'
}
$Ansible.Changed = $true
- name: Check whether target WSL distribution exists
tags: [packages, services, wsl]
ansible.windows.win_powershell:
script: |
$distributions = @(wsl --list --quiet) | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
$ubuntuDistribution = $distributions | Where-Object { $_ -like '{{ windows_wsl_distribution_name }}*' } | Select-Object -First 1
if ($null -ne $ubuntuDistribution) {
$Ansible.Result = @{ exists = $true; distribution = $ubuntuDistribution }
$Ansible.Changed = $false
return
}
$Ansible.Result = @{ exists = $false; distribution = $null }
$Ansible.Changed = $false
register: windows_wsl_distribution_state
- name: Fail when target WSL distribution is missing
tags: [packages, services, wsl]
ansible.builtin.fail:
msg: >-
No WSL Ubuntu distribution matching {{ windows_wsl_distribution_name }}* is installed on the Windows host.
Run scripts/bootstrap_windows_workstation.ps1 first and launch the distro once before applying the Windows play.
when: not (windows_wsl_distribution_state.result.exists | default(false))
- name: Install Windows workstation applications with winget
tags: [packages]
ansible.windows.win_powershell:
script: |
$packageId = '{{ item.id }}'
$packageName = '{{ item.name | default(item.id) }}'
$installed = & winget list --id $packageId --exact --accept-source-agreements --disable-interactivity 2>$null
if ($LASTEXITCODE -eq 0 -and $installed -match [regex]::Escape($packageId)) {
$Ansible.Changed = $false
return
}
& winget install --id $packageId --exact --silent --accept-package-agreements --accept-source-agreements --disable-interactivity
if ($LASTEXITCODE -ne 0) {
throw "Failed to install $packageName with winget"
}
$Ansible.Changed = $true
loop: "{{ windows_winget_packages | default([]) }}"
loop_control:
label: "{{ item.id }}"
- name: Install VS Code WSL extensions on Windows host
tags: [packages, vscode]
ansible.windows.win_powershell:
script: |
$extensionId = '{{ item }}'
$code = Get-Command code.cmd -ErrorAction SilentlyContinue
if ($null -eq $code) {
throw 'code.cmd is not available. Ensure Visual Studio Code is installed before managing extensions.'
}
$installedExtensions = & $code.Source --list-extensions
if ($installedExtensions -contains $extensionId) {
$Ansible.Changed = $false
return
}
& $code.Source --install-extension $extensionId --force
if ($LASTEXITCODE -ne 0) {
throw "Failed to install VS Code extension $extensionId"
}
$Ansible.Changed = $true
loop: "{{ windows_vscode_extensions | default([]) }}"
loop_control:
label: "{{ item }}"