Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/synkronus-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
with:
context: ./synkronus
file: ./synkronus/Dockerfile
platforms: linux/amd64,linux/arm64
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Expand Down
100 changes: 100 additions & 0 deletions .github/workflows/synkronus-portal-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Synkronus Portal Docker Build & Publish

on:
push:
branches:
- main
- dev
paths:
- 'synkronus-portal/**'
- '.github/workflows/synkronus-portal-docker.yml'
pull_request:
paths:
- 'synkronus-portal/**'
- '.github/workflows/synkronus-portal-docker.yml'
workflow_dispatch:
inputs:
version:
description: 'Version tag (e.g., v1.0.0). If empty, tags will be derived from the current ref.'
required: false
type: string
release:
types: [published]

env:
REGISTRY: ghcr.io
IMAGE_NAME: opendataensemble/synkronus-portal

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
id-token: write
attestations: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: all

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

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

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# For main branch: latest + version tag (manual dispatch) or release tag
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
# When triggered via manual dispatch with a version input
type=semver,pattern=v{{version}},enable=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }},value=${{ github.event.inputs.version }}
type=semver,pattern=v{{major}}.{{minor}},enable=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }},value=${{ github.event.inputs.version }}
# When triggered from a GitHub Release event, use the release tag name as the semver source
type=semver,pattern=v{{version}},enable=${{ github.event_name == 'release' }},value=${{ github.event.release.tag_name }}
type=semver,pattern=v{{major}}.{{minor}},enable=${{ github.event_name == 'release' }},value=${{ github.event.release.tag_name }}
# For other branches: branch name (pre-release)
type=ref,event=branch,enable=${{ github.event_name != 'release' && github.ref != 'refs/heads/main' }}
# For PRs: pr-number
type=ref,event=pr
# SHA for traceability (only for non-release events)
type=sha,prefix=sha-,enable=${{ github.event_name != 'release' && github.event_name != 'pull_request' }}
labels: |
org.opencontainers.image.title=Synkronus Portal
org.opencontainers.image.description=Synchronization API for offline-first applications
org.opencontainers.image.vendor=Open Data Ensemble

- name: Build and push Docker image
id: build
uses: docker/build-push-action@v5
with:
context: ./synkronus-portal
file: ./synkronus-portal/Dockerfile
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Generate artifact attestation
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@v1
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
15 changes: 15 additions & 0 deletions synkronus-portal/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
node_modules
npm-debug.log
dist
.git
.gitignore
.env
.env.local
.env.*.local
*.log
.DS_Store
coverage
.vscode
.idea


25 changes: 25 additions & 0 deletions synkronus-portal/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
docker-compose.dev.yml

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
53 changes: 53 additions & 0 deletions synkronus-portal/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Multi-stage build for Synkronus Portal
# Stage 1: Build the React application
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Stage 2: Serve with nginx
FROM nginx:alpine

# Copy built assets from builder
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy custom nginx configuration for SPA routing
RUN echo 'server { \
listen 80; \
server_name localhost; \
root /usr/share/nginx/html; \
index index.html; \
location / { \
try_files $uri $uri/ /index.html; \
} \
# Proxy API requests to backend \
location /api { \
rewrite ^/api(.*)$ $1 break; \
proxy_pass http://synkronus:8080; \
proxy_http_version 1.1; \
proxy_set_header Upgrade $http_upgrade; \
proxy_set_header Connection "upgrade"; \
proxy_set_header Host $host; \
proxy_set_header X-Real-IP $remote_addr; \
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; \
proxy_set_header X-Forwarded-Proto $scheme; \
client_max_body_size 100M; \
} \
}' > /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]


Loading