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
|
||||
- Unit tests using Vitest and React Testing Library
|
||||
- Test files in `__tests__` directories
|
||||
- Run with `npm test`
|
||||
- Test configuration in `vitest.config.ts` with jsdom environment
|
||||
- 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
|
||||
- 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
|
||||
- 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