Add Docker support for FoodsharingSiegen project

Introduced Docker setup, including `docker-compose.yml`, `dockerfile.server`, and related Python helper scripts (`image-create.py`, `image-push.py`, `publish-project.py`, `publish-aio.py`) for building, managing, and deploying Docker images. Updated `.gitignore` to exclude published files, and renamed a field label in the Blazor component for better clarity.
This commit is contained in:
Andre Beging
2025-03-28 16:29:43 +01:00
parent 3d92833199
commit 859b0c3712
8 changed files with 206 additions and 2 deletions

4
.gitignore vendored
View File

@@ -2,4 +2,6 @@ bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
/_ReSharper.Caches/
Publish/

10
Docker/docker-compose.yml Normal file
View File

@@ -0,0 +1,10 @@
services:
server:
container_name: fs-onboarding
hostname: fs-onboarding
image: ghcr.io/troogs/fs-onboarding/server:latest
ports:
- "8100:56000"
volumes:
- /docker/data/fs-onboarding/config:/app/config/
- /docker/data/fs-onboarding/data:/app/data/

11
Docker/dockerfile.server Normal file
View File

@@ -0,0 +1,11 @@
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
RUN apt-get update && apt-get install iputils-ping -y
LABEL org.opencontainers.image.source=https://github.com/TroogS/FoodsharingSiegen
WORKDIR /app
# Copy the Blazor Server published files from the Publish folder relative to the build context
COPY ../Publish/Server/ .
ENTRYPOINT ["dotnet", "FoodsharingSiegen.Server.dll"]

63
Docker/image-create.py Normal file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python3
import subprocess
from datetime import datetime
def image_exists_on_server(full_image_tag):
"""
Check if a Docker image exists on the remote server.
Uses 'docker manifest inspect' which returns 0 if the image is found.
Debug output is printed for inspection.
"""
try:
subprocess.run(
["docker", "manifest", "inspect", full_image_tag],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
print(f"[DEBUG] Found image on server: {full_image_tag}")
return True
except subprocess.CalledProcessError as e:
print(f"[DEBUG] Image not found on server: {full_image_tag}. Error: {e.stderr.strip()}")
return False
def get_date_tag(base_tag, today):
"""
Given the base image tag (e.g. 'ghcr.io/troogs/fs-onboarding/server')
and today's date, find the first available tag in the format YYYYMMDD-i
that is not already present on the remote server.
"""
i = 1
while True:
date_tag = f"{today}-{i}"
full_tag = f"{base_tag}:{date_tag}"
print(f"[DEBUG] Checking tag {date_tag}: {full_tag}")
if not image_exists_on_server(full_tag):
print(f"[DEBUG] Tag {date_tag} is available for use.")
return date_tag
else:
print(f"[DEBUG] Tag {date_tag} is already in use. Incrementing index.")
i += 1
def main():
# Get today's date in YYYYMMDD format.
today = datetime.now().strftime("%Y%m%d")
print(f"Using date tag base: {today}")
base_image_tag = "ghcr.io/troogs/fs-onboarding/server"
date_tag = get_date_tag(base_image_tag, today)
print(f"Common date tag determined: {date_tag}")
# Build command using the common date tag for the server image.
command = (
f"docker build -f dockerfile.server "
f"-t {base_image_tag}:latest "
f"-t {base_image_tag}:{date_tag} ..\\"
)
print(f"Executing: {command}")
subprocess.run(command, shell=True, check=True)
if __name__ == "__main__":
main()

65
Docker/image-push.py Normal file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
import subprocess
import re
def get_latest_timestamp_tag():
"""
Searches local images for tags matching the pattern:
ghcr.io/troogs/fs-onboarding/server:YYYYMMDD-i
and returns the latest timestamp tag (YYYYMMDD-i) based on date and index.
"""
# Regular expression to match tags like: ghcr.io/troogs/fs-onboarding/server:20250328-1
pattern = re.compile(r"ghcr\.io/troogs/fs-onboarding/server:(\d{8})-(\d+)$")
# List images for the repository using the docker images command.
result = subprocess.run(
["docker", "images", "ghcr.io/troogs/fs-onboarding/server", "--format", "{{.Repository}}:{{.Tag}}"],
stdout=subprocess.PIPE,
text=True,
check=True
)
tags = result.stdout.strip().splitlines()
timestamp_tags = []
for tag in tags:
m = pattern.match(tag)
if m:
date_part, index = m.groups()
timestamp_tags.append((date_part, int(index)))
if not timestamp_tags:
return None
# Choose the maximum tuple; lexicographical order works for YYYYMMDD and then by numeric index.
latest = max(timestamp_tags, key=lambda x: (x[0], x[1]))
return f"{latest[0]}-{latest[1]}"
def push_image(image_tag):
"""
Pushes the specified image tag to the container registry.
"""
print(f"Pushing image: {image_tag}")
subprocess.run(["docker", "push", image_tag], check=True)
def main():
# Retrieve the latest timestamp tag for the server image.
server_timestamp = get_latest_timestamp_tag()
if server_timestamp is None:
print("Error: Could not find timestamp tagged images for server.")
return
common_timestamp = server_timestamp
print(f"Common timestamp tag identified: {common_timestamp}")
# Prepare the list of images to push: both 'latest' and the timestamp-tagged images.
images_to_push = [
"ghcr.io/troogs/fs-onboarding/server:latest",
f"ghcr.io/troogs/fs-onboarding/server:{common_timestamp}"
]
for image in images_to_push:
push_image(image)
if __name__ == "__main__":
main()

15
Docker/publish-aio.py Normal file
View File

@@ -0,0 +1,15 @@
import subprocess
import sys
# List of scripts to run in order
scripts = ['publish-project.py', 'image-create.py', 'image-push.py']
for script in scripts:
print(f"Executing {script}...")
try:
# Run the script using the current Python interpreter
subprocess.run([sys.executable, script], check=True)
print(f"{script} executed successfully.\n")
except subprocess.CalledProcessError as error:
print(f"Error: {script} failed with exit code {error.returncode}.")
break

38
Docker/publish-project.py Normal file
View File

@@ -0,0 +1,38 @@
import os
import subprocess
import sys
def main():
# Determine the absolute path of this script's directory
script_dir = os.path.dirname(os.path.abspath(__file__))
# Compute the path to the FoodsharingSiegen.Server project directory
# This assumes the following structure:
# files/
# Docker/ <-- this script resides here
# FoodsharingSiegen.Server/
project_dir = os.path.normpath(os.path.join(script_dir, '..', 'FoodsharingSiegen.Server'))
# Check if the project directory exists
if not os.path.isdir(project_dir):
print(f"Error: Project directory not found: {project_dir}")
sys.exit(1)
# Prepare the dotnet publish command
command = ["dotnet", "publish", project_dir]
print("Running command:", " ".join(command))
# Execute the command and capture the output
try:
result = subprocess.run(command, capture_output=True, text=True, check=True)
print(result.stdout)
if result.stderr:
print("Warnings/Errors:", result.stderr, file=sys.stderr)
except subprocess.CalledProcessError as e:
print("An error occurred while publishing the dotnet project.")
print(e.stdout)
print(e.stderr, file=sys.stderr)
sys.exit(e.returncode)
if __name__ == '__main__':
main()

View File

@@ -23,7 +23,7 @@
</div>
<Field>
<FieldLabel>Memo (optional)</FieldLabel>
<FieldLabel>Info (optional)</FieldLabel>
<TextEdit @bind-Text="Prospect.Memo" Placeholder="Beliebige Info"></TextEdit>
</Field>