Skip to content

Commit 0e61859

Browse files
committed
feat: ✨ Add version information endpoint and display in UI; update Dockerfile for metadata
1 parent 381658b commit 0e61859

File tree

5 files changed

+97
-8
lines changed

5 files changed

+97
-8
lines changed

.github/workflows/docker-publish.yml

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ on:
44
push:
55
branches:
66
- main
7+
tags:
8+
- 'v*'
9+
pull_request:
10+
branches:
11+
- main
712

813
permissions:
914
contents: read
@@ -23,23 +28,44 @@ jobs:
2328
uses: docker/setup-buildx-action@v2
2429

2530
- name: Log in to GitHub Container Registry
31+
if: github.event_name != 'pull_request'
2632
uses: docker/login-action@v2
2733
with:
2834
registry: ghcr.io
2935
username: ${{ github.actor }}
3036
password: ${{ secrets.GITHUB_TOKEN }}
3137

32-
- name: Set lowercase repository owner
33-
id: setup
34-
run: echo "owner_lower=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
38+
- name: Extract metadata
39+
id: meta
40+
uses: docker/metadata-action@v5
41+
with:
42+
images: ghcr.io/${{ github.repository_owner }}/calendar-proxy
43+
tags: |
44+
# set latest tag for default branch
45+
type=raw,value=latest,enable={{is_default_branch}}
46+
# tag event
47+
type=ref,event=tag
48+
# branch event
49+
type=ref,event=branch
50+
# pr event
51+
type=ref,event=pr
52+
# set version tag for tags starting with v
53+
type=semver,pattern={{version}}
54+
type=semver,pattern={{major}}.{{minor}}
55+
type=semver,pattern={{major}}
3556
3657
- name: Build and push Docker image (multi-arch)
3758
uses: docker/build-push-action@v4
3859
with:
3960
context: .
40-
push: true
61+
push: ${{ github.event_name != 'pull_request' }}
4162
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386
42-
tags: |
43-
ghcr.io/${{ steps.setup.outputs.owner_lower }}/calendar-proxy:latest
44-
ghcr.io/${{ steps.setup.outputs.owner_lower }}/calendar-proxy:${{ github.sha }}
63+
tags: ${{ steps.meta.outputs.tags }}
64+
labels: ${{ steps.meta.outputs.labels }}
65+
build-args: |
66+
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
67+
BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
68+
COMMIT=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
69+
cache-from: type=gha
70+
cache-to: type=gha,mode=max
4571
file: Dockerfile

Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,26 @@ RUN mkdir -p static && tailwindcss -i ./input.css --content ./index.html -o ./st
2222
# Runtime stage - minimal Python environment
2323
FROM python:3.13.7-slim AS runtime
2424

25+
# Build arguments for metadata
26+
ARG VERSION=dev
27+
ARG BUILD_DATE
28+
ARG COMMIT
29+
30+
# OCI Image Labels for better metadata
31+
LABEL org.opencontainers.image.title="Calendar Proxy" \
32+
org.opencontainers.image.description="A secure proxy service that makes browser-only calendar links (like Office 365 shared calendars) accessible to calendar clients that can't authenticate or handle custom headers" \
33+
org.opencontainers.image.version="${VERSION}" \
34+
org.opencontainers.image.created="${BUILD_DATE}" \
35+
org.opencontainers.image.revision="${COMMIT}" \
36+
org.opencontainers.image.vendor="OidaTiftla" \
37+
org.opencontainers.image.licenses="MIT" \
38+
org.opencontainers.image.source="https://github.com/OidaTiftla/calendar-proxy"
39+
40+
# Set environment variables for runtime access
41+
ENV APP_VERSION=${VERSION} \
42+
APP_BUILD_DATE=${BUILD_DATE} \
43+
APP_COMMIT=${COMMIT}
44+
2545
# Create non-root user for security
2646
RUN groupadd --gid 1000 appuser && \
2747
useradd --uid 1000 --gid appuser --shell /bin/bash --create-home appuser

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ Open `http://<host>:8000/` in a browser to use the included `index.html` UI for
144144

145145
- `GET /sub/{token}/{b64url}/{name}.ics` — subscription proxy endpoint.
146146
- `GET /healthz` — health check JSON response.
147+
- `GET /version` — version information JSON response (includes version, build date, and commit).
147148
- `GET /` — web UI for building calendar subscription URLs.
148149
- `GET /static/{path}` — static files (CSS, etc.) for the web UI.
149150

src/app.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,16 @@ def sanitize_url_for_logging(url: str) -> str:
9696
@app.on_event("startup")
9797
async def startup_event():
9898
"""Initialize any startup tasks."""
99+
# Get version info from environment variables
100+
version = os.getenv("APP_VERSION", "dev")
101+
build_date = os.getenv("APP_BUILD_DATE", "unknown")
102+
commit = os.getenv("APP_COMMIT", "unknown")
103+
99104
logging.info("Calendar Proxy starting up")
105+
logging.info(f"Version: {version}")
106+
logging.info(f"Build Date: {build_date}")
107+
logging.info(f"Commit: {commit}")
108+
100109
# Do initial cleanup of in-memory rate limiter
101110
if not redis_client:
102111
await _cleanup_inmem_limits()
@@ -375,13 +384,37 @@ def health():
375384
return {"status": "ok"}
376385

377386

387+
@app.get("/version")
388+
def version():
389+
"""Get version information"""
390+
return {
391+
"version": os.getenv("APP_VERSION", "dev"),
392+
"build_date": os.getenv("APP_BUILD_DATE", "unknown"),
393+
"commit": os.getenv("APP_COMMIT", "unknown")
394+
}
395+
396+
378397
# Serve a small static index.html UI if present (file saved alongside app)
379398
@app.get("/", response_class=HTMLResponse)
380399
async def index():
381400
try:
382401
here = os.path.dirname(__file__)
383402
with open(os.path.join(here, "index.html"), "r", encoding="utf-8") as f:
384-
return HTMLResponse(f.read())
403+
html_content = f.read()
404+
405+
# Replace placeholders with actual version information
406+
version = os.getenv("APP_VERSION", "dev")
407+
build_date = os.getenv("APP_BUILD_DATE", "unknown")
408+
commit = os.getenv("APP_COMMIT", "unknown")
409+
410+
# Truncate commit to 8 characters if it's not 'unknown'
411+
commit_display = commit[:8] if commit != 'unknown' else commit
412+
413+
html_content = html_content.replace("{{APP_VERSION}}", version)
414+
html_content = html_content.replace("{{APP_BUILD_DATE}}", build_date)
415+
html_content = html_content.replace("{{APP_COMMIT}}", commit_display)
416+
417+
return HTMLResponse(html_content)
385418
except FileNotFoundError:
386419
return PlainTextResponse("Index not found", status_code=404)
387420

src/index.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,15 @@ <h1 class="text-3xl font-bold text-cyan-400 mb-2">Calendar Proxy</h1>
229229
</div>
230230
</div>
231231
</section>
232+
233+
<!-- Version Information -->
234+
<div class="text-center mt-8 pt-6 border-t border-slate-700">
235+
<p class="text-xs text-slate-500">
236+
Version: <span class="text-slate-400">{{APP_VERSION}}</span> | Build:
237+
<span class="text-slate-400">{{APP_BUILD_DATE}}</span> | Commit:
238+
<span class="text-slate-400">{{APP_COMMIT}}</span>
239+
</p>
240+
</div>
232241
</main>
233242

234243
<script>

0 commit comments

Comments
 (0)