Skip to content

Commit

Permalink
feat: skip assets processing if no changes were made to assets
Browse files Browse the repository at this point in the history
  • Loading branch information
carlalexander committed Apr 14, 2023
1 parent a723a80 commit 76daebd
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 12 deletions.
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -32,7 +32,7 @@
"symfony/polyfill-php80": "^1.24",
"symfony/process": "^5.4|^6.0",
"symfony/yaml": "^5.4|^6.0",
"ymirapp/ymir-sdk-php": "^0.1.1"
"ymirapp/ymir-sdk-php": "^0.1.2"
},
"require-dev": {
"fakerphp/faker": "^1.17",
Expand Down
4 changes: 2 additions & 2 deletions src/ApiClient.php
Expand Up @@ -150,9 +150,9 @@ public function createDatabaseUser(int $databaseServerId, string $username, arra
/**
* Create a new deployment for the given project on the given environment.
*/
public function createDeployment(int $projectId, string $environment, ProjectConfiguration $projectConfiguration): Collection
public function createDeployment(int $projectId, string $environment, ProjectConfiguration $projectConfiguration, ?string $assetsHash = null): Collection
{
return $this->client->createDeployment($projectId, $environment, $projectConfiguration->toArray());
return $this->client->createDeployment($projectId, $environment, $projectConfiguration->toArray(), $assetsHash);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Command/Project/AbstractProjectDeploymentCommand.php
Expand Up @@ -56,7 +56,7 @@ protected function perform(InputInterface $input, OutputInterface $output)
$environment = $this->getStringArgument($input, 'environment');

foreach ($this->deploymentSteps as $deploymentStep) {
$deploymentStep->perform($deployment, $output);
$deploymentStep->perform($deployment, $environment, $output);
}

$output->info($this->getSuccessMessage($environment));
Expand Down
28 changes: 26 additions & 2 deletions src/Command/Project/DeployProjectCommand.php
Expand Up @@ -40,6 +40,13 @@ class DeployProjectCommand extends AbstractProjectDeploymentCommand
*/
public const NAME = 'project:deploy';

/**
* The assets directory where the asset files were copied to.
*
* @var string
*/
private $assetsDirectory;

/**
* The build "uploads" directory.
*
Expand All @@ -50,10 +57,11 @@ class DeployProjectCommand extends AbstractProjectDeploymentCommand
/**
* Constructor.
*/
public function __construct(ApiClient $apiClient, CliConfiguration $cliConfiguration, ProjectConfiguration $projectConfiguration, string $uploadsDirectory, array $deploymentSteps = [])
public function __construct(ApiClient $apiClient, string $assetsDirectory, CliConfiguration $cliConfiguration, ProjectConfiguration $projectConfiguration, string $uploadsDirectory, array $deploymentSteps = [])
{
parent::__construct($apiClient, $cliConfiguration, $projectConfiguration, $deploymentSteps);

$this->assetsDirectory = $assetsDirectory;
$this->uploadsDirectory = $uploadsDirectory;
}

Expand Down Expand Up @@ -86,7 +94,7 @@ protected function createDeployment(InputInterface $input, OutputInterface $outp
$this->invoke($output, ImportUploadsCommand::NAME, ['path' => $this->uploadsDirectory, '--environment' => $environment, '--force' => null]);
}

$deployment = $this->apiClient->createDeployment($projectId, $environment, $this->projectConfiguration);
$deployment = $this->apiClient->createDeployment($projectId, $environment, $this->projectConfiguration, $this->generateDirectoryHash($this->assetsDirectory));

if (!$deployment->has('id')) {
throw new RuntimeException('There was an error creating the deployment');
Expand All @@ -102,4 +110,20 @@ protected function getSuccessMessage(string $environment): string
{
return sprintf('Project deployed successfully to "<comment>%s</comment>" environment', $environment);
}

/**
* Generate a hash for the content of the given directory.
*/
private function generateDirectoryHash(string $directory): string
{
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory), \RecursiveIteratorIterator::SELF_FIRST);

return hash('sha256', collect($iterator)->filter(function (\SplFileInfo $file) {
return $file->isFile();
})->mapWithKeys(function (\SplFileInfo $file) {
return [substr($file->getRealPath(), (int) strrpos($file->getRealPath(), '.ymir')) => $file->getRealPath()];
})->map(function (string $realPath, string $relativePath) {
return sprintf('%s|%s', $relativePath, hash_file('sha256', $realPath));
})->implode(''));
}
}
2 changes: 1 addition & 1 deletion src/Deployment/DeploymentStepInterface.php
Expand Up @@ -21,5 +21,5 @@ interface DeploymentStepInterface
/**
* Perform the deployment step and generate the console output.
*/
public function perform(Collection $deployment, OutputInterface $output);
public function perform(Collection $deployment, string $environment, OutputInterface $output);
}
23 changes: 21 additions & 2 deletions src/Deployment/ProcessAssetsStep.php
Expand Up @@ -20,6 +20,7 @@
use Ymir\Cli\ApiClient;
use Ymir\Cli\Console\OutputInterface;
use Ymir\Cli\FileUploader;
use Ymir\Cli\ProjectConfiguration\ProjectConfiguration;

class ProcessAssetsStep implements DeploymentStepInterface
{
Expand All @@ -37,6 +38,13 @@ class ProcessAssetsStep implements DeploymentStepInterface
*/
private $assetsDirectory;

/**
* The Ymir project configuration.
*
* @var ProjectConfiguration
*/
private $projectConfiguration;

/**
* The uploader used to upload all the build files.
*
Expand All @@ -47,18 +55,29 @@ class ProcessAssetsStep implements DeploymentStepInterface
/**
* Constructor.
*/
public function __construct(ApiClient $apiClient, string $assetsDirectory, FileUploader $uploader)
public function __construct(ApiClient $apiClient, string $assetsDirectory, ProjectConfiguration $projectConfiguration, FileUploader $uploader)
{
$this->apiClient = $apiClient;
$this->assetsDirectory = $assetsDirectory;
$this->projectConfiguration = $projectConfiguration;
$this->uploader = $uploader;
}

/**
* {@inheritdoc}
*/
public function perform(Collection $deployment, OutputInterface $output)
public function perform(Collection $deployment, string $environment, OutputInterface $output)
{
$deploymentWithAssetsHash = $this->apiClient->getDeployments($this->projectConfiguration->getProjectId(), $environment)
->where('status', 'finished')
->firstWhere('assets_hash', $deployment->get('assets_hash'));

if (null !== $deploymentWithAssetsHash) {
$output->infoWithWarning('No assets change detected', 'skipping processing assets');

return;
}

$output->info('Processing assets');

$output->writeStep('Getting signed asset URLs');
Expand Down
2 changes: 1 addition & 1 deletion src/Deployment/StartAndMonitorDeploymentStep.php
Expand Up @@ -38,7 +38,7 @@ public function __construct(ApiClient $apiClient)
/**
* {@inheritdoc}
*/
public function perform(Collection $deployment, OutputInterface $output)
public function perform(Collection $deployment, string $environment, OutputInterface $output)
{
$output->info(sprintf('%s starting', ucfirst($deployment->get('type', 'deployment'))));

Expand Down
3 changes: 1 addition & 2 deletions src/Deployment/UploadFunctionCodeStep.php
Expand Up @@ -74,10 +74,9 @@ public function __construct(ApiClient $apiClient, string $buildArtifactPath, str
/**
* {@inheritdoc}
*/
public function perform(Collection $deployment, OutputInterface $output)
public function perform(Collection $deployment, string $environment, OutputInterface $output)
{
$configuration = $deployment->get('configuration');
$environment = Arr::first(array_keys($configuration['environments']));
$deploymentType = Arr::get($configuration, sprintf('environments.%s.deployment', $environment), 'zip');

if ('image' === $deploymentType) {
Expand Down

0 comments on commit 76daebd

Please sign in to comment.