107 lines
3.3 KiB
Bash
Executable File
107 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
readonly PROJECT_PATH="${REPO_ROOT}/RpgRoller/RpgRoller.csproj"
|
|
|
|
readonly REMOTE_HOST="myvserver"
|
|
readonly REMOTE_ROOT="/root/docker/rpgroller"
|
|
readonly REMOTE_RELEASES_DIR="${REMOTE_ROOT}/releases"
|
|
readonly REMOTE_CURRENT_LINK="${REMOTE_ROOT}/current"
|
|
readonly REMOTE_DATA_DIR="${REMOTE_ROOT}/data"
|
|
|
|
readonly CONTAINER_NAME="rpgroller"
|
|
readonly IMAGE_NAME="rpgroller"
|
|
readonly CONTAINER_PORT="8080"
|
|
readonly HOST_PORT="8082"
|
|
readonly RELEASE_TIMESTAMP="$(date -u +%Y%m%d%H%M%S)"
|
|
readonly LOCAL_STAGE_DIR="${REPO_ROOT}/artifacts/deploy/${RELEASE_TIMESTAMP}"
|
|
readonly LOCAL_PUBLISH_DIR="${LOCAL_STAGE_DIR}/publish"
|
|
readonly REMOTE_RELEASE_DIR="${REMOTE_RELEASES_DIR}/${RELEASE_TIMESTAMP}"
|
|
|
|
cleanup() {
|
|
rm -rf "${LOCAL_STAGE_DIR}"
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
require_tool() {
|
|
local tool_name="$1"
|
|
if ! command -v "${tool_name}" >/dev/null 2>&1; then
|
|
printf 'Required tool not found: %s\n' "${tool_name}" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
printf 'Deploying release %s\n' "${RELEASE_TIMESTAMP}"
|
|
|
|
require_tool dotnet
|
|
require_tool rsync
|
|
require_tool ssh
|
|
|
|
mkdir -p "${LOCAL_PUBLISH_DIR}"
|
|
|
|
printf '1) Publishing app locally...\n'
|
|
dotnet publish "${PROJECT_PATH}" -c Release -o "${LOCAL_PUBLISH_DIR}"
|
|
|
|
cat > "${LOCAL_STAGE_DIR}/Dockerfile" <<'EOF'
|
|
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"]
|
|
EOF
|
|
|
|
printf '2) Preparing remote release directory...\n'
|
|
ssh "${REMOTE_HOST}" "mkdir -p '${REMOTE_RELEASES_DIR}' '${REMOTE_DATA_DIR}' && test ! -e '${REMOTE_RELEASE_DIR}'"
|
|
|
|
printf '3) Uploading release payload...\n'
|
|
rsync -az --delete "${LOCAL_STAGE_DIR}/" "${REMOTE_HOST}:${REMOTE_RELEASE_DIR}/"
|
|
|
|
printf '4) Building image and restarting container on remote host...\n'
|
|
ssh "${REMOTE_HOST}" "bash -se" <<EOF
|
|
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
|
|
EOF
|
|
|
|
printf '5) Deployment complete.\n'
|