Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Build and Publish Docker Image

on:
workflow_call:
inputs:
version:
required: true
type: string

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build:
name: Build Docker Image (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v7

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

- name: Log in to Container Registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Sanitize platform for tag
id: platform
run: |
PLATFORM_TAG=$(echo "${{ matrix.platform }}" | tr '/' '-')
echo "tag=$PLATFORM_TAG" >> "$GITHUB_OUTPUT"

- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: .
push: true
build-args: |
ORIGIN=${{ vars.FRONTEND_ORIGIN || 'https://www.sgf.dev' }}
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.version }}-${{ steps.platform.outputs.tag }}
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-to: type=inline
platforms: ${{ matrix.platform }}
provenance: false

merge:
name: Create Multi-Arch Manifest
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
packages: write
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

- name: Log in to Container Registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create and push multi-arch manifest
run: |
docker buildx imagetools create \
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.version }} \
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.version }}-linux-amd64 \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.version }}-linux-arm64
42 changes: 42 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Release

on:
push:
branches: [main]

permissions:
contents: write
issues: write
pull-requests: write
packages: write

jobs:
release:
runs-on: ubuntu-latest
outputs:
new-release-published: ${{ steps.semantic.outputs.new-release-published }}
release-version: ${{ steps.semantic.outputs.release-version }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0

- uses: actions/setup-node@v7
with:
node-version: "lts/*"

- run: npm install -g semantic-release@25.0.9 conventional-changelog-conventionalcommits@8.0.0 @semantic-release/exec@7.1.0

- name: Create Release
id: semantic
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release

docker:
needs: release
if: ${{ needs.release.outputs.new-release-published == 'true' }}
uses: ./.github/workflows/docker-publish.yml
with:
version: ${{ needs.release.outputs.release-version }}
secrets: inherit
19 changes: 19 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"branches": ["main"],
"plugins": [
["@semantic-release/commit-analyzer", {
"preset": "conventionalcommits",
"releaseRules": [
{ "type": "chore", "release": "patch" },
{ "type": "build", "release": "patch" }
]
}],
["@semantic-release/release-notes-generator", {
"preset": "conventionalcommits"
}],
"@semantic-release/github",
["@semantic-release/exec", {
"successCmd": "echo 'new-release-published=true' >> $GITHUB_OUTPUT && echo 'release-version=${nextRelease.version}' >> $GITHUB_OUTPUT"
}]
]
}
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ The image defaults to port 3000 and `BODY_SIZE_LIMIT=9M`; it does not include a

`.dockerignore` permits only build inputs and excludes local secrets and output.
Run `npm test` for the unit suite. It type-checks tests once and runs every test
folder, without a browser or live CMS. CI runs the same command, Svelte checking,
generated API freshness checks and build. It does not deploy, use action secrets
or build/push an image. Visual checks and complete member journeys belong in staging.
folder, without a browser or live CMS. PR CI runs the same command, Svelte checking,
generated API freshness checks and build. It does not deploy or build/push images.

On `main`, semantic-release creates GitHub releases from Conventional Commits.
A new release calls the reusable Docker publishing workflow to push native
`linux/amd64` and `linux/arm64` images to `ghcr.io/sgfdevs/sgf.dev`, tagged
with the release version and `latest`.

Images build for the public repository Actions variable `FRONTEND_ORIGIN`,
defaulting to `https://www.sgf.dev`. The workflow passes it as the Docker `ORIGIN`
build argument. No CMS keys or other secrets are build arguments. Rebuild for a
different origin; setting a runtime `ORIGIN` cannot change the built origin.
Staging and production promotions remain manual; automated promotion jobs are
disabled. Test staging, including visual checks and complete member journeys,
before promoting to production.
Loading