Refactor code structure for improved readability and maintainability #32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build, Test & Deploy to IONOS | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| jobs: | |
| build-test-deploy: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: 📥 Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: 🔧 Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: 📦 Install Dependencies | |
| run: npm ci | |
| - name: 🧪 Run Tests with Coverage | |
| run: npm test -- --no-watch --code-coverage --browsers=ChromeHeadless | |
| - name: 📊 Generate Coverage Badge | |
| run: | | |
| COVERAGE=$(grep -oP 'Lines\s*:\s*\K[\d.]+' coverage/portfolio/index.html | head -1 || echo "0") | |
| echo "Coverage: $COVERAGE%" | |
| echo "COVERAGE=$COVERAGE" >> $GITHUB_ENV | |
| - name: 📚 Generate TypeDoc Documentation | |
| run: npm run docs | |
| continue-on-error: true | |
| - name: 🏗️ Build Production | |
| run: npm run build -- --configuration production | |
| - name: 📁 Prepare Deployment Structure | |
| run: | | |
| mkdir -p deploy | |
| cp -r dist/portfolio/browser/* deploy/ | |
| cp -r coverage/portfolio deploy/coverage | |
| cp -r docs deploy/jsdoc | |
| # Create simple .htaccess for deployment | |
| cat > deploy/.htaccess << 'EOF' | |
| RewriteEngine On | |
| RewriteBase /portfolio/ | |
| DirectoryIndex index.html | |
| # Force HTTPS | |
| RewriteCond %{HTTPS} off | |
| RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] | |
| # API Routes Protection - Don't redirect to index.html | |
| RewriteCond %{REQUEST_URI} ^/portfolio/api/ | |
| RewriteRule . - [L] | |
| # Angular SPA Routing | |
| RewriteCond %{REQUEST_FILENAME} !-f | |
| RewriteCond %{REQUEST_FILENAME} !-d | |
| RewriteCond %{REQUEST_URI} !^/portfolio/api/ | |
| RewriteRule . index.html [L] | |
| # ------------------------------------------------------ | |
| # 🚀 Cache Static Assets | |
| # ------------------------------------------------------ | |
| <IfModule mod_expires.c> | |
| ExpiresActive on | |
| ExpiresByType text/css "access plus 1 year" | |
| ExpiresByType application/javascript "access plus 1 year" | |
| ExpiresByType image/png "access plus 1 year" | |
| ExpiresByType image/jpg "access plus 1 year" | |
| ExpiresByType image/jpeg "access plus 1 year" | |
| ExpiresByType image/webp "access plus 1 year" | |
| ExpiresByType image/avif "access plus 1 year" | |
| ExpiresByType image/svg+xml "access plus 1 year" | |
| ExpiresByType font/woff2 "access plus 1 year" | |
| ExpiresByType application/json "access plus 0 seconds" | |
| </IfModule> | |
| # ------------------------------------------------------ | |
| # 🔐 Security Headers | |
| # ------------------------------------------------------ | |
| <IfModule mod_headers.c> | |
| Header always set X-Content-Type-Options nosniff | |
| Header always set X-Frame-Options DENY | |
| Header always set X-XSS-Protection "1; mode=block" | |
| Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" | |
| Header unset Server | |
| </IfModule> | |
| EOF | |
| - name: 📤 Deploy to IONOS via SFTP | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| uses: Dylan700/sftp-upload-action@v1.1.4 | |
| with: | |
| server: ${{ secrets.FTP_HOST }} | |
| username: ${{ secrets.FTP_USERNAME }} | |
| password: ${{ secrets.FTP_PASSWORD }} | |
| port: 22 | |
| uploads: | | |
| ./deploy/ => ./portfolio/ | |
| delete: false | |
| - name: ✅ Deployment Complete | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| run: | | |
| echo "🎉 Deployment successful!" | |
| echo "🌐 App: https://portfolio.dev2k.org" | |
| echo "📊 Coverage: https://portfolio.dev2k.org/coverage/index.html" | |
| echo "📚 Docs: https://portfolio.dev2k.org/jsdoc/index.html" | |
| - name: 💬 Comment on PR | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| // COVERAGE is set in the "Run Tests & Coverage" step via GITHUB_ENV | |
| const coverage = '${{ env.COVERAGE }}' || 'N/A'; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: `## ✅ Build & Test Successful\n\n- 🧪 Tests: Passed\n- 📊 Coverage: ${coverage}%\n\nReady to merge!` | |
| }) |