Consolidate bundled plugin schema into the core migration chain

The core chain already owns and reproduces the full bundled schema (deploys run
`flask db upgrade` only). The per-plugin Alembic baselines duplicated those
tables, so `flask plugin upgrade-all` would conflict - a footgun. Remove the 6
bundled plugin migration dirs; the per-plugin Alembic helpers
(alembic_template, PluginMigrationRunner) remain for external/filesystem
plugins. upgrade-all now cleanly no-ops for bundled plugins.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-06-26 14:48:07 -04:00
parent 919e0b8600
commit 4f1638bda0
28 changed files with 7 additions and 662 deletions

View File

@@ -1,30 +0,0 @@
[alembic]
script_location = .
prepend_sys_path = .
file_template = %%(rev)s_%%(slug)s
[logging]
keys = root
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = INFO
formatter = generic
[formatter_generic]
format = %%(levelname)-5.5s [%%(name)s] %%(message)s

View File

@@ -1,12 +0,0 @@
"""Alembic env.py for the computers plugin.
Thin shim that sets PLUGIN_NAME then delegates to the shared template at
shopdb.plugins.alembic_template, which filters MetaData to only this
plugin's tables and runs Alembic against the Flask app's engine.
"""
import os
os.environ['PLUGIN_NAME'] = 'computers'
from shopdb.plugins.alembic_template import run_migrations
run_migrations()

View File

@@ -1,23 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -1,27 +0,0 @@
"""computers plugin: baseline schema
Creates every table owned by the computers plugin per
shopdb.plugins.alembic_template.PLUGIN_TABLE_OWNERS. The table definitions
are derived from the SQLAlchemy models at migration runtime so this stays
in lockstep with the model layer without duplication.
Revision ID: 0001_baseline_computers
Revises:
Create Date: 2026-05-30
"""
from shopdb.plugins.alembic_template import create_plugin_tables, drop_plugin_tables
revision = '0001_baseline_computers'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
create_plugin_tables('computers')
def downgrade():
drop_plugin_tables('computers')

View File

@@ -1,44 +0,0 @@
"""Add PC hardware make/model + installed-app version string
Adds computers.vendorid + computers.modelnumberid (PCs carry vendor/model like
equipment) and computerinstalledapps.installedversion (raw version string from
automated collection, when there is no curated AppVersion). Supports moving the
PC form and the collector off the legacy Machine model onto the asset/computer
model (ADR-001).
Revision ID: 0002_pc_hardware
Revises: 0001_baseline_computers
Create Date: 2026-06-26
"""
from alembic import op
import sqlalchemy as sa
revision = '0002_pc_hardware'
down_revision = '0001_baseline_computers'
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table('computers') as batch_op:
batch_op.add_column(sa.Column('vendorid', sa.Integer(), nullable=True))
batch_op.add_column(sa.Column('modelnumberid', sa.Integer(), nullable=True))
batch_op.create_foreign_key('fk_computers_vendor', 'vendors',
['vendorid'], ['vendorid'])
batch_op.create_foreign_key('fk_computers_model', 'models',
['modelnumberid'], ['modelnumberid'])
with op.batch_alter_table('computerinstalledapps') as batch_op:
batch_op.add_column(sa.Column('installedversion', sa.String(length=100),
nullable=True))
def downgrade():
with op.batch_alter_table('computerinstalledapps') as batch_op:
batch_op.drop_column('installedversion')
with op.batch_alter_table('computers') as batch_op:
batch_op.drop_constraint('fk_computers_model', type_='foreignkey')
batch_op.drop_constraint('fk_computers_vendor', type_='foreignkey')
batch_op.drop_column('modelnumberid')
batch_op.drop_column('vendorid')

View File

@@ -1,30 +0,0 @@
[alembic]
script_location = .
prepend_sys_path = .
file_template = %%(rev)s_%%(slug)s
[logging]
keys = root
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = INFO
formatter = generic
[formatter_generic]
format = %%(levelname)-5.5s [%%(name)s] %%(message)s

View File

