Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog

## 0.1.1 — 2026-10-06

- Use stable Starter 1.1.8 for new projects instead of the removed development branch.
- Keep the requested version and verified commit fixed while loading repository catalogs. A catalog's default version is metadata; its repository and package identity must still match.
- Cover default and explicitly selected tagged templates without creating files during dry runs.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ sympress project:create [directory] [options]
- `--dev-package=vendor/name[:constraint]`
- `--no-suggested-packages`
- `--repository=https://github.com/SymPress/starter`
- `--template-version=1.0.x-dev`
- `--template-version=1.1.8`
- `--manifest=./sympress-cli.json`
- `--manifest=https://github.com/SymPress/starter`
- `--manifest-ref=<40-character-Git-SHA>`
Expand All @@ -183,7 +183,7 @@ Update options:
- `--type=website|app|microservice|commerce`
- `--template=sympress-starter`
- `--repository=https://github.com/SymPress/starter`
- `--template-version=1.0.x-dev`
- `--template-version=1.1.8`
- `--manifest=./sympress-cli.json`
- `--manifest-ref=<40-character-Git-SHA>`
- `--no-remote-manifest`
Expand All @@ -202,6 +202,11 @@ When the starter changes, update this manifest in the starter repository. The CL
will pick it up on the next run, so setup commands, project types and package
suggestions can evolve with the repository instead of being hardcoded in the CLI.

New projects default to the stable Starter 1.1.8 tag. An explicitly selected
version and its verified commit remain fixed even when the catalog at that commit
advertises a different default. Repository and Composer package identities must
still match before any project is materialized.

Example:

