Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jolicode/docker-starter
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 211b26fd9b82b53dc9c018d48b2afbccbfccf790
Choose a base ref
..
head repository: jolicode/docker-starter
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c0902e4e5a89c7ced763ba609adaebd1cbc7b051
Choose a head ref
215 changes: 121 additions & 94 deletions .castor/docker.php
Original file line number Diff line number Diff line change
@@ -6,17 +6,21 @@
use Castor\Attribute\AsOption;
use Castor\Attribute\AsTask;
use Castor\Context;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Process\Exception\ExceptionInterface;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface as HttpExceptionInterface;

use function Castor\cache;
use function Castor\capture;
use function Castor\context;
use function Castor\finder;
use function Castor\fs;
use function Castor\http_client;
use function Castor\io;
use function Castor\log;
use function Castor\open;
use function Castor\run;
use function Castor\variable;

@@ -32,9 +36,11 @@ function about(): void
io()->section('Available URLs for this project:');
$urls = [variable('root_domain'), ...variable('extra_domains')];

$payload = @file_get_contents(sprintf('http://%s:8080/api/http/routers', variable('root_domain')));
if ($payload) {
$routers = json_decode($payload, true);
try {
$routers = http_client()
->request('GET', sprintf('http://%s:8080/api/http/routers', variable('root_domain')))
->toArray()
;
$projectName = variable('project_name');
foreach ($routers as $router) {
if (!preg_match("{^{$projectName}-(.*)@docker$}", $router['name'])) {
@@ -49,40 +55,68 @@ function about(): void
$hosts = explode('`) || Host(`', $matches['hosts']);
$urls = [...$urls, ...$hosts];
}
} catch (HttpExceptionInterface) {
}
io()->listing(array_map(fn ($url) => "https://{$url}", $urls));

io()->listing(array_map(fn ($url) => "https://{$url}", array_unique($urls)));
}

#[AsTask(description: 'Opens the project in your browser', namespace: '')]
function open(): void
#[AsTask(description: 'Opens the project in your browser', namespace: '', aliases: ['open'])]
function open_project(): void
{
run(['open', 'https://' . variable('root_domain')], quiet: true);
open('https://' . variable('root_domain'));
}

#[AsTask(description: 'Builds the infrastructure', aliases: ['build'])]
function build(): void
{
function build(
?string $service = null,
?string $profile = null,
): void {
io()->title('Building infrastructure');

$userId = variable('user_id');
$phpVersion = variable('php_version');
$command = [];

if ($profile) {
$command[] = '--profile';
$command[] = $profile;
}

$command = [
...$command,
'build',
'--build-arg', "USER_ID={$userId}",
'--build-arg', "PHP_VERSION={$phpVersion}",
'--build-arg', 'USER_ID=' . variable('user_id'),
'--build-arg', 'PHP_VERSION=' . variable('php_version'),
'--build-arg', 'PROJECT_NAME=' . variable('project_name'),
];

if ($service) {
$command[] = $service;
}

docker_compose($command, withBuilder: true);
}

/**
* @param list<string> $profiles
*/
#[AsTask(description: 'Builds and starts the infrastructure', aliases: ['up'])]
function up(): void
{
io()->title('Starting infrastructure');
function up(
?string $service = null,
#[AsOption(mode: InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED)]
array $profiles = [],
): void {
if (!$service && !$profiles) {
io()->title('Starting infrastructure');
}

$command = ['up', '--detach', '--no-build'];

if ($service) {
$command[] = $service;
}

try {
docker_compose(['up', '--detach', '--no-build']);
docker_compose($command, profiles: $profiles);
} catch (ExceptionInterface $e) {
io()->error('An error occured while starting the infrastructure.');
io()->note('Did you forget to run "castor docker:build"?');
@@ -92,12 +126,26 @@ function up(): void
}
}

/**
* @param list<string> $profiles
*/
#[AsTask(description: 'Stops the infrastructure', aliases: ['stop'])]
function stop(): void
{
io()->title('Stopping infrastructure');
function stop(
?string $service = null,
#[AsOption(mode: InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED)]
array $profiles = [],
): void {
if (!$service || !$profiles) {
io()->title('Stopping infrastructure');
}

docker_compose(['stop']);
$command = ['stop'];

if ($service) {
$command[] = $service;
}

docker_compose($command, profiles: $profiles);
}

#[AsTask(description: 'Opens a shell (bash) into a builder container', aliases: ['builder'])]
@@ -112,10 +160,22 @@ function builder(): void
docker_compose_run('bash', c: $c);
}

/**
* @param list<string> $profiles
*/
#[AsTask(description: 'Displays infrastructure logs', aliases: ['logs'])]
function logs(): void
{
docker_compose(['logs', '-f', '--tail', '150'], c: context()->withTty());
function logs(
?string $service = null,
#[AsOption(mode: InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED)]
array $profiles = [],
): void {
$command = ['logs', '-f', '--tail', '150'];

if ($service) {
$command[] = $service;
}

docker_compose($command, c: context()->withTty(), profiles: $profiles);
}

#[AsTask(description: 'Lists containers status', aliases: ['ps'])]
@@ -150,7 +210,7 @@ function destroy(
fs()->remove($files);
}

#[AsTask(description: 'Generates SSL certificates (with mkcert if available or self-signed if not)', namespace: '')]
#[AsTask(description: 'Generates SSL certificates (with mkcert if available or self-signed if not)')]
function generate_certificates(
#[AsOption(description: 'Force the certificates re-generation without confirmation', shortcut: 'f')]
bool $force = false,
@@ -224,49 +284,15 @@ function workers_start(): void
{
io()->title('Starting workers');

$workers = get_workers();

if (!$workers) {
return;
}

run([
'docker',
'update',
'--restart=unless-stopped',
...$workers,
], quiet: true);

run([
'docker',
'start',
...$workers,
], quiet: true);
up(profiles: ['worker']);
}

#[AsTask(description: 'Stops the workers', namespace: 'docker:worker', name: 'stop', aliases: ['stop-workers'])]
function workers_stop(): void
{
io()->title('Stopping workers');

$workers = get_workers();

if (!$workers) {
return;
}

run([
'docker',
'update',
'--restart=no',
...$workers,
]);

run([
'docker',
'stop',
...$workers,
]);
stop(profiles: ['worker']);
}

#[AsTask(description: 'Push images cache to the registry', namespace: 'docker', name: 'push', aliases: ['push'])]
@@ -302,9 +328,9 @@ function create_default_context(): Context
],
'macos' => false,
'power_shell' => false,
'user_id' => posix_geteuid(),
// check if posix_geteuid is available, if not, use getmyuid (windows)
'user_id' => \function_exists('posix_geteuid') ? posix_geteuid() : getmyuid(),
'root_dir' => \dirname(__DIR__),
'env' => $_SERVER['CI'] ?? false ? 'ci' : 'dev',
];

if (file_exists($data['root_dir'] . '/infrastructure/docker/docker-compose.override.yml')) {
@@ -323,7 +349,7 @@ function create_default_context(): Context
// If the directory does not exist, we create it. Otherwise, docker
// will do, as root, and the user will not be able to write in it.
if (!is_dir($composerCacheDir)) {
mkdir($composerCacheDir, 0777, true);
mkdir($composerCacheDir, 0o777, true);
}
}

@@ -334,7 +360,7 @@ function create_default_context(): Context
if (str_contains($platform, 'darwin')) {
$data['macos'] = true;
$data['docker_compose_files'][] = 'docker-compose.docker-for-x.yml';
} elseif (\in_array($platform, ['win32', 'win64'])) {
} elseif (\in_array($platform, ['win32', 'win64', 'windows nt'])) {
$data['docker_compose_files'][] = 'docker-compose.docker-for-x.yml';
$data['power_shell'] = true;
}
@@ -348,15 +374,38 @@ function create_default_context(): Context
$data['user_id'] = 1000;
}

return new Context($data, pty: 'dev' === $data['env']);
return new Context(
$data,
pty: Process::isPtySupported(),
environment: [
'BUILDKIT_PROGRESS' => 'plain',
]
);
}

#[AsContext(name: 'ci')]
function create_ci_context(): Context
{
$c = create_default_context();

return $c
->withData([
// override the default context here
])
->withEnvironment([
'COMPOSE_ANSI' => 'never',
])
;
}

/**
* @param array<string> $subCommand
* @param list<string> $subCommand
* @param list<string> $profiles
*/
function docker_compose(array $subCommand, ?Context $c = null, bool $withBuilder = false): Process
function docker_compose(array $subCommand, ?Context $c = null, bool $withBuilder = false, array $profiles = []): Process
{
$c ??= context();
$profiles = $profiles ?: ['default'];

$domains = [variable('root_domain'), ...variable('extra_domains')];
$domains = '`' . implode('`) || Host(`', $domains) . '`';
@@ -370,7 +419,6 @@ function docker_compose(array $subCommand, ?Context $c = null, bool $withBuilder
'USER_ID' => variable('user_id'),
'COMPOSER_CACHE_DIR' => variable('composer_cache_dir'),
'PHP_VERSION' => variable('php_version'),
'BUILDKIT_PROGRESS' => 'plain',
])
;

@@ -379,6 +427,10 @@ function docker_compose(array $subCommand, ?Context $c = null, bool $withBuilder
'compose',
'-p', variable('project_name'),
];
foreach ($profiles as $profile) {
$command[] = '--profile';
$command[] = $profile;
}

foreach (variable('docker_compose_files') as $file) {
$command[] = '-f';
@@ -464,28 +516,3 @@ function run_in_docker_or_locally_for_mac(string $command, ?Context $c = null):
docker_compose_run($command, c: $c);
}
}

