Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
9 changes: 9 additions & 0 deletions .docker/Dockerfile.nginx-port-selector
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM docker:27-cli

RUN apk add --no-cache docker-cli-compose

COPY scripts/nginx-port-selector.sh /usr/local/bin/nginx-port-selector.sh

RUN chmod +x /usr/local/bin/nginx-port-selector.sh

ENTRYPOINT ["/usr/local/bin/nginx-port-selector.sh"]
2 changes: 1 addition & 1 deletion .docker/scripts/nextcloud-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,5 @@ exec busybox crond -f -l 0 -L /dev/stdout > /dev/null 2>&1 &
runuser -u www-data -- php -f /var/www/html/cron.php

# Start PHP-FPM
echo "πŸ’™ Nextcloud is up!"
echo "Starting PHP-FPM..."
exec "$@"
125 changes: 125 additions & 0 deletions .docker/scripts/nginx-port-selector.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/bin/sh

set -eu

compose_project() {
docker inspect --format '{{ index .Config.Labels "com.docker.compose.project" }}' "$(hostname)"
}

project="$(compose_project)"
if [ -z "$project" ]; then
echo 'Could not determine the Compose project from the selector container.' >&2
exit 1
fi

if [ -z "${PROJECT_DIR:-}" ]; then
echo 'The host project directory was not provided to the selector.' >&2
exit 1
fi

compose() {
docker compose \
--project-name "$project" \
--project-directory "$PROJECT_DIR" \
--file "$PROJECT_DIR/docker-compose.yml" \
"$@"
}

echo "Validating Compose project ${project} at ${PROJECT_DIR}."
compose config --quiet

cleanup_nginx() {
compose rm --force --stop nginx || true
}

trap cleanup_nginx INT TERM

start_nginx() {
HTTP_PORT="$1" HTTPS_PORT="$2" IP_BIND="${IP_BIND:-127.0.0.1}" \
compose up --detach --no-deps --scale nginx=1 nginx
}

published_port() {
mapping="$(compose port "$1" "$2" 2>/dev/null || true)"
[ -n "$mapping" ] || return 0
printf '%s\n' "${mapping##*:}"
}

report_environment_ready() {
selector_http_port="$1"
selector_https_port="$2"
mailpit_port="$(published_port mailpit 8025)"
eurooffice_port="$(published_port eurooffice 80)"
playwright_port="$(published_port playwright 9323)"
signal_port="$(published_port signal-gateway 8080)"

compose exec -T \
-e ENV_HTTP_PORT="$selector_http_port" \
-e ENV_HTTPS_PORT="$selector_https_port" \
-e ENV_MAILPIT_PORT="$mailpit_port" \
-e ENV_EUROOFFICE_PORT="$eurooffice_port" \
-e ENV_PLAYWRIGHT_PORT="$playwright_port" \
-e ENV_SIGNAL_PORT="$signal_port" \
-e ENV_ADMIN_USER="$NEXTCLOUD_ADMIN_USER" \
-e ENV_ADMIN_PASSWORD="$NEXTCLOUD_ADMIN_PASSWORD" \
-e ENV_NEXTCLOUD_BRANCH="$VERSION_NEXTCLOUD" \
nextcloud sh /var/www/scripts/report-environment-ready
}

exit_successfully() {
echo 'βœ… Port selection complete. Exiting normally.'
exit 0
}

if [ "${HTTP_PORT+x}" = x ] || [ "${HTTPS_PORT+x}" = x ]; then
echo "Explicit ports requested: HTTP ${HTTP_PORT:-80}, HTTPS ${HTTPS_PORT:-443}."
set +e
output="$(start_nginx "${HTTP_PORT:-80}" "${HTTPS_PORT:-443}" 2>&1)"
status=$?
set -e
printf '%s\n' "$output"
if [ "$status" -ne 0 ]; then
cleanup_nginx
exit "$status"
fi
echo 'nginx started with explicit ports.'
if ! report_environment_ready "${HTTP_PORT:-80}" "${HTTPS_PORT:-443}"; then
echo 'Could not print environment banner.' >&2
fi
exit_successfully
fi

offset=0
while [ "$offset" -le 19 ]; do
http_port=$((80 + offset))
https_port=$((443 + offset))
echo "Trying nginx ports HTTP ${http_port} / HTTPS ${https_port}."

set +e
output="$(start_nginx "$http_port" "$https_port" 2>&1)"
status=$?
set -e
printf '%s\n' "$output"

if [ "$status" -eq 0 ]; then
echo "nginx started with HTTP ${http_port} / HTTPS ${https_port}."
if ! report_environment_ready "$http_port" "$https_port"; then
echo 'Could not print environment banner.' >&2
fi
exit_successfully
fi

