mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-06-15 13:32:43 +01:00
Compare commits
4 Commits
feature/pu
...
feature/do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
595132ec2c | ||
|
|
0659cb3993 | ||
|
|
e8fc6fd77e | ||
|
|
a04185921d |
258
.github/workflows/image-smoke-test.yml
vendored
Normal file
258
.github/workflows/image-smoke-test.yml
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
name: Image Smoke Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docker/prod/**'
|
||||
- '.github/workflows/image-smoke-test.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
smoke:
|
||||
name: Smoke (${{ matrix.mode }})
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
mode:
|
||||
- default
|
||||
- puid-pgid
|
||||
- openshift
|
||||
- drop-never
|
||||
- diagnostic
|
||||
- puid-mismatch-warning
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Copy .env template
|
||||
run: |
|
||||
cp .env.production .env
|
||||
rm .env.production .env.ci .env.example
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
extensions: mbstring, dom, fileinfo, pgsql
|
||||
|
||||
- name: Composer install
|
||||
run: composer install --no-dev --no-ansi --no-interaction --prefer-dist --ignore-platform-reqs --classmap-authoritative
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: NPM ci
|
||||
run: npm ci
|
||||
|
||||
- name: NPM build
|
||||
run: npm run build
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build smoke image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/prod/Dockerfile
|
||||
build-args: |
|
||||
DOCKER_FILES_BASE_PATH=docker/prod/
|
||||
load: true
|
||||
tags: solidtime-smoke:test
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: "Smoke: default (image config + fresh deploy with empty bind mounts)"
|
||||
if: matrix.mode == 'default'
|
||||
run: |
|
||||
echo "[smoke] image's default USER is root (entrypoint needs root to drop privs)"
|
||||
user=$(docker inspect --format '{{.Config.User}}' solidtime-smoke:test)
|
||||
if [ "$user" != "root" ]; then
|
||||
echo "Expected 'root', got '$user'. The Dockerfile must end with USER root so the entrypoint can chown/usermod and drop privileges."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[smoke] storage tree is group-0 owned (OpenShift / arbitrary-UID compat)"
|
||||
group=$(docker run --rm --entrypoint stat solidtime-smoke:test -c '%g' /var/www/html/storage)
|
||||
if [ "$group" != "0" ]; then
|
||||
echo "Expected group 0, got '$group'. The Dockerfile must chgrp -R 0 storage bootstrap/cache so arbitrary-UID containers can write."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p test-storage test-cache
|
||||
docker run --rm \
|
||||
-v "$(pwd)/test-storage:/var/www/html/storage" \
|
||||
-v "$(pwd)/test-cache:/var/www/html/bootstrap/cache" \
|
||||
solidtime-smoke:test \
|
||||
sh -c '
|
||||
set -e
|
||||
echo "[smoke] framework subdirs exist"
|
||||
test -d /var/www/html/storage/framework/cache/data
|
||||
test -d /var/www/html/storage/framework/sessions
|
||||
test -d /var/www/html/storage/framework/views
|
||||
test -d /var/www/html/storage/framework/testing
|
||||
test -d /var/www/html/storage/logs
|
||||
test -d /var/www/html/storage/app/public
|
||||
test -d /var/www/html/storage/app/private
|
||||
test -d /var/www/html/bootstrap/cache
|
||||
echo "[smoke] storage is writable"
|
||||
touch /var/www/html/storage/framework/cache/data/test-file
|
||||
echo "[smoke] running as octane (UID 1000)"
|
||||
[ "$(id -u)" = "1000" ]
|
||||
echo "[smoke] PASS"
|
||||
'
|
||||
|
||||
- name: "Smoke: PUID/PGID remap"
|
||||
if: matrix.mode == 'puid-pgid'
|
||||
run: |
|
||||
mkdir -p test-storage test-cache
|
||||
sudo chown -R 1501:1501 test-storage test-cache
|
||||
docker run --rm \
|
||||
-e PUID=1501 -e PGID=1501 \
|
||||
-v "$(pwd)/test-storage:/var/www/html/storage" \
|
||||
-v "$(pwd)/test-cache:/var/www/html/bootstrap/cache" \
|
||||
solidtime-smoke:test \
|
||||
sh -c '
|
||||
set -e
|
||||
echo "[smoke] running as remapped UID/GID 1501"
|
||||
[ "$(id -u)" = "1501" ]
|
||||
[ "$(id -g)" = "1501" ]
|
||||
echo "[smoke] storage is writable as 1501"
|
||||
touch /var/www/html/storage/framework/cache/data/test-file
|
||||
echo "[smoke] PASS"
|
||||
'
|
||||
|
||||
- name: "Smoke: OpenShift / arbitrary UID + group 0"
|
||||
if: matrix.mode == 'openshift'
|
||||
run: |
|
||||
mkdir -p test-storage test-cache
|
||||
sudo chown -R 2000:0 test-storage test-cache
|
||||
sudo chmod -R g+rwX test-storage test-cache
|
||||
docker run --rm --user 2000:0 \
|
||||
-v "$(pwd)/test-storage:/var/www/html/storage" \
|
||||
-v "$(pwd)/test-cache:/var/www/html/bootstrap/cache" \
|
||||
solidtime-smoke:test \
|
||||
sh -c '
|
||||
set -e
|
||||
echo "[smoke] running as arbitrary UID 2000, group 0"
|
||||
[ "$(id -u)" = "2000" ]
|
||||
[ "$(id -g)" = "0" ]
|
||||
echo "[smoke] storage is writable via group 0"
|
||||
touch /var/www/html/storage/framework/cache/data/test-file
|
||||
echo "[smoke] PASS"
|
||||
'
|
||||
|
||||
- name: "Smoke: SOLIDTIME_DROP_PRIVILEGES=never (run as root)"
|
||||
if: matrix.mode == 'drop-never'
|
||||
run: |
|
||||
mkdir -p test-storage test-cache
|
||||
docker run --rm \
|
||||
-e SOLIDTIME_DROP_PRIVILEGES=never \
|
||||
-v "$(pwd)/test-storage:/var/www/html/storage" \
|
||||
-v "$(pwd)/test-cache:/var/www/html/bootstrap/cache" \
|
||||
solidtime-smoke:test \
|
||||
sh -c '
|
||||
set -e
|
||||
echo "[smoke] running as root (privilege drop disabled)"
|
||||
[ "$(id -u)" = "0" ]
|
||||
echo "[smoke] bootstrap still ran"
|
||||
test -d /var/www/html/storage/framework/cache/data
|
||||
echo "[smoke] storage writable as root"
|
||||
touch /var/www/html/storage/framework/cache/data/test-file
|
||||
echo "[smoke] PASS"
|
||||
'
|
||||
|
||||
- name: "Smoke: PUID set + started non-root prints a warning but continues"
|
||||
if: matrix.mode == 'puid-mismatch-warning'
|
||||
run: |
|
||||
mkdir -p test-storage test-cache
|
||||
sudo chown -R 1500:1500 test-storage test-cache
|
||||
|
||||
set +e
|
||||
docker run --rm \
|
||||
--user 1500:1500 \
|
||||
-e PUID=1500 -e PGID=1500 \
|
||||
-v "$(pwd)/test-storage:/var/www/html/storage" \
|
||||
-v "$(pwd)/test-cache:/var/www/html/bootstrap/cache" \
|
||||
solidtime-smoke:test \
|
||||
sh -c '
|
||||
set -e
|
||||
echo "[smoke] running as 1500 (user: directive wins)"
|
||||
[ "$(id -u)" = "1500" ]
|
||||
echo "[smoke] storage is writable as 1500"
|
||||
touch /var/www/html/storage/framework/cache/data/test-file
|
||||
echo "[smoke] container completed successfully"
|
||||
' \
|
||||
>stdout.log 2>stderr.log
|
||||
exit_code=$?
|
||||
set -e
|
||||
|
||||
echo "[smoke] exit code: $exit_code"
|
||||
echo "--- stderr ---"
|
||||
cat stderr.log
|
||||
echo "--- end stderr ---"
|
||||
|
||||
if [ "$exit_code" -ne 0 ]; then
|
||||
echo "Expected the entrypoint to continue (warning is non-fatal)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for needle in "PUID/PGID is set but the container started as UID" "remove any 'user:' directive" "Continuing as UID"; do
|
||||
if ! grep -q "$needle" stderr.log; then
|
||||
echo "Missing warning fragment: $needle"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "[smoke] PASS"
|
||||
|
||||
- name: "Smoke: diagnostic error path (read-only storage mount)"
|
||||
if: matrix.mode == 'diagnostic'
|
||||
run: |
|
||||
# Pre-create the full storage tree on the host so the entrypoint's
|
||||
# bootstrap_storage_tree() is a no-op (mkdir -p on existing dirs
|
||||
# returns 0 even on a read-only mount). The write test then fires
|
||||
# against the RO mount and triggers our diagnostic.
|
||||
mkdir -p test-storage/framework/cache/data \
|
||||
test-storage/framework/sessions \
|
||||
test-storage/framework/views \
|
||||
test-storage/framework/testing \
|
||||
test-storage/logs \
|
||||
test-storage/app/public \
|
||||
test-storage/app/private \
|
||||
test-cache
|
||||
|
||||
set +e
|
||||
docker run --rm \
|
||||
-v "$(pwd)/test-storage:/var/www/html/storage:ro" \
|
||||
-v "$(pwd)/test-cache:/var/www/html/bootstrap/cache:ro" \
|
||||
solidtime-smoke:test \
|
||||
true \
|
||||
>stdout.log 2>stderr.log
|
||||
exit_code=$?
|
||||
set -e
|
||||
|
||||
echo "[smoke] exit code: $exit_code"
|
||||
echo "--- stderr ---"
|
||||
cat stderr.log
|
||||
echo "--- end stderr ---"
|
||||
|
||||
if [ "$exit_code" -eq 0 ]; then
|
||||
echo "Expected the entrypoint to exit non-zero on an unwritable storage mount."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for needle in "not writable" "PUID=" "permissions"; do
|
||||
if ! grep -q "$needle" stderr.log; then
|
||||
echo "Missing diagnostic fragment: $needle"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "[smoke] PASS"
|
||||
@@ -68,6 +68,7 @@ RUN apt-get update; \
|
||||
wget \
|
||||
vim \
|
||||
git \
|
||||
gosu \
|
||||
ncdu \
|
||||
procps \
|
||||
unzip \
|
||||
@@ -193,9 +194,20 @@ COPY --link --chown=${WWWUSER}:${WWWUSER} . .
|
||||
#COPY --link --chown=${WWWUSER}:${WWWUSER} --from=build ${ROOT}/public public
|
||||
|
||||
RUN mkdir -p \
|
||||
storage/framework/{sessions,views,cache,testing} \
|
||||
storage/framework/{sessions,views,cache/data,testing} \
|
||||
storage/logs \
|
||||
bootstrap/cache && chmod -R a+rw storage
|
||||
storage/app/public \
|
||||
storage/app/private \
|
||||
bootstrap/cache && \
|
||||
ln -s ../storage/app/public public/storage && \
|
||||
chmod -R a+rw storage bootstrap/cache
|
||||
|
||||
# OpenShift / arbitrary-UID compatibility: group 0 (root group) gets read+write+execute
|
||||
# on writable paths. Any UID can run the container if it joins the root group.
|
||||
# https://docs.openshift.com/container-platform/latest/openshift_images/create-images.html
|
||||
USER root
|
||||
RUN chgrp -R 0 storage bootstrap/cache && \
|
||||
chmod -R g+rwX storage bootstrap/cache
|
||||
|
||||
#RUN composer install \
|
||||
# --classmap-authoritative \
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[program:octane]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = php %(ENV_ROOT)s/artisan octane:frankenphp --host=0.0.0.0 --port=8000 --admin-port=2019 --caddyfile=%(ENV_ROOT)s/docker/prod/deployment/octane/FrankenPHP/Caddyfile
|
||||
user = %(ENV_USER)s
|
||||
priority = 1
|
||||
autostart = true
|
||||
autorestart = true
|
||||
@@ -14,7 +13,6 @@ stderr_logfile_maxbytes = 0
|
||||
[program:horizon]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = php %(ENV_ROOT)s/artisan horizon
|
||||
user = %(ENV_USER)s
|
||||
priority = 3
|
||||
autostart = %(ENV_WITH_HORIZON)s
|
||||
autorestart = true
|
||||
@@ -27,7 +25,6 @@ stopwaitsecs = 3600
|
||||
[program:scheduler]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = supercronic -overlapping /etc/supercronic/laravel
|
||||
user = %(ENV_USER)s
|
||||
autostart = %(ENV_WITH_SCHEDULER)s
|
||||
autorestart = true
|
||||
stdout_logfile = %(ENV_ROOT)s/storage/logs/scheduler.log
|
||||
@@ -38,7 +35,6 @@ stderr_logfile_maxbytes = 200MB
|
||||
[program:clear-scheduler-cache]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = php %(ENV_ROOT)s/artisan schedule:clear-cache
|
||||
user = %(ENV_USER)s
|
||||
autostart = %(ENV_WITH_SCHEDULER)s
|
||||
autorestart = false
|
||||
startsecs = 0
|
||||
@@ -51,7 +47,6 @@ stderr_logfile_maxbytes = 200MB
|
||||
[program:reverb]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = php %(ENV_ROOT)s/artisan reverb:start
|
||||
user = %(ENV_USER)s
|
||||
priority = 2
|
||||
autostart = %(ENV_WITH_REVERB)s
|
||||
autorestart = true
|
||||
|
||||
@@ -1,6 +1,240 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# ============================================================================
|
||||
# Solidtime container entrypoint.
|
||||
#
|
||||
# Layout:
|
||||
# 1. Storage tree bootstrap (idempotent, runs as any user)
|
||||
# 2. UID/GID remap + chown (root only, controlled by PUID/PGID env vars)
|
||||
# 3. Pre-flight write test (fails fast with a diagnosis message)
|
||||
# 4. Privilege drop via gosu, then re-exec self as APP_USER
|
||||
# 5. Original CONTAINER_MODE routing (runs as APP_USER)
|
||||
#
|
||||
# Env vars:
|
||||
# PUID, PGID UID/GID for the application user. Defaults 1000:1000.
|
||||
# Only takes effect when the container starts as root
|
||||
# (which is the image's default — if you set a
|
||||
# `user:` directive in compose, PUID/PGID are ignored
|
||||
# and a startup warning is printed).
|
||||
# SOLIDTIME_DROP_PRIVILEGES auto (default) | never
|
||||
# auto: if started as root, drop privileges to APP_USER; otherwise just exec.
|
||||
# never: never drop privileges. Run as whatever UID/GID was started.
|
||||
# ============================================================================
|
||||
|
||||
APP_USER="octane"
|
||||
APP_PATH="${ROOT:-/var/www/html}"
|
||||
STORAGE_PATH="${APP_PATH}/storage"
|
||||
CACHE_PATH="${APP_PATH}/bootstrap/cache"
|
||||
DEFAULT_UID=1000
|
||||
DEFAULT_GID=1000
|
||||
TARGET_UID="${PUID:-${DEFAULT_UID}}"
|
||||
TARGET_GID="${PGID:-${DEFAULT_GID}}"
|
||||
DROP_PRIVS="${SOLIDTIME_DROP_PRIVILEGES:-auto}"
|
||||
WRITABLE_PATHS=(
|
||||
"${STORAGE_PATH}/framework/cache/data"
|
||||
"${STORAGE_PATH}/framework/sessions"
|
||||
"${STORAGE_PATH}/framework/views"
|
||||
"${STORAGE_PATH}/framework/testing"
|
||||
"${STORAGE_PATH}/logs"
|
||||
"${STORAGE_PATH}/app/public"
|
||||
"${STORAGE_PATH}/app/private"
|
||||
"${CACHE_PATH}"
|
||||
)
|
||||
|
||||
case "${DROP_PRIVS}" in
|
||||
never) SHOULD_DROP=0 ;;
|
||||
auto)
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
SHOULD_DROP=1
|
||||
else
|
||||
SHOULD_DROP=0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "[entrypoint] ERROR: invalid SOLIDTIME_DROP_PRIVILEGES='${DROP_PRIVS}'" >&2
|
||||
echo "[entrypoint] Valid values: auto (default), never" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Warn if PUID/PGID are set but the container started non-root. PUID/PGID only
|
||||
# take effect during the drop-privileges flow, which requires starting as root.
|
||||
# A common cause is leaving `user:` in the compose file alongside PUID env vars.
|
||||
if { [ -n "${PUID}" ] || [ -n "${PGID}" ]; } \
|
||||
&& [ "$(id -u)" != "0" ] \
|
||||
&& [ "${SOLIDTIME_PRIVILEGES_DROPPED:-0}" != "1" ]; then
|
||||
cat >&2 <<EOF
|
||||
[entrypoint] WARNING: PUID/PGID is set but the container started as UID $(id -u) (not root).
|
||||
[entrypoint] WARNING: PUID/PGID only apply when the entrypoint runs as root and drops privileges.
|
||||
[entrypoint] WARNING:
|
||||
[entrypoint] WARNING: To use PUID/PGID: remove any 'user:' directive from your compose file.
|
||||
[entrypoint] WARNING: To run as a fixed UID: remove PUID/PGID from your env.
|
||||
[entrypoint] WARNING:
|
||||
[entrypoint] WARNING: Continuing as UID $(id -u). See:
|
||||
[entrypoint] WARNING: https://docs.solidtime.io/self-hosting/guides/permissions
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
bootstrap_storage_tree() {
|
||||
mkdir -p "${WRITABLE_PATHS[@]}" 2>/dev/null || return 1
|
||||
}
|
||||
|
||||
# Proactive warning when the existing storage directory is owned by a non-default
|
||||
# UID (typical on NAS systems where host users aren't UID 1000) and PUID/PGID
|
||||
# aren't set. Without this nudge, the chown step silently re-owns the files to
|
||||
# 1000:1000 and the user only discovers the mismatch later when host-side tools
|
||||
# (backup, file browser, rsync) show unfamiliar ownership.
|
||||
maybe_warn_ownership_mismatch() {
|
||||
[ "${SHOULD_DROP}" = "1" ] || return 0
|
||||
[ -n "${PUID}" ] && return 0
|
||||
[ -n "${PGID}" ] && return 0
|
||||
[ -d "${STORAGE_PATH}" ] || return 0
|
||||
|
||||
local owner_uid owner_gid
|
||||
owner_uid="$(stat -c '%u' "${STORAGE_PATH}" 2>/dev/null)" || return 0
|
||||
owner_gid="$(stat -c '%g' "${STORAGE_PATH}" 2>/dev/null)" || return 0
|
||||
|
||||
# Root-owned: probably freshly created by the entrypoint, will be chowned shortly.
|
||||
[ "${owner_uid}" = "0" ] && return 0
|
||||
# Already the target: nothing to warn about.
|
||||
[ "${owner_uid}" = "${TARGET_UID}" ] && return 0
|
||||
|
||||
cat >&2 <<EOF
|
||||
[entrypoint] NOTE: ${STORAGE_PATH} is owned by UID ${owner_uid}:${owner_gid},
|
||||
[entrypoint] but the container is starting as UID ${TARGET_UID}:${TARGET_GID}.
|
||||
[entrypoint] Files will be chowned to ${TARGET_UID}:${TARGET_GID} and may
|
||||
[entrypoint] appear with an unfamiliar owner on the host.
|
||||
[entrypoint]
|
||||
[entrypoint] If you want the container to write as UID ${owner_uid} (common
|
||||
[entrypoint] on Synology / TrueNAS / Unraid where host users aren't UID
|
||||
[entrypoint] 1000), set in your env and restart:
|
||||
[entrypoint]
|
||||
[entrypoint] PUID=${owner_uid}
|
||||
[entrypoint] PGID=${owner_gid}
|
||||
[entrypoint]
|
||||
[entrypoint] More: https://docs.solidtime.io/self-hosting/guides/permissions
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
print_write_test_failure() {
|
||||
local owner
|
||||
owner="$(stat -c '%u:%g' "${STORAGE_PATH}" 2>/dev/null || echo unknown)"
|
||||
local runtime_uid
|
||||
local runtime_gid
|
||||
if [ "$(id -u)" = "0" ] && [ "${SHOULD_DROP}" = "1" ]; then
|
||||
runtime_uid="${TARGET_UID}"
|
||||
runtime_gid="${TARGET_GID}"
|
||||
else
|
||||
runtime_uid="$(id -u)"
|
||||
runtime_gid="$(id -g)"
|
||||
fi
|
||||
local owner_uid
|
||||
owner_uid="$(stat -c '%u' "${STORAGE_PATH}" 2>/dev/null || echo 1000)"
|
||||
local owner_gid
|
||||
owner_gid="$(stat -c '%g' "${STORAGE_PATH}" 2>/dev/null || echo 1000)"
|
||||
cat >&2 <<EOF
|
||||
|
||||
============================================================
|
||||
ERROR: Solidtime writable directories are not writable.
|
||||
|
||||
Diagnosis:
|
||||
Container will run as: UID ${runtime_uid}, GID ${runtime_gid}
|
||||
Storage directory owner: ${owner}
|
||||
|
||||
Likely cause: a bind-mounted host directory is owned by a different
|
||||
user than the container's application user.
|
||||
|
||||
Fix on the host:
|
||||
|
||||
sudo chown -R ${runtime_uid}:${runtime_gid} <your-bind-mount-path>
|
||||
|
||||
Or set PUID/PGID to match the host directory owner:
|
||||
|
||||
PUID=${owner_uid}
|
||||
PGID=${owner_gid}
|
||||
|
||||
To run intentionally as root, set:
|
||||
|
||||
SOLIDTIME_DROP_PRIVILEGES=never
|
||||
|
||||
For more help: https://docs.solidtime.io/self-hosting/guides/permissions
|
||||
============================================================
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
write_test_as_user() {
|
||||
local user="$1"
|
||||
local script='
|
||||
set -e
|
||||
for dir in "$@"; do
|
||||
test_file="${dir}/.solidtime-write-test"
|
||||
touch "${test_file}"
|
||||
rm -f "${test_file}"
|
||||
done
|
||||
'
|
||||
if [ -n "${user}" ]; then
|
||||
gosu "${user}" sh -c "${script}" sh "${WRITABLE_PATHS[@]}" 2>/dev/null
|
||||
else
|
||||
sh -c "${script}" sh "${WRITABLE_PATHS[@]}" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Root preamble: bootstrap, remap, chown, write-test, then drop and re-exec.
|
||||
# ----------------------------------------------------------------------------
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
if ! bootstrap_storage_tree; then
|
||||
echo "[entrypoint] ERROR: failed to create storage subdirectories at ${STORAGE_PATH}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${SHOULD_DROP}" = "1" ]; then
|
||||
maybe_warn_ownership_mismatch
|
||||
if [ "${TARGET_UID}" != "${DEFAULT_UID}" ] || [ "${TARGET_GID}" != "${DEFAULT_GID}" ]; then
|
||||
echo "[entrypoint] Remapping ${APP_USER} to ${TARGET_UID}:${TARGET_GID}"
|
||||
groupmod -o -g "${TARGET_GID}" "${APP_USER}"
|
||||
usermod -o -u "${TARGET_UID}" "${APP_USER}"
|
||||
fi
|
||||
# Idempotent chown: only fix entries whose owner or group is wrong.
|
||||
# On large storage volumes (lots of user uploads) this is dramatically
|
||||
# faster than a blanket `chown -R` every restart. Pattern borrowed from
|
||||
# docker-library/postgres and linuxserver.io's baseimage.
|
||||
find "${STORAGE_PATH}" "${CACHE_PATH}" \
|
||||
\( ! -user "${TARGET_UID}" -o ! -group "${TARGET_GID}" \) \
|
||||
-exec chown "${TARGET_UID}:${TARGET_GID}" {} + 2>/dev/null || true
|
||||
|
||||
if ! write_test_as_user "${APP_USER}"; then
|
||||
print_write_test_failure
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec gosu "${APP_USER}" env SOLIDTIME_PRIVILEGES_DROPPED=1 "$0" "$@"
|
||||
fi
|
||||
|
||||
if ! write_test_as_user ""; then
|
||||
print_write_test_failure
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if ! bootstrap_storage_tree; then
|
||||
echo "[entrypoint] WARNING: could not create some storage subdirectories at ${STORAGE_PATH} (will continue if existing tree is writable)" >&2
|
||||
fi
|
||||
if ! write_test_as_user ""; then
|
||||
print_write_test_failure
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Application: runs as APP_USER (or whatever non-root UID was started).
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
unset SOLIDTIME_PRIVILEGES_DROPPED
|
||||
|
||||
container_mode=${CONTAINER_MODE:-"http"}
|
||||
octane_server=${OCTANE_SERVER}
|
||||
auto_db_migrate=${AUTO_DB_MIGRATE:-false}
|
||||
@@ -8,14 +242,16 @@ auto_db_migrate=${AUTO_DB_MIGRATE:-false}
|
||||
initialStuff() {
|
||||
echo "Container mode: $container_mode"
|
||||
|
||||
if [ ${auto_db_migrate} = "true" ]; then
|
||||
if [ "${auto_db_migrate}" = "true" ]; then
|
||||
echo "Auto database migration enabled."
|
||||
php artisan migrate --isolated --force
|
||||
fi
|
||||
|
||||
php artisan storage:link; \
|
||||
php artisan optimize:clear; \
|
||||
php artisan optimize;
|
||||
if [ ! -L "${APP_PATH}/public/storage" ]; then
|
||||
php artisan storage:link
|
||||
fi
|
||||
php artisan optimize:clear
|
||||
php artisan optimize
|
||||
}
|
||||
|
||||
if [ "$1" != "" ]; then
|
||||
@@ -23,11 +259,11 @@ if [ "$1" != "" ]; then
|
||||
elif [ "${container_mode}" = "http" ]; then
|
||||
initialStuff
|
||||
echo "Octane Server: $octane_server"
|
||||
if [ "${octane_server}" = "frankenphp" ]; then
|
||||
if [ "${octane_server}" = "frankenphp" ]; then
|
||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.frankenphp.conf
|
||||
elif [ "${octane_server}" = "swoole" ]; then
|
||||
elif [ "${octane_server}" = "swoole" ]; then
|
||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.swoole.conf
|
||||
elif [ "${octane_server}" = "roadrunner" ]; then
|
||||
elif [ "${octane_server}" = "roadrunner" ]; then
|
||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.roadrunner.conf
|
||||
else
|
||||
echo "Invalid Octane server supplied."
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
[supervisord]
|
||||
nodaemon = true
|
||||
user = %(ENV_USER)s
|
||||
logfile = /var/log/supervisor/supervisord.log
|
||||
pidfile = /var/run/supervisord.pid
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[program:horizon]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = php %(ENV_ROOT)s/artisan horizon
|
||||
user = %(ENV_USER)s
|
||||
autostart = true
|
||||
autorestart = true
|
||||
stdout_logfile = /dev/stdout
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[program:reverb]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = php %(ENV_ROOT)s/artisan reverb:start
|
||||
user = %(ENV_USER)s
|
||||
autostart = true
|
||||
autorestart = true
|
||||
stdout_logfile = /dev/stdout
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[program:scheduler]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = supercronic -overlapping /etc/supercronic/laravel
|
||||
user = %(ENV_USER)s
|
||||
autostart = true
|
||||
autorestart = true
|
||||
stdout_logfile = /dev/stdout
|
||||
@@ -12,7 +11,6 @@ stderr_logfile_maxbytes = 0
|
||||
[program:clear-scheduler-cache]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = php %(ENV_ROOT)s/artisan schedule:clear-cache
|
||||
user = %(ENV_USER)s
|
||||
autostart = true
|
||||
autorestart = false
|
||||
startsecs = 0
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[program:worker]
|
||||
process_name = %(program_name)s_%(process_num)s
|
||||
command = %(ENV_WORKER_COMMAND)s
|
||||
user = %(ENV_USER)s
|
||||
autostart = true
|
||||
autorestart = true
|
||||
stdout_logfile = /dev/stdout
|
||||
|
||||
Reference in New Issue
Block a user