/**
* Find worker containers for the current project.
*
* @return array<string>
*/
function get_workers(): array
{
$command = [
'docker',
'ps',
'-a',
'--filter', 'label=docker-starter.worker.' . variable('project_name'),
'--quiet',
];
$out = capture($command);

if (!$out) {
return [];
}

$workers = explode("\n", $out);

return array_map('trim', $workers);
}
15 changes: 14 additions & 1 deletion .castor/qa.php
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
use Castor\Attribute\AsTask;

use function Castor\io;
use function Castor\variable;
use function docker\docker_compose_run;
use function docker\docker_exit_code;

@@ -16,7 +17,7 @@ function all(): int
$phpstan = phpstan();
// $phpunit = phpunit();

return max($cs, $phpstan/*, $phpunit*/);
return max($cs, $phpstan/* , $phpunit */);
}

#[AsTask(description: 'Installs tooling')]
@@ -37,12 +38,24 @@ function install(): void
#[AsTask(description: 'Runs PHPStan', aliases: ['phpstan'])]
function phpstan(): int
{
if (!is_dir(variable('root_dir') . '/tools/phpstan/vendor')) {
io()->error('PHPStan is not installed. Run `castor qa:install` first.');

return 1;
}

return docker_exit_code('phpstan', workDir: '/var/www');
}

#[AsTask(description: 'Fixes Coding Style', aliases: ['cs'])]
function cs(bool $dryRun = false): int
{
if (!is_dir(variable('root_dir') . '/tools/php-cs-fixer/vendor')) {
io()->error('PHP-CS-Fixer is not installed. Run `castor qa:install` first.');

return 1;
}

if ($dryRun) {
return docker_exit_code('php-cs-fixer fix --dry-run --diff', workDir: '/var/www');
}
Loading