docs: update all markdown documentation
All checks were successful
Release / Release (push) Successful in 9m39s
Release / Build & Push Docker Image (push) Has been skipped

- README.md: Update tech stack versions, simplify content, add links to docs
- CONTRIBUTING.md: Add commit guidelines, testing instructions, PR process
- SECURITY.md: Add security measures, vulnerability reporting process
- PULL_REQUEST_TEMPLATE.md: Add conventional commit types, breaking change section
- Remove outdated plan documents (already implemented or superseded)
- architecture.md: Already updated with comprehensive system documentation
- release-engineering.md: Already updated with current pipeline status
This commit is contained in:
Tuan-Dat Tran
2026-02-23 22:53:03 +01:00
parent 8d7000eb31
commit d73e8769c0
8 changed files with 319 additions and 3070 deletions

View File

@@ -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
<!-- Describe how you tested these changes -->
## Screenshots (if applicable)
<!-- Add screenshots here -->
<!-- Add screenshots for UI changes -->
## Related Issues
<!-- Link any related issues: Closes #123 -->
<!-- Link issues: Closes #123, Fixes #456 -->

View File

@@ -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/):
```
<type>(<scope>): <subject>
[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.
Open an issue for questions or discussions about contributions.

194
README.md
View File

@@ -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/):
```
<type>(<scope>): <description>
[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.

View File

@@ -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!
## Contact
For security concerns, use the vulnerability reporting process above.
Thank you for helping keep this project secure!

View File

@@ -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

View File

@@ -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

View File

@@ -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
```
<type>(<scope>): <description>
[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/)

View File

@@ -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:
```
<type>(<scope>): <description>
[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