Add PlotDirector development refresh script
This commit is contained in:
parent
ec8ce94082
commit
8cc0baa4b8
@ -54,3 +54,4 @@ Additional areas may be added when a repeated operational need exists.
|
|||||||
- Discovery and validation scripts must not dump environment variables, private keys, tokens, or complete sensitive configuration files.
|
- Discovery and validation scripts must not dump environment variables, private keys, tokens, or complete sensitive configuration files.
|
||||||
- SQL helper scripts must not embed or print SQL administrator passwords.
|
- SQL helper scripts must not embed or print SQL administrator passwords.
|
||||||
- Firewall inspection should use `sudo ufw status numbered`.
|
- Firewall inspection should use `sudo ufw status numbered`.
|
||||||
|
- Development refresh scripts may operate on dirty application working trees, but must not change Git state or run schema migrations implicitly.
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
# Development Scripts
|
# Development Scripts
|
||||||
|
|
||||||
This directory is reserved for development workflow helpers that do not belong to installation, deployment, backup, restore, monitoring, maintenance, or system validation.
|
This directory contains development workflow helpers that do not belong to installation, deployment, backup, restore, monitoring, maintenance, or system validation.
|
||||||
|
|
||||||
|
`refresh-plotdirector.sh` rebuilds and republishes the current `/srv/repos/PlotDirector` working tree to `/srv/apps/plotdirector-dev/current`, restarts `plotdirector-dev`, and smoke-tests `/Account/Login` through nginx. It allows uncommitted application changes and does not pull, switch branches, commit, push, reset, clean, alter the database schema, run SQL scripts, or modify `/etc/plotdirector/plotdirector.env`.
|
||||||
|
|
||||||
Do not store secrets, generated logs, package caches, or one-off experiments here.
|
Do not store secrets, generated logs, package caches, or one-off experiments here.
|
||||||
|
|
||||||
Future structure is `TODO: Confirm during the relevant implementation phase.`
|
Generated publish output remains outside the repository.
|
||||||
|
|||||||
169
scripts/development/refresh-plotdirector.sh
Executable file
169
scripts/development/refresh-plotdirector.sh
Executable file
@ -0,0 +1,169 @@
|
|||||||
|
#!/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."
|
||||||
Loading…
x
Reference in New Issue
Block a user