This commit is contained in:
parent
1d37af7282
commit
059d3d2f12
23
.forgejo/workflows/ci.yml
Normal file
23
.forgejo/workflows/ci.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request_target:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.4'
|
||||||
|
extensions: mbstring, pdo_mysql, json
|
||||||
|
coverage: none
|
||||||
|
- name: Install dependencies
|
||||||
|
run: composer install --no-progress --prefer-dist
|
||||||
|
- name: Run tests
|
||||||
|
run: vendor/bin/codecept run
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
milestones:
|
milestones:
|
||||||
# Milestone 0: Project Foundation & Setup
|
# Milestone 0: Project Foundation & Setup
|
||||||
milestone_0_project_foundation:
|
milestone_0_project_foundation:
|
||||||
status: TODO
|
status: COMPLETE
|
||||||
description: >-
|
description: >-
|
||||||
Set up the project structure, Composer dependencies, autoloading,
|
Set up the project structure, Composer dependencies, autoloading,
|
||||||
and basic bootstrap files. This establishes the base for all subsequent
|
and basic bootstrap files. This establishes the base for all subsequent
|
||||||
|
|
@ -32,7 +32,7 @@ milestones:
|
||||||
|
|
||||||
# Milestone 1: Configuration & Environment Management
|
# Milestone 1: Configuration & Environment Management
|
||||||
milestone_1_configuration:
|
milestone_1_configuration:
|
||||||
status: TODO
|
status: COMPLETE
|
||||||
description: >-
|
description: >-
|
||||||
Implement environment configuration loading, Infisical integration,
|
Implement environment configuration loading, Infisical integration,
|
||||||
and .env handling. This provides secure runtime secrets and
|
and .env handling. This provides secure runtime secrets and
|
||||||
|
|
|
||||||
2
_bootstrap.php
Normal file
2
_bootstrap.php
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?php
|
||||||
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
17
codeception.yml
Normal file
17
codeception.yml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
actor: Tester
|
||||||
|
paths:
|
||||||
|
tests: tests
|
||||||
|
output: tests/_output
|
||||||
|
data: tests/_data
|
||||||
|
support: tests/_support
|
||||||
|
bootstrap: _bootstrap.php
|
||||||
|
settings:
|
||||||
|
colors: true
|
||||||
|
suites:
|
||||||
|
unit:
|
||||||
|
actor: UnitTester
|
||||||
|
path: unit
|
||||||
|
class_name: UnitSuite
|
||||||
|
modules:
|
||||||
|
enabled:
|
||||||
|
- Asserts
|
||||||
|
|
@ -12,7 +12,8 @@
|
||||||
"phpmailer/phpmailer": "^7.1",
|
"phpmailer/phpmailer": "^7.1",
|
||||||
"guzzlehttp/guzzle": "^7.8",
|
"guzzlehttp/guzzle": "^7.8",
|
||||||
"symfony/http-foundation": "^6.3",
|
"symfony/http-foundation": "^6.3",
|
||||||
"infisical/php-sdk": "^0.0.2"
|
"infisical/php-sdk": "^0.0.2",
|
||||||
|
"ext-dom": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"codeception/codeception": "^5.3",
|
"codeception/codeception": "^5.3",
|
||||||
|
|
@ -27,12 +28,17 @@
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"SiteWeaver\\": "src/Site/",
|
"SiteWeaver\\": "src/Site/",
|
||||||
"Intervention\\": "src/Intervention/",
|
"SiteWeaver\\Plugins\\": "site/plugins/",
|
||||||
"Tests\\": "tests/"
|
"SiteWeaver\\Themes\\": "site/themes/",
|
||||||
|
"SiteWeaver\\AdminThemes\\": "site/admin_themes/",
|
||||||
|
"Intervention\\": "src/Intervention/"
|
||||||
},
|
},
|
||||||
"classmap": [
|
"classmap": []
|
||||||
"src/Intervention/Image/ImageManager.php"
|
},
|
||||||
]
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"migrate:up": "php vendor/bin/phinx:migrations:up",
|
"migrate:up": "php vendor/bin/phinx:migrations:up",
|
||||||
|
|
|
||||||
17
composer.lock
generated
17
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "1976fa1898ee5b599738fe40055ea1be",
|
"content-hash": "f9abfc5b9d7ebdcca8c6410cf861c505",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "aws/aws-crt-php",
|
"name": "aws/aws-crt-php",
|
||||||
|
|
@ -62,16 +62,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "aws/aws-sdk-php",
|
"name": "aws/aws-sdk-php",
|
||||||
"version": "3.394.10",
|
"version": "3.394.11",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||||
"reference": "3b7cbf17b8ba568f77546d1e5edd3f2071861c8f"
|
"reference": "0a16c43453b30b5ec4025293f33028e299cf448e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3b7cbf17b8ba568f77546d1e5edd3f2071861c8f",
|
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0a16c43453b30b5ec4025293f33028e299cf448e",
|
||||||
"reference": "3b7cbf17b8ba568f77546d1e5edd3f2071861c8f",
|
"reference": "0a16c43453b30b5ec4025293f33028e299cf448e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -153,9 +153,9 @@
|
||||||
"support": {
|
"support": {
|
||||||
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
||||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.394.10"
|
"source": "https://github.com/aws/aws-sdk-php/tree/3.394.11"
|
||||||
},
|
},
|
||||||
"time": "2026-09-08T18:07:43+00:00"
|
"time": "2026-09-09T18:06:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ezyang/htmlpurifier",
|
"name": "ezyang/htmlpurifier",
|
||||||
|
|
@ -10534,7 +10534,8 @@
|
||||||
"prefer-stable": false,
|
"prefer-stable": false,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^8.4"
|
"php": "^8.4",
|
||||||
|
"ext-dom": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": {},
|
"platform-dev": {},
|
||||||
"plugin-api-version": "2.9.0"
|
"plugin-api-version": "2.9.0"
|
||||||
|
|
|
||||||
13
config/.env.example
Normal file
13
config/.env.example
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Example .env file for SiteWeaverCMS
|
||||||
|
#
|
||||||
|
# Required Infisical integration variables (set by your Nginx config or deployment environment)
|
||||||
|
INFISICAL_URL="https://your.infisical.instance"
|
||||||
|
INFISICAL_ENV="dev"
|
||||||
|
INFISICAL_PROJECT_ID="YOUR_PROJECT_ID"
|
||||||
|
|
||||||
|
# Optional client credentials for machine authentication
|
||||||
|
INFISICAL_CLIENT_ID="YOUR_CLIENT_ID"
|
||||||
|
INFISICAL_CLIENT_SECRET="YOUR_CLIENT_SECRET"
|
||||||
|
|
||||||
|
# Other application-specific environment variables can be added below
|
||||||
|
# e.g., DATABASE_URL, APP_ENV, etc.
|
||||||
18
config/bootstrap.php
Normal file
18
config/bootstrap.php
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
|
||||||
|
$dotenv->safeLoad();
|
||||||
|
|
||||||
|
$mergedEnv = $_ENV + $_SERVER;
|
||||||
|
|
||||||
|
use League\Container\Container;
|
||||||
|
use SiteWeaver\Core\Config;
|
||||||
|
$container = new Container();
|
||||||
|
$container->add('config', function () use ($mergedEnv) {
|
||||||
|
return new Config($mergedEnv);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $container;
|
||||||
7
public/index.php
Normal file
7
public/index.php
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
$container = require __DIR__ . '/../config/bootstrap.php';
|
||||||
|
|
||||||
|
// Placeholder: output a simple message.
|
||||||
|
echo "SiteWeaverCMS running.";
|
||||||
77
src/Site/Core/Config.php
Normal file
77
src/Site/Core/Config.php
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SiteWeaver\Core;
|
||||||
|
|
||||||
|
use Infisical\SDK\InfisicalSDK;
|
||||||
|
use Infisical\SDK\Services\UniversalAuthService;
|
||||||
|
use Infisical\SDK\Models\MachineIdentityCredential;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration service that loads environment variables and optionally fetches secrets from Infisical.
|
||||||
|
*/
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
/** @var array<string, mixed> */
|
||||||
|
private array $values = [];
|
||||||
|
|
||||||
|
public function __construct(array $env = null)
|
||||||
|
{
|
||||||
|
// Use provided env array or fallback to $_ENV
|
||||||
|
$this->values = $env ?? $_ENV;
|
||||||
|
|
||||||
|
// If Infisical integration variables are present, attempt to fetch secrets
|
||||||
|
if ($this->hasInfisicalConfig()) {
|
||||||
|
try {
|
||||||
|
$sdk = new InfisicalSDK($this->get('INFISICAL_URL'));
|
||||||
|
|
||||||
|
// Optional authentication using client credentials
|
||||||
|
$clientId = $this->get('INFISICAL_CLIENT_ID');
|
||||||
|
$clientSecret = $this->get('INFISICAL_CLIENT_SECRET');
|
||||||
|
if ($clientId !== null && $clientSecret !== null) {
|
||||||
|
/** @var UniversalAuthService $auth */
|
||||||
|
$auth = $sdk->auth()->universalAuth();
|
||||||
|
/** @var MachineIdentityCredential $credential */
|
||||||
|
$credential = $auth->login($clientId, $clientSecret);
|
||||||
|
// The SDK will internally set the auth token via callback
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch secrets and merge into values if not already defined
|
||||||
|
foreach ($sdk->secrets()->list() as $secret) {
|
||||||
|
$key = $secret->secretKey ?? null;
|
||||||
|
$value = $secret->secretValue ?? null;
|
||||||
|
if ($key !== null && $value !== null && !array_key_exists($key, $this->values)) {
|
||||||
|
$this->values[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// If Infisical integration fails, rethrow as runtime exception
|
||||||
|
throw new \RuntimeException('Failed to fetch secrets from Infisical: ' . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if required Infisical config variables are present.
|
||||||
|
*/
|
||||||
|
private function hasInfisicalConfig(): bool
|
||||||
|
{
|
||||||
|
return !empty($this->values['INFISICAL_URL']) &&
|
||||||
|
!empty($this->values['INFISICAL_ENV']) &&
|
||||||
|
!empty($this->values['INFISICAL_PROJECT_ID']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $key, mixed $default = null): mixed
|
||||||
|
{
|
||||||
|
return $this->values[$key] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all configuration values.
|
||||||
|
*/
|
||||||
|
public function all(): array
|
||||||
|
{
|
||||||
|
return $this->values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
2
tests/_bootstrap.php
Normal file
2
tests/_bootstrap.php
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?php
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
26
tests/_support/UnitTester.php
Normal file
26
tests/_support/UnitTester.php
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inherited Methods
|
||||||
|
* @method void wantTo($text)
|
||||||
|
* @method void wantToTest($text)
|
||||||
|
* @method void execute($callable)
|
||||||
|
* @method void expectTo($prediction)
|
||||||
|
* @method void expect($prediction)
|
||||||
|
* @method void amGoingTo($argumentation)
|
||||||
|
* @method void am($role)
|
||||||
|
* @method void lookForwardTo($achieveValue)
|
||||||
|
* @method void comment($description)
|
||||||
|
* @method void pause($vars = [])
|
||||||
|
*
|
||||||
|
* @SuppressWarnings(PHPMD)
|
||||||
|
*/
|
||||||
|
class UnitTester extends \Codeception\Actor
|
||||||
|
{
|
||||||
|
use _generated\UnitTesterActions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define custom actions here
|
||||||
|
*/
|
||||||
|
}
|
||||||
2073
tests/_support/_generated/UnitTesterActions.php
Normal file
2073
tests/_support/_generated/UnitTesterActions.php
Normal file
File diff suppressed because it is too large
Load diff
40
tests/unit/Core/ConfigTest.php
Normal file
40
tests/unit/Core/ConfigTest.php
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\unit\Core;
|
||||||
|
|
||||||
|
use SiteWeaver\Core\Config;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class ConfigTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testLoadsEnvironmentVariables(): void
|
||||||
|
{
|
||||||
|
$env = [
|
||||||
|
'APP_ENV' => 'testing',
|
||||||
|
'DB_HOST' => 'localhost',
|
||||||
|
'DEBUG' => true,
|
||||||
|
];
|
||||||
|
|
||||||
|
$config = new Config($env);
|
||||||
|
|
||||||
|
$this->assertSame('testing', $config->get('APP_ENV'));
|
||||||
|
$this->assertSame('localhost', $config->get('DB_HOST'));
|
||||||
|
$this->assertTrue($config->get('DEBUG'));
|
||||||
|
|
||||||
|
// unknown key returns null
|
||||||
|
$this->assertNull($config->get('NON_EXISTENT_KEY'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllReturnsMergedArray(): void
|
||||||
|
{
|
||||||
|
$env = [
|
||||||
|
'FOO' => 'bar',
|
||||||
|
'BAZ' => 123,
|
||||||
|
];
|
||||||
|
|
||||||
|
$config = new Config($env);
|
||||||
|
|
||||||
|
$this->assertSame($env, $config->all());
|
||||||
|
}
|
||||||
|
}
|
||||||
7
tests/unit/UnitTester.php
Normal file
7
tests/unit/UnitTester.php
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\unit;
|
||||||
|
|
||||||
|
class UnitTester extends \Codeception\Actor {
|
||||||
|
use \Codeception\AssertThrows;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue