organize doc files and remove unecessary files
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 47s
Tests / PHPUnit (push) Successful in 1m21s

This commit is contained in:
root
2026-09-02 23:24:43 -04:00
parent e81a1832ad
commit 8d644a2c85
43 changed files with 1 additions and 804 deletions
+709
View File
@@ -0,0 +1,709 @@
#!/usr/bin/env bash
#
# build_production.sh
#
# Build a deployable CodeIgniter 4 production directory from a complete
# project checkout. The script deliberately refuses incomplete projects,
# development Composer dependencies, and non-production environment files.
#
# Usage:
# ./build_production.sh
#
# Optional:
# --skip-composer Use the existing vendor directory. The script rejects
# known development packages such as PHPUnit.
# --skip-lint Skip PHP syntax checks.
# --no-archive Do not create alrahma.zip.
# --no-force Refuse to replace an existing output directory.
# --help Show help.
#
# The web-server document root must be <production-folder>/public.
#
set -Eeuo pipefail
IFS=$'\n\t'
umask 027
SCRIPT_NAME="$(basename "$0")"
SOURCE_DIR="$(pwd)"
OUTPUT_DIR="$(pwd)/build"
ENV_FILE="$(pwd)/env.production"
SKIP_COMPOSER=0
SKIP_LINT=0
MAKE_ARCHIVE=1
FORCE=1
STAGE_ROOT=""
log() {
printf '[%s] %s\n' "$SCRIPT_NAME" "$*"
}
warn() {
printf '[%s] WARNING: %s\n' "$SCRIPT_NAME" "$*" >&2
}
die() {
printf '[%s] ERROR: %s\n' "$SCRIPT_NAME" "$*" >&2
exit 1
}
usage() {
cat <<'USAGE'
Build a CodeIgniter 4 production directory.
Default:
./build_production.sh builds the current project into ./build, copies
./env.production as .env, replaces any existing ./build directory, and
creates ./alrahma.zip.
Options:
--source DIR Complete project root. Default: current directory.
--output DIR Destination. Default: ./build.
--env FILE Production environment file. Default: ./env.production.
--skip-composer Keep the existing vendor directory instead of running
Composer. Known development dependencies are rejected.
--skip-lint Skip PHP syntax checks.
--archive Create ./alrahma.zip after building. Enabled by
default. The archive contains .env and must be
protected.
--no-archive Do not create ./alrahma.zip.
--force Replace an existing output directory. Enabled by
default.
--no-force Refuse to replace an existing output directory.
-h, --help Show this help.
Example:
./build_production.sh
USAGE
}
cleanup() {
if [[ -n "${STAGE_ROOT:-}" && -d "$STAGE_ROOT" ]]; then
rm -rf -- "$STAGE_ROOT"
fi
}
trap cleanup EXIT
trap 'die "Build failed near line $LINENO."' ERR
while [[ $# -gt 0 ]]; do
case "$1" in
--source)
[[ $# -ge 2 ]] || die "--source requires a directory."
SOURCE_DIR="$2"
shift 2
;;
--output)
[[ $# -ge 2 ]] || die "--output requires a directory."
OUTPUT_DIR="$2"
shift 2
;;
--env)
[[ $# -ge 2 ]] || die "--env requires a file."
ENV_FILE="$2"
shift 2
;;
--skip-composer)
SKIP_COMPOSER=1
shift
;;
--skip-lint)
SKIP_LINT=1
shift
;;
--archive)
MAKE_ARCHIVE=1
shift
;;
--no-archive)
MAKE_ARCHIVE=0
shift
;;
--force)
FORCE=1
shift
;;
--no-force)
FORCE=0
shift
;;
-h|--help)
usage
exit 0
;;
*)
die "Unknown option: $1"
;;
esac
done
for command_name in php rsync find awk sed grep sort xargs sha256sum zip; do
command -v "$command_name" >/dev/null 2>&1 \
|| die "Required command is missing: $command_name"
done
[[ -d "$SOURCE_DIR" ]] || die "Source directory does not exist: $SOURCE_DIR"
SOURCE_DIR="$(cd "$SOURCE_DIR" && pwd -P)"
OUTPUT_PARENT="$(dirname "$OUTPUT_DIR")"
mkdir -p -- "$OUTPUT_PARENT"
OUTPUT_PARENT="$(cd "$OUTPUT_PARENT" && pwd -P)"
OUTPUT_DIR="$OUTPUT_PARENT/$(basename "$OUTPUT_DIR")"
[[ "$OUTPUT_DIR" != "/" ]] || die "Refusing to use / as the output directory."
[[ "$OUTPUT_DIR" != "$SOURCE_DIR" ]] \
|| die "Output directory must not be the project root."
[[ -n "$ENV_FILE" ]] \
|| die "Pass a production environment file with --env."
[[ -f "$ENV_FILE" ]] \
|| die "Environment file does not exist: $ENV_FILE"
ENV_FILE="$(cd "$(dirname "$ENV_FILE")" && pwd -P)/$(basename "$ENV_FILE")"
required_paths=(
"app"
"app/Config/Paths.php"
"public"
"public/index.php"
"writable"
"spark"
"composer.json"
"composer.lock"
)
missing_paths=()
for required_path in "${required_paths[@]}"; do
if [[ ! -e "$SOURCE_DIR/$required_path" ]]; then
missing_paths+=("$required_path")
fi
done
if (( ${#missing_paths[@]} > 0 )); then
printf '[%s] Missing required project items:\n' "$SCRIPT_NAME" >&2
printf ' - %s\n' "${missing_paths[@]}" >&2
die "The source is not a complete deployable CodeIgniter project."
fi
[[ -f "$SOURCE_DIR/vendor/autoload.php" || "$SKIP_COMPOSER" -eq 0 ]] \
|| die "--skip-composer requires an existing vendor/autoload.php."
if [[ -e "$OUTPUT_DIR" ]]; then
(( FORCE == 1 )) \
|| die "Output already exists. Use --force to replace it: $OUTPUT_DIR"
log "Removing existing output: $OUTPUT_DIR"
rm -rf -- "$OUTPUT_DIR"
fi
env_value() {
local key="$1"
local file="$2"
awk -F '=' -v wanted="$key" '
/^[[:space:]]*[#;]/ { next }
NF < 2 { next }
{
key = $1
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
if (key != wanted) {
next
}
value = substr($0, index($0, "=") + 1)
sub(/[[:space:]]+[;#].*$/, "", value)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
gsub(/^["'\'']|["'\'']$/, "", value)
print value
exit
}
' "$file"
}
CI_ENVIRONMENT_VALUE="$(env_value "CI_ENVIRONMENT" "$ENV_FILE" || true)"
CI_ENVIRONMENT_VALUE="$(printf '%s' "$CI_ENVIRONMENT_VALUE" | tr '[:upper:]' '[:lower:]')"
[[ "$CI_ENVIRONMENT_VALUE" == "production" ]] \
|| die "The supplied environment file must set CI_ENVIRONMENT = production."
for recommended_key in \
"app.baseURL" \
"database.default.hostname" \
"database.default.database" \
"database.default.username"
do
if [[ -z "$(env_value "$recommended_key" "$ENV_FILE" || true)" ]]; then
warn "Production environment does not define '$recommended_key'. Verify that it is injected another way."
fi
done
STAGE_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/ci4-production-build.XXXXXXXX")"
STAGE_DIR="$STAGE_ROOT/release"
mkdir -p -- "$STAGE_DIR"
log "Copying application files into a clean staging directory."
copy_production_path() {
local relative_path="$1"
local source_path="$SOURCE_DIR/$relative_path"
local destination_path="$STAGE_DIR/$relative_path"
[[ -e "$source_path" ]] || return 0
mkdir -p -- "$(dirname "$destination_path")"
if [[ -d "$source_path" ]]; then
mkdir -p -- "$destination_path"
rsync -a \
--exclude='.DS_Store' \
--exclude='__MACOSX/' \
"$source_path/" "$destination_path/"
else
install -m 640 "$source_path" "$destination_path"
fi
}
copy_public_files() {
mkdir -p -- "$STAGE_DIR/public"
rsync -a \
--exclude='.DS_Store' \
--exclude='__MACOSX/' \
--exclude='/docs/***' \
--exclude='/gitlab/***' \
--exclude='/jenkins/***' \
--exclude='/uploads/***' \
"$SOURCE_DIR/public/" "$STAGE_DIR/public/"
}
production_paths=(
"app"
"bootstrap"
"routes"
"composer.json"
"composer.lock"
"spark"
"preload.php"
"LICENSE"
)
for production_path in "${production_paths[@]}"; do
copy_production_path "$production_path"
done
copy_public_files
mkdir -p -- "$STAGE_DIR/writable"
copy_production_path "writable/.htaccess"
copy_production_path "writable/index.html"
copy_production_path "writable/.gitkeep"
if (( SKIP_COMPOSER == 1 )); then
copy_production_path "vendor"
fi
install -m 600 "$ENV_FILE" "$STAGE_DIR/.env"
# Remove platform debris even when it was nested in an unexpected directory.
find "$STAGE_DIR" -name '.DS_Store' -type f -delete
find "$STAGE_DIR" -name '__MACOSX' -type d -prune -exec rm -rf {} +
runtime_directories=(
"writable/cache"
"writable/debugbar"
"writable/logs"
"writable/session"
"writable/tmp"
"writable/uploads"
"writable/uploads/class_material"
"writable/uploads/early_dismissal_signatures"
"writable/uploads/exams"
"writable/uploads/print_requests"
"writable/uploads/receipts"
"writable/uploads/reimbursements"
"writable/reports"
"writable/report_assets"
"public/uploads"
"public/uploads/event_flyers"
"public/uploads/print_requests"
)
for runtime_directory in "${runtime_directories[@]}"; do
mkdir -p -- "$STAGE_DIR/$runtime_directory"
: > "$STAGE_DIR/$runtime_directory/.gitkeep"
done
chmod 750 "$STAGE_DIR/spark"
chmod 600 "$STAGE_DIR/.env"
if (( SKIP_COMPOSER == 0 )); then
command -v composer >/dev/null 2>&1 \
|| die "Composer is required. Install Composer or use --skip-composer with an already production-only vendor directory."
log "Installing production Composer dependencies."
(
cd "$STAGE_DIR"
COMPOSER_ALLOW_SUPERUSER=1 composer install \
--no-dev \
--prefer-dist \
--optimize-autoloader \
--classmap-authoritative \
--no-interaction \
--no-progress
COMPOSER_ALLOW_SUPERUSER=1 composer check-platform-reqs --no-dev
)
else
log "Using the existing vendor directory."
[[ -f "$STAGE_DIR/vendor/autoload.php" ]] \
|| die "Existing vendor/autoload.php is missing."
forbidden_vendor_paths=(
"vendor/phpunit"
"vendor/fakerphp"
"vendor/mockery"
"vendor/myclabs/deep-copy"
"vendor/phar-io"
"vendor/sebastian"
"vendor/theseer"
)
found_dev_dependencies=()
for vendor_path in "${forbidden_vendor_paths[@]}"; do
if [[ -e "$STAGE_DIR/$vendor_path" ]]; then
found_dev_dependencies+=("$vendor_path")
fi
done
if (( ${#found_dev_dependencies[@]} > 0 )); then
printf '[%s] Development packages remain in vendor:\n' "$SCRIPT_NAME" >&2
printf ' - %s\n' "${found_dev_dependencies[@]}" >&2
die "Run Composer with --no-dev instead of using --skip-composer."
fi
fi
[[ -f "$STAGE_DIR/vendor/autoload.php" ]] \
|| die "Production vendor/autoload.php was not created."
php -r '
require $argv[1];
fwrite(STDOUT, "Composer autoload check passed.\n");
' "$STAGE_DIR/vendor/autoload.php"
if (( SKIP_LINT == 0 )); then
log "Linting application PHP files."
lint_failure_file="$STAGE_ROOT/php-lint-failures.txt"
: > "$lint_failure_file"
while IFS= read -r -d '' php_file; do
if ! php -l "$php_file" >/dev/null 2>&1; then
printf '%s\n' "$php_file" >> "$lint_failure_file"
fi
done < <(
find \
"$STAGE_DIR/app" \
"$STAGE_DIR/public" \
-type f -name '*.php' -print0
)
if [[ -s "$lint_failure_file" ]]; then
cat "$lint_failure_file" >&2
die "PHP lint failed."
fi
fi
# Apply conservative baseline permissions. The deployment user or PHP-FPM
# group can be supplied later using chown/chgrp on the server.
find "$STAGE_DIR" -type d -exec chmod 750 {} +
find "$STAGE_DIR" -type f -exec chmod 640 {} +
chmod 750 "$STAGE_DIR/spark"
chmod 600 "$STAGE_DIR/.env"
for runtime_directory in "${runtime_directories[@]}"; do
chmod 770 "$STAGE_DIR/$runtime_directory"
done
mkdir -p "$STAGE_DIR/.deployment"
cat > "$STAGE_DIR/.deployment/release-check.sh" <<'RELEASE_CHECK'
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
errors=0
fail() {
printf 'FAIL: %s\n' "$*" >&2
errors=$((errors + 1))
}
pass() {
printf 'PASS: %s\n' "$*"
}
[[ -f "$ROOT/.env" ]] \
&& pass ".env exists" \
|| fail ".env is missing"
if grep -Eq '^[[:space:]]*CI_ENVIRONMENT[[:space:]]*=[[:space:]]*["'\'']?production["'\'']?[[:space:]]*$' "$ROOT/.env"; then
pass "CI_ENVIRONMENT is production"
else
fail "CI_ENVIRONMENT is not production"
fi
for required in \
"app/Config/Paths.php" \
"public/index.php" \
"vendor/autoload.php" \
"spark"
do
[[ -e "$ROOT/$required" ]] \
&& pass "$required exists" \
|| fail "$required is missing"
done
for writable_path in \
"writable/cache" \
"writable/logs" \
"writable/session" \
"writable/tmp" \
"writable/uploads"
do
if [[ -d "$ROOT/$writable_path" && -w "$ROOT/$writable_path" ]]; then
pass "$writable_path is writable"
else
fail "$writable_path is not writable"
fi
done
for forbidden in \
".git" \
"tests" \
"node_modules" \
"vendor/phpunit" \
"phpunit.xml"
do
[[ ! -e "$ROOT/$forbidden" ]] \
&& pass "$forbidden is absent" \
|| fail "$forbidden must not be deployed"
done
php -r 'require $argv[1];' "$ROOT/vendor/autoload.php" \
&& pass "Composer autoload works" \
|| fail "Composer autoload failed"
printf '\nRequired web document root:\n %s/public\n' "$ROOT"
printf '\nBefore traffic is enabled, run database migration status and the application financial smoke tests.\n'
if (( errors > 0 )); then
printf '\nRelease check failed with %d error(s).\n' "$errors" >&2
exit 1
fi
printf '\nRelease check passed.\n'
RELEASE_CHECK
chmod 750 "$STAGE_DIR/.deployment/release-check.sh"
cat > "$STAGE_DIR/.deployment/nginx.conf.example" <<'NGINX'
server {
listen 80;
server_name __DOMAIN__;
# Point the web root only at public. Exposing the project root is unsafe.
root __APP_ROOT__/public;
index index.php;
client_max_body_size 25m;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_PROXY "";
fastcgi_pass __PHP_FPM_SOCKET__;
}
location ~ /\.(?!well-known).* {
deny all;
}
location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|ico|webp|woff|woff2|ttf)$ {
expires 7d;
access_log off;
try_files $uri =404;
}
}
NGINX
cat > "$STAGE_DIR/.deployment/apache-vhost.conf.example" <<'APACHE'
<VirtualHost *:80>
ServerName __DOMAIN__
DocumentRoot "__APP_ROOT__/public"
<Directory "__APP_ROOT__/public">
AllowOverride All
Options FollowSymLinks
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/school-app-error.log
CustomLog ${APACHE_LOG_DIR}/school-app-access.log combined
</VirtualHost>
APACHE
cat > "$STAGE_DIR/.deployment/DEPLOYMENT.md" <<'DEPLOYMENT'
# Production Deployment Notes
## Required document root
Configure the web server document root as:
```text
<release-directory>/public
```
Never expose the project root, `app`, `vendor`, `writable`, or `.env`.
## Server preparation
1. Install the PHP version and extensions accepted by:
```bash
composer check-platform-reqs --no-dev
```
2. Set the release owner and PHP-FPM group:
```bash
sudo chown -R DEPLOY_USER:PHP_FPM_GROUP /path/to/release
```
3. Keep application code read-only for PHP-FPM. Grant write access only to runtime locations:
```bash
sudo find /path/to/release -type d -exec chmod 750 {} \;
sudo find /path/to/release -type f -exec chmod 640 {} \;
sudo chmod 600 /path/to/release/.env
sudo chmod 750 /path/to/release/spark
sudo chmod -R 770 /path/to/release/writable
sudo chmod -R 770 /path/to/release/public/uploads
```
4. Run the packaged release check:
```bash
/path/to/release/.deployment/release-check.sh
```
## Database deployment
Back up the production database first.
Review migration status before executing migrations:
```bash
cd /path/to/release
php spark migrate:status
php spark migrate --all
```
Financial hardening migrations must fail if required constraints cannot be installed. Do not accept warning-only deployment of uniqueness or check constraints.
## Persistent data
Do not overwrite persistent production data during release replacement:
- `.env`
- `writable/uploads`
- `writable/reports`
- `writable/report_assets`
- `public/uploads`
Mount or copy these from controlled persistent storage.
## Traffic activation
Before enabling traffic:
1. Run migrations.
2. Run release checks.
3. Run financial smoke tests for invoice, payment, refund, discount, expense, reimbursement, and purchase-order workflows.
4. Confirm scheduled jobs and workers point at the new release.
5. Confirm writable ownership.
6. Confirm the web server points to `public`.
7. Confirm `CI_ENVIRONMENT = production`.
8. Confirm debug output is disabled.
9. Confirm database backups and rollback instructions exist.
Use an atomic `current` symlink switch where possible rather than copying files over a live release.
DEPLOYMENT
SOURCE_COMMIT="not-a-git-checkout"
if command -v git >/dev/null 2>&1 && git -C "$SOURCE_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
SOURCE_COMMIT="$(git -C "$SOURCE_DIR" rev-parse HEAD)"
fi
cat > "$STAGE_DIR/.deployment/BUILD_INFO" <<BUILD_INFO
built_at_utc=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
source_directory=$SOURCE_DIR
source_commit=$SOURCE_COMMIT
php_version=$(php -r 'echo PHP_VERSION;')
composer_mode=$([[ "$SKIP_COMPOSER" -eq 1 ]] && printf 'existing-vendor' || printf 'composer-no-dev')
BUILD_INFO
(
cd "$STAGE_DIR"
find . -type f \
! -path './.env' \
! -path './.deployment/MANIFEST.sha256' \
-print0 \
| sort -z \
| xargs -0 sha256sum \
> .deployment/MANIFEST.sha256
)
log "Running packaged release verification."
"$STAGE_DIR/.deployment/release-check.sh"
log "Publishing production directory: $OUTPUT_DIR"
mkdir -p -- "$(dirname "$OUTPUT_DIR")"
mv -- "$STAGE_DIR" "$OUTPUT_DIR"
if (( MAKE_ARCHIVE == 1 )); then
ARCHIVE_PATH="$(dirname "$OUTPUT_DIR")/alrahma.zip"
rm -f -- "$ARCHIVE_PATH"
warn "The archive contains the production .env. Store and transfer it as a secret."
(
cd "$(dirname "$OUTPUT_DIR")"
zip -qr "$ARCHIVE_PATH" "$(basename "$OUTPUT_DIR")"
)
chmod 600 "$ARCHIVE_PATH"
log "Created protected archive: $ARCHIVE_PATH"
fi
cat <<SUMMARY
Production build completed.
Directory:
$OUTPUT_DIR
Web document root:
$OUTPUT_DIR/public
Verification:
$OUTPUT_DIR/.deployment/release-check.sh
Deployment notes:
$OUTPUT_DIR/.deployment/DEPLOYMENT.md
The build excludes runtime logs, sessions, caches, existing uploads, tests,
development metadata, and local environment files. The supplied production
environment file was copied as .env with mode 600.
SUMMARY
+230
View File
@@ -0,0 +1,230 @@
#!/bin/bash
# ===============================
# Al Rahma Sunday School Deployment Script
# ===============================
# ----- Domain and App Info -----
DM_NAME="home.alrahmaisgl.org"
domain_app="alrahma"
# ----- Database credentials -----
DB_HOST="localhost"
DB_NAME="u280815660_school"
DB_USER="u280815660_melabidi"
DB_PASS=">tNxlRzP/W8"
# ----- Directories -----
ZIP_FILE="$1"
BASE_DIR="/home/u280815660/domains"
DEPLOY_DIR="$BASE_DIR/$domain_app"
APP_DIR="$BASE_DIR/$DM_NAME/$domain_app"
PUBLIC_DIR="$BASE_DIR/$DM_NAME/public_html"
BACKUP_DIR="$BASE_DIR/archive"
SECRETS_DIR="$BASE_DIR/deploy_secrets"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
# ----- Runtime binaries -----
# Hostinger/CloudLinux exposes PHP 8.5 here. The default CLI `php` may still be
# PHP 8.2, which cannot install this project's PHP 8.5 lock file.
PHP_BIN="${PHP_BIN:-/opt/alt/php85/usr/bin/php}"
COMPOSER_BIN="${COMPOSER_BIN:-$(command -v composer2 || command -v composer || true)}"
# ===============================
# 1. VALIDATE INPUT
# ===============================
if [ -z "$ZIP_FILE" ] || [ ! -f "$ZIP_FILE" ]; then
echo "? Please provide the ZIP file: ./deploy_home.sh alrahma_deploy.zip"
exit 1
fi
echo "?? Starting deployment of $ZIP_FILE..."
if [ ! -x "$PHP_BIN" ]; then
echo "? PHP 8.5 binary was not found or is not executable: $PHP_BIN"
echo " Set PHP_BIN=/path/to/php85 and rerun the deployment."
exit 1
fi
if [ -z "$COMPOSER_BIN" ] || [ ! -e "$COMPOSER_BIN" ]; then
echo "? composer2/composer was not found in PATH."
echo " Set COMPOSER_BIN=/path/to/composer2 and rerun the deployment."
exit 1
fi
echo "?? Using PHP: $("$PHP_BIN" -v | head -n 1)"
echo "?? Using Composer: $COMPOSER_BIN"
# ===============================
# 2. BACKUP EXISTING SITE
# ===============================
echo "??? Backing up current site..."
mkdir -p "$BACKUP_DIR"
cp -r "$PUBLIC_DIR" "$BACKUP_DIR/public_html_$TIMESTAMP"
cp -r "$APP_DIR" "$BACKUP_DIR/$domain_app_$TIMESTAMP"
# ===============================
# 3. BACKUP PERSISTENT FILES
# ===============================
echo "?? Backing up persistent files..."
mkdir -p "$SECRETS_DIR"
PERSIST_BACKUP="$BASE_DIR/persist_backup_$TIMESTAMP"
mkdir -p "$PERSIST_BACKUP"
# writable/
if [ -d "$APP_DIR/writable" ]; then
echo " ? Backing up writable/"
cp -r "$APP_DIR/writable" "$PERSIST_BACKUP/writable"
fi
# .env
if [ -f "$APP_DIR/.env" ]; then
echo " ? Backing up .env"
cp "$APP_DIR/.env" "$PERSIST_BACKUP/.env"
fi
# public/.htaccess backup is kept for rollback only. The new release file is
# deployed below so stale CSP/rewrite rules do not survive deployments.
if [ -f "$PUBLIC_DIR/.htaccess" ]; then
echo " ? Backing up public .htaccess"
cp "$PUBLIC_DIR/.htaccess" "$PERSIST_BACKUP/.htaccess"
fi
# public/index.php backup is kept for rollback only. The new release file is
# deployed below and then patched to point at the sibling app directory.
if [ -f "$PUBLIC_DIR/index.php" ]; then
echo " ? Backing up public index.php"
cp "$PUBLIC_DIR/index.php" "$PERSIST_BACKUP/index.php"
fi
# ===============================
# 4. CLEAN & EXTRACT NEW DEPLOY
# ===============================
echo "?? Cleaning old deployment..."
rm -rf "$DEPLOY_DIR"
unzip "$ZIP_FILE" -d "$BASE_DIR"
# ===============================
# 5. DEPLOY APP FILES
# ===============================
echo "?? Deploying new app files..."
rm -rf "$APP_DIR"
mkdir -p "$APP_DIR"
cp -r "$DEPLOY_DIR"/. "$APP_DIR"
# ===============================
# 6. RESTORE PERSISTENT FILES
# ===============================
echo "?? Restoring persistent files..."
# writable/
if [ -d "$PERSIST_BACKUP/writable" ]; then
echo " ? Restoring writable/"
rm -rf "$APP_DIR/writable"
cp -r "$PERSIST_BACKUP/writable" "$APP_DIR/writable"
fi
# .env
if [ -f "$PERSIST_BACKUP/.env" ]; then
echo " ? Restoring .env"
cp "$PERSIST_BACKUP/.env" "$APP_DIR/.env"
fi
# ===============================
# 7. DEPLOY PUBLIC FILES
# ===============================
echo "?? Deploying public files..."
mkdir -p "$PUBLIC_DIR"
find "$PUBLIC_DIR" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
cp -a "$APP_DIR/public/." "$PUBLIC_DIR/"
# ===============================
# 8. FIX index.php PATH
# ===============================
echo "?? Fixing index.php path..."
sed -i "s|require FCPATH . '../app/Config/Paths.php';|require FCPATH . '../alrahma/app/Config/Paths.php';|" "$PUBLIC_DIR/index.php"
# ===============================
# 9. UPDATE BASE URL & DB CONFIG
# ===============================
APP_CONFIG="$APP_DIR/app/Config/App.php"
DB_CONFIG="$APP_DIR/app/Config/Database.php"
echo "?? Updating configuration files..."
# Base URL
sed -i "s|public string \$baseURL = .*|public string \$baseURL = 'https://$DM_NAME/';|" "$APP_CONFIG"
# Database.php
sed -i "s|'hostname' => .*|'hostname' => '$DB_HOST',|" "$DB_CONFIG"
sed -i "s|'username' => .*|'username' => '$DB_USER',|" "$DB_CONFIG"
sed -i "s|'password' => .*|'password' => '$DB_PASS',|" "$DB_CONFIG"
sed -i "s|'database' => .*|'database' => '$DB_NAME',|" "$DB_CONFIG"
# ===============================
# 10. UPDATE db_connection.php
# ===============================
DB_CONN_FILE="$APP_DIR/app/db_connection.php"
if [ -f "$DB_CONN_FILE" ]; then
echo "??? Updating db_connection.php..."
sed -i "s|\$host = '.*';|\$host = '$DB_HOST';|" "$DB_CONN_FILE"
sed -i "s|\$dbname = '.*';|\$dbname = '$DB_NAME';|" "$DB_CONN_FILE"
sed -i "s|\$username = '.*';|\$username = '$DB_USER';|" "$DB_CONN_FILE"
sed -i "s|\$password = '.*';|\$password = '$DB_PASS';|" "$DB_CONN_FILE"
fi
# ===============================
# 11. FIX FPDF PATHS
# ===============================
echo "?? Fixing FPDF paths..."
grep -rl "ThirdParty\\\\fpdf\\\\fpdf.php" "$APP_DIR/app" | while read -r file; do
sed -i "s|ThirdParty\\\\fpdf\\\\fpdf.php|ThirdParty/fpdf/fpdf.php|g" "$file"
echo "? Fixed path in: $file"
done
# ===============================
# 12. COMPOSER INSTALL
# ===============================
echo "?? Installing production Composer dependencies with PHP 8.5..."
cd "$APP_DIR" || exit
export PATH="$(dirname "$PHP_BIN"):$PATH"
export COMPOSER_ALLOW_SUPERUSER=1
if "$PHP_BIN" "$COMPOSER_BIN" --version >/dev/null 2>&1; then
"$PHP_BIN" "$COMPOSER_BIN" install \
--no-dev \
--prefer-dist \
--optimize-autoloader \
--classmap-authoritative \
--no-interaction \
--no-progress
else
"$COMPOSER_BIN" install \
--no-dev \
--prefer-dist \
--optimize-autoloader \
--classmap-authoritative \
--no-interaction \
--no-progress
fi
"$PHP_BIN" spark --version
# ===============================
# 13. RESTORE PERMISSIONS
# ===============================
echo "?? Setting permissions..."
chmod 644 "$PUBLIC_DIR/.htaccess"
chmod 644 "$PUBLIC_DIR/index.php"
chmod -R 755 "$APP_DIR"
chmod 644 "$APP_DIR/app/db_connection.php"
chmod -R 777 "$APP_DIR/writable"
# ===============================
# ? DONE
# ===============================
echo "? Deployment completed successfully at $TIMESTAMP"
echo "?? Backup stored in: $BACKUP_DIR"
echo "?? Persistent files restored from: $PERSIST_BACKUP"
echo "?? Writable and .env restored; public .htaccess and index.php deployed from the new release."
Binary file not shown.
+28
View File
@@ -0,0 +1,28 @@
services:
mysql:
image: mysql:8.4
container_name: mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: PMPS5k0D7rUeJOk0NkhI5bRtoGjkUqjK
MYSQL_DATABASE: school_prod
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
platform: linux/amd64
container_name: phpmyadmin
restart: unless-stopped
environment:
PMA_HOST: mysql
PMA_PORT: 3306
ports:
- "8081:80"
depends_on:
- mysql
volumes:
mysql_data:
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# save as find_env_issue.sh and run: bash find_env_issue.sh
PHP_BIN="/opt/lampp/bin/php" # change if needed
ROOT="$(pwd)"
INPUT="$ROOT/.env.full"
OUT="$ROOT/.env"
> "$OUT"
n=0
while IFS= read -r line || [ -n "$line" ]; do
n=$((n+1))
printf "%s\n" "$line" >> "$OUT"
"$PHP_BIN" -r "require 'vendor/autoload.php'; try { (new \CodeIgniter\Config\DotEnv(getcwd().'/.env'))->load(); echo 'OK\n'; } catch (Throwable \$e) { echo 'FAIL\n'.get_class(\$e).': '.\$e->getMessage().PHP_EOL; exit(1); }" >/tmp/env_check.log 2>&1
if [ $? -ne 0 ]; then
echo \"Problem detected when adding line $n:\"
sed -n \"${n}p\" \"$INPUT\"
echo
echo \"See /tmp/env_check.log for full output.\"
exit 0
fi
done < "$INPUT"
echo "No problems found by incremental test (unexpected)."
+25
View File
@@ -0,0 +1,25 @@
import subprocess
import time
# === Configuration ===
php_path = r"C:\wamp64\bin\php\php8.3.14\php.exe"
project_path = r"C:\wamp64\www\alrahma_school_sunday"
command = f'"{php_path}" "{project_path}\\spark" users:delete-inactive-users'
# === Loop Settings ===
interval_seconds = 60 * 15 # Run every 5 minutes (change as needed)
while True:
try:
print("\n🕐 Running delete-inactive-users command...")
subprocess.run(command, check=True, shell=True)
print("✅ Command executed successfully.")
except subprocess.CalledProcessError as e:
print("❌ Failed to run the command.")
print(e)
print(f"⏳ Waiting {interval_seconds} seconds before next run...\n")
time.sleep(interval_seconds)
#/var/lib/vz/dump/vzdump-lxc-104-2025_05_27-02_51_09.tar.zst'