"""apitokens.resourcescopes: pin a service token to specific resource scopes. A geenforce.fetch token handed to a fleet (e.g. displays) should reach only its own manifest scope(s) and the blobs those scopes ship, not every scope by name or every blob by hash. This nullable JSON column carries that allowlist; NULL = unrestricted, so every existing token keeps working unchanged. Idempotent. """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '7d30_apitoken_resourcescopes' down_revision = '7d29_supportteam_webhookurl' branch_labels = None depends_on = None def _has_column(bind, table, column): inspector = sa.inspect(bind) if table not in inspector.get_table_names(): return False return column in {c['name'] for c in inspector.get_columns(table)} def upgrade(): bind = op.get_bind() if not _has_column(bind, 'apitokens', 'resourcescopes'): op.add_column('apitokens', sa.Column('resourcescopes', sa.Text(), nullable=True)) def downgrade(): bind = op.get_bind() if _has_column(bind, 'apitokens', 'resourcescopes'): op.drop_column('apitokens', 'resourcescopes')