@@ -1,12 +0,0 @@
"""Alembic env.py for the equipment plugin.
Thin shim that sets PLUGIN_NAME then delegates to the shared template at
shopdb.plugins.alembic_template, which filters MetaData to only this
plugin's tables and runs Alembic against the Flask app's engine.
"""
import os
os.environ['PLUGIN_NAME'] = 'equipment'
from shopdb.plugins.alembic_template import run_migrations
run_migrations()

View File

@@ -1,23 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -1,27 +0,0 @@
"""equipment plugin: baseline schema
Creates every table owned by the equipment plugin per
shopdb.plugins.alembic_template.PLUGIN_TABLE_OWNERS. The table definitions
are derived from the SQLAlchemy models at migration runtime so this stays
in lockstep with the model layer without duplication.
Revision ID: 0001_baseline_equipment
Revises:
Create Date: 2026-05-30
"""
from shopdb.plugins.alembic_template import create_plugin_tables, drop_plugin_tables
revision = '0001_baseline_equipment'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
create_plugin_tables('equipment')
def downgrade():
drop_plugin_tables('equipment')

View File

@@ -1,30 +0,0 @@
[alembic]
script_location = .
prepend_sys_path = .
file_template = %%(rev)s_%%(slug)s
[logging]
keys = root
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = INFO
formatter = generic
[formatter_generic]
format = %%(levelname)-5.5s [%%(name)s] %%(message)s

View File

@@ -1,12 +0,0 @@
"""Alembic env.py for the network plugin.
Thin shim that sets PLUGIN_NAME then delegates to the shared template at
shopdb.plugins.alembic_template, which filters MetaData to only this
plugin's tables and runs Alembic against the Flask app's engine.
"""
import os
os.environ['PLUGIN_NAME'] = 'network'
from shopdb.plugins.alembic_template import run_migrations
run_migrations()

View File

@@ -1,23 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -1,27 +0,0 @@
"""network plugin: baseline schema
Creates every table owned by the network plugin per
shopdb.plugins.alembic_template.PLUGIN_TABLE_OWNERS. The table definitions
are derived from the SQLAlchemy models at migration runtime so this stays
in lockstep with the model layer without duplication.
Revision ID: 0001_baseline_network
Revises:
Create Date: 2026-05-30
"""
from shopdb.plugins.alembic_template import create_plugin_tables, drop_plugin_tables
revision = '0001_baseline_network'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
create_plugin_tables('network')
def downgrade():
drop_plugin_tables('network')

View File

@@ -1,30 +0,0 @@
[alembic]
script_location = .
prepend_sys_path = .
file_template = %%(rev)s_%%(slug)s
[logging]
keys = root
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = INFO
formatter = generic
[formatter_generic]
format = %%(levelname)-5.5s [%%(name)s] %%(message)s

View File

@@ -1,12 +0,0 @@
"""Alembic env.py for the notifications plugin.
Thin shim that sets PLUGIN_NAME then delegates to the shared template at
shopdb.plugins.alembic_template, which filters MetaData to only this
plugin's tables and runs Alembic against the Flask app's engine.
"""
import os
os.environ['PLUGIN_NAME'] = 'notifications'
from shopdb.plugins.alembic_template import run_migrations
run_migrations()

View File

@@ -1,23 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -1,27 +0,0 @@
"""notifications plugin: baseline schema
Creates every table owned by the notifications plugin per
shopdb.plugins.alembic_template.PLUGIN_TABLE_OWNERS. The table definitions
are derived from the SQLAlchemy models at migration runtime so this stays
in lockstep with the model layer without duplication.
Revision ID: 0001_baseline_notifications
Revises:
Create Date: 2026-05-30
"""
from shopdb.plugins.alembic_template import create_plugin_tables, drop_plugin_tables
revision = '0001_baseline_notifications'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
create_plugin_tables('notifications')
def downgrade():
drop_plugin_tables('notifications')

View File

@@ -1 +0,0 @@
"""Printers plugin migrations."""

View File

@@ -1,30 +0,0 @@
[alembic]
script_location = .
prepend_sys_path = .
file_template = %%(rev)s_%%(slug)s
[logging]
keys = root
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = INFO
formatter = generic
[formatter_generic]
format = %%(levelname)-5.5s [%%(name)s] %%(message)s

View File