```json
Expand All @@ -215,7 +220,7 @@ Example:
"packageName": "sympress/starter",
"repositoryUrl": "https://github.com/SymPress/starter",
"description": "DDEV-ready SymPress WordPress starter.",
"defaultVersion": "1.0.x-dev",
"defaultVersion": "1.1.8",
"setupCommand": ["bin/console", "setup", "{project_slug}"]
}
],
Expand Down
7 changes: 4 additions & 3 deletions src/Command/UpdateProjectCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,14 @@ private function catalogs(InputInterface $input, ProjectMetadata $metadata): arr
foreach ($remoteManifest->templates as $candidate) {
if (
$candidate->id === $selected->id && ($candidate->repositoryUrl !== $selected->repositoryUrl
|| $candidate->packageName !== $selected->packageName
|| $candidate->defaultVersion !== $selected->defaultVersion)
|| $candidate->packageName !== $selected->packageName)
) {
throw new RuntimeException('Snapshot manifest cannot redirect the selected template.');
}
}
return [...$this->applyManifest($remoteManifest, $templates, $profiles, $packages), $revision];
[$templates, $profiles, $packages] = $this->applyManifest($remoteManifest, $templates, $profiles, $packages);
$templates = $templates->withTemplates([$templates->get($selected->id)->withVersion($version)]);
return [$templates, $profiles, $packages, $revision];
}

private function level(InputInterface $input, SymfonyStyle $io): string
Expand Down
3 changes: 1 addition & 2 deletions src/Generator/ProjectGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,10 @@ public function generate(ProjectConfiguration $configuration, SymfonyStyle $io):
if (
$candidate->repositoryUrl !== $setupTemplate->repositoryUrl
|| $candidate->packageName !== $setupTemplate->packageName
|| $candidate->defaultVersion !== $setupTemplate->defaultVersion
) {
throw new RuntimeException('Snapshot manifest cannot redirect the selected template.');
}
$setupTemplate = $candidate;
$setupTemplate = $candidate->withVersion($configuration->templateVersion ?: $setupTemplate->defaultVersion);
}
$this->filesystem->remove($configuration->directory . '/.git');
$io->section('Apply initial configuration');
Expand Down
7 changes: 4 additions & 3 deletions src/IO/ProjectQuestionnaire.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,13 @@ public function ask(InputInterface $input, OutputInterface $output, string $cwd)
$updatedTemplate = $templates->get($template->id);
if (
$updatedTemplate->repositoryUrl !== $template->repositoryUrl
|| $updatedTemplate->defaultVersion !== $template->defaultVersion
|| $updatedTemplate->packageName !== $template->packageName
) {
throw new \RuntimeException('A manifest cannot redirect the selected template repository or version.');
}
$template = $updatedTemplate;
// Catalog metadata may advertise a different default. The version
// already resolved to a SHA remains the user's selected version.
$template = $updatedTemplate->withVersion($version);

if ($this->emptyToNull($input->getOption('repository')) === null) {
$templateRepository = $template->repositoryUrl;
Expand Down Expand Up @@ -125,7 +126,7 @@ public function ask(InputInterface $input, OutputInterface $output, string $cwd)
runSetup: $this->runSetup($input, $io, $interactive),
dryRun: (bool) $input->getOption('dry-run'),
composerBinary: (string) $input->getOption('composer-bin'),
templateVersion: $this->emptyToNull($input->getOption('template-version')),
templateVersion: $version,
templateRepository: $templateRepository,
templateRevision: $revision,
allowTemplateExecution: (bool) $input->getOption('allow-template-execution'),
Expand Down
15 changes: 14 additions & 1 deletion src/Model/TemplateDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,24 @@ public function __construct(
public string $packageName,
public string $repositoryUrl,
public string $description,
public string $defaultVersion = '1.0.x-dev',
public string $defaultVersion = '1.1.8',
public array $setupCommand = ['bin/console', 'setup', '{project_slug}'],
) {
}

public function withVersion(string $version): self
{
return new self(
$this->id,
$this->label,
$this->packageName,
$this->repositoryUrl,
$this->description,
$version,
$this->setupCommand,
);
}

public function packageSpec(?string $version = null): string
{
$selectedVersion = $version ?: $this->defaultVersion;
Expand Down
2 changes: 1 addition & 1 deletion src/Repository/RepositoryManifestParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ private function template(mixed $template, string $path): TemplateDefinition
repositoryUrl: $repositoryUrl,
description: $this->string($template, 'description', $path)
?? 'Repository-provided starter template.',
defaultVersion: $this->string($template, 'defaultVersion', $path) ?? '1.0.x-dev',
defaultVersion: $this->string($template, 'defaultVersion', $path) ?? '1.1.8',
setupCommand: $setupCommand,
);
}
Expand Down
46 changes: 46 additions & 0 deletions tests/Command/CreateProjectCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,55 @@
use PHPUnit\Framework\TestCase;
use SymPress\Cli\Command\CreateProjectCommand;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\Process;

final class CreateProjectCommandTest extends TestCase
{
public function testDefaultAndExplicitReleaseKeepTheirResolvedRevisionDespiteManifestDefault(): void
{
$root = $this->temporaryDirectory();
$repository = $root . '/repository';
mkdir($repository . '/.sympress', 0700, true);
$git = static function (array $arguments) use ($repository): string {
$process = new Process(['git', '-C', $repository, ...$arguments]);
$process->setEnv(['GIT_DIR' => false, 'GIT_WORK_TREE' => false, 'GIT_INDEX_FILE' => false]);
$process->mustRun();
return trim($process->getOutput());
};
try {
$git(['init', '-b', 'main']);
$git(['config', 'user.name', 'Fixture']);
$git(['config', 'user.email', 'fixture@example.test']);
file_put_contents($repository . '/composer.json', '{"name":"sympress/starter"}');
file_put_contents($repository . '/.sympress/cli.json', json_encode([
'schemaVersion' => 1, 'templates' => [[
'id' => 'sympress-starter', 'label' => 'Starter', 'packageName' => 'sympress/starter',
'repositoryUrl' => 'https://github.com/SymPress/starter', 'description' => 'Fixture',
'defaultVersion' => '1.0.0',
]],
], JSON_THROW_ON_ERROR));
$git(['add', '.']);
$git(['commit', '-m', 'Release with older manifest default']);
$sha = $git(['rev-parse', 'HEAD']);
$git(['tag', 'v1.1.8']);
$git(['tag', 'v1.1.6']);
foreach ([null, '1.1.6'] as $version) {
$options = ['directory' => $root . '/project', '--repository' => $repository,
'--dry-run' => true, '--no-setup' => true, '--name' => 'Fixture'];
if ($version !== null) {
$options['--template-version'] = $version;
}
$tester = new CommandTester(new CreateProjectCommand());
self::assertSame(0, $tester->execute($options, ['interactive' => false]), $tester->getDisplay());
self::assertStringContainsString('sympress/starter:' . ($version ?? '1.1.8'), $tester->getDisplay());
self::assertStringContainsString($sha, $tester->getDisplay());
self::assertDirectoryDoesNotExist($root . '/project');
}
} finally {
new Filesystem()->remove($root);
}
}
public function testDryRunUsesManifestProvidedCatalogEntries(): void
{
$directory = $this->temporaryDirectory();
Expand Down
Loading