Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7.0.1
- name: Setup WSL
uses: Particular/setup-wsl-action@v1.0.0
- name: Run
uses: ./
with:
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@

This action handles the setup and teardown of a PostgreSQL database.

## Prerequisites

This action does **not** provision WSL or Docker itself. On **Windows** runners it requires [setup-wsl-action](https://github.com/Particular/setup-wsl-action) to run **first** in the same job — that action provisions WSL2 + Docker, keeps the instance alive, and exports the `WSL_DISTRIBUTION`, `WSL_IP`, and `WSL_TOOLS_MODULE_PATH` environment variables this action relies on. On **Linux** runners setup-wsl-action is a no-op but should still be included so the workflow is uniform.

If setup-wsl-action has not run, the action fails fast with a clear error.

## Usage

See [action.yml](action.yml)

```yaml
steps:
- name: Setup WSL
uses: Particular/setup-wsl-action@v1
- name: Setup Postgres
uses: Particular/setup-postgres-action@v1.0.0
with:
Expand All @@ -25,7 +33,7 @@ For logging into a container registry when running on Windows:
* `registry-login-server` defaults to `index.docker.io` and is not required if logging into Docker Hub.
* `registry-username` and `registry-password` are optional and will result in pulling the container anonymously if omitted.

On Linux runners the PostgreSQL container runs directly through Docker. On Windows runners the Linux PostgreSQL container runs inside WSL2 (the action installs and starts Docker inside the WSL distribution), so no Azure Container Instances are required.
On Linux runners the PostgreSQL container runs directly through Docker. On Windows runners the Linux PostgreSQL container runs inside WSL2 provisioned by [setup-wsl-action](https://github.com/Particular/setup-wsl-action) — run it first, see [Prerequisites](#prerequisites).

## License

Expand Down Expand Up @@ -92,3 +100,5 @@ To test the cleanup action set the required environment variables and execute `c
$Env:RUNNER_OS=Windows
.\cleanup.ps1 -ContainerName psw-postgres-1
```

> Running `setup.ps1`/`cleanup.ps1` directly on Windows requires `WSL_TOOLS_MODULE_PATH` to point at setup-wsl-action's `WslTools` module (set it by running setup-wsl-action first).
12 changes: 9 additions & 3 deletions cleanup.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
param (
[string]$ContainerName
)

# The Windows container removal runs through the WslTools module (Invoke-Wsl), which
# setup-wsl-action exports at WSL_TOOLS_MODULE_PATH. Import it here with a clear guard.
if (-not $Env:WSL_TOOLS_MODULE_PATH) {
throw "This action requires Particular/setup-wsl-action to run first — it provisions WSL/Docker and exports the WslTools module at WSL_TOOLS_MODULE_PATH."
}
Import-Module $Env:WSL_TOOLS_MODULE_PATH -Force

$runnerOs = $Env:RUNNER_OS ?? "Linux"

if ($runnerOs -eq "Linux") {
Expand All @@ -11,10 +19,8 @@ if ($runnerOs -eq "Linux") {
docker rm $ContainerName
}
elseif ($runnerOs -eq "Windows") {
$wslDistribution = $Env:WSL_DISTRIBUTION_OVERRIDE ?? "Debian"

Write-Output "Removing WSL Docker container $ContainerName"
wsl.exe --distribution $wslDistribution --user root -- bash -c "docker rm --force ${ContainerName} 2>/dev/null || true"
Invoke-Wsl -Command "docker rm --force ${ContainerName} 2>/dev/null || true"
}
else {
Write-Output "$runnerOs not supported"
Expand Down
14 changes: 0 additions & 14 deletions modules/WslTools/WslTools.psd1

This file was deleted.

17 changes: 0 additions & 17 deletions modules/WslTools/WslTools.psm1

This file was deleted.

83 changes: 13 additions & 70 deletions setup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ $runnerOs = $Env:RUNNER_OS ?? "Linux"

$env:PGPASSWORD = $password

# Import local module containing Invoke-Wsl
$modulePath = Join-Path $PSScriptRoot 'modules' 'WslTools'
Import-Module $modulePath -Force
# Import the WslTools module (Invoke-Wsl) exported by setup-wsl-action. The guard gives a
# clear error if setup-wsl-action hasn't run, since this action no longer provisions WSL itself.
if (-not $Env:WSL_TOOLS_MODULE_PATH) {
throw "This action requires Particular/setup-wsl-action to run first — it provisions WSL/Docker and exports the WslTools module at WSL_TOOLS_MODULE_PATH."
}
Import-Module $Env:WSL_TOOLS_MODULE_PATH -Force

if ($runnerOs -eq "Linux") {
Write-Output "Running Postgres in container $($ContainerName) using Docker"
Expand All @@ -33,45 +36,15 @@ elseif ($runnerOs -eq "Windows") {
# psql is not in PATH on Windows
$Env:PATH = $Env:PATH + ';' + $Env:PGBIN

$wslDistribution = $Env:WSL_DISTRIBUTION_OVERRIDE ?? "Debian"

# Constrain the WSL2 VM (memory) and keep it from being shut down when idle, so that Docker
# and the container are not torn down between this setup step and the test step. Only write
# when the file is absent so local development configurations are left untouched.
$wslConfigPath = Join-Path $Env:USERPROFILE ".wslconfig"
if (-not (Test-Path $wslConfigPath)) {
$wslMemory = $Env:WSL_MEMORY_OVERRIDE ?? "3GB"
Write-Output "Writing $wslConfigPath (memory=$wslMemory) to constrain the WSL2 VM"
Set-Content -Path $wslConfigPath -Value "[wsl2]`nmemory=$wslMemory`nvmIdleTimeout=-1" -Encoding ASCII
}

Write-Output "::group::Preparing WSL ($wslDistribution)"

wsl.exe --set-default-version 2 | Out-Null

# Install the distribution if it is not already registered.
$installedDistributions = ((wsl.exe --list --quiet) -replace "`0", "") |
ForEach-Object { $_.Trim() } |
Where-Object { $_ -ne "" }
# WSL and Docker were provisioned by setup-wsl-action. Read the distribution and the WSL
# VM IP from the environment it exported rather than provisioning or detecting them here.
$wslDistribution = $Env:WSL_DISTRIBUTION
$ipAddress = $Env:WSL_IP

if ($installedDistributions -notcontains $wslDistribution) {
Write-Output "Installing $wslDistribution in WSL"
wsl.exe --install $wslDistribution --web-download --no-launch
if ($LASTEXITCODE -ne 0) {
throw "Failed to install $wslDistribution in WSL"
}
}
else {
Write-Output "$wslDistribution is already installed"
if (-not $ipAddress) {
throw "WSL_IP is not set. Run Particular/setup-wsl-action before this action."
}

# Ensure Docker is installed inside the WSL distribution.
Write-Output "Ensuring Docker is installed inside $wslDistribution"
Invoke-Wsl -Distribution $wslDistribution -CheckExitCode -Command "command -v docker >/dev/null 2>&1 || { apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --yes docker.io; }"

# Start the Docker daemon via systemd when available, otherwise via the SysV service.
Write-Output "Starting Docker daemon inside $wslDistribution"
Invoke-Wsl -Distribution $wslDistribution -CheckExitCode -Command "docker info >/dev/null 2>&1 || { if [ -d /run/systemd/system ]; then systemctl start docker; else service docker start; fi; }"
Write-Output "WSL address: $ipAddress"

# Optionally log in to the container registry to avoid rate limits when pulling.
if ($registryUser -and $registryPass) {
Expand All @@ -87,39 +60,9 @@ elseif ($runnerOs -eq "Windows") {
Write-Output "Using anonymous credentials"
}

# Keep the WSL instance alive for the rest of the job. WSL terminates an instance when no
# processes remain under its init (PID 2); a plain background process (e.g. sleep) does not
# prevent this, but a D-Bus session bus launched through `wsl --exec` does. vmIdleTimeout
# above covers the VM-level idle timeout; this covers the separate instance-level shutdown.
# See https://github.com/microsoft/WSL/issues/10138 and
# https://blog.lecoteauverdoyant.co.uk/articles/wsl-keep-alive.html
Write-Output "Starting a D-Bus session to keep the WSL instance alive for the job"
# dbus-launch ships in the dbus-x11 package (not dbus). Verify it is present afterwards so a
# packaging change can never silently leave the instance unguarded again.
Invoke-Wsl -Distribution $wslDistribution -CheckExitCode -Command "command -v dbus-launch >/dev/null 2>&1 || { apt-get update && apt-get install -y dbus-x11; }; command -v dbus-launch >/dev/null 2>&1 || { echo 'dbus-launch is unavailable after installing dbus-x11' >&2; exit 1; }"
wsl.exe --distribution $wslDistribution --user root --exec /usr/bin/dbus-launch true
if ($LASTEXITCODE -ne 0) {
throw "dbus-launch keep-alive failed with exit code $LASTEXITCODE"
}

Write-Output "::endgroup::"

Write-Output "::group::Starting PostgreSQL container"
Invoke-Wsl -Distribution $wslDistribution -CheckExitCode -Command "docker run --name $ContainerName --detach --restart unless-stopped --publish ${port}:${port} -e POSTGRES_PASSWORD='$password' -e POSTGRES_USER='$userName' -e POSTGRES_DB='$databaseName' $dockerImage -c max_prepared_transactions=10"
Invoke-Wsl -Distribution $wslDistribution -Command "docker ps --filter name=$ContainerName"

# Determine the WSL VM IPv4 address so that Windows can reach the published port.
$wslIp = ((wsl.exe --distribution $wslDistribution --user root -- hostname -I) -replace "`0", "").Trim().Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries) |
Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' } |
Select-Object -First 1

if (-not $wslIp) {
throw "Could not determine the WSL IPv4 address"
}

$ipAddress = $wslIp
Write-Output "WSL address: $ipAddress"

Write-Output "::endgroup::"
}
else {
Expand Down