Skip to content

Commit

Permalink
Add a migration system (#2760)
Browse files Browse the repository at this point in the history
* Add a Minz_Migrator class

Until now, we updated the database structure somewhere in the code but
it wasn't always consistent and somehow complicated to find. Also, this
code was always checked for nothing.

The Migrator aims to improve and ease the creation of migrations. It
should improve the way we apply the updates, making the update server
almost useless.

References:

- example of migration (before Migrator): cc0db9a#diff-11a53443fa81512b128c66b065df0679R10
- update server: https://github.com/FreshRSS/update.freshrss.org
- PR moving the code of the update server to the core: #1760

* Automatically apply migrations

For now, administrators are used to have nothing to do during an update
else than getting the new code. I suggest to keep this behaviour and
automatically apply migrations if we detect new ones.

Another solution would be to create a CLI command and ask admins to call
it after getting the new code. It could hide migrations errors to end
users, but admin can forget to apply migrations since there are not used
to it.

* Add documentation for Minz Migrator

* Execute migrations even if next ones are applied

* Change mechanism to prevent multiple update at once

* Use mkdir to create the lock and to test it exists

Reference: https://stackoverflow.com/a/731634

* Append .lock to applied_migrations_path

There are no needs to define another file to serve as a lock.

* Change migrations naming convention

* Apply suggestions from code review

Co-Authored-By: Alexandre Alapetite <alexandre@alapetite.fr>

* Perform a low-cost migration versions comparaison

* Clarify version numbers concerning the migration system

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
  • Loading branch information
marienfressinaud and Alkarex committed Jul 8, 2020
1 parent 4894a9e commit 8619cf6
Show file tree
Hide file tree
Showing 18 changed files with 760 additions and 34 deletions.
2 changes: 1 addition & 1 deletion app/install.php
Expand Up @@ -674,7 +674,7 @@ function printStep5() {
case 4:
break;
case 5:
if (deleteInstall()) {
if (setupMigrations() && deleteInstall()) {
header('Location: index.php');
}
break;
Expand Down
Empty file added app/migrations/.keep
Empty file.
4 changes: 4 additions & 0 deletions cli/do-install.php
Expand Up @@ -109,6 +109,10 @@

accessRights();

if (!setupMigrations()) {
fail('FreshRSS access right problem while creating migrations version file!');
}

if (!deleteInstall()) {
fail('FreshRSS access right problem while deleting install file!');
}
Expand Down
3 changes: 3 additions & 0 deletions constants.php
Expand Up @@ -6,6 +6,8 @@
define('FRESHRSS_WEBSITE', 'https://freshrss.org');
define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/');

define('APP_NAME', 'FreshRSS');

define('FRESHRSS_PATH', __DIR__);
define('PUBLIC_PATH', FRESHRSS_PATH . '/p');
define('PUBLIC_TO_INDEX_PATH', '/i');
Expand All @@ -14,6 +16,7 @@
define('LIB_PATH', FRESHRSS_PATH . '/lib');
define('APP_PATH', FRESHRSS_PATH . '/app');
define('CORE_EXTENSIONS_PATH', LIB_PATH . '/core-extensions');
define('TESTS_PATH', FRESHRSS_PATH . '/tests');
//</Not customisable>

function safe_define($name, $value) {
Expand Down
2 changes: 2 additions & 0 deletions data/.gitignore
Expand Up @@ -8,3 +8,5 @@ no-cache.txt
update.php
tos.html
opml.xml
applied_migrations.txt
applied_migrations.txt.lock
2 changes: 1 addition & 1 deletion docs/en/developers/01_First_steps.md
Expand Up @@ -57,7 +57,7 @@ The `TAG` variable can be anything (e.g. `local`). You can target a specific arc

# Project architecture

**TODO**
- the PHP framework: [Minz](Minz/index.md)

# Extensions

Expand Down
27 changes: 0 additions & 27 deletions docs/en/developers/03_Backend/02_Minz.md

This file was deleted.

19 changes: 19 additions & 0 deletions docs/en/developers/Minz/index.md
@@ -0,0 +1,19 @@
# Minz

Minz is the homemade PHP framework used by FreshRSS.

The documentation is still incomplete and it would be great to explain:

- routing, controllers and actions
- configuration
- models and database
- views
- URLs management
- sessions
- internationalisation
- extensions
- mailer

Existing documentation includes:

- [How to manage migrations](migrations.md)
39 changes: 39 additions & 0 deletions docs/en/developers/Minz/migrations.md
@@ -0,0 +1,39 @@
# How to manage migrations with Minz

Migrations are the way to modify the database or the structure of files under the `data/` path.

## How to write a migration?

Migrations are placed under the `app/migrations` folder.

Good practice is to prepend the filename by the current date and explain what does the migration do in few words (e.g. `2020_01_11_CreateFooTable.php`).

The files must contain a class which name starts with `FreshRSS_Migration_`, followed by the basename of the file (e.g. `FreshRSS_Migration_2020_01_11_CreateFooTable`).

The class must declare a `migrate` static function. It must return `true` or a string to indicate the migration is applied, or `false` otherwise. It can also raise an exception: the message will be used to detail the error.

Example:

```php
// File: app/migrations/2020_01_11_CreateFooTable.php
class FreshRSS_Migration_2020_01_11_CreateFooTable {
public static function migrate() {
$pdo = new MinzPDOSQLite('sqlite:/some/path/db.sqlite');
$result = $pdo->exec('CREATE TABLE foos (bar TEXT)');
if ($result === false) {
$error = $pdo->errorInfo();
raise Exception('Error in SQL statement: ' . $error[2]);
}

return true;
}
}
```

## How to apply migrations?

They are automatically applied one by one when a user accesses FreshRSS.

Before being applied, migrations are sorted by filenames (see the [`strnatcmp`](https://php.net/strnatcmp) function). Already applied migrations are skipped (the list can be found in the `data/applied_migrations.txt` file).

To ensure migrations are not applied several times if two users access FreshRSS at the same time, a folder named `data/applied_migrations.txt.lock` is created, then deleted at the end of the process.

0 comments on commit 8619cf6

Please sign in to comment.