diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7d7027d --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# Local defaults: one Express process serves both frontend and API. +APP_ENV=local + +# Set only when building a separate static site in staging or production. +# API_BASE_URL=https://your-api.onrender.com + +# Set only on staging/production API Web Services. +# SITE_ORIGIN=https://your-static-site.onrender.com diff --git a/.github/workflows/cd-production.yml b/.github/workflows/cd-production.yml index f01f408..6e55d25 100644 --- a/.github/workflows/cd-production.yml +++ b/.github/workflows/cd-production.yml @@ -29,6 +29,9 @@ jobs: run: npm ci - name: Build + env: + APP_ENV: production + API_BASE_URL: ${{ vars.API_URL }} run: npm run build - name: Deploy to production diff --git a/.github/workflows/cd-staging.yml b/.github/workflows/cd-staging.yml index 2529ab9..8261b40 100644 --- a/.github/workflows/cd-staging.yml +++ b/.github/workflows/cd-staging.yml @@ -27,6 +27,9 @@ jobs: run: npm ci - name: Build + env: + APP_ENV: staging + API_BASE_URL: ${{ vars.API_URL }} run: npm run build - name: Deploy to staging @@ -48,7 +51,7 @@ jobs: run: sleep 60 - name: Smoke test staging site - run: curl -sSL "${{ vars.SITE_URL }}" | grep "Hello, World!" + run: curl -fsSL "${{ vars.SITE_URL }}" | grep -F "${{ vars.API_URL }}/api/hello" - name: Smoke test staging API run: curl -sSL "${{ vars.API_URL }}/api/hello" | grep "Hello, World!" diff --git a/README.md b/README.md index 1589003..504d5d7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,39 @@ # github-actions-demo -Test the github action workflow on a simple test +Express API plus static frontend CI/CD example. + +## Local development + +```sh +cp .env.example .env +npm ci +npm run build +npm start +``` + +`APP_ENV=local` is default. Express serves `dist` and `/api/hello` from one process, so frontend +uses same-origin `/api/hello`. + +## Render: separate API and static site + +Staging and production each need two Render services. Do not host frontend through API Web Service. + +| Service | Render type | Build command | Start/publish setting | +| ------- | ----------- | ------------------------- | ------------------------- | +| API | Web Service | `npm ci` | `npm start` | +| Site | Static Site | `npm ci && npm run build` | Publish directory: `dist` | + +Set these environment variables in Render: + +| Environment | Static Site variables | API Web Service variables | +| ----------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| Staging | `APP_ENV=staging`, `API_BASE_URL=https://.onrender.com` | `APP_ENV=staging`, `SITE_ORIGIN=https://.onrender.com` | +| Production | `APP_ENV=production`, `API_BASE_URL=https://github-actions-demo-tk0d.onrender.com` | `APP_ENV=production`, `SITE_ORIGIN=https://.onrender.com` | + +`API_BASE_URL` is required for staging/production static builds and must be absolute HTTP(S). +`SITE_ORIGIN` is required for staging/production API services and accepts comma-separated static +site origins. Application exits at startup/build when deployed configuration is incomplete. + +GitHub `staging` and `production` environments need `SITE_URL` and `API_URL` variables plus +separate `RENDER_SITE_DEPLOY_HOOK` and `RENDER_API_DEPLOY_HOOK` secrets. The deploy workflows +trigger both independent Render services; Render builds the site using its own `API_BASE_URL`. diff --git a/package.json b/package.json index a32b6a4..a29c69c 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "main": "src/server.js", "type": "commonjs", "scripts": { - "build": "node scripts/build.js", - "start": "node src/server.js", + "build": "node --env-file-if-exists=.env scripts/build.js", + "start": "node --env-file-if-exists=.env src/server.js", "test": "jest --testPathPattern=tests/unit", "test:integration": "jest --testPathPattern=tests/integration", "test:all": "jest", diff --git a/scripts/build.js b/scripts/build.js index 1a35b7e..3f509e3 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,11 +1,13 @@ const fs = require('fs'); const path = require('path'); +const { getApiBaseUrl } = require('../src/config'); const distDir = path.join(__dirname, '..', 'dist'); -// Simulate a "build": in a real app this might be webpack/vite/esbuild. -// Here we just generate a static index.html that calls the API. -const html = ` +function createHtml(apiBaseUrl) { + const helloUrl = `${apiBaseUrl}/api/hello`; + + return ` @@ -14,7 +16,7 @@ const html = `

Loading...