CI - Test & Build / Lint & Type Check (push) Canceled after 0s
CI - Test & Build / Backend Unit Tests (push) Canceled after 0s
CI - Test & Build / Frontend Build (push) Canceled after 0s
CI - Test & Build / Security Scan (push) Canceled after 0s
CI - Test & Build / E2E Tests (Playwright) (push) Canceled after 0s
CI - Test & Build / CI Summary (push) Canceled after 0s
* feat: move login credentials to a DB-backed users table with an admin management page Replaces the hardcoded AUTH_USERNAME/AUTH_PASSWORD login check with a new auth_accounts table (bcrypt-hashed passwords, admin/user roles). Adds admin-only /api/users CRUD routes and a "Brugere" admin page in the frontend for managing logins without redeploying. Removes the unused, unmounted duplicate login route in src/routes/auth.js. * docs: add architecture codemaps with diagrams for the whole system Adds codemaps/architecture.md, backend.md, frontend.md, and data.md — Mermaid-diagrammed design documentation verified against the live codebase and database rather than assumed from CLAUDE.md. Covers the unified-server.js request flow (mounted routers + ~183 inline routes), 68 backend services grouped by domain, the frontend's state-driven view-switch (no React Router in practice despite BrowserRouter being present), and the full 122-table DB schema with the auth_accounts vs unrelated users table naming trap flagged explicitly. Links added from the root README. Co-Authored-By: Claude Sonnet 5 <[email protected]> * feat: ship canonical roof quote workflow * fix: keep migration dry-run idempotent * [verified] feat: complete Smart Pakker management * [verified] fix: ignore blank task dependencies * [verified] fix: align package duplication with schema * [verified] fix: enforce Discord status limits * [verified] fix: link Smart Pakke materials safely * [verified] fix: harden material link review * [verified] feat: improve material matching * fix: scope pitch validation to roof packages * fix: support canonical snapshots on production schema * [verified] fix: hide internal package metadata from PDF * [verified] feat: deliver sales-ready customer PDF * [verified] feat: ship sales-ready PDF with AI overview * [verified] fix: authenticate project list requests * [verified] fix: refresh project-list authentication * [verified] fix: open existing project details * [verified] fix: keep roof components searchable in builder * [verified] fix: expose all Smart Package categories * [verified] fix: authenticate project creation * [verified] feat: make Smart Pakker the universal project flow * [verified] feat: preview Smart Package contents * [verified] test: keep generic release isolated from downpipe work * feat: add first-class Smart Pakke rentals * [verified] feat: add gutter and downpipe smart packages * [verified] fix: prepare six-house gutter quote flow * [verified] fix: open generic quotes without roof geometry * [verified] fix: review generic quotes with authenticated APIs * [verified] fix: calculate generic Smart Package quotes * [verified] fix: return generic calculation breakdown * feat: checkpoint generic signed snapshot validation with red-green tests * feat: complete fail-closed generic quote approval and customer PDF flow * feat: use generic signed snapshot in final review * feat: redesign generic quote final review * fix: harden generic review summaries * feat: add auditable six-house package basis * [verified] feat: finish auditable Smart Pakke UI * [verified] fix: bind auditable quantity and price bases * [verified] fix: keep six-house basis across package versions * [verified] fix: complete smart package discovery management * [verified] fix: simplify composition and generic scope * [verified] test: keep explicit roof contracts fail closed * [verified] fix: harden generic quote snapshots * fix: make generic quote delivery customer safe * [verified] fix: secure package catalog reads * [verified] fix: close workspace provenance blockers * fix: harden customer document language boundary * [verified] fix: secure smart package internal reads * fix: version package child mutations atomically * feat: add generic customer quote text flow * [verified] fix: allow manual customer numbers * [verified] fix: expose optional roof geometry * [verified] fix: rebase hydrated packages after geometry edits * [verified] feat: add free editable site area map * [verified] fix: harden map recovery and geocoding gate * fix: bind map quantities to authoritative geometry * fix: release geocoder lock before dispatch * fix: separate roof and site geometry provenance * fix: revoke stale admin authorization * fix: migrate task geometry basis * fix: make backend CI dependency-complete * ci: seed isolated e2e login account * fix: allow clean database bootstrap * fix: skip indexes for optional tables * test: use canonical mansard geometry in e2e * [verified] fix(auth): enforce live operator boundary * fix: fail close Ordrestyring offer transport * fix(frontend): authenticate customer project requests * fix: align canonical roof type contract * [verified] fix: reconcile legacy package labor safely * [verified] fix: audit site geometry deletion * docs: add PR 31 reviewer guide * docs: synchronize Obsidian vault * docs: sync integrated reviewer guide to Obsidian * ci: seed isolated auth account explicitly * fix: close offer bootstrap and service readiness gaps * fix: authenticate protected package callers * fix: provision initial admin and disable generic send * [verified] fix: close final quote release blockers * [verified] fix: seed gutter packages before deployment --------- Co-authored-by: alexpolo1 <[email protected]> Co-authored-by: Claude Sonnet 5 <[email protected]>
274 lines
8.4 KiB
YAML
274 lines
8.4 KiB
YAML
name: Deploy to Test Environment
|
|
|
|
on:
|
|
push:
|
|
branches: [ develop ]
|
|
workflow_dispatch:
|
|
inputs:
|
|
branch:
|
|
description: 'Branch to deploy'
|
|
required: true
|
|
default: 'develop'
|
|
|
|
env:
|
|
NODE_VERSION: '22'
|
|
TEST_SERVER_HOST: ${{ secrets.TEST_SERVER_HOST }}
|
|
TEST_SERVER_USER: ${{ secrets.TEST_SERVER_USER }}
|
|
TEST_SERVER_PATH: ${{ secrets.TEST_SERVER_PATH }}
|
|
TEST_APP_URL: ${{ secrets.TEST_APP_URL }}
|
|
|
|
jobs:
|
|
# ============================================
|
|
# Build & Test
|
|
# ============================================
|
|
build:
|
|
name: Build Application
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event.inputs.branch || github.ref }}
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
cache: 'npm'
|
|
|
|
- name: Generate version
|
|
id: version
|
|
run: |
|
|
VERSION="test-$(date +'%Y%m%d-%H%M%S')-${GITHUB_SHA::7}"
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
echo "Version: $VERSION"
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
npm ci
|
|
cd frontend && npm ci
|
|
cd ../backend && npm ci
|
|
|
|
- name: Run backend tests
|
|
working-directory: backend
|
|
run: npm test -- --passWithNoTests
|
|
continue-on-error: true
|
|
|
|
- name: Build frontend
|
|
working-directory: frontend
|
|
env:
|
|
CI: false
|
|
REACT_APP_ENV: test
|
|
REACT_APP_VERSION: ${{ steps.version.outputs.version }}
|
|
run: npm run build
|
|
|
|
- name: Create deployment package
|
|
run: |
|
|
mkdir -p deploy-package
|
|
cp -r frontend/build deploy-package/frontend-build
|
|
cp -r backend deploy-package/backend
|
|
cp -r database deploy-package/database
|
|
cp ecosystem.config.js deploy-package/
|
|
cp package.json deploy-package/
|
|
rm -rf deploy-package/backend/node_modules
|
|
echo "${{ steps.version.outputs.version }}" > deploy-package/VERSION
|
|
|
|
- name: Upload deployment package
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: deploy-package-test
|
|
path: deploy-package
|
|
retention-days: 3
|
|
|
|
# ============================================
|
|
# Deploy to Test Server
|
|
# ============================================
|
|
deploy:
|
|
name: Deploy to Test Server
|
|
runs-on: ubuntu-latest
|
|
needs: build
|
|
environment:
|
|
name: test
|
|
url: ${{ env.TEST_APP_URL }}
|
|
steps:
|
|
- name: Download deployment package
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: deploy-package-test
|
|
path: deploy-package
|
|
|
|
- name: Setup SSH
|
|
uses: webfactory/[email protected]
|
|
with:
|
|
ssh-private-key: ${{ secrets.TEST_SSH_PRIVATE_KEY }}
|
|
|
|
- name: Add server to known hosts
|
|
run: |
|
|
mkdir -p ~/.ssh
|
|
ssh-keyscan -H ${{ env.TEST_SERVER_HOST }} >> ~/.ssh/known_hosts
|
|
|
|
- name: Deploy to test server
|
|
run: |
|
|
# Create deployment directory
|
|
ssh ${{ env.TEST_SERVER_USER }}@${{ env.TEST_SERVER_HOST }} "mkdir -p ${{ env.TEST_SERVER_PATH }}/releases/${{ needs.build.outputs.version }}"
|
|
|
|
# Upload deployment package
|
|
scp -r deploy-package/* ${{ env.TEST_SERVER_USER }}@${{ env.TEST_SERVER_HOST }}:${{ env.TEST_SERVER_PATH }}/releases/${{ needs.build.outputs.version }}/
|
|
|
|
# Run deployment script on server
|
|
ssh ${{ env.TEST_SERVER_USER }}@${{ env.TEST_SERVER_HOST }} << 'DEPLOY_SCRIPT'
|
|
set -e
|
|
|
|
DEPLOY_PATH="${{ env.TEST_SERVER_PATH }}"
|
|
VERSION="${{ needs.build.outputs.version }}"
|
|
RELEASE_PATH="$DEPLOY_PATH/releases/$VERSION"
|
|
|
|
echo "=== Deploying version: $VERSION ==="
|
|
|
|
# Install backend dependencies
|
|
cd $RELEASE_PATH/backend
|
|
npm ci --production
|
|
|
|
# Copy environment file
|
|
cp $DEPLOY_PATH/shared/.env $RELEASE_PATH/backend/.env
|
|
|
|
# Apply roof catalog contracts and fail-closed lifecycle changes
|
|
# before the release can become current or accept traffic.
|
|
cd $RELEASE_PATH
|
|
node database/migrations/20260902_gutter_meter_packages.js --apply
|
|
node database/migrations/20260910_complete_roof_package_contract.js
|
|
node database/migrations/20260918_fail_closed_roof_lifecycle.js --apply
|
|
|
|
# Copy frontend build to correct location
|
|
mkdir -p $RELEASE_PATH/backend/../frontend
|
|
mv $RELEASE_PATH/frontend-build $RELEASE_PATH/frontend/build
|
|
|
|
# Update symlink
|
|
ln -sfn $RELEASE_PATH $DEPLOY_PATH/current
|
|
|
|
# Restart PM2
|
|
cd $DEPLOY_PATH/current
|
|
pm2 restart ecosystem.config.js --env test || pm2 start ecosystem.config.js --env test
|
|
|
|
# Cleanup old releases (keep last 5)
|
|
cd $DEPLOY_PATH/releases
|
|
ls -t | tail -n +6 | xargs -r rm -rf
|
|
|
|
echo "=== Deployment complete ==="
|
|
DEPLOY_SCRIPT
|
|
|
|
# ============================================
|
|
# Post-Deploy Verification
|
|
# ============================================
|
|
verify:
|
|
name: Verify Deployment
|
|
runs-on: ubuntu-latest
|
|
needs: deploy
|
|
steps:
|
|
- name: Wait for server startup
|
|
run: sleep 15
|
|
|
|
- name: Health check
|
|
run: |
|
|
MAX_RETRIES=5
|
|
RETRY_COUNT=0
|
|
|
|
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
|
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" ${{ env.TEST_APP_URL }}/api/health || echo "000")
|
|
|
|
if [ "$HTTP_STATUS" = "200" ]; then
|
|
echo "Health check passed!"
|
|
exit 0
|
|
fi
|
|
|
|
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
echo "Health check failed (HTTP $HTTP_STATUS), retry $RETRY_COUNT/$MAX_RETRIES..."
|
|
sleep 10
|
|
done
|
|
|
|
echo "Health check failed after $MAX_RETRIES retries"
|
|
exit 1
|
|
|
|
- name: API smoke tests
|
|
run: |
|
|
echo "Testing API endpoints..."
|
|
|
|
# Test health endpoint
|
|
curl -f ${{ env.TEST_APP_URL }}/api/health
|
|
|
|
# Test materials endpoint
|
|
curl -f ${{ env.TEST_APP_URL }}/api/materials || true
|
|
|
|
# Test smart packages endpoint
|
|
curl -f ${{ env.TEST_APP_URL }}/api/smart-packages/ || true
|
|
|
|
echo "Smoke tests completed!"
|
|
|
|
- name: Notify deployment success
|
|
if: success()
|
|
run: |
|
|
echo "Test deployment successful!"
|
|
echo "Version: ${{ needs.build.outputs.version }}"
|
|
echo "URL: ${{ env.TEST_APP_URL }}"
|
|
|
|
- name: Notify deployment failure
|
|
if: failure()
|
|
run: |
|
|
echo "Test deployment failed!"
|
|
echo "Please check the logs for more details."
|
|
exit 1
|
|
|
|
# ============================================
|
|
# Run E2E Tests on Test Environment
|
|
# ============================================
|
|
e2e-tests:
|
|
name: E2E Tests on Test Environment
|
|
runs-on: ubuntu-latest
|
|
needs: verify
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
cache: 'npm'
|
|
|
|
- name: Install test dependencies
|
|
working-directory: tests
|
|
run: npm ci
|
|
|
|
- name: Install Playwright browsers
|
|
working-directory: tests
|
|
run: npx playwright install --with-deps chromium
|
|
|
|
- name: Run Playwright tests
|
|
working-directory: tests
|
|
env:
|
|
PLAYWRIGHT_BASE_URL: ${{ env.TEST_APP_URL }}
|
|
run: npx playwright test --project=chromium --reporter=html
|
|
continue-on-error: true
|
|
|
|
- name: Upload test report
|
|
uses: actions/upload-artifact@v7
|
|
if: always()
|
|
# Artifact upload must never fail the job (storage quota can be full)
|
|
continue-on-error: true
|
|
with:
|
|
name: e2e-test-report
|
|
path: tests/playwright-report
|
|
retention-days: 3
|
|
|
|
- name: Upload test screenshots
|
|
uses: actions/upload-artifact@v7
|
|
if: failure()
|
|
# Artifact upload must never fail the job (storage quota can be full)
|
|
continue-on-error: true
|
|
with:
|
|
name: e2e-test-screenshots
|
|
path: tests/test-results
|
|
retention-days: 3
|