This commit is contained in:
parent
1d37af7282
commit
c3ec2b86d9
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:
|
||||
# Milestone 0: Project Foundation & Setup
|
||||
milestone_0_project_foundation:
|
||||
status: TODO
|
||||
status: COMPLETE
|
||||
description: >-
|
||||
Set up the project structure, Composer dependencies, autoloading,
|
||||
and basic bootstrap files. This establishes the base for all subsequent
|
||||
|
|
@ -32,7 +32,7 @@ milestones:
|
|||
|
||||
# Milestone 1: Configuration & Environment Management
|
||||
milestone_1_configuration:
|
||||
status: TODO
|
||||
status: COMPLETE
|
||||
description: >-
|
||||
Implement environment configuration loading, Infisical integration,
|
||||
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",
|
||||
"guzzlehttp/guzzle": "^7.8",
|
||||
"symfony/http-foundation": "^6.3",
|
||||
"infisical/php-sdk": "^0.0.2"
|
||||
"infisical/php-sdk": "^0.0.2",
|
||||
"ext-dom": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^5.3",
|
||||
|
|
@ -27,12 +28,17 @@
|
|||
"autoload": {
|
||||
"psr-4": {
|
||||
"SiteWeaver\\": "src/Site/",
|
||||
"Intervention\\": "src/Intervention/",
|
||||
"Tests\\": "tests/"
|
||||
"SiteWeaver\\Plugins\\": "site/plugins/",
|
||||
"SiteWeaver\\Themes\\": "site/themes/",
|
||||
"SiteWeaver\\AdminThemes\\": "site/admin_themes/",
|
||||
"Intervention\\": "src/Intervention/"
|
||||
},
|
||||
"classmap": [
|
||||
"src/Intervention/Image/ImageManager.php"
|
||||
]
|
||||
"classmap": []
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"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",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1976fa1898ee5b599738fe40055ea1be",
|
||||
"content-hash": "f9abfc5b9d7ebdcca8c6410cf861c505",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
|
|
@ -62,16 +62,16 @@
|
|||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.394.10",
|
||||
"version": "3.394.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "3b7cbf17b8ba568f77546d1e5edd3f2071861c8f"
|
||||
"reference": "0a16c43453b30b5ec4025293f33028e299cf448e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3b7cbf17b8ba568f77546d1e5edd3f2071861c8f",
|
||||
"reference": "3b7cbf17b8ba568f77546d1e5edd3f2071861c8f",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0a16c43453b30b5ec4025293f33028e299cf448e",
|
||||
"reference": "0a16c43453b30b5ec4025293f33028e299cf448e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -153,9 +153,9 @@
|
|||
"support": {
|
||||
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
||||
"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",
|
||||
|
|
@ -10534,7 +10534,8 @@
|
|||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^8.4"
|
||||
"php": "^8.4",
|
||||
"ext-dom": "*"
|
||||
},
|
||||
"platform-dev": {},
|
||||
"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