Skip to content

Commit

Permalink
Refactor janus to be a proper application
Browse files Browse the repository at this point in the history
  • Loading branch information
apfelbox committed Jun 4, 2024
1 parent accee2b commit d95d92f
Show file tree
Hide file tree
Showing 9 changed files with 456 additions and 320 deletions.
327 changes: 7 additions & 320 deletions bin/janus
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
#!/usr/bin/env php
<?php

use Janus\Command\InitializeCommand;
use Janus\Command\LegacyCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;

require_once $_composer_autoload_path ?? (__DIR__ . '/../vendor/autoload.php');

Expand All @@ -18,316 +14,7 @@ $output = new ConsoleOutput();

$allowedCommands = ["init-symfony", "init-library"];


(new SingleCommandApplication())
->setName("Janus")
->addArgument(
"command",
InputArgument::REQUIRED,
suggestedValues: $allowedCommands,
)
->setCode(
function (InputInterface $input, OutputInterface $output) : int
{
$io = new SymfonyStyle($input, $output);
$io->title("Janus");
$command = $input->getArgument("command");

try
{
return match ($command)
{
"init-symfony" => initializeSymfony($io),
"init-library" => initializeLibrary($io),
null => printUsage(),
default => printError($io, "Unknown command: {$command}"),
};
}
catch (Throwable $exception)
{
$io->error("Running janus failed: {$exception->getMessage()}");
return 2;
}
}
)
->run();

return;


// region Commands
/**
*/
function initializeSymfony (SymfonyStyle $io) : int
{
$io->writeln("• Copying config files to the project...");
copyFilesIntoProject($io, "symfony");

$io->writeln("• Updating <fg=yellow>composer.json</>...");
addToProjectComposerJson([
"config" => [
"allow-plugins" => [
"bamarni/composer-bin-plugin" => true,
],
"sort-packages" => true,
],
"extra" => [
"bamarni-bin" => [
"bin-links" => false,
"forward-command" => true,
],
],
"require-dev" => [
"bamarni/composer-bin-plugin" => "^1.8",
"roave/security-advisories" => "dev-latest",
],
]);
updateProjectComposerJsonScripts("fix-lint", [
"normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --ansi",
"cs-fixer" => "vendor-bin/cs-fixer/vendor/bin/php-cs-fixer fix --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi",
]);
updateProjectComposerJsonScripts("lint", [
"lint:yaml" => "bin/console lint:yaml config --parse-tags",
"lint:twig" => "bin/console lint:twig templates",
"normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --dry-run --ansi",
"cs-fixer" => "vendor-bin/cs-fixer/vendor/bin/php-cs-fixer fix --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --no-interaction --ansi",
]);
updateProjectComposerJsonScripts("test", [
"phpstan" => "vendor-bin/phpstan/vendor/bin/phpstan analyze -c phpstan.neon . --ansi",
]);

$io->writeln("• Running <fg=blue>composer update</>...");
runComposerInProject($io, ["update"]);

return 0;
}


/**
*/
function initializeLibrary (SymfonyStyle $io) : int
{
$io->writeln("• Copying config files to the project...");
copyFilesIntoProject($io, "library");

$io->writeln("• Updating <fg=yellow>composer.json</>...");
addToProjectComposerJson([
"config" => [
"allow-plugins" => [
"bamarni/composer-bin-plugin" => true,
],
"sort-packages" => true,
],
"extra" => [
"bamarni-bin" => [
"bin-links" => false,
"forward-command" => true,
],
],
"require-dev" => [
"bamarni/composer-bin-plugin" => "^1.8",
"roave/security-advisories" => "dev-latest",
],
]);
updateProjectComposerJsonScripts("fix-lint", [
"normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --ansi",
"cs-fixer" => "vendor-bin/cs-fixer/vendor/bin/php-cs-fixer fix --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi",
]);
updateProjectComposerJsonScripts("lint", [
"normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --dry-run --ansi",
"cs-fixer" => "vendor-bin/cs-fixer/vendor/bin/php-cs-fixer fix --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --no-interaction --ansi",
]);
updateProjectComposerJsonScripts("test", [
"phpstan" => "vendor-bin/phpstan/vendor/bin/phpstan analyze -c phpstan.neon . --ansi",
]);

$io->writeln("• Running <fg=blue>composer update</>...");
runComposerInProject($io, ["update"]);

return 0;
}
// endregion


