Milestones 0, 1, 2
Some checks are pending
CI / build_and_test (push) Waiting to run

This commit is contained in:
Funky Waddle 2026-09-09 18:58:20 -05:00
parent 1d37af7282
commit c3ec2b86d9
15 changed files with 2328 additions and 16 deletions

23
.forgejo/workflows/ci.yml Normal file
View 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

View file

@ -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
View file

@ -0,0 +1,2 @@
<?php
require __DIR__ . '/vendor/autoload.php';

17
codeception.yml Normal file
View 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

View file

@ -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
View file

@ -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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,2 @@
<?php
require __DIR__ . '/../vendor/autoload.php';

View 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
*/
}

File diff suppressed because it is too large Load diff

View 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());
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace Tests\unit;
class UnitTester extends \Codeception\Actor {
use \Codeception\AssertThrows;
}