diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml
new file mode 100644
index 0000000..2025f3e
--- /dev/null
+++ b/.forgejo/workflows/build.yml
@@ -0,0 +1,48 @@
+name: Build and push docker image
+
+# start workflow every time a tag/release is created
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/catthehacker/ubuntu:act-latest
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ defaults:
+ run:
+ working-directory: /tmp
+
+ # build the docker container
+ steps:
+ - name: ✨ Installing just command runner
+ run: |
+ # download and extract just to /bin/just
+ curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /bin
+ just --help || echo "Error: 'just' wasn't found. Maybe the download failed?"
+
+ - name: ⬇️ Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: 🐋 Build Container
+ run: |
+ # extract tag name without the prefix
+ export TAG=$(echo "${{ github.ref }}" | sed 's/refs\/tags\///')
+ # build the container
+ just -f /workspace/sangelo/website/Justfile build "${TAG}"
+
+ - name: 🐳 Publish Container
+ run: |
+ export TAG=$(echo "${{ github.ref }}" | sed 's/refs\/tags\///')
+ echo "${{ secrets.PUBLISH_TOKEN }}" | docker login gitpot.dev -u sangelo --password-stdin
+ docker push "gitpot.dev/sangelo/website:${TAG}"
+
+ # publish tag latest as well
+ if echo "${{ github.ref }}" | grep -q "refs/tags/"; then
+ docker tag "gitpot.dev/sangelo/website:${TAG}" "gitpot.dev/sangelo/website:latest"
+ docker push "gitpot.dev/sangelo/website:latest"
+ fi
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..68fe4b9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,35 @@
+# Builder stage
+FROM node:20-alpine AS builder
+
+# Set the working directory in the container
+WORKDIR /app
+
+# Copy the repository contents into the container
+COPY . .
+
+# Install dependencies and build the site. Output directory will be /app/build
+RUN yarn install && yarn run build
+
+# Final stage
+FROM caddy:2-alpine
+
+# Set the working directory in the container
+WORKDIR /web
+
+# Copy the build directory from the builder stage to /web
+COPY --from=builder /app/build /web
+
+# Caddyfile configuration to serve files from /web
+RUN echo -e ":80 {\n root * /web\n file_server\n}" > /etc/caddy/Caddyfile
+
+# Expose port 80
+EXPOSE 80
+
+# Start Caddy with the specified Caddyfile
+CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
+
+# Description for docker container
+LABEL description="Sangelo's Space website, packaged as a docker container, with the Caddy webserver."
+
+# Remove intermediate images after build
+ONBUILD RUN rm -rf /app
diff --git a/Justfile b/Justfile
new file mode 100644
index 0000000..6266967
--- /dev/null
+++ b/Justfile
@@ -0,0 +1,84 @@
+# settings
+set dotenv-load
+
+# defaults
+default_runner := 'docker'
+default_tag := 'latest'
+default_image := 'gitpot.dev/sangelo/website:latest'
+
+# run development server by default
+default: dev
+
+# aliases
+alias c := clean
+alias b := build
+alias r := run
+alias d := dev
+alias p := preview
+
+# building
+
+# run all docker related recipes (clean, build, run) (default runner: docker)
+all tag=default_tag runner=default_runner:
+ just -f {{justfile()}} clean {{runner}} {{tag}}
+ just -f {{justfile()}} build {{tag}} {{runner}}
+ just -f {{justfile()}} run {{tag}} {{runner}}
+
+# clean containers, images and temporary svelte dirs with specified runner (default runner: docker)
+clean runner=default_runner tag=default_tag:
+ @echo "Cleaning dev environment..."
+ rm -rf build/ .svelte-kit/
+ @echo "Cleaning containers with '{{runner}}'..."
+ TAG="{{tag}}" {{runner}} compose -f docker-compose.build.yml down
+ @echo "Cleaning images with '{{runner}}'..."
+ just -f {{justfile()}} _clean_images {{runner}}
+
+# clean images function
+_clean_images runner=default_runner:
+ #!/usr/bin/env bash
+ set -o pipefail
+ image_ids=$({{runner}} image ls | grep gitpot.dev/sangelo/website | awk '{print $3}')
+ if [ -n "$image_ids" ]; then
+ for image_id in $image_ids; do
+ {{runner}} image rm $image_id
+ echo "Image with ID $image_id deleted successfully."
+ done
+ else
+ echo "No images matching the repository and tag found."
+ fi
+
+# build container image with specified runner (default runner: docker)
+build tag=default_tag runner=default_runner:
+ @echo "Running with '{{runner}}' and tagging as '{{tag}}'..."
+ TAG="{{tag}}" {{runner}} compose -f docker-compose.build.yml build --no-cache
+
+# run container image with specified runner (default runner: docker)
+run tag=default_tag runner=default_runner:
+ @echo "Running with '{{runner}}'..."
+ TAG={{tag}} {{runner}} compose -f docker-compose.build.yml up -d --force-recreate
+ @# watch -n 1 {{runner}} compose -f docker-compose.build.yml ps
+
+publish image=default_image runner=default_runner:
+ @echo "Publishing with '{{runner}}'..."
+ @# log into gitpot
+ echo "$GITPOT_TOKEN" | {{runner}} login gitpot.dev -u $GITPOT_USERNAME --password-stdin
+ @# push the specified image to the container registry
+ {{runner}} push {{image}}
+ @echo "Published {{image}} successfuly! Use '{{runner}} pull {{image}}' to pull the container."
+
+# development
+
+# install dependencies
+_install:
+ yarn install
+
+# run vite dev server
+dev: _install
+ @echo "Running vite development server..."
+ yarn run dev --open
+
+# run vite preview server
+preview: _install
+ @echo "Running vite preview server..."
+ yarn run build
+ yarn run preview --open
diff --git a/README.md b/README.md
index 47e4f2c..d466751 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,20 @@ Feel free to explore!
You're welcome to contribute to this website if you have a Lunivity account (see homepage for details if registrations aren't open).
Once you fork and clone the repository, follow the next steps.
+If you have `just` installed, setting up is pretty easy:
+
+```bash
+# setup dependencies and run dev server
+just
+
+# you can also run `just dev`
+just dev
+```
+
+View a list of all possible `just` recipes with `just -l`.
+
+Otherwise, if you don't already have or don't want to install `just`, you can run the commands manually:
+
```bash
# install dependencies
yarn install
@@ -23,14 +37,44 @@ Once you've made your changes, you can create a Pull Request and I'll make sure
## Building
-To create a production version of this website:
+### Build locally
+
+To create a production version of this website without docker:
```bash
+# automatically build & preview
+just preview
+
+# or, manually build
yarn run build
```
-You can preview the production build with `yarn run preview`.
+You can then preview the production build locally with `yarn run preview --open`.
+
+The files will be available in the repo, in the `build/` directory.
+
+### Build with Docker
+
+To build a docker container image with `just`:
+
+```bash
+# build and run container image with docker, tag: latest
+just build
+
+# build with podman
+just build podman
+
+# clean, build, and run container image with docker, tag: latest
+just all
+# clean, build, and run container image with podman, tag: dev
+just all dev podman
+```
+
+You can preview the produced docker build with `just run [tag] [runner]`.
## License
-You can view this project's license [here](./LICENSE).
+You can view this project's source code license [here](./LICENSE).
+
+All assets (images, logos, etc.) created by me (Sangelo) are Copyright (c) 2019-2024 Sangelo unless otherwise stated.
+Brand logos and icons (such as Discord, Github, YouTube, etc.) are trademarked by and copyright of their respective owners.
diff --git a/build-podman.sh b/build-podman.sh
new file mode 100755
index 0000000..2807101
--- /dev/null
+++ b/build-podman.sh
@@ -0,0 +1,5 @@
+podman compose -f docker-compose.build.yml down && \
+#podman compose -f docker-compose.build.yml rm --all && \
+podman compose -f docker-compose.build.yml build --no-cache && \
+podman compose -f docker-compose.build.yml up -d --force-recreate && \
+watch -n 1 podman compose -f docker-compose.build.yml ps
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..30bb601
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,5 @@
+sudo docker compose -f docker-compose.build.yml down && \
+#sudo docker compose -f docker-compose.build.yml rm --all && \
+sudo docker compose -f docker-compose.build.yml build --no-cache && \
+sudo docker compose -f docker-compose.build.yml up -d --force-recreate && \
+watch -n 1 sudo docker compose -f docker-compose.build.yml ps
diff --git a/docker-compose.build.yml b/docker-compose.build.yml
new file mode 100644
index 0000000..60767d7
--- /dev/null
+++ b/docker-compose.build.yml
@@ -0,0 +1,9 @@
+services:
+ web:
+ image: gitpot.dev/sangelo/website:${TAG}
+ build:
+ context: .
+ dockerfile: Dockerfile
+ no_cache: true
+ ports:
+ - "3000:80"
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..e69de29