diff --git a/config/.env.example b/.env.example similarity index 73% rename from config/.env.example rename to .env.example index 793f83b10..ba2382539 100644 --- a/config/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Used as a default to seed config/.env which +# Used as a default to seed `.env` which # enables you to use environment variables to configure # the aspects of your application that vary by # environment. @@ -7,8 +7,9 @@ # Having this file in production is considered a **SECURITY RISK** and also decreases # the bootstrap performance of your application. # -# To use this file, first copy it into `config/.env`. Also ensure the related -# code block for loading this file is uncommented in `config/bootstrap.php` +# To use this file, first copy it into `.env` in the project root. It is loaded via +# `config/bootstrap.php`. Optional PHP overrides: copy `config/app_local.example.php` +# to `config/app_local.php` (not created on install). # # In development .env files are parsed by PHP # and set into the environment. This provides a simpler @@ -30,11 +31,17 @@ export SECURITY_SALT="__SALT__" #export CACHE_CAKEMODEL_URL="file:///path/to/tmp/cache/models?prefix=${APP_NAME}_cake_model_&serialize=true&duration=${CACHE_DURATION}" # Uncomment these to define email transport configuration via environment variables. +#export EMAIL_HOST="localhost" +#export EMAIL_PORT="25" #export EMAIL_TRANSPORT_DEFAULT_URL="" # Uncomment these to define database configuration via environment variables. -#export DATABASE_URL="mysql://my_app:secret@localhost/${APP_NAME}?encoding=utf8&timezone=UTC&cacheMetadata=true"eIdentifiers=false&persistent=false" -#export DATABASE_TEST_URL="mysql://my_app:secret@localhost/test_${APP_NAME}?encoding=utf8&timezone=UTC&cacheMetadata=true"eIdentifiers=false&persistent=false" +export DB_HOST="localhost" +export DB_USERNAME="my_app" +export DB_PASSWORD="secret" +export DB_DATABASE="my_app" +export DATABASE_URL="mysql://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_DATABASE}?encoding=utf8mb4&timezone=UTC&cacheMetadata=true"eIdentifiers=false&persistent=false" +export DATABASE_TEST_URL="sqlite://127.0.0.1/tmp/tests.sqlite" # Uncomment these to define logging configuration via environment variables. #export LOG_DEBUG_URL="file:///path/to/logs/?levels[]=notice&levels[]=info&levels[]=debug&file=debug" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f221f290a..f2b102bd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: dependencies: 'highest' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index b450d4bc4..49b62ef67 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@v10 + - uses: actions/stale@v11 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue is stale because it has been open for 120 days with no activity. Remove the `stale` label or comment or this will be closed in 15 days' diff --git a/.gitignore b/.gitignore index 03ee77c4f..92ca4c98f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # CakePHP specific files # ########################## /config/app_local.php -/config/.env +/.env /logs/* /tmp/* /vendor/* diff --git a/README.md b/README.md index 5a614fa26..59837b2f2 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,20 @@ automated upgrades, so you have to do any updates manually. ## Configuration -Read and edit the environment specific `config/app_local.php` and set up the -`'Datasources'` and any other configuration relevant for your application. -Other environment agnostic settings can be changed in `config/app.php`. +```text +.env infrastructure vars + └─ app.php application config (reads `.env` via env()) + └─ app_local.php local application overrides (gitignored, manual) +``` + +| File | Role | +|------|------| +| `.env.example` → `.env` | Environment variables: database, salt, app name, URLs (created on install) | +| `config/app.php` | Base application config; reads `.env` through `env()` | +| `config/app_local.example.php` → `app_local.php` | Optional local overrides on top of `app.php` (stock CakePHP) | + +**`.env`** — infrastructure and deployment variables. +**`app_local.php`** — application tuning (debug defaults, datasource/email overrides). Not created on install; copy from `config/app_local.example.php` if needed. ## Layout diff --git a/composer.json b/composer.json index cd196be4f..9ec38086a 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "homepage": "https://cakephp.org", "require": { "php": ">=8.2", - "cakephp/cakephp": "5.3.*", + "cakephp/cakephp": "5.4.*", "cakephp/migrations": "^5.0", "cakephp/plugin-installer": "^2.0", "mobiledetect/mobiledetectlib": "^4.8.03" diff --git a/config/app.php b/config/app.php index 9876cf421..e841847c6 100644 --- a/config/app.php +++ b/config/app.php @@ -226,8 +226,8 @@ * The keys host, port, timeout, username, password, client and tls * are used in SMTP transports */ - 'host' => 'localhost', - 'port' => 25, + 'host' => env('EMAIL_HOST', 'localhost'), + 'port' => env('EMAIL_PORT', 25), 'timeout' => 30, /* * It is recommended to set these options through your environment or app_local.php @@ -265,6 +265,8 @@ * Connection information used by the ORM to connect * to your application's datastores. * + * Values are read from the root `.env` file via env(). Override in app_local.php. + * * ### Notes * - Drivers include Mysql Postgres Sqlite Sqlserver * See vendor\cakephp\cakephp\src\Database\Driver for the complete list @@ -281,15 +283,18 @@ * * The values in app_local.php will override any values set here * and should be used for local and per-environment configurations. - * - * Environment variable-based configurations can be loaded here or - * in app_local.php depending on the application's needs. */ 'default' => [ 'className' => Connection::class, 'driver' => Mysql::class, 'persistent' => false, 'timezone' => 'UTC', + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT'), + 'username' => env('DB_USERNAME', 'my_app'), + 'password' => env('DB_PASSWORD', 'secret'), + 'database' => env('DB_DATABASE', env('APP_NAME', 'my_app')), + 'url' => env('DATABASE_URL', null), /* * For MariaDB/MySQL the internal default changed from utf8 to utf8mb4, aka full utf-8 support @@ -339,6 +344,7 @@ 'quoteIdentifiers' => false, 'log' => false, //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + 'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tmp/tests.sqlite'), ], ], diff --git a/config/bootstrap.php b/config/bootstrap.php index 82a92c60f..9268b8b7d 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -55,8 +55,7 @@ /* * See https://github.com/josegonzalez/php-dotenv for API details. * - * Uncomment block of code below if you want to use `.env` file during development. - * You should copy `config/.env.example` to `config/.env` and set/modify the + * You should copy `.env.example` to `.env` and set/modify the * variables as required. * * The purpose of the .env file is to emulate the presence of the environment @@ -66,13 +65,16 @@ * security risks. See https://github.com/josegonzalez/php-dotenv#general-security-information * for more information for recommended practices. */ -// if (!env('APP_NAME') && file_exists(CONFIG . '.env')) { -// $dotenv = new \josegonzalez\Dotenv\Loader([CONFIG . '.env']); -// $dotenv->parse() -// ->putenv() -// ->toEnv() -// ->toServer(); -// } +if (!env('APP_NAME') && is_readable(ROOT . DS . '.env')) { + if (class_exists(\josegonzalez\Dotenv\Loader::class)) { + (new \josegonzalez\Dotenv\Loader([ROOT . DS . '.env'])) + ->parse() + ->skipExisting() + ->putenv() + ->toEnv() + ->toServer(); + } +} /* * Initializes default Config store and loads the main configuration file (app.php) diff --git a/src/Application.php b/src/Application.php index 98ef0b958..619028491 100644 --- a/src/Application.php +++ b/src/Application.php @@ -35,8 +35,6 @@ * * This defines the bootstrapping logic and middleware layers you * want to use in your application. - * - * @extends \Cake\Http\BaseApplication<\App\Application> */ class Application extends BaseApplication { diff --git a/src/Console/Installer.php b/src/Console/Installer.php index 6ef3c2dd8..0052bf74d 100644 --- a/src/Console/Installer.php +++ b/src/Console/Installer.php @@ -61,7 +61,7 @@ public static function postInstall(Event $event): void $rootDir = dirname(__DIR__, 2); - static::createAppLocalConfig($rootDir, $io); + static::createEnvFile($rootDir, $io); static::createWritableDirectories($rootDir, $io); static::setFolderPermissions($rootDir, $io); @@ -73,19 +73,19 @@ public static function postInstall(Event $event): void } /** - * Create config/app_local.php file if it does not exist. + * Create .env file if it does not exist. * * @param string $dir The application's root directory. * @param \Composer\IO\IOInterface $io IO interface to write to console. * @return void */ - public static function createAppLocalConfig(string $dir, IOInterface $io): void + public static function createEnvFile(string $dir, IOInterface $io): void { - $appLocalConfig = $dir . '/config/app_local.php'; - $appLocalConfigTemplate = $dir . '/config/app_local.example.php'; - if (!file_exists($appLocalConfig)) { - copy($appLocalConfigTemplate, $appLocalConfig); - $io->write('Created `config/app_local.php` file'); + $envFile = $dir . '/.env'; + $envTemplate = $dir . '/.env.example'; + if (!file_exists($envFile)) { + copy($envTemplate, $envFile); + $io->write('Created `.env` file'); } } @@ -183,7 +183,7 @@ public static function setFolderPermissions(string $dir, IOInterface $io): void public static function setSecuritySalt(string $dir, IOInterface $io): void { $newKey = hash('sha256', Security::randomBytes(64)); - static::setSecuritySaltInFile($dir, $io, $newKey, 'app_local.php'); + static::setSecuritySaltInFile($dir, $io, $newKey, '.env'); } /** @@ -197,10 +197,10 @@ public static function setSecuritySalt(string $dir, IOInterface $io): void */ public static function setSecuritySaltInFile(string $dir, IOInterface $io, string $newKey, string $file): void { - $config = $dir . '/config/' . $file; + $config = $dir . '/' . $file; $content = file_get_contents($config); if ($content === false) { - $io->write('Config file not readable or not found: config/' . $file); + $io->write('Config file not readable or not found: ' . $file); return; } @@ -215,7 +215,7 @@ public static function setSecuritySaltInFile(string $dir, IOInterface $io, strin $result = file_put_contents($config, $content); if ($result) { - $io->write('Updated Security.salt value in config/' . $file); + $io->write('Updated Security.salt value in ' . $file); return; } @@ -233,10 +233,10 @@ public static function setSecuritySaltInFile(string $dir, IOInterface $io, strin */ public static function setAppNameInFile(string $dir, IOInterface $io, string $appName, string $file): void { - $config = $dir . '/config/' . $file; + $config = $dir . '/' . $file; $content = file_get_contents($config); if ($content === false) { - $io->write('Config file not readable or not found: config/' . $file); + $io->write('Config file not readable or not found: ' . $file); return; } @@ -251,7 +251,7 @@ public static function setAppNameInFile(string $dir, IOInterface $io, string $ap $result = file_put_contents($config, $content); if ($result) { - $io->write('Updated __APP_NAME__ value in config/' . $file); + $io->write('Updated __APP_NAME__ value in ' . $file); return; } diff --git a/templates/Pages/home.php b/templates/Pages/home.php index 4b38d0f0e..710ad2f03 100644 --- a/templates/Pages/home.php +++ b/templates/Pages/home.php @@ -39,7 +39,7 @@ } if ($name === 'debug_kit') { $error = 'Try adding your current top level domain to the - DebugKit.safeTld + DebugKit.safeTld config and reload.'; if (!in_array('sqlite', \PDO::getAvailableDrivers())) { $error .= '
You need to install the PHP extension pdo_sqlite so DebugKit can work properly.';