Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to specify args that will be passed to docker during the build #232

Merged
merged 13 commits into from
Aug 9, 2023
17 changes: 14 additions & 3 deletions src/BuildProcess/BuildContainerImage.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ class BuildContainerImage
*/
protected $cliBuildArgs;

/**
* The Docker CLI options.
*
* @var array
*/
protected $cliBuildOptions;

/**
* The Docker manifest build arguments.
*
Expand All @@ -55,14 +62,17 @@ class BuildContainerImage
* Create a new project builder.
*
* @param string|null $environment
* @param array $buildArgs
* @param array $cliBuildArgs
* @param array $cliBuildOptions
* @param array $manifestBuildArgs
* @return void
*/
public function __construct($environment = null, $cliBuildArgs = [], $manifestBuildArgs = [])
public function __construct($environment = null, $cliBuildArgs = [], $cliBuildOptions = [], $manifestBuildArgs = [])
{
$this->baseConstructor($environment);

$this->cliBuildArgs = $cliBuildArgs;
$this->cliBuildOptions = $cliBuildOptions;
$this->manifestBuildArgs = $manifestBuildArgs;
}

Expand Down Expand Up @@ -96,7 +106,8 @@ public function __invoke()
$this->appPath,
Manifest::name(),
$this->environment,
$this->formatBuildArguments()
$this->formatBuildArguments(),
$this->cliBuildOptions
);
}

Expand Down
8 changes: 7 additions & 1 deletion src/Commands/BuildCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ protected function configure()
->addArgument('environment', InputArgument::OPTIONAL, 'The environment name')
->addOption('asset-url', null, InputOption::VALUE_OPTIONAL, 'The asset base URL')
->addOption('build-arg', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Docker build argument')
->addOption('build-option', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Docker build option')
->setDescription('Build the project archive');
}

Expand Down Expand Up @@ -88,7 +89,12 @@ public function handle()
new ExtractVendorToSeparateDirectory($this->argument('environment')),
new CompressApplication($this->argument('environment')),
new CompressVendor($this->argument('environment')),
new BuildContainerImage($this->argument('environment'), $this->option('build-arg'), Manifest::dockerBuildArgs($this->argument('environment'))),
new BuildContainerImage(
$this->argument('environment'),
$this->option('build-arg'),
$this->option('build-option'),
Manifest::dockerBuildArgs($this->argument('environment'))
),
])->each->__invoke();

$time = (new DateTime())->diff($startedAt)->format('%im%Ss');
Expand Down
2 changes: 2 additions & 0 deletions src/Commands/DeployCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ protected function configure()
->addOption('without-waiting', null, InputOption::VALUE_NONE, 'Deploy without waiting for progress')
->addOption('fresh-assets', null, InputOption::VALUE_NONE, 'Upload a fresh copy of all assets')
->addOption('build-arg', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Docker build argument')
->addOption('build-option', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Docker build option')
->addOption('debug', null, InputOption::VALUE_OPTIONAL, 'Deploy with debug mode enabled', 'unset')
->setDescription('Deploy an environment');
}
Expand Down Expand Up @@ -123,6 +124,7 @@ protected function buildProject(array $project)
'--asset-url' => $this->assetDomain($project).'/'.$uuid,
'--manifest' => Path::manifest(),
'--build-arg' => $this->option('build-arg'),
'--build-option' => $this->option('build-option'),
]);

return $this->uploadArtifact(
Expand Down
78 changes: 63 additions & 15 deletions src/Docker.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,24 @@ class Docker
* @param string $project
* @param string $environment
* @param array $cliBuildArgs
* @param array $cliBuildOptions
* @return void
*/
public static function build($path, $project, $environment, $cliBuildArgs)
public static function build($path, $project, $environment, $cliBuildArgs, $cliBuildOptions)
{
$buildCommand = static::buildCommand(
$project,
$environment,
$cliBuildArgs,
Manifest::dockerBuildArgs($environment),
$cliBuildOptions,
Manifest::dockerBuildOptions($environment)
);

Helpers::line(sprintf('Build command: %s', $buildCommand));

Process::fromShellCommandline(
static::buildCommand($project, $environment, $cliBuildArgs, Manifest::dockerBuildArgs($environment)),
$buildCommand,
$path
)->setTimeout(null)->mustRun(function ($type, $line) {
Helpers::write($line);
Expand All @@ -34,24 +46,60 @@ public static function build($path, $project, $environment, $cliBuildArgs)
* @param string $environment
* @param array $cliBuildArgs
* @param array $manifestBuildArgs
* @param array $cliBuildOptions
* @param array $manifestBuildOptions
* @return string
*/
public static function buildCommand($project, $environment, $cliBuildArgs, $manifestBuildArgs)
public static function buildCommand($project, $environment, $cliBuildArgs, $manifestBuildArgs, $cliBuildOptions, $manifestBuildOptions)
{
return sprintf('docker build --pull --file=%s --tag=%s %s.',
$command = sprintf(
'docker build --pull --file=%s --tag=%s ',
Manifest::dockerfile($environment),
Str::slug($project).':'.$environment,
Collection::make($manifestBuildArgs)
->merge(Collection::make($cliBuildArgs)
->mapWithKeys(function ($value) {
[$key, $value] = explode('=', $value, 2);

return [$key => $value];
})
)->map(function ($value, $key) {
return '--build-arg='.escapeshellarg("{$key}={$value}").' ';
})->implode('')
Str::slug($project).':'.$environment
);

$buildArgs = Collection::make($manifestBuildArgs)
->merge(Collection::make($cliBuildArgs)
->mapWithKeys(function ($value) {
[$key, $value] = explode('=', $value, 2);

return [$key => $value];
})
)->map(function ($value, $key) {
return '--build-arg='.escapeshellarg("{$key}={$value}");
})->implode(' ');

$buildOptions = Collection::make($manifestBuildOptions)
->mapWithKeys(function ($value) {
if (is_array($value)) {
return $value;
}

return [$value => null];
})
->merge(Collection::make($cliBuildOptions)
->mapWithKeys(function ($value) {
if (! str_contains($value, '=')) {
return [$value => null];
}

[$key, $value] = explode('=', $value, 2);

return [$key => $value];
})
)->map(function ($value, $key) {
if ($value === null) {
return "--{$key}";
}

return "--{$key}=".escapeshellarg($value);
})->implode(' ');

$command = $buildArgs ? $command.$buildArgs.' ' : $command;
$command = $buildOptions ? $command.$buildOptions.' ' : $command;
$command .= '.';

return $command;
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/Manifest.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ public static function dockerBuildArgs($environment)
return static::current()['environments'][$environment]['docker-build-args'] ?? [];
}

/**
* Get the Docker options.
*
* @param string $environment
* @return array
*/
public static function dockerBuildOptions($environment)
{
return static::current()['environments'][$environment]['docker-build-options'] ?? [];
}

/**
* Determine if the environment uses a database proxy.
*
Expand Down
4 changes: 2 additions & 2 deletions tests/BuildContainerImageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function test_docker_build_arguments_can_be_formatted_correctly()
],
]));

$buildArgs = (new BuildContainerImage('production', ['FOO=BAR', 'BAR=BAZ']))->formatBuildArguments();
$buildArgs = (new BuildContainerImage('production', ['FOO=BAR', 'BAR=BAZ'], []))->formatBuildArguments();

$this->assertSame(['__VAPOR_RUNTIME=docker', 'FOO=BAR', 'BAR=BAZ'], $buildArgs);
}
Expand All @@ -72,7 +72,7 @@ public function test_runtime_variable_cannot_be_overridden()
],
]));

$buildArgs = (new BuildContainerImage('production', ['__VAPOR_RUNTIME=foo']))->formatBuildArguments();
$buildArgs = (new BuildContainerImage('production', [], ['__VAPOR_RUNTIME=foo']))->formatBuildArguments();

$this->assertSame(['__VAPOR_RUNTIME=docker'], $buildArgs);
}
Expand Down
48 changes: 43 additions & 5 deletions tests/DockerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ protected function tearDown(): void

public function test_build_command_no_build_args()
{
$command = Docker::buildCommand('my-project', 'production', [], []);
$command = Docker::buildCommand('my-project', 'production', [], [], [], []);
$expectedCommand = 'docker build --pull --file=production.Dockerfile --tag=my-project:production .';
$this->assertEquals($expectedCommand, $command);
}

public function test_build_command_cli_build_args()
{
$cliBuildArgs = ['FOO=BAR', 'FIZZ=BUZZ'];
$command = Docker::buildCommand('my-project', 'production', $cliBuildArgs, []);
$command = Docker::buildCommand('my-project', 'production', $cliBuildArgs, [], [], []);
$expectedCommand = 'docker build --pull --file=production.Dockerfile --tag=my-project:production '.
"--build-arg='FOO=BAR' --build-arg='FIZZ=BUZZ' .";
$this->assertEquals($expectedCommand, $command);
Expand All @@ -41,7 +41,7 @@ public function test_build_command_cli_build_args()
public function test_build_command_manifest_build_args()
{
$manifestBuildArgs = ['FOO' => 'BAR', 'FIZZ' => 'BUZZ'];
$command = Docker::buildCommand('my-project', 'production', [], $manifestBuildArgs);
$command = Docker::buildCommand('my-project', 'production', [], $manifestBuildArgs, [], []);
$expectedCommand = 'docker build --pull --file=production.Dockerfile --tag=my-project:production '.
"--build-arg='FOO=BAR' --build-arg='FIZZ=BUZZ' .";
$this->assertEquals($expectedCommand, $command);
Expand All @@ -51,12 +51,50 @@ public function test_build_command_cli_and_manifest_build_args()
{
$cliBuildArgs = ['BAR=FOO', 'FIZZ=BAZZ'];
$manifestBuildArgs = ['FOO' => 'BAR', 'FIZZ' => 'BUZZ'];
$command = Docker::buildCommand('my-project', 'production', $cliBuildArgs, $manifestBuildArgs);
$command = Docker::buildCommand('my-project', 'production', $cliBuildArgs, $manifestBuildArgs, [], []);
$expectedCommand = 'docker build --pull --file=production.Dockerfile --tag=my-project:production '.
"--build-arg='FOO=BAR' --build-arg='FIZZ=BAZZ' --build-arg='BAR=FOO' .";
$this->assertEquals($expectedCommand, $command);
}

public function test_build_command_cli_docker_options()
{
$cliBuildOptions = ['BAR=FOO', 'FIZZ=BAZZ', 'FIZZLE', 'BUZZLE'];
$command = Docker::buildCommand('my-project', 'production', [], [], $cliBuildOptions, []);
$expectedCommand = 'docker build --pull --file=production.Dockerfile --tag=my-project:production '.
"--BAR='FOO' --FIZZ='BAZZ' --FIZZLE --BUZZLE .";
$this->assertEquals($expectedCommand, $command);
}

public function test_build_command_manifest_docker_options()
{
$manifestBuildOptions = [['FOO' => 'BAR'], ['FIZZ' => 'BUZZ'], 'FIZZLE', 'BUZZLE'];
$command = Docker::buildCommand('my-project', 'production', [], [], [], $manifestBuildOptions);
$expectedCommand = 'docker build --pull --file=production.Dockerfile --tag=my-project:production '.
"--FOO='BAR' --FIZZ='BUZZ' --FIZZLE --BUZZLE .";
$this->assertEquals($expectedCommand, $command);
}

public function test_build_command_cli_and_manifest_docker_args()
{
$cliBuildOptions = ['BAR=FOO', 'FIZZ=BAZZ', 'FIZZLE', 'BUZZLE'];
$manifestBuildOptions = [['FOO' => 'BAR'], ['FIZZ' => 'BUZZ'], 'FIZZLY', 'BUZZLY'];
$command = Docker::buildCommand('my-project', 'production', [], [], $cliBuildOptions, $manifestBuildOptions);
$expectedCommand = 'docker build --pull --file=production.Dockerfile --tag=my-project:production '.
"--FOO='BAR' --FIZZ='BAZZ' --FIZZLY --BUZZLY --BAR='FOO' --FIZZLE --BUZZLE .";
$this->assertEquals($expectedCommand, $command);
}

public function test_build_command_cli_docker_options_and_cli_build_args()
{
$cliBuildOptions = ['BAR=FOO', 'FIZZ=BAZZ'];
$cliBuildArgs = ['BAR=FOO', 'FIZZ=BAZZ'];
$command = Docker::buildCommand('my-project', 'production', $cliBuildArgs, [], $cliBuildOptions, []);
$expectedCommand = 'docker build --pull --file=production.Dockerfile --tag=my-project:production '.
"--build-arg='BAR=FOO' --build-arg='FIZZ=BAZZ' --BAR='FOO' --FIZZ='BAZZ' .";
$this->assertEquals($expectedCommand, $command);
}

public function test_dockerfile_from_manifest()
{
file_put_contents(Container::getInstance()->offsetGet('manifest'), Yaml::dump([
Expand All @@ -69,7 +107,7 @@ public function test_dockerfile_from_manifest()
],
],
]));
$command = Docker::buildCommand('my-project', 'production', [], []);
$command = Docker::buildCommand('my-project', 'production', [], [], [], []);
$expectedCommand = 'docker build --pull --file=docker/shared.Dockerfile --tag=my-project:production .';
$this->assertEquals($expectedCommand, $command);
}
Expand Down