diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f1c8c71..8920b47 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,22 +4,37 @@ ## Type of Change -- [ ] Bug fix (non-breaking change that fixes an issue) -- [ ] New feature (non-breaking change that adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Documentation update +- [ ] `feat` - New feature (minor version bump) +- [ ] `fix` - Bug fix (patch version bump) +- [ ] `perf` - Performance improvement (patch version bump) +- [ ] `docs` - Documentation only +- [ ] `refactor` - Code refactoring +- [ ] `test` - Adding/updating tests +- [ ] `ci` - CI/CD changes +- [ ] `chore` - Maintenance tasks + +## Breaking Changes + +- [ ] This is a breaking change (major version bump) + +**If breaking, describe the migration path:** ## Checklist -- [ ] I have followed the [Contributing Guidelines](CONTRIBUTING.md) +- [ ] Commit messages follow [Conventional Commits](CONTRIBUTING.md#commit-guidelines) - [ ] Lint passes (`npm run lint`) - [ ] Build succeeds (`npm run build`) -- [ ] I have tested my changes locally +- [ ] Tests pass (`npm run test:run`) +- [ ] API documentation updated (if applicable) + +## Testing + + ## Screenshots (if applicable) - + ## Related Issues - \ No newline at end of file + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21dac7a..fbe4c13 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,60 +1,164 @@ -# Contributing to CV +# Contributing Guidelines -Thank you for your interest in contributing! +Thank you for your interest in contributing to this project! This document provides guidelines and instructions for contributing. -## Quick Start +## Code of Conduct + +Be respectful and constructive. Treat all contributors with courtesy. + +## Getting Started + +### Prerequisites + +- Node.js 20+ +- npm 10+ +- Gitleaks (for secret scanning) + +### Setup 1. Fork the repository -2. Create your feature branch (`git checkout -b feature/amazing-feature`) -3. Make your changes -4. Run linting (`npm run lint`) -5. Commit your changes (`git commit -m 'Add amazing feature'`) -6. Push to the branch (`git push origin feature/amazing-feature`) -7. Open a Pull Request +2. Clone your fork +3. Install dependencies: -## Development Guidelines +```bash +npm install +cd backend && npm install && cd .. +``` + +4. Install Gitleaks (for pre-commit hooks): + +```bash +# macOS +brew install gitleaks + +# Linux +curl -sSfL https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_linux_x64.tar.gz | tar -xz +sudo mv gitleaks /usr/local/bin/ +``` + +## Development Workflow + +### Branch Naming + +- `feat/feature-name` - New features +- `fix/bug-name` - Bug fixes +- `docs/topic` - Documentation changes +- `refactor/component` - Code refactoring +- `test/test-name` - Test additions/changes + +### Commit Guidelines + +This project uses [Conventional Commits](https://www.conventionalcommits.org/): + +``` +(): + +[optional body] + +[optional footer] +``` + +#### Types + +| Type | Description | Version Impact | +|------|-------------|----------------| +| `feat` | New feature | Minor | +| `fix` | Bug fix | Patch | +| `perf` | Performance improvement | Patch | +| `revert` | Revert previous commit | Patch | +| `docs` | Documentation only | None | +| `style` | Code style (formatting) | None | +| `refactor` | Code refactoring | None | +| `test` | Adding/updating tests | None | +| `build` | Build system changes | None | +| `ci` | CI/CD changes | None | +| `chore` | Maintenance tasks | None | + +#### Scopes + +| Scope | Description | +|-------|-------------| +| `admin` | Admin panel components | +| `api` | Backend API | +| `ui` | Frontend UI components | +| `docker` | Docker configuration | +| `ci` | CI/CD workflows | +| `deps` | Dependencies | +| `release` | Release configuration | +| `auth` | Authentication | + +#### Examples + +```bash +feat(admin): add export to PDF functionality +fix(api): resolve JWT token expiration issue +docs(readme): update installation instructions +ci(workflow): add staging deployment +``` + +### Pre-commit Hooks + +The project uses Husky for git hooks: + +- **pre-commit**: Runs ESLint and Gitleaks secret scanning +- **commit-msg**: Validates commit message format with commitlint ### Code Style -- Follow the existing code style +- ESLint configuration is enforced - Run `npm run lint` before committing -- Use meaningful commit messages +- Fix issues with `npm run lint -- --fix` -### Component Structure +### Testing -```jsx -// Imports at top -import { motion } from 'framer-motion'; -import { Icon } from 'lucide-react'; - -// Component definition -export default function Component() { - return ( - // JSX - ); -} -``` - -### Testing Your Changes +Run tests before submitting: ```bash -# Start dev server -npm run dev +# Unit tests +npm run test:run -# Build for production -npm run build +# Integration tests +npm run test:integration -# Preview production build -npm run preview +# E2E tests (requires running server) +npm run test:e2e + +# All tests with coverage +npm run test:coverage ``` -## Pull Request Checklist +## Pull Request Process -- [ ] Code follows the existing style -- [ ] Linting passes (`npm run lint`) +1. Create a feature branch from `master` +2. Make your changes following the guidelines above +3. Ensure all tests pass +4. Ensure lint passes (`npm run lint`) +5. Ensure build succeeds (`npm run build`) +6. Push to your fork and create a pull request +7. Fill out the PR template completely +8. Wait for review + +### PR Checklist + +- [ ] Follows contributing guidelines +- [ ] Commit messages follow conventional commits +- [ ] Lint passes (`npm run lint`) +- [ ] Tests pass (`npm run test:run`) - [ ] Build succeeds (`npm run build`) -- [ ] Changes are documented in PR description +- [ ] Documentation updated if needed + +## Release Process + +Releases are automated via semantic-release: + +1. Merge PR to `master` +2. CI runs tests and build +3. semantic-release analyzes commits +4. Version bumped, changelog updated +5. Git tag and release created + +No manual release steps required. ## Questions? -Feel free to open an issue for any questions or discussions. \ No newline at end of file +Open an issue for questions or discussions about contributions. diff --git a/README.md b/README.md index 74396b2..ca428d0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # CV - Tuan-Dat Tran ![License](https://img.shields.io/badge/license-MIT-blue.svg) -![React](https://img.shields.io/badge/React-18-61DAFB?logo=react) -![Tailwind CSS](https://img.shields.io/badge/Tailwind-4.1-38B2AC?logo=tailwindcss) +![React](https://img.shields.io/badge/React-19-61DAFB?logo=react) +![Tailwind CSS](https://img.shields.io/badge/Tailwind-4-38B2AC?logo=tailwindcss) ![Vite](https://img.shields.io/badge/Vite-7-646CFF?logo=vite) A modern, minimalist CV/Resume single-page application with admin panel and persistent storage. @@ -29,32 +29,33 @@ A modern, minimalist CV/Resume single-page application with admin panel and pers - Modern, responsive design - Smooth scroll animations with Framer Motion -- Admin panel for easy CV editing (password protected) +- Admin panel for CV editing (password protected) - Persistent storage with SQLite - Docker Compose deployment -- API-based architecture +- RESTful API with OpenAPI documentation - Optional Keycloak integration for SSO +- Automated releases with semantic-release ## Tech Stack ### Frontend -- **React 18** - UI Library -- **Vite** - Build Tool +- **React 19** - UI Library +- **Vite 7** - Build Tool - **Tailwind CSS 4** - Styling -- **Framer Motion** - Animations +- **Framer Motion 12** - Animations - **Lucide React** - Icons ### Backend -- **Express.js** - API Server -- **SQLite** - Database -- **better-sqlite3** - SQLite driver +- **Express.js 4** - API Server +- **Knex.js** - SQL Query Builder +- **SQLite** with **better-sqlite3** - Database ## Getting Started ### Prerequisites -- Node.js 18+ -- npm 9+ +- Node.js 20+ +- npm 10+ - Docker & Docker Compose (optional) ### Option 1: Docker Compose (Recommended) @@ -66,6 +67,7 @@ docker-compose up -d # Frontend: http://localhost:5173 # Backend API: http://localhost:3001 # Admin Panel: http://localhost:5173/admin +# API Docs: http://localhost:3001/api/docs ``` ### Option 2: Local Development @@ -86,27 +88,28 @@ npm run dev ### Environment Variables -Frontend (`.env`): -``` -VITE_API_URL=http://localhost:3001 -``` - Backend (`backend/.env`): ``` PORT=3001 DB_PATH=./data/cv.db -USE_KEYCLOAK=false -# Keycloak settings (required if USE_KEYCLOAK=true) +AUTH_MODE=simple +# Keycloak settings (required if AUTH_MODE=keycloak) KEYCLOAK_URL=https://keycloak.example.com KEYCLOAK_REALM=your-realm -KEYCLOAK_CLIENT_ID=cv-admin +KEYCLOAK_CLIENT_ID=cv-app ``` ## Development Setup ### Pre-commit Setup -This project uses Gitleaks for secret scanning. Install it before committing: +This project uses: +- **ESLint** for code linting +- **commitlint** for commit message validation +- **Husky** for git hooks +- **Gitleaks** for secret scanning + +Install Gitleaks before committing: ```bash # macOS @@ -120,152 +123,41 @@ sudo mv gitleaks /usr/local/bin/ scoop install gitleaks ``` -### API Documentation +### Commit Guidelines -API documentation is available at `/api/docs` when running the server. - -Access the interactive Swagger UI at: `http://localhost:3001/api/docs` - -## Admin Authentication - -### Simple Mode (Default) -- A random password is generated and printed to the console on server startup -- Look for the "ADMIN PASSWORD" banner in the logs -- Enter this password to access the admin panel - -### Keycloak Mode -Set `USE_KEYCLOAK=true` and configure Keycloak environment variables: -```bash -USE_KEYCLOAK=true -KEYCLOAK_URL=https://keycloak.example.com -KEYCLOAK_REALM=your-realm -KEYCLOAK_CLIENT_ID=cv-admin -``` - -## API Endpoints - -| Method | Endpoint | Auth | Description | -|--------|----------|------|-------------| -| GET | `/api/cv` | No | Get CV data | -| PUT | `/api/cv` | Yes | Update CV data | -| GET | `/api/cv/export` | No | Export as JSON | -| POST | `/api/cv/import` | Yes | Import JSON data | -| GET | `/api/auth/config` | No | Get auth configuration | -| POST | `/api/auth/login` | No | Login with password | -| GET | `/api/docs` | No | Swagger API documentation | -| GET | `/health` | No | Health check | - -## Project Structure - -``` -. -├── src/ # Frontend source -│ ├── components/ # CV components -│ ├── admin/ # Admin panel -│ ├── lib/ # Utilities -│ └── App.jsx -├── backend/ # Backend source -│ ├── routes/ # API routes -│ ├── db/ # Database -│ └── server.js -├── docker-compose.yml -├── Dockerfile -└── nginx.conf -``` - -## Testing - -```bash -# Frontend tests -npm run test:run - -# Backend tests -cd backend && npm test -``` - -## Release Process - -This project uses [semantic-release](https://semantic-release.gitbook.io/) for automated versioning and releases. - -### Conventional Commits - -All commit messages must follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: +This project uses [Conventional Commits](https://www.conventionalcommits.org/): ``` (): -[optional body] - -[optional footer] +# Examples: +feat(ui): add export button +fix(api): resolve authentication issue +docs(readme): update installation steps ``` -#### Commit Types +### API Documentation -| Type | Description | Version Bump | -|------|-------------|--------------| -| `feat` | New feature | Minor | -| `fix` | Bug fix | Patch | -| `feat!` or `BREAKING CHANGE` | Breaking change | Major | -| `docs`, `style`, `refactor`, `test`, `build`, `ci`, `chore` | Non-release changes | None | +Interactive Swagger UI available at: `http://localhost:3001/api/docs` -#### Scopes +## Admin Authentication -Available scopes: `admin`, `api`, `ui`, `docker`, `ci`, `deps`, `release`, `auth`, `skills`, `experience`, `education`, `projects`, `personal` +### Simple Mode (Default) +- Random password generated on server startup +- Password displayed in console logs +- JWT token-based authentication -### Release Workflow - -1. Push to `master` branch -2. CI runs tests and linting -3. semantic-release analyzes commits -4. If release needed: - - Version is bumped in package.json - - CHANGELOG.md is updated - - Git tag is created - - GitHub release is published - - Docker images are built and pushed - -### Docker Images - -Images are published to both Docker Hub and GitHub Container Registry: - -| Tag | Description | -|-----|-------------| -| `latest` | Latest stable release | -| `v1.2.3` | Specific version | -| `staging` | Staging environment | -| `nightly`, `edge` | Daily builds from master | -| `YYYY-MM-DD` | Dated nightly build | - -```bash -# Pull from Docker Hub -docker pull username/cv-app:latest - -# Pull from GHCR -docker pull ghcr.io/owner/cv-app:latest -``` - -### Environments - -| Environment | Branch | Trigger | -|-------------|--------|---------| -| Production | `master` | semantic-release | -| Staging | `staging` | Push to staging | -| Nightly | `master` | Daily at 02:00 UTC | - -For detailed documentation, see [Release Engineering Documentation](docs/release-engineering.md). +### Keycloak Mode +- SSO integration via Keycloak +- Configure via `AUTH_MODE=keycloak` and related environment variables ## Documentation | Document | Description | |----------|-------------| -| [Release Engineering](docs/release-engineering.md) | Comprehensive release pipeline documentation | -| [Contributing](CONTRIBUTING.md) | Contribution guidelines | -| [Security](SECURITY.md) | Security policy | - -## Contributing - -Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details. +| [Architecture](docs/architecture.md) | System architecture and design patterns | +| [Release Engineering](docs/release-engineering.md) | Release and deployment documentation | ## License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. +MIT License - see [LICENSE](LICENSE) for details. diff --git a/SECURITY.md b/SECURITY.md index 22319c5..ff4dae6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,33 +4,124 @@ | Version | Supported | | ------- | ------------------ | -| main | :white_check_mark: | +| 1.x | :white_check_mark: | +| < 1.0 | :x: | ## Reporting a Vulnerability -We take security seriously. If you discover a security vulnerability, please follow these steps: +We take security seriously. If you discover a security vulnerability, please follow responsible disclosure. -1. **Do not** open a public issue -2. Email the maintainer directly at `tuan-dat.tran@example.com` -3. Include: - - Description of the vulnerability - - Steps to reproduce - - Potential impact - - Suggested fix (if any) +### How to Report -### What to Expect +**Preferred Method**: Open a security advisory -- Acknowledgment within 48 hours -- Assessment within 7 days -- Fix timeline based on severity: - - Critical: 24-72 hours - - High: 1 week - - Medium/Low: Next release +1. Go to the repository +2. Click "Security" tab +3. Click "Report a vulnerability" +4. Fill out the advisory form -### Disclosure Policy +**Alternative**: Email the maintainer directly (if available in profile) -- Please allow time for the fix before public disclosure +### What to Include + +- Description of the vulnerability +- Steps to reproduce +- Affected versions +- Potential impact +- Suggested fix (if available) + +### Response Timeline + +| Action | Timeline | +|--------|----------| +| Initial response | Within 48 hours | +| Vulnerability assessment | Within 7 days | +| Fix timeline based on severity | See below | + +### Fix Timeline by Severity + +| Severity | Timeline | +|----------|----------| +| Critical | 24-72 hours | +| High | 1 week | +| Medium | Next release | +| Low | Next release | + +## Security Measures + +This project implements several security measures: + +### Code Quality + +- ESLint for code analysis +- Automated testing before merge +- Code review required for all changes + +### Secret Management + +- Gitleaks pre-commit hook prevents committing secrets +- Environment variables for sensitive configuration +- No secrets in version control + +### Authentication + +- JWT tokens with configurable expiration +- Password hashed in database +- Token-based API authentication + +### Dependencies + +- Regular dependency audits via `npm audit` +- Dependabot for automated updates (if enabled) + +## Best Practices for Contributors + +### Do Not Commit + +- API keys or tokens +- Database credentials +- JWT secrets +- Password files +- Private keys or certificates + +### Environment Variables + +Store sensitive configuration in environment variables: + +```bash +# Backend +PORT=3001 +DB_PATH=./data/cv.db +AUTH_MODE=simple +JWT_SECRET=your-secret-here + +# Keycloak (if used) +KEYCLOAK_URL=https://keycloak.example.com +KEYCLOAK_REALM=your-realm +KEYCLOAK_CLIENT_ID=cv-app +``` + +### Running Security Audit + +```bash +# Check for vulnerable dependencies +npm audit + +# Fix vulnerabilities +npm audit fix + +# Check backend dependencies +cd backend && npm audit +``` + +## Disclosure Policy + +- Please allow reasonable time for the fix before public disclosure - Coordinated disclosure is appreciated -- Credit will be given in the fix commit +- Credit will be given in the fix commit (if desired) -Thank you for helping keep this project secure! \ No newline at end of file +## Contact + +For security concerns, use the vulnerability reporting process above. + +Thank you for helping keep this project secure! diff --git a/docs/plans/2026-02-20-devops-features-design.md b/docs/plans/2026-02-20-devops-features-design.md deleted file mode 100644 index b9a351d..0000000 --- a/docs/plans/2026-02-20-devops-features-design.md +++ /dev/null @@ -1,549 +0,0 @@ -# DevOps Features Design - -**Date:** 2026-02-20 -**Status:** Approved -**Author:** Claude (AI Assistant) - -## Overview - -Implement four DevOps features for the CV application: -1. **Swagger/OpenAPI Documentation** - Interactive API docs at `/api/docs` -2. **Database Migrations** - Knex.js-based migration system with auto-migration -3. **Quality Gates** - Code coverage, bundle size, npm audit, Lighthouse CI -4. **Secret Scanning** - Gitleaks pre-commit hook - -## 1. Swagger/OpenAPI Documentation - -### Architecture - -``` -Route Files (JSDoc comments) - │ - ▼ - swagger-jsdoc - (generates spec at runtime) - │ - ▼ - swagger-ui-express - │ - ▼ - GET /api/docs → Interactive Swagger UI - GET /api/docs.json → Raw OpenAPI spec -``` - -### Endpoints - -| Method | Path | Description | -|--------|------|-------------| -| GET | `/api/docs` | Swagger UI interface | -| GET | `/api/docs.json` | Raw OpenAPI JSON spec | - -### Implementation - -**Dependencies:** -- `swagger-jsdoc` - Generate OpenAPI spec from JSDoc -- `swagger-ui-express` - Serve Swagger UI - -**JSDoc format in route files:** -```javascript -/** - * @openapi - * /api/cv: - * get: - * summary: Get CV data - * tags: [CV] - * responses: - * 200: - * description: CV data - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/CVData' - * 404: - * description: CV data not found - */ -router.get('/', (req, res) => { ... }); - -/** - * @openapi - * /api/cv: - * put: - * summary: Update CV data - * tags: [CV] - * security: - * - bearerAuth: [] - * requestBody: - * required: true - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/CVData' - * responses: - * 200: - * description: CV data updated - * 401: - * description: Unauthorized - */ -router.put('/', authMiddleware, (req, res) => { ... }); -``` - -**Swagger setup:** -```javascript -// backend/routes/docs.js -import swaggerJsdoc from 'swagger-jsdoc'; -import swaggerUi from 'swagger-ui-express'; - -const options = { - definition: { - openapi: '3.0.0', - info: { - title: 'CV API', - version: '1.0.0', - description: 'API for CV/Resume management', - }, - servers: [ - { url: '/api', description: 'API server' } - ], - components: { - securitySchemes: { - bearerAuth: { - type: 'http', - scheme: 'bearer', - }, - }, - schemas: { - CVData: { /* schema definition */ }, - }, - }, - }, - apis: ['./routes/*.js'], -}; - -const specs = swaggerJsdoc(options); - -router.use('/docs', swaggerUi.serve, swaggerUi.setup(specs)); -router.get('/docs.json', (req, res) => res.json(specs)); -``` - ---- - -## 2. Database Migrations (Knex.js) - -### Architecture - -``` -backend/ -├── knexfile.js # Knex configuration -├── migrations/ -│ ├── 20260220000001_initial_schema.js -│ └── 20260220000002_add_auth_tables.js -├── seeds/ -│ └── initial_cv_data.js -└── db/ - ├── connection.js # Knex instance - └── init.js # Auto-migration on startup -``` - -### Knex Configuration - -```javascript -// knexfile.js -export default { - client: 'better-sqlite3', - connection: { - filename: process.env.DB_PATH || './data/cv.db' - }, - migrations: { - directory: './migrations', - tableName: 'knex_migrations' - }, - seeds: { - directory: './seeds' - }, - useNullAsDefault: true -}; -``` - -### Migration File Format - -```javascript -// migrations/20260220000001_initial_schema.js -export async function up(knex) { - await knex.schema.createTable('cv_data', (table) => { - table.integer('id').primary().checkTo('=', 1); - table.text('data').notNullable(); - table.datetime('updated_at').defaultTo(knex.fn.now()); - }); -} - -export async function down(knex) { - await knex.schema.dropTableIfExists('cv_data'); -} -``` - -### Auto-Migration on Startup - -```javascript -// db/init.js -import knex from 'knex'; -import config from '../knexfile.js'; - -let db = null; - -export async function getDB() { - if (!db) { - db = knex(config); - await db.migrate.latest(); - } - return db; -} - -export async function closeDB() { - if (db) { - await db.destroy(); - db = null; - } -} -``` - -### CLI Commands - -```bash -# Create new migration -npx knex migrate:make migration_name --knexfile knexfile.js - -# Run migrations manually -npx knex migrate:latest --knexfile knexfile.js - -# Rollback last migration -npx knex migrate:rollback --knexfile knexfile.js - -# Run seeds -npx knex seed:run --knexfile knexfile.js -``` - -### Dependencies - -- `knex` - SQL query builder and migrations - ---- - -## 3. Quality Gates - -### Overview - -| Gate | Tool | Threshold | Action | -|------|------|-----------|--------| -| Code Coverage | vitest + Codecov | 80% minimum | Fail CI | -| Bundle Size | bundlesize | 500KB max | Fail CI | -| npm audit | npm audit | Moderate+ | Fail CI | -| Lighthouse | @lhci/cli | Performance 90+ | Fail CI | - -### CI Workflow Integration - -```yaml -# .github/workflows/ci.yml additions - -jobs: - quality: - runs-on: ubuntu-latest - steps: - - name: Checkout - 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 - - # Code Coverage - - name: Run tests with coverage - run: npm run test:coverage - - - name: Upload to Codecov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: true - - # Bundle Size - - name: Build - run: npm run build - - - name: Check bundle size - run: npx bundlesize - - # npm audit - - name: Security audit - run: npm audit --audit-level=moderate - - lighthouse: - runs-on: ubuntu-latest - steps: - - name: Checkout - 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: Build - run: npm run build - - - name: Run Lighthouse CI - run: npm run test:lighthouse -``` - -### Configuration Files - -**package.json:** -```json -{ - "scripts": { - "test:coverage": "vitest run --coverage" - }, - "bundlesize": [ - { - "path": "./dist/assets/*.js", - "maxSize": "500kb" - }, - { - "path": "./dist/assets/*.css", - "maxSize": "100kb" - } - ] -} -``` - -**vitest.config.js:** -```javascript -export default defineConfig({ - test: { - coverage: { - provider: 'v8', - reporter: ['text', 'lcov', 'html'], - exclude: ['node_modules/', 'tests/'], - lines: 80, - functions: 80, - branches: 80, - statements: 80 - } - } -}); -``` - -**.lighthouserc.json:** -```json -{ - "ci": { - "collect": { - "url": ["http://localhost:4173/"], - "numberOfRuns": 3 - }, - "assert": { - "assertions": { - "categories:performance": ["error", { "minScore": 0.9 }], - "categories:accessibility": ["warn", { "minScore": 0.9 }], - "categories:best-practices": ["warn", { "minScore": 0.9 }] - } - }, - "upload": { - "target": "temporary-public-storage" - } - } -} -``` - -### Dependencies - -- `@vitest/coverage-v8` - Coverage reporter -- `bundlesize` - Bundle size checking - -### Secrets Required - -| Secret | Description | -|--------|-------------| -| `CODECOV_TOKEN` | Codecov upload token | - ---- - -## 4. Secret Scanning (Gitleaks) - -### Architecture - -``` -Developer stages files - │ - ▼ - git commit - │ - ▼ - .husky/pre-commit - │ - ├─► npm run lint - │ - └─► gitleaks protect --staged - │ - ▼ - Secrets found? - │ │ - Yes No - │ │ - ▼ ▼ - Block commit Allow commit -``` - -### Pre-commit Hook - -```bash -# .husky/pre-commit -npm run lint - -# Gitleaks secret scanning -if command -v gitleaks &> /dev/null; then - gitleaks protect --verbose --staged - if [ $? -eq 1 ]; then - echo "" - echo "❌ Secrets detected in staged files!" - echo "Please remove sensitive data before committing." - echo "" - exit 1 - fi -fi -``` - -### Gitleaks Configuration - -```toml -# .gitleaks.toml -title = "Gitleaks Configuration" - -[extend] -useDefault = true - -[[allowlists]] -description = "Allowlisted files" -paths = [ - 'package-lock.json', - 'backend/package-lock.json', - '.*\\.md$' -] - -[[allowlists]] -description = "Allowlisted patterns" -regexes = [ - 'VITE_API_URL.*localhost', -] -``` - -### Developer Setup - -Gitleaks must be installed on developer machines: - -```bash -# macOS -brew install gitleaks - -# Linux -# Download from https://github.com/gitleaks/gitleaks/releases -# Or use go install -go install github.com/gitleaks/gitleaks/v8@latest - -# Windows -scoop install gitleaks -# Or -choco install gitleaks -``` - -### Documentation - -Add to README or CONTRIBUTING.md: - -```markdown -### Pre-commit Setup - -This project uses Gitleaks for secret scanning. Install it before committing: - -```bash -# macOS -brew install gitleaks - -# Linux -curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.18.1/gitleaks_8.18.1_linux_x64.tar.gz | tar -xz -sudo mv gitleaks /usr/local/bin/ -``` - -The pre-commit hook will automatically scan for secrets before each commit. -``` - ---- - -## Implementation Summary - -### New Dependencies - -**Backend:** -- `knex` - Database migrations and query builder -- `swagger-jsdoc` - OpenAPI spec generation -- `swagger-ui-express` - Swagger UI middleware - -**Frontend:** -- `@vitest/coverage-v8` - Coverage reporting -- `bundlesize` - Bundle size checking - -**Dev:** -- `gitleaks` (binary, not npm package) - -### New Files - -``` -backend/ -├── knexfile.js -├── migrations/ -│ └── 20260220000001_initial_schema.js -├── seeds/ -│ └── initial_cv_data.js -├── routes/ -│ └── docs.js -└── db/ - └── connection.js - -.gitleaks.toml -.lighthouserc.json -``` - -### Modified Files - -``` -package.json # Add scripts, bundlesize config -vitest.config.js # Add coverage config -.github/workflows/ci.yml # Add quality gates -.husky/pre-commit # Add gitleaks -backend/db/init.js # Use Knex instead of better-sqlite3 directly -backend/routes/cv.js # Add JSDoc comments -backend/routes/auth.js # Add JSDoc comments -README.md # Add gitleaks setup instructions -``` - ---- - -## Post-Implementation Checklist - -- [ ] Install dependencies -- [ ] Configure Knex and create initial migration -- [ ] Add Swagger UI route -- [ ] Add JSDoc comments to all API routes -- [ ] Configure Codecov (add CODECOV_TOKEN secret) -- [ ] Add bundlesize configuration -- [ ] Update CI workflow with quality gates -- [ ] Add gitleaks to pre-commit hook -- [ ] Update documentation diff --git a/docs/plans/2026-02-20-devops-features.md b/docs/plans/2026-02-20-devops-features.md deleted file mode 100644 index 36ee362..0000000 --- a/docs/plans/2026-02-20-devops-features.md +++ /dev/null @@ -1,948 +0,0 @@ -# DevOps Features Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Implement Swagger docs, Knex migrations, quality gates, and secret scanning. - -**Architecture:** Swagger UI served at /api/docs with JSDoc-annotated routes. Knex.js handles database migrations with auto-migration on startup. Quality gates integrated into CI workflow. Gitleaks pre-commit hook for secret scanning. - -**Tech Stack:** swagger-jsdoc, swagger-ui-express, knex, vitest coverage, bundlesize, gitleaks - ---- - -## Task 1: Install Backend Dependencies - -**Files:** -- Modify: `backend/package.json` - -**Step 1: Install Knex for migrations** - -```bash -cd backend && npm install knex swagger-jsdoc swagger-ui-express -``` - -**Step 2: Verify installation** - -Run: `cd backend && npm list knex swagger-jsdoc swagger-ui-express --depth=0` -Expected: All packages listed with versions - -**Step 3: Commit** - -```bash -git add backend/package.json backend/package-lock.json -git commit -m "build(deps): add knex, swagger-jsdoc, swagger-ui-express" -``` - ---- - -## Task 2: Install Frontend Quality Gate Dependencies - -**Files:** -- Modify: `package.json` - -**Step 1: Install coverage and bundlesize packages** - -```bash -npm install --save-dev @vitest/coverage-v8 bundlesize -``` - -**Step 2: Verify installation** - -Run: `npm list @vitest/coverage-v8 bundlesize --depth=0` -Expected: All packages listed with versions - -**Step 3: Commit** - -```bash -git add package.json package-lock.json -git commit -m "build(deps): add coverage and bundlesize for quality gates" -``` - ---- - -## Task 3: Create Knex Configuration - -**Files:** -- Create: `backend/knexfile.js` - -**Step 1: Create knexfile.js** - -Create `backend/knexfile.js`: - -```javascript -export default { - client: 'better-sqlite3', - connection: { - filename: process.env.DB_PATH || './data/cv.db' - }, - migrations: { - directory: './migrations', - tableName: 'knex_migrations' - }, - seeds: { - directory: './seeds' - }, - useNullAsDefault: true -}; -``` - -**Step 2: Verify syntax** - -Run: `cd backend && node -e "import('./knexfile.js').then(m => console.log(m.default))"` -Expected: Knex config object printed - -**Step 3: Commit** - -```bash -git add backend/knexfile.js -git commit -m "feat(db): add knex configuration" -``` - ---- - -## Task 4: Create Initial Migration - -**Files:** -- Create: `backend/migrations/20260220000001_initial_schema.js` - -**Step 1: Create migrations directory** - -```bash -mkdir -p backend/migrations -``` - -**Step 2: Create initial migration** - -Create `backend/migrations/20260220000001_initial_schema.js`: - -```javascript -export async function up(knex) { - await knex.schema.createTable('cv_data', (table) => { - table.integer('id').primary(); - table.text('data').notNullable(); - table.datetime('updated_at').defaultTo(knex.fn.now()); - }); -} - -export async function down(knex) { - await knex.schema.dropTableIfExists('cv_data'); -} -``` - -**Step 3: Commit** - -```bash -git add backend/migrations/ -git commit -m "feat(db): add initial schema migration" -``` - ---- - -## Task 5: Create Seed File - -**Files:** -- Create: `backend/seeds/initial_cv_data.js` - -**Step 1: Create seeds directory** - -```bash -mkdir -p backend/seeds -``` - -**Step 2: Create seed file** - -Create `backend/seeds/initial_cv_data.js`: - -```javascript -export async function seed(knex) { - const existing = await knex('cv_data').where({ id: 1 }).first(); - - if (!existing) { - await knex('cv_data').insert({ - id: 1, - data: JSON.stringify({ - personal: { - name: "Tuan-Dat Tran", - title: "Junior DevOps Engineer", - intro: "Passionierter DevOps Engineer mit Fokus auf Cloud-Infrastruktur, Container-Orchestrierung und automatisierte Deployment-Pipelines.", - email: "tuan-dat.tran@example.com", - github: "https://github.com/tuan-dat-tran", - linkedin: "https://linkedin.com/in/tuan-dat-tran", - location: "Deutschland" - }, - experience: [], - skills: {}, - education: [], - projects: [] - }) - }); - } -} -``` - -**Step 3: Commit** - -```bash -git add backend/seeds/ -git commit -m "feat(db): add initial cv data seed" -``` - ---- - -## Task 6: Update Database Init to Use Knex - -**Files:** -- Modify: `backend/db/init.js` - -**Step 1: Replace init.js with Knex-based version** - -Replace `backend/db/init.js` with: - -```javascript -import knex from 'knex'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import config from '../knexfile.js'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -let db = null; - -export async function getDB() { - if (!db) { - db = knex(config); - await db.migrate.latest(); - } - return db; -} - -export async function initDB() { - const db = await getDB(); - - const existing = await db('cv_data').where({ id: 1 }).first(); - if (!existing) { - await db('cv_data').insert({ - id: 1, - data: JSON.stringify({ - personal: { - name: "Tuan-Dat Tran", - title: "Junior DevOps Engineer", - intro: "Passionierter DevOps Engineer mit Fokus auf Cloud-Infrastruktur.", - email: "tuan-dat.tran@example.com", - github: "https://github.com/tuan-dat-tran", - linkedin: "https://linkedin.com/in/tuan-dat-tran", - location: "Deutschland" - }, - experience: [], - skills: {}, - education: [], - projects: [] - }) - }); - console.log('Initialized database with default CV data'); - } - - console.log(`Database initialized`); -} - -export async function closeDB() { - if (db) { - await db.destroy(); - db = null; - } -} -``` - -**Step 2: Verify syntax** - -Run: `cd backend && node -c db/init.js` -Expected: No syntax errors - -**Step 3: Commit** - -```bash -git add backend/db/init.js -git commit -m "refactor(db): use knex for database operations" -``` - ---- - -## Task 7: Update CV Routes for Knex - -**Files:** -- Modify: `backend/routes/cv.js` - -**Step 1: Update routes to use Knex** - -Replace `backend/routes/cv.js` with: - -```javascript -import { Router } from 'express'; -import { getDB } from '../db/init.js'; -import { authMiddleware } from '../middleware/auth.js'; - -const router = Router(); - -/** - * @openapi - * /cv: - * get: - * summary: Get CV data - * tags: [CV] - * responses: - * 200: - * description: CV data - * content: - * application/json: - * schema: - * type: object - * 404: - * description: CV data not found - */ -router.get('/', async (req, res) => { - try { - const db = await getDB(); - const row = await db('cv_data').where({ id: 1 }).first(); - - if (!row) { - return res.status(404).json({ error: 'CV data not found' }); - } - - res.json(JSON.parse(row.data)); - } catch (error) { - console.error('Error fetching CV:', error); - res.status(500).json({ error: 'Failed to fetch CV data' }); - } -}); - -/** - * @openapi - * /cv: - * put: - * summary: Update CV data - * tags: [CV] - * security: - * - bearerAuth: [] - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * responses: - * 200: - * description: CV data updated - * 401: - * description: Unauthorized - */ -router.put('/', authMiddleware, async (req, res) => { - try { - const data = req.body; - - if (!data || typeof data !== 'object') { - return res.status(400).json({ error: 'Invalid CV data' }); - } - - if (!data.personal?.name) { - return res.status(400).json({ error: 'personal.name is required' }); - } - - const db = await getDB(); - await db('cv_data').where({ id: 1 }).update({ - data: JSON.stringify(data), - updated_at: db.fn.now() - }); - - res.json({ success: true, message: 'CV data updated' }); - } catch (error) { - console.error('Error updating CV:', error); - res.status(500).json({ error: 'Failed to update CV data' }); - } -}); - -/** - * @openapi - * /cv/export: - * get: - * summary: Export CV data as JSON file - * tags: [CV] - * responses: - * 200: - * description: CV JSON file - * content: - * application/json: - * schema: - * type: object - */ -router.get('/export', async (req, res) => { - try { - const db = await getDB(); - const row = await db('cv_data').where({ id: 1 }).first(); - - if (!row) { - return res.status(404).json({ error: 'CV data not found' }); - } - - res.setHeader('Content-Type', 'application/json'); - res.setHeader('Content-Disposition', 'attachment; filename="cv.json"'); - res.send(row.data); - } catch (error) { - console.error('Error exporting CV:', error); - res.status(500).json({ error: 'Failed to export CV data' }); - } -}); - -/** - * @openapi - * /cv/import: - * post: - * summary: Import CV data from JSON - * tags: [CV] - * security: - * - bearerAuth: [] - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * responses: - * 200: - * description: CV data imported - * 401: - * description: Unauthorized - */ -router.post('/import', authMiddleware, async (req, res) => { - try { - const data = req.body; - - if (!data || typeof data !== 'object') { - return res.status(400).json({ error: 'Invalid CV data' }); - } - - if (!data.personal?.name) { - return res.status(400).json({ error: 'personal.name is required' }); - } - - const db = await getDB(); - await db('cv_data').where({ id: 1 }).update({ - data: JSON.stringify(data), - updated_at: db.fn.now() - }); - - res.json({ success: true, message: 'CV data imported' }); - } catch (error) { - console.error('Error importing CV:', error); - res.status(500).json({ error: 'Failed to import CV data' }); - } -}); - -export default router; -``` - -**Step 2: Commit** - -```bash -git add backend/routes/cv.js -git commit -m "refactor(api): update cv routes for knex with openapi docs" -``` - ---- - -## Task 8: Create Swagger Docs Route - -**Files:** -- Create: `backend/routes/docs.js` - -**Step 1: Create docs route** - -Create `backend/routes/docs.js`: - -```javascript -import { Router } from 'express'; -import swaggerJsdoc from 'swagger-jsdoc'; -import swaggerUi from 'swagger-ui-express'; - -const router = Router(); - -const options = { - definition: { - openapi: '3.0.0', - info: { - title: 'CV API', - version: '1.0.0', - description: 'API for CV/Resume management', - }, - servers: [ - { url: '/api', description: 'API server' } - ], - components: { - securitySchemes: { - bearerAuth: { - type: 'http', - scheme: 'bearer', - }, - }, - }, - }, - apis: ['./routes/*.js'], -}; - -const specs = swaggerJsdoc(options); - -router.use('/docs', swaggerUi.serve, swaggerUi.setup(specs, { - customCss: '.swagger-ui .topbar { display: none }', - customSiteTitle: 'CV API Documentation' -})); - -router.get('/docs.json', (req, res) => { - res.setHeader('Content-Type', 'application/json'); - res.send(specs); -}); - -export default router; -``` - -**Step 2: Commit** - -```bash -git add backend/routes/docs.js -git commit -m "feat(api): add swagger docs route" -``` - ---- - -## Task 9: Add OpenAPI Docs to Auth Routes - -**Files:** -- Modify: `backend/routes/auth.js` - -**Step 1: Add JSDoc comments to auth routes** - -Read `backend/routes/auth.js` and add OpenAPI JSDoc comments to each endpoint: - -- Add `@openapi` comments for `/auth/config`, `/auth/login` -- Document request bodies and responses - -**Step 2: Commit** - -```bash -git add backend/routes/auth.js -git commit -m "docs(api): add openapi docs to auth routes" -``` - ---- - -## Task 10: Register Swagger Route in Server - -**Files:** -- Modify: `backend/server.js` - -**Step 1: Import and use docs route** - -Add to `backend/server.js`: - -```javascript -import docsRoutes from './routes/docs.js'; - -// After other route imports -app.use('/api', docsRoutes); -``` - -**Step 2: Commit** - -```bash -git add backend/server.js -git commit -m "feat(api): register swagger docs route" -``` - ---- - -## Task 11: Configure Vitest Coverage - -**Files:** -- Modify: `vitest.config.js` - -**Step 1: Add coverage configuration** - -Update `vitest.config.js` to include coverage settings: - -```javascript -coverage: { - provider: 'v8', - reporter: ['text', 'lcov', 'html'], - exclude: [ - 'node_modules/', - 'tests/', - '**/*.test.js', - '**/*.config.js' - ], - lines: 80, - functions: 80, - branches: 80, - statements: 80 -} -``` - -**Step 2: Add coverage script to package.json** - -Add script: `"test:coverage": "vitest run --coverage"` - -**Step 3: Commit** - -```bash -git add vitest.config.js package.json -git commit -m "feat(ci): add coverage configuration" -``` - ---- - -## Task 12: Configure Bundle Size Checking - -**Files:** -- Modify: `package.json` - -**Step 1: Add bundlesize configuration** - -Add to `package.json`: - -```json -"bundlesize": [ - { - "path": "./dist/assets/*.js", - "maxSize": "500kb" - }, - { - "path": "./dist/assets/*.css", - "maxSize": "100kb" - } -] -``` - -**Step 2: Add bundle check script** - -Add script: `"bundle:check": "npm run build && bundlesize"` - -**Step 3: Commit** - -```bash -git add package.json -git commit -m "feat(ci): add bundlesize configuration" -``` - ---- - -## Task 13: Create Lighthouse CI Configuration - -**Files:** -- Create: `.lighthouserc.json` - -**Step 1: Create lighthouse config** - -Create `.lighthouserc.json`: - -```json -{ - "ci": { - "collect": { - "url": ["http://localhost:4173/cv/"], - "numberOfRuns": 3 - }, - "assert": { - "assertions": { - "categories:performance": ["error", { "minScore": 0.8 }], - "categories:accessibility": ["warn", { "minScore": 0.9 }], - "categories:best-practices": ["warn", { "minScore": 0.9 }] - } - }, - "upload": { - "target": "temporary-public-storage" - } - } -} -``` - -**Step 2: Commit** - -```bash -git add .lighthouserc.json -git commit -m "feat(ci): add lighthouse ci configuration" -``` - ---- - -## Task 14: Add Quality Gates to CI Workflow - -**Files:** -- Modify: `.github/workflows/ci.yml` - -**Step 1: Add quality job** - -Add a new `quality` job to `.github/workflows/ci.yml`: - -```yaml - quality: - name: Quality Gates - runs-on: ubuntu-latest - - steps: - - name: Checkout - 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 run test:coverage - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: false - - - name: Build - run: npm run build - - - name: Check bundle size - run: npx bundlesize - - - name: Security audit - run: npm audit --audit-level=moderate - continue-on-error: true -``` - -**Step 2: Update lighthouse job** - -Update the existing `lighthouse` job to use the new config and enforce thresholds. - -**Step 3: Commit** - -```bash -git add .github/workflows/ci.yml -git commit -m "feat(ci): add quality gates to ci workflow" -``` - ---- - -## Task 15: Create Gitleaks Configuration - -**Files:** -- Create: `.gitleaks.toml` - -**Step 1: Create gitleaks config** - -Create `.gitleaks.toml`: - -```toml -title = "Gitleaks Configuration" - -[extend] -useDefault = true - -[[allowlists]] -description = "Allowlisted files" -paths = [ - 'package-lock.json', - 'backend/package-lock.json' -] - -[[allowlists]] -description = "Test secrets" -regexes = [ - 'test.*password', - 'test.*secret', - 'test.*token' -] -``` - -**Step 2: Commit** - -```bash -git add .gitleaks.toml -git commit -m "feat(security): add gitleaks configuration" -``` - ---- - -## Task 16: Add Gitleaks to Pre-commit Hook - -**Files:** -- Modify: `.husky/pre-commit` - -**Step 1: Update pre-commit hook** - -Update `.husky/pre-commit` to: - -```bash -npm run lint - -# Gitleaks secret scanning -if command -v gitleaks &> /dev/null; then - gitleaks protect --verbose --staged - if [ $? -eq 1 ]; then - echo "" - echo "❌ Secrets detected in staged files!" - echo "Please remove sensitive data before committing." - echo "" - exit 1 - fi -fi -``` - -**Step 2: Commit** - -```bash -git add .husky/pre-commit -git commit -m "feat(security): add gitleaks to pre-commit hook" -``` - ---- - -## Task 17: Update Backend Tests for Knex - -**Files:** -- Modify: `backend/__tests__/api.test.js` - -**Step 1: Update tests to use async/await with Knex** - -Update the test file to work with the new async Knex-based API. - -**Step 2: Run tests** - -Run: `cd backend && npm test` -Expected: All tests pass - -**Step 3: Commit** - -```bash -git add backend/__tests__/ -git commit -m "test(api): update tests for knex" -``` - ---- - -## Task 18: Update Documentation - -**Files:** -- Modify: `README.md` - -**Step 1: Add gitleaks setup instructions** - -Add to README.md under a new "Development Setup" section: - -```markdown -### Pre-commit Setup - -This project uses Gitleaks for secret scanning. Install it before committing: - -\`\`\`bash -# macOS -brew install gitleaks - -# Linux -curl -sSfL https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_linux_x64.tar.gz | tar -xz -sudo mv gitleaks /usr/local/bin/ - -# Windows -scoop install gitleaks -\`\`\` - -### API Documentation - -API documentation is available at `/api/docs` when running the server. -``` - -**Step 2: Commit** - -```bash -git add README.md -git commit -m "docs(readme): add gitleaks setup and api docs info" -``` - ---- - -## Task 19: Verify All Configurations - -**Step 1: Run lint** - -```bash -npm run lint -``` - -Expected: No errors - -**Step 2: Run tests** - -```bash -npm run test:run -``` - -Expected: All tests pass - -**Step 3: Run backend tests** - -```bash -cd backend && npm test -``` - -Expected: All tests pass - -**Step 4: Test coverage** - -```bash -npm run test:coverage -``` - -Expected: Coverage report generated - -**Step 5: Build and check bundle** - -```bash -npm run build && npx bundlesize -``` - -Expected: Bundle sizes within limits - ---- - -## Task 20: Final Commit and Summary - -**Step 1: Verify all files are committed** - -```bash -git status -``` - -Expected: Nothing to commit - -**Step 2: View commit history** - -```bash -git log --oneline -20 -``` - -**Step 3: Summary** - -DevOps features implemented: -- **Swagger UI** at `/api/docs` with OpenAPI 3.0 spec -- **Knex migrations** with auto-migration on startup -- **Quality gates**: coverage, bundle size, npm audit, Lighthouse CI -- **Secret scanning**: Gitleaks pre-commit hook - ---- - -## Post-Implementation Checklist - -- [ ] Add `CODECOV_TOKEN` secret to GitHub -- [ ] Test Swagger UI at `/api/docs` -- [ ] Verify migrations run on startup -- [ ] Install gitleaks on developer machine -- [ ] Run full CI pipeline to verify quality gates \ No newline at end of file diff --git a/docs/plans/2026-02-20-release-engineering-design.md b/docs/plans/2026-02-20-release-engineering-design.md deleted file mode 100644 index 34b3a75..0000000 --- a/docs/plans/2026-02-20-release-engineering-design.md +++ /dev/null @@ -1,382 +0,0 @@ -# Release Engineering Design - -**Date:** 2026-02-20 -**Status:** Approved -**Author:** Claude (AI Assistant) - -## Overview - -Implement a comprehensive release engineering pipeline for the CV application using semantic-release with Docker multi-registry publishing, automated changelog generation, and multi-environment deployment (production, staging, nightly). - -## Requirements - -- **Versioning:** Semantic Versioning with Conventional Commits -- **Changelog:** Automated from conventional commit messages -- **Release Trigger:** Continuous Delivery (automatic from main branch) -- **Deployment Targets:** Docker Hub + GitHub Container Registry (GHCR) -- **Environments:** Production, Staging, Nightly/Edge - -## Architecture - -``` - ┌─────────────────────────────────────────────────────────┐ - │ GitHub Repository │ - │ │ -┌──────────┐ │ main ─────────────────────────────────────────────────► │ Production Release -│ Developer│───────►│ │ │ ├─ Git tag v1.0.0 -└──────────┘ │ └─────► semantic-release │ ├─ GitHub Release - │ ├─ version bump │ ├─ Docker Hub: latest, v1.0.0 - │ ├─ CHANGELOG.md │ └─ GHCR: latest, v1.0.0 - │ ├─ git tag │ - │ └─ GitHub release │ - │ │ - │ staging ───────► Deploy to staging environment │ Staging Deployment - │ (Docker tag: staging) │ - │ │ - │ nightly ────────► Build nightly from main │ Nightly/Edge Builds - │ (cron job) (Docker tag: nightly, edge) │ - │ │ - └─────────────────────────────────────────────────────────┘ -``` - -## Versioning Strategy - -### Semantic Versioning (SemVer) - -Version format: `MAJOR.MINOR.PATCH` (e.g., `1.2.3`) - -| Commit Type | Version Bump | Example | -|-------------|--------------|---------| -| `feat:` | Minor | 1.0.0 → 1.1.0 | -| `fix:` | Patch | 1.0.0 → 1.0.1 | -| `feat!:` or `BREAKING CHANGE:` | Major | 1.0.0 → 2.0.0 | -| `chore:`, `docs:`, `test:`, `style:`, `refactor:`, `perf:` | None | No release | - -### Conventional Commits Format - -``` -(): - -[optional body] - -[optional footer(s)] -``` - -Examples: -- `feat(admin): add password protection for admin panel` -- `fix(skills): prevent focus loss when editing category headers` -- `docs(readme): update installation instructions` -- `feat(api)!: remove deprecated endpoints` - -### Commit Types - -| Type | Description | Release? | -|------|-------------|----------| -| `feat` | New feature | Yes (minor) | -| `fix` | Bug fix | Yes (patch) | -| `docs` | Documentation changes | No | -| `style` | Code style changes (formatting) | No | -| `refactor` | Code refactoring | No | -| `perf` | Performance improvements | No | -| `test` | Adding/updating tests | No | -| `build` | Build system changes | No | -| `ci` | CI/CD changes | No | -| `chore` | Maintenance tasks | No | -| `revert` | Revert previous commit | Yes (if reverted commit was releasing) | - -## Release Pipeline - -### Production Release (main branch) - -1. Developer pushes/merges to `main` -2. CI workflow runs (lint, test, build, e2e, integration) -3. semantic-release analyzes commits since last release -4. If release-worthy commits found: - - Calculates new version - - Updates `package.json` version - - Generates/updates `CHANGELOG.md` - - Creates git tag (e.g., `v1.0.0`) - - Creates GitHub release with release notes - - Builds Docker image - - Pushes to Docker Hub and GHCR with version tags - -### Staging Deployment - -1. Push to `staging` branch -2. CI workflow runs (lint, test, build) -3. Build Docker image -4. Push to registries with `staging` tag -5. Deploy to staging environment - -### Nightly Builds - -1. Scheduled workflow runs at 02:00 UTC daily -2. Checkout `main` branch -3. Build Docker image -4. Push to registries with tags: - - `nightly` - - `edge` - - `YYYY-MM-DD` (e.g., `2026-02-20`) - -## Docker Registry Publishing - -### Registry Configuration - -**Docker Hub:** -- Image: `DOCKERHUB_USERNAME/cv-app` -- Requires: `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN` secrets - -**GitHub Container Registry (GHCR):** -- Image: `ghcr.io/GITHUB_OWNER/cv-app` -- Requires: `GITHUB_TOKEN` (automatic) - -### Tag Strategy - -| Event | Docker Hub Tags | GHCR Tags | -|-------|-----------------|-----------| -| Production release v1.2.3 | `latest`, `v1.2.3`, `1.2`, `1` | `latest`, `v1.2.3`, `1.2`, `1` | -| Staging push | `staging` | `staging` | -| Nightly build | `nightly`, `edge`, `2026-02-20` | `nightly`, `edge`, `2026-02-20` | - -### Multi-architecture Support - -Build for multiple platforms: -- `linux/amd64` (x86_64) -- `linux/arm64` (Apple Silicon, AWS Graviton) - -## Changelog Generation - -### Format - -```markdown -## [1.1.0] - 2026-02-20 - -### Features -* **admin:** add password protection for admin panel (#123) ([abc1234](https://github.com/owner/repo/commit/abc1234)) -* **api:** add authentication endpoints (#120) ([def5678](https://github.com/owner/repo/commit/def5678)) - -### Bug Fixes -* **skills:** prevent focus loss when editing category headers (#124) ([ghi9012](https://github.com/owner/repo/commit/ghi9012)) - -### Documentation -* update installation instructions (#125) ([jkl3456](https://github.com/owner/repo/commit/jkl3456)) - -### BREAKING CHANGES -* **api:** remove deprecated `/api/v1/*` endpoints - ---- - -## [1.0.0] - 2026-02-15 -... -``` - -### Tools - -- `@semantic-release/changelog` - Generates CHANGELOG.md -- `@semantic-release/git` - Commits updated files back to repo -- `conventional-changelog-conventionalcommits` - Preset for conventional commits - -## GitHub Environments - -### Production - -- **Protection:** Required reviewers (1+) -- **Deploy Trigger:** semantic-release on main branch merge -- **Secrets:** `DOCKERHUB_TOKEN`, `DOCKERHUB_USERNAME` -- **URL:** Production deployment URL - -### Staging - -- **Protection:** Optional reviewers -- **Deploy Trigger:** Push to `staging` branch -- **URL:** Staging deployment URL - -### Nightly - -- **Protection:** None -- **Deploy Trigger:** Scheduled (cron: `0 2 * * *`) -- **URL:** Nightly build URL - -## Branch Protection Rules - -### main branch - -| Rule | Setting | -|------|---------| -| Require PR reviews | 1+ | -| Dismiss stale reviews | Yes | -| Require linear history | Yes | -| Restrict force pushes | Yes | -| Allow deletions | No | -| Required status checks | `frontend`, `backend`, `integration`, `e2e` | - -### staging branch - -| Rule | Setting | -|------|---------| -| Require PR reviews | No | -| Require linear history | No | -| Restrict force pushes | No | -| Required status checks | `frontend`, `backend` | - -## File Structure - -``` -.github/ -├── workflows/ -│ ├── ci.yml # Enhanced with commitlint -│ ├── release.yml # semantic-release workflow -│ ├── staging.yml # Staging deployment -│ └── nightly.yml # Nightly builds -├── commitlint.config.js # Conventional commits config -└── .releaserc.json # semantic-release config - -scripts/ -├── docker-tags.sh # Docker tag computation -└── release-notes.sh # Custom release notes - -docker/ -├── Dockerfile # Multi-stage build -└── docker-compose.prod.yml # Production compose file - -CHANGELOG.md # Generated by semantic-release -package.json # Version updated by semantic-release -``` - -## Implementation Components - -### 1. semantic-release Configuration - -`.releaserc.json`: -```json -{ - "branches": ["main"], - "plugins": [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", - "@semantic-release/changelog", - "@semantic-release/npm", - "@semantic-release/git", - "@semantic-release/github" - ] -} -``` - -### 2. commitlint Configuration - -`commitlint.config.js`: -```javascript -module.exports = { - extends: ['@commitlint/config-conventional'], - rules: { - 'type-enum': [2, 'always', [ - 'feat', 'fix', 'docs', 'style', 'refactor', - 'perf', 'test', 'build', 'ci', 'chore', 'revert' - ]], - 'scope-enum': [2, 'always', [ - 'admin', 'api', 'ui', 'docker', 'ci', 'deps' - ]] - } -}; -``` - -### 3. Husky Git Hooks - -```bash -npx husky init -echo "npx commitlint --edit \$1" > .husky/commit-msg -``` - -### 4. GitHub Workflows - -**release.yml:** -- Triggers on push to main -- Runs semantic-release -- Builds and pushes Docker images - -**staging.yml:** -- Triggers on push to staging branch -- Builds and pushes Docker images with staging tag - -**nightly.yml:** -- Triggers on schedule (daily at 02:00 UTC) -- Builds from main with nightly/edge tags - -### 5. Docker Configuration - -Multi-stage Dockerfile with: -- Build stage for frontend -- Build stage for backend -- Production stage with nginx - -Multi-platform build using `docker buildx` - -## Security Considerations - -1. **Secrets Management:** - - Use GitHub Secrets for sensitive values - - Never commit secrets to repository - - Use `GITHUB_TOKEN` for GHCR (automatic) - -2. **Branch Protection:** - - Require PR reviews for main - - Require status checks to pass - - Restrict force pushes - -3. **Docker Image Security:** - - Use minimal base images (alpine) - - Run as non-root user - - Scan images for vulnerabilities (optional: Trivy) - -4. **Environment Protection:** - - Required reviewers for production - - Environment-specific secrets - -## Migration Plan - -1. **Phase 1: Setup Tooling** - - Install semantic-release and plugins - - Configure commitlint and husky - - Create .releaserc.json - -2. **Phase 2: Update Workflows** - - Enhance ci.yml with commitlint - - Create release.yml - - Create staging.yml - - Create nightly.yml - -3. **Phase 3: Docker Publishing** - - Configure Docker Hub credentials - - Update Dockerfile for multi-platform - - Test image publishing - -4. **Phase 4: Branch Protection** - - Create staging branch - - Configure branch protection rules - - Create GitHub environments - -5. **Phase 5: First Release** - - Make a conventional commit - - Verify semantic-release runs - - Check GitHub release created - - Verify Docker images published - -## Success Criteria - -- [ ] Conventional commits enforced via commitlint -- [ ] Version automatically bumped based on commit types -- [ ] CHANGELOG.md automatically generated and updated -- [ ] Git tags created for each release -- [ ] GitHub releases created with release notes -- [ ] Docker images published to Docker Hub and GHCR -- [ ] Multi-platform Docker images (amd64, arm64) -- [ ] Staging deployments on staging branch push -- [ ] Nightly builds running daily -- [ ] Branch protection rules enforced - -## References - -- [semantic-release Documentation](https://semantic-release.gitbook.io/) -- [Conventional Commits Specification](https://www.conventionalcommits.org/) -- [GitHub Actions Documentation](https://docs.github.com/en/actions) -- [Docker Multi-platform Builds](https://docs.docker.com/build/building/multi-platform/) diff --git a/docs/plans/2026-02-20-release-engineering.md b/docs/plans/2026-02-20-release-engineering.md deleted file mode 100644 index 5c36fd8..0000000 --- a/docs/plans/2026-02-20-release-engineering.md +++ /dev/null @@ -1,974 +0,0 @@ -# Release Engineering Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Implement comprehensive release engineering with semantic-release, Docker multi-registry publishing, and multi-environment deployment (production, staging, nightly). - -**Architecture:** semantic-release handles versioning, changelog, and GitHub releases from conventional commits. GitHub Actions workflows build and publish Docker images to Docker Hub and GHCR. Separate workflows for staging and nightly deployments. - -**Tech Stack:** semantic-release, commitlint, husky, Docker buildx, GitHub Actions - ---- - -## Task 1: Install Release Engineering Dependencies - -**Files:** -- Modify: `package.json` -- Create: `.releaserc.json` -- Create: `commitlint.config.js` - -**Step 1: Install semantic-release and plugins** - -```bash -npm install --save-dev semantic-release @semantic-release/changelog @semantic-release/git @semantic-release/github @commitlint/cli @commitlint/config-conventional husky -``` - -**Step 2: Verify installation** - -Run: `npm list semantic-release @commitlint/cli husky --depth=0` -Expected: All packages listed with versions - -**Step 3: Commit** - -```bash -git add package.json package-lock.json -git commit -m "build: add semantic-release, commitlint, and husky dependencies" -``` - ---- - -## Task 2: Configure semantic-release - -**Files:** -- Create: `.releaserc.json` - -**Step 1: Create semantic-release configuration file** - -Create `.releaserc.json`: - -```json -{ - "branches": ["main", { "name": "staging", "prerelease": true }], - "plugins": [ - [ - "@semantic-release/commit-analyzer", - { - "preset": "conventionalcommits", - "releaseRules": [ - { "type": "feat", "release": "minor" }, - { "type": "fix", "release": "patch" }, - { "type": "perf", "release": "patch" }, - { "type": "revert", "release": "patch" }, - { "type": "docs", "release": false }, - { "type": "style", "release": false }, - { "type": "refactor", "release": false }, - { "type": "test", "release": false }, - { "type": "build", "release": false }, - { "type": "ci", "release": false }, - { "type": "chore", "release": false } - ] - } - ], - [ - "@semantic-release/release-notes-generator", - { - "preset": "conventionalcommits", - "presetConfig": { - "types": [ - { "type": "feat", "section": "Features", "hidden": false }, - { "type": "fix", "section": "Bug Fixes", "hidden": false }, - { "type": "perf", "section": "Performance Improvements", "hidden": false }, - { "type": "revert", "section": "Reverts", "hidden": false }, - { "type": "docs", "section": "Documentation", "hidden": false }, - { "type": "style", "section": "Styles", "hidden": true }, - { "type": "refactor", "section": "Code Refactoring", "hidden": true }, - { "type": "test", "section": "Tests", "hidden": true }, - { "type": "build", "section": "Build System", "hidden": true }, - { "type": "ci", "section": "Continuous Integration", "hidden": true }, - { "type": "chore", "section": "Chores", "hidden": true } - ] - } - } - ], - [ - "@semantic-release/changelog", - { - "changelogFile": "CHANGELOG.md", - "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file." - } - ], - [ - "@semantic-release/npm", - { - "npmPublish": false - } - ], - [ - "@semantic-release/git", - { - "assets": ["package.json", "CHANGELOG.md"], - "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" - } - ], - [ - "@semantic-release/github", - { - "assets": [ - { "path": "dist/**/*", "label": "Distribution" } - ] - } - ] - ] -} -``` - -**Step 2: Verify JSON is valid** - -Run: `node -e "console.log(JSON.parse(require('fs').readFileSync('.releaserc.json', 'utf8')))"` -Expected: JSON object printed without errors - -**Step 3: Commit** - -```bash -git add .releaserc.json -git commit -m "chore(ci): configure semantic-release" -``` - ---- - -## Task 3: Configure commitlint - -**Files:** -- Create: `commitlint.config.js` - -**Step 1: Create commitlint configuration file** - -Create `commitlint.config.js`: - -```javascript -export default { - extends: ['@commitlint/config-conventional'], - rules: { - 'type-enum': [ - 2, - 'always', - [ - 'feat', - 'fix', - 'docs', - 'style', - 'refactor', - 'perf', - 'test', - 'build', - 'ci', - 'chore', - 'revert', - ], - ], - 'scope-enum': [ - 2, - 'always', - [ - 'admin', - 'api', - 'ui', - 'docker', - 'ci', - 'deps', - 'release', - 'auth', - 'skills', - 'experience', - 'education', - 'projects', - 'personal', - ], - ], - 'scope-empty': [1, 'never'], - 'subject-case': [2, 'always', 'lower-case'], - 'subject-max-length': [2, 'always', 72], - 'body-max-line-length': [2, 'always', 100], - }, -}; -``` - -**Step 2: Verify configuration is valid** - -Run: `npx commitlint --from HEAD~1 --to HEAD --verbose` (skip if only one commit) -Expected: Either passes or reports linting issues - -**Step 3: Commit** - -```bash -git add commitlint.config.js -git commit -m "chore(ci): configure commitlint for conventional commits" -``` - ---- - -## Task 4: Configure Husky Git Hooks - -**Files:** -- Create: `.husky/commit-msg` -- Create: `.husky/pre-commit` - -**Step 1: Initialize husky** - -```bash -npx husky init -``` - -Expected: `.husky/` directory created - -**Step 2: Create commit-msg hook** - -Replace `.husky/commit-msg` content with: - -```bash -npx --no -- commitlint --edit "$1" -``` - -**Step 3: Create pre-commit hook** - -Replace `.husky/pre-commit` content with: - -```bash -npm run lint -``` - -**Step 4: Make hooks executable** - -```bash -chmod +x .husky/pre-commit .husky/commit-msg -``` - -**Step 5: Verify husky is configured in package.json** - -Run: `cat package.json | grep -A 2 '"scripts"'` -Expected: `prepare` script present with `husky` - -If not present, add to package.json scripts: -```json -"prepare": "husky" -``` - -**Step 6: Commit** - -```bash -git add .husky package.json -git commit -m "chore(ci): configure husky git hooks for commitlint" -``` - ---- - -## Task 5: Create Docker Multi-Platform Configuration - -**Files:** -- Modify: `Dockerfile` -- Create: `docker bake.hcl` - -**Step 1: Update Dockerfile for multi-platform support** - -Read current `Dockerfile` and ensure it supports multi-platform. The existing Dockerfile should already work, but verify: -- No hardcoded architecture-specific paths -- Uses multi-stage build -- Backend builds better-sqlite3 from source - -**Step 2: Create docker-bake.hcl for multi-registry publishing** - -Create `docker-bake.hcl`: - -```hcl -variable "REGISTRY_USER" { - default = "" -} - -variable "IMAGE_NAME" { - default = "cv-app" -} - -variable "TAG" { - default = "latest" -} - -variable "VERSION" { - default = "" -} - -group "default" { - targets = ["cv-app"] -} - -target "cv-app" { - context = "." - platforms = ["linux/amd64", "linux/arm64"] - tags = [ - notequal("",VERSION) ? "${REGISTRY_USER}/${IMAGE_NAME}:${VERSION}" : "", - notequal("",VERSION) ? "${REGISTRY_USER}/${IMAGE_NAME}:latest" : "", - notequal("",VERSION) ? "ghcr.io/${REGISTRY_USER}/${IMAGE_NAME}:${VERSION}" : "", - notequal("",VERSION) ? "ghcr.io/${REGISTRY_USER}/${IMAGE_NAME}:latest" : "", - ] - args = { - NODE_VERSION = "20" - } -} -``` - -**Step 3: Commit** - -```bash -git add Dockerfile docker-bake.hcl -git commit -m "feat(docker): add multi-platform build configuration" -``` - ---- - -## Task 6: Create Release Workflow - -**Files:** -- Modify: `.github/workflows/release.yml` - -**Step 1: Replace existing release.yml** - -Replace `.github/workflows/release.yml` content with: - -```yaml -name: Release - -on: - push: - branches: [main] - workflow_dispatch: - -permissions: - contents: write - packages: write - id-token: write - -jobs: - release: - name: Release - runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[skip ci]')" - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Install backend dependencies - working-directory: ./backend - run: npm ci - - - name: Lint - run: npm run lint - - - name: Run tests - run: npm run test:run - - - name: Run backend tests - working-directory: ./backend - run: npm test - - - name: Build - run: npm run build - - - name: Release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npx semantic-release - - docker: - name: Build & Push Docker Image - runs-on: ubuntu-latest - needs: release - if: needs.release.result == 'success' - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Get version - id: version - run: | - VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "latest") - echo "version=${VERSION#v}" >> $GITHUB_OUTPUT - echo "tag=${VERSION}" >> $GITHUB_OUTPUT - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ${{ secrets.DOCKERHUB_USERNAME }}/cv-app:latest - ${{ secrets.DOCKERHUB_USERNAME }}/cv-app:${{ steps.version.outputs.version }} - ghcr.io/${{ github.repository_owner }}/cv-app:latest - ghcr.io/${{ github.repository_owner }}/cv-app:${{ steps.version.outputs.version }} - cache-from: type=gha - cache-to: type=gha,mode=max -``` - -**Step 2: Verify YAML syntax** - -Run: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/release.yml'))"` -Expected: No errors - -**Step 3: Commit** - -```bash -git add .github/workflows/release.yml -git commit -m "feat(ci): add semantic-release workflow with Docker publishing" -``` - ---- - -## Task 7: Create Staging Workflow - -**Files:** -- Create: `.github/workflows/staging.yml` - -**Step 1: Create staging workflow** - -Create `.github/workflows/staging.yml`: - -```yaml -name: Staging Deploy - -on: - push: - branches: [staging] - workflow_dispatch: - -permissions: - contents: read - packages: write - -jobs: - build-and-deploy: - name: Build & Deploy to Staging - runs-on: ubuntu-latest - environment: staging - - steps: - - name: Checkout - 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: Install backend dependencies - working-directory: ./backend - run: npm ci - - - name: Lint - run: npm run lint - - - name: Run tests - run: npm run test:run - - - name: Build - run: npm run build - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ${{ secrets.DOCKERHUB_USERNAME }}/cv-app:staging - ghcr.io/${{ github.repository_owner }}/cv-app:staging - cache-from: type=gha - cache-to: type=gha,mode=max -``` - -**Step 2: Verify YAML syntax** - -Run: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/staging.yml'))"` -Expected: No errors - -**Step 3: Commit** - -```bash -git add .github/workflows/staging.yml -git commit -m "feat(ci): add staging deployment workflow" -``` - ---- - -## Task 8: Create Nightly Build Workflow - -**Files:** -- Create: `.github/workflows/nightly.yml` - -**Step 1: Create nightly workflow** - -Create `.github/workflows/nightly.yml`: - -```yaml -name: Nightly Build - -on: - schedule: - - cron: '0 2 * * *' - workflow_dispatch: - -permissions: - contents: read - packages: write - -jobs: - nightly: - name: Build Nightly Image - runs-on: ubuntu-latest - environment: nightly - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: main - - - name: Get date - id: date - run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Install backend dependencies - working-directory: ./backend - run: npm ci - - - name: Build - run: npm run build - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ${{ secrets.DOCKERHUB_USERNAME }}/cv-app:nightly - ${{ secrets.DOCKERHUB_USERNAME }}/cv-app:edge - ${{ secrets.DOCKERHUB_USERNAME }}/cv-app:${{ steps.date.outputs.date }} - ghcr.io/${{ github.repository_owner }}/cv-app:nightly - ghcr.io/${{ github.repository_owner }}/cv-app:edge - ghcr.io/${{ github.repository_owner }}/cv-app:${{ steps.date.outputs.date }} - cache-from: type=gha - cache-to: type=gha,mode=max -``` - -**Step 2: Verify YAML syntax** - -Run: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/nightly.yml'))"` -Expected: No errors - -**Step 3: Commit** - -```bash -git add .github/workflows/nightly.yml -git commit -m "feat(ci): add nightly build workflow" -``` - ---- - -## Task 9: Enhance CI Workflow with Commitlint - -**Files:** -- Modify: `.github/workflows/ci.yml` - -**Step 1: Add commitlint job to CI** - -Add this job to `.github/workflows/ci.yml` after the `jobs:` line: - -```yaml - commitlint: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Validate current commit - if: github.event_name == 'push' - run: npx commitlint --from HEAD~1 --to HEAD --verbose - - - name: Validate PR commits - if: github.event_name == 'pull_request' - run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose -``` - -**Step 2: Update job dependencies** - -Add `commitlint` to the `needs` array of downstream jobs. For example, change: -```yaml - integration: - runs-on: ubuntu-latest - needs: [frontend, backend] -``` -to: -```yaml - integration: - runs-on: ubuntu-latest - needs: [frontend, backend, commitlint] -``` - -Do the same for `e2e` and `regression` jobs. - -**Step 3: Verify YAML syntax** - -Run: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/ci.yml'))"` -Expected: No errors - -**Step 4: Commit** - -```bash -git add .github/workflows/ci.yml -git commit -m "feat(ci): add commitlint validation to CI workflow" -``` - ---- - -## Task 10: Create CHANGELOG.md - -**Files:** -- Create: `CHANGELOG.md` - -**Step 1: Create initial CHANGELOG** - -Create `CHANGELOG.md`: - -```markdown -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -### Added -- Release engineering with semantic-release -- Multi-platform Docker builds (amd64, arm64) -- Docker Hub and GHCR publishing -- Staging and nightly deployment workflows -- Commitlint for conventional commits - -## [0.0.0] - 2026-02-20 - -### Added -- Initial CV application -- React frontend with Tailwind CSS v4 -- Express.js backend with SQLite -- Admin panel for CV editing -- Password protection for admin -- Docker Compose deployment -``` - -**Step 2: Commit** - -```bash -git add CHANGELOG.md -git commit -m "docs: add initial CHANGELOG.md" -``` - ---- - -## Task 11: Update README with Release Information - -**Files:** -- Modify: `README.md` - -**Step 1: Add release engineering section to README** - -Add this section after the "Testing" section: - -```markdown -## Release Process - -This project uses [semantic-release](https://semantic-release.gitbook.io/) for automated versioning and releases. - -### Conventional Commits - -All commit messages must follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: - -``` -(): - -[optional body] - -[optional footer] -``` - -#### Commit Types - -| Type | Description | Version Bump | -|------|-------------|--------------| -| `feat` | New feature | Minor | -| `fix` | Bug fix | Patch | -| `feat!` or `BREAKING CHANGE` | Breaking change | Major | -| `docs`, `style`, `refactor`, `test`, `build`, `ci`, `chore` | Non-release changes | None | - -#### Scopes - -Available scopes: `admin`, `api`, `ui`, `docker`, `ci`, `deps`, `release`, `auth`, `skills`, `experience`, `education`, `projects`, `personal` - -### Release Workflow - -1. Push to `main` branch -2. CI runs tests and linting -3. semantic-release analyzes commits -4. If release needed: - - Version is bumped in package.json - - CHANGELOG.md is updated - - Git tag is created - - GitHub release is published - - Docker images are built and pushed - -### Docker Images - -Images are published to both Docker Hub and GitHub Container Registry: - -| Tag | Description | -|-----|-------------| -| `latest` | Latest stable release | -| `v1.2.3` | Specific version | -| `staging` | Staging environment | -| `nightly`, `edge` | Daily builds from main | -| `YYYY-MM-DD` | Dated nightly build | - -```bash -# Pull from Docker Hub -docker pull username/cv-app:latest - -# Pull from GHCR -docker pull ghcr.io/owner/cv-app:latest -``` - -### Environments - -| Environment | Branch | Trigger | -|-------------|--------|---------| -| Production | `main` | semantic-release | -| Staging | `staging` | Push to staging | -| Nightly | `main` | Daily at 02:00 UTC | -``` - -**Step 2: Commit** - -```bash -git add README.md -git commit -m "docs: add release process documentation" -``` - ---- - -## Task 12: Update package.json Version - -**Files:** -- Modify: `package.json` - -**Step 1: Update version field** - -Change `"version": "0.0.0"` to `"version": "0.0.0-dev.1"` in package.json. - -This indicates the project is in development before the first release. semantic-release will set the proper version on first release. - -**Step 2: Commit** - -```bash -git add package.json -git commit -m "chore: set initial development version" -``` - ---- - -## Task 13: Verify All Configurations - -**Step 1: Run lint** - -```bash -npm run lint -``` - -Expected: No errors - -**Step 2: Run tests** - -```bash -npm run test:run -``` - -Expected: All tests pass - -**Step 3: Test commitlint locally** - -```bash -echo "feat(test): test commit message" | npx commitlint -``` - -Expected: No errors - -```bash -echo "invalid commit message" | npx commitlint -``` - -Expected: Error message about invalid format - -**Step 4: Verify husky hooks** - -```bash -ls -la .husky/ -``` - -Expected: `pre-commit` and `commit-msg` files exist and are executable - ---- - -## Task 14: Final Commit and Summary - -**Step 1: Verify all files are committed** - -```bash -git status -``` - -Expected: "nothing to commit, working tree clean" or only untracked files that shouldn't be committed - -**Step 2: View commit history** - -```bash -git log --oneline -15 -``` - -Expected: All 12+ commits from this plan visible - -**Step 3: Summary** - -The release engineering system is now configured: - -- **semantic-release** handles automated versioning and releases -- **commitlint** enforces conventional commits -- **husky** provides git hooks for validation -- **GitHub Actions** workflows for release, staging, and nightly builds -- **Docker** multi-platform images published to Docker Hub and GHCR - ---- - -## Required Secrets - -Before the first release, configure these secrets in GitHub repository settings: - -| Secret | Description | -|--------|-------------| -| `DOCKERHUB_USERNAME` | Docker Hub username | -| `DOCKERHUB_TOKEN` | Docker Hub access token | -| `GITHUB_TOKEN` | Automatic (no configuration needed) | - ---- - -## Post-Implementation Checklist - -- [ ] Configure Docker Hub secrets in GitHub -- [ ] Create `staging` branch -- [ ] Configure GitHub environments (production, staging, nightly) -- [ ] Set up branch protection rules for `main` -- [ ] Test release by merging a `feat:` commit to main