mirror of
https://gitlab.gnome.org/World/Authenticator.git
synced 2025-03-04 08:44:40 +01:00
Implement a database migration manager using yoyo
This commit is contained in:
parent
1531eab89b
commit
9721f4c645
7 changed files with 153 additions and 37 deletions
|
@ -213,6 +213,44 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "yoyo-migrations",
|
||||
"buildsystem" : "simple",
|
||||
"build-commands" : [
|
||||
"pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} yoyo-migrations"
|
||||
],
|
||||
"ensure-writable" : [
|
||||
"/lib/python*/site-packages/easy-install.pth",
|
||||
"/lib/python*/site-packages/setuptools.pth",
|
||||
"/app/lib/python*/site-packages/easy-install.pth",
|
||||
"/app/lib/python*/site-packages/setuptools.pth"
|
||||
],
|
||||
"sources" : [
|
||||
{
|
||||
"type" : "file",
|
||||
"url" : "https://files.pythonhosted.org/packages/5f/cf/f6d468c6929e8739cd12bf1a9cf3719e0be739e09acfaddc0f9ade67e67c/yoyo_migrations-6.1.0-py2.py3-none-any.whl",
|
||||
"sha256" : "95e5c49a797873d3b86e5a7714c1a714bea8728a6fc1d6f6f5019d3d058471e5"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"url": "https://files.pythonhosted.org/packages/79/42/d717cc2b4520fb09e45b344b1b0b4e81aa672001dd128c180fabc655c341/text_unidecode-1.2-py2.py3-none-any.whl",
|
||||
"sha256": "801e38bd550b943563660a91de8d4b6fa5df60a542be9093f7abf819f86050cc"
|
||||
}
|
||||
],
|
||||
"modules": [{
|
||||
"name": "iniherit",
|
||||
"buildsystem" : "simple",
|
||||
"build-commands" : [
|
||||
"python3 setup.py install --prefix=/app"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://files.pythonhosted.org/packages/65/a5/5bb95059c92c23560a80bcd599bc737a4175b275b3a577cb19f66bd302e3/iniherit-0.3.9.tar.gz",
|
||||
"sha256": "06d90849ff0c4fadb7e255ce31e7c8e188a99af90d761435c72b79b36adbb67a"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name" : "libhandy",
|
||||
"buildsystem" : "meson",
|
||||
|
|
|
@ -2,4 +2,5 @@ beautifulsoup4==4.7.0
|
|||
Pillow==5.0.0
|
||||
pyotp==2.2.7
|
||||
pyzbar==0.1.8
|
||||
requests==2.21.0
|
||||
requests==2.21.0
|
||||
yoyo-migrations==6.1.0
|
|
@ -0,0 +1,19 @@
|
|||
"""
|
||||
Create table accounts
|
||||
"""
|
||||
|
||||
from yoyo import step
|
||||
|
||||
__depends__ = {}
|
||||
|
||||
steps = [
|
||||
step('''
|
||||
CREATE TABLE "accounts" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"username" VARCHAR NOT NULL,
|
||||
"provider" VARCHAR NOT NULL,
|
||||
"secret_id" VARCHAR NOT NULL UNIQUE
|
||||
''',
|
||||
'DROP TABLE "accounts"',
|
||||
ignore_errors='apply')
|
||||
]
|
|
@ -0,0 +1,22 @@
|
|||
"""
|
||||
Create table providers
|
||||
"""
|
||||
|
||||
from yoyo import step
|
||||
|
||||
__depends__ = {'authenticator_20190525_01_GdUDU-create-table-accounts'}
|
||||
|
||||
steps = [
|
||||
step(
|
||||
'''CREATE TABLE "providers" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"name" VARCHAR NOT NULL,
|
||||
"website" VARCHAR NULL,
|
||||
"doc_url" VARCHAR NULL,
|
||||
"image" VARCHAR NULL
|
||||
)
|
||||
''',
|
||||
'DROP TABLE "providers"',
|
||||
ignore_errors='apply'
|
||||
)
|
||||
]
|
|
@ -0,0 +1,33 @@
|
|||
"""
|
||||
Restore old accounts
|
||||
"""
|
||||
|
||||
from yoyo import step
|
||||
|
||||
__depends__ = {'authenticator_20190525_02_mdR2o-create-table-providers'}
|
||||
|
||||
|
||||
def do_step(conn):
|
||||
accounts = conn.execute("SELECT * FROM accounts").fetchall()
|
||||
_accounts = []
|
||||
cursor = conn.cursor()
|
||||
for account_id, username, provider_name, secret_id in accounts:
|
||||
cursor.execute("INSERT INTO providers (name) VALUES (?)", (provider_name, ))
|
||||
provider_id = cursor.lastrowid
|
||||
_accounts.append((account_id, username, provider_id, secret_id))
|
||||
|
||||
cursor.execute(" ALTER TABLE accounts RENAME TO tmp;")
|
||||
cursor.execute(''' CREATE TABLE "accounts" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"username" VARCHAR NOT NULL,
|
||||
"token_id" VARCHAR NOT NULL UNIQUE,
|
||||
"provider" INTEGER NOT NULL
|
||||
)''')
|
||||
cursor.execute("DROP TABLE tmp;")
|
||||
for account_id, username, provider_id, token_id in _accounts:
|
||||
cursor.execute("INSERT INTO accounts (username, provider, token_id) VALUES (?, ?, ?)", (username, provider_id, token_id))
|
||||
|
||||
|
||||
steps = [
|
||||
step(do_step, ignore_errors='apply')
|
||||
]
|
|
@ -20,6 +20,7 @@ import sqlite3
|
|||
from os import path, makedirs
|
||||
from gi.repository import GLib, Gio
|
||||
from collections import namedtuple
|
||||
from shutil import move
|
||||
from Authenticator.models import Logger
|
||||
import json
|
||||
|
||||
|
@ -37,11 +38,12 @@ class Database:
|
|||
db_version = 7
|
||||
|
||||
def __init__(self):
|
||||
self.migrations_dir = path.join(path.dirname(__file__), '../migrations')
|
||||
database_created = self.__create_database_file()
|
||||
self.conn = sqlite3.connect(self.db_file)
|
||||
if database_created:
|
||||
self.__create_tables()
|
||||
self.__fill_providers()
|
||||
self.__apply_migrations()
|
||||
self.conn = sqlite3.connect(self.db_file)
|
||||
self.__fill_providers()
|
||||
|
||||
@staticmethod
|
||||
def get_default():
|
||||
|
@ -51,9 +53,13 @@ class Database:
|
|||
return Database.instance
|
||||
|
||||
@property
|
||||
def db_file(self):
|
||||
def db_dir(self):
|
||||
return path.join(GLib.get_user_config_dir(),
|
||||
'Authenticator',
|
||||
'Authenticator')
|
||||
|
||||
@property
|
||||
def db_file(self):
|
||||
return path.join(self.db_dir,
|
||||
'database-{}.db'.format(str(Database.db_version))
|
||||
)
|
||||
|
||||
|
@ -249,41 +255,31 @@ class Database:
|
|||
"""
|
||||
Create an empty database file for the first start of the application.
|
||||
"""
|
||||
makedirs(path.dirname(self.db_file), exist_ok=True)
|
||||
if not path.exists(self.db_file):
|
||||
with open(self.db_file, 'w') as file_obj:
|
||||
file_obj.write('')
|
||||
return True
|
||||
return False
|
||||
created = False
|
||||
for i in range(3, self.db_version + 1):
|
||||
db_file = path.join(self.db_dir, "database-{}.db".format(i))
|
||||
if path.exists(db_file):
|
||||
move(db_file, self.db_file)
|
||||
created = True
|
||||
break
|
||||
if not created:
|
||||
makedirs(path.dirname(self.db_file), exist_ok=True)
|
||||
if not path.exists(self.db_file):
|
||||
with open(self.db_file, 'w') as file_obj:
|
||||
file_obj.write('')
|
||||
created = True
|
||||
return created
|
||||
|
||||
def __create_tables(self):
|
||||
def __apply_migrations(self):
|
||||
"""
|
||||
Create the needed tables to run the application.
|
||||
"""
|
||||
accounts_table = '''
|
||||
CREATE TABLE "accounts" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"username" VARCHAR NOT NULL,
|
||||
"token_id" VARCHAR NOT NULL UNIQUE,
|
||||
"provider" INTEGER NOT NULL
|
||||
);
|
||||
'''
|
||||
providers_table = '''
|
||||
CREATE TABLE "providers" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
|
||||
"name" VARCHAR NOT NULL,
|
||||
"website" VARCHAR NULL,
|
||||
"doc_url" VARCHAR NULL,
|
||||
"image" VARCHAR NULL
|
||||
)
|
||||
'''
|
||||
try:
|
||||
self.conn.execute(accounts_table)
|
||||
self.conn.execute(providers_table)
|
||||
self.conn.commit()
|
||||
except Exception as error:
|
||||
Logger.error("[SQL] Impossible to create table 'accounts'")
|
||||
Logger.error(str(error))
|
||||
from yoyo import read_migrations
|
||||
from yoyo import get_backend
|
||||
backend = get_backend('sqlite:///' + self.db_file)
|
||||
migrations = read_migrations(self.migrations_dir)
|
||||
with backend.lock():
|
||||
backend.apply_migrations(backend.to_apply(migrations))
|
||||
|
||||
def __count(self, table_name):
|
||||
query = "SELECT COUNT(id) AS count FROM " + table_name
|
||||
|
|
7
yoyo.ini
Normal file
7
yoyo.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
[DEFAULT]
|
||||
sources = ./src/Authenticator/migrations
|
||||
migration_table = _yoyo_migration
|
||||
batch_mode = on
|
||||
verbosity = 0
|
||||
editor = code {}
|
||||
prefix = authenticator_
|
Loading…
Add table
Reference in a new issue