Prepare CI and deployment scaffolding
This commit is contained in:
10
.env.example
Normal file
10
.env.example
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Global defaults
|
||||||
|
NODE_ENV=development
|
||||||
|
|
||||||
|
# Backend service
|
||||||
|
DATABASE_URL=postgresql://merchantsofhope_user:merchantsofhope_password@merchantsofhope-supplyanddemandportal-database:5432/merchantsofhope_supplyanddemandportal
|
||||||
|
JWT_SECRET=merchantsofhope_jwt_secret_key_2024
|
||||||
|
PORT=3001
|
||||||
|
|
||||||
|
# Frontend service
|
||||||
|
REACT_APP_API_URL=http://localhost:3001
|
||||||
137
.gitea/workflows/ci.yml
Normal file
137
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
backend:
|
||||||
|
name: Backend Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: merchantsofhope_test
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
options: >-
|
||||||
|
--health-cmd="pg_isready -U postgres" --health-interval=10s --health-timeout=5s --health-retries=5
|
||||||
|
env:
|
||||||
|
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/merchantsofhope_test
|
||||||
|
JWT_SECRET: merchantsofhope_test_secret
|
||||||
|
NODE_ENV: test
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: backend/package-lock.json
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
working-directory: backend
|
||||||
|
|
||||||
|
- name: Run database migrations
|
||||||
|
run: npm run migrate
|
||||||
|
working-directory: backend
|
||||||
|
|
||||||
|
- name: Run backend tests
|
||||||
|
run: npm test -- --runInBand
|
||||||
|
working-directory: backend
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
name: Frontend Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: frontend/package-lock.json
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
working-directory: frontend
|
||||||
|
|
||||||
|
- name: Run frontend tests
|
||||||
|
run: npm test -- --watchAll=false
|
||||||
|
working-directory: frontend
|
||||||
|
|
||||||
|
docker-images:
|
||||||
|
name: Build Docker Images
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [backend, frontend]
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
env:
|
||||||
|
REGISTRY_HOST: ${{ secrets.REGISTRY_HOST }}
|
||||||
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
IMAGE_TAG: ${{ github.sha }}
|
||||||
|
BACKEND_IMAGE: merchantsofhope-supplyanddemandportal-backend
|
||||||
|
FRONTEND_IMAGE: merchantsofhope-supplyanddemandportal-frontend
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to registry
|
||||||
|
if: env.REGISTRY_HOST != ''
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY_HOST }}
|
||||||
|
username: ${{ env.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ env.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Determine image tags
|
||||||
|
id: meta
|
||||||
|
run: |
|
||||||
|
if [ -n "${REGISTRY_HOST}" ]; then
|
||||||
|
echo "push=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "backend_tag=${REGISTRY_HOST}/${BACKEND_IMAGE}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
||||||
|
echo "frontend_tag=${REGISTRY_HOST}/${FRONTEND_IMAGE}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "push=false" >> $GITHUB_OUTPUT
|
||||||
|
echo "backend_tag=${BACKEND_IMAGE}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
||||||
|
echo "frontend_tag=${FRONTEND_IMAGE}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build backend image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: backend
|
||||||
|
file: backend/Dockerfile
|
||||||
|
push: ${{ steps.meta.outputs.push == 'true' }}
|
||||||
|
tags: ${{ steps.meta.outputs.backend_tag }}
|
||||||
|
|
||||||
|
- name: Build frontend image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: frontend
|
||||||
|
file: frontend/Dockerfile
|
||||||
|
push: ${{ steps.meta.outputs.push == 'true' }}
|
||||||
|
tags: ${{ steps.meta.outputs.frontend_tag }}
|
||||||
|
|
||||||
|
- name: Summary
|
||||||
|
run: |
|
||||||
|
echo "Backend image tag: ${{ steps.meta.outputs.backend_tag }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Frontend image tag: ${{ steps.meta.outputs.frontend_tag }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
if [ "${{ steps.meta.outputs.push }}" = 'true' ]; then
|
||||||
|
echo "Images pushed to ${{ env.REGISTRY_HOST }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "Images built locally (not pushed). Set REGISTRY_* secrets to enable pushing." >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
!.env.example
|
||||||
*.log
|
*.log
|
||||||
tmp/
|
tmp/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
50
README.md
50
README.md
@@ -55,12 +55,18 @@ A comprehensive SAAS application for managing recruiter workflows, built with mo
|
|||||||
cd MerchantsOfHope-SupplyANdDemandPortal
|
cd MerchantsOfHope-SupplyANdDemandPortal
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Start the application**
|
2. **Copy environment template**
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
The defaults support Docker-based development. Adjust values as needed for local tooling or deployment pipelines.
|
||||||
|
|
||||||
|
3. **Start the application with Docker (recommended for parity)**
|
||||||
```bash
|
```bash
|
||||||
docker-compose up --build
|
docker-compose up --build
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Initialize the database**
|
4. **Initialize the database**
|
||||||
```bash
|
```bash
|
||||||
# Run database migrations
|
# Run database migrations
|
||||||
docker-compose exec merchantsofhope-supplyanddemandportal-backend npm run migrate
|
docker-compose exec merchantsofhope-supplyanddemandportal-backend npm run migrate
|
||||||
@@ -69,11 +75,31 @@ A comprehensive SAAS application for managing recruiter workflows, built with mo
|
|||||||
docker-compose exec merchantsofhope-supplyanddemandportal-backend npm run seed
|
docker-compose exec merchantsofhope-supplyanddemandportal-backend npm run seed
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Access the application**
|
5. **Access the application**
|
||||||
- Frontend: http://localhost:3000
|
- Frontend: http://localhost:3000
|
||||||
- Backend API: http://localhost:3001
|
- Backend API: http://localhost:3001
|
||||||
- Database: localhost:5432
|
- Database: localhost:5432
|
||||||
|
|
||||||
|
### Alternative: Native Node.js workflow
|
||||||
|
|
||||||
|
If you prefer running services outside Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
cd backend && npm install
|
||||||
|
cd ../frontend && npm install
|
||||||
|
|
||||||
|
# Start backend (uses .env)
|
||||||
|
cd ../backend
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# In a separate terminal start frontend
|
||||||
|
cd ../frontend
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensure a PostgreSQL instance is running and the `DATABASE_URL` in `.env` points to it.
|
||||||
|
|
||||||
### Demo Accounts
|
### Demo Accounts
|
||||||
|
|
||||||
The application comes with pre-seeded demo accounts:
|
The application comes with pre-seeded demo accounts:
|
||||||
@@ -140,9 +166,23 @@ docker-compose exec merchantsofhope-supplyanddemandportal-backend npm run test:w
|
|||||||
docker-compose exec merchantsofhope-supplyanddemandportal-frontend npm test
|
docker-compose exec merchantsofhope-supplyanddemandportal-frontend npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
To run tests without Docker, execute `npm test` inside `backend/` or `frontend/` after installing dependencies.
|
||||||
|
|
||||||
### Project Structure
|
## Continuous Integration
|
||||||
|
|
||||||
|
Gitea Actions configuration lives in `.gitea/workflows/ci.yml`. It:
|
||||||
|
- Runs backend and frontend unit tests on every push or pull request.
|
||||||
|
- Builds Docker images on pushes to the `main` branch, ready to publish to a registry (requires `REGISTRY_*` secrets).
|
||||||
|
|
||||||
|
See inline comments in the workflow for required secrets.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Coolify
|
||||||
|
|
||||||
|
Follow `docs/COOLIFY_DEPLOYMENT.md` for guidance on connecting this repository to a Coolify environment, configuring secrets, and enabling automated deploys via Gitea CI.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
```
|
```
|
||||||
MerchantsOfHope-SupplyANdDemandPortal/
|
MerchantsOfHope-SupplyANdDemandPortal/
|
||||||
├── backend/
|
├── backend/
|
||||||
|
|||||||
45
deploy/coolify/docker-compose.yml
Normal file
45
deploy/coolify/docker-compose.yml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
merchantsofhope-supplyanddemandportal-database:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: merchantsofhope-supplyanddemandportal-database
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-merchantsofhope_supplyanddemandportal}
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-merchantsofhope}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- merchantsofhope-supplyanddemandportal-postgres-data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER:-merchantsofhope}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
merchantsofhope-supplyanddemandportal-backend:
|
||||||
|
image: ${BACKEND_IMAGE:?set BACKEND_IMAGE}
|
||||||
|
container_name: merchantsofhope-supplyanddemandportal-backend
|
||||||
|
depends_on:
|
||||||
|
merchantsofhope-supplyanddemandportal-database:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
PORT: 3001
|
||||||
|
DATABASE_URL: postgresql://${POSTGRES_USER:-merchantsofhope}:${POSTGRES_PASSWORD}@merchantsofhope-supplyanddemandportal-database:5432/${POSTGRES_DB:-merchantsofhope_supplyanddemandportal}
|
||||||
|
JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET}
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:3001:3001"
|
||||||
|
|
||||||
|
merchantsofhope-supplyanddemandportal-frontend:
|
||||||
|
image: ${FRONTEND_IMAGE:?set FRONTEND_IMAGE}
|
||||||
|
container_name: merchantsofhope-supplyanddemandportal-frontend
|
||||||
|
depends_on:
|
||||||
|
- merchantsofhope-supplyanddemandportal-backend
|
||||||
|
environment:
|
||||||
|
PORT: 3000
|
||||||
|
REACT_APP_API_URL: ${REACT_APP_API_URL:-http://merchantsofhope-supplyanddemandportal-backend:3001}
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:3000:3000"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
merchantsofhope-supplyanddemandportal-postgres-data:
|
||||||
88
docs/COOLIFY_DEPLOYMENT.md
Normal file
88
docs/COOLIFY_DEPLOYMENT.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Coolify Deployment Guide
|
||||||
|
|
||||||
|
This guide summarizes how to promote MerchantsOfHope-SupplyANdDemandPortal from Gitea CI to a Coolify-managed environment.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- A Coolify instance with access to your container registry.
|
||||||
|
- Gitea repository hosting this project, with Gitea Actions enabled and a runner that supports Docker builds.
|
||||||
|
- Registry credentials (username, password/access token, hostname) for publishing backend and frontend images.
|
||||||
|
- Optional: a managed PostgreSQL instance. The provided Compose file creates one automatically if you do not have an external database.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
1. Gitea Actions builds and (optionally) pushes backend and frontend images when changes land on the `main` branch.
|
||||||
|
2. Coolify pulls those images and runs the stack defined in `deploy/coolify/docker-compose.yml`.
|
||||||
|
3. Post-deploy hooks run database migrations so the schema matches the current code.
|
||||||
|
|
||||||
|
## Configure Gitea CI
|
||||||
|
|
||||||
|
Secrets required by `.gitea/workflows/ci.yml`:
|
||||||
|
|
||||||
|
| Secret | Purpose |
|
||||||
|
| ------ | ------- |
|
||||||
|
| `REGISTRY_HOST` | Hostname of the registry (e.g., `registry.example.com`). Leave blank to skip pushing. |
|
||||||
|
| `REGISTRY_USERNAME` | Registry account used to push images. |
|
||||||
|
| `REGISTRY_PASSWORD` | Token/password for the account. |
|
||||||
|
|
||||||
|
The workflow publishes two images using the commit SHA as the tag:
|
||||||
|
|
||||||
|
- `${REGISTRY_HOST}/merchantsofhope-supplyanddemandportal-backend:<sha>`
|
||||||
|
- `${REGISTRY_HOST}/merchantsofhope-supplyanddemandportal-frontend:<sha>`
|
||||||
|
|
||||||
|
Expose the tag you want Coolify to deploy by either:
|
||||||
|
|
||||||
|
- Creating a Git tag/release and configuring Coolify to deploy tags, or
|
||||||
|
- Using Coolify's "Deploy on new commit" option and translating the latest SHA into the `BACKEND_IMAGE` / `FRONTEND_IMAGE` environment variables.
|
||||||
|
|
||||||
|
## Prepare the Coolify Stack
|
||||||
|
|
||||||
|
1. **Add the repository**
|
||||||
|
- In Coolify, create a new *Docker Compose Application*.
|
||||||
|
- Connect your Gitea account and select this repository.
|
||||||
|
- Set the Compose path to `deploy/coolify/docker-compose.yml`.
|
||||||
|
|
||||||
|
2. **Set environment variables** (use the UI or a `.env` file uploaded to Coolify):
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
| -------- | ----------- |
|
||||||
|
| `BACKEND_IMAGE` | Fully qualified image tag published by CI (e.g., `registry.example.com/merchantsofhope-supplyanddemandportal-backend:abcd123`). |
|
||||||
|
| `FRONTEND_IMAGE` | Fully qualified image tag published by CI. |
|
||||||
|
| `POSTGRES_DB` | Database name (defaults to `merchantsofhope_supplyanddemandportal`). |
|
||||||
|
| `POSTGRES_USER` | Database user (defaults to `merchantsofhope`). |
|
||||||
|
| `POSTGRES_PASSWORD` | **Required.** Strong password for the database. |
|
||||||
|
| `JWT_SECRET` | **Required.** Secret key for backend token signing. |
|
||||||
|
| `REACT_APP_API_URL` | URL the frontend uses to reach the backend (defaults to the internal service URL). |
|
||||||
|
|
||||||
|
Configure any additional secrets used by your environment (mail providers, analytics, etc.).
|
||||||
|
|
||||||
|
3. **Networking and ports**
|
||||||
|
- Expose port `3000` externally for the frontend.
|
||||||
|
- Optionally expose `3001` if you want direct API access; otherwise rely on the frontend or internal services.
|
||||||
|
- Attach the application to an HTTPS domain using Coolify's built-in proxy configuration.
|
||||||
|
|
||||||
|
4. **Database migrations**
|
||||||
|
- Add a post-deployment command in Coolify to run `npm run migrate` inside the backend container:
|
||||||
|
```bash
|
||||||
|
docker compose exec merchantsofhope-supplyanddemandportal-backend npm run migrate
|
||||||
|
```
|
||||||
|
- If you maintain seed data, run `npm run seed` the same way.
|
||||||
|
|
||||||
|
5. **Zero-downtime considerations**
|
||||||
|
- Enable health checks in Coolify so new backend containers pass readiness before switching traffic.
|
||||||
|
- Consider scaling the backend to more than one replica once load requires it.
|
||||||
|
|
||||||
|
## Local Development Parity
|
||||||
|
|
||||||
|
- Use `docker-compose up --build` to replicate the production stack locally.
|
||||||
|
- Keep `.env` aligned with the variables described above so Gitea CI, local development, and Coolify deployment share the same configuration keys.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Likely Cause | Fix |
|
||||||
|
| ------- | ------------ | --- |
|
||||||
|
| Backend container exits immediately | Missing or incorrect `DATABASE_URL`/`POSTGRES_*` values | Verify Coolify environment variables and that the database service is healthy. |
|
||||||
|
| Frontend cannot reach API | `REACT_APP_API_URL` points to an unreachable host | Adjust to Coolify-provided domain or internal service URL. |
|
||||||
|
| CI cannot push images | Registry secrets not configured or incorrect permissions | Update `REGISTRY_*` secrets and ensure the runner has network access to the registry. |
|
||||||
|
|
||||||
|
For additional customization (e.g., connecting to an external PostgreSQL cluster or adding Redis), copy `deploy/coolify/docker-compose.yml` and extend it with your required services.
|
||||||
Reference in New Issue
Block a user