Merge ~cnihelton/ubuntu/+source/wsl-setup:lp-2122047-new-upstream-version into ubuntu/+source/wsl-setup:ubuntu/devel
- Git
- lp:~cnihelton/ubuntu/+source/wsl-setup
- lp-2122047-new-upstream-version
- Merge into ubuntu/devel
| Status: | Merged |
|---|---|
| Merged at revision: | ecf8d011e9cc2e09421859238d286dfd329650a2 |
| Proposed branch: | ~cnihelton/ubuntu/+source/wsl-setup:lp-2122047-new-upstream-version |
| Merge into: | ubuntu/+source/wsl-setup:ubuntu/devel |
| Diff against target: |
1517 lines (+1226/-100) 20 files modified
.github/actions/get-wsl-image/action.yaml (+118/-0) .github/actions/validate-wsl/action.yaml (+113/-0) .github/dependabot.yaml (+18/-0) .github/workflows/integration.yaml (+328/-0) .github/workflows/shellcheck.yaml (+27/-0) .shellcheckrc (+1/-0) CONTRIBUTING.md (+102/-0) SECURITY.md (+53/-0) debian/changelog (+12/-0) debian/control (+1/-0) debian/install (+2/-1) debian/rules (+0/-1) dev/null (+0/-7) test/basic-assertions.sh (+30/-0) test/e2e/setup.expect (+113/-0) test/systemd-assertions.sh (+33/-0) test/ubuntu-insights-assertions.sh (+33/-0) ubuntu-insights.sh (+143/-0) wait-for-cloud-init (+2/-2) wsl-setup (+97/-89) |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Nick Rosbrook (community) | Approve | ||
| git-ubuntu import | Pending | ||
|
Review via email:
|
|||
Commit message
wsl-setup (0.6.1) resolute; urgency=medium
[ Kat Kuo]
* Added ubuntu-insights integration
* Added integration tests in upstream CI
[Carlos Nihelton]
* Case-fold the Windows username before sanitization (LP: #2122047)
* Remove the systemd-
Description of the change
This imports the new upstream version of the wsl-setup package to Resolute Racoon, fixing the bug LP #2122047 and improving the package quality by fixing some lintian warnings.
A considerable size in the diff is due new CI workflows being added for enhanced automated testing of this package in the upstream repository CI (GitHub). Those tests are run in upstream CI at pull requests, but not during package building time because most of them needs to run on Windows.
Given the fact this is a native package, uploading this branch would be equivalent in terms of contents to just checking out the repository at the tag 0.6.0, creating the source package and uploading it. The commit IDs wouldn't match though because this branch is created by turning the diff between ubuntu/devel and upstream/tags/0.6.1 into patches applied with `git am`, thus shown as new and linear commits.
Preview Diff
| 1 | diff --git a/.github/actions/get-wsl-image/action.yaml b/.github/actions/get-wsl-image/action.yaml |
| 2 | new file mode 100644 |
| 3 | index 0000000..b93fd29 |
| 4 | --- /dev/null |
| 5 | +++ b/.github/actions/get-wsl-image/action.yaml |
| 6 | @@ -0,0 +1,118 @@ |
| 7 | +# This action gets the latest WSL image, leveraging both the persistent behavior of our runners, and the GitHub cache |
| 8 | +name: Get WSL Image |
| 9 | +description: Get the latest WSL image, leveraging both the persistent behavior of our runners, and the GitHub cache |
| 10 | + |
| 11 | +inputs: |
| 12 | + base_url: |
| 13 | + description: "The base URL to download the WSL image from" |
| 14 | + required: false |
| 15 | + default: "https://cdimages.ubuntu.com/ubuntu-wsl/daily-live/current/" |
| 16 | + arch: |
| 17 | + description: "The architecture of the WSL image to download (amd64 or arm64)" |
| 18 | + required: false |
| 19 | + default: "amd64" |
| 20 | + output_path: |
| 21 | + description: "The path to save the downloaded WSL image to" |
| 22 | + required: false |
| 23 | + default: "daily-image.wsl" |
| 24 | +outputs: |
| 25 | + cache-key: |
| 26 | + description: "The cache key for the WSL image" |
| 27 | + value: ${{ steps.compute-cache-key.outputs.cache-key }} |
| 28 | + image-name: |
| 29 | + description: "The name of the image we found" |
| 30 | + value: ${{ steps.compute-cache-key.outputs.image-name }} |
| 31 | + |
| 32 | +runs: |
| 33 | + using: "composite" |
| 34 | + steps: |
| 35 | + - name: Compute image cache key |
| 36 | + id: compute-cache-key |
| 37 | + shell: bash |
| 38 | + run: | |
| 39 | + set -euo pipefail |
| 40 | + |
| 41 | + shafile="${{ runner.temp }}/SHA256SUMS" |
| 42 | + uri="${{ inputs.base_url }}/SHA256SUMS" |
| 43 | + if ! curl --fail -o "$shafile" "${uri}"; then |
| 44 | + echo "::error:: Failed to download the checksum file from $uri." |
| 45 | + exit 1 |
| 46 | + fi |
| 47 | + |
| 48 | + # Look for a line matching the pattern: <sha> *<name>-wsl-<arch>.wsl |
| 49 | + arch="${{ inputs.arch }}" |
| 50 | + line=$(grep -E "^[a-f0-9]+[[:space:]]+\*.*-wsl-${arch}\.wsl$" "$shafile") |
| 51 | + if [ -z "$line" ]; then |
| 52 | + echo "::error:: No WSL image found for architecture '$arch' in the $shafile file." |
| 53 | + exit 2 |
| 54 | + fi |
| 55 | + |
| 56 | + # Extract the SHA (first field) and image name (second field without the *) |
| 57 | + sha=$(echo "$line" | awk '{print $1}') |
| 58 | + image_name=$(echo "$line" | awk '{print $2}' | sed 's/^\*//') |
| 59 | + |
| 60 | + echo "cache-key=$sha" >> "$GITHUB_OUTPUT" |
| 61 | + echo "image-name=$image_name" >> "$GITHUB_OUTPUT" |
| 62 | + |
| 63 | + - name: Validate image |
| 64 | + id: check-locally |
| 65 | + shell: bash |
| 66 | + run: | |
| 67 | + set -euo pipefail |
| 68 | + |
| 69 | + expected_sha="${{ steps.compute-cache-key.outputs.cache-key }}" |
| 70 | + output_path="${{ inputs.output_path }}" |
| 71 | + |
| 72 | + if [ -r "$output_path" ]; then |
| 73 | + echo "Found local image at $output_path" |
| 74 | + |
| 75 | + # Compare the SHAs |
| 76 | + if (echo "$expected_sha $output_path" | sha256sum -c); then |
| 77 | + echo "✅ Local image matches expected SHA. Using local file." |
| 78 | + echo "is-valid=true" >> "$GITHUB_OUTPUT" |
| 79 | + else |
| 80 | + echo "❌ Local image SHA does not match expected SHA. Will need to download." |
| 81 | + rm "$output_path" |
| 82 | + echo "is-valid=false" >> "$GITHUB_OUTPUT" |
| 83 | + fi |
| 84 | + else |
| 85 | + echo "No local image found at $output_path" |
| 86 | + echo "is-valid=false" >> "$GITHUB_OUTPUT" |
| 87 | + fi |
| 88 | + |
| 89 | + - name: Restore image cache |
| 90 | + id: restore-cache |
| 91 | + if: ${{ steps.check-locally.outputs.is-valid != 'true' }} |
| 92 | + uses: actions/cache/restore@v4 |
| 93 | + with: |
| 94 | + path: ${{ inputs.output_path }} |
| 95 | + key: ${{ steps.compute-cache-key.outputs.cache-key }} |
| 96 | + enableCrossOsArchive: true |
| 97 | + |
| 98 | + - name: Download image |
| 99 | + if: ${{ steps.check-locally.outputs.is-valid != 'true' && steps.restore-cache.outputs.cache-hit != 'true' }} |
| 100 | + shell: bash |
| 101 | + run: | |
| 102 | + set -euo pipefail |
| 103 | + |
| 104 | + expected_sha="${{ steps.compute-cache-key.outputs.cache-key }}" |
| 105 | + output_path="${{ inputs.output_path }}" |
| 106 | + |
| 107 | + uri="${{ inputs.base_url }}/${{ steps.compute-cache-key.outputs.image-name }}" |
| 108 | + if ! curl --fail -o "$output_path" "$uri"; then |
| 109 | + echo "::error:: Failed to download the WSL image from $uri" |
| 110 | + exit 1 |
| 111 | + fi |
| 112 | + |
| 113 | + if ! (echo "$expected_sha $output_path" | sha256sum -c); then |
| 114 | + echo "::error:: Downloaded WSL image SHA does not match expected SHA." |
| 115 | + exit 2 |
| 116 | + fi |
| 117 | + |
| 118 | + - name: Save image to cache |
| 119 | + if: ${{ steps.restore-cache.outputs.cache-hit != 'true' }} |
| 120 | + uses: actions/cache/save@v4 |
| 121 | + with: |
| 122 | + path: ${{ inputs.output_path }} |
| 123 | + key: ${{ steps.restore-cache.outputs.cache-primary-key }} |
| 124 | + enableCrossOsArchive: true |
| 125 | diff --git a/.github/actions/validate-wsl/action.yaml b/.github/actions/validate-wsl/action.yaml |
| 126 | new file mode 100644 |
| 127 | index 0000000..ac24cb8 |
| 128 | --- /dev/null |
| 129 | +++ b/.github/actions/validate-wsl/action.yaml |
| 130 | @@ -0,0 +1,113 @@ |
| 131 | +# This action validates a newly set up WSL instance by running a set of basic assertions. Interop must be enabled. |
| 132 | +name: Validate WSL Setup |
| 133 | +description: Validate a newly set up WSL instance by running a set of basic assertions. |
| 134 | + |
| 135 | +inputs: |
| 136 | + instance: |
| 137 | + description: "The name of the WSL instance to validate" |
| 138 | + required: true |
| 139 | + working-dir: |
| 140 | + description: "The working directory to checkout scripts to inside the WSL instance" |
| 141 | + required: true |
| 142 | + expected-user: |
| 143 | + description: "The expected default user in the WSL instance" |
| 144 | + required: true |
| 145 | + expected-internal-consent-state: |
| 146 | + description: "The expected default Ubuntu Insights internal consent state (true, false)" |
| 147 | + required: true |
| 148 | + expected-registry-consent-state: |
| 149 | + description: "The expected Ubuntu Insights registry consent state. If skip, asserts that the registry key doesn't exist." |
| 150 | + required: false |
| 151 | + default: "skip" |
| 152 | + skip-systemd-assertions: |
| 153 | + description: "Whether to skip systemd specific tests" |
| 154 | + required: false |
| 155 | + default: "false" |
| 156 | + skip-font-assertions: |
| 157 | + description: "Whether to skip the font installation test" |
| 158 | + required: false |
| 159 | + default: "false" |
| 160 | + |
| 161 | +runs: |
| 162 | + using: "composite" |
| 163 | + steps: |
| 164 | + - name: Terminate WSL instance |
| 165 | + shell: powershell |
| 166 | + run: | |
| 167 | + wsl --terminate ${{ inputs.instance }} |
| 168 | + |
| 169 | + - name: Checkout scripts into WSL |
| 170 | + uses: ubuntu/WSL/.github/actions/wsl-checkout@main |
| 171 | + with: |
| 172 | + distro: ${{ inputs.instance }} |
| 173 | + working-dir: ${{ inputs.working-dir }} |
| 174 | + |
| 175 | + - name: Run basic assertions |
| 176 | + uses: ubuntu/WSL/.github/actions/wsl-bash@main |
| 177 | + with: |
| 178 | + distro: ${{ inputs.instance }} |
| 179 | + working-dir: ${{ inputs.working-dir }} |
| 180 | + exec: | |
| 181 | + source test/basic-assertions.sh "${{ inputs.expected-user }}" |
| 182 | + |
| 183 | + - name: Run font installation assertions |
| 184 | + if: ${{ inputs.skip-font-assertions == 'false' }} |
| 185 | + shell: powershell |
| 186 | + run: | |
| 187 | + if (!(Test-Path -Path "${env:LocalAppData}\Microsoft\Windows\Fonts\Ubuntu*.ttf")) { |
| 188 | + Write-Error "Ubuntu font was not installed as expected" |
| 189 | + Exit 1 |
| 190 | + } |
| 191 | + if (!(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts\" -Name "Ubuntu*" )) { |
| 192 | + Write-Error "Ubuntu font was not registered" |
| 193 | + Exit 2 |
| 194 | + } |
| 195 | + |
| 196 | + - name: Run systemd assertions |
| 197 | + if: ${{ inputs.skip-systemd-assertions == 'false' }} |
| 198 | + uses: ubuntu/WSL/.github/actions/wsl-bash@main |
| 199 | + with: |
| 200 | + distro: ${{ inputs.instance }} |
| 201 | + working-dir: ${{ inputs.working-dir }} |
| 202 | + exec: | |
| 203 | + source test/systemd-assertions.sh |
| 204 | + |
| 205 | + - name: Run internal insights consent assertions |
| 206 | + uses: ubuntu/WSL/.github/actions/wsl-bash@main |
| 207 | + with: |
| 208 | + distro: ${{ inputs.instance }} |
| 209 | + working-dir: ${{ inputs.working-dir }} |
| 210 | + exec: | |
| 211 | + source test/ubuntu-insights-assertions.sh "${{ inputs.expected-internal-consent-state }}" |
| 212 | + |
| 213 | + - name: Run registry insights consent assertions |
| 214 | + shell: powershell |
| 215 | + run: | |
| 216 | + $registry_key_path = "HKCU:\Software\Canonical\Ubuntu\" |
| 217 | + $registry_value_name = "UbuntuInsightsConsent" |
| 218 | + |
| 219 | + if ("${{ inputs.expected-registry-consent-state }}" -eq "skip") { |
| 220 | + # Expect the registry key to not exist |
| 221 | + try { |
| 222 | + $reg = Get-ItemProperty -Path $registry_key_path -Name $registry_value_name -ErrorAction Stop |
| 223 | + Write-Error "Ubuntu Insights registry key was found but was not expected" |
| 224 | + Exit 1 |
| 225 | + } catch { |
| 226 | + # Key does not exist as expected |
| 227 | + Exit 0 |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + try { |
| 232 | + $reg = Get-ItemProperty -Path $registry_key_path -Name $registry_value_name -ErrorAction Stop |
| 233 | + } catch { |
| 234 | + Write-Error "Ubuntu Insights registry key not found" |
| 235 | + Exit 1 |
| 236 | + } |
| 237 | + |
| 238 | + $actual = $reg.UbuntuInsightsConsent |
| 239 | + $expected = "${{ inputs.expected-registry-consent-state }}" |
| 240 | + if (-not ("$actual".Trim() -ieq "$expected".Trim())) { |
| 241 | + Write-Error "Ubuntu Insights registry value mismatch. Expected '$expected' but found '$actual'" |
| 242 | + Exit 2 |
| 243 | + } |
| 244 | diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml |
| 245 | new file mode 100644 |
| 246 | index 0000000..1ee0310 |
| 247 | --- /dev/null |
| 248 | +++ b/.github/dependabot.yaml |
| 249 | @@ -0,0 +1,18 @@ |
| 250 | +version: 2 |
| 251 | +updates: |
| 252 | + # Infrastructure |
| 253 | + ## GitHub Actions |
| 254 | + - package-ecosystem: "github-actions" |
| 255 | + # Workflow files stored in the |
| 256 | + # default location of `.github/workflows` |
| 257 | + directory: "/" |
| 258 | + schedule: |
| 259 | + interval: "weekly" |
| 260 | + day: "thursday" |
| 261 | + time: "09:00" |
| 262 | + groups: |
| 263 | + gh-actions: |
| 264 | + #applies-to: version-updates |
| 265 | + patterns: ["*"] |
| 266 | + commit-message: |
| 267 | + prefix: "deps(ci)" |
| 268 | diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml |
| 269 | new file mode 100644 |
| 270 | index 0000000..9bfd6c3 |
| 271 | --- /dev/null |
| 272 | +++ b/.github/workflows/integration.yaml |
| 273 | @@ -0,0 +1,328 @@ |
| 274 | +name: Integration tests |
| 275 | +on: |
| 276 | + pull_request: |
| 277 | + workflow_dispatch: |
| 278 | + schedule: |
| 279 | + - cron: "4 3 * * 2" # Tuesdays 03:04 am. |
| 280 | + push: |
| 281 | + branches: ["main"] |
| 282 | +env: |
| 283 | + instance: Ubuntu-${{ github.run_id }} |
| 284 | + profile_script: /etc/profile.d/99-quit-wsl.sh |
| 285 | + instance_working_dir: ~/wsl-setup-${{ github.run_id }} |
| 286 | + insights_registry_key_path: "HKCU:\\Software\\Canonical\\Ubuntu" |
| 287 | + insights_registry_value_name: "UbuntuInsightsConsent" |
| 288 | + |
| 289 | +jobs: |
| 290 | + build-deb: |
| 291 | + name: Build wsl-setup debian package |
| 292 | + runs-on: ubuntu-latest |
| 293 | + steps: |
| 294 | + - name: Check out repository |
| 295 | + uses: actions/checkout@v6 |
| 296 | + - name: Build deb |
| 297 | + uses: canonical/desktop-engineering/gh-actions/common/build-debian@main |
| 298 | + with: |
| 299 | + token: ${{ secrets.GITHUB_TOKEN }} |
| 300 | + docker-image: ubuntu:devel |
| 301 | + |
| 302 | + cache-img: |
| 303 | + name: Cache the daily-live image |
| 304 | + runs-on: ubuntu-latest |
| 305 | + steps: |
| 306 | + - name: Check out repository |
| 307 | + uses: actions/checkout@v6 |
| 308 | + - name: Get WSL Image |
| 309 | + uses: ./.github/actions/get-wsl-image |
| 310 | + with: |
| 311 | + output_path: images.wsl |
| 312 | + |
| 313 | + cloud-init-test: |
| 314 | + needs: [build-deb, cache-img] |
| 315 | + name: "Cloud-init tests" |
| 316 | + runs-on: windows-2025 # WSL and winget are preinstalled and working |
| 317 | + strategy: |
| 318 | + fail-fast: false |
| 319 | + matrix: |
| 320 | + ubuntu-insights-flag: ["0", "1", "12", "skip"] # False, True, Invalid, Not set |
| 321 | + ubuntu-insights-state: ["false", "true", "null", "skip"] |
| 322 | + exclude: |
| 323 | + # Skip combinations that would result in an interactive prompt |
| 324 | + - ubuntu-insights-flag: "skip" |
| 325 | + ubuntu-insights-state: "null" |
| 326 | + - ubuntu-insights-flag: "12" |
| 327 | + ubuntu-insights-state: "null" |
| 328 | + - ubuntu-insights-flag: "skip" |
| 329 | + ubuntu-insights-state: "skip" |
| 330 | + - ubuntu-insights-flag: "12" |
| 331 | + ubuntu-insights-state: "skip" |
| 332 | + |
| 333 | + steps: |
| 334 | + - name: Check out repository |
| 335 | + uses: actions/checkout@v6 |
| 336 | + |
| 337 | + - name: Download artifacts |
| 338 | + uses: actions/download-artifact@v7 |
| 339 | + with: |
| 340 | + path: ci-artifacts |
| 341 | + # name: is left blank so that all artifacts are downloaded, thus we don't couple on |
| 342 | + # whatever name was chosen in the build-debian action. |
| 343 | + |
| 344 | + - name: Get WSL image |
| 345 | + uses: ./.github/actions/get-wsl-image |
| 346 | + with: |
| 347 | + output_path: images.wsl |
| 348 | + |
| 349 | + - name: Prepare Ubuntu Insights consent registry key |
| 350 | + if: matrix.ubuntu-insights-flag != 'skip' |
| 351 | + shell: powershell |
| 352 | + run: | |
| 353 | + New-Item -Path "${{ env.insights_registry_key_path }}" -Force |
| 354 | + New-ItemProperty -Path "${{ env.insights_registry_key_path }}" -Name "${{ env.insights_registry_value_name }}" -Value "${{ matrix.ubuntu-insights-flag }}" -PropertyType DWord -Force |
| 355 | + |
| 356 | + - name: Prepare WSL instance and cloud-init |
| 357 | + shell: powershell |
| 358 | + env: |
| 359 | + WSL_UTF8: "1" # Recommended otherwise it's hard to read wsl output on Github |
| 360 | + # Just to skip the interactive session |
| 361 | + cloudinit: | |
| 362 | + #cloud-config |
| 363 | + users: |
| 364 | + - name: u |
| 365 | + gecos: Ubuntu User |
| 366 | + groups: [adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev] |
| 367 | + sudo: ALL=(ALL) NOPASSWD:ALL |
| 368 | + shell: /bin/bash |
| 369 | + packages: [hello] |
| 370 | + write_files: |
| 371 | + - path: /etc/wsl.conf |
| 372 | + append: true |
| 373 | + content: | |
| 374 | + [user] |
| 375 | + default=u |
| 376 | + run: | |
| 377 | + if ("${{ matrix.ubuntu-insights-state }}" -ne "skip") { |
| 378 | + $env:cloudinit += "`- path: /home/u/.config/ubuntu-insights/wsl_setup-consent.toml`n owner: u:u`n permissions: '0755'`n defer: true`n content: |`n consent_state = ${{ matrix.ubuntu-insights-state }}" |
| 379 | + } |
| 380 | + |
| 381 | + # No launch so we can install the deb before running the OOBE |
| 382 | + wsl --install --no-launch --from-file "images.wsl" --name "${{ env.instance }}" |
| 383 | + wsl -d "${{ env.instance }}" -u root -- ls -l "./ci-artifacts/wsl-setup_*/" |
| 384 | + wsl -d "${{ env.instance }}" -u root -- dpkg -i "./ci-artifacts/wsl-setup_*/wsl-setup_*.deb" |
| 385 | + |
| 386 | + # In case cloud-init fails we don't get stuck on endless prompting. |
| 387 | + wsl -d "${{ env.instance }}" -u root -- adduser --quiet --gecos '' --disabled-password ubuntu |
| 388 | + wsl -d "${{ env.instance }}" -u root -- usermod ubuntu -aG "adm,cdrom,sudo,dip,plugdev" |
| 389 | + wsl -d "${{ env.instance }}" -u root -- cloud-init status --wait |
| 390 | + wsl -d "${{ env.instance }}" -u root -- bash -ec "rm /etc/cloud/cloud-init.disabled || true" |
| 391 | + wsl -d "${{ env.instance }}" -u root -- bash -ec "echo -e '#!/bin/bash\necho Exiting because the profile says so\nexit 0' > ${{ env.profile_script }}" |
| 392 | + wsl -d "${{ env.instance }}" -u root -- chmod '0777' ${{ env.profile_script }} |
| 393 | + wsl -d "${{ env.instance }}" -u root -- cloud-init clean --logs |
| 394 | + wsl --shutdown |
| 395 | + |
| 396 | + # Write the cloud-init user-data contents. |
| 397 | + $cloudinitdir = New-Item -ItemType "Directory" -Path "${env:UserProfile}\.cloud-init\" -Force |
| 398 | + # TODO: Investigate why UTF-8 BOM seems to break cloud-init. |
| 399 | + $filePath="${cloudinitdir}\${{ env.instance }}.user-data" |
| 400 | + $utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False |
| 401 | + [System.IO.File]::WriteAllLines($filePath, $env:cloudinit, $utf8NoBomEncoding) |
| 402 | + Write-Output "Testing wsl-setup with cloud-init user data at ${cloudinitdir} :" |
| 403 | + Get-Content "${cloudinitdir}\${{ env.instance }}.user-data" |
| 404 | + |
| 405 | + - name: Test initial setup |
| 406 | + timeout-minutes: 10 |
| 407 | + shell: powershell |
| 408 | + run: | |
| 409 | + wsl -d "${{ env.instance }}" # Will run the wsl-setup script. |
| 410 | + |
| 411 | + wsl -d "${{ env.instance }}" -u root -- bash -ec "rm ${{ env.profile_script }} || true" |
| 412 | + wsl -d "${{ env.instance }}" -u root -- bash -ec "cat /var/log/cloud-init.log || true" |
| 413 | + wsl --terminate ${{ env.instance }} |
| 414 | + |
| 415 | + - name: Determine expected consent state |
| 416 | + id: expected-consent |
| 417 | + shell: powershell |
| 418 | + run: | |
| 419 | + $state = "${{ matrix.ubuntu-insights-state }}" |
| 420 | + $flag = "${{ matrix.ubuntu-insights-flag }}" |
| 421 | + |
| 422 | + if ($state -eq "true" -or $state -eq "false") { |
| 423 | + $expected = $state |
| 424 | + } elseif ($flag -eq "1") { |
| 425 | + $expected = "true" |
| 426 | + } elseif ($flag -eq "0") { |
| 427 | + $expected = "false" |
| 428 | + } else { |
| 429 | + Write-Error "Internal test setup error: invalid state='$state' and flag='$flag'. Expected state to be 'true' or 'false', or flag to be '0' or '1'." |
| 430 | + Exit 1 |
| 431 | + } |
| 432 | + |
| 433 | + Add-Content -Path $env:GITHUB_OUTPUT -Value "expected=$expected" |
| 434 | + |
| 435 | + - name: Validate instance state |
| 436 | + uses: ./.github/actions/validate-wsl |
| 437 | + with: |
| 438 | + instance: ${{ env.instance }} |
| 439 | + working-dir: ${{ env.instance_working_dir }} |
| 440 | + expected-user: u |
| 441 | + expected-internal-consent-state: ${{ steps.expected-consent.outputs.expected }} |
| 442 | + expected-registry-consent-state: ${{ matrix.ubuntu-insights-flag }} |
| 443 | + |
| 444 | + - name: Run hello package |
| 445 | + uses: ubuntu/WSL/.github/actions/wsl-bash@main |
| 446 | + with: |
| 447 | + distro: ${{ env.instance }} |
| 448 | + working-dir: ${{ env.instance_working_dir }} |
| 449 | + exec: | |
| 450 | + if ! hello; then |
| 451 | + echo "::error:: Failed to execute the hello program that should have been installed by cloud-init" |
| 452 | + exit 1 |
| 453 | + fi |
| 454 | + |
| 455 | + - name: Clean up # Probably not necessary since we're leveraging ephemeral GH's runners. |
| 456 | + if: always() |
| 457 | + shell: powershell |
| 458 | + run: | |
| 459 | + wsl --unregister ${{ env.instance }} |
| 460 | + |
| 461 | + interactive-test: |
| 462 | + needs: [build-deb, cache-img] |
| 463 | + name: "Interactive Expect tests" |
| 464 | + runs-on: windows-2025 # WSL is preinstalled and working |
| 465 | + env: |
| 466 | + username: test-ubuntu-user |
| 467 | + password: TestPass123! |
| 468 | + strategy: |
| 469 | + fail-fast: false |
| 470 | + matrix: |
| 471 | + include: |
| 472 | + # Basic interactive consent |
| 473 | + - consent: "default" |
| 474 | + consent-registry-flag: "skip" |
| 475 | + config-state: "skip" |
| 476 | + expected-internal: "true" |
| 477 | + expected-registry: "1" |
| 478 | + - consent: "yes" |
| 479 | + consent-registry-flag: "skip" |
| 480 | + config-state: "skip" |
| 481 | + expected-internal: "true" |
| 482 | + expected-registry: "1" |
| 483 | + - consent: "no" |
| 484 | + consent-registry-flag: "skip" |
| 485 | + config-state: "skip" |
| 486 | + expected-internal: "false" |
| 487 | + expected-registry: "0" |
| 488 | + # Handle null states |
| 489 | + - consent: "yes" |
| 490 | + consent-registry-flag: "123" |
| 491 | + config-state: "null" |
| 492 | + expected-internal: "true" |
| 493 | + expected-registry: "1" |
| 494 | + # Config state takes precedence over registry flag |
| 495 | + - consent: "skip" |
| 496 | + consent-registry-flag: "1" |
| 497 | + config-state: "false" |
| 498 | + expected-internal: "false" |
| 499 | + expected-registry: "1" |
| 500 | + - consent: "skip" |
| 501 | + consent-registry-flag: "0" |
| 502 | + config-state: "true" |
| 503 | + expected-internal: "true" |
| 504 | + expected-registry: "0" |
| 505 | + # Registry flag takes precedence over interactive prompt |
| 506 | + - consent: "skip" |
| 507 | + consent-registry-flag: "1" |
| 508 | + config-state: "skip" |
| 509 | + expected-internal: "true" |
| 510 | + expected-registry: "1" |
| 511 | + - consent: "skip" |
| 512 | + consent-registry-flag: "0" |
| 513 | + config-state: "null" |
| 514 | + expected-internal: "false" |
| 515 | + expected-registry: "0" |
| 516 | + |
| 517 | + steps: |
| 518 | + - name: Check out repository |
| 519 | + uses: actions/checkout@v6 |
| 520 | + |
| 521 | + - name: Download artifacts |
| 522 | + uses: actions/download-artifact@v7 |
| 523 | + with: |
| 524 | + path: ci-artifacts |
| 525 | + |
| 526 | + - name: Get WSL image |
| 527 | + uses: ./.github/actions/get-wsl-image |
| 528 | + with: |
| 529 | + output_path: images.wsl |
| 530 | + |
| 531 | + - name: Prepare Ubuntu Insights consent registry key |
| 532 | + if: matrix.consent-registry-flag != 'skip' |
| 533 | + shell: powershell |
| 534 | + run: | |
| 535 | + New-Item -Path "${{ env.insights_registry_key_path }}" -Force |
| 536 | + New-ItemProperty -Path "${{ env.insights_registry_key_path }}" -Name "${{ env.insights_registry_value_name }}" -Value "${{ matrix.consent-registry-flag }}" -PropertyType DWord -Force |
| 537 | + |
| 538 | + - name: Install WSL instance |
| 539 | + shell: powershell |
| 540 | + env: |
| 541 | + WSL_UTF8: "1" |
| 542 | + run: | |
| 543 | + wsl --install --no-launch --from-file "images.wsl" --name "${{ env.instance }}" |
| 544 | + |
| 545 | + - name: Install wsl-setup and test dependencies |
| 546 | + shell: powershell |
| 547 | + env: |
| 548 | + WSL_UTF8: "1" |
| 549 | + run: | |
| 550 | + # Install the deb package |
| 551 | + wsl -d "${{ env.instance }}" -u root -- dpkg -i "./ci-artifacts/wsl-setup_*/wsl-setup_*.deb" |
| 552 | + # Install expect |
| 553 | + wsl -d "${{ env.instance }}" -u root -- apt-get update |
| 554 | + wsl -d "${{ env.instance }}" -u root -- apt-get install -y expect |
| 555 | + |
| 556 | + - name: Configure consent file |
| 557 | + if: matrix.config-state != 'skip' |
| 558 | + shell: powershell |
| 559 | + env: |
| 560 | + WSL_UTF8: "1" |
| 561 | + run: | |
| 562 | + $configContent = "consent_state = ${{ matrix.config-state }}" |
| 563 | + wsl -d "${{ env.instance }}" -u root -- bash -c "mkdir -p /etc/skel/.config/ubuntu-insights" |
| 564 | + wsl -d "${{ env.instance }}" -u root -- bash -c "echo '$configContent' > /etc/skel/.config/ubuntu-insights/wsl_setup-consent.toml" |
| 565 | + |
| 566 | + - name: Reset WSL instance state |
| 567 | + shell: powershell |
| 568 | + env: |
| 569 | + WSL_UTF8: "1" |
| 570 | + run: | |
| 571 | + wsl -d "${{ env.instance }}" -u root -- cloud-init status --wait |
| 572 | + wsl -d "${{ env.instance }}" -u root -- bash -ec "rm /etc/cloud/cloud-init.disabled || true" |
| 573 | + wsl -d "${{ env.instance }}" -u root -- cloud-init clean --logs |
| 574 | + wsl --shutdown |
| 575 | + |
| 576 | + - name: Run expect test |
| 577 | + shell: powershell |
| 578 | + env: |
| 579 | + WSL_UTF8: "1" |
| 580 | + timeout-minutes: 10 |
| 581 | + run: | |
| 582 | + # Copy expect script to instance |
| 583 | + wsl -d "${{ env.instance }}" -u root -- cp "test/e2e/setup.expect" "/tmp/setup.expect" |
| 584 | + |
| 585 | + # Run the expect script |
| 586 | + wsl -d "${{ env.instance }}" -u root -- expect /tmp/setup.expect "${{ env.username }}" "${{ env.password }}" "${{ matrix.consent }}" |
| 587 | + |
| 588 | + - name: Validate instance state |
| 589 | + uses: ./.github/actions/validate-wsl |
| 590 | + with: |
| 591 | + instance: ${{ env.instance }} |
| 592 | + working-dir: ${{ env.instance_working_dir }} |
| 593 | + expected-user: ${{ env.username }} |
| 594 | + expected-internal-consent-state: ${{ matrix.expected-internal }} |
| 595 | + expected-registry-consent-state: ${{ matrix.expected-registry }} |
| 596 | + |
| 597 | + - name: Clean up # Probably not necessary since we're leveraging ephemeral GH's runners. |
| 598 | + if: always() |
| 599 | + shell: powershell |
| 600 | + run: | |
| 601 | + wsl --unregister ${{ env.instance }} |
| 602 | diff --git a/.github/workflows/shellcheck.yaml b/.github/workflows/shellcheck.yaml |
| 603 | new file mode 100644 |
| 604 | index 0000000..77cf70a |
| 605 | --- /dev/null |
| 606 | +++ b/.github/workflows/shellcheck.yaml |
| 607 | @@ -0,0 +1,27 @@ |
| 608 | +name: ShellCheck QA |
| 609 | + |
| 610 | +on: |
| 611 | + push: |
| 612 | + branches: [main] |
| 613 | + pull_request: |
| 614 | +permissions: {} |
| 615 | + |
| 616 | +env: |
| 617 | + EXTENSIONLESS_SCRIPTS: "wsl-setup wait-for-cloud-init ubuntu-insights" |
| 618 | + |
| 619 | +jobs: |
| 620 | + shellcheck: |
| 621 | + name: Run ShellCheck |
| 622 | + runs-on: ubuntu-24.04 |
| 623 | + steps: |
| 624 | + - name: Checkout repository |
| 625 | + uses: actions/checkout@v6 |
| 626 | + |
| 627 | + - name: Find and Run ShellCheck |
| 628 | + run: | |
| 629 | + exec shellcheck -f gcc $( |
| 630 | + find . \ |
| 631 | + \( -name '*.sh' -o -name '*.bash' -o -name '*.zsh' \ |
| 632 | + $(for f in $EXTENSIONLESS_SCRIPTS; do echo -o -name "$f"; done) \) \ |
| 633 | + -type f |
| 634 | + ) |
| 635 | diff --git a/.shellcheckrc b/.shellcheckrc |
| 636 | new file mode 100644 |
| 637 | index 0000000..8226afb |
| 638 | --- /dev/null |
| 639 | +++ b/.shellcheckrc |
| 640 | @@ -0,0 +1 @@ |
| 641 | +external-sources=true |
| 642 | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md |
| 643 | new file mode 100644 |
| 644 | index 0000000..67c33a0 |
| 645 | --- /dev/null |
| 646 | +++ b/CONTRIBUTING.md |
| 647 | @@ -0,0 +1,102 @@ |
| 648 | +# Contributing to `wsl-setup` |
| 649 | + |
| 650 | +A big welcome and thank you for considering contributing to `wsl-setup` and Ubuntu! It’s people like you that make it a reality for users in our community. |
| 651 | + |
| 652 | +Reading and following these guidelines will help us make the contribution process easy and effective for everyone involved. It also communicates that you agree to respect the time of the developers managing and developing this project. In return, we will reciprocate that respect by addressing your issue, assessing changes, and helping you finalize your pull requests. |
| 653 | + |
| 654 | +These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. |
| 655 | + |
| 656 | +## Quick links |
| 657 | + |
| 658 | +* [Code of conduct](#code-of-conduct) |
| 659 | +* [Getting started](#getting-started) |
| 660 | +* [Issues](#issues) |
| 661 | +* [Pull requests](#pull-requests) |
| 662 | +* [Contributing to the code](#contributing-to-the-code) |
| 663 | +* [Contributor license agreement](#contributor-license-agreement) |
| 664 | +* [Getting help](#getting-help) |
| 665 | + |
| 666 | +## Code of conduct |
| 667 | + |
| 668 | +We take our community seriously and hold ourselves and other contributors to high standards of communication. By participating and contributing to this project, you agree to uphold our [Code of Conduct](https://ubuntu.com/community/code-of-conduct). |
| 669 | + |
| 670 | +## Getting started |
| 671 | + |
| 672 | +Contributions are made to this project via issues and pull requests (PRs). A few general guidelines that cover both: |
| 673 | + |
| 674 | +* To report security vulnerabilities, please use the advisories page of the repository and not a public bug report. Please use [GitHub security advisories](https://github.com/ubuntu/wsl-setup/security/advisories/new) [launchpad private bugs](https://bugs.launchpad.net/ubuntu/+source/wsl-setup/+filebug) which is monitored by our security team. On an Ubuntu machine, it’s best to use `ubuntu-bug wsl-setup` to collect relevant information. |
| 675 | +* Search for existing issues and PRs on this repository before creating your own. |
| 676 | +* We work hard to makes sure issues are handled in a timely manner but, depending on the impact, it could take a while to investigate the root cause. A friendly ping in the comment thread to the submitter or a contributor can help draw attention if your issue is blocking. |
| 677 | +* If you've never contributed before, see [this Ubuntu community page](https://ubuntu.com/community/docs/contribute) for resources and tips on how to get started. |
| 678 | + |
| 679 | +### Issues |
| 680 | + |
| 681 | +Issues should be used to report problems with the software, request a new feature, or to discuss potential changes before a PR is created. When you create a new issue, a template will be loaded that will guide you through collecting and providing the information we need to investigate. |
| 682 | + |
| 683 | +If you find an issue that addresses the problem you're having, please add your own reproduction information to the existing issue rather than creating a new one. Adding a [reaction](https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) can also help be indicating to our maintainers that a particular problem is affecting more than just the reporter. |
| 684 | + |
| 685 | +### Pull requests |
| 686 | + |
| 687 | +PRs to our project are always welcome and can be a quick way to get your fix or improvement slated for the next release. In general, PRs should: |
| 688 | + |
| 689 | +* Only fix/add the functionality in question **OR** address wide-spread whitespace/style issues, not both. |
| 690 | +* Add unit or integration tests for fixed or changed functionality. |
| 691 | +* Address a single concern in the least number of changed lines as possible. |
| 692 | +* Include documentation in the repo or on our [docs site](https://documentation.ubuntu.com/wsl). |
| 693 | +* Be accompanied by a complete pull request template (loaded automatically when a PR is created). |
| 694 | + |
| 695 | +For changes that address core functionality or would require breaking changes (e.g. a major release), it's best to open an issue to discuss your proposal first. This is not required but can save time creating and reviewing changes. |
| 696 | + |
| 697 | +In general, we follow the ["fork-and-pull" Git workflow](https://github.com/susam/gitpr) |
| 698 | + |
| 699 | +1. Fork the repository to your own GitHub account |
| 700 | +1. Clone the project to your machine |
| 701 | +1. Create a branch locally with a succinct but descriptive name |
| 702 | +1. Commit changes to the branch |
| 703 | +1. Following any formatting and testing guidelines specific to this repo |
| 704 | +1. Push changes to your fork |
| 705 | +1. Open a PR in our repository and follow the PR template so that we can efficiently review the changes. |
| 706 | + |
| 707 | +> PRs will trigger unit and integration tests with and without race detection, linting and formatting validations, static and security checks, freshness of generated files verification. All the tests must pass before merging in main branch. |
| 708 | + |
| 709 | +Once merged to the main branch, `po` files and any documentation change will be automatically updated. Those are thus not necessary in the pull request itself to minimize diff review. |
| 710 | + |
| 711 | +## Contributing to the code |
| 712 | + |
| 713 | +### Building and running |
| 714 | + |
| 715 | +This project is packaged as a Debian package. |
| 716 | + |
| 717 | +To build from source, run the following command from the root folder of the repository. |
| 718 | + |
| 719 | +```bash |
| 720 | +debuild |
| 721 | +``` |
| 722 | + |
| 723 | +The output will be available in the parent directory. |
| 724 | + |
| 725 | +Please see the [Ubuntu Packaging Guide](https://canonical-ubuntu-packaging-guide.readthedocs-hosted.com) for more details. |
| 726 | + |
| 727 | +### About the test suite |
| 728 | + |
| 729 | +The project includes a comprehensive test suite made of unit and integration tests. All the tests must pass before the review is considered. If you have troubles with the test suite, feel free to mention it on your PR description. |
| 730 | + |
| 731 | +Some of our tests utilize [expect](https://linux.die.net/man/1/expect) and supplementary shell scripts. Please see `test/` as well as the workflows and actions present in `.github/`. |
| 732 | + |
| 733 | +The test suite must pass before merging the PR to our main branch. Any new feature, change or fix must be covered by corresponding tests. |
| 734 | + |
| 735 | +### Code style |
| 736 | + |
| 737 | +This project utilizes [ShellCheck](https://github.com/koalaman/shellcheck) for static analysis of shell scripts. |
| 738 | + |
| 739 | +## Contributor license agreement |
| 740 | + |
| 741 | +It is required to sign the [Contributor License Agreement](https://ubuntu.com/legal/contributors) in order to contribute to this project. |
| 742 | + |
| 743 | +An automated test is executed on PRs to check if it has been accepted. |
| 744 | + |
| 745 | +This project is covered by [GPL-3.0](LICENSE). |
| 746 | + |
| 747 | +## Getting help |
| 748 | + |
| 749 | +Join us in the [Ubuntu Community](https://discourse.ubuntu.com/c/project/wsl/27) and post your question there with a descriptive tag. |
| 750 | diff --git a/SECURITY.md b/SECURITY.md |
| 751 | new file mode 100644 |
| 752 | index 0000000..e54ba42 |
| 753 | --- /dev/null |
| 754 | +++ b/SECURITY.md |
| 755 | @@ -0,0 +1,53 @@ |
| 756 | +# Security policy |
| 757 | + |
| 758 | +## Supported versions |
| 759 | + |
| 760 | +`wsl-setup` is distributed via the Ubuntu archive as the [`wsl-setup` package](https://launchpad.net/ubuntu/+source/wsl-setup). |
| 761 | + |
| 762 | +Currently, we provide security updates for supported LTS releases of Ubuntu on WSL. |
| 763 | + |
| 764 | +If you are unsure of the Ubuntu version you are using, please run the following command in a |
| 765 | +WSL terminal running your Ubuntu distro: |
| 766 | + |
| 767 | +```bash |
| 768 | +lsb_release -a |
| 769 | +``` |
| 770 | + |
| 771 | +## Reporting a vulnerability |
| 772 | + |
| 773 | +If you discover a security vulnerability within this repository, we encourage |
| 774 | +responsible disclosure. Please report any security issues to help us keep |
| 775 | +`wsl-setup` and Ubuntu on WSL secure for everyone. |
| 776 | + |
| 777 | +### Private vulnerability reporting |
| 778 | + |
| 779 | +The most straightforward way to report a security vulnerability is through |
| 780 | +[GitHub](https://github.com/ubuntu/wsl-setup/security/advisories/new). For detailed |
| 781 | +instructions, please review the |
| 782 | +[Privately reporting a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) |
| 783 | +documentation. This method enables you to communicate vulnerabilities directly |
| 784 | +and confidentially with the `wsl-setup` maintainers. |
| 785 | + |
| 786 | +The project's admins will be notified of the issue and will work with you to |
| 787 | +determine whether the issue qualifies as a security issue and, if so, in which |
| 788 | +component. We will then handle finding a fix, getting a CVE assigned and |
| 789 | +coordinating the release of the fix to the various Linux distributions. |
| 790 | + |
| 791 | +The [Ubuntu Security disclosure and embargo policy](https://ubuntu.com/security/disclosure-policy) |
| 792 | +contains more information about what you can expect when you contact us, and what we expect from you. |
| 793 | + |
| 794 | +#### Steps to report a vulnerability on GitHub |
| 795 | + |
| 796 | +1. Go to the [Security Advisories Page](https://github.com/ubuntu/wsl-setup/security/advisories) of the `wsl-setup` repository. |
| 797 | +2. Click "Report a Vulnerability" |
| 798 | +3. Provide detailed information about the vulnerability, including steps to reproduce, affected versions, and potential impact. |
| 799 | + |
| 800 | +## Security resources |
| 801 | + |
| 802 | +* [Canonical's Security Site](https://ubuntu.com/security) |
| 803 | +* [Ubuntu Security disclosure and embargo policy](https://ubuntu.com/security/disclosure-policy) |
| 804 | +* [Ubuntu Security Notices](https://ubuntu.com/security/notices) |
| 805 | +* [Ubuntu on WSL documentation](https://documentation.ubuntu.com/wsl/en/latest/) |
| 806 | + |
| 807 | +If you have any questions regarding security vulnerabilities, please reach out |
| 808 | +to the maintainers through the aforementioned channels. |
| 809 | diff --git a/debian/changelog b/debian/changelog |
| 810 | index 628d1d5..b807dd6 100644 |
| 811 | --- a/debian/changelog |
| 812 | +++ b/debian/changelog |
| 813 | @@ -1,3 +1,15 @@ |
| 814 | +wsl-setup (0.6.1) resolute; urgency=medium |
| 815 | + |
| 816 | + [ Kat Kuo] |
| 817 | + * Added ubuntu-insights integration |
| 818 | + * Added integration tests in upstream CI |
| 819 | + |
| 820 | + [Carlos Nihelton] |
| 821 | + * Case-fold the Windows username before sanitization (LP: #2122047) |
| 822 | + * Remove the systemd-timesyncd.service override that would enable it in WSL |
| 823 | + |
| 824 | + -- Kat Kuo <kat.kuo@canonical.com> Tue, 25 Nov 2025 19:56:56 -0500 |
| 825 | + |
| 826 | wsl-setup (0.5.10) questing; urgency=medium |
| 827 | |
| 828 | * Fix Ubuntu on WSL install fails on non-ASCII usernames (LP: #2118617) |
| 829 | diff --git a/debian/control b/debian/control |
| 830 | index e20198d..d54931f 100644 |
| 831 | --- a/debian/control |
| 832 | +++ b/debian/control |
| 833 | @@ -19,5 +19,6 @@ Depends: adduser, |
| 834 | systemd, |
| 835 | ${shlibs:Depends}, |
| 836 | ${misc:Depends}, |
| 837 | +Recommends: ubuntu-insights, |
| 838 | Description: ${source:Synopsis} |
| 839 | ${source:Extended-Description} |
| 840 | diff --git a/debian/install b/debian/install |
| 841 | index 8490e53..d27180f 100644 |
| 842 | --- a/debian/install |
| 843 | +++ b/debian/install |
| 844 | @@ -1,6 +1,7 @@ |
| 845 | cloud/ etc/ |
| 846 | update-motd.d/99-wsl etc/update-motd.d/ |
| 847 | -systemd/ lib/ |
| 848 | +systemd/ usr/lib/ |
| 849 | +ubuntu-insights.sh usr/lib/wsl/ |
| 850 | wsl-setup usr/lib/wsl/ |
| 851 | wait-for-cloud-init usr/lib/wsl/ |
| 852 | wsl/ usr/share/ |
| 853 | diff --git a/debian/rules b/debian/rules |
| 854 | index 80b3fe1..ed58acc 100755 |
| 855 | --- a/debian/rules |
| 856 | +++ b/debian/rules |
| 857 | @@ -3,4 +3,3 @@ |
| 858 | |
| 859 | %: |
| 860 | dh $@ |
| 861 | - |
| 862 | diff --git a/systemd/system/systemd-timesyncd.service.d/wsl.conf b/systemd/system/systemd-timesyncd.service.d/wsl.conf |
| 863 | deleted file mode 100644 |
| 864 | index bcb2e53..0000000 |
| 865 | --- a/systemd/system/systemd-timesyncd.service.d/wsl.conf |
| 866 | +++ /dev/null |
| 867 | @@ -1,7 +0,0 @@ |
| 868 | -# Enable timesyncd on WSL machines |
| 869 | -# so WSL clock is synced on resume from suspend of the host. |
| 870 | - |
| 871 | -[Unit] |
| 872 | -ConditionVirtualization= |
| 873 | -ConditionVirtualization=|!container |
| 874 | -ConditionVirtualization=|wsl |
| 875 | diff --git a/test/basic-assertions.sh b/test/basic-assertions.sh |
| 876 | new file mode 100755 |
| 877 | index 0000000..69fc1cb |
| 878 | --- /dev/null |
| 879 | +++ b/test/basic-assertions.sh |
| 880 | @@ -0,0 +1,30 @@ |
| 881 | +#!/bin/bash |
| 882 | +# This is a set of basic assertions to verify a WSL instance is correctly set up. |
| 883 | +# This should run inside a WSL instance or machine prepared for testing purposes. |
| 884 | +# It requires installing the wsl-setup Debian package to assert on its results. |
| 885 | +# The expected default user can be passed as the first argument. |
| 886 | + |
| 887 | +EXPECTED_USER=${1:-u} |
| 888 | + |
| 889 | +brandingdir="/usr/share/wsl" |
| 890 | +if [[ ! -r "${brandingdir}/ubuntu.ico" ]]; then |
| 891 | + echo "::error:: Missing Ubuntu icon in the $brandingdir directory." |
| 892 | + ls "${brandingdir}" |
| 893 | + exit 1 |
| 894 | +fi |
| 895 | + |
| 896 | +if [[ ! -r "${brandingdir}/terminal-profile.json" ]]; then |
| 897 | + echo "::error:: Missing terminal profile fragment in the $brandingdir directory." |
| 898 | + ls "${brandingdir}" |
| 899 | + exit 2 |
| 900 | +fi |
| 901 | + |
| 902 | +if [[ $(id -u) == 0 ]]; then |
| 903 | + echo "::error:: Default user shouldn't be root" |
| 904 | + exit 3 |
| 905 | +fi |
| 906 | + |
| 907 | +if [[ $(whoami) != "$EXPECTED_USER" ]]; then |
| 908 | + echo "::error:: Default user doesn't match expected user '$EXPECTED_USER'." |
| 909 | + exit 4 |
| 910 | +fi |
| 911 | diff --git a/test/e2e/setup.expect b/test/e2e/setup.expect |
| 912 | new file mode 100644 |
| 913 | index 0000000..76ff5cc |
| 914 | --- /dev/null |
| 915 | +++ b/test/e2e/setup.expect |
| 916 | @@ -0,0 +1,113 @@ |
| 917 | +#!/usr/bin/expect -f |
| 918 | + |
| 919 | +set timeout 240 |
| 920 | + |
| 921 | +set username [lindex $argv 0] |
| 922 | +set password [lindex $argv 1] |
| 923 | +set consent [lindex $argv 2] |
| 924 | + |
| 925 | +if {$username == ""} { |
| 926 | + puts "Error: Username argument is required." |
| 927 | + exit 1 |
| 928 | +} |
| 929 | + |
| 930 | +if {$password == ""} { |
| 931 | + puts "Error: Password argument is required." |
| 932 | + exit 1 |
| 933 | +} |
| 934 | + |
| 935 | +if {$consent == ""} { |
| 936 | + puts "Error: Consent argument is required (yes, no, skip, default)." |
| 937 | + exit 1 |
| 938 | +} |
| 939 | + |
| 940 | +# Spawn the setup script |
| 941 | +spawn /usr/lib/wsl/wsl-setup |
| 942 | + |
| 943 | +# Handle Username |
| 944 | +expect { |
| 945 | + "Create a default Unix user account:" { |
| 946 | + # Send Ctrl-U to clear the pre-filled default username |
| 947 | + send "\025" |
| 948 | + send "$username\r" |
| 949 | + } |
| 950 | + timeout { |
| 951 | + puts "Error: Timeout waiting for username prompt." |
| 952 | + exit 1 |
| 953 | + } |
| 954 | +} |
| 955 | + |
| 956 | +# Handle Password |
| 957 | +expect { |
| 958 | + "New password:" { |
| 959 | + send "$password\r" |
| 960 | + } |
| 961 | + timeout { |
| 962 | + puts "Error: Timeout waiting for password prompt." |
| 963 | + exit 1 |
| 964 | + } |
| 965 | +} |
| 966 | + |
| 967 | +expect { |
| 968 | + "Retype new password:" { |
| 969 | + send "$password\r" |
| 970 | + } |
| 971 | + timeout { |
| 972 | + puts "Error: Timeout waiting for retype password prompt." |
| 973 | + exit 1 |
| 974 | + } |
| 975 | +} |
| 976 | + |
| 977 | +# Handle Consent |
| 978 | +if {$consent == "skip"} { |
| 979 | + # If we expect to skip, we should NOT see the consent prompt. |
| 980 | + expect { |
| 981 | + "Would you like to opt-in to platform metrics collection (Y/n)? To see an example of the data collected, enter 'e'." { |
| 982 | + puts "Error: Consent prompt appeared but should have been skipped." |
| 983 | + exit 1 |
| 984 | + } |
| 985 | + eof { |
| 986 | + # Script finished successfully |
| 987 | + } |
| 988 | + timeout { |
| 989 | + puts "Error: Timeout waiting for EOF (skip scenario)." |
| 990 | + exit 1 |
| 991 | + } |
| 992 | + } |
| 993 | +} else { |
| 994 | + expect { |
| 995 | + "\\\[Y/n/e\\\]: " { |
| 996 | + if {$consent == "default"} { |
| 997 | + send "\r" |
| 998 | + } else { |
| 999 | + # Send Ctrl-U to clear the pre-filled default consent value |
| 1000 | + send "\025" |
| 1001 | + |
| 1002 | + if {$consent == "yes"} { |
| 1003 | + send "y\r" |
| 1004 | + } elseif {$consent == "no"} { |
| 1005 | + send "n\r" |
| 1006 | + } else { |
| 1007 | + puts "Error: Invalid consent value '$consent'. Use yes, no, skip, or default." |
| 1008 | + exit 1 |
| 1009 | + } |
| 1010 | + } |
| 1011 | + } |
| 1012 | + timeout { |
| 1013 | + puts "Error: Timeout waiting for consent prompt." |
| 1014 | + exit 1 |
| 1015 | + } |
| 1016 | + eof { |
| 1017 | + puts "Error: Unexpected EOF while waiting for consent prompt." |
| 1018 | + exit 1 |
| 1019 | + } |
| 1020 | + } |
| 1021 | + expect eof |
| 1022 | +} |
| 1023 | + |
| 1024 | +# Check exit status |
| 1025 | +lassign [wait] pid spawnid os_error_flag value |
| 1026 | +if {$value != 0} { |
| 1027 | + puts "Error: wsl-setup failed with exit code $value" |
| 1028 | + exit $value |
| 1029 | +} |
| 1030 | diff --git a/test/systemd-assertions.sh b/test/systemd-assertions.sh |
| 1031 | new file mode 100755 |
| 1032 | index 0000000..8827d27 |
| 1033 | --- /dev/null |
| 1034 | +++ b/test/systemd-assertions.sh |
| 1035 | @@ -0,0 +1,33 @@ |
| 1036 | +#!/bin/bash |
| 1037 | +# This is a set of basic assertions to verify systemd specific conditions in a newly set up WSL instance. |
| 1038 | +# This should run inside a WSL instance or machine prepared for testing purposes. |
| 1039 | +# It requires installing the wsl-setup Debian package to assert on its results. |
| 1040 | + |
| 1041 | +if [[ $(LANG=C systemctl is-system-running) != "running" ]]; then |
| 1042 | + systemctl --failed |
| 1043 | + exit 1 |
| 1044 | +fi |
| 1045 | + |
| 1046 | +if [[ $(LANG=C systemctl is-active multipathd.service) != "inactive" ]]; then |
| 1047 | + echo "::error:: Unit multipathd.service should have been disabled by the multipathd.service.d/container.conf override" |
| 1048 | + systemctl status multipathd.service |
| 1049 | + exit 2 |
| 1050 | +fi |
| 1051 | + |
| 1052 | +# It's been a while since WSL kernel implemented a patch specifically for time sync with Hyper-V. |
| 1053 | +# With that NTS clients inside WSL instances can cause more trouble if they don't sync with the same |
| 1054 | +# source as Windows does. So we're no longer overriding this systemd unit, let be the defaults. |
| 1055 | +# Let's not worry about chrony just yet. |
| 1056 | +nts_unit="systemd-timesyncd.service" |
| 1057 | +if systemctl is-enabled "${nts_unit}"; then |
| 1058 | + if [[ $(LANG=C systemctl is-active "${nts_unit}") != "inactive" ]]; then |
| 1059 | + echo "::error:: Unit ${nts_unit} should be disabled by default." |
| 1060 | + systemctl status ${nts_unit} |
| 1061 | + exit 3 |
| 1062 | + fi |
| 1063 | +fi |
| 1064 | + |
| 1065 | +if [[ ! -r "/etc/cloud/cloud-init.disabled" ]]; then |
| 1066 | + echo "::error:: Missing cloud-init.disabled marker file" |
| 1067 | + exit 4 |
| 1068 | +fi |
| 1069 | diff --git a/test/ubuntu-insights-assertions.sh b/test/ubuntu-insights-assertions.sh |
| 1070 | new file mode 100755 |
| 1071 | index 0000000..f1c6bef |
| 1072 | --- /dev/null |
| 1073 | +++ b/test/ubuntu-insights-assertions.sh |
| 1074 | @@ -0,0 +1,33 @@ |
| 1075 | +#!/bin/bash |
| 1076 | +# This is a set of basic assertions partially validating Ubuntu Insights consent setup in WSL. |
| 1077 | +# This should run inside a WSL instance or machine prepared for testing purposes. |
| 1078 | +# It requires installing the wsl-setup Debian package to assert on its results. |
| 1079 | +# The expected consent state can be passed as the first argument: true or false. |
| 1080 | + |
| 1081 | +EXPECTED_CONSENT=${1} |
| 1082 | + |
| 1083 | +if [[ "$EXPECTED_CONSENT" != "true" && "$EXPECTED_CONSENT" != "false" ]]; then |
| 1084 | + echo "::error:: Expected first argument to be 'true' or 'false', got '$EXPECTED_CONSENT'" |
| 1085 | + exit 1 |
| 1086 | +fi |
| 1087 | + |
| 1088 | +if ! ubuntu-insights consent wsl_setup | grep -q "wsl_setup: $EXPECTED_CONSENT"; then |
| 1089 | + echo "::error:: Ubuntu-Insights Consent state assertion: Expected 'wsl_setup: $EXPECTED_CONSENT'" |
| 1090 | + exit 1 |
| 1091 | +fi |
| 1092 | + |
| 1093 | +# Verify that a report was created for the wsl_setup source. |
| 1094 | +# Use max int32 for period (2147483647) to ensure we cover the timestamp of the existing report. |
| 1095 | +OUTPUT=$(ubuntu-insights collect --period 2147483647 --dry-run wsl_setup <(echo "{}") 2>&1 >/dev/null) |
| 1096 | +EXIT_CODE=$? |
| 1097 | + |
| 1098 | +if [ $EXIT_CODE -eq 0 ]; then |
| 1099 | + echo "::error:: Expected non-zero exit code from ubuntu-insights collect, got 0." |
| 1100 | + exit 1 |
| 1101 | +fi |
| 1102 | + |
| 1103 | +if ! echo "$OUTPUT" | grep -q "report already exists for this period"; then |
| 1104 | + echo "::error:: Expected 'report already exists for this period' error from ubuntu-insights collect." |
| 1105 | + echo "Output was: $OUTPUT" |
| 1106 | + exit 1 |
| 1107 | +fi |
| 1108 | diff --git a/ubuntu-insights.sh b/ubuntu-insights.sh |
| 1109 | new file mode 100755 |
| 1110 | index 0000000..0d1a5c8 |
| 1111 | --- /dev/null |
| 1112 | +++ b/ubuntu-insights.sh |
| 1113 | @@ -0,0 +1,143 @@ |
| 1114 | +#!/bin/bash |
| 1115 | +# Manages Ubuntu Insights consent during WSL setup. |
| 1116 | +# It prioritizes any existing local consent settings, then the Windows registry. |
| 1117 | +# If no consent is found, it prompts the user for consent. |
| 1118 | +set -euo pipefail |
| 1119 | + |
| 1120 | +sources=("linux" "wsl_setup" "ubuntu_release_upgrader") |
| 1121 | + |
| 1122 | +registry_key_path="HKCU:\Software\Canonical\Ubuntu" |
| 1123 | +registry_value_name="UbuntuInsightsConsent" |
| 1124 | + |
| 1125 | +# List of regular users |
| 1126 | +readarray -t users < <(getent passwd | grep -Ev '/nologin|/false|/sync' | awk -F: '$3 >= 1000 { print $1 }') |
| 1127 | + |
| 1128 | +function ask_question() { |
| 1129 | + # Only ask if we are in an interactive terminal |
| 1130 | + if [ ! -t 0 ]; then |
| 1131 | + return |
| 1132 | + fi |
| 1133 | + |
| 1134 | + local choice_val="" |
| 1135 | + local view_choice="" |
| 1136 | + while true; do |
| 1137 | + echo "Would you like to opt-in to platform metrics collection (Y/n)? To see an example of the data collected, enter 'e'." |
| 1138 | + read -rep "[Y/n/e]: " -i "y" view_choice |
| 1139 | + |
| 1140 | + if [[ $view_choice =~ ^[Ee]$ ]]; then |
| 1141 | + ubuntu-insights collect -df 2>/dev/null |
| 1142 | + continue |
| 1143 | + elif [[ $view_choice =~ ^[Yy]$ ]]; then |
| 1144 | + choice_val=1 |
| 1145 | + break |
| 1146 | + elif [[ $view_choice =~ ^[Nn]$ ]]; then |
| 1147 | + choice_val=0 |
| 1148 | + break |
| 1149 | + else |
| 1150 | + echo "Invalid input (Y/n/e)." |
| 1151 | + fi |
| 1152 | + done |
| 1153 | + |
| 1154 | + apply_consent "$choice_val" |
| 1155 | + set_consent_registry "$choice_val" >/dev/null || true |
| 1156 | +} |
| 1157 | + |
| 1158 | +function apply_consent() { |
| 1159 | + local consent="$1" |
| 1160 | + |
| 1161 | + for user in "${users[@]}"; do |
| 1162 | + for source in "${sources[@]}"; do |
| 1163 | + # shellcheck disable=SC2016 # Intentional due to how we pass parameters to a su command |
| 1164 | + su "$user" -c 'ubuntu-insights consent "$0" -s="$1" > /dev/null' -- "$source" "$([[ $consent -eq 1 ]] && echo true || echo false)" |
| 1165 | + done |
| 1166 | + done |
| 1167 | +} |
| 1168 | + |
| 1169 | +function read_consent_registry() { |
| 1170 | + local consent_value="" |
| 1171 | + consent_value=$(powershell.exe -NoProfile -Command "& { |
| 1172 | + param(\$Path, \$Name) |
| 1173 | + [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 |
| 1174 | + try { |
| 1175 | + return (Get-ItemProperty -Path \$Path -Name \$Name -ErrorAction Stop).\$Name |
| 1176 | + } |
| 1177 | + catch { |
| 1178 | + return \"\" |
| 1179 | + } |
| 1180 | + }" -Path "${registry_key_path}" -Name "${registry_value_name}" 2>/dev/null) || true |
| 1181 | + # strip control chars like \r and \n |
| 1182 | + echo "${consent_value//[[:cntrl:]]/}" |
| 1183 | +} |
| 1184 | + |
| 1185 | +function set_consent_registry() { |
| 1186 | + local consent="$1" |
| 1187 | + powershell.exe -NoProfile -Command "& { |
| 1188 | + param(\$Path, \$Name, \$Consent) |
| 1189 | + if (-not (Test-Path -Path \$Path)) { |
| 1190 | + New-Item -Path \$Path -Force | Out-Null |
| 1191 | + } |
| 1192 | + New-ItemProperty -Path \$Path -Name \$Name -Value \$Consent -PropertyType DWord -Force |
| 1193 | + }" -Path "${registry_key_path}" -Name "${registry_value_name}" -Consent "${consent}" 2>/dev/null || true |
| 1194 | +} |
| 1195 | + |
| 1196 | +function check_local_consent() { |
| 1197 | + # If any of the users has the wsl_setup consent set, we consider that consent is set locally |
| 1198 | + for user in "${users[@]}"; do |
| 1199 | + # Check wsl_setup source, if exit code is 0, consent is set |
| 1200 | + if su "$user" -c 'ubuntu-insights consent wsl_setup' >/dev/null 2>&1; then |
| 1201 | + return 0 |
| 1202 | + fi |
| 1203 | + done |
| 1204 | + return 1 |
| 1205 | +} |
| 1206 | + |
| 1207 | +function collect() { |
| 1208 | + # Collect insights and upload in background for all users |
| 1209 | + for user in "${users[@]}"; do |
| 1210 | + if su "$user" -c 'echo "{}" | ubuntu-insights collect wsl_setup /dev/stdin -f > /dev/null 2>&1'; then |
| 1211 | + su "$user" -c 'nohup ubuntu-insights upload wsl_setup -rf > /dev/null 2>&1 &' |
| 1212 | + fi |
| 1213 | + done |
| 1214 | +} |
| 1215 | + |
| 1216 | +# Check if ubuntu-insights is installed |
| 1217 | +if ! command -v ubuntu-insights >/dev/null 2>&1; then |
| 1218 | + # shellcheck disable=SC2317 # For when not run in a function |
| 1219 | + return 0 2>/dev/null || exit 0 |
| 1220 | +fi |
| 1221 | + |
| 1222 | +# Skip if no users found |
| 1223 | +if [ "${#users[@]}" -eq 0 ]; then |
| 1224 | + # shellcheck disable=SC2317 # For when not run in a function |
| 1225 | + return 0 2>/dev/null || exit 0 |
| 1226 | +fi |
| 1227 | + |
| 1228 | +# Check if consent is already set locally |
| 1229 | +if check_local_consent; then |
| 1230 | + collect |
| 1231 | + # shellcheck disable=SC2317 # For when not run in a function |
| 1232 | + return 0 2>/dev/null || exit 0 |
| 1233 | +fi |
| 1234 | + |
| 1235 | +# Check if we have a stored consent value in the Windows registry. |
| 1236 | +consent_value=$(read_consent_registry) |
| 1237 | +if [[ "$consent_value" =~ ^[01]$ ]]; then |
| 1238 | + apply_consent "$consent_value" |
| 1239 | + collect |
| 1240 | + # shellcheck disable=SC2317 # For when not run in a function |
| 1241 | + return 0 2>/dev/null || exit 0 |
| 1242 | +fi |
| 1243 | + |
| 1244 | +# Failed to read consent from the Windows registry, ask the user. |
| 1245 | +echo "Help improve Ubuntu! |
| 1246 | + |
| 1247 | +You can share anonymous data with the Ubuntu development team so we can improve your experience. |
| 1248 | +If you agree, we will collect and report anonymous hardware and system information. |
| 1249 | +This information can't be used to identify a single machine. |
| 1250 | +For legal details, please visit: https://ubuntu.com/legal/systems-information-notice |
| 1251 | + |
| 1252 | +We will save your answer to Windows and will only ask you once. |
| 1253 | +" |
| 1254 | +ask_question |
| 1255 | + |
| 1256 | +collect |
| 1257 | diff --git a/wait-for-cloud-init b/wait-for-cloud-init |
| 1258 | index 96f9821..1f3ee40 100644 |
| 1259 | --- a/wait-for-cloud-init |
| 1260 | +++ b/wait-for-cloud-init |
| 1261 | @@ -7,6 +7,6 @@ |
| 1262 | set -euo pipefail |
| 1263 | |
| 1264 | if status=$(LANG=C systemctl is-system-running 2>/dev/null) || [ "${status}" != "offline" ] && systemctl is-enabled --quiet cloud-init-local.service 2>/dev/null; then |
| 1265 | - cloud-init status --wait > /dev/null 2>&1 || true |
| 1266 | - touch /etc/cloud/cloud-init.disabled || true |
| 1267 | + cloud-init status --wait >/dev/null 2>&1 || true |
| 1268 | + touch /etc/cloud/cloud-init.disabled || true |
| 1269 | fi |
| 1270 | diff --git a/wsl-setup b/wsl-setup |
| 1271 | index a8f698f..bdb712b 100755 |
| 1272 | --- a/wsl-setup |
| 1273 | +++ b/wsl-setup |
| 1274 | @@ -3,122 +3,127 @@ set -euo pipefail |
| 1275 | |
| 1276 | # command_not_found_handle is a noop function that prevents printing error messages if WSL interop is disabled. |
| 1277 | function command_not_found_handle() { |
| 1278 | - : |
| 1279 | + : |
| 1280 | } |
| 1281 | |
| 1282 | # get_first_interactive_uid returns first interactive non system user uid with uid >=1000. |
| 1283 | function get_first_interactive_uid() { |
| 1284 | - getent passwd | egrep -v '/nologin|/false|/sync' | sort -t: -k3,3n | awk -F: '$3 >= 1000 { print $3; exit }' |
| 1285 | + getent passwd | grep -E -v '/nologin|/false|/sync' | sort -t: -k3,3n | awk -F: '$3 >= 1000 { print $3; exit }' |
| 1286 | } |
| 1287 | |
| 1288 | # create_regular_user prompts user for a username and assign default WSL permissions. |
| 1289 | # First argument is the prefilled username. |
| 1290 | function create_regular_user() { |
| 1291 | - local default_username="${1}" |
| 1292 | - |
| 1293 | - local valid_username_regex='^[a-z_][a-z0-9_-]*$' |
| 1294 | - local DEFAULT_GROUPS='adm,cdrom,sudo,dip,plugdev' |
| 1295 | - |
| 1296 | - # Filter the prefilled username to remove invalid characters. |
| 1297 | - default_username=$(echo "${default_username}" | sed 's/[^a-z0-9_-]//g') |
| 1298 | - # It should start with a character or _. |
| 1299 | - default_username=$(echo "${default_username}" | sed 's/^[^a-z_]//') |
| 1300 | - |
| 1301 | - # Ensure a valid username |
| 1302 | - while true; do |
| 1303 | - # Prefill the prompt with the Windows username. |
| 1304 | - read -e -p "Create a default Unix user account: " -i "${default_username}" username |
| 1305 | - |
| 1306 | - # Validate the username. |
| 1307 | - if [[ ! "${username}" =~ ${valid_username_regex} ]]; then |
| 1308 | - echo "Invalid username. A valid username must start with a lowercase letter or underscore, and can contain lowercase letters, digits, underscores, and dashes." |
| 1309 | - continue |
| 1310 | - fi |
| 1311 | - |
| 1312 | - # Create the user and change its default groups. |
| 1313 | - if ! /usr/sbin/adduser --quiet --gecos '' "${username}"; then |
| 1314 | - echo "Failed to create user '${username}'. Please choose a different name." |
| 1315 | - continue |
| 1316 | - fi |
| 1317 | - |
| 1318 | - if ! /usr/sbin/usermod "${username}" -aG "${DEFAULT_GROUPS}"; then |
| 1319 | - echo "Failed to add '${username}' to default groups. Attempting cleanup." |
| 1320 | - /usr/sbin/deluser --quiet "${username}" |
| 1321 | - continue |
| 1322 | - fi |
| 1323 | - |
| 1324 | - break |
| 1325 | - done |
| 1326 | + local default_username="${1}" |
| 1327 | + |
| 1328 | + local valid_username_regex='^[a-z_][a-z0-9_-]*$' |
| 1329 | + local DEFAULT_GROUPS='adm,cdrom,sudo,dip,plugdev' |
| 1330 | + |
| 1331 | + # Filter the prefilled username to remove invalid characters. |
| 1332 | + # Remove all characters except a-z, 0-9, _ and - |
| 1333 | + default_username="${default_username//[^a-z0-9_-]/}" |
| 1334 | + # Remove leading character if not a-z or _ |
| 1335 | + default_username="${default_username#[!a-z_]}" |
| 1336 | + |
| 1337 | + # Ensure a valid username |
| 1338 | + while true; do |
| 1339 | + # Prefill the prompt with the Windows username. |
| 1340 | + read -er -p "Create a default Unix user account: " -i "${default_username}" username |
| 1341 | + |
| 1342 | + # Validate the username. |
| 1343 | + if [[ ! "${username}" =~ ${valid_username_regex} ]]; then |
| 1344 | + echo "Invalid username. A valid username must start with a lowercase letter or underscore, and can contain lowercase letters, digits, underscores, and dashes." |
| 1345 | + continue |
| 1346 | + fi |
| 1347 | + |
| 1348 | + # Create the user and change its default groups. |
| 1349 | + if ! /usr/sbin/adduser --quiet --gecos '' "${username}"; then |
| 1350 | + echo "Failed to create user '${username}'. Please choose a different name." |
| 1351 | + continue |
| 1352 | + fi |
| 1353 | + |
| 1354 | + if ! /usr/sbin/usermod "${username}" -aG "${DEFAULT_GROUPS}"; then |
| 1355 | + echo "Failed to add '${username}' to default groups. Attempting cleanup." |
| 1356 | + /usr/sbin/deluser --quiet "${username}" |
| 1357 | + continue |
| 1358 | + fi |
| 1359 | + |
| 1360 | + break |
| 1361 | + done |
| 1362 | } |
| 1363 | |
| 1364 | # set_user_as_default sets the given username as the default user in the wsl.conf configuration. |
| 1365 | # It will only set it if there is no existing default under the [user] section. |
| 1366 | function set_user_as_default() { |
| 1367 | - local username="${1}" |
| 1368 | + local username="${1}" |
| 1369 | |
| 1370 | - local wsl_conf="/etc/wsl.conf" |
| 1371 | - touch "${wsl_conf}" |
| 1372 | + local wsl_conf="/etc/wsl.conf" |
| 1373 | + touch "${wsl_conf}" |
| 1374 | |
| 1375 | - # Append [user] section with default if they don't exist. |
| 1376 | - if ! grep -q "^\[user\]" "${wsl_conf}"; then |
| 1377 | - echo -e "\n[user]\ndefault=${username}" >> "${wsl_conf}" |
| 1378 | - return |
| 1379 | - fi |
| 1380 | + # Append [user] section with default if they don't exist. |
| 1381 | + if ! grep -q "^\[user\]" "${wsl_conf}"; then |
| 1382 | + echo -e "\n[user]\ndefault=${username}" >>"${wsl_conf}" |
| 1383 | + return |
| 1384 | + fi |
| 1385 | |
| 1386 | - # If default is missing from the user section, append it to it. |
| 1387 | - if ! sed -n '/^\[user\]/,/^\[/{/^\s*default\s*=/p}' "${wsl_conf}" | grep -q .; then |
| 1388 | - sed -i '/^\[user\]/a\default='"${username}" "${wsl_conf}" |
| 1389 | - fi |
| 1390 | + # If default is missing from the user section, append it to it. |
| 1391 | + if ! sed -n '/^\[user\]/,/^\[/{/^\s*default\s*=/p}' "${wsl_conf}" | grep -q .; then |
| 1392 | + sed -i '/^\[user\]/a\default='"${username}" "${wsl_conf}" |
| 1393 | + fi |
| 1394 | } |
| 1395 | |
| 1396 | # powershell_env outputs the contents of PowerShell.exe environment variables $Env:<ARG> |
| 1397 | # encoded in UTF-8. |
| 1398 | function powershell_env() { |
| 1399 | - local var="$1" |
| 1400 | - if [ "$#" != 1 ]; then |
| 1401 | - echo "powershell_env: expected 1 argument, got $# ." |
| 1402 | - return 1 |
| 1403 | - fi |
| 1404 | - |
| 1405 | - local ret=$(powershell.exe -NoProfile -Command '& { |
| 1406 | + local var="$1" |
| 1407 | + if [ "$#" != 1 ]; then |
| 1408 | + echo "powershell_env: expected 1 argument, got $# ." |
| 1409 | + return 1 |
| 1410 | + fi |
| 1411 | + |
| 1412 | + local ret |
| 1413 | + # shellcheck disable=SC2016 # Intentional due to how we use powershell |
| 1414 | + ret=$(powershell.exe -NoProfile -Command '& { |
| 1415 | [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 |
| 1416 | $Env:'"${var}"'}') 2>/dev/null || true |
| 1417 | - # strip control chars like \r and \n |
| 1418 | - ret="${ret%%[[:cntrl:]]}" |
| 1419 | - echo "$ret" |
| 1420 | + # strip control chars like \r and \n |
| 1421 | + ret="${ret%%[[:cntrl:]]}" |
| 1422 | + echo "$ret" |
| 1423 | } |
| 1424 | |
| 1425 | # install_ubuntu_font copies the Ubuntu font into Windows filesystem and register it for the current Windows user. |
| 1426 | function install_ubuntu_font() { |
| 1427 | - local local_app_data=$(powershell_env "LocalAppData") |
| 1428 | - if [ -z "${local_app_data}" ]; then |
| 1429 | - return |
| 1430 | - fi |
| 1431 | - |
| 1432 | - local_app_data=$(wslpath -au "${local_app_data}") 2>/dev/null || true |
| 1433 | - local fonts_dir="${local_app_data}/Microsoft/Windows/Fonts" |
| 1434 | - local font="UbuntuMono[wght].ttf" |
| 1435 | - mkdir -p "${fonts_dir}" 2>/dev/null |
| 1436 | - if [ -f "${fonts_dir}/${font}" ]; then |
| 1437 | - return |
| 1438 | - fi |
| 1439 | - |
| 1440 | - cp "/usr/share/fonts/truetype/ubuntu/${font}" "${fonts_dir}" 2>/dev/null || true |
| 1441 | - |
| 1442 | - # Register the font for the current user. |
| 1443 | - local dst=$(wslpath -aw "${fonts_dir}/${font}") 2>/dev/null || true |
| 1444 | - powershell.exe -NoProfile -Command '& { |
| 1445 | + local local_app_data |
| 1446 | + local_app_data=$(powershell_env "LocalAppData") |
| 1447 | + if [ -z "${local_app_data}" ]; then |
| 1448 | + return |
| 1449 | + fi |
| 1450 | + |
| 1451 | + local_app_data=$(wslpath -au "${local_app_data}") 2>/dev/null || true |
| 1452 | + local fonts_dir="${local_app_data}/Microsoft/Windows/Fonts" |
| 1453 | + local font="UbuntuMono[wght].ttf" |
| 1454 | + mkdir -p "${fonts_dir}" 2>/dev/null |
| 1455 | + if [ -f "${fonts_dir}/${font}" ]; then |
| 1456 | + return |
| 1457 | + fi |
| 1458 | + |
| 1459 | + cp "/usr/share/fonts/truetype/ubuntu/${font}" "${fonts_dir}" 2>/dev/null || true |
| 1460 | + |
| 1461 | + # Register the font for the current user. |
| 1462 | + local dst |
| 1463 | + dst=$(wslpath -aw "${fonts_dir}/${font}") 2>/dev/null || true |
| 1464 | + # shellcheck disable=SC2016 # Intentional due to how we use powershell |
| 1465 | + powershell.exe -NoProfile -Command '& { |
| 1466 | $null = New-Item -Path "HKCU:\Software\Microsoft\Windows NT\CurrentVersion" -Name "Fonts" 2>$null; |
| 1467 | $null = New-ItemProperty -Name "Ubuntu Mono (TrueType)" -Path "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts" -PropertyType string -Value "'"${dst}"'" 2>$null; |
| 1468 | - }' || true |
| 1469 | + }' >/dev/null || true |
| 1470 | } |
| 1471 | |
| 1472 | - |
| 1473 | echo "Provisioning the new WSL instance $WSL_DISTRO_NAME" |
| 1474 | echo "This might take a while..." |
| 1475 | |
| 1476 | # Read the Windows user name. |
| 1477 | -win_username=$(powershell_env "UserName") |
| 1478 | +win_username=$(powershell_env "UserName.ToLower()") |
| 1479 | # replace any potential whitespaces with underscores. |
| 1480 | win_username="${win_username// /_}" |
| 1481 | |
| 1482 | @@ -126,23 +131,26 @@ install_ubuntu_font |
| 1483 | |
| 1484 | # Wait for cloud-init to finish if systemd and its service is enabled |
| 1485 | # by running the script located at the same dir as this one. |
| 1486 | -this_dir=$(dirname "$(realpath $0)") |
| 1487 | +this_dir=$(dirname "$(realpath "$0")") |
| 1488 | source "${this_dir}/wait-for-cloud-init" |
| 1489 | |
| 1490 | # Check if there is a pre-provisioned users (pre-baked on the rootfs or created by cloud-init). |
| 1491 | user_id=$(get_first_interactive_uid) |
| 1492 | |
| 1493 | # If we don’t have a non system user, let’s create it. |
| 1494 | -if [ -z "${user_id}" ] ; then |
| 1495 | - create_regular_user "${win_username}" |
| 1496 | - |
| 1497 | - user_id=$(get_first_interactive_uid) |
| 1498 | - if [ -z "${user_id}" ] ; then |
| 1499 | - echo 'Failed to create a regular user account' |
| 1500 | - exit 1 |
| 1501 | - fi |
| 1502 | +if [ -z "${user_id}" ]; then |
| 1503 | + create_regular_user "${win_username}" |
| 1504 | + |
| 1505 | + user_id=$(get_first_interactive_uid) |
| 1506 | + if [ -z "${user_id}" ]; then |
| 1507 | + echo 'Failed to create a regular user account' |
| 1508 | + exit 1 |
| 1509 | + fi |
| 1510 | fi |
| 1511 | |
| 1512 | # Set the newly created user as the WSL default. |
| 1513 | username=$(id -un "${user_id}") |
| 1514 | set_user_as_default "${username}" |
| 1515 | + |
| 1516 | +# Start Ubuntu Insights consent script |
| 1517 | +"${this_dir}/ubuntu-insights.sh" |

This is a bit awkward of a PR, because it wsl-setup is native, and in it's actual git repo this is already merged... so there is not much to "review" at this point.
If you just want the git-ubuntu history to be aligned with upstream, fair enough. Otherwise, there's not too much reason to make git-ubuntu PRs for this package in the future.
In any case, sponsoring.