if ! printf '%s\n' "$output" | grep -Eiq \
'address already in use|port is already allocated|port is already in use|failed to bind host port|cannot bind .* port'; then
echo 'nginx failed for a reason unrelated to a port conflict; stopping.' >&2
cleanup_nginx
exit "$status"
fi

echo "Ports ${http_port}/${https_port} are unavailable; trying the next pair." >&2
cleanup_nginx
offset=$((offset + 1))
done

echo 'No available nginx port pair found in HTTP 80-99 / HTTPS 443-462.' >&2
exit 1
58 changes: 58 additions & 0 deletions .docker/scripts/report-environment-ready
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/sh

set -eu

wait_for_php_fpm() {
attempt=0
while [ "$attempt" -lt 100 ]; do
if php -r '
$socket = @fsockopen("127.0.0.1", 9000, $errno, $errstr, 0.1);
if ($socket === false) {
exit(1);
}
fclose($socket);
' >/dev/null 2>&1; then
return 0
fi
attempt=$((attempt + 1))
sleep 0.1
done
return 1
}

url() {
port="$1"
base="$2"
if [ "$port" = "80" ] || [ "$port" = "443" ]; then
printf '%s' "$base"
else
printf '%s:%s' "$base" "$port"
fi
}

emit_banner() {
{
printf '\n'
printf 'β”Œβ”€ πŸ’™ Environment ready ─────────────────────\n'
printf 'β”‚\n'
[ -z "${ENV_HTTP_PORT:-}" ] || printf 'β”‚ %-16s %s\n' 'Nextcloud HTTP' "$(url "$ENV_HTTP_PORT" http://localhost)"
[ -z "${ENV_HTTPS_PORT:-}" ] || printf 'β”‚ %-16s %s\n' 'Nextcloud HTTPS' "$(url "$ENV_HTTPS_PORT" https://localhost)"
[ -z "${ENV_MAILPIT_PORT:-}" ] || printf 'β”‚ %-16s %s\n' 'Mailpit' "http://localhost:$ENV_MAILPIT_PORT"
[ -z "${ENV_EUROOFFICE_PORT:-}" ] || printf 'β”‚ %-16s %s\n' 'EuroOffice' "http://localhost:$ENV_EUROOFFICE_PORT"
[ -z "${ENV_PLAYWRIGHT_PORT:-}" ] || printf 'β”‚ %-16s %s\n' 'Playwright' "http://localhost:$ENV_PLAYWRIGHT_PORT"
[ -z "${ENV_SIGNAL_PORT:-}" ] || printf 'β”‚ %-16s %s\n' 'Signal' "http://localhost:$ENV_SIGNAL_PORT"
printf 'β”‚\n'
[ -z "${ENV_NEXTCLOUD_BRANCH:-}" ] || printf 'β”‚ %-16s %s\n' 'Nextcloud branch' "$ENV_NEXTCLOUD_BRANCH"
[ -z "${ENV_ADMIN_USER:-}" ] || printf 'β”‚ %-16s %s\n' 'Admin user' "$ENV_ADMIN_USER"
[ -z "${ENV_ADMIN_PASSWORD:-}" ] || printf 'β”‚ %-16s %s\n' 'Admin password' "$ENV_ADMIN_PASSWORD"
printf 'β”‚\n'
printf '└────────────────────────────────────────────\n'
} > /proc/1/fd/2
}

if ! wait_for_php_fpm; then
echo 'Could not confirm PHP-FPM readiness; skipping environment banner.' >&2
exit 0
fi

emit_banner
24 changes: 22 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ services:
- host.docker.internal:host-gateway
nginx:
image: ghcr.io/librecodecoop/nextcloud-dev-nginx:latest
scale: 0
# build:
# context: .docker/
# dockerfile: Dockerfile.nginx
Expand All @@ -60,11 +61,30 @@ services:
- ./volumes/nginx/certs:/certs
ports:
- target: 80
published: "${HTTP_PORT:-80-99}"
published: "${HTTP_PORT:-80}"
host_ip: ${IP_BIND:-127.0.0.1}
- target: 443
published: "${HTTPS_PORT:-443-462}"
published: "${HTTPS_PORT:-443}"
host_ip: ${IP_BIND:-127.0.0.1}
nginx-port-selector:
build:
context: .docker/
dockerfile: Dockerfile.nginx-port-selector
volumes:
- ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock
- .:${PWD}:ro
working_dir: ${PWD}
environment:
- HTTP_PORT
- HTTPS_PORT
- IP_BIND
- PROJECT_DIR=${PWD}
- NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER:-admin}
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD:-admin}
- VERSION_NEXTCLOUD=${VERSION_NEXTCLOUD:-master}
restart: "no"
depends_on:
- nextcloud
mailpit:
image: axllent/mailpit
ports:
Expand Down