Skip to content

Commit

Permalink
Add ability to specify args that will be passed to docker during the …
Browse files Browse the repository at this point in the history
…build (#232)

* feat: Add ability to specify args that will be passed to docker during the build

* fix: docblock

* chore: handle docker args flags as well as long flags

* fix: formatting

* fix: spacing

* raname args to options

* fix output formatting

* pass options

* rename to buildOptions

* revert

* chore: output docker build command

* chore: move args in front of options in function signatures

* chore: move docker build args in front of build options in signature

---------

Co-authored-by: Joe Dixon <[email protected]>
  • Loading branch information
chrisneal and joedixon authored Aug 9, 2023
1 parent 3f3317a commit 84d0bb4
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 26 deletions.
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

0 comments on commit 84d0bb4

Please sign in to comment.