diff --git a/.gitignore b/.gitignore index add57be..fd31aee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ bin/ obj/ /packages/ riderModule.iml -/_ReSharper.Caches/ \ No newline at end of file +/_ReSharper.Caches/ + +Publish/ \ No newline at end of file diff --git a/Docker/docker-compose.yml b/Docker/docker-compose.yml new file mode 100644 index 0000000..f013431 --- /dev/null +++ b/Docker/docker-compose.yml @@ -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/ \ No newline at end of file diff --git a/Docker/dockerfile.server b/Docker/dockerfile.server new file mode 100644 index 0000000..8cf04a6 --- /dev/null +++ b/Docker/dockerfile.server @@ -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"] \ No newline at end of file diff --git a/Docker/image-create.py b/Docker/image-create.py new file mode 100644 index 0000000..2382435 --- /dev/null +++ b/Docker/image-create.py @@ -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() diff --git a/Docker/image-push.py b/Docker/image-push.py new file mode 100644 index 0000000..1890130 --- /dev/null +++ b/Docker/image-push.py @@ -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() diff --git a/Docker/publish-aio.py b/Docker/publish-aio.py new file mode 100644 index 0000000..d5cb7cb --- /dev/null +++ b/Docker/publish-aio.py @@ -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 diff --git a/Docker/publish-project.py b/Docker/publish-project.py new file mode 100644 index 0000000..9eb1088 --- /dev/null +++ b/Docker/publish-project.py @@ -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() diff --git a/FoodsharingSiegen.Server/Dialogs/AddProspectModal.razor b/FoodsharingSiegen.Server/Dialogs/AddProspectModal.razor index dfa4a9c..28c9d04 100644 --- a/FoodsharingSiegen.Server/Dialogs/AddProspectModal.razor +++ b/FoodsharingSiegen.Server/Dialogs/AddProspectModal.razor @@ -23,7 +23,7 @@ - Memo (optional) + Info (optional)