Add CI/CD pipeline with Gitea Actions and Portainer deployment
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
- Create Gitea Actions workflow for automated Docker builds on push to main - Add docker-compose.portainer.yml for production Portainer deployment - Create comprehensive DEPLOYMENT.md guide with step-by-step instructions - Update CLAUDE.md with CI/CD pipeline documentation Images are built and pushed to Gitea registry at: code.puffinoffset.com/matt/puffin-app:latest code.puffinoffset.com/matt/puffin-app:main-<sha> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
01b232f909
commit
1c9c570ece
47
.gitea/workflows/build-deploy.yml
Normal file
47
.gitea/workflows/build-deploy.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
name: Build and Push Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Gitea Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: code.puffinoffset.com
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: code.puffinoffset.com/matt/puffin-app
|
||||||
|
tags: |
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
type=sha,prefix={{branch}}-
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=registry,ref=code.puffinoffset.com/matt/puffin-app:buildcache
|
||||||
|
cache-to: type=registry,ref=code.puffinoffset.com/matt/puffin-app:buildcache,mode=max
|
||||||
|
|
||||||
|
- name: Image digest
|
||||||
|
run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}"
|
||||||
55
CLAUDE.md
55
CLAUDE.md
@ -92,11 +92,58 @@ VITE_FORMSPREE_OFFSET_ID= # Offset order form endpoint
|
|||||||
|
|
||||||
### Testing Strategy
|
### Testing Strategy
|
||||||
- Unit tests using Vitest and React Testing Library
|
- Unit tests using Vitest and React Testing Library
|
||||||
- Test files in `__tests__` directories
|
- Test configuration in `vitest.config.ts` with jsdom environment
|
||||||
- Run with `npm test`
|
- Setup file at `src/test/setup.ts`
|
||||||
|
- Test files use pattern `*.test.ts` or `*.test.tsx`
|
||||||
|
- Run all tests: `npm test`
|
||||||
|
- Tests run with global test APIs enabled
|
||||||
|
|
||||||
### Build & Deployment
|
### Build & Deployment
|
||||||
- Production builds output to `dist/`
|
- Production builds output to `dist/` directory
|
||||||
|
- Vite build configuration includes:
|
||||||
|
- Source maps enabled for debugging
|
||||||
|
- Code splitting with vendor chunk (React/React-DOM)
|
||||||
|
- Lucide-react excluded from optimizeDeps for compatibility
|
||||||
- Docker deployment uses Nginx to serve static files on port 3800
|
- Docker deployment uses Nginx to serve static files on port 3800
|
||||||
- Host Nginx reverse proxy configuration available in `nginx-host.conf`
|
- Host Nginx reverse proxy configuration available in `nginx-host.conf`
|
||||||
- PWA manifest and service worker for mobile app installation
|
- PWA manifest (`public/manifest.json`) and service worker (`public/sw.js`) for mobile app installation
|
||||||
|
|
||||||
|
### CI/CD Pipeline
|
||||||
|
|
||||||
|
**Automated Builds**:
|
||||||
|
- Gitea Actions workflow at `.gitea/workflows/build-deploy.yml`
|
||||||
|
- Triggers on push to `main` branch
|
||||||
|
- Builds multi-stage Docker image (Node build → Nginx serve)
|
||||||
|
- Pushes to Gitea container registry at `code.puffinoffset.com/matt/puffin-app`
|
||||||
|
|
||||||
|
**Image Tagging Strategy**:
|
||||||
|
- `latest`: Always points to most recent main branch build
|
||||||
|
- `main-<commit-sha>`: Specific commit version for rollbacks
|
||||||
|
|
||||||
|
**Container Registry**:
|
||||||
|
- Gitea's built-in Docker registry
|
||||||
|
- Authentication via Gitea credentials
|
||||||
|
- Registry URL: `code.puffinoffset.com`
|
||||||
|
|
||||||
|
**Deployment**:
|
||||||
|
- Manual deployment via Portainer
|
||||||
|
- Use `docker-compose.portainer.yml` for production stack configuration
|
||||||
|
- Environment variables mounted via `.env` file volume
|
||||||
|
- Detailed deployment instructions in `DEPLOYMENT.md`
|
||||||
|
|
||||||
|
**Workflow Optimization**:
|
||||||
|
- Docker buildx for multi-platform support
|
||||||
|
- Layer caching to speed up builds
|
||||||
|
- Separate build cache stored in registry
|
||||||
|
|
||||||
|
### Key Implementation Details
|
||||||
|
|
||||||
|
**Routing without React Router**: App uses state-based routing via `currentPage` state and `window.history.pushState()`. The `/mobile-app` route is detected by checking `window.location.pathname` and renders only `MobileCalculator` without the standard layout.
|
||||||
|
|
||||||
|
**Sample Vessel Data**: A hardcoded `sampleVessel` object is used as default vessel data in `App.tsx` (lines 17-24) since AIS client currently returns mock data.
|
||||||
|
|
||||||
|
**Analytics Integration**: `utils/analytics.ts` tracks page views via Google Analytics. Called in `App.tsx` useEffect when route changes.
|
||||||
|
|
||||||
|
**Currency Support**: `utils/currencies.ts` provides multi-currency conversion. Calculator components support USD, EUR, GBP selection.
|
||||||
|
|
||||||
|
**Error Handling**: `ErrorBoundary.tsx` component wraps the app to catch React rendering errors gracefully.
|
||||||
243
DEPLOYMENT.md
Normal file
243
DEPLOYMENT.md
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
# Puffin Offset - Deployment Guide
|
||||||
|
|
||||||
|
This guide covers deploying the Puffin Offset application using Gitea Actions for automated builds and Portainer for container orchestration.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
Push to main → Gitea Actions → Build Docker Image → Push to Registry → Manual Deploy via Portainer
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Pipeline
|
||||||
|
|
||||||
|
### Automated Build Process
|
||||||
|
|
||||||
|
When code is pushed to the `main` branch:
|
||||||
|
|
||||||
|
1. **Gitea Actions triggers** automatically
|
||||||
|
2. **Docker image is built** using the multi-stage Dockerfile
|
||||||
|
3. **Image is pushed** to Gitea's container registry with two tags:
|
||||||
|
- `latest` - Always points to the most recent build
|
||||||
|
- `main-<commit-sha>` - Specific commit for rollback capability
|
||||||
|
|
||||||
|
### Registry Location
|
||||||
|
|
||||||
|
Images are stored at:
|
||||||
|
```
|
||||||
|
code.puffinoffset.com/matt/puffin-app:latest
|
||||||
|
code.puffinoffset.com/matt/puffin-app:main-<sha>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Portainer Deployment
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
1. **Portainer installed and accessible**
|
||||||
|
2. **Gitea registry credentials configured in Portainer**
|
||||||
|
3. **Environment variables prepared** (see below)
|
||||||
|
|
||||||
|
### Step 1: Configure Gitea Registry in Portainer
|
||||||
|
|
||||||
|
1. Navigate to **Portainer → Registries**
|
||||||
|
2. Click **"Add registry"**
|
||||||
|
3. Configure:
|
||||||
|
- **Name**: `Gitea - code.puffinoffset.com`
|
||||||
|
- **Registry URL**: `code.puffinoffset.com`
|
||||||
|
- **Authentication**: Enabled
|
||||||
|
- **Username**: Your Gitea username (e.g., `matt`)
|
||||||
|
- **Password**: Gitea access token or password
|
||||||
|
4. Click **"Add registry"**
|
||||||
|
|
||||||
|
### Step 2: Prepare Environment Variables
|
||||||
|
|
||||||
|
Create a `.env` file on your server with the required variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example: /path/to/puffin-app/.env
|
||||||
|
VITE_WREN_API_TOKEN=your-wren-api-token
|
||||||
|
VITE_FORMSPREE_CONTACT_ID=your-formspree-contact-id
|
||||||
|
VITE_FORMSPREE_OFFSET_ID=your-formspree-offset-id
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The env.sh script in the Docker image will inject these at runtime.
|
||||||
|
|
||||||
|
### Step 3: Create Portainer Stack
|
||||||
|
|
||||||
|
#### Option A: Using Portainer UI
|
||||||
|
|
||||||
|
1. Navigate to **Portainer → Stacks**
|
||||||
|
2. Click **"Add stack"**
|
||||||
|
3. Configure:
|
||||||
|
- **Name**: `puffin-app`
|
||||||
|
- **Build method**: "Web editor"
|
||||||
|
4. Paste the contents of `docker-compose.portainer.yml`
|
||||||
|
5. In **Environment variables** section, add:
|
||||||
|
- Name: `ENV_FILE_PATH`
|
||||||
|
- Value: `/path/to/your/.env`
|
||||||
|
6. Click **"Deploy the stack"**
|
||||||
|
|
||||||
|
#### Option B: Using Git Repository
|
||||||
|
|
||||||
|
1. Navigate to **Portainer → Stacks**
|
||||||
|
2. Click **"Add stack"**
|
||||||
|
3. Configure:
|
||||||
|
- **Name**: `puffin-app`
|
||||||
|
- **Build method**: "Repository"
|
||||||
|
- **Repository URL**: `https://code.puffinoffset.com/matt/puffin-app.git`
|
||||||
|
- **Repository reference**: `refs/heads/main`
|
||||||
|
- **Compose path**: `docker-compose.portainer.yml`
|
||||||
|
- **Authentication**: Configure with your Gitea credentials
|
||||||
|
4. Click **"Deploy the stack"**
|
||||||
|
|
||||||
|
### Step 4: Verify Deployment
|
||||||
|
|
||||||
|
1. Check that the container is running:
|
||||||
|
- Navigate to **Portainer → Containers**
|
||||||
|
- Look for `puffin-app-web-1` or similar
|
||||||
|
- Status should be "running"
|
||||||
|
|
||||||
|
2. Test the application:
|
||||||
|
- Access: `http://your-server:3800`
|
||||||
|
- Or via your Nginx reverse proxy configuration
|
||||||
|
|
||||||
|
3. Check logs if needed:
|
||||||
|
- Click on the container
|
||||||
|
- Select **"Logs"** tab
|
||||||
|
|
||||||
|
## Updating to New Versions
|
||||||
|
|
||||||
|
### Method 1: Using Portainer UI (Recommended)
|
||||||
|
|
||||||
|
1. Navigate to **Portainer → Stacks**
|
||||||
|
2. Click on **puffin-app** stack
|
||||||
|
3. Click **"Update the stack"**
|
||||||
|
4. Enable **"Pull latest image version"**
|
||||||
|
5. Enable **"Re-pull image and redeploy"**
|
||||||
|
6. Click **"Update"**
|
||||||
|
|
||||||
|
Portainer will:
|
||||||
|
- Pull the latest image from Gitea registry
|
||||||
|
- Recreate the container with the new image
|
||||||
|
- Maintain your environment variables and configuration
|
||||||
|
|
||||||
|
### Method 2: Pull Specific Version
|
||||||
|
|
||||||
|
To rollback or deploy a specific commit:
|
||||||
|
|
||||||
|
1. Edit the stack in Portainer
|
||||||
|
2. Change the image tag from `latest` to `main-<commit-sha>`
|
||||||
|
```yaml
|
||||||
|
image: code.puffinoffset.com/matt/puffin-app:main-abc1234
|
||||||
|
```
|
||||||
|
3. Update the stack
|
||||||
|
|
||||||
|
## Monitoring Build Status
|
||||||
|
|
||||||
|
### Check Gitea Actions
|
||||||
|
|
||||||
|
1. Go to your Gitea repository: `https://code.puffinoffset.com/matt/puffin-app`
|
||||||
|
2. Navigate to **"Actions"** tab
|
||||||
|
3. View recent workflow runs
|
||||||
|
4. Click on a run to see detailed logs
|
||||||
|
|
||||||
|
### Verify Image in Registry
|
||||||
|
|
||||||
|
1. In Gitea, navigate to **"Packages"** or **"Registry"**
|
||||||
|
2. Look for `puffin-app` images
|
||||||
|
3. Verify tags: `latest` and `main-<sha>` tags should be present
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Build Failed in Gitea Actions
|
||||||
|
|
||||||
|
**Check the workflow logs:**
|
||||||
|
1. Go to Gitea → puffin-app → Actions
|
||||||
|
2. Click on the failed run
|
||||||
|
3. Review error messages
|
||||||
|
|
||||||
|
**Common issues:**
|
||||||
|
- Registry authentication failed: Check secrets.GITHUB_TOKEN is available
|
||||||
|
- Build errors: Review Dockerfile and build logs
|
||||||
|
- Network issues: Check runner connectivity
|
||||||
|
|
||||||
|
### Image Pull Failed in Portainer
|
||||||
|
|
||||||
|
**Error:** `unauthorized: authentication required`
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Verify registry is configured in Portainer (Step 1)
|
||||||
|
2. Check credentials are correct
|
||||||
|
3. Ensure registry URL is `code.puffinoffset.com` (no `https://`)
|
||||||
|
|
||||||
|
### Container Won't Start
|
||||||
|
|
||||||
|
**Check environment variables:**
|
||||||
|
1. Verify `.env` file exists on the host
|
||||||
|
2. Ensure volume mount path is correct in docker-compose.portainer.yml
|
||||||
|
3. Check container logs for missing env var errors
|
||||||
|
|
||||||
|
**Check port conflicts:**
|
||||||
|
1. Ensure port 3800 is not already in use
|
||||||
|
2. Run `netstat -tuln | grep 3800` on the host
|
||||||
|
|
||||||
|
### Application Not Accessible
|
||||||
|
|
||||||
|
**Verify container is running:**
|
||||||
|
```bash
|
||||||
|
docker ps | grep puffin-app
|
||||||
|
```
|
||||||
|
|
||||||
|
**Check nginx reverse proxy:**
|
||||||
|
- Review host Nginx configuration (`nginx-host.conf`)
|
||||||
|
- Ensure proxy_pass points to correct container port
|
||||||
|
- Test direct access: `http://server-ip:3800`
|
||||||
|
|
||||||
|
**Check firewall:**
|
||||||
|
```bash
|
||||||
|
# Example for ufw
|
||||||
|
sudo ufw status
|
||||||
|
sudo ufw allow 3800/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables Reference
|
||||||
|
|
||||||
|
| Variable | Description | Required |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `VITE_WREN_API_TOKEN` | Wren Climate API authentication token | Yes |
|
||||||
|
| `VITE_FORMSPREE_CONTACT_ID` | Formspree contact form endpoint ID | Yes |
|
||||||
|
| `VITE_FORMSPREE_OFFSET_ID` | Formspree offset order form endpoint ID | Yes |
|
||||||
|
| `NODE_ENV` | Node environment (set to `production`) | Auto-set |
|
||||||
|
|
||||||
|
## Rollback Procedure
|
||||||
|
|
||||||
|
To rollback to a previous version:
|
||||||
|
|
||||||
|
1. Find the commit SHA of the working version
|
||||||
|
2. In Portainer, edit the stack
|
||||||
|
3. Change image tag to `main-<previous-sha>`
|
||||||
|
4. Update the stack
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```yaml
|
||||||
|
# Before (current broken version)
|
||||||
|
image: code.puffinoffset.com/matt/puffin-app:latest
|
||||||
|
|
||||||
|
# After (rollback to specific commit)
|
||||||
|
image: code.puffinoffset.com/matt/puffin-app:main-ab0dbbd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
1. **Never commit `.env` file to git** (already in .gitignore)
|
||||||
|
2. **Use Gitea access tokens** instead of passwords for registry auth
|
||||||
|
3. **Restrict registry access** to necessary users
|
||||||
|
4. **Review Gitea Actions logs** for sensitive data exposure
|
||||||
|
5. **Keep base images updated** (node:20-alpine, nginx:alpine)
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Set up monitoring/alerting for failed builds
|
||||||
|
- Configure automatic backups of `.env` file
|
||||||
|
- Implement health checks in docker-compose
|
||||||
|
- Set up SSL certificates via Let's Encrypt
|
||||||
|
- Configure log aggregation for production debugging
|
||||||
29
docker-compose.portainer.yml
Normal file
29
docker-compose.portainer.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: code.puffinoffset.com/matt/puffin-app:latest
|
||||||
|
ports:
|
||||||
|
- "3800:3800"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
# Mount .env file for runtime environment variable injection
|
||||||
|
- ./.env:/usr/share/nginx/html/.env:ro
|
||||||
|
# Optional: override nginx config if needed
|
||||||
|
# - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
|
||||||
|
# Production deployment notes:
|
||||||
|
# 1. Ensure .env file exists on the host with required variables:
|
||||||
|
# - VITE_WREN_API_TOKEN
|
||||||
|
# - VITE_FORMSPREE_CONTACT_ID
|
||||||
|
# - VITE_FORMSPREE_OFFSET_ID
|
||||||
|
#
|
||||||
|
# 2. Configure Gitea registry authentication in Portainer before deploying
|
||||||
|
#
|
||||||
|
# 3. To update to new image:
|
||||||
|
# - Navigate to stack in Portainer
|
||||||
|
# - Click "Update the stack"
|
||||||
|
# - Enable "Pull latest image version"
|
||||||
|
# - Click "Update"
|
||||||
Loading…
x
Reference in New Issue
Block a user