From 00fe1b3dad1e64ca95fb25736e2c8ab1782936fe Mon Sep 17 00:00:00 2001 From: Raki <46581776+mdrakiburrahman@users.noreply.github.com> Date: Fri, 23 Oct 2026 10:42:03 -0400 Subject: [PATCH] chore(devbox): one-shot Taskfile to recreate WSL and open the devcontainer (#315) * chore(devbox): one-shot Taskfile to recreate WSL and open the devcontainer - contrib/bootstrap-dev-env.yaml: default recreates WSL and clones the current branch, then runs up; up bootstraps the WSL host, starts the devcontainer with the labels VS Code uses, and opens VS Code in it. - contrib/bootstrap-dev-env.ps1: no prompts; picks the drive with the most free space, creates the default Linux user, and clones the current branch into /workspaces/dbt-fabricspark with Git Credential Manager. - contrib/bootstrap-dev-env.sh: fail fast; az login completes through the signed-in Windows browser with single sign-on; ACR login only uses ACR_PASSWORD, since the image pulls anonymously. - CONTRIBUTING.md: replace the manual WSL steps with the Task command. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cc682684-642c-4667-a3e0-6bad55f87574 * fix(devbox): clone from the WSL home directory Run from a git worktree, WSL git found the Windows worktree's .git file in the current directory and failed before cloning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cc682684-642c-4667-a3e0-6bad55f87574 * style(devbox): drop a stray space Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cc682684-642c-4667-a3e0-6bad55f87574 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cc682684-642c-4667-a3e0-6bad55f87574 --- CONTRIBUTING.md | 42 +++++--------------- contrib/bootstrap-dev-env.ps1 | 70 ++++++++++++++++++++++------------ contrib/bootstrap-dev-env.sh | 29 +++++++------- contrib/bootstrap-dev-env.yaml | 26 +++++++++++++ 4 files changed, 93 insertions(+), 74 deletions(-) create mode 100644 contrib/bootstrap-dev-env.yaml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b809a75..6b40a2b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,47 +18,23 @@ Therefore, if the tests pass locally, they are highly likely to pass in CI as we winget install -e --id Microsoft.VisualStudioCode ``` -1. Get a fresh new WSL machine up: +1. Clone your fork on Windows, then check out and push the branch you want to work on: ```powershell - $GIT_ROOT = git rev-parse --show-toplevel - & "$GIT_ROOT\contrib\bootstrap-dev-env.ps1" + git clone https://github.com//dbt-fabricspark.git ``` -1. Clone the repo, and open VSCode in it: +1. From an **elevated PowerShell 7** prompt in the checkout, install [Task](https://taskfile.dev) and run [`contrib/bootstrap-dev-env.yaml`](contrib/bootstrap-dev-env.yaml): - ```bash - sudo mkdir -p /workspaces && sudo chmod 777 /workspaces && cd /workspaces + > ⚠️ This **deletes every WSL distribution** on the machine before recreating `Ubuntu-24.04`. Push any work inside WSL first. - read -p "Enter your name (e.g. 'FirstName LastName'): " user_name - read -p "Enter your email (e.g. 'your-alias@foo.com'): " user_email - read -p "Enter your git fork (e.g. 'https://github.com/microsoft/dbt-fabricspark.git'): " git_fork_url - read -p "Enter the existing branch to switch to: (e.g. 'main'): " branch_name - - git config --global user.name "$user_name" - git config --global user.email "$user_email" - git clone "$git_fork_url" - - cd /workspaces/dbt-fabricspark - git checkout "$branch_name" - - code . + ```powershell + winget install Task.Task + $env:PATH = [Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'User') + task --taskfile contrib/bootstrap-dev-env.yaml ``` -1. Run the bootstrapper script, that installs all tools idempotently: - - ```bash - GIT_ROOT=$(git rev-parse --show-toplevel) - chmod +x ${GIT_ROOT}/contrib/bootstrap-dev-env.sh && ${GIT_ROOT}/contrib/bootstrap-dev-env.sh - ``` - -1. Launch the devcontainer: - - ```bash - cd /workspaces/dbt-fabricspark - HEX=$(printf '%s' "$(wslpath -w .)" | xxd -ps -c 256) - code --folder-uri "vscode-remote://dev-container+${HEX}/workspaces/dbt-fabricspark" - ``` + Without any prompts, this recreates WSL on the drive with the most free space and clones your current branch into `/workspaces/dbt-fabricspark`. It then runs [`contrib/bootstrap-dev-env.sh`](contrib/bootstrap-dev-env.sh), which signs in the Azure CLI through your signed-in browser with single sign-on (no clicks), then starts the devcontainer and opens it in VS Code. After a restart, run `task --taskfile contrib/bootstrap-dev-env.yaml up` instead; it keeps WSL and reopens the devcontainer. 1. Install recommended developer tooling (optional): diff --git a/contrib/bootstrap-dev-env.ps1 b/contrib/bootstrap-dev-env.ps1 index da0b0bf..0dcdf58 100644 --- a/contrib/bootstrap-dev-env.ps1 +++ b/contrib/bootstrap-dev-env.ps1 @@ -5,6 +5,7 @@ .NOTES + - Non-interactive: recreates WSL and clones -Branch (default: this checkout's branch) into /workspaces/dbt-fabricspark. - The script uninstalls Docker Desktop as it interferes with WSL2. - Must be run as Administrator in PowerShell 7+. @@ -12,6 +13,11 @@ #Requires -RunAsAdministrator +param( + [string]$Branch = (git -C $PSScriptRoot rev-parse --abbrev-ref HEAD), + [string]$LinuxUser = ($env:USERNAME.ToLower() -replace '[^a-z0-9_-]', '') +) + code --install-extension ms-vscode-remote.remote-wsl code --install-extension ms-vscode-remote.remote-containers @@ -20,6 +26,21 @@ if ($PSVersionTable.PSVersion.Major -lt 7) { exit 1 } +# Git Credential Manager lets git inside WSL reuse this checkout's sign-in; install it if Git for Windows lacks it. +if (-not (git credential-manager --version 2>$null)) { + winget install -e --id Git.GCM --silent --accept-package-agreements --accept-source-agreements --disable-interactivity + $env:PATH = [Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'User') +} +$gcm = @((Get-Command git-credential-manager -ErrorAction SilentlyContinue).Source, "$(Split-Path (Split-Path (git --exec-path)))\bin\git-credential-manager.exe") | + Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1 + +# Fail before deleting WSL if the branch can't be cloned without a prompt. +$origin = git -C $PSScriptRoot remote get-url origin +$env:GCM_INTERACTIVE = 'never' +$env:GIT_TERMINAL_PROMPT = '0' +git ls-remote --exit-code --heads $origin $Branch | Out-Null +if ($LASTEXITCODE) { throw "Cannot read $Branch from $origin without a prompt; push it, or run 'git fetch' in $PSScriptRoot once to sign in, then rerun." } + # See: https://github.com/microsoft/vscode-remote-release/issues/11480 $sshDir = Join-Path $env:USERPROFILE ".ssh" $knownHosts = Join-Path $sshDir "known_hosts" @@ -55,6 +76,12 @@ if ($pkg) { } Remove-Item -Path "$env:PROGRAMFILES\Docker", "$env:PROGRAMDATA\Docker*", "$env:LOCALAPPDATA\Docker*", "$env:APPDATA\Docker*" -Recurse -Force -ErrorAction SilentlyContinue +# VS Code windows attached to WSL go stale when it is recreated, and VS Code would focus them instead of reconnecting. +Add-Type -AssemblyName UIAutomationClient +[Windows.Automation.AutomationElement]::RootElement.FindAll('Children', [Windows.Automation.Condition]::TrueCondition) | + Where-Object { $_.Current.Name -match '\[(Dev Container|WSL)\b.*Visual Studio Code' } | + ForEach-Object { Write-Host "Closing VS Code window: $($_.Current.Name)"; $_.GetCurrentPattern([Windows.Automation.WindowPattern]::Pattern).Close() } + $distros = (wsl -l -q) | Where-Object { $_ -and $_.Trim() } | ForEach-Object { $_.Trim().Replace("`0", "") } | Where-Object { $_ } foreach ($distro in $distros) { Write-Host "Unregistering WSL distro: $distro" @@ -70,15 +97,12 @@ $swap=[math]::Floor($memGB/4) if ($cpu -lt $RECOMMENDED_CORES) { Write-Host "WARNING: This machine has $cpu cores, which is below the recommended $RECOMMENDED_CORES cores." -ForegroundColor DarkYellow - $requiredResponse = "I am OK with having a subpar development experience" - do { - $response = Read-Host "Please type '$requiredResponse' **EXACTLY** as is to continue" - } while ($response -ne $requiredResponse) } else { Write-Host "(detected $cpu cores, ${memGB}GB RAM)" -ForegroundColor Green } $driveOptions = @(Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | + Where-Object { $_.VolumeName -notmatch 'Temporary' } | Sort-Object -Property FreeSpace -Descending | ForEach-Object { [PSCustomObject]@{ @@ -92,28 +116,12 @@ if ($driveOptions.Count -eq 0) { exit 1 } -Write-Host "" -Write-Host "Choose a drive to store the WSL VHD (the repo recommends at least ${RECOMMENDED_FREE_GB} GB free):" -ForegroundColor Cyan -for ($i = 0; $i -lt $driveOptions.Count; $i++) { - $opt = $driveOptions[$i] - Write-Host (" [{0}] {1} ({2} GB free)" -f ($i + 1), $opt.Path, $opt.FreeGB) -} - -do { - $pick = Read-Host "Pick a drive number [1-$($driveOptions.Count)]" -} while (-not ($pick -as [int]) -or [int]$pick -lt 1 -or [int]$pick -gt $driveOptions.Count) - -$wslLocation = $driveOptions[[int]$pick - 1] +$wslLocation = $driveOptions[0] if ($wslLocation.FreeGB -lt $RECOMMENDED_FREE_GB) { Write-Host "WARNING: $($wslLocation.Path) has only $($wslLocation.FreeGB) GB free, which is below the recommended ${RECOMMENDED_FREE_GB} GB." -ForegroundColor DarkYellow - $requiredResponse = "I am OK with having my WSL crash when my hard disk fills up" - do { - $response = Read-Host "Please type '$requiredResponse' **EXACTLY** as is to continue" - } while ($response -ne $requiredResponse) -} else { - Write-Host "Using $($wslLocation.Path) for the WSL VHD." -ForegroundColor Green } +Write-Host "Using $($wslLocation.Path), the drive with the most free space, for the WSL VHD." -ForegroundColor Green New-Item -ItemType Directory -Path $wslLocation.Path -Force | Out-Null @@ -158,13 +166,14 @@ memory=${memGB}GB processors=$cpu swap=${swap}GB networkingMode=NAT + +[general] +instanceIdleTimeout=-1 "@ | Set-Content -Path "$env:USERPROFILE\.wslconfig" Write-Host "Restarting WSL to apply settings" wsl --shutdown -winget install -e --id Microsoft.GitCredentialManagerCore - Write-Host "Updating WSL (the --location flag requires WSL 2.4.4+)" wsl --update @@ -189,4 +198,15 @@ if ($wslVersion -lt $minWslVersion) { Write-Host "WSL version $wslVersion detected." -ForegroundColor Green Write-Host "Installing Ubuntu to $($wslLocation.Path)" -wsl --install -d Ubuntu-24.04 --location $wslLocation.Path \ No newline at end of file +wsl --install -d Ubuntu-24.04 --location $wslLocation.Path --no-launch --web-download +if ($LASTEXITCODE) { throw "wsl --install failed with exit code $LASTEXITCODE." } + +Write-Host "Creating default Linux user $LinuxUser with passwordless sudo" +wsl -d Ubuntu-24.04 -u root --exec bash -c 'useradd -m -s /bin/bash -G sudo "$1" && echo "$1 ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$1" && install -d -o "$1" /workspaces' bash $LinuxUser +if ($LASTEXITCODE) { throw "Could not create Linux user $LinuxUser." } +wsl --manage Ubuntu-24.04 --set-default-user $LinuxUser +if ($LASTEXITCODE) { throw "Could not make $LinuxUser the default WSL user." } + +Write-Host "Cloning $Branch into /workspaces/dbt-fabricspark" +wsl -d Ubuntu-24.04 --cd ~ --exec bash -c 'git config --global user.name "$1" && git config --global user.email "$2" && git config --global credential.helper "$(printf %q "$(wslpath -u "$5")")" && GIT_TERMINAL_PROMPT=0 git clone --branch "$3" "$4" /workspaces/dbt-fabricspark' bash (git -C $PSScriptRoot config user.name) (git -C $PSScriptRoot config user.email) $Branch $origin $gcm +if ($LASTEXITCODE) { throw "Could not clone $Branch into /workspaces/dbt-fabricspark." } \ No newline at end of file diff --git a/contrib/bootstrap-dev-env.sh b/contrib/bootstrap-dev-env.sh index 09cef11..c07ec55 100755 --- a/contrib/bootstrap-dev-env.sh +++ b/contrib/bootstrap-dev-env.sh @@ -9,6 +9,8 @@ # --------------------------------------------------------------------------------------- # +set -eo pipefail + REPO_ROOT=$(git rev-parse --show-toplevel) ACR_NAME="dbtfabric" @@ -49,23 +51,16 @@ echo "docker is installed, restarting..." sudo systemctl restart docker sudo chmod 666 /var/run/docker.sock +sudo usermod -aG docker "$USER" docker container ls docker ps -q | xargs -r docker kill if grep -q "$ACR_URL" ~/.docker/config.json 2>/dev/null; then echo "Already logged in to ${ACR_URL}" - else - if [ -n "$ACR_PASSWORD" ]; then - docker_password="$ACR_PASSWORD" - else - read -sp "If you are a Microsoft Employee and you plan on contributing to the Devcontainer image, please ping @mdrrahman and enter Docker Admin password for ${ACR_URL} - otherwise, leave blank and press [ENTER]: " docker_password - echo - fi - if [ -z "$docker_password" ]; then - echo "You left the password empty, skipping docker login" - else - echo "$docker_password" | docker login "$ACR_URL" --username "$ACR_NAME" --password-stdin - fi +elif [ -z "$ACR_PASSWORD" ]; then + echo "ACR_PASSWORD is not set, skipping docker login; only devcontainer image publishers need it" +else + echo "$ACR_PASSWORD" | docker login "$ACR_URL" --username "$ACR_NAME" --password-stdin fi docker pull "$(jq -r .image "$REPO_ROOT/.devcontainer/devcontainer.json")" @@ -79,7 +74,7 @@ else echo "WSL npm is available." fi -AZ_PATH=$(which az 2>/dev/null) +AZ_PATH=$(which az 2>/dev/null || true) if [[ -z "$AZ_PATH" || "$AZ_PATH" == *"/mnt/c"* ]]; then echo "Native Linux Azure CLI not found, installing..." curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash @@ -88,10 +83,12 @@ if [[ -z "$AZ_PATH" || "$AZ_PATH" == *"/mnt/c"* ]]; then else echo "Native Linux Azure CLI already installed at: $AZ_PATH" fi -az account get-access-token --query "expiresOn" -o tsv >/dev/null 2>&1 -if [[ $? -ne 0 ]]; then +if ! az account get-access-token --query "expiresOn" -o tsv >/dev/null 2>&1; then echo "az is not logged in, logging in..." - az login >/dev/null + # prompt=none lets the signed-in Windows browser complete the login with single sign-on, without clicks. + # Login experience v2 off skips the subscription picker that waits for input in a terminal. + BROWSER='bash -c "exec wslview \"${0/prompt=select_account/prompt=none}\"" %s' AZURE_CORE_LOGIN_EXPERIENCE_V2=off \ + az login --output none fi cd "$REPO_ROOT" diff --git a/contrib/bootstrap-dev-env.yaml b/contrib/bootstrap-dev-env.yaml new file mode 100644 index 0000000..2ed403d --- /dev/null +++ b/contrib/bootstrap-dev-env.yaml @@ -0,0 +1,26 @@ +version: '3' + +vars: + DISTRO: Ubuntu-24.04 + REPO: /workspaces/dbt-fabricspark + UNC: \\wsl.localhost\{{.DISTRO}}\workspaces\dbt-fabricspark + +tasks: + default: + desc: Delete and recreate WSL, clone the current branch into it, then run the up task + platforms: [windows] + cmds: + - pwsh -NoProfile -NonInteractive -File bootstrap-dev-env.ps1 + - task: up + + up: + desc: Bootstrap the existing WSL clone, start its devcontainer, and open VS Code (use after a restart) + platforms: [windows] + cmds: + - wsl -d {{.DISTRO}} --cd {{.REPO}} --exec ./contrib/bootstrap-dev-env.sh + # These labels match the ones VS Code uses, so VS Code attaches to this container instead of creating another. + - >- + wsl -d {{.DISTRO}} --cd {{.REPO}} --exec npx --no-install devcontainer up --workspace-folder {{.REPO}} + --id-label 'devcontainer.local_folder={{.UNC}}' + --id-label 'devcontainer.config_file={{.REPO}}/.devcontainer/devcontainer.json' + - code --folder-uri 'vscode-remote://dev-container+{{printf "%x" .UNC}}{{.REPO}}'