// region CommandHelpers
function copyFilesIntoProject (SymfonyStyle $io, string $directory) : void
{
$sourceDir = __DIR__ . "/../_init/{$directory}/.";

runProcessInProject($io, [
"cp",
"-a",
$sourceDir,
".",
]);
}

/**
*
*/
function runProcessInProject (SymfonyStyle $io, array $cmd) : void
{
$io->writeln(sprintf(
"$> Running command <fg=blue>%s</>",
implode(" ", $cmd),
));

$process = new Process(
$cmd,
cwd: getcwd(),
);
$process->mustRun();

$output = trim(sprintf("%s\n%s", $process->getErrorOutput(), $process->getOutput()));

if ("" !== $output)
{
$io->block(
trim(sprintf("%s\n%s", $process->getErrorOutput(), $process->getOutput())),
prefix: "",
);
}
}


function runComposerInProject (SymfonyStyle $io, array $cmd) : void
{
$finder = new ExecutableFinder();
$composer = $finder->find("composer");

if (null === $composer)
{
throw new Exception("Could not find locally installed composer");
}

array_unshift($cmd, $composer);
$cmd[] = "--ansi";
runProcessInProject($io, $cmd);
}

/**
*/
function addToProjectComposerJson (array $config) : void
{
$filePath = getcwd() . "/composer.json";

$jsonContent = json_decode(
file_get_contents($filePath),
true,
flags: JSON_THROW_ON_ERROR
);

$jsonContent = array_replace_recursive($jsonContent, $config);
file_put_contents(
$filePath,
json_encode(
$jsonContent,
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
JSON_THROW_ON_ERROR
),
);
}

/**
* Takes a list of scripts to replace and updates the configs.
*
* The $scripts array has a keywords as key, and replaces the line containing that keyword.
* So for example the key "phpunit" would replace the line that contains "phpunit".
* If there are multiple lines matching, all will be replaced.
* If there are no lines matching, the call will just be appended.
*
*
* @param string $key The scripts key to update.
* @param array<string, string> $scripts The scripts to replace.
*/
function updateProjectComposerJsonScripts (string $key, array $scripts) : void
{
$filePath = getcwd() . "/composer.json";

$jsonContent = json_decode(
file_get_contents($filePath),
true,
flags: JSON_THROW_ON_ERROR
);

$existingScripts = $jsonContent["scripts"][$key] ?? [];
// keep existing scripts
$result = [];

foreach ($existingScripts as $line)
{
foreach ($scripts as $replacedKeyword => $newLine)
{
if (str_contains($line, $replacedKeyword))
{
continue 2;
}
}

// append the line if no replacement matches
$result[] = $line;
}

// append all new lines
foreach ($scripts as $newLine)
{
$result[] = $newLine;
}

$jsonContent["scripts"][$key] = $result;
file_put_contents(
$filePath,
json_encode(
$jsonContent,
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
JSON_THROW_ON_ERROR
),
);
}
// endregion


// region Output Helpers
/**
* Prints an error message + usage and returns an error status code
*/
function printError (SymfonyStyle $io, string $message) : int
{
global $allowedCommands;

$io->error($message);
$io->writeln("Allowed commands:");
$io->listing(
array_map(
static fn (string $command) => "<fg=yellow>{$command}</>",
$allowedCommands,
),
);

return 1;
}


/**
* Prints the usage and returns a success status code
*/
function printUsage () : int
{
echo " ~~~~~~~\n";
echo " Janus \n";
echo " ~~~~~~~\n";
echo "\n";
echo "Usage:\n";
echo " janus [command] [argument]\n";
echo "\n";
echo "\n";
echo "Commands:\n";
echo "\n";
echo " init-symfony .. to initialize a symfony application\n";
echo " init-library .. to initialize a library\n";
echo "\n";
echo "\n";

return 0;
}
// endregion
$application = new Application("Janus");
$application->add(new InitializeCommand());
$application->add(new LegacyCommand());
$application->run();
6 changes: 6 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
],
"require": {
"php": ">= 8.3",
"21torr/cli": "^1.2",
"symfony/console": "^7.0",
"symfony/process": "^7.0"
},
Expand All @@ -21,6 +22,11 @@
"bin": [
"bin/janus"
],
"autoload": {
"psr-4": {
"Janus\\": "src/"
}
},
"config": {
"sort-packages": true,
"allow-plugins": {
Expand Down
Loading

0 comments on commit d95d92f

Please sign in to comment.