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