This commit is contained in:
1 parent
36b1badc7e
commit
a935cc2470
1 file changed
+104
-171
+104
-171
@@ -2,93 +2,109 @@ name: Docker Build, Test & Push
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches-ignore:
|
||||||
- main
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
paths:
|
|
||||||
- 'dockerfile/**'
|
|
||||||
- 'docker-compose.example.yaml'
|
|
||||||
- '.example.env'
|
|
||||||
- '.gitea/workflows/**'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
- main
|
||||||
|
- default
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# KONFIGURATION (Für andere Repositories einfach hier oben anpassen)
|
||||||
|
# ==============================================================================
|
||||||
env:
|
env:
|
||||||
|
# Registry & Image-Name (CUSTOM_IMAGE_NAME leer lassen = automatisch <user>/<repo>)
|
||||||
REGISTRY: git.dsit.me
|
REGISTRY: git.dsit.me
|
||||||
|
CUSTOM_IMAGE_NAME: ""
|
||||||
|
|
||||||
|
# Pfad zum Build-Kontext und Dockerfile
|
||||||
|
BUILD_CONTEXT: ./dockerfile
|
||||||
|
DOCKERFILE: ./dockerfile/Dockerfile
|
||||||
|
|
||||||
|
# Name des temporären Test-Containers & Start-Timeout (in Sekunden)
|
||||||
|
CONTAINER_NAME: ci-test-container
|
||||||
|
STARTUP_TIMEOUT: 15
|
||||||
|
|
||||||
|
# Umgebungsvariablen für den Test-Container (leer lassen falls nicht benötigt)
|
||||||
|
CONTAINER_ENV: >-
|
||||||
|
-e SMTP_HOST=127.0.0.1
|
||||||
|
-e SMTP_PORT=2525
|
||||||
|
-e SMTP_USER=testuser@example.com
|
||||||
|
-e SMTP_PASS=supersecret
|
||||||
|
-e SMTP_FROM=noreply@example.com
|
||||||
|
-e SMTP_TLS=off
|
||||||
|
-e SMTP_STARTTLS=off
|
||||||
|
|
||||||
|
# Optionale Checks im Container (jeweils leer "" lassen zum Überspringen)
|
||||||
|
READINESS_CMD: "apache2ctl -t"
|
||||||
|
REQUIRED_PHP_EXTENSIONS: "mysqli gd imagick intl zip exif"
|
||||||
|
REQUIRED_APACHE_MODULES: "ssl_module rewrite_module"
|
||||||
|
TEST_HTTP_PORT: "80"
|
||||||
|
TEST_HTTPS_PORT: "443"
|
||||||
|
WEBROOT_DIR: "/var/www/html"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-test-push:
|
build-test-push:
|
||||||
name: Build, Test & Push PHP 8.5 Apache Image
|
name: Build, Test & Push (${{ gitea.ref_name }})
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: gitea.ref_name != 'main' && gitea.ref_name != 'default'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set Image Metadata
|
- name: Prepare Image Tag from Branch
|
||||||
id: meta
|
id: meta
|
||||||
run: |
|
run: |
|
||||||
IMAGE_NAME=$(echo "${{ gitea.repository }}" | tr '[:upper:]' '[:lower:]')
|
if [ -n "${CUSTOM_IMAGE_NAME}" ]; then
|
||||||
SHORT_SHA=$(echo "${{ gitea.sha }}" | cut -c1-7)
|
IMAGE_REPO=$(echo "${CUSTOM_IMAGE_NAME}" | tr '[:upper:]' '[:lower:]')
|
||||||
echo "image=${REGISTRY}/${IMAGE_NAME}" >> "$GITEA_OUTPUT"
|
else
|
||||||
echo "sha_tag=${REGISTRY}/${IMAGE_NAME}:${SHORT_SHA}" >> "$GITEA_OUTPUT"
|
IMAGE_REPO=$(echo "${{ gitea.repository }}" | tr '[:upper:]' '[:lower:]')
|
||||||
echo "latest_tag=${REGISTRY}/${IMAGE_NAME}:latest" >> "$GITEA_OUTPUT"
|
fi
|
||||||
|
|
||||||
|
# Branch-Namen als Docker-Tag nutzen (Schrägstriche durch Bindestriche ersetzen)
|
||||||
|
BRANCH_TAG=$(echo "${{ gitea.ref_name }}" | sed 's/[^a-zA-Z0-9._-]/-/g')
|
||||||
|
FULL_TAG="${REGISTRY}/${IMAGE_REPO}:${BRANCH_TAG}"
|
||||||
|
|
||||||
|
echo "Target Image Tag: ${FULL_TAG}"
|
||||||
|
echo "image_tag=${FULL_TAG}" >> "$GITEA_OUTPUT"
|
||||||
|
|
||||||
- name: Build Docker Image
|
- name: Build Docker Image
|
||||||
run: |
|
run: |
|
||||||
docker build \
|
docker build \
|
||||||
-t webhost-test:ci \
|
-f "${DOCKERFILE}" \
|
||||||
-t "${{ steps.meta.outputs.sha_tag }}" \
|
-t "${{ steps.meta.outputs.image_tag }}" \
|
||||||
-t "${{ steps.meta.outputs.latest_tag }}" \
|
"${BUILD_CONTEXT}"
|
||||||
./dockerfile
|
|
||||||
|
|
||||||
- name: Start Container with Test ENV
|
- name: Start Container & Wait for Readiness
|
||||||
run: |
|
run: |
|
||||||
docker rm -f webhost-test 2>/dev/null || true
|
docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true
|
||||||
|
# shellcheck disable=SC2086
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name webhost-test \
|
--name "${CONTAINER_NAME}" \
|
||||||
-e SMTP_HOST=127.0.0.1 \
|
${CONTAINER_ENV} \
|
||||||
-e SMTP_PORT=2525 \
|
"${{ steps.meta.outputs.image_tag }}"
|
||||||
-e SMTP_USER=testuser@example.com \
|
|
||||||
-e SMTP_PASS=supersecret \
|
|
||||||
-e SMTP_FROM=noreply@example.com \
|
|
||||||
-e SMTP_TLS=off \
|
|
||||||
-e SMTP_STARTTLS=off \
|
|
||||||
webhost-test:ci
|
|
||||||
|
|
||||||
echo "Waiting for Apache to start..."
|
echo "Waiting up to ${STARTUP_TIMEOUT}s for container to become ready..."
|
||||||
for i in $(seq 1 15); do
|
for i in $(seq 1 "${STARTUP_TIMEOUT}"); do
|
||||||
if [ "$(docker inspect -f '{{.State.Running}}' webhost-test 2>/dev/null)" = "true" ]; then
|
if [ "$(docker inspect -f '{{.State.Running}}' "${CONTAINER_NAME}" 2>/dev/null)" = "true" ]; then
|
||||||
if docker exec webhost-test apache2ctl -t >/dev/null 2>&1; then
|
if [ -z "${READINESS_CMD}" ] || docker exec "${CONTAINER_NAME}" sh -c "${READINESS_CMD}" >/dev/null 2>&1; then
|
||||||
echo "Container is running and Apache config is valid."
|
echo "Container is running and ready."
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [ "$i" -eq 15 ]; then
|
if [ "$i" -eq "${STARTUP_TIMEOUT}" ]; then
|
||||||
echo "Container failed to start in time!"
|
echo "Container failed readiness check!"
|
||||||
docker logs webhost-test
|
docker logs "${CONTAINER_NAME}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Verify PHP Version & Extensions
|
- name: Verify PHP Extensions
|
||||||
|
if: env.REQUIRED_PHP_EXTENSIONS != ''
|
||||||
run: |
|
run: |
|
||||||
echo "--- Checking PHP Version (8.5.x) ---"
|
docker exec "${CONTAINER_NAME}" php -v
|
||||||
docker exec webhost-test php -v
|
docker exec -e REQUIRED_EXTS="${REQUIRED_PHP_EXTENSIONS}" "${CONTAINER_NAME}" php -r '
|
||||||
docker exec webhost-test php -r '
|
$required = preg_split("/\s+/", trim(getenv("REQUIRED_EXTS")), -1, PREG_SPLIT_NO_EMPTY);
|
||||||
if (version_compare(PHP_VERSION, "8.5.0-dev", "<")) {
|
|
||||||
fwrite(STDERR, "Expected PHP 8.5+, got " . PHP_VERSION . PHP_EOL);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
'
|
|
||||||
|
|
||||||
echo "--- Checking Required PHP Extensions ---"
|
|
||||||
docker exec webhost-test php -r '
|
|
||||||
$required = ["mysqli", "gd", "imagick", "intl", "zip", "exif", "Zend OPcache"];
|
|
||||||
$missing = [];
|
$missing = [];
|
||||||
foreach ($required as $ext) {
|
foreach ($required as $ext) {
|
||||||
if (!extension_loaded($ext)) {
|
if (!extension_loaded($ext)) {
|
||||||
@@ -99,140 +115,57 @@ jobs:
|
|||||||
fwrite(STDERR, "Missing PHP extensions: " . implode(", ", $missing) . PHP_EOL);
|
fwrite(STDERR, "Missing PHP extensions: " . implode(", ", $missing) . PHP_EOL);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
echo "All required extensions are loaded: " . implode(", ", $required) . PHP_EOL;
|
echo "Verified PHP extensions: " . implode(", ", $required) . PHP_EOL;
|
||||||
'
|
'
|
||||||
|
|
||||||
echo "--- Checking GD WebP & PNG Support ---"
|
- name: Verify Apache Modules
|
||||||
docker exec webhost-test php -r '
|
if: env.REQUIRED_APACHE_MODULES != ''
|
||||||
$gd = gd_info();
|
|
||||||
if (empty($gd["WebP Support"]) || empty($gd["PNG Support"])) {
|
|
||||||
fwrite(STDERR, "GD is missing WebP or PNG support!" . PHP_EOL);
|
|
||||||
print_r($gd);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
echo "GD WebP & PNG support verified." . PHP_EOL;
|
|
||||||
'
|
|
||||||
|
|
||||||
echo "--- Checking OPcache Configuration ---"
|
|
||||||
docker exec webhost-test php -r '
|
|
||||||
$checks = [
|
|
||||||
"opcache.enable" => "1",
|
|
||||||
"opcache.validate_timestamps" => "0",
|
|
||||||
"opcache.max_accelerated_files" => "10000",
|
|
||||||
"opcache.memory_consumption" => "192",
|
|
||||||
"opcache.max_wasted_percentage" => "10",
|
|
||||||
"opcache.interned_strings_buffer" => "16",
|
|
||||||
];
|
|
||||||
foreach ($checks as $key => $expected) {
|
|
||||||
$actual = (string) ini_get($key);
|
|
||||||
if ($actual !== $expected) {
|
|
||||||
fwrite(STDERR, "Config mismatch for $key: expected $expected, got $actual" . PHP_EOL);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo "OPcache settings verified." . PHP_EOL;
|
|
||||||
'
|
|
||||||
|
|
||||||
- name: Verify Apache Modules & SSL Certificate
|
|
||||||
run: |
|
run: |
|
||||||
echo "--- Checking Apache Modules (ssl, rewrite) ---"
|
MODULES=$(docker exec "${CONTAINER_NAME}" apache2ctl -M)
|
||||||
MODULES=$(docker exec webhost-test apache2ctl -M)
|
for mod in ${REQUIRED_APACHE_MODULES}; do
|
||||||
echo "$MODULES" | grep -q "ssl_module" || { echo "ssl_module not enabled"; exit 1; }
|
echo "$MODULES" | grep -q "$mod" || { echo "Missing Apache module: $mod"; exit 1; }
|
||||||
echo "$MODULES" | grep -q "rewrite_module" || { echo "rewrite_module not enabled"; exit 1; }
|
done
|
||||||
echo "Apache modules ssl and rewrite are enabled."
|
echo "Verified Apache modules: ${REQUIRED_APACHE_MODULES}"
|
||||||
|
|
||||||
echo "--- Checking Self-Signed SSL Certificate ---"
|
- name: Verify HTTP / HTTPS Response
|
||||||
docker exec webhost-test test -s /etc/apache2/certs/apache.crt
|
if: env.TEST_HTTP_PORT != '' || env.TEST_HTTPS_PORT != ''
|
||||||
docker exec webhost-test test -s /etc/apache2/certs/apache.key
|
|
||||||
docker exec webhost-test openssl x509 -in /etc/apache2/certs/apache.crt -noout -subject -dates
|
|
||||||
|
|
||||||
- name: Verify msmtp Entrypoint & Configuration
|
|
||||||
run: |
|
run: |
|
||||||
echo "--- Checking /etc/msmtprc & /var/log/msmtp.log ---"
|
if [ -n "${WEBROOT_DIR}" ]; then
|
||||||
docker exec webhost-test sh -c '
|
docker exec "${CONTAINER_NAME}" sh -c "
|
||||||
test -f /etc/msmtprc
|
mkdir -p '${WEBROOT_DIR}' &&
|
||||||
PERMS=$(stat -c "%a" /etc/msmtprc)
|
if [ ! -f '${WEBROOT_DIR}/index.php' ] && [ ! -f '${WEBROOT_DIR}/index.html' ]; then
|
||||||
OWNER=$(stat -c "%U:%G" /etc/msmtprc)
|
echo 'OK' > '${WEBROOT_DIR}/index.html'
|
||||||
if [ "$PERMS" != "600" ] || [ "$OWNER" != "www-data:www-data" ]; then
|
fi
|
||||||
echo "Unexpected /etc/msmtprc permissions ($PERMS) or owner ($OWNER)"
|
"
|
||||||
exit 1
|
fi
|
||||||
fi
|
|
||||||
grep -q "^host 127.0.0.1$" /etc/msmtprc
|
|
||||||
grep -q "^port 2525$" /etc/msmtprc
|
|
||||||
grep -q "^from noreply@example.com$" /etc/msmtprc
|
|
||||||
grep -q "^user testuser@example.com$" /etc/msmtprc
|
|
||||||
grep -q "^password supersecret$" /etc/msmtprc
|
|
||||||
|
|
||||||
test -f /etc/aliases
|
if [ -n "${TEST_HTTP_PORT}" ]; then
|
||||||
grep -q "^default: noreply@example.com$" /etc/aliases
|
echo "Testing HTTP on port ${TEST_HTTP_PORT}..."
|
||||||
|
docker exec "${CONTAINER_NAME}" curl -fsS "http://127.0.0.1:${TEST_HTTP_PORT}/" >/dev/null
|
||||||
|
echo "HTTP check passed."
|
||||||
|
fi
|
||||||
|
|
||||||
test -f /var/log/msmtp.log
|
if [ -n "${TEST_HTTPS_PORT}" ]; then
|
||||||
LOG_OWNER=$(stat -c "%U:%G" /var/log/msmtp.log)
|
echo "Testing HTTPS on port ${TEST_HTTPS_PORT}..."
|
||||||
if [ "$LOG_OWNER" != "www-data:www-data" ]; then
|
docker exec "${CONTAINER_NAME}" curl -fsSk "https://127.0.0.1:${TEST_HTTPS_PORT}/" >/dev/null
|
||||||
echo "Unexpected /var/log/msmtp.log owner ($LOG_OWNER)"
|
echo "HTTPS check passed."
|
||||||
exit 1
|
fi
|
||||||
fi
|
|
||||||
echo "msmtp configuration and permissions verified."
|
|
||||||
'
|
|
||||||
|
|
||||||
- name: Test HTTP, HTTPS & .htaccess Rewrite
|
|
||||||
run: |
|
|
||||||
docker exec webhost-test sh -c '
|
|
||||||
cat << "EOF" > /var/www/html/index.php
|
|
||||||
<?php
|
|
||||||
header("Content-Type: application/json");
|
|
||||||
echo json_encode([
|
|
||||||
"status" => "ok",
|
|
||||||
"php" => PHP_VERSION,
|
|
||||||
"https" => (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off"),
|
|
||||||
"rewritten" => ($_GET["rewritten"] ?? "0") === "1"
|
|
||||||
]);
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat << "EOF" > /var/www/html/.htaccess
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteRule ^rewrite-test$ index.php?rewritten=1 [L,QSA]
|
|
||||||
EOF
|
|
||||||
'
|
|
||||||
|
|
||||||
echo "--- Testing HTTP (Port 80) ---"
|
|
||||||
HTTP_RESP=$(docker exec webhost-test curl -fsS http://127.0.0.1/index.php)
|
|
||||||
echo "HTTP Response: $HTTP_RESP"
|
|
||||||
echo "$HTTP_RESP" | grep -q '"status":"ok"'
|
|
||||||
|
|
||||||
echo "--- Testing HTTPS (Port 443) ---"
|
|
||||||
HTTPS_RESP=$(docker exec webhost-test curl -fsSk https://127.0.0.1/index.php)
|
|
||||||
echo "HTTPS Response: $HTTPS_RESP"
|
|
||||||
echo "$HTTPS_RESP" | grep -q '"status":"ok"'
|
|
||||||
echo "$HTTPS_RESP" | grep -q '"https":true'
|
|
||||||
|
|
||||||
echo "--- Testing .htaccess mod_rewrite ---"
|
|
||||||
REWRITE_RESP=$(docker exec webhost-test curl -fsS http://127.0.0.1/rewrite-test)
|
|
||||||
echo "Rewrite Response: $REWRITE_RESP"
|
|
||||||
echo "$REWRITE_RESP" | grep -q '"rewritten":true'
|
|
||||||
|
|
||||||
- name: Login to Gitea Container Registry
|
- name: Login to Gitea Container Registry
|
||||||
if: success() && gitea.event_name != 'pull_request'
|
if: success() && gitea.event_name != 'pull_request'
|
||||||
run: |
|
run: |
|
||||||
echo "${{ secrets.PACKAGE_TOKEN || secrets.GITEA_TOKEN || gitea.token }}" | \
|
echo "${{ secrets.PACKAGE_TOKEN || gitea.token }}" | \
|
||||||
docker login "${REGISTRY}" -u "${{ gitea.actor }}" --password-stdin
|
docker login "${REGISTRY}" -u "${{ gitea.actor }}" --password-stdin
|
||||||
|
|
||||||
- name: Push Docker Image to Gitea Registry
|
- name: Push Docker Image
|
||||||
if: success() && gitea.event_name != 'pull_request'
|
if: success() && gitea.event_name != 'pull_request'
|
||||||
run: |
|
run: |
|
||||||
docker push "${{ steps.meta.outputs.sha_tag }}"
|
docker push "${{ steps.meta.outputs.image_tag }}"
|
||||||
docker push "${{ steps.meta.outputs.latest_tag }}"
|
|
||||||
|
|
||||||
if [ "${{ gitea.ref_type }}" = "tag" ]; then
|
- name: Cleanup
|
||||||
TAG_IMAGE="${{ steps.meta.outputs.image }}:${{ gitea.ref_name }}"
|
|
||||||
docker tag webhost-test:ci "${TAG_IMAGE}"
|
|
||||||
docker push "${TAG_IMAGE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Cleanup Test Container
|
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
echo "--- Container Logs ---"
|
echo "--- Container Logs ---"
|
||||||
docker logs webhost-test || true
|
docker logs "${CONTAINER_NAME}" || true
|
||||||
docker rm -f webhost-test || true
|
docker rm -f "${CONTAINER_NAME}" || true
|
||||||
docker logout "${REGISTRY}" 2>/dev/null || true
|
docker logout "${REGISTRY}" 2>/dev/null || true
|
||||||
Reference in new issue
Block a user