130 lines
4.5 KiB
YAML
130 lines
4.5 KiB
YAML
name: Verify and deploy production
|
|
|
|
concurrency:
|
|
group: rednote-tool-studio-production
|
|
cancel-in-progress: false
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
verify-and-deploy:
|
|
runs-on: ces-control
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: npm
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Type check
|
|
run: npm run typecheck
|
|
|
|
- name: Unit tests
|
|
run: npm test
|
|
|
|
- name: Production build
|
|
run: npm run build
|
|
|
|
- name: Deploy persistent production service
|
|
if: gitea.event_name == 'workflow_dispatch' || (gitea.event_name == 'push' && gitea.ref == 'refs/heads/main')
|
|
env:
|
|
STUDIO_USERNAME: studio
|
|
STUDIO_PASSWORD: ${{ secrets.STUDIO_PASSWORD }}
|
|
DEPLOY_PORT: ${{ vars.DEPLOY_PORT }}
|
|
RELEASE_SHA: ${{ gitea.sha }}
|
|
run: |
|
|
set -eu
|
|
if [ -z "$STUDIO_PASSWORD" ]; then
|
|
echo "STUDIO_PASSWORD repository secret is required for production deployment"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$(id -u)" -eq 0 ]; then
|
|
SUDO=""
|
|
elif sudo -n true 2>/dev/null; then
|
|
SUDO="sudo -n"
|
|
else
|
|
echo "The ces-control runner needs root or passwordless sudo for persistent systemd deployment"
|
|
exit 1
|
|
fi
|
|
|
|
PORT="${DEPLOY_PORT:-3000}"
|
|
APP_ROOT=/opt/rednote-tool-studio
|
|
RELEASE_DIR="$APP_ROOT/releases/$RELEASE_SHA"
|
|
DATA_ROOT=/var/lib/rednote-tool-studio
|
|
SERVICE_USER=rednote-studio
|
|
|
|
if ! id "$SERVICE_USER" >/dev/null 2>&1; then
|
|
$SUDO useradd --system --home "$DATA_ROOT" --shell /usr/sbin/nologin "$SERVICE_USER"
|
|
fi
|
|
|
|
$SUDO mkdir -p "$APP_ROOT/releases" "$APP_ROOT/bin" "$DATA_ROOT/projects"
|
|
$SUDO install -m 0755 "$(command -v node)" "$APP_ROOT/bin/node"
|
|
$SUDO mkdir -p "$RELEASE_DIR/.next" "$RELEASE_DIR/data"
|
|
$SUDO cp -a .next/standalone/. "$RELEASE_DIR/"
|
|
$SUDO cp -a .next/static "$RELEASE_DIR/.next/static"
|
|
$SUDO cp -a templates vendor "$RELEASE_DIR/"
|
|
$SUDO ln -sfn "$DATA_ROOT/projects" "$RELEASE_DIR/data/projects"
|
|
$SUDO chown -R root:root "$APP_ROOT"
|
|
$SUDO chown -R "$SERVICE_USER:$SERVICE_USER" "$DATA_ROOT"
|
|
$SUDO ln -sfn "$RELEASE_DIR" "$APP_ROOT/current"
|
|
|
|
printf '%s' "$STUDIO_PASSWORD" | $SUDO tee /etc/rednote-tool-studio.password >/dev/null
|
|
$SUDO chown root:"$SERVICE_USER" /etc/rednote-tool-studio.password
|
|
$SUDO chmod 640 /etc/rednote-tool-studio.password
|
|
|
|
cat > rednote-tool-studio.service <<EOF
|
|
[Unit]
|
|
Description=Rednote Content Studio
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=$SERVICE_USER
|
|
Group=$SERVICE_USER
|
|
WorkingDirectory=$APP_ROOT/current
|
|
Environment=NODE_ENV=production
|
|
Environment=HOSTNAME=0.0.0.0
|
|
Environment=PORT=$PORT
|
|
Environment=STUDIO_USERNAME=$STUDIO_USERNAME
|
|
ExecStart=/bin/sh -c 'export STUDIO_PASSWORD="\$(cat /etc/rednote-tool-studio.password)"; exec $APP_ROOT/bin/node server.js'
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
$SUDO install -m 0644 rednote-tool-studio.service /etc/systemd/system/rednote-tool-studio.service
|
|
$SUDO systemctl daemon-reload
|
|
$SUDO systemctl enable --now rednote-tool-studio.service
|
|
$SUDO systemctl restart rednote-tool-studio.service
|
|
|
|
for attempt in $(seq 1 30); do
|
|
if "$APP_ROOT/bin/node" -e "fetch('http://127.0.0.1:$PORT/api/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"; then
|
|
break
|
|
fi
|
|
if [ "$attempt" -eq 30 ]; then
|
|
$SUDO systemctl status rednote-tool-studio.service --no-pager || true
|
|
$SUDO journalctl -u rednote-tool-studio.service -n 100 --no-pager || true
|
|
exit 1
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
$SUDO systemctl is-enabled rednote-tool-studio.service
|
|
$SUDO systemctl is-active rednote-tool-studio.service
|
|
echo "Persistent deployment verified: service=rednote-tool-studio port=$PORT data=$DATA_ROOT/projects release=$RELEASE_SHA"
|