Collapse the separate backend (Node) and frontend (nginx) images into one image where the Express backend serves the built React app in addition to the API and game content, so deployment is a single container and one origin. - Add a root Dockerfile with frontend-builder + backend-builder stages producing one runtime image; remove backend/ and frontend/ Dockerfiles, the nginx config, and the frontend entrypoint - Serve the frontend dist and SPA fallback from Express (config.serveFrontend / FRONTEND_DIST_PATH), before auth/maintenance so the shell always loads and before the API 404 so client routes resolve to index.html - Port the SPA-oriented CSP from nginx into Helmet, gated on serveFrontend; keep the strict API-only policy otherwise - Reduce both compose files to a single flashpoint-web service on WEB_PORT -> 3100; drop API_PORT, BACKEND_HOST, BACKEND_PORT - Build one image in the release workflow instead of a two-service matrix - Update .env.example, README, workflow README, and env var docs Also add .gitattributes (*.sh eol=lf) and normalize the entrypoint line endings — without it, Windows checkouts bake CRLF into the image and the shebang fails with "no such file or directory".
145 lines
4.7 KiB
YAML
145 lines
4.7 KiB
YAML
name: Build and Push Docker Images
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*.*.*'
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
REGISTRY_DOCKERHUB: docker.io
|
|
|
|
jobs:
|
|
build-and-push:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
outputs:
|
|
version: ${{ steps.meta.outputs.version }}
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
with:
|
|
driver-opts: |
|
|
image=moby/buildkit:latest
|
|
network=host
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY_DOCKERHUB }}
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Extract metadata (tags, labels)
|
|
id: meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ${{ env.REGISTRY_DOCKERHUB }}/${{ secrets.DOCKERHUB_USERNAME }}/flashpoint-web
|
|
tags: |
|
|
# Tag with full version (v1.0.0 -> 1.0.0)
|
|
type=semver,pattern={{version}}
|
|
# Tag with major.minor (v1.0.0 -> 1.0)
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
# Tag with major version (v1.0.0 -> 1)
|
|
type=semver,pattern={{major}}
|
|
# Latest tag
|
|
type=raw,value=latest
|
|
|
|
- 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=gha,scope=flashpoint-web
|
|
cache-to: type=gha,scope=flashpoint-web,mode=max
|
|
platforms: linux/amd64
|
|
build-args: |
|
|
BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
|
|
VCS_REF=${{ github.sha }}
|
|
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
|
provenance: false
|
|
sbom: false
|
|
|
|
summary:
|
|
needs: build-and-push
|
|
runs-on: ubuntu-latest
|
|
if: always()
|
|
steps:
|
|
- name: Build Summary
|
|
run: |
|
|
echo "## Docker Build Summary" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### Image Built:" >> $GITHUB_STEP_SUMMARY
|
|
echo "- \`flashpoint-web\` (API + UI)" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### Registry:" >> $GITHUB_STEP_SUMMARY
|
|
echo "- Docker Hub: \`${{ secrets.DOCKERHUB_USERNAME }}/flashpoint-web\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### Pull Command:" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
|
|
echo "docker pull ${{ secrets.DOCKERHUB_USERNAME }}/flashpoint-web:latest" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
|
|
create-release:
|
|
needs: build-and-push
|
|
runs-on: ubuntu-latest
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Get version from tag
|
|
id: version
|
|
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
|
|
- name: Generate release notes
|
|
id: release-notes
|
|
run: |
|
|
# Get the previous tag
|
|
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
|
|
|
echo "## Docker Image" >> release_notes.md
|
|
echo "" >> release_notes.md
|
|
echo "Pull the image from Docker Hub:" >> release_notes.md
|
|
echo "" >> release_notes.md
|
|
echo "\`\`\`bash" >> release_notes.md
|
|
echo "docker pull ${{ secrets.DOCKERHUB_USERNAME }}/flashpoint-web:${{ steps.version.outputs.version }}" >> release_notes.md
|
|
echo "\`\`\`" >> release_notes.md
|
|
echo "" >> release_notes.md
|
|
|
|
if [ -n "$PREV_TAG" ]; then
|
|
echo "## Changes since $PREV_TAG" >> release_notes.md
|
|
echo "" >> release_notes.md
|
|
git log --pretty=format:"- %s (%h)" $PREV_TAG..HEAD >> release_notes.md
|
|
echo "" >> release_notes.md
|
|
fi
|
|
|
|
- name: Create GitHub Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
tag_name: ${{ steps.version.outputs.version }}
|
|
name: Release ${{ steps.version.outputs.version }}
|
|
body_path: release_notes.md
|
|
draft: false
|
|
prerelease: ${{ contains(steps.version.outputs.version, '-') }}
|
|
generate_release_notes: true
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|