'exampleorg', 'projects' => [ 'exampleproj' ], 'token' => 'd47828...', 'version' => '0.0.1', ]); ``` ### Suggested Usage Since you should only notify Sentry of a successful deployment, the deploy:sentry task should be executed right at the end. ```php // deploy.php after('deploy', 'deploy:sentry'); ``` */ namespace Deployer; use Closure; use DateTime; use Deployer\Exception\ConfigurationException; use Deployer\Utility\Httpie; desc('Notifies Sentry of deployment'); task( 'deploy:sentry', static function () { $now = date('c'); $defaultConfig = [ 'version' => getReleaseGitRef(), 'version_prefix' => null, 'refs' => [], 'ref' => null, 'commits' => getGitCommitsRefs(), 'url' => null, 'date_released' => $now, 'date_deploy_started' => $now, 'date_deploy_finished' => $now, 'sentry_server' => 'https://sentry.io', 'previous_commit' => null, 'environment' => get('symfony_env', 'prod'), 'deploy_name' => null, ]; $config = array_merge($defaultConfig, (array) get('sentry')); array_walk( $config, static function (&$value) use ($config) { if (is_callable($value)) { $value = $value($config); } } ); if ( !isset($config['organization'], $config['token'], $config['version']) || (empty($config['projects']) || !is_array($config['projects'])) ) { throw new \RuntimeException( << 'exampleorg', 'projects' => [ 'exampleproj', 'exampleproje2' ], 'token' => 'd47828...', ] );" EXAMPLE ); } $releaseData = array_filter( [ 'version' => ($config['version_prefix'] ?? '') . $config['version'], 'refs' => $config['refs'], 'ref' => $config['ref'], 'url' => $config['url'], 'commits' => array_slice($config['commits'] ?? [], 0), // reset keys to serialize as array in json 'dateReleased' => $config['date_released'], 'projects' => $config['projects'], 'previousCommit' => $config['previous_commit'], ] ); $releasesApiUrl = $config['sentry_server'] . '/api/0/organizations/' . $config['organization'] . '/releases/'; $response = Httpie::post( $releasesApiUrl ) ->header('Authorization', sprintf('Bearer %s', $config['token'])) ->jsonBody($releaseData) ->getJson(); if (!isset($response['version'], $response['projects'])) { throw new \RuntimeException(sprintf('Unable to create a release: %s', print_r($response, true))); } writeln( sprintf( 'Sentry: Release of version %s ' . 'for projects: %s created successfully.', $response['version'], implode(', ', array_column($response['projects'], 'slug')) ) ); $deployData = array_filter( [ 'environment' => $config['environment'], 'name' => $config['deploy_name'], 'url' => $config['url'], 'dateStarted' => $config['date_deploy_started'], 'dateFinished' => $config['date_deploy_finished'], ] ); $response = Httpie::post( $releasesApiUrl . $response['version'] . '/deploys/' ) ->header('Authorization', sprintf('Bearer %s', $config['token'])) ->jsonBody($deployData) ->getJson(); if (!isset($response['id'], $response['environment'])) { throw new \RuntimeException(sprintf('Unable to create a deployment: %s', print_r($response, true))); } writeln( sprintf( 'Sentry: Deployment %s ' . 'for environment %s created successfully', $response['id'], $response['environment'] ) ); } ); function getPreviousReleaseRevision() { switch (get('update_code_strategy')) { case 'archive': if (has('previous_release')) { return run('cat {{previous_release}}/REVISION'); } return null; case 'clone': if (has('previous_release')) { cd('{{previous_release}}'); return trim(run('git rev-parse HEAD')); } return null; default: throw new ConfigurationException(parse("Unknown `update_code_strategy` option: {{update_code_strategy}}.")); } } function getCurrentReleaseRevision() { switch (get('update_code_strategy')) { case 'archive': return run('cat {{release_path}}/REVISION'); case 'clone': cd('{{release_path}}'); return trim(run('git rev-parse HEAD')); default: throw new ConfigurationException(parse("Unknown `update_code_strategy` option: {{update_code_strategy}}.")); } } function getReleaseGitRef(): Closure { return static function ($config = []): string { if (get('update_code_strategy') === 'archive') { if (isset($config['git_version_command'])) { cd('{{deploy_path}}/.dep/repo'); return trim(run($config['git_version_command'])); } return run('cat {{current_path}}/REVISION'); } cd('{{release_path}}'); if (isset($config['git_version_command'])) { return trim(run($config['git_version_command'])); } return trim(run('git log -n 1 --format="%h"')); }; } function getGitCommitsRefs(): Closure { return static function ($config = []): array { $previousReleaseRevision = getPreviousReleaseRevision(); $currentReleaseRevision = getCurrentReleaseRevision() ?: 'HEAD'; if ($previousReleaseRevision === null) { $commitRange = $currentReleaseRevision; } else { $commitRange = $previousReleaseRevision . '..' . $currentReleaseRevision; } try { if (get('update_code_strategy') === 'archive') { cd('{{deploy_path}}/.dep/repo'); } else { cd('{{release_path}}'); } $result = run(sprintf('git rev-list --pretty="%s" %s', 'format:%H#%an#%ae#%at#%s', $commitRange)); $lines = array_filter( // limit number of commits for first release with many commits array_map('trim', array_slice(explode("\n", $result), 0, 200)), static function (string $line): bool { return !empty($line) && strpos($line, 'commit') !== 0; } ); return array_map( static function (string $line): array { [$ref, $authorName, $authorEmail, $timestamp, $message] = explode('#', $line, 5); return [ 'id' => $ref, 'author_name' => $authorName, 'author_email' => $authorEmail, 'message' => $message, 'timestamp' => date(\DateTime::ATOM, (int) $timestamp), ]; }, $lines ); } catch (\Deployer\Exception\RunException $e) { writeln($e->getMessage()); return []; } }; }