Skip to content

Commit

Permalink
Setup database and migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
marienfressinaud committed May 8, 2020
1 parent 51720fd commit 4ddba8c
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
/.env
/vendor

/data/*
!/data/.keep
67 changes: 67 additions & 0 deletions cli
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/env php
<?php

/**
* @author Marien Fressinaud <[email protected]>
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL
*/

if (php_sapi_name() !== 'cli') {
die('This script must be called from command line.');
}

// Setup the Minz framework
$app_path = __DIR__;

include $app_path . '/autoload.php';
\Minz\Configuration::load('dotenv', $app_path);
\Minz\Environment::initialize();

// Read command line parameters to create a Request
$shortopts = 'p::';
$longopts = [
'request:',
];
$options = getopt($shortopts, $longopts);

if (!isset($options['request']) || !$options['request']) {
die("--request is required and must be a valid URI path.\n");
}

$parameters = [];
if (isset($options['p'])) {
$cli_parameters = $options['p'];
if (!is_array($cli_parameters)) {
$cli_parameters = array($cli_parameters);
}

foreach ($cli_parameters as $parameter) {
if (strpos($parameter, '=') === false) {
die("Parameters must be in the form key=value ({$parameter}).\n");
}

list($key, $value) = explode('=', $parameter);
$parameters[$key] = $value;
}
}

try {
$request = new \Minz\Request('cli', $options['request'], $parameters);
} catch (\Minz\Errors\RequestError $e) {
die($e->getMessage() . "\n");
}

// Initialize the Application and execute the request to get a Response
$application = new \flusio\cli\Application();
$response = $application->run($request);

// Display the content
echo $response->render();
echo "\n";

$code = $response->code();
if ($code >= 200 && $code < 300) {
exit(0);
} else {
exit(1);
}
Empty file added data/.keep
Empty file.
3 changes: 3 additions & 0 deletions docker/bin/cli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

USER=$(id -u):$(id -g) docker-compose -f ./docker/docker-compose.yml run --rm --no-deps php php ./cli $*
9 changes: 9 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,14 @@ services:
links:
- database

database:
image: postgres:12-alpine
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"

volumes:
composer: {}
41 changes: 41 additions & 0 deletions src/cli/Application.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace flusio\cli;

/**
* This is the central class for the CLI. It is called from the cli file.
*
* @author Marien Fressinaud <[email protected]>
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL
*/
class Application
{
/** @var \Minz\Engine **/
private $engine;

/**
* Setup a Router and declare its routes.
*/
public function __construct()
{
// Initialize the routes
$router = new \Minz\Router();
$router->addRoute('cli', '/system/setup', 'cli/System#setup');
$router->addRoute('cli', '/database/status', 'cli/Database#status');

$this->engine = new \Minz\Engine($router);
\Minz\Url::setRouter($router);
}

/**
* Execute a request.
*
* @param \Minz\Request $request
*
* @return \Minz\Response
*/
public function run($request)
{
return $this->engine->run($request);
}
}
36 changes: 36 additions & 0 deletions src/cli/Database.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace flusio\cli;

use Minz\Response;

/**
* Manipulate the database of the application.
*
* @author Marien Fressinaud <[email protected]>
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL
*/
class Database
{
/**
* Return whether the database can be reached or not.
*
* @return \Minz\Response
*/
public function status()
{
try {
$database = \Minz\Database::get(false);
$result = $database->exec('SELECT 1');
if ($result !== false) {
return Response::text(200, 'Database status: OK');
} else {
$status = $database->errorInfo()[2];
return Response::text(500, 'Database status: ' . $status);
}
} catch (\Minz\Errors\DatabaseError $e) {
$status = $e->getMessage();
return Response::text(500, 'Database status: ' . $status);
}
}
}
115 changes: 115 additions & 0 deletions src/cli/System.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

namespace flusio\cli;

use Minz\Response;

/**
* Manipulate the system to setup the application.
*
* @author Marien Fressinaud <[email protected]>
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL
*/
class System
{
/**
* Call init or migrate depending on the presence of a migrations version file.
*
* @return \Minz\Response
*/
public function setup()
{
$data_path = \Minz\Configuration::$data_path;
$migrations_version_path = $data_path . '/migrations_version.txt';

if (file_exists($migrations_version_path)) {
return $this->migrate();
} else {
return $this->init();
}
}

/**
* Initialize the database and set the migration version.
*
* @return \Minz\Response
*/
private function init()
{
$app_path = \Minz\Configuration::$app_path;
$data_path = \Minz\Configuration::$data_path;
$schema_path = $app_path . '/src/schema.sql';
$migrations_path = $app_path . '/src/migrations';
$migrations_version_path = $data_path . '/migrations_version.txt';

\Minz\Database::reset();

$schema = file_get_contents($schema_path);
if ($schema) {
$database = \Minz\Database::get();
$result = $database->exec($schema);
if (!$result) {
return Response::text(500, 'The database schema couldn’t be loaded.');
}
}

$migrator = new \Minz\Migrator($migrations_path);
$version = $migrator->lastVersion();
$saved = @file_put_contents($migrations_version_path, $version);
if ($saved === false) {
return Response::text(500, 'Cannot create the migrations version file.');
}

return Response::text(200, 'The system has been initialized.');
}

/**
* Execute the migrations under src/migrations/. The version is stored in
* the data/migrations_version.txt file.
*
* @return \Minz\Response
*/
private function migrate()
{
$app_path = \Minz\Configuration::$app_path;
$data_path = \Minz\Configuration::$data_path;
$migrations_path = $app_path . '/src/migrations';
$migrations_version_path = $data_path . '/migrations_version.txt';

$migration_version = @file_get_contents($migrations_version_path);
if ($migration_version === false) {
return Response::text(500, 'Cannot read the migrations version file.');
}

$migrator = new \Minz\Migrator($migrations_path);
$migration_version = trim($migration_version);
if ($migration_version) {
$migrator->setVersion($migration_version);
}

if ($migrator->upToDate()) {
return Response::text(200, 'Your system is already up to date.');
}

$results = $migrator->migrate();

$new_version = $migrator->version();
$saved = @file_put_contents($migrations_version_path, $new_version);
if ($saved === false) {
return Response::text(500, "Cannot save the migrations version file (version: {$version}).");
}

$code = 200;
$text = '';
foreach ($results as $migration => $result) {
if ($result === true) {
$result = 'OK';
} elseif ($result === false) {
$code = 500;
$result = 'KO';
}
$text .= "\n" . $migration . ': ' . $result;
}
return Response::text($code, $text);
}
}
Empty file added src/migrations/.keep
Empty file.
Empty file added src/schema.sql
Empty file.

0 comments on commit 4ddba8c

Please sign in to comment.