Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f63b0e0
first attempt at future proofing architecture
defunctl Sep 4, 2026
af7347a
Removed configured provider, add docblocks and organize classes better
defunctl Sep 4, 2026
1bf1a88
Fix incorrect container docs
defunctl Sep 4, 2026
692c175
Add missing import
defunctl Sep 4, 2026
f932ce5
Update namespace to Plugin, make examples use full providers
defunctl Sep 4, 2026
32dea02
Clarify and simplify the upgrade to 2.0 process
defunctl Sep 4, 2026
b0c7e5b
Update Configuration interface + docs
defunctl Sep 4, 2026
4c1c783
Normalize placeholder `YourPlugin` namespace to `Plugin` across all docs
defunctl Sep 4, 2026
11a942b
Rename to plugin.php
defunctl Sep 4, 2026
069399e
Remove redundant install instructions in overview
defunctl Sep 4, 2026
7a877a4
Move container/app testing into its own page
defunctl Sep 4, 2026
2844ddb
Show how to make a create_container() helper instead of building it m…
defunctl Sep 4, 2026
7bc1b28
Remove useless continue sections, we have nav already
defunctl Sep 4, 2026
2d367e5
Show how we get the default container in tests
defunctl Sep 4, 2026
7ebe49e
Add deterministic migration ordering and improve command option descr…
defunctl Sep 4, 2026
c5f9321
Refactor database schemas around migration-owned blueprints
defunctl Sep 4, 2026
3ebbd2c
Update AGENTS.md
defunctl Sep 7, 2026
8d10e42
Add more logic reasoning to AGENTS.md
defunctl Sep 7, 2026
e8efb27
Add pre-configured LockOperation and PredisConnectionProvider
defunctl Sep 7, 2026
2061ac7
Refactor how logs are configured, allowing configurable stacks
defunctl Sep 7, 2026
1837114
Add additional tests + improve shutdown documentation
defunctl Sep 7, 2026
f3873bf
Reject newline characters in ULID
defunctl Sep 7, 2026
99f2e03
Improve pipeline tests + docs
defunctl Sep 7, 2026
0e5ca8d
Update wp-cli docs to explain the prefix
defunctl Sep 7, 2026
d3684be
Clarify database docs
defunctl Sep 7, 2026
b03af6a
Fix schema verification and normalize decimal declarations
defunctl Sep 7, 2026
ca9fa17
Skip completed index replacements and verify migration recovery
defunctl Sep 7, 2026
4dd5988
Clarify application table naming in database documentation
defunctl Sep 8, 2026
c87ace4
Simplify schema column comparison
defunctl Sep 8, 2026
c385547
Allow log channels to configure their own streams and stack should co…
defunctl Sep 8, 2026
40e7b3b
update examples in docs
defunctl Sep 8, 2026
d887968
Replace useless methods with a NodeFinder instance
defunctl Sep 8, 2026
463f67b
Allow subpackages to be discovered in multiple ways, don't try to sca…
defunctl Sep 8, 2026
73ba02d
Remove the `close-pull-request.yml` workflows and requirements as we …
defunctl Sep 8, 2026
ae4353e
Remove inheritance from Pipeline
defunctl Sep 8, 2026
ee2fec7
Allow global configuration for generator namespace paths (where the f…
defunctl Sep 8, 2026
8af359f
Load project tooling and custom generators through Foundation CLI
defunctl Sep 8, 2026
304c610
Move monorepo maintenance commands into dev tooling
defunctl Sep 8, 2026
cf3eda5
Preserve disabled-command behavior when checking CLI collisions
defunctl Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/bin/repo-map.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail

# Builds a JSON map of the sub-repos name and directory relative to the "src/" dir
# for use in splitting monorepo packages via GitHub Actions.
Expand All @@ -19,7 +20,7 @@
# ]

# Check if $GITHUB_WORKSPACE is defined
if [ -z "$GITHUB_WORKSPACE" ]; then
if [ -z "${GITHUB_WORKSPACE:-}" ]; then
# If not defined, assign a specific relative path
root=$( git rev-parse --show-toplevel )
else
Expand All @@ -30,8 +31,13 @@ fi
# Use jq to generate the JSON array directly without line breaks
packages_json=$(find "$root/src" -mindepth 2 -maxdepth 2 \( -name composer.json -o -name package.json \) -print0 |
while IFS= read -r -d $'\0' file; do
# Each directory is one split package; Composer owns mixed PHP/npm packages.
if [[ "$(basename "$file")" == "package.json" && -f "$(dirname "$file")/composer.json" ]]; then
continue
fi

# Extract the package name and directory
package_name=$(jq -r '.name' < "$file" | sed -E 's/^@?stellarwp\///')
package_name=$(jq -r '.name' < "$file" | sed -E 's/^@?stellarwp\///') || exit "$?"
relative_directory=$(realpath --relative-to="$root/src" "$(dirname "$file")")

# Build the JSON object for each package
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/check-for-required-files.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ jobs:
# Find all subdirectories in src/ and look for required files
for folder in */; do
file_paths=(
"$folder.github/workflows/close-pull-request.yml"
"$folder/.gitattributes"
"$folder/.gitignore"
)
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/monorepo-split.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ jobs:
# Dynamically build an array of JSON objects that each contain "name" and "directory" properties.
- name: Set repo matrix
id: set-matrix
run: echo "matrix=$(./.github/bin/repo-map.sh)" >> "$GITHUB_OUTPUT"
run: |
matrix=$(./.github/bin/repo-map.sh)
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"

split:
needs: list-repos
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ jobs:
composer run coverage:phpcov-install
composer run coverage:prepare
"${SLIC_BIN}" pcov on --yes
"${SLIC_BIN}" run unit --coverage coverage/unit.cov --ext DotReporter
"${SLIC_BIN}" composer run test:coverage:unit -- --ext DotReporter
"${SLIC_BIN}" run feature --coverage coverage/feature.cov --ext DotReporter
"${SLIC_BIN}" run redis --coverage coverage/redis.cov --ext DotReporter
"${SLIC_BIN}" run integration --coverage coverage/integration.cov --ext DotReporter
Expand Down
140 changes: 80 additions & 60 deletions AGENTS.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ composer run foundation -- package:create <Package>

The package argument can be a new package component such as `WPCli`, an existing package directory, short package name, or Composer package name, for example `Log`, `foundation-log`, or `stellarwp/foundation-log`.

If the package does not exist yet, the command asks whether to create the local scaffold in `src/<Package>` and asks for the Composer package name with a default such as `stellarwp/foundation-wpcli`. The scaffold includes the required `composer.json`, `README.md`, `.gitattributes`, `.gitignore`, and `close-pull-request.yml` files. After scaffolding, the command runs `composer monorepo merge` so the root `composer.json` includes the new package.
If the package does not exist yet, the command asks whether to create the local scaffold in `src/<Package>` and asks for the Composer package name with a default such as `stellarwp/foundation-wpcli`. The scaffold includes the required `composer.json`, `README.md`, `.gitattributes`, and `.gitignore` files. After scaffolding, the command runs `composer monorepo merge` so the root `composer.json` includes the new package.

The command runs as a dry run by default. It validates the required split package files, prints the target repository name and description, and shows the GitHub CLI commands it will run.

Expand All @@ -171,7 +171,7 @@ The command runs as a dry run by default. It validates the required split packag
composer run foundation -- package:create <Package> --apply
```

The command creates the `stellarwp/foundation-<package>` repository with the standard `[READ ONLY]` description and disables issues, wiki, projects, and pull requests. The package's `close-pull-request.yml` workflow is still included as a fallback guard for read-only split repositories.
The command creates the `stellarwp/foundation-<package>` repository with the standard `[READ ONLY]` description and disables issues, wiki, projects, and pull requests.

#### Generating WP-CLI Commands

Expand Down
1 change: 1 addition & 0 deletions codeception.dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ coverage:
enabled: true
include:
- src/*
- dev/Cli/*
paths:
tests: tests
log: tests/_output
Expand Down
10 changes: 7 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"lucatume/di52": ">=4.1",
"monolog/monolog": "^2.11",
"nikic/php-parser": ">=5.0 <6.0",
"psr/container": ">=1.1",
"psr/log": ">=1.0",
"stellarwp/container-contract": "^1.1",
"symfony/console": ">=5.4",
Expand Down Expand Up @@ -72,6 +73,7 @@
},
"autoload-dev": {
"psr-4": {
"StellarWP\\Foundation\\Dev\\": "dev/",
"StellarWP\\Foundation\\Tests\\Feature\\": "tests/Feature/",
"StellarWP\\Foundation\\Tests\\Integration\\": "tests/integration/",
"StellarWP\\Foundation\\Tests\\Redis\\": "tests/redis/",
Expand All @@ -96,7 +98,7 @@
}
},
"scripts": {
"foundation": "@php src/Cli/bin/foundation",
"foundation": "@php dev/bin/foundation",
"monorepo": "@php vendor/bin/monorepo-builder",
"test": "@php vendor/bin/phpunit -c phpunit.xml.dist --colors=always -d memory_limit=2G",
"test:unit": "@test --testsuite Unit",
Expand All @@ -109,6 +111,7 @@
"test:wpunit": "slic run wpunit",
"test:wpcli": "slic run wpcli",
"test:coverage": "@test:coverage:split",
"test:coverage:unit": "@php -d pcov.directory=. vendor/bin/codecept -c codeception.slic.yml run unit --coverage coverage/unit.cov",
"test:coverage-html": "@test:coverage-html:split",
"test:clean": "rm -f clover.xml tests/_output/coverage/*.cov && rm -rf ./coverage",
"coverage:phpcov-install": [
Expand All @@ -121,13 +124,13 @@
"test:coverage:split": [
"@coverage:phpcov-install",
"@coverage:prepare",
"slic pcov on --yes && slic run unit --coverage coverage/unit.cov && slic run feature --coverage coverage/feature.cov && slic run redis --coverage coverage/redis.cov && slic run integration --coverage coverage/integration.cov && slic run wpunit --coverage coverage/wpunit.cov && slic run wpcli --coverage coverage/wpcli.cov; rc=$?; slic pcov off; exit $rc",
"slic pcov on --yes && slic composer run test:coverage:unit && slic run feature --coverage coverage/feature.cov && slic run redis --coverage coverage/redis.cov && slic run integration --coverage coverage/integration.cov && slic run wpunit --coverage coverage/wpunit.cov && slic run wpcli --coverage coverage/wpcli.cov; rc=$?; slic pcov off; exit $rc",
"slic composer run coverage:merge"
],
"test:coverage-html:split": [
"@coverage:phpcov-install",
"@coverage:prepare",
"slic pcov on --yes && slic run unit --coverage coverage/unit.cov && slic run feature --coverage coverage/feature.cov && slic run redis --coverage coverage/redis.cov && slic run integration --coverage coverage/integration.cov && slic run wpunit --coverage coverage/wpunit.cov && slic run wpcli --coverage coverage/wpcli.cov; rc=$?; slic pcov off; exit $rc",
"slic pcov on --yes && slic composer run test:coverage:unit && slic run feature --coverage coverage/feature.cov && slic run redis --coverage coverage/redis.cov && slic run integration --coverage coverage/integration.cov && slic run wpunit --coverage coverage/wpunit.cov && slic run wpcli --coverage coverage/wpcli.cov; rc=$?; slic pcov off; exit $rc",
"slic composer run coverage:merge-html"
],
"analyze": "@php vendor/bin/phpstan analyse --ansi --memory-limit 2G",
Expand All @@ -148,6 +151,7 @@
"test:wpunit": "Run the WordPress-loaded wpunit suite through SLIC.",
"test:wpcli": "Run the shared WP-CLI command suite through SLIC.",
"test:coverage": "Generate merged Clover coverage from split SLIC suite artifacts.",
"test:coverage:unit": "Collect unit coverage for src and dev/Cli. Run through SLIC with PCOV enabled.",
"test:coverage-html": "Generate merged Clover and HTML coverage from split SLIC suite artifacts.",
"test:clean": "Remove generated test coverage reports and serialized coverage artifacts.",
"coverage:phpcov-install": "Download the pinned phpcov PHAR used to merge split coverage artifacts.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package\Contracts;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package\Contracts;

use StellarWP\Foundation\Cli\Commands\Package\PackageRepositoryPlan;
use StellarWP\Foundation\Dev\Cli\Commands\Package\PackageRepositoryPlan;

/**
* Creates or configures the external repository for a Foundation split package.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package;

use RuntimeException;
use StellarWP\Foundation\Cli\Commands\Package\Contracts\PackageRepositoryCreator;
use StellarWP\Foundation\Cli\Process\Contracts\ProcessRunner;
use StellarWP\Foundation\Cli\Process\ShellCommand;
use StellarWP\Foundation\Dev\Cli\Commands\Package\Contracts\PackageRepositoryCreator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -38,7 +38,7 @@ public function __construct(

protected function configure(): void {
$this->setDescription('Create and configure a read-only GitHub sub-repository for a Foundation split package.')
->addArgument('package', InputArgument::REQUIRED, 'Package directory, package short name, or Composer package name.')
->addArgument('package', InputArgument::REQUIRED, 'Package directory, short name, repository name, or full Composer or npm package name.')
->addOption('apply', null, InputOption::VALUE_NONE, 'Run the generated GitHub actions. Without this option, the command is a dry run.');
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package;

use RuntimeException;
use StellarWP\Foundation\Cli\Commands\Package\Contracts\PackageRepositoryCreator;
use StellarWP\Foundation\Cli\Process\Contracts\ProcessRunner;
use StellarWP\Foundation\Cli\Process\ShellCommand;
use StellarWP\Foundation\Dev\Cli\Commands\Package\Contracts\PackageRepositoryCreator;

/**
* GitHub CLI implementation for creating Foundation split repositories.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package;

/**
* Resolved Foundation split package metadata from the monorepo.
Expand All @@ -15,11 +15,14 @@ public function __construct(
public string $component,
public string $directory,
public string $path,
public string $composerPath
public string $manifestPath
) {
}

/**
* Return the split repository name for a Composer or npm package.
*/
public function repoName(): string {
return str_replace('stellarwp/', '', $this->name);
return str_replace(['@stellarwp/', 'stellarwp/'], '', $this->name);
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package;

/**
* Validates that a split package has the files required for repository creation.
*
* Use this before creating the external repository so missing package metadata,
* read-only warnings, or pull-request closing workflow files are caught early.
* Use this before creating the external repository to identify missing package
* manifests, README files, and Git configuration files.
*/
final class PackageFilesValidator
{
/**
* @var list<string>
*/
private const array REQUIRED_FILES = [
'composer.json',
'README.md',
'.gitattributes',
'.gitignore',
'.github/workflows/close-pull-request.yml',
];

/**
* List the missing manifest and shared split repository files.
*
* @return list<string>
*/
public function missingFiles(Package $package): array {
$missingFiles = [];

foreach (self::REQUIRED_FILES as $requiredFile) {
foreach ([basename($package->manifestPath), ...self::REQUIRED_FILES] as $requiredFile) {
if (! file_exists($package->path . '/' . $requiredFile)) {
$missingFiles[] = $requiredFile;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package;

/**
* Repository creation plan derived from a resolved Foundation package.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package;

/**
* Builds the standard GitHub repository plan for a Foundation split package.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package;

use JsonException;
use RuntimeException;

/**
* Finds Foundation split packages from user-friendly command input.
*
* Use this in package commands that accept a directory name, short package name,
* repository name, or full Composer package name and need the matching package.
* repository name, or full Composer or npm package name and need the matching package.
*/
final readonly class PackageResolver
{
Expand All @@ -17,6 +18,12 @@ public function __construct(
) {
}

/**
* Resolve an existing split package by its name or directory.
*
* @throws RuntimeException When no Foundation package matches the input.
* @throws JsonException When a package manifest contains invalid JSON.
*/
public function resolve(string $input): Package {
$normalizedInput = $this->normalizeInput($input);

Expand All @@ -33,15 +40,21 @@ public function resolve(string $input): Package {
* @return list<Package>
*/
private function packages(): array {
$composerPaths = glob($this->rootPath . '/src/*/composer.json') ?: [];
$manifestPaths = glob($this->rootPath . '/src/*/{composer,package}.json', GLOB_BRACE) ?: [];
$packages = [];

foreach ($composerPaths as $composerPath) {
$packagePath = dirname($composerPath);
$composer = json_decode((string) file_get_contents($composerPath), true, 512, JSON_THROW_ON_ERROR);
$name = $composer['name'] ?? '';
foreach ($manifestPaths as $manifestPath) {
$packagePath = dirname($manifestPath);

// A Composer package may also have an npm manifest for frontend tooling.
if (basename($manifestPath) === 'package.json' && is_file($packagePath . '/composer.json')) {
continue;
}

$manifest = json_decode((string) file_get_contents($manifestPath), true, 512, JSON_THROW_ON_ERROR);
$name = $manifest['name'] ?? '';

if (! is_string($name) || ! str_starts_with($name, 'stellarwp/foundation-')) {
if (! is_string($name) || ! preg_match('#^@?stellarwp/foundation-#', $name)) {
continue;
}

Expand All @@ -50,7 +63,7 @@ private function packages(): array {
component: basename($packagePath),
directory: 'src/' . basename($packagePath),
path: $packagePath,
composerPath: $composerPath
manifestPath: $manifestPath
);
}

Expand All @@ -59,6 +72,9 @@ private function packages(): array {

private function matches(Package $package, string $input): bool {
return in_array($input, [
$this->normalizeInput($package->directory),
$this->normalizeInput('./' . $package->directory),
$this->normalizeInput($package->path),
$this->normalizeInput($package->component),
$this->normalizeInput($package->name),
$this->normalizeInput($package->repoName()),
Expand All @@ -67,6 +83,6 @@ private function matches(Package $package, string $input): bool {
}

private function normalizeInput(string $input): string {
return strtolower(trim($input));
return strtolower(rtrim(trim($input), '/'));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php declare(strict_types=1);

namespace StellarWP\Foundation\Cli\Commands\Package;
namespace StellarWP\Foundation\Dev\Cli\Commands\Package;

/**
* Result of creating or completing a Foundation split package scaffold.
Expand Down
Loading