Skip to content

Add Docker setup and SQL Server container support to Copilot workflow #131

Add Docker setup and SQL Server container support to Copilot workflow

Add Docker setup and SQL Server container support to Copilot workflow #131

name: 'Copilot Setup Steps'
# This workflow sets up a complete development environment for the PowerShell module project
# when executed by GitHub Copilot Agent for development assistance.
on:
workflow_dispatch:
pull_request:
paths:
- '.github/workflows/copilot-setup-steps.yml'
push:
paths:
- '.github/workflows/copilot-setup-steps.yml'
# cSpell: ignore unshallow LASTEXITCODE PSDSC noreport dearmor distro keyrings endgroup libmi libc
jobs:
copilot-setup-steps:
name: Setup PowerShell Development Environment
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Set Environment Variables
shell: bash
run: |
echo "Setting MODULE_NAME=$MODULE_NAME to GitHub environment"
echo "MODULE_NAME=$MODULE_NAME" >> "$GITHUB_ENV"
env:
MODULE_NAME: 'SqlServerDsc'
- name: Checkout Repository
uses: actions/checkout@v5
with:
fetch-depth: 0 # Full history needed for GitVersion
# This step is needed for GitVersion because Copilot switches to its working branch
# after checkout and fetches only depth 2.
- name: Ensure full history for GitVersion
shell: pwsh
run: |
Write-Host 'Ensuring full history for GitVersion...'
$isShallow = (& git rev-parse --is-shallow-repository) -eq 'true'
if ($isShallow)
{
Write-Host 'Repository is shallow. Fetching full history and tags...'
git fetch --prune --unshallow --tags --no-recurse-submodules
if ($LASTEXITCODE -ne 0)
{
throw 'git fetch --unshallow failed'
}
}
else
{
Write-Host 'Repository is not shallow. Refreshing tags...'
git fetch --prune --tags --no-recurse-submodules
if ($LASTEXITCODE -ne 0)
{
throw 'git fetch --tags failed'
}
}
Write-Host 'History ready for GitVersion.'
- name: Install Linux Dependencies
shell: bash
run: |
echo "Installing Linux dependencies for PowerShell DSC..."
# Update package list
echo "::group::Update package list"
echo "Updating package list..."
sudo apt-get update
echo "Package list updated successfully"
echo "::endgroup::"
# Ensure gpg is available
sudo apt-get install -y gnupg
# Check if Microsoft repository is already added, if not add it
if ! grep -q "packages.microsoft.com" /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null; then
echo "::group::Add Microsoft repository"
echo "Adding Microsoft repository..."
# Add Microsoft's GPG key and repository (keyring)
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/keyrings/microsoft.gpg >/dev/null
sudo chmod a+r /etc/apt/keyrings/microsoft.gpg
DISTRO_CODENAME="$(lsb_release -cs)"
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/microsoft.gpg] https://packages.microsoft.com/repos/microsoft-ubuntu-${DISTRO_CODENAME}-prod ${DISTRO_CODENAME} main" | sudo tee "/etc/apt/sources.list.d/microsoft-${DISTRO_CODENAME}-prod.list" >/dev/null
# Update package list again after adding the new repo
echo "Updating package list after adding Microsoft repository..."
if ! sudo apt-get update; then
echo "::warning::apt-get update failed after adding Microsoft repo (repo may be unavailable for this distro). Continuing..."
fi
echo "Microsoft repository added successfully"
echo "::endgroup::"
else
echo "Microsoft repository already configured, skipping..."
fi
# Install OMI (Open Management Infrastructure) which provides libmi
echo "::group::Install OMI and dependencies"
if apt-cache policy omi 2>/dev/null | grep -q 'Candidate:'; then
echo "Installing OMI package..."
# Set a shorter hostname to avoid SSL certificate generation errors
# OMI generates SSL certificates during installation and GitHub Actions runners
# often have very long hostnames that exceed the 64-character limit for CN fields
echo "Setting temporary hostname to avoid SSL certificate generation errors..."
ORIGINAL_HOSTNAME=$(hostname)
# Install trap to always restore hostname on EXIT or ERR
restore_hostname() {
echo "Restoring original hostname..."
sudo hostname "$ORIGINAL_HOSTNAME"
trap - EXIT ERR # Remove the trap after restore
}
trap 'restore_hostname' EXIT ERR
sudo hostname "dsc-runner"
if ! sudo apt-get install -y omi; then
echo "OMI package failed to install; continuing..."
echo "::warning::OMI package failed to install; continuing without OMI"
else
echo "OMI package installed successfully"
fi
# Restore original hostname and remove trap
restore_hostname
else
echo "Package 'omi' not found in repositories; skipping OMI install."
echo "::warning::OMI package not found in repositories, skipping installation"
fi
echo "::endgroup::"
echo "::group::Installing additional development dependencies..."
echo "Installing development dependencies..."
sudo apt-get install -y libc6-dev libssl-dev tree
echo "Development dependencies installed successfully"
echo "::endgroup::"
# Set LD_LIBRARY_PATH permanently for the runner session
echo "Setting LD_LIBRARY_PATH environment variable..."
echo "LD_LIBRARY_PATH=/opt/omi/lib:/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH}" >> "$GITHUB_ENV"
echo "Environment variable set successfully"
echo "Linux dependencies installed successfully"
- name: Setup Docker
shell: bash
run: |
echo "Setting up Docker for SQL Server Linux containers..."
echo "::group::Setup Docker"
echo "Verifying Docker installation..."
if ! command -v docker &> /dev/null; then
echo "Docker not found, installing..."
sudo apt-get update
sudo apt-get install -y docker.io
fi
# Start Docker service if not running
if ! sudo systemctl is-active --quiet docker; then
echo "Starting Docker service..."
sudo systemctl start docker
sudo systemctl enable docker
fi
# Add current user to docker group for permissions
sudo usermod -aG docker $USER
# Verify Docker is working
docker --version
sudo docker run --rm hello-world
echo "Docker setup completed successfully"
echo "::endgroup::"
- name: Setup SQL Server on Linux Container
shell: bash
run: |
echo "Setting up SQL Server Linux container..."
# Set SQL Server environment variables
SQLSERVER_SA_PASSWORD="MyStr0ng!Passw0rd123"
SQLSERVER_CONTAINER_NAME="sqlserver-linux-ci"
SQLSERVER_PORT="1433"
echo "::group::Pull SQL Server Linux container image"
echo "Pulling SQL Server 2022 Linux container image..."
sudo docker pull mcr.microsoft.com/mssql/server:2022-latest
echo "Container image pulled successfully"
echo "::endgroup::"
echo "::group::Run SQL Server Linux container"
echo "Starting SQL Server Linux container..."
# Create volume for SQL Server data persistence
sudo docker volume create sqlserver-data
# Run SQL Server container with proper configuration
sudo docker run -e 'ACCEPT_EULA=Y' \
-e "MSSQL_SA_PASSWORD=${SQLSERVER_SA_PASSWORD}" \
-e 'MSSQL_PID=Developer' \
-p "${SQLSERVER_PORT}:1433" \
--name "${SQLSERVER_CONTAINER_NAME}" \
--hostname sqlserver-linux \
-v sqlserver-data:/var/opt/mssql \
-d mcr.microsoft.com/mssql/server:2022-latest
echo "Waiting for SQL Server container to start..."
sleep 30
# Check if container is running
if sudo docker ps | grep -q "${SQLSERVER_CONTAINER_NAME}"; then
echo "SQL Server Linux container started successfully"
# Display container information
echo "Container status:"
sudo docker ps --filter "name=${SQLSERVER_CONTAINER_NAME}" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Check SQL Server logs for startup confirmation
echo "Checking SQL Server startup logs..."
sudo docker logs "${SQLSERVER_CONTAINER_NAME}" 2>&1 | tail -10
# Set environment variables for later steps
echo "SQLSERVER_SA_PASSWORD=${SQLSERVER_SA_PASSWORD}" >> "$GITHUB_ENV"
echo "SQLSERVER_CONTAINER_NAME=${SQLSERVER_CONTAINER_NAME}" >> "$GITHUB_ENV"
echo "SQLSERVER_PORT=${SQLSERVER_PORT}" >> "$GITHUB_ENV"
echo "SQLSERVER_CONNECTION_STRING=Server=localhost,${SQLSERVER_PORT};User Id=sa;Password=${SQLSERVER_SA_PASSWORD};TrustServerCertificate=true" >> "$GITHUB_ENV"
else
echo "::error::Failed to start SQL Server Linux container"
sudo docker logs "${SQLSERVER_CONTAINER_NAME}" || true
exit 1
fi
echo "::endgroup::"
- name: Install SQL Server Command Line Tools
shell: bash
run: |
echo "Installing SQL Server command line tools (sqlcmd)..."
echo "::group::Install mssql-tools"
# Install the Microsoft SQL Server command-line tools
if ! grep -q "packages.microsoft.com" /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null; then
echo "Microsoft repository not found, adding it..."
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/keyrings/microsoft.gpg >/dev/null
sudo chmod a+r /etc/apt/keyrings/microsoft.gpg
DISTRO_CODENAME="$(lsb_release -cs)"
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/microsoft.gpg] https://packages.microsoft.com/repos/microsoft-ubuntu-${DISTRO_CODENAME}-prod ${DISTRO_CODENAME} main" | sudo tee "/etc/apt/sources.list.d/microsoft-${DISTRO_CODENAME}-prod.list" >/dev/null
sudo apt-get update
fi
# Accept EULA for mssql-tools (required for non-interactive installation)
echo 'mssql-tools accept-eula/accept-eula-license boolean true' | sudo debconf-set-selections
# Install mssql-tools18 (latest version with enhanced security)
if sudo apt-get install -y mssql-tools18; then
echo "mssql-tools18 installed successfully"
# Add sqlcmd to PATH for current session
export PATH="$PATH:/opt/mssql-tools18/bin"
echo "/opt/mssql-tools18/bin" | sudo tee -a /etc/environment
echo "PATH=$PATH:/opt/mssql-tools18/bin" >> "$GITHUB_ENV"
else
echo "::warning::mssql-tools18 installation failed, trying mssql-tools"
# Fallback to older version if needed
sudo apt-get install -y mssql-tools
export PATH="$PATH:/opt/mssql-tools/bin"
echo "/opt/mssql-tools/bin" | sudo tee -a /etc/environment
echo "PATH=$PATH:/opt/mssql-tools/bin" >> "$GITHUB_ENV"
fi
# Verify sqlcmd installation
which sqlcmd || echo "::warning::sqlcmd not found in PATH"
echo "::endgroup::"
- name: Verify SQL Server Linux Container Connection
shell: bash
run: |
echo "Testing connection to SQL Server Linux container..."
echo "::group::Test SQL Server connection"
# Wait a bit more for SQL Server to be fully ready
echo "Waiting for SQL Server to be fully ready..."
sleep 15
# Test connection using sqlcmd with retry logic
max_retries=5
retry_count=0
while [ $retry_count -lt $max_retries ]; do
echo "Connection attempt $((retry_count + 1)) of $max_retries..."
if sqlcmd -S localhost,"$SQLSERVER_PORT" -U sa -P "$SQLSERVER_SA_PASSWORD" -C -Q "SELECT @@VERSION, @@SERVERNAME, GETDATE() AS CurrentDateTime;" -o /tmp/sqltest.out; then
echo "✅ Successfully connected to SQL Server Linux container!"
echo "Query results:"
cat /tmp/sqltest.out
break
else
echo "❌ Connection attempt failed"
retry_count=$((retry_count + 1))
if [ $retry_count -lt $max_retries ]; then
echo "Retrying in 10 seconds..."
sleep 10
fi
fi
done
if [ $retry_count -eq $max_retries ]; then
echo "::error::Failed to connect to SQL Server after $max_retries attempts"
echo "Container logs:"
sudo docker logs "$SQLSERVER_CONTAINER_NAME" | tail -20
exit 1
fi
# Test creating a simple database
echo "Testing database creation..."
if sqlcmd -S localhost,"$SQLSERVER_PORT" -U sa -P "$SQLSERVER_SA_PASSWORD" -C -Q "CREATE DATABASE TestDB; SELECT name FROM sys.databases WHERE name = 'TestDB';" -o /tmp/dbtest.out; then
echo "✅ Successfully created test database!"
cat /tmp/dbtest.out
else
echo "::warning::Failed to create test database"
fi
# Display container resource usage
echo "Container resource usage:"
sudo docker stats "$SQLSERVER_CONTAINER_NAME" --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
echo "::endgroup::"
echo "✅ SQL Server Linux container setup completed successfully!"
echo "Connection details:"
echo " Server: localhost,$SQLSERVER_PORT"
echo " User: sa"
echo " Connection String: $SQLSERVER_CONNECTION_STRING"
- name: Configure PowerShell Environment
shell: pwsh
run: |
Write-Host "Configure PowerShell environment..."
# Install dependent PowerShell modules
Write-Host "Installing SqlServer module..."
Install-PSResource -Name 'SqlServer' -Version '21.1.18256' -Scope 'CurrentUser' -Repository 'PSGallery' -TrustRepository
Write-Host "SqlServer module installed successfully"
Write-Host "Installing PSDSC module..."
Install-PSResource -Name 'PSDSC' -Scope 'CurrentUser' -Repository 'PSGallery' -TrustRepository
Write-Host "PSDSC module installed successfully"
Write-Host "PowerShell environment configuration complete"
- name: Install DSCv3
shell: pwsh
run: |
Write-Host "Install DSCv3 in environment..."
# Install dependent PowerShell modules
Write-Host "Installing DSCv3 executable..."
Install-DscExe -IncludePrerelease -Force
Write-Host "DSCv3 installed successfully"
Write-Host "DSCv3 install complete"
- name: Verify DSCv3
shell: pwsh
run: |
Write-Host "Running DSCv3 to validate correct operation..."
dsc --version
- name: Install .NET Tools
shell: pwsh
run: |
Write-Host 'Installing/Updating .NET tools...'
# Install GitVersion for semantic versioning (idempotent)
dotnet tool update --global GitVersion.Tool --version 5.* `
|| dotnet tool install --global GitVersion.Tool --version 5.*
# Verify installation
dotnet-gitversion /version
Write-Host '.NET tools ready.'
- name: Verify GitVersion
shell: pwsh
run: |
Write-Host 'Running GitVersion to determine semantic version (parsing to PowerShell object)...'
dotnet-gitversion | ConvertFrom-Json
- name: Resolve Dependencies
shell: pwsh
run: |
Write-Host 'Resolving project dependencies...'
# Run dependency resolution
./build.ps1 -ResolveDependency -Tasks 'noop' -UseModuleFast -ErrorAction Stop
Write-Host 'Dependencies resolved successfully.'
- name: Build Module
shell: pwsh
run: |
Write-Host "Building $env:MODULE_NAME module..."
# Build the module
./build.ps1 -Tasks 'build' -ErrorAction Stop
# Verify build output
$builtPath = "output/builtModule/$env:MODULE_NAME"
if (Test-Path -Path $builtPath)
{
Write-Host "Module built successfully at: $builtPath"
# Show files with paths relative to the built module base path using `tree` (run directly in PowerShell)
$base = (Resolve-Path -Path $builtPath).ProviderPath
Write-Host "Files under $base (relative paths):"
# Run tree directly and strip leading './' from its output
tree --noreport $builtPath
}
else
{
Write-Error 'Module build failed - output directory not found'
exit 1
}
- name: Import Built Module
shell: pwsh
run: |
Write-Host "Importing built $env:MODULE_NAME module..."
./build.ps1 -Tasks 'noop'
Import-Module -Name $env:MODULE_NAME -Force
# Verify module is loaded
$module = Get-Module -Name $env:MODULE_NAME -ErrorAction SilentlyContinue
if ($module)
{
Write-Host 'Module imported successfully:'
Write-Host " Name: $($module.Name)"
Write-Host " Version: $($module.Version)"
Write-Host " Path: $($module.Path)"
# Show available commands
$commands = @(Get-Command -Module $env:MODULE_NAME)
if ($commands.Count -gt 0)
{
Write-Host " Exported Commands: $($commands.Count)"
Write-Host "`nAvailable Commands:"
$commands |
Select-Object Name, ModuleName | Format-Table -AutoSize
}
else
{
Write-Host 'No commands exported by the module.'
}
$dscResources = @(dsc resource list --adapter Microsoft.DSC/PowerShell --output-format json | ConvertFrom-Json)
Write-Host "`n Available class-based DSC Resources: $($dscResources.Count)`n"
$dscResources | Format-Table type, capabilities, implementedAs, requireAdapter -GroupBy kind -AutoSize
}
else
{
Write-Error 'Failed to import module'
exit 1
}