170 lines
4.8 KiB
Bash
Executable File
170 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
SOURCE_REPO="/srv/repos/PlotDirector"
|
|
PUBLISH_DIR="/srv/apps/plotdirector-dev/current"
|
|
SERVICE_NAME="plotdirector-dev"
|
|
SMOKE_URL="http://127.0.0.1/Account/Login"
|
|
SMOKE_HOST="dev.plotdirector.com"
|
|
CONFIGURATION="Release"
|
|
SKIP_BUILD=0
|
|
DRY_RUN=0
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: scripts/development/refresh-plotdirector.sh [options]
|
|
|
|
Quickly refresh the PlotDirector development instance from the current working
|
|
tree. This script does not pull, switch branches, commit, push, reset, clean,
|
|
change database schema, run SQL scripts, or modify runtime secrets.
|
|
|
|
Options:
|
|
--help Show this help text.
|
|
--dry-run Show intended actions without changing the running application.
|
|
--skip-build Skip dotnet build and publish the current working tree directly.
|
|
|
|
Defaults:
|
|
Source repository: /srv/repos/PlotDirector
|
|
Publish path: /srv/apps/plotdirector-dev/current
|
|
Service: plotdirector-dev
|
|
Smoke route: http://127.0.0.1/Account/Login with Host dev.plotdirector.com
|
|
|
|
USAGE
|
|
}
|
|
|
|
log() {
|
|
printf '%s\n' "$*"
|
|
}
|
|
|
|
fail() {
|
|
printf 'ERROR: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
show_recent_logs() {
|
|
log "Latest ${SERVICE_NAME} logs:"
|
|
sudo -n journalctl -u "$SERVICE_NAME" --no-pager -n 100 | sed -E \
|
|
-e 's/(Password=)[^;[:space:]]+/\1<redacted>/g' \
|
|
-e 's/(SecretKey=)[^;[:space:]]+/\1<redacted>/g' \
|
|
-e 's/(ApiKey=)[^;[:space:]]+/\1<redacted>/g' || true
|
|
}
|
|
|
|
cleanup() {
|
|
if [[ -n "${PUBLISH_TMP:-}" && -d "${PUBLISH_TMP:-}" ]]; then
|
|
rm -rf "$PUBLISH_TMP"
|
|
fi
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
while [[ "$#" -gt 0 ]]; do
|
|
case "$1" in
|
|
--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=1
|
|
;;
|
|
--skip-build)
|
|
SKIP_BUILD=1
|
|
;;
|
|
*)
|
|
fail "Unknown option: $1"
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
[[ -d "$SOURCE_REPO/.git" ]] || fail "PlotDirector repository not found at ${SOURCE_REPO}"
|
|
[[ -f "${SOURCE_REPO}/PlotLine/PlotLine.csproj" ]] || fail "PlotLine project not found"
|
|
command -v dotnet >/dev/null 2>&1 || fail "dotnet command not found"
|
|
command -v rsync >/dev/null 2>&1 || fail "rsync command not found"
|
|
command -v curl >/dev/null 2>&1 || fail "curl command not found"
|
|
sudo -n true >/dev/null 2>&1 || fail "passwordless non-interactive sudo is required"
|
|
|
|
case "$PUBLISH_DIR" in
|
|
""|"/"|"/srv"|"/srv/"|"/srv/apps"|"/srv/apps/")
|
|
fail "Unsafe publish path: ${PUBLISH_DIR}"
|
|
;;
|
|
esac
|
|
|
|
if [[ "$PUBLISH_DIR" != "/srv/apps/plotdirector-dev/current" ]]; then
|
|
fail "Publish path must be /srv/apps/plotdirector-dev/current; got ${PUBLISH_DIR}"
|
|
fi
|
|
|
|
branch="$(git -C "$SOURCE_REPO" branch --show-current)"
|
|
dirty="no"
|
|
if [[ -n "$(git -C "$SOURCE_REPO" status --porcelain)" ]]; then
|
|
dirty="yes"
|
|
fi
|
|
|
|
log "PlotDirector source: ${SOURCE_REPO}"
|
|
log "Current branch: ${branch:-detached}"
|
|
log "Working tree dirty: ${dirty}"
|
|
log "Publish target: ${PUBLISH_DIR}"
|
|
|
|
if [[ "$SKIP_BUILD" != "1" ]]; then
|
|
log "Building PlotDirector (${CONFIGURATION})..."
|
|
dotnet build "${SOURCE_REPO}/PlotLine/PlotLine.csproj" --configuration "$CONFIGURATION"
|
|
else
|
|
log "Skipping build by request."
|
|
fi
|
|
|
|
PUBLISH_TMP="$(mktemp -d /tmp/plotdirector-refresh.XXXXXX)"
|
|
log "Publishing to temporary directory..."
|
|
dotnet publish "${SOURCE_REPO}/PlotLine/PlotLine.csproj" --configuration "$CONFIGURATION" --no-build --output "$PUBLISH_TMP"
|
|
|
|
[[ -f "${PUBLISH_TMP}/PlotLine.dll" ]] || fail "Publish output did not contain PlotLine.dll"
|
|
|
|
if [[ "$DRY_RUN" == "1" ]]; then
|
|
log "Dry-run mode: publish succeeded; running application was not changed."
|
|
log "Would stop ${SERVICE_NAME}, replace ${PUBLISH_DIR}, start ${SERVICE_NAME}, and smoke test ${SMOKE_URL}."
|
|
exit 0
|
|
fi
|
|
|
|
log "Stopping ${SERVICE_NAME}..."
|
|
sudo -n systemctl stop "$SERVICE_NAME"
|
|
|
|
log "Replacing published application files..."
|
|
sudo -n install -d -m 0750 -o plotdirector -g plotdirector "$PUBLISH_DIR"
|
|
sudo -n rsync -a --delete "${PUBLISH_TMP}/" "${PUBLISH_DIR}/"
|
|
sudo -n chown -R plotdirector:plotdirector "$PUBLISH_DIR"
|
|
sudo -n find "$PUBLISH_DIR" -type d -exec chmod 0750 {} +
|
|
sudo -n find "$PUBLISH_DIR" -type f -exec chmod 0640 {} +
|
|
|
|
log "Starting ${SERVICE_NAME}..."
|
|
sudo -n systemctl start "$SERVICE_NAME"
|
|
|
|
for _ in {1..20}; do
|
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
if ! systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
log "${SERVICE_NAME} did not become active."
|
|
show_recent_logs
|
|
exit 1
|
|
fi
|
|
|
|
log "Smoke testing ${SMOKE_URL}..."
|
|
smoke_ok=0
|
|
for _ in {1..30}; do
|
|
if curl --fail --silent --show-error --noproxy '*' -H "Host: ${SMOKE_HOST}" "$SMOKE_URL" >/dev/null 2>&1; then
|
|
smoke_ok=1
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
if [[ "$smoke_ok" != "1" ]]; then
|
|
log "Smoke test failed for ${SMOKE_URL}."
|
|
curl --fail --silent --show-error --noproxy '*' -H "Host: ${SMOKE_HOST}" "$SMOKE_URL" >/dev/null || true
|
|
show_recent_logs
|
|
exit 1
|
|
fi
|
|
|
|
log "Refresh complete. ${SERVICE_NAME} is active and ${SMOKE_URL} returned HTTP 200."
|