Enhance deploy-test.sh with functions for Node.js version management, dependency installation, and public document synchronization. Implement checks for Node.js version requirements and improve error handling for document syncing. Update environment configuration in harheimertc.test.config.cjs to support development and test environments. Modify email recipient logic in contact and email service APIs to prevent notifications in test environments. Add tests to verify behavior in test conditions.
This commit is contained in:
@@ -3,7 +3,7 @@ name: Code Analysis and Production Deploy
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches: [ main, dev ]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -119,3 +119,34 @@ jobs:
|
||||
-p "${{ vars.PROD_PORT }}" \
|
||||
"${{ vars.PROD_USER }}@${{ vars.PROD_HOST }}" \
|
||||
"bash -lc 'cd /var/www/harheimertc && ./deploy-production.sh'"
|
||||
|
||||
deploy-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: analyze
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
|
||||
steps:
|
||||
- name: Prepare SSH
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p ~/.ssh
|
||||
printf "%s" "${{ secrets.PROD_SSH_KEY }}" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -p "${{ vars.PROD_PORT }}" "${{ vars.PROD_HOST }}" >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Test SSH connection
|
||||
run: |
|
||||
ssh -i ~/.ssh/id_ed25519 \
|
||||
-o StrictHostKeyChecking=no \
|
||||
-o BatchMode=yes \
|
||||
-p "${{ vars.PROD_PORT }}" \
|
||||
"${{ vars.PROD_USER }}@${{ vars.PROD_HOST }}" \
|
||||
"echo SSH OK"
|
||||
|
||||
- name: Run test deployment script
|
||||
run: |
|
||||
ssh -i ~/.ssh/id_ed25519 \
|
||||
-o BatchMode=yes \
|
||||
-p "${{ vars.PROD_PORT }}" \
|
||||
"${{ vars.PROD_USER }}@${{ vars.PROD_HOST }}" \
|
||||
"bash -lc 'cd /var/www/harheimertc.test && ./deploy-test.sh'"
|
||||
|
||||
@@ -67,6 +67,77 @@ has_tracked_files_under() {
|
||||
git ls-files "$prefix" | head -n 1 | grep -q .
|
||||
}
|
||||
|
||||
install_dependencies() {
|
||||
if [ -f "package-lock.json" ]; then
|
||||
echo " Running: npm ci"
|
||||
npm ci
|
||||
else
|
||||
echo " WARNING: package-lock.json fehlt. Führe npm install aus..."
|
||||
npm install
|
||||
fi
|
||||
}
|
||||
|
||||
use_project_node() {
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
if [ -f ".nvmrc" ]; then
|
||||
echo " Using Node version from .nvmrc..."
|
||||
nvm use
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_node_version() {
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
echo "ERROR: Node.js ist nicht im PATH."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local node_version
|
||||
node_version="$(node -p 'process.versions.node')"
|
||||
if ! node -e 'const [major, minor] = process.versions.node.split(".").map(Number); process.exit(major > 22 || (major === 22 && minor >= 12) ? 0 : 1)' >/dev/null 2>&1; then
|
||||
echo "ERROR: Node.js >= 22.12.0 wird benötigt, aktuell ist $node_version aktiv."
|
||||
echo "Bitte Node 22 installieren/aktivieren, z.B.:"
|
||||
echo " nvm install 22"
|
||||
echo " nvm alias default 22"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Node.js $node_version"
|
||||
}
|
||||
|
||||
sync_public_documents_to_build() {
|
||||
if [ ! -d "public/documents" ]; then
|
||||
echo " No public/documents directory to sync"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ ! -d ".output/public" ]; then
|
||||
echo "ERROR: .output/public fehlt, kann public/documents nicht synchronisieren."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p ".output/public/documents"
|
||||
cp -a "public/documents/." ".output/public/documents/"
|
||||
echo " ✓ public/documents -> .output/public/documents synchronisiert"
|
||||
|
||||
local template_pdf="beitrittserklärung_template.pdf"
|
||||
if [ -f "public/documents/$template_pdf" ]; then
|
||||
local source_size output_size
|
||||
source_size=$(stat -f%z "public/documents/$template_pdf" 2>/dev/null || stat -c%s "public/documents/$template_pdf" 2>/dev/null || echo "0")
|
||||
output_size=$(stat -f%z ".output/public/documents/$template_pdf" 2>/dev/null || stat -c%s ".output/public/documents/$template_pdf" 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$source_size" != "$output_size" ] || [ "$source_size" = "0" ]; then
|
||||
echo "ERROR: .output/public/documents/$template_pdf stimmt nicht mit public/documents überein (Source: $source_size, Output: $output_size)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " ✓ $template_pdf im Build verifiziert ($output_size bytes)"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "0. Ensuring persistent data directories (recommended)..."
|
||||
# IMPORTANT: Only symlink server/data if it's not tracked by git.
|
||||
if has_tracked_files_under "server/data"; then
|
||||
@@ -143,7 +214,9 @@ git clean -fd \
|
||||
|
||||
# Pull latest changes
|
||||
echo " Pulling latest changes..."
|
||||
if ! git pull --ff-only; then
|
||||
git fetch origin dev
|
||||
git checkout dev
|
||||
if ! git pull --ff-only origin dev; then
|
||||
echo "ERROR: git pull fehlgeschlagen."
|
||||
echo ""
|
||||
echo "Häufige Ursache: SSH-Key für den aktuellen User fehlt."
|
||||
@@ -170,7 +243,9 @@ fi
|
||||
# 3. Install dependencies
|
||||
echo ""
|
||||
echo "3. Installing dependencies..."
|
||||
npm install
|
||||
use_project_node
|
||||
ensure_node_version
|
||||
install_dependencies
|
||||
|
||||
# 4. Remove old build (but keep data!)
|
||||
echo ""
|
||||
@@ -204,7 +279,7 @@ fi
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo ""
|
||||
echo "WARNING: node_modules fehlt. Installiere Dependencies..."
|
||||
npm install
|
||||
install_dependencies
|
||||
fi
|
||||
|
||||
# 5. Build
|
||||
@@ -217,7 +292,7 @@ echo " (This may take a few minutes...)"
|
||||
echo " Checking dependencies..."
|
||||
if [ ! -f "node_modules/.package-lock.json" ] && [ ! -f "package-lock.json" ]; then
|
||||
echo " WARNING: package-lock.json fehlt. Führe npm install aus..."
|
||||
npm install
|
||||
install_dependencies
|
||||
fi
|
||||
|
||||
# Build mit expliziter Fehlerbehandlung und Output-Capture
|
||||
@@ -234,6 +309,10 @@ if [ "$BUILD_EXIT_CODE" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Synchronizing public documents into build output..."
|
||||
sync_public_documents_to_build
|
||||
|
||||
# Prüfe auf Warnungen im Build-Output, die auf Probleme hinweisen
|
||||
if echo "$BUILD_OUTPUT" | grep -qi "error\|failed\|missing"; then
|
||||
echo ""
|
||||
|
||||
@@ -10,7 +10,8 @@ try {
|
||||
// Helper function to create env object
|
||||
function createEnv(port) {
|
||||
return {
|
||||
NODE_ENV: 'production',
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
APP_ENV: process.env.APP_ENV || 'test',
|
||||
PORT: port,
|
||||
// Secrets/Config (loaded from .env above, if present)
|
||||
ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
|
||||
|
||||
@@ -24,6 +24,12 @@ async function loadConfig() {
|
||||
}
|
||||
|
||||
async function collectRecipients(config) {
|
||||
const isProduction = process.env.NODE_ENV === 'production' && process.env.APP_ENV !== 'test'
|
||||
|
||||
if (!isProduction) {
|
||||
return ['tsschulz@tsschulz.de']
|
||||
}
|
||||
|
||||
const recipients = []
|
||||
|
||||
// Vorstand
|
||||
|
||||
@@ -41,7 +41,7 @@ async function loadConfig() {
|
||||
* @returns {Array<string>} Email addresses
|
||||
*/
|
||||
function getEmailRecipients(data, config) {
|
||||
const isProduction = process.env.NODE_ENV === 'production'
|
||||
const isProduction = process.env.NODE_ENV === 'production' && process.env.APP_ENV !== 'test'
|
||||
|
||||
if (!isProduction) {
|
||||
return ['tsschulz@tsschulz.de']
|
||||
@@ -215,4 +215,4 @@ export async function sendRegistrationNotification(data) {
|
||||
console.error('sendRegistrationNotification failed:', error.message || error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,11 @@ import resetPasswordHandler from '../server/api/auth/reset-password.post.js'
|
||||
import statusHandler from '../server/api/auth/status.get.js'
|
||||
|
||||
describe('Auth API Endpoints', () => {
|
||||
afterEach(() => {
|
||||
delete process.env.NODE_ENV
|
||||
delete process.env.APP_ENV
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
// Setze SMTP-Credentials für Tests
|
||||
process.env.SMTP_USER = 'test@example.com'
|
||||
@@ -171,6 +176,30 @@ describe('Auth API Endpoints', () => {
|
||||
})
|
||||
expect(nodemailer.default.createTransport).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('benachrichtigt in Testumgebung nicht die Vorstand-Empfänger', async () => {
|
||||
process.env.NODE_ENV = 'production'
|
||||
process.env.APP_ENV = 'test'
|
||||
|
||||
const event = createEvent()
|
||||
mockSuccessReadBody({
|
||||
name: 'Max',
|
||||
email: 'max@example.com',
|
||||
password: '12345678',
|
||||
phone: '123',
|
||||
geburtsdatum: '2000-01-01'
|
||||
})
|
||||
authUtils.readUsers.mockResolvedValue([])
|
||||
authUtils.hashPassword.mockResolvedValue('hashed')
|
||||
authUtils.writeUsers.mockResolvedValue(true)
|
||||
|
||||
await registerHandler(event)
|
||||
|
||||
const transporter = nodemailer.default.createTransport.mock.results[0].value
|
||||
expect(transporter.sendMail).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
||||
to: 'tsschulz@tsschulz.de'
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /api/auth/reset-password', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createEvent, mockSuccessReadBody } from './setup'
|
||||
import fsPromises from 'fs/promises'
|
||||
import { promises as fs } from 'fs'
|
||||
@@ -26,6 +26,11 @@ import termineHandler from '../server/api/termine.get.js'
|
||||
import spielplaeneHandler from '../server/api/spielplaene.get.js'
|
||||
|
||||
describe('Öffentliche API-Endpunkte', () => {
|
||||
afterEach(() => {
|
||||
delete process.env.NODE_ENV
|
||||
delete process.env.APP_ENV
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
// Setze SMTP-Credentials für Tests
|
||||
process.env.SMTP_USER = 'test@example.com'
|
||||
@@ -58,6 +63,21 @@ describe('Öffentliche API-Endpunkte', () => {
|
||||
expect(response.success).toBe(true)
|
||||
expect(nodemailer.default.createTransport).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('sendet in Testumgebung nicht an Vorstand-Empfänger', async () => {
|
||||
process.env.NODE_ENV = 'production'
|
||||
process.env.APP_ENV = 'test'
|
||||
|
||||
const event = createEvent()
|
||||
mockSuccessReadBody({ name: 'Max', email: 'max@example.com', subject: 'Frage', message: 'Hallo' })
|
||||
|
||||
await contactHandler(event)
|
||||
|
||||
const transporter = nodemailer.default.createTransport.mock.results[0].value
|
||||
expect(transporter.sendMail).toHaveBeenCalledWith(expect.objectContaining({
|
||||
to: 'tsschulz@tsschulz.de'
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/galerie', () => {
|
||||
|
||||
Reference in New Issue
Block a user