168 lines
5.3 KiB
PowerShell
168 lines
5.3 KiB
PowerShell
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Require-Tool {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Name
|
|
)
|
|
|
|
if ($null -eq (Get-Command $Name -ErrorAction SilentlyContinue)) {
|
|
throw "Required tool not found: $Name"
|
|
}
|
|
}
|
|
|
|
function Invoke-NativeCommand {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Name,
|
|
[Parameter(Mandatory = $true)][scriptblock]$Action
|
|
)
|
|
|
|
Write-Host $Name
|
|
& $Action
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Step failed: $Name"
|
|
}
|
|
}
|
|
|
|
function Get-RemoteScript {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$RemoteReleaseDir,
|
|
[Parameter(Mandatory = $true)][string]$RemoteCurrentLink,
|
|
[Parameter(Mandatory = $true)][string]$ContainerName,
|
|
[Parameter(Mandatory = $true)][string]$ImageName,
|
|
[Parameter(Mandatory = $true)][string]$ReleaseTimestamp,
|
|
[Parameter(Mandatory = $true)][string]$RemoteDataDir,
|
|
[Parameter(Mandatory = $true)][string]$ContainerPort,
|
|
[Parameter(Mandatory = $true)][string]$HostPort
|
|
)
|
|
|
|
$script = @'
|
|
set -euo pipefail
|
|
|
|
remote_release_dir='__REMOTE_RELEASE_DIR__'
|
|
remote_current_link='__REMOTE_CURRENT_LINK__'
|
|
container_name='__CONTAINER_NAME__'
|
|
image_name='__IMAGE_NAME__'
|
|
release_timestamp='__RELEASE_TIMESTAMP__'
|
|
remote_data_dir='__REMOTE_DATA_DIR__'
|
|
container_port='__CONTAINER_PORT__'
|
|
host_port='__HOST_PORT__'
|
|
|
|
previous_current_target=''
|
|
if [ -L "${remote_current_link}" ]; then
|
|
previous_current_target="$(readlink -f "${remote_current_link}")"
|
|
fi
|
|
|
|
docker build -t "${image_name}:${release_timestamp}" -t "${image_name}:latest" "${remote_release_dir}"
|
|
ln -sfn "${remote_release_dir}" "${remote_current_link}"
|
|
|
|
if docker ps -aq --filter "name=^/${container_name}$" | grep -q .; then
|
|
docker rm -f "${container_name}" >/dev/null
|
|
fi
|
|
|
|
if ! docker run -d \
|
|
--name "${container_name}" \
|
|
--restart unless-stopped \
|
|
-p "127.0.0.1:${host_port}:${container_port}" \
|
|
-e ASPNETCORE_ENVIRONMENT=Production \
|
|
-e ASPNETCORE_URLS="http://+:${container_port}" \
|
|
-e ConnectionStrings__RpgRoller="Data Source=/app/data/rpgroller.db" \
|
|
-v "${remote_data_dir}:/app/data" \
|
|
"${image_name}:${release_timestamp}" >/dev/null; then
|
|
if [ -n "${previous_current_target}" ]; then
|
|
ln -sfn "${previous_current_target}" "${remote_current_link}"
|
|
fi
|
|
exit 1
|
|
fi
|
|
'@
|
|
|
|
return $script.
|
|
Replace("__REMOTE_RELEASE_DIR__", $RemoteReleaseDir).
|
|
Replace("__REMOTE_CURRENT_LINK__", $RemoteCurrentLink).
|
|
Replace("__CONTAINER_NAME__", $ContainerName).
|
|
Replace("__IMAGE_NAME__", $ImageName).
|
|
Replace("__RELEASE_TIMESTAMP__", $ReleaseTimestamp).
|
|
Replace("__REMOTE_DATA_DIR__", $RemoteDataDir).
|
|
Replace("__CONTAINER_PORT__", $ContainerPort).
|
|
Replace("__HOST_PORT__", $HostPort)
|
|
}
|
|
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$repoRoot = Split-Path -Parent $scriptDir
|
|
$projectPath = Join-Path $repoRoot "RpgRoller\RpgRoller.csproj"
|
|
|
|
$remoteHost = "myvserver"
|
|
$remoteRoot = "/root/docker/rpgroller"
|
|
$remoteReleasesDir = "$remoteRoot/releases"
|
|
$remoteCurrentLink = "$remoteRoot/current"
|
|
$remoteDataDir = "$remoteRoot/data"
|
|
|
|
$containerName = "rpgroller"
|
|
$imageName = "rpgroller"
|
|
$containerPort = "8080"
|
|
$hostPort = "8082"
|
|
$releaseTimestamp = (Get-Date).ToUniversalTime().ToString("yyyyMMddHHmmss")
|
|
$localStageDir = Join-Path $repoRoot "artifacts\deploy\$releaseTimestamp"
|
|
$localPublishDir = Join-Path $localStageDir "publish"
|
|
$remoteReleaseDir = "$remoteReleasesDir/$releaseTimestamp"
|
|
|
|
Write-Host "Deploying release $releaseTimestamp"
|
|
|
|
Require-Tool -Name "dotnet"
|
|
Require-Tool -Name "ssh"
|
|
Require-Tool -Name "scp"
|
|
|
|
try {
|
|
New-Item -ItemType Directory -Path $localPublishDir -Force | Out-Null
|
|
|
|
Invoke-NativeCommand -Name "1) Publishing app locally..." -Action {
|
|
dotnet publish $projectPath -c Release -o $localPublishDir
|
|
}
|
|
|
|
@'
|
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0
|
|
WORKDIR /app
|
|
ENV ASPNETCORE_URLS=http://+:8080
|
|
ENV DOTNET_EnableDiagnostics=0
|
|
EXPOSE 8080
|
|
COPY publish/ ./
|
|
RUN mkdir -p /app/data
|
|
ENTRYPOINT ["dotnet", "RpgRoller.dll"]
|
|
'@ | Set-Content -Path (Join-Path $localStageDir "Dockerfile") -NoNewline
|
|
|
|
Invoke-NativeCommand -Name "2) Preparing remote release directory..." -Action {
|
|
ssh $remoteHost "mkdir -p '$remoteReleasesDir' '$remoteDataDir' && test ! -e '$remoteReleaseDir' && mkdir -p '$remoteReleaseDir'"
|
|
}
|
|
|
|
Invoke-NativeCommand -Name "3) Uploading release payload..." -Action {
|
|
Push-Location $localStageDir
|
|
try {
|
|
scp -r "Dockerfile" "publish" "${remoteHost}:$remoteReleaseDir/"
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
$remoteScript = Get-RemoteScript `
|
|
-RemoteReleaseDir $remoteReleaseDir `
|
|
-RemoteCurrentLink $remoteCurrentLink `
|
|
-ContainerName $containerName `
|
|
-ImageName $imageName `
|
|
-ReleaseTimestamp $releaseTimestamp `
|
|
-RemoteDataDir $remoteDataDir `
|
|
-ContainerPort $containerPort `
|
|
-HostPort $hostPort
|
|
|
|
Invoke-NativeCommand -Name "4) Building image and restarting container on remote host..." -Action {
|
|
$remoteScript | ssh $remoteHost "bash -se"
|
|
}
|
|
|
|
Write-Host "5) Deployment complete."
|
|
}
|
|
finally {
|
|
if (Test-Path $localStageDir) {
|
|
Remove-Item -Path $localStageDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|