@@ -1,12 +0,0 @@
"""Alembic env.py for the printers plugin.
Thin shim that sets PLUGIN_NAME then delegates to the shared template at
shopdb.plugins.alembic_template, which filters MetaData to only this
plugin's tables and runs Alembic against the Flask app's engine.
"""
import os
os.environ['PLUGIN_NAME'] = 'printers'
from shopdb.plugins.alembic_template import run_migrations
run_migrations()

View File

@@ -1,23 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -1,27 +0,0 @@
"""printers plugin: baseline schema
Creates every table owned by the printers plugin per
shopdb.plugins.alembic_template.PLUGIN_TABLE_OWNERS. The table definitions
are derived from the SQLAlchemy models at migration runtime so this stays
in lockstep with the model layer without duplication.
Revision ID: 0001_baseline_printers
Revises:
Create Date: 2026-05-30
"""
from shopdb.plugins.alembic_template import create_plugin_tables, drop_plugin_tables
revision = '0001_baseline_printers'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
create_plugin_tables('printers')
def downgrade():
drop_plugin_tables('printers')

View File

@@ -1,44 +0,0 @@
"""printers plugin: add modelsupplies table
Data-driven model -> toner/drum/waste part-number mapping. Replaces the old
hardcoded supply_parts table.
Revision ID: 0002_modelsupplies_printers
Revises: 0001_baseline_printers
Create Date: 2026-06-25
"""
import sqlalchemy as sa
from alembic import op
revision = '0002_modelsupplies_printers'
down_revision = '0001_baseline_printers'
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
'modelsupplies',
sa.Column('modelsupplyid', sa.Integer, primary_key=True),
sa.Column('modelnumberid', sa.Integer,
sa.ForeignKey('models.modelnumberid'), nullable=False),
sa.Column('supplytype', sa.String(20), nullable=False, server_default='toner'),
sa.Column('color', sa.String(20), nullable=False, server_default='none'),
sa.Column('capacitytier', sa.String(20), nullable=False, server_default='standard'),
sa.Column('partnumber', sa.String(50), nullable=False),
sa.Column('marketingname', sa.String(120)),
sa.Column('pageyield', sa.Integer),
sa.Column('notes', sa.Text),
sa.Column('createddate', sa.DateTime, nullable=False),
sa.Column('modifieddate', sa.DateTime, nullable=False),
sa.Column('isactive', sa.Boolean, nullable=False, server_default=sa.true()),
sa.UniqueConstraint('modelnumberid', 'partnumber', name='uq_modelsupply_part'),
)
op.create_index('idx_modelsupplies_modelnumberid', 'modelsupplies', ['modelnumberid'])
def downgrade():
op.drop_index('idx_modelsupplies_modelnumberid', table_name='modelsupplies')
op.drop_table('modelsupplies')

View File

@@ -1,30 +0,0 @@
[alembic]
script_location = .
prepend_sys_path = .
file_template = %%(rev)s_%%(slug)s
[logging]
keys = root
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = INFO
formatter = generic
[formatter_generic]
format = %%(levelname)-5.5s [%%(name)s] %%(message)s

View File

@@ -1,12 +0,0 @@
"""Alembic env.py for the usb plugin.
Thin shim that sets PLUGIN_NAME then delegates to the shared template at
shopdb.plugins.alembic_template, which filters MetaData to only this
plugin's tables and runs Alembic against the Flask app's engine.
"""
import os
os.environ['PLUGIN_NAME'] = 'usb'
from shopdb.plugins.alembic_template import run_migrations
run_migrations()

View File

@@ -1,23 +0,0 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -1,27 +0,0 @@
"""usb plugin: baseline schema
Creates every table owned by the usb plugin per
shopdb.plugins.alembic_template.PLUGIN_TABLE_OWNERS. The table definitions
are derived from the SQLAlchemy models at migration runtime so this stays
in lockstep with the model layer without duplication.
Revision ID: 0001_baseline_usb
Revises:
Create Date: 2026-05-30
"""
from shopdb.plugins.alembic_template import create_plugin_tables, drop_plugin_tables
revision = '0001_baseline_usb'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
create_plugin_tables('usb')
def downgrade():
drop_plugin_tables('usb')