This commit is contained in:
Mateusz Krajewski 2026-08-11 16:44:02 -07:00 committed by GitHub
commit bc015f4c75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 236 additions and 83 deletions

View file

@ -1,3 +1,6 @@
# CodeIgniter environment: development | testing | production (developer UI / error display)
CLOUDLOG_ENV=development
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=cloudlog
MYSQL_USER=cloudlog
@ -6,4 +9,10 @@ MYSQL_HOST=db
MYSQL_PORT=3306
BASE_LOCATOR=IO91WM
WEBSITE_URL=http://localhost
DIRECTORY=/var/www/html
DIRECTORY=/var/www/html
# Docker images (GitHub Container Registry). Override for forks, e.g. ghcr.io/yourusername
# CLOUDLOG_IMAGE_REGISTRY=ghcr.io/magicbug
# CLOUDLOG_IMAGE_TAG=latest
#
# Compose: development uses docker-compose.yml (default); production uses docker-compose.prod.yml

View file

@ -48,7 +48,7 @@ jobs:
sudo chmod +x /usr/local/bin/docker-compose
- name: Build Docker services
run: docker-compose up -d
run: docker-compose up -d --build
- name: Wait for services to start
run: |

61
.github/workflows/docker-publish.yml vendored Normal file
View file

@ -0,0 +1,61 @@
name: Publish Docker images
on:
push:
tags:
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
build-and-push:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- dockerfile: Dockerfile
image: cloudlog-web
- dockerfile: Dockerfile-db
image: cloudlog-db
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}
tags: |
type=semver,pattern={{version}}
type=sha
type=raw,value=latest,enable=${{ github.ref_type == 'tag' }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -1,39 +1,35 @@
# Use the official image for PHP and Apache
FROM php:7.4-apache
# Set the working directory to /var/www/html
WORKDIR /var/www/html
# Install system dependencies, including git and libxml2
RUN apt-get update && apt-get install -y \
libcurl4-openssl-dev \
libxml2-dev \
libzip-dev \
zlib1g-dev \
libpng-dev \
libonig-dev \
default-mysql-client \
curl \
&& apt-get clean \
# Single layer: APT cleanup + PHP extensions (curl used by application PHP code)
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
libcurl4-openssl-dev \
libxml2-dev \
libzip-dev \
zlib1g-dev \
libpng-dev \
libonig-dev \
default-mysql-client \
&& rm -rf /var/lib/apt/lists/* \
&& docker-php-ext-install pdo_mysql \
&& docker-php-ext-install mysqli \
&& docker-php-ext-install gd \
&& docker-php-ext-install mbstring \
&& docker-php-ext-install zip \
&& docker-php-ext-install xml \
&& docker-php-ext-install -j"$(nproc)" curl pdo_mysql mysqli gd mbstring zip xml \
&& a2enmod rewrite
# Copy script.sh and make it executable
COPY . /var/www/html/
# Duplicate for seeding an empty Docker volume mounted over /var/www/html (Compose cloudlog_html)
RUN cp -a /var/www/html /usr/local/share/cloudlog-stock
COPY script.sh /usr/local/bin/startup.sh
RUN sed -i 's/\r$//' /usr/local/bin/startup.sh && chmod +x /usr/local/bin/startup.sh
# Configure PHP for larger file uploads (30MB)
RUN echo "upload_max_filesize = 30M" >> /usr/local/etc/php/conf.d/uploads.ini \
&& echo "post_max_size = 35M" >> /usr/local/etc/php/conf.d/uploads.ini \
&& echo "memory_limit = 64M" >> /usr/local/etc/php/conf.d/uploads.ini \
&& echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/uploads.ini \
&& echo "max_input_time = 300" >> /usr/local/etc/php/conf.d/uploads.ini
RUN printf '%s\n' \
'upload_max_filesize = 30M' \
'post_max_size = 35M' \
'memory_limit = 64M' \
'max_execution_time = 300' \
'max_input_time = 300' \
> /usr/local/etc/php/conf.d/uploads.ini
# Expose port 80
EXPOSE 80
EXPOSE 80

View file

@ -4,9 +4,13 @@ FROM mariadb:latest
# Add the install.sql file to the docker image
ADD install/assets/install.sql /docker-entrypoint-initdb.d
# Create a healthcheck script that uses mariadb-admin
RUN echo '#!/bin/bash\nmariadb-admin ping -h "localhost" --silent' > /usr/local/bin/healthcheck.sh \
# Healthcheck: must use DB credentials (MariaDB 12 rejects anonymous/root-without-password probes → log spam)
RUN printf '%s\n' \
'#!/bin/bash' \
'set -e' \
'exec mariadb-admin ping -h localhost --silent -u"${MYSQL_USER}" -p"${MYSQL_PASSWORD}"' \
> /usr/local/bin/healthcheck.sh \
&& chmod +x /usr/local/bin/healthcheck.sh
# Expose port 3306
EXPOSE 3306
EXPOSE 3306

View file

@ -1,15 +1,19 @@
networks:
mynet:
services:
web:
build: .
image: ghcr.io/magicbug/cloudlog-web:latest
build:
context: .
dockerfile: Dockerfile
env_file:
- .env
ports:
- "80:80"
volumes:
- ./:/var/www/html:rw
- ./web:/var/www/html
command: ["/usr/local/bin/startup.sh"]
depends_on:
db:
@ -19,13 +23,14 @@ services:
restart: on-failure
db:
image: ghcr.io/magicbug/cloudlog-db:latest
build:
context: .
dockerfile: Dockerfile-db
env_file:
- .env
volumes:
- db_data:/var/lib/mysql
- ./db_data:/var/lib/mysql
networks:
- mynet
healthcheck:
@ -35,5 +40,3 @@ services:
interval: 10s
start_period: 40s
volumes:
db_data: {}

View file

@ -36,6 +36,40 @@
* @filesource
*/
/*
*---------------------------------------------------------------
* Optional .env next to this file (existing getenv/$_SERVER values are not overwritten Docker/OS wins)
*---------------------------------------------------------------
*/
$cloudlogEnvFile = __DIR__ . DIRECTORY_SEPARATOR . '.env';
if (is_readable($cloudlogEnvFile)) {
foreach (file($cloudlogEnvFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
$line = trim($line);
if ($line === '' || (isset($line[0]) && $line[0] === '#')) {
continue;
}
$eq = strpos($line, '=');
if ($eq === false) {
continue;
}
$name = trim(substr($line, 0, $eq));
$value = trim(substr($line, $eq + 1));
if ($name === '') {
continue;
}
if (strlen($value) >= 2
&& (($value[0] === '"' && substr($value, -1) === '"')
|| ($value[0] === "'" && substr($value, -1) === "'"))) {
$value = substr($value, 1, -1);
}
if (getenv($name) === false) {
putenv($name . '=' . $value);
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
}
}
}
/*
*---------------------------------------------------------------
* APPLICATION ENVIRONMENT
@ -51,10 +85,18 @@
* testing
* production
*
* Set CLOUDLOG_ENV or CI_ENV in .env (or in the server environment). Defaults to development.
*
* NOTE: If you change these, also change the error_reporting() code below
*/
#define('ENVIRONMENT', isset($_SERVER['CI_ENV']) ? $_SERVER['CI_ENV'] : 'development');
define('ENVIRONMENT', 'development');
$cloudlog_environment = getenv('CLOUDLOG_ENV');
if ($cloudlog_environment === false || $cloudlog_environment === '') {
$cloudlog_environment = getenv('CI_ENV');
}
if ($cloudlog_environment === false || $cloudlog_environment === '') {
$cloudlog_environment = 'development';
}
define('ENVIRONMENT', $cloudlog_environment);
/*
*---------------------------------------------------------------

124
script.sh
View file

@ -1,24 +1,34 @@
#!/bin/bash
# Define the file path for .env and the file to modify
if [ -f "./.env" ]; then
ENV_FILE="./.env"
else
ENV_FILE="./.env.sample"
# App root (Dockerfile WORKDIR); ensures ./.env resolves even if the process cwd differs
cd /var/www/html || exit 1
# Named volume on /var/www/html starts empty and hides image layers — copy stock tree once.
if [ ! -f "index.php" ] && [ -d "/usr/local/share/cloudlog-stock" ]; then
echo "Copying Cloudlog into /var/www/html from image (first run or empty volume)..."
cp -a /usr/local/share/cloudlog-stock/. /var/www/html/
fi
CONFIG_FILE="install/config/config.php"
DATABASE_FILE="install/config/database.php"
DEST_DIR="application/config"
# Check if .env file exists
if [ ! -f "$ENV_FILE" ]; then
echo ".env file not found!"
# Load variables: prefer files on the bind mount; otherwise use env already injected by Compose (env_file)
if [ -f "./.env" ]; then
# shellcheck disable=SC1091
source "./.env"
elif [ -f "./.env.sample" ]; then
# shellcheck disable=SC1091
source "./.env.sample"
elif [ -n "${MYSQL_DATABASE:-}" ] && [ -n "${MYSQL_USER:-}" ] && [ -n "${MYSQL_PASSWORD:-}" ] && \
[ -n "${MYSQL_HOST:-}" ] && [ -n "${BASE_LOCATOR:-}" ] && [ -n "${WEBSITE_URL:-}" ] && [ -n "${DIRECTORY:-}" ]; then
:
else
echo ".env file not found under /var/www/html and required variables are not set in the environment."
echo "Add .env next to docker-compose.yml (bind-mounted as /var/www/html) or pass variables via Compose env_file / environment."
exit 1
fi
# Read the .env file
source $ENV_FILE
# Check if MYSQL_DATABASE is set
if [ -z "${MYSQL_DATABASE}" ]; then
echo "MYSQL_DATABASE is not set in .env file!"
@ -55,17 +65,30 @@ if [ -z "${DIRECTORY}" ]; then
exit 1
fi
# Normalize filesystem root (avoids //var/www/html when DIRECTORY is already absolute)
APP_ROOT="${DIRECTORY%/}"
case "$APP_ROOT" in
/*) ;;
*) APP_ROOT="/${APP_ROOT}" ;;
esac
# Check if destination directory exists, if not create it
if [ ! -d "$DEST_DIR" ]; then
mkdir -p $DEST_DIR
mkdir -p "$DEST_DIR"
fi
# Check if configuration has already been processed
if [ -f "$DEST_DIR/database.php" ] && [ -f "$DEST_DIR/config.php" ]; then
echo "Configuration files already exist, skipping processing..."
else
if [ ! -f "$DATABASE_FILE" ] || [ ! -f "$CONFIG_FILE" ]; then
echo "ERROR: Installer templates missing under /var/www/html (install/config/)."
echo "Rebuild the web image so it includes the full Cloudlog tree, or bind-mount a git checkout."
exit 1
fi
echo "Processing configuration files..."
# Use sed with a different delimiter (`|`) to avoid conflicts with special characters
# Strip any trailing whitespace/newlines from variables before using them
CLEAN_DATABASE=$(echo "${MYSQL_DATABASE}" | tr -d '\r\n')
@ -75,21 +98,21 @@ else
CLEAN_LOCATOR=$(echo "${BASE_LOCATOR}" | tr -d '\r\n')
CLEAN_URL=$(echo "${WEBSITE_URL}" | tr -d '\r\n')
CLEAN_DIRECTORY=$(echo "${DIRECTORY}" | tr -d '\r\n')
sed -i "s|%DATABASE%|${CLEAN_DATABASE}|g" $DATABASE_FILE
sed -i "s|%USERNAME%|${CLEAN_USER}|g" $DATABASE_FILE
sed -i "s|%PASSWORD%|${CLEAN_PASSWORD}|g" $DATABASE_FILE
sed -i "s|%HOSTNAME%|${CLEAN_HOST}|g" $DATABASE_FILE
sed -i "s|%baselocator%|${CLEAN_LOCATOR}|g" $CONFIG_FILE
sed -i "s|%websiteurl%|${CLEAN_URL}|g" $CONFIG_FILE
sed -i "s|%directory%|${CLEAN_DIRECTORY}|g" $CONFIG_FILE
sed -i "s|%DATABASE%|${CLEAN_DATABASE}|g" "$DATABASE_FILE"
sed -i "s|%USERNAME%|${CLEAN_USER}|g" "$DATABASE_FILE"
sed -i "s|%PASSWORD%|${CLEAN_PASSWORD}|g" "$DATABASE_FILE"
sed -i "s|%HOSTNAME%|${CLEAN_HOST}|g" "$DATABASE_FILE"
sed -i "s|%baselocator%|${CLEAN_LOCATOR}|g" "$CONFIG_FILE"
sed -i "s|%websiteurl%|${CLEAN_URL}|g" "$CONFIG_FILE"
sed -i "s|%directory%|${CLEAN_DIRECTORY}|g" "$CONFIG_FILE"
# Move the files to the destination directory
mv $CONFIG_FILE $DEST_DIR
mv $DATABASE_FILE $DEST_DIR
mv "$CONFIG_FILE" "$DEST_DIR"
mv "$DATABASE_FILE" "$DEST_DIR"
# Delete the /install directory
rm -rf /install
# Remove installer templates from the bind-mounted tree (under app root, not filesystem /install)
rm -rf ./install
fi
echo "Replacement complete."
@ -106,24 +129,39 @@ else
echo "Database connection failed, but continuing anyway since healthcheck passed..."
fi
# Set Permissions
chown -R root:www-data /${DIRECTORY}/application/config/
chown -R root:www-data /${DIRECTORY}/application/logs/
chown -R root:www-data /${DIRECTORY}/assets/qslcard/
chown -R root:www-data /${DIRECTORY}/backup/
chown -R root:www-data /${DIRECTORY}/updates/
chown -R root:www-data /${DIRECTORY}/uploads/
chown -R root:www-data /${DIRECTORY}/images/eqsl_card_images/
chown -R root:www-data /${DIRECTORY}/assets/json
# Writable paths (create if missing, then fix ownership)
mkdir -p \
"${APP_ROOT}/application/config" \
"${APP_ROOT}/application/cache" \
"${APP_ROOT}/application/logs" \
"${APP_ROOT}/assets/qslcard" \
"${APP_ROOT}/backup" \
"${APP_ROOT}/updates" \
"${APP_ROOT}/uploads" \
"${APP_ROOT}/images/eqsl_card_images" \
"${APP_ROOT}/assets/json"
chmod -R g+rw /${DIRECTORY}/application/config/
chmod -R g+rw /${DIRECTORY}/application/logs/
chmod -R g+rw /${DIRECTORY}/assets/qslcard/
chmod -R g+rw /${DIRECTORY}/backup/
chmod -R g+rw /${DIRECTORY}/updates/
chmod -R g+rw /${DIRECTORY}/uploads/
chmod -R g+rw /${DIRECTORY}/images/eqsl_card_images/
chmod -R g+rw /${DIRECTORY}/assets/json
chown -R root:www-data \
"${APP_ROOT}/application/config" \
"${APP_ROOT}/application/cache" \
"${APP_ROOT}/application/logs" \
"${APP_ROOT}/assets/qslcard" \
"${APP_ROOT}/backup" \
"${APP_ROOT}/updates" \
"${APP_ROOT}/uploads" \
"${APP_ROOT}/images/eqsl_card_images" \
"${APP_ROOT}/assets/json"
chmod -R g+rw \
"${APP_ROOT}/application/config" \
"${APP_ROOT}/application/cache" \
"${APP_ROOT}/application/logs" \
"${APP_ROOT}/assets/qslcard" \
"${APP_ROOT}/backup" \
"${APP_ROOT}/updates" \
"${APP_ROOT}/uploads" \
"${APP_ROOT}/images/eqsl_card_images" \
"${APP_ROOT}/assets/json"
# Start Apache in the foreground
exec apache2-foreground
exec apache2-foreground