Drop SQLite support (#2405)

This commit is contained in:
Kevin Papst
2021-03-08 16:06:22 +01:00
committed by GitHub
parent 70f7f35009
commit 30ac5e4c24
78 changed files with 1165 additions and 1596 deletions

View File

@@ -9,6 +9,7 @@
namespace App\Command;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
@@ -20,23 +21,24 @@ use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Command used to execute all the basic application bootstrapping AFTER "composer install" was executed.
*
* This command is NOT used during runtime and only meant for developers on their local machines.
* I am too lazy to think about how this could be tested ... and this is one of the rare edge cases where I don't
* feel like it is necessary, so I "cheat" with:
* Base class for all re-installation commands, which are not used during application runtime.
* @codeCoverageIgnore
*/
class ResetCommand extends Command
abstract class AbstractResetCommand extends Command
{
/**
* @var string
*/
private $environment;
/**
* @var EntityManagerInterface
*/
protected $entityManager;
public function __construct(string $kernelEnvironment)
public function __construct(string $kernelEnvironment, EntityManagerInterface $entityManager)
{
$this->environment = $kernelEnvironment;
$this->entityManager = $entityManager;
parent::__construct();
}
@@ -46,11 +48,11 @@ class ResetCommand extends Command
protected function configure()
{
$this
->setName('kimai:reset-dev')
->setName('kimai:reset-' . $this->getEnvName())
->setDescription('Resets the dev environment')
->setHelp(
<<<EOT
This command will drop and re-create the database and its schemas, load development fixtures and clear the cache.
This command will drop and re-create the database and its schemas, load data and clear the cache.
Use the <info>-n</info> switch to skip the question.
EOT
)
@@ -81,7 +83,8 @@ EOT
if ($this->askConfirmation($input, $output, 'Do you want to create the database y/N ?')) {
try {
$command = $this->getApplication()->find('doctrine:database:create');
$command->run(new ArrayInput([]), $output);
$options = ['--if-not-exists' => true];
$command->run(new ArrayInput($options), $output);
} catch (Exception $ex) {
$io->error('Failed to create database: ' . $ex->getMessage());
@@ -130,12 +133,9 @@ EOT
}
try {
$command = $this->getApplication()->find('doctrine:fixtures:load');
$cmdInput = new ArrayInput([]);
$cmdInput->setInteractive(false);
$command->run($cmdInput, $output);
$this->loadData($input, $output);
} catch (Exception $ex) {
$io->error('Failed to import fixtures: ' . $ex->getMessage());
$io->error('Failed to import data: ' . $ex->getMessage());
return 6;
}
@@ -173,4 +173,8 @@ EOT
return $questionHelper->ask($input, $output, $question);
}
abstract protected function getEnvName(): string;
abstract protected function loadData(InputInterface $input, OutputInterface $output): void;
}

View File

@@ -1,159 +0,0 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Command;
use App\Constants;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Command used to create a release package with pre-installed composer, SQLite database and user.
*
* @codeCoverageIgnore
*/
class CreateReleaseCommand extends Command
{
public const CLONE_CMD = 'git clone -b %s --depth 1 https://github.com/kevinpapst/kimai2.git';
/**
* @var string
*/
private $rootDir = '';
/**
* @var string
*/
private $environment;
public function __construct(string $projectDirectory, string $kernelEnvironment)
{
$this->rootDir = realpath($projectDirectory);
$this->environment = $kernelEnvironment;
parent::__construct();
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('kimai:create-release')
->setDescription('Create a pre-installed release package')
->setHelp('This command will create a release package with pre-installed composer, SQLite database and user.')
->addOption('directory', null, InputOption::VALUE_OPTIONAL, 'Directory where the release package will be stored', '/tmp/')
->addOption('release', null, InputOption::VALUE_OPTIONAL, 'The version that should be zipped', Constants::VERSION)
;
}
/**
* Make sure that this command CANNOT be executed in production.
*
* @return bool
*/
public function isEnabled()
{
return $this->environment !== 'prod';
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$directory = $input->getOption('directory');
if ($directory[0] === '/') {
$directory = realpath($directory);
} else {
$directory = realpath($this->rootDir . '/' . $directory);
}
$tmpDir = $directory . '/' . uniqid('kimai_release_');
if (!is_dir($directory)) {
$io->error('Given directory is not existing: ' . $directory);
return 1;
}
if (is_dir($directory) && !is_writable($directory)) {
$io->error('Cannot write in directory: ' . $directory);
return 1;
}
$version = $input->getOption('release');
$io->success('Prepare new packages for Kimai ' . $version . ' in ' . $tmpDir);
$gitCmd = sprintf(self::CLONE_CMD, $version);
$zip = 'kimai-release-' . $version . '.zip';
$prefix = 'APP_ENV=prod DATABASE_URL=sqlite:///%kernel.project_dir%/var/data/kimai.sqlite';
$commands = [
'Clone repository' => $gitCmd . ' ' . $tmpDir,
'Install composer dependencies' => sprintf('cd %s && %s composer install --no-dev --optimize-autoloader', $tmpDir, $prefix),
'Create database' => sprintf('cd %s && %s bin/console kimai:install -n', $tmpDir, $prefix),
];
$filesToDelete = [
'.git*',
'.codecov.yml',
'.editorconfig',
'.php_cs.dist',
'phpstan.neon',
'phpunit.xml.dist',
'webpack.config.js',
// this seems to be required, see https://github.com/kevinpapst/kimai2/issues/1586
//'assets/',
'tests/',
'var/cache/*',
'var/data/kimai_test.sqlite',
'var/log/*.log',
'var/sessions/*',
];
foreach ($filesToDelete as $deleteMe) {
$commands['Delete ' . $deleteMe] = 'cd ' . $tmpDir . ' && rm -rf ' . $deleteMe;
}
$commands = array_merge($commands, [
'Create release zip' => 'cd ' . $tmpDir . ' && zip -q -r ' . $directory . '/' . $zip . ' .',
'Remove tmp directory' => 'rm -rf ' . $tmpDir,
]);
$exitCode = 0;
foreach ($commands as $title => $command) {
passthru($command, $exitCode);
if ($exitCode !== 0) {
$io->error('Failed with command: ' . $command);
return -1;
} else {
$io->success($title);
}
}
$io->success(
'New release package available at: ' . PHP_EOL .
$directory . '/' . $zip
);
return 0;
}
}

View File

@@ -24,6 +24,8 @@ use Symfony\Component\HttpKernel\KernelInterface;
/**
* Command used to do the basic installation steps for Kimai.
*
* @codeCoverageIgnore
*/
final class InstallCommand extends Command
{
@@ -156,10 +158,7 @@ final class InstallCommand extends Command
throw new \Exception('Skipped database creation, aborting installation');
}
$options = [];
if ($this->connection->getDatabasePlatform()->getName() !== 'sqlite') {
$options = ['--if-not-exists' => true];
}
$options = ['--if-not-exists' => true];
$command = $this->getApplication()->find('doctrine:database:create');
$result = $command->run(new ArrayInput($options), $output);

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command used to execute all the basic application bootstrapping AFTER "composer install" was executed.
*
* This command is NOT used during runtime and only meant for developers on their local machines.
* This is one of the cases where I don't feel like it is necessary to add tests, so lets "cheat" with:
* @codeCoverageIgnore
*/
class ResetDevelopmentCommand extends AbstractResetCommand
{
protected function getEnvName(): string
{
return 'dev';
}
protected function loadData(InputInterface $input, OutputInterface $output): void
{
$command = $this->getApplication()->find('doctrine:fixtures:load');
$cmdInput = new ArrayInput([]);
$cmdInput->setInteractive(false);
$command->run($cmdInput, $output);
}
}

View File

@@ -0,0 +1,137 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Command;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
use App\Entity\UserPreference;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command used to execute all the basic application bootstrapping AFTER "composer install" was executed.
*
* This command is NOT used during runtime and only meant for developers and the CI processes for quality management.
* This is one of the cases where I don't feel like it is necessary to add tests, so lets "cheat" with:
* @codeCoverageIgnore
*/
class ResetTestCommand extends AbstractResetCommand
{
protected function getEnvName(): string
{
return 'test';
}
protected function loadData(InputInterface $input, OutputInterface $output): void
{
$activity = new Activity();
$activity->setName('Test');
$activity->setComment('Test comment');
$activity->setVisible(true);
$activity->setTimeBudget(100000);
$activity->setBudget(1000);
$this->entityManager->persist($activity);
$customer = new Customer();
$customer->setNumber('1');
$customer->setComment('Test comment');
$customer->setContact('Test');
$customer->setAddress('Test');
$customer->setCompany('Test');
$customer->setName('Test');
$customer->setCountry('DE');
$customer->setCurrency('EUR');
$customer->setPhone('111');
$customer->setFax('222');
$customer->setMobile('333');
$customer->setEmail('test@example.com');
$customer->setTimeBudget(100000);
$customer->setBudget(1000);
$customer->setTimezone('Europe/Berlin');
$this->entityManager->persist($customer);
$project = new Project();
$project->setComment('Test comment');
$project->setName('Test');
$project->setOrderNumber('111');
$project->setTimeBudget(100000);
$project->setBudget(1000);
$project->setCustomer($customer);
$this->entityManager->persist($project);
$users = [
// 0=id, 1=hourly rate, 2=Alias, 3=registration date, 4=title, 5=avatar, 6=enabled, 7=password, 8=roles, 9=username, 10=username canonical, 11=email, 12=email canonical, 13=salt, 14=last login, 15=confirmation token, 16=password requested at, 17=api_token
[1, 53, 'Clara Haynes', '2018-02-06 23:28:57', 'CFO', 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=monsterid&f=y', 1, '$2y$04$kKBYJ8sKCOhhakCjm9sCp.TQdwLTS1FPkPiWn2KBmaCA7xFL0NA42', ['ROLE_CUSTOMER'], 'clara_customer', 'clara_customer', 'clara_customer@example.com', 'clara_customer@example.com', null, null, null, null, '$2y$13$X8/msijlFUgvRaiGLCJP/ep2hRyjpd.TSNz3cuutZLp05FpuBsYfO'],
[2, 82, 'John Doe', '2018-02-06 23:28:57', 'Developer', 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=retro&f=y', 1, '$2y$04$36P/xyhP6FbnfFYbXy7V0.ioSe8HjMlJQFYnlIzz2T6Agfi8ob6jK', [], 'john_user', 'john_user', 'john_user@example.com', 'john_user@example.com', null, null, null, null, '$2y$13$X8/msijlFUgvRaiGLCJP/ep2hRyjpd.TSNz3cuutZLp05FpuBsYfO'],
[3, 35, 'Chris Deactive', '2018-02-06 23:28:57', 'Developer (left company)', 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=retro&f=y', 0, '$2y$04$MLtQBZ9JLzWu1Y01QnNjsuoLm8qC9XRkpUywf6DIbpd9OAL1mEcCi', [], 'chris_user', 'chris_user', 'chris_user@example.com', 'chris_user@example.com', null, null, null, null, '$2y$13$X8/msijlFUgvRaiGLCJP/ep2hRyjpd.TSNz3cuutZLp05FpuBsYfO'],
[4, 35, 'Tony Maier', '2018-02-06 23:28:57', 'Head of Development', 'https://en.gravatar.com/userimage/3533186/bf2163b1dd23f3107a028af0195624e9.jpeg', 1, '$2y$04$rqxiiExfUVzIYRVL2x4JJumQWNPIG6PazXwrSJm/VQFEesR08Uj5i', ['ROLE_TEAMLEAD'], 'tony_teamlead', 'tony_teamlead', 'tony_teamlead@example.com', 'tony_teamlead@example.com', null, null, null, null, '$2y$13$X8/msijlFUgvRaiGLCJP/ep2hRyjpd.TSNz3cuutZLp05FpuBsYfO'],
[5, 81, 'Anna Smith', '2018-02-06 23:28:57', 'Administrator', null, 1, '$2y$04$ct/rVb.naDzYZECnvfTJ2uns/zPHv8.8KcunhTjYFwWQeg1dywI8G', ['ROLE_ADMIN'], 'anna_admin', 'anna_admin', 'anna_admin@example.com', 'anna_admin@example.com', null, null, null, null, '$2y$13$X8/msijlFUgvRaiGLCJP/ep2hRyjpd.TSNz3cuutZLp05FpuBsYfO'],
[6, 46, null, '2018-02-06 23:28:57', 'Super Administrator', '/bundles/avanzuadmintheme/img/avatar.png', 1, '$2y$04$kuhEEPw/CBMYc3x7SOv27eC1hQSmrtFvgJI2ULRuJeddAVDyrPKJ2', ['ROLE_SUPER_ADMIN'], 'susan_super', 'susan_super', 'susan_super@example.com', 'susan_super@example.com', null, '2020-04-14 09:50:38', null, null, '$2y$13$X8/msijlFUgvRaiGLCJP/ep2hRyjpd.TSNz3cuutZLp05FpuBsYfO'],
[7, null, 'Test User 1', null, 'Quality Tester 1', null, 1, '$2y$04$kuhEEPw/CBMYc3x7SOv27eC1hQSmrtFvgJI2ULRuJeddAVDyrPKJ2', [], 'test_user_1', 'test_user_1', 'test_user_1@example.com', 'test_user_1@example.com', null, null, null, null, '$2y$13$X8/msijlFUgvRaiGLCJP/ep2hRyjpd.TSNz3cuutZLp05FpuBsYfO'],
[8, null, 'Test User 2', null, 'Quality Tester 2', null, 1, '$2y$04$kuhEEPw/CBMYc3x7SOv27eC1hQSmrtFvgJI2ULRuJeddAVDyrPKJ2', [], 'test_user_2', 'test_user_2', 'test_user_2@example.com', 'test_user_2@example.com', null, null, null, null, '$2y$13$X8/msijlFUgvRaiGLCJP/ep2hRyjpd.TSNz3cuutZLp05FpuBsYfO'],
];
$userEntities = [];
foreach ($users as $userConf) {
$user = new User();
if ($userConf[1] !== null) {
$user->setPreferenceValue(UserPreference::HOURLY_RATE, $userConf[1]);
}
if ($userConf[2] !== null) {
$user->setAlias($userConf[2]);
}
if ($userConf[3] !== null) {
$user->setRegisteredAt(new \DateTime($userConf[3]));
}
if ($userConf[4] !== null) {
$user->setTitle($userConf[4]);
}
if ($userConf[5] !== null) {
$user->setAvatar($userConf[5]);
}
if ($userConf[6] !== null) {
$user->setEnabled((bool) $userConf[6]);
}
$user->setPassword($userConf[7]);
if ($userConf[8] !== null && !empty($userConf[8])) {
$user->setRoles($userConf[8]);
} else {
$user->setRoles(['ROLE_USER']);
}
$user->setUsername($userConf[9]);
if ($userConf[10] !== null) {
$user->setUsernameCanonical($userConf[10]);
}
if ($userConf[11] !== null) {
$user->setEmail($userConf[11]);
}
if ($userConf[12] !== null) {
$user->setEmailCanonical($userConf[12]);
}
if ($userConf[17] !== null) {
$user->setApiToken($userConf[17]);
}
$this->entityManager->persist($user);
$userEntities[] = $user;
}
$team = new Team();
$team->setName('Test team');
$team->setTeamLead($userEntities[6]);
$team->addUser($userEntities[7]);
$this->entityManager->persist($team);
$this->entityManager->flush();
}
}