dirigent-core 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. dirigent_core/__init__.py +26 -0
  2. dirigent_core/alembic/env.py +71 -0
  3. dirigent_core/alembic/script.py.mako +26 -0
  4. dirigent_core/alembic/versions/0001_baseline_schema.py +1016 -0
  5. dirigent_core/alerting.py +805 -0
  6. dirigent_core/artifacts.py +73 -0
  7. dirigent_core/auth.py +529 -0
  8. dirigent_core/blockdocs.py +223 -0
  9. dirigent_core/config.py +421 -0
  10. dirigent_core/configdocs.py +134 -0
  11. dirigent_core/database.py +177 -0
  12. dirigent_core/directory.py +339 -0
  13. dirigent_core/documents.py +878 -0
  14. dirigent_core/documentschema.py +92 -0
  15. dirigent_core/engine/__init__.py +120 -0
  16. dirigent_core/engine/claim.py +156 -0
  17. dirigent_core/engine/context.py +420 -0
  18. dirigent_core/engine/definition.py +543 -0
  19. dirigent_core/engine/executor.py +1029 -0
  20. dirigent_core/engine/failure.py +71 -0
  21. dirigent_core/engine/recovery.py +137 -0
  22. dirigent_core/engine/references.py +239 -0
  23. dirigent_core/engine/runs.py +865 -0
  24. dirigent_core/engine/services.py +74 -0
  25. dirigent_core/engine/state.py +434 -0
  26. dirigent_core/ids.py +39 -0
  27. dirigent_core/logging.py +310 -0
  28. dirigent_core/migrations.py +98 -0
  29. dirigent_core/models.py +626 -0
  30. dirigent_core/pipelines.py +494 -0
  31. dirigent_core/plugins.py +255 -0
  32. dirigent_core/protocol.py +276 -0
  33. dirigent_core/py.typed +0 -0
  34. dirigent_core/ratelimit.py +42 -0
  35. dirigent_core/registry.py +32 -0
  36. dirigent_core/retention.py +293 -0
  37. dirigent_core/scheduler.py +491 -0
  38. dirigent_core/schemas.py +147 -0
  39. dirigent_core/secrets.py +164 -0
  40. dirigent_core/storage.py +349 -0
  41. dirigent_core/telemetry.py +402 -0
  42. dirigent_core/trigger_documents.py +193 -0
  43. dirigent_core/triggers/__init__.py +117 -0
  44. dirigent_core/triggers/backfill.py +131 -0
  45. dirigent_core/triggers/materialize.py +218 -0
  46. dirigent_core/triggers/schedules.py +531 -0
  47. dirigent_core/triggers/webhooks.py +586 -0
  48. dirigent_core/types.py +59 -0
  49. dirigent_core/worker.py +350 -0
  50. dirigent_core-0.9.0.dist-info/METADATA +29 -0
  51. dirigent_core-0.9.0.dist-info/RECORD +53 -0
  52. dirigent_core-0.9.0.dist-info/WHEEL +4 -0
  53. dirigent_core-0.9.0.dist-info/licenses/LICENSE +18 -0
@@ -0,0 +1,1016 @@
1
+ """The whole schema, as one revision.
2
+
3
+ There were eight: a baseline, and then a migration for each thing that changed after it --
4
+ a sealed webhook secret, an idempotency key, a waiting status, a step order, one name for a
5
+ row's age. Nothing had shipped, so none of them had a database to move forward, and each was
6
+ a place where an ALTER behaves differently from the CREATE that would have said the same
7
+ thing. One of them proved it: SQLite accepts a CURRENT_TIMESTAMP default on an empty table
8
+ and refuses it on one with rows, so a column added by ALTER broke a development instance
9
+ that a test on a fresh database could never have caught.
10
+
11
+ This is what those eight amounted to. What proves it faithful is not this file but
12
+ ``test_the_migrations_leave_no_drift_against_the_models_on_sqlite`` and its PostgreSQL twin,
13
+ which compare a migrated database against the models.
14
+
15
+ Revision ID: 0001_baseline
16
+ Revises: nothing
17
+ Create Date: 2026-08-31
18
+
19
+ """
20
+
21
+ from collections.abc import Sequence
22
+
23
+ import sqlalchemy as sa
24
+ from alembic import op
25
+ from sqlalchemy import Text
26
+ from sqlalchemy.dialects import postgresql
27
+
28
+ import dirigent_core.types
29
+
30
+ revision: str = "0001_baseline"
31
+ down_revision: str | None = None
32
+ branch_labels: str | Sequence[str] | None = None
33
+ depends_on: str | Sequence[str] | None = None
34
+
35
+
36
+ def upgrade() -> None:
37
+ """Create every table, index and constraint the models declare."""
38
+ op.create_table(
39
+ "connections",
40
+ sa.Column("id", sa.Uuid(), nullable=False),
41
+ sa.Column("code", sa.String(length=200), nullable=False),
42
+ sa.Column("name", sa.String(length=200), nullable=True),
43
+ sa.Column("kind", sa.String(length=100), nullable=False),
44
+ sa.Column("description", sa.Text(), nullable=True),
45
+ sa.Column("config", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
46
+ sa.Column("secret_envelope", sa.LargeBinary(), nullable=True),
47
+ sa.Column("secret_key_id", sa.String(length=100), nullable=True),
48
+ sa.Column("last_check_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
49
+ sa.Column("last_check_healthy", sa.Boolean(), nullable=True),
50
+ sa.Column("last_check_detail", sa.Text(), nullable=True),
51
+ sa.Column(
52
+ "created_at",
53
+ dirigent_core.types.UtcDateTime(timezone=True),
54
+ server_default=sa.func.now(),
55
+ nullable=False,
56
+ ),
57
+ sa.Column(
58
+ "updated_at",
59
+ dirigent_core.types.UtcDateTime(timezone=True),
60
+ server_default=sa.func.now(),
61
+ nullable=False,
62
+ ),
63
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_connections")),
64
+ sa.UniqueConstraint("code", name=op.f("uq_connections_code")),
65
+ )
66
+ with op.batch_alter_table("connections", schema=None) as batch_op:
67
+ batch_op.create_index(batch_op.f("ix_connections_kind"), ["kind"], unique=False)
68
+
69
+ op.create_table(
70
+ "schemas",
71
+ sa.Column("id", sa.Uuid(), nullable=False),
72
+ sa.Column("code", sa.String(length=200), nullable=False),
73
+ sa.Column("name", sa.String(length=200), nullable=True),
74
+ sa.Column("description", sa.Text(), nullable=True),
75
+ sa.Column("body", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
76
+ sa.Column(
77
+ "created_at",
78
+ dirigent_core.types.UtcDateTime(timezone=True),
79
+ server_default=sa.func.now(),
80
+ nullable=False,
81
+ ),
82
+ sa.Column(
83
+ "updated_at",
84
+ dirigent_core.types.UtcDateTime(timezone=True),
85
+ server_default=sa.func.now(),
86
+ nullable=False,
87
+ ),
88
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_schemas")),
89
+ sa.UniqueConstraint("code", name=op.f("uq_schemas_code")),
90
+ )
91
+
92
+ op.create_table(
93
+ "pipelines",
94
+ sa.Column("id", sa.Uuid(), nullable=False),
95
+ sa.Column("code", sa.String(length=200), nullable=False),
96
+ sa.Column("name", sa.String(length=200), nullable=True),
97
+ sa.Column("description", sa.Text(), nullable=True),
98
+ sa.Column("tags", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
99
+ sa.Column("active", sa.Boolean(), server_default=sa.true(), nullable=False),
100
+ sa.Column("current_version", sa.Integer(), nullable=True),
101
+ sa.Column(
102
+ "created_at",
103
+ dirigent_core.types.UtcDateTime(timezone=True),
104
+ server_default=sa.func.now(),
105
+ nullable=False,
106
+ ),
107
+ sa.Column(
108
+ "updated_at",
109
+ dirigent_core.types.UtcDateTime(timezone=True),
110
+ server_default=sa.func.now(),
111
+ nullable=False,
112
+ ),
113
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_pipelines")),
114
+ sa.UniqueConstraint("code", name=op.f("uq_pipelines_code")),
115
+ )
116
+
117
+ op.create_table(
118
+ "users",
119
+ sa.Column("id", sa.Uuid(), nullable=False),
120
+ sa.Column("username", sa.String(length=200), nullable=False),
121
+ sa.Column("name", sa.String(length=200), nullable=True),
122
+ sa.Column("email", sa.String(length=320), nullable=True),
123
+ sa.Column("password_hash", sa.Text(), nullable=False),
124
+ sa.Column(
125
+ "role",
126
+ sa.Enum("admin", "operator", "viewer", name="user_role", native_enum=False, length=32),
127
+ nullable=False,
128
+ ),
129
+ sa.Column("active", sa.Boolean(), server_default=sa.true(), nullable=False),
130
+ sa.Column("last_login_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
131
+ sa.Column(
132
+ "created_at",
133
+ dirigent_core.types.UtcDateTime(timezone=True),
134
+ server_default=sa.func.now(),
135
+ nullable=False,
136
+ ),
137
+ sa.Column(
138
+ "updated_at",
139
+ dirigent_core.types.UtcDateTime(timezone=True),
140
+ server_default=sa.func.now(),
141
+ nullable=False,
142
+ ),
143
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_users")),
144
+ sa.UniqueConstraint("username", name=op.f("uq_users_username")),
145
+ sa.UniqueConstraint("email", name=op.f("uq_users_email")),
146
+ )
147
+ op.create_table(
148
+ "workers",
149
+ sa.Column("id", sa.Uuid(), nullable=False),
150
+ sa.Column("name", sa.String(length=200), nullable=False),
151
+ sa.Column("hostname", sa.String(length=255), nullable=False),
152
+ sa.Column("version", sa.String(length=50), nullable=False),
153
+ sa.Column(
154
+ "plugins", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False
155
+ ),
156
+ sa.Column("tags", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
157
+ sa.Column("concurrency", sa.Integer(), server_default=sa.text("1"), nullable=False),
158
+ sa.Column(
159
+ "status",
160
+ sa.Enum("starting", "running", "draining", "stopped", name="worker_status", native_enum=False, length=32),
161
+ nullable=False,
162
+ ),
163
+ sa.Column("catalog_digest", sa.String(length=71), nullable=True),
164
+ sa.Column(
165
+ "created_at",
166
+ dirigent_core.types.UtcDateTime(timezone=True),
167
+ server_default=sa.func.now(),
168
+ nullable=False,
169
+ ),
170
+ sa.Column(
171
+ "updated_at",
172
+ dirigent_core.types.UtcDateTime(timezone=True),
173
+ server_default=sa.func.now(),
174
+ nullable=False,
175
+ ),
176
+ sa.Column(
177
+ "last_seen_at",
178
+ dirigent_core.types.UtcDateTime(timezone=True),
179
+ server_default=sa.func.now(),
180
+ nullable=False,
181
+ ),
182
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_workers")),
183
+ sa.UniqueConstraint("name", name=op.f("uq_workers_name")),
184
+ )
185
+ with op.batch_alter_table("workers", schema=None) as batch_op:
186
+ batch_op.create_index(batch_op.f("ix_workers_last_seen_at"), ["last_seen_at"], unique=False)
187
+
188
+ op.create_table(
189
+ "alert_rules",
190
+ sa.Column("id", sa.Uuid(), nullable=False),
191
+ sa.Column("code", sa.String(length=200), nullable=False),
192
+ sa.Column("name", sa.String(length=200), nullable=True),
193
+ sa.Column("description", sa.Text(), nullable=True),
194
+ sa.Column(
195
+ "event",
196
+ sa.Enum(
197
+ "run_failed",
198
+ "run_completed_with_errors",
199
+ "run_succeeded",
200
+ "run_stuck",
201
+ name="alert_event",
202
+ native_enum=False,
203
+ length=32,
204
+ ),
205
+ nullable=False,
206
+ ),
207
+ sa.Column(
208
+ "scope", sa.Enum("global", "pipeline", name="alert_scope", native_enum=False, length=32), nullable=False
209
+ ),
210
+ sa.Column("pipeline_id", sa.Uuid(), nullable=True),
211
+ sa.Column("notifier", sa.String(length=100), nullable=False),
212
+ sa.Column("connection_id", sa.Uuid(), nullable=True),
213
+ sa.Column("template", sa.Text(), nullable=True),
214
+ sa.Column("throttle_seconds", sa.Integer(), server_default=sa.text("0"), nullable=False),
215
+ sa.Column("active", sa.Boolean(), server_default=sa.true(), nullable=False),
216
+ sa.Column("paused", sa.Boolean(), server_default=sa.false(), nullable=False),
217
+ sa.Column("last_sent_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
218
+ sa.Column(
219
+ "created_at",
220
+ dirigent_core.types.UtcDateTime(timezone=True),
221
+ server_default=sa.func.now(),
222
+ nullable=False,
223
+ ),
224
+ sa.Column(
225
+ "updated_at",
226
+ dirigent_core.types.UtcDateTime(timezone=True),
227
+ server_default=sa.func.now(),
228
+ nullable=False,
229
+ ),
230
+ sa.ForeignKeyConstraint(
231
+ ["connection_id"],
232
+ ["connections.id"],
233
+ name=op.f("fk_alert_rules_connection_id_connections"),
234
+ ondelete="RESTRICT",
235
+ ),
236
+ sa.ForeignKeyConstraint(
237
+ ["pipeline_id"], ["pipelines.id"], name=op.f("fk_alert_rules_pipeline_id_pipelines"), ondelete="CASCADE"
238
+ ),
239
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_alert_rules")),
240
+ sa.UniqueConstraint("code", name=op.f("uq_alert_rules_code")),
241
+ )
242
+ with op.batch_alter_table("alert_rules", schema=None) as batch_op:
243
+ batch_op.create_index(batch_op.f("ix_alert_rules_event"), ["event"], unique=False)
244
+ batch_op.create_index(batch_op.f("ix_alert_rules_pipeline_id"), ["pipeline_id"], unique=False)
245
+
246
+ op.create_table(
247
+ "api_tokens",
248
+ sa.Column("id", sa.Uuid(), nullable=False),
249
+ sa.Column("user_id", sa.Uuid(), nullable=False),
250
+ sa.Column("name", sa.String(length=200), nullable=False),
251
+ sa.Column("kind", sa.Enum("api", "session", name="token_kind", native_enum=False, length=32), nullable=False),
252
+ sa.Column("token_hash", sa.String(length=64), nullable=False),
253
+ sa.Column("prefix", sa.String(length=16), nullable=False),
254
+ sa.Column("expires_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
255
+ sa.Column("revoked_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
256
+ sa.Column("last_used_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
257
+ sa.Column(
258
+ "created_at",
259
+ dirigent_core.types.UtcDateTime(timezone=True),
260
+ server_default=sa.func.now(),
261
+ nullable=False,
262
+ ),
263
+ sa.Column(
264
+ "updated_at",
265
+ dirigent_core.types.UtcDateTime(timezone=True),
266
+ server_default=sa.func.now(),
267
+ nullable=False,
268
+ ),
269
+ sa.ForeignKeyConstraint(
270
+ ["user_id"], ["users.id"], name=op.f("fk_api_tokens_user_id_users"), ondelete="CASCADE"
271
+ ),
272
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_api_tokens")),
273
+ sa.UniqueConstraint("token_hash", name=op.f("uq_api_tokens_token_hash")),
274
+ )
275
+ with op.batch_alter_table("api_tokens", schema=None) as batch_op:
276
+ batch_op.create_index("ix_api_tokens_user_id_kind", ["user_id", "kind"], unique=False)
277
+
278
+ op.create_table(
279
+ "pipeline_versions",
280
+ sa.Column("id", sa.Uuid(), nullable=False),
281
+ sa.Column("pipeline_id", sa.Uuid(), nullable=False),
282
+ sa.Column("version", sa.Integer(), nullable=False),
283
+ sa.Column(
284
+ "document", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False
285
+ ),
286
+ sa.Column(
287
+ "step_order", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False
288
+ ),
289
+ sa.Column("digest", sa.String(length=71), nullable=False),
290
+ sa.Column(
291
+ "provenance_source",
292
+ sa.Enum("ui", "file", "url", "api", name="provenance_source", native_enum=False, length=32),
293
+ nullable=False,
294
+ ),
295
+ sa.Column("provenance_ref", sa.Text(), nullable=True),
296
+ sa.Column("applied_by", sa.String(length=200), nullable=True),
297
+ sa.Column(
298
+ "created_at",
299
+ dirigent_core.types.UtcDateTime(timezone=True),
300
+ server_default=sa.func.now(),
301
+ nullable=False,
302
+ ),
303
+ sa.Column(
304
+ "updated_at",
305
+ dirigent_core.types.UtcDateTime(timezone=True),
306
+ server_default=sa.func.now(),
307
+ nullable=False,
308
+ ),
309
+ sa.ForeignKeyConstraint(
310
+ ["pipeline_id"],
311
+ ["pipelines.id"],
312
+ name=op.f("fk_pipeline_versions_pipeline_id_pipelines"),
313
+ ondelete="CASCADE",
314
+ ),
315
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_pipeline_versions")),
316
+ sa.UniqueConstraint("pipeline_id", "version", name=op.f("uq_pipeline_versions_pipeline_id_version")),
317
+ )
318
+ with op.batch_alter_table("pipeline_versions", schema=None) as batch_op:
319
+ batch_op.create_index(batch_op.f("ix_pipeline_versions_digest"), ["digest"], unique=False)
320
+ batch_op.create_index(batch_op.f("ix_pipeline_versions_pipeline_id"), ["pipeline_id"], unique=False)
321
+
322
+ op.create_table(
323
+ "trigger_documents",
324
+ sa.Column("id", sa.Uuid(), nullable=False),
325
+ sa.Column("code", sa.String(length=200), nullable=False),
326
+ sa.Column("name", sa.String(length=200), nullable=True),
327
+ sa.Column("description", sa.Text(), nullable=True),
328
+ sa.Column("pipeline_id", sa.Uuid(), nullable=False),
329
+ sa.Column(
330
+ "document", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False
331
+ ),
332
+ sa.Column("digest", sa.String(length=71), nullable=False),
333
+ sa.Column(
334
+ "provenance_source",
335
+ sa.Enum("ui", "file", "url", "api", "directory", name="provenance_source", native_enum=False, length=32),
336
+ nullable=False,
337
+ ),
338
+ sa.Column("provenance_ref", sa.Text(), nullable=True),
339
+ sa.Column("applied_by", sa.String(length=200), nullable=True),
340
+ sa.Column(
341
+ "created_at",
342
+ dirigent_core.types.UtcDateTime(timezone=True),
343
+ server_default=sa.func.now(),
344
+ nullable=False,
345
+ ),
346
+ sa.Column(
347
+ "updated_at",
348
+ dirigent_core.types.UtcDateTime(timezone=True),
349
+ server_default=sa.func.now(),
350
+ nullable=False,
351
+ ),
352
+ sa.ForeignKeyConstraint(
353
+ ["pipeline_id"],
354
+ ["pipelines.id"],
355
+ name=op.f("fk_trigger_documents_pipeline_id_pipelines"),
356
+ ondelete="CASCADE",
357
+ ),
358
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_trigger_documents")),
359
+ sa.UniqueConstraint("code", name=op.f("uq_trigger_documents_code")),
360
+ )
361
+ with op.batch_alter_table("trigger_documents", schema=None) as batch_op:
362
+ batch_op.create_index(batch_op.f("ix_trigger_documents_digest"), ["digest"], unique=False)
363
+ batch_op.create_index(batch_op.f("ix_trigger_documents_pipeline_id"), ["pipeline_id"], unique=False)
364
+
365
+ op.create_table(
366
+ "schedules",
367
+ sa.Column("id", sa.Uuid(), nullable=False),
368
+ sa.Column("pipeline_id", sa.Uuid(), nullable=False),
369
+ sa.Column("code", sa.String(length=200), nullable=False),
370
+ sa.Column("name", sa.String(length=200), nullable=True),
371
+ sa.Column("description", sa.Text(), nullable=True),
372
+ sa.Column(
373
+ "kind",
374
+ sa.Enum("cron", "interval", "one_time", name="schedule_kind", native_enum=False, length=32),
375
+ nullable=False,
376
+ ),
377
+ sa.Column("cron", sa.String(length=200), nullable=True),
378
+ sa.Column("interval_seconds", sa.Integer(), nullable=True),
379
+ sa.Column("run_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
380
+ sa.Column("timezone", sa.String(length=64), server_default="UTC", nullable=False),
381
+ sa.Column("params", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
382
+ sa.Column(
383
+ "connection_pins",
384
+ sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"),
385
+ nullable=False,
386
+ ),
387
+ sa.Column(
388
+ "log_levels", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True
389
+ ),
390
+ sa.Column(
391
+ "priority",
392
+ sa.Enum("low", "normal", "high", name="run_priority", native_enum=False, length=32),
393
+ nullable=True,
394
+ ),
395
+ sa.Column("managed", sa.Boolean(), server_default=sa.false(), nullable=False),
396
+ sa.Column("trigger_document_id", sa.Uuid(), nullable=True),
397
+ sa.Column("paused", sa.Boolean(), server_default=sa.false(), nullable=False),
398
+ sa.Column("next_fire_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
399
+ sa.Column("last_fired_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
400
+ sa.Column(
401
+ "created_at",
402
+ dirigent_core.types.UtcDateTime(timezone=True),
403
+ server_default=sa.func.now(),
404
+ nullable=False,
405
+ ),
406
+ sa.Column(
407
+ "updated_at",
408
+ dirigent_core.types.UtcDateTime(timezone=True),
409
+ server_default=sa.func.now(),
410
+ nullable=False,
411
+ ),
412
+ sa.ForeignKeyConstraint(
413
+ ["pipeline_id"], ["pipelines.id"], name=op.f("fk_schedules_pipeline_id_pipelines"), ondelete="CASCADE"
414
+ ),
415
+ sa.ForeignKeyConstraint(
416
+ ["trigger_document_id"],
417
+ ["trigger_documents.id"],
418
+ name=op.f("fk_schedules_trigger_document_id_trigger_documents"),
419
+ ondelete="CASCADE",
420
+ ),
421
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_schedules")),
422
+ sa.UniqueConstraint("pipeline_id", "code", name=op.f("uq_schedules_pipeline_id_code")),
423
+ )
424
+ with op.batch_alter_table("schedules", schema=None) as batch_op:
425
+ batch_op.create_index("ix_schedules_paused_next_fire_at", ["paused", "next_fire_at"], unique=False)
426
+ batch_op.create_index(batch_op.f("ix_schedules_pipeline_id"), ["pipeline_id"], unique=False)
427
+ batch_op.create_index(batch_op.f("ix_schedules_trigger_document_id"), ["trigger_document_id"], unique=False)
428
+
429
+ op.create_table(
430
+ "webhook_triggers",
431
+ sa.Column("id", sa.Uuid(), nullable=False),
432
+ sa.Column("pipeline_id", sa.Uuid(), nullable=False),
433
+ sa.Column("code", sa.String(length=200), nullable=False),
434
+ sa.Column("name", sa.String(length=200), nullable=True),
435
+ sa.Column("description", sa.Text(), nullable=True),
436
+ sa.Column("token_hash", sa.String(length=128), nullable=False),
437
+ sa.Column("token_prefix", sa.String(length=16), server_default="", nullable=False),
438
+ sa.Column("hmac_secret", sa.LargeBinary(), nullable=True),
439
+ sa.Column("hmac_secret_key_id", sa.String(length=100), nullable=True),
440
+ sa.Column(
441
+ "params_from_payload",
442
+ sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"),
443
+ nullable=False,
444
+ ),
445
+ sa.Column(
446
+ "priority",
447
+ sa.Enum("low", "normal", "high", name="run_priority", native_enum=False, length=32),
448
+ nullable=True,
449
+ ),
450
+ sa.Column("managed", sa.Boolean(), server_default=sa.false(), nullable=False),
451
+ sa.Column("trigger_document_id", sa.Uuid(), nullable=True),
452
+ sa.Column("active", sa.Boolean(), server_default=sa.true(), nullable=False),
453
+ sa.Column("rate_limit_per_minute", sa.Integer(), server_default=sa.text("60"), nullable=False),
454
+ sa.Column("last_delivery_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
455
+ sa.Column(
456
+ "created_at",
457
+ dirigent_core.types.UtcDateTime(timezone=True),
458
+ server_default=sa.func.now(),
459
+ nullable=False,
460
+ ),
461
+ sa.Column(
462
+ "updated_at",
463
+ dirigent_core.types.UtcDateTime(timezone=True),
464
+ server_default=sa.func.now(),
465
+ nullable=False,
466
+ ),
467
+ sa.ForeignKeyConstraint(
468
+ ["pipeline_id"],
469
+ ["pipelines.id"],
470
+ name=op.f("fk_webhook_triggers_pipeline_id_pipelines"),
471
+ ondelete="CASCADE",
472
+ ),
473
+ sa.ForeignKeyConstraint(
474
+ ["trigger_document_id"],
475
+ ["trigger_documents.id"],
476
+ name=op.f("fk_webhook_triggers_trigger_document_id_trigger_documents"),
477
+ ondelete="CASCADE",
478
+ ),
479
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_webhook_triggers")),
480
+ sa.UniqueConstraint("pipeline_id", "code", name=op.f("uq_webhook_triggers_pipeline_id_code")),
481
+ sa.UniqueConstraint("token_hash", name=op.f("uq_webhook_triggers_token_hash")),
482
+ )
483
+ with op.batch_alter_table("webhook_triggers", schema=None) as batch_op:
484
+ batch_op.create_index(batch_op.f("ix_webhook_triggers_pipeline_id"), ["pipeline_id"], unique=False)
485
+ batch_op.create_index(
486
+ batch_op.f("ix_webhook_triggers_trigger_document_id"), ["trigger_document_id"], unique=False
487
+ )
488
+
489
+ op.create_table(
490
+ "runs",
491
+ sa.Column("id", sa.Uuid(), nullable=False),
492
+ sa.Column("pipeline_id", sa.Uuid(), nullable=False),
493
+ sa.Column("pipeline_version_id", sa.Uuid(), nullable=False),
494
+ sa.Column(
495
+ "status",
496
+ sa.Enum(
497
+ "queued",
498
+ "running",
499
+ "succeeded",
500
+ "completed_with_errors",
501
+ "failed",
502
+ "cancelled",
503
+ name="run_status",
504
+ native_enum=False,
505
+ length=32,
506
+ ),
507
+ nullable=False,
508
+ ),
509
+ sa.Column(
510
+ "priority",
511
+ sa.Enum("low", "normal", "high", name="run_priority", native_enum=False, length=32),
512
+ server_default="normal",
513
+ nullable=False,
514
+ ),
515
+ sa.Column("params", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
516
+ sa.Column(
517
+ "triggered_by_kind",
518
+ sa.Enum(
519
+ "adhoc",
520
+ "schedule",
521
+ "webhook",
522
+ "api_token",
523
+ "user",
524
+ "pipeline",
525
+ "backfill",
526
+ name="trigger_kind",
527
+ native_enum=False,
528
+ length=32,
529
+ ),
530
+ nullable=False,
531
+ ),
532
+ sa.Column("triggered_by_id", sa.Uuid(), nullable=True),
533
+ sa.Column("triggered_by_label", sa.String(length=200), nullable=True),
534
+ sa.Column("traceparent", sa.String(length=64), nullable=True),
535
+ sa.Column("error", sa.Text(), nullable=True),
536
+ sa.Column("started_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
537
+ sa.Column("finished_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
538
+ sa.Column("window_start", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
539
+ sa.Column("window_end", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
540
+ sa.Column(
541
+ "log_levels", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True
542
+ ),
543
+ sa.Column(
544
+ "worker_tags", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False
545
+ ),
546
+ sa.Column(
547
+ "created_at",
548
+ dirigent_core.types.UtcDateTime(timezone=True),
549
+ server_default=sa.func.now(),
550
+ nullable=False,
551
+ ),
552
+ sa.Column(
553
+ "updated_at",
554
+ dirigent_core.types.UtcDateTime(timezone=True),
555
+ server_default=sa.func.now(),
556
+ nullable=False,
557
+ ),
558
+ sa.ForeignKeyConstraint(
559
+ ["pipeline_id"], ["pipelines.id"], name=op.f("fk_runs_pipeline_id_pipelines"), ondelete="RESTRICT"
560
+ ),
561
+ sa.ForeignKeyConstraint(
562
+ ["pipeline_version_id"],
563
+ ["pipeline_versions.id"],
564
+ name=op.f("fk_runs_pipeline_version_id_pipeline_versions"),
565
+ ondelete="RESTRICT",
566
+ ),
567
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_runs")),
568
+ )
569
+ with op.batch_alter_table("runs", schema=None) as batch_op:
570
+ batch_op.create_index("ix_runs_finished_at", ["finished_at"], unique=False)
571
+ batch_op.create_index(
572
+ "ix_runs_pipeline_id_status_created_at", ["pipeline_id", "status", "created_at"], unique=False
573
+ )
574
+ batch_op.create_index(batch_op.f("ix_runs_status"), ["status"], unique=False)
575
+
576
+ op.create_table(
577
+ "notifications",
578
+ sa.Column("id", sa.Uuid(), nullable=False),
579
+ sa.Column("alert_rule_id", sa.Uuid(), nullable=True),
580
+ sa.Column("run_id", sa.Uuid(), nullable=True),
581
+ sa.Column(
582
+ "event",
583
+ sa.Enum(
584
+ "run_failed",
585
+ "run_completed_with_errors",
586
+ "run_succeeded",
587
+ "run_stuck",
588
+ name="alert_event",
589
+ native_enum=False,
590
+ length=32,
591
+ ),
592
+ nullable=False,
593
+ ),
594
+ sa.Column("notifier", sa.String(length=100), nullable=False),
595
+ sa.Column("connection_id", sa.Uuid(), nullable=True),
596
+ sa.Column("subject", sa.Text(), nullable=False),
597
+ sa.Column("body", sa.Text(), server_default="", nullable=False),
598
+ sa.Column(
599
+ "context", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False
600
+ ),
601
+ sa.Column(
602
+ "status",
603
+ sa.Enum("pending", "sending", "sent", "failed", name="notification_status", native_enum=False, length=32),
604
+ nullable=False,
605
+ ),
606
+ sa.Column("attempt", sa.Integer(), server_default=sa.text("0"), nullable=False),
607
+ sa.Column("available_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=False),
608
+ sa.Column("lease_owner", sa.String(length=200), nullable=True),
609
+ sa.Column("lease_expires_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
610
+ sa.Column("sent_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
611
+ sa.Column("error", sa.Text(), nullable=True),
612
+ sa.Column(
613
+ "created_at",
614
+ dirigent_core.types.UtcDateTime(timezone=True),
615
+ server_default=sa.func.now(),
616
+ nullable=False,
617
+ ),
618
+ sa.Column(
619
+ "updated_at",
620
+ dirigent_core.types.UtcDateTime(timezone=True),
621
+ server_default=sa.func.now(),
622
+ nullable=False,
623
+ ),
624
+ sa.ForeignKeyConstraint(
625
+ ["alert_rule_id"],
626
+ ["alert_rules.id"],
627
+ name=op.f("fk_notifications_alert_rule_id_alert_rules"),
628
+ ondelete="CASCADE",
629
+ ),
630
+ sa.ForeignKeyConstraint(
631
+ ["connection_id"],
632
+ ["connections.id"],
633
+ name=op.f("fk_notifications_connection_id_connections"),
634
+ ondelete="RESTRICT",
635
+ ),
636
+ sa.ForeignKeyConstraint(["run_id"], ["runs.id"], name=op.f("fk_notifications_run_id_runs"), ondelete="CASCADE"),
637
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_notifications")),
638
+ sa.UniqueConstraint(
639
+ "alert_rule_id", "run_id", "event", name=op.f("uq_notifications_alert_rule_id_run_id_event")
640
+ ),
641
+ )
642
+ with op.batch_alter_table("notifications", schema=None) as batch_op:
643
+ batch_op.create_index(batch_op.f("ix_notifications_alert_rule_id"), ["alert_rule_id"], unique=False)
644
+ batch_op.create_index("ix_notifications_created_at", ["created_at"], unique=False)
645
+ batch_op.create_index(batch_op.f("ix_notifications_run_id"), ["run_id"], unique=False)
646
+ batch_op.create_index("ix_notifications_status_available_at", ["status", "available_at"], unique=False)
647
+
648
+ op.create_table(
649
+ "run_items",
650
+ sa.Column("id", sa.Uuid(), nullable=False),
651
+ sa.Column("run_id", sa.Uuid(), nullable=False),
652
+ sa.Column("step_name", sa.String(length=200), nullable=False),
653
+ sa.Column("item_index", sa.Integer(), nullable=False),
654
+ sa.Column("item_key", sa.String(length=500), nullable=False),
655
+ sa.Column(
656
+ "item_value", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True
657
+ ),
658
+ sa.Column(
659
+ "status",
660
+ sa.Enum(
661
+ "pending",
662
+ "running",
663
+ "succeeded",
664
+ "failed",
665
+ "skipped",
666
+ name="run_item_status",
667
+ native_enum=False,
668
+ length=32,
669
+ ),
670
+ nullable=False,
671
+ ),
672
+ sa.Column("failing_step", sa.String(length=200), nullable=True),
673
+ sa.Column("error", sa.Text(), nullable=True),
674
+ sa.Column("started_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
675
+ sa.Column("finished_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
676
+ sa.Column(
677
+ "created_at",
678
+ dirigent_core.types.UtcDateTime(timezone=True),
679
+ server_default=sa.func.now(),
680
+ nullable=False,
681
+ ),
682
+ sa.Column(
683
+ "updated_at",
684
+ dirigent_core.types.UtcDateTime(timezone=True),
685
+ server_default=sa.func.now(),
686
+ nullable=False,
687
+ ),
688
+ sa.ForeignKeyConstraint(["run_id"], ["runs.id"], name=op.f("fk_run_items_run_id_runs"), ondelete="CASCADE"),
689
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_run_items")),
690
+ sa.UniqueConstraint("run_id", "step_name", "item_index", name=op.f("uq_run_items_run_id_step_name_item_index")),
691
+ )
692
+ with op.batch_alter_table("run_items", schema=None) as batch_op:
693
+ batch_op.create_index(batch_op.f("ix_run_items_run_id"), ["run_id"], unique=False)
694
+ batch_op.create_index(batch_op.f("ix_run_items_status"), ["status"], unique=False)
695
+
696
+ op.create_table(
697
+ "schedule_firings",
698
+ sa.Column("id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), autoincrement=True, nullable=False),
699
+ sa.Column("schedule_id", sa.Uuid(), nullable=False),
700
+ sa.Column("run_id", sa.Uuid(), nullable=True),
701
+ sa.Column("scheduled_for", dirigent_core.types.UtcDateTime(timezone=True), nullable=False),
702
+ sa.Column(
703
+ "created_at",
704
+ dirigent_core.types.UtcDateTime(timezone=True),
705
+ server_default=sa.func.now(),
706
+ nullable=False,
707
+ ),
708
+ sa.Column(
709
+ "outcome",
710
+ sa.Enum(
711
+ "fired", "queued", "replaced", "skipped", "failed", name="firing_outcome", native_enum=False, length=32
712
+ ),
713
+ nullable=False,
714
+ ),
715
+ sa.Column("misfired", sa.Boolean(), server_default=sa.false(), nullable=False),
716
+ sa.Column("detail", sa.Text(), nullable=True),
717
+ sa.ForeignKeyConstraint(
718
+ ["run_id"], ["runs.id"], name=op.f("fk_schedule_firings_run_id_runs"), ondelete="SET NULL"
719
+ ),
720
+ sa.ForeignKeyConstraint(
721
+ ["schedule_id"],
722
+ ["schedules.id"],
723
+ name=op.f("fk_schedule_firings_schedule_id_schedules"),
724
+ ondelete="CASCADE",
725
+ ),
726
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_schedule_firings")),
727
+ )
728
+ with op.batch_alter_table("schedule_firings", schema=None) as batch_op:
729
+ batch_op.create_index("ix_schedule_firings_created_at", ["created_at"], unique=False)
730
+ batch_op.create_index("ix_schedule_firings_schedule_id_id", ["schedule_id", "id"], unique=False)
731
+
732
+ op.create_table(
733
+ "webhook_deliveries",
734
+ sa.Column("id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), autoincrement=True, nullable=False),
735
+ sa.Column("webhook_id", sa.Uuid(), nullable=False),
736
+ sa.Column("run_id", sa.Uuid(), nullable=True),
737
+ sa.Column(
738
+ "created_at",
739
+ dirigent_core.types.UtcDateTime(timezone=True),
740
+ server_default=sa.func.now(),
741
+ nullable=False,
742
+ ),
743
+ sa.Column(
744
+ "outcome",
745
+ sa.Enum("accepted", "skipped", "rejected", name="webhook_outcome", native_enum=False, length=32),
746
+ nullable=False,
747
+ ),
748
+ sa.Column("reason", sa.Text(), nullable=True),
749
+ sa.Column("payload", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
750
+ sa.Column(
751
+ "mapped_params", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True
752
+ ),
753
+ sa.Column("source", sa.String(length=64), nullable=True),
754
+ sa.ForeignKeyConstraint(
755
+ ["run_id"], ["runs.id"], name=op.f("fk_webhook_deliveries_run_id_runs"), ondelete="SET NULL"
756
+ ),
757
+ sa.ForeignKeyConstraint(
758
+ ["webhook_id"],
759
+ ["webhook_triggers.id"],
760
+ name=op.f("fk_webhook_deliveries_webhook_id_webhook_triggers"),
761
+ ondelete="CASCADE",
762
+ ),
763
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_webhook_deliveries")),
764
+ )
765
+ with op.batch_alter_table("webhook_deliveries", schema=None) as batch_op:
766
+ batch_op.create_index("ix_webhook_deliveries_created_at", ["created_at"], unique=False)
767
+ batch_op.create_index("ix_webhook_deliveries_webhook_id_id", ["webhook_id", "id"], unique=False)
768
+
769
+ op.create_table(
770
+ "step_attempts",
771
+ sa.Column("id", sa.Uuid(), nullable=False),
772
+ sa.Column("run_id", sa.Uuid(), nullable=False),
773
+ sa.Column("run_item_id", sa.Uuid(), nullable=True),
774
+ sa.Column("step_name", sa.String(length=200), nullable=False),
775
+ sa.Column("block_id", sa.String(length=200), nullable=False),
776
+ sa.Column("attempt", sa.Integer(), nullable=False),
777
+ sa.Column(
778
+ "kind", sa.Enum("automatic", "manual", name="attempt_kind", native_enum=False, length=32), nullable=False
779
+ ),
780
+ sa.Column(
781
+ "status",
782
+ sa.Enum(
783
+ "pending",
784
+ "queued",
785
+ "running",
786
+ "waiting",
787
+ "succeeded",
788
+ "failed",
789
+ "skipped",
790
+ "cancelled",
791
+ name="attempt_status",
792
+ native_enum=False,
793
+ length=32,
794
+ ),
795
+ nullable=False,
796
+ ),
797
+ sa.Column("available_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
798
+ sa.Column("next_poll_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
799
+ sa.Column("deadline_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
800
+ sa.Column("lease_owner", sa.String(length=200), nullable=True),
801
+ sa.Column("lease_expires_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
802
+ sa.Column("heartbeat_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
803
+ sa.Column(
804
+ "remote_handle", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True
805
+ ),
806
+ sa.Column(
807
+ "poke_cursor", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True
808
+ ),
809
+ sa.Column("gone_probes", sa.Integer(), server_default=sa.text("0"), nullable=False),
810
+ sa.Column("poke_count", sa.Integer(), server_default=sa.text("0"), nullable=False),
811
+ sa.Column("waiting_message", sa.String(length=500), nullable=True),
812
+ sa.Column("waiting_progress", sa.Float(), nullable=True),
813
+ sa.Column("idempotency_key", sa.String(length=400), nullable=True),
814
+ sa.Column("input", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
815
+ sa.Column("output", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
816
+ sa.Column("output_artifact_id", sa.Uuid(), nullable=True),
817
+ sa.Column("error", sa.Text(), nullable=True),
818
+ sa.Column("error_class", sa.String(length=32), nullable=True),
819
+ sa.Column("started_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
820
+ sa.Column("finished_at", dirigent_core.types.UtcDateTime(timezone=True), nullable=True),
821
+ sa.Column(
822
+ "created_at",
823
+ dirigent_core.types.UtcDateTime(timezone=True),
824
+ server_default=sa.func.now(),
825
+ nullable=False,
826
+ ),
827
+ sa.Column(
828
+ "updated_at",
829
+ dirigent_core.types.UtcDateTime(timezone=True),
830
+ server_default=sa.func.now(),
831
+ nullable=False,
832
+ ),
833
+ sa.ForeignKeyConstraint(["run_id"], ["runs.id"], name=op.f("fk_step_attempts_run_id_runs"), ondelete="CASCADE"),
834
+ sa.ForeignKeyConstraint(
835
+ ["run_item_id"], ["run_items.id"], name=op.f("fk_step_attempts_run_item_id_run_items"), ondelete="CASCADE"
836
+ ),
837
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_step_attempts")),
838
+ sa.UniqueConstraint("run_id", "idempotency_key", name=op.f("uq_step_attempts_run_id_idempotency_key")),
839
+ sa.UniqueConstraint(
840
+ "run_id",
841
+ "step_name",
842
+ "run_item_id",
843
+ "attempt",
844
+ name=op.f("uq_step_attempts_run_id_step_name_run_item_id_attempt"),
845
+ ),
846
+ )
847
+ with op.batch_alter_table("step_attempts", schema=None) as batch_op:
848
+ batch_op.create_index("ix_step_attempts_lease_expires_at", ["lease_expires_at"], unique=False)
849
+ batch_op.create_index(batch_op.f("ix_step_attempts_run_id"), ["run_id"], unique=False)
850
+ batch_op.create_index(batch_op.f("ix_step_attempts_run_item_id"), ["run_item_id"], unique=False)
851
+ batch_op.create_index("ix_step_attempts_status_available_at", ["status", "available_at"], unique=False)
852
+ batch_op.create_index("ix_step_attempts_status_next_poll_at", ["status", "next_poll_at"], unique=False)
853
+
854
+ op.create_table(
855
+ "artifact_refs",
856
+ sa.Column("id", sa.Uuid(), nullable=False),
857
+ sa.Column("run_id", sa.Uuid(), nullable=False),
858
+ sa.Column("step_attempt_id", sa.Uuid(), nullable=True),
859
+ sa.Column("step_name", sa.String(length=200), nullable=True),
860
+ sa.Column("uri", sa.Text(), nullable=True),
861
+ sa.Column("scheme", sa.String(length=32), nullable=True),
862
+ sa.Column("content_type", sa.String(length=255), nullable=True),
863
+ sa.Column("size_bytes", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), nullable=True),
864
+ sa.Column("digest", sa.String(length=71), nullable=True),
865
+ sa.Column(
866
+ "inline_value", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True
867
+ ),
868
+ sa.Column(
869
+ "created_at",
870
+ dirigent_core.types.UtcDateTime(timezone=True),
871
+ server_default=sa.func.now(),
872
+ nullable=False,
873
+ ),
874
+ sa.Column(
875
+ "updated_at",
876
+ dirigent_core.types.UtcDateTime(timezone=True),
877
+ server_default=sa.func.now(),
878
+ nullable=False,
879
+ ),
880
+ sa.ForeignKeyConstraint(["run_id"], ["runs.id"], name=op.f("fk_artifact_refs_run_id_runs"), ondelete="CASCADE"),
881
+ sa.ForeignKeyConstraint(
882
+ ["step_attempt_id"],
883
+ ["step_attempts.id"],
884
+ name=op.f("fk_artifact_refs_step_attempt_id_step_attempts"),
885
+ ondelete="CASCADE",
886
+ ),
887
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_artifact_refs")),
888
+ )
889
+ with op.batch_alter_table("artifact_refs", schema=None) as batch_op:
890
+ batch_op.create_index(batch_op.f("ix_artifact_refs_run_id"), ["run_id"], unique=False)
891
+
892
+ op.create_table(
893
+ "log_entries",
894
+ sa.Column("id", sa.BigInteger().with_variant(sa.Integer(), "sqlite"), autoincrement=True, nullable=False),
895
+ sa.Column("run_id", sa.Uuid(), nullable=False),
896
+ sa.Column("run_item_id", sa.Uuid(), nullable=True),
897
+ sa.Column("step_attempt_id", sa.Uuid(), nullable=True),
898
+ sa.Column("step_name", sa.String(length=200), nullable=True),
899
+ sa.Column(
900
+ "level",
901
+ sa.Enum("debug", "info", "warning", "error", name="log_level", native_enum=False, length=32),
902
+ nullable=False,
903
+ ),
904
+ sa.Column("message", sa.Text(), nullable=False),
905
+ sa.Column("fields", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=True),
906
+ sa.Column(
907
+ "created_at",
908
+ dirigent_core.types.UtcDateTime(timezone=True),
909
+ server_default=sa.func.now(),
910
+ nullable=False,
911
+ ),
912
+ sa.ForeignKeyConstraint(["run_id"], ["runs.id"], name=op.f("fk_log_entries_run_id_runs"), ondelete="CASCADE"),
913
+ sa.ForeignKeyConstraint(
914
+ ["run_item_id"], ["run_items.id"], name=op.f("fk_log_entries_run_item_id_run_items"), ondelete="CASCADE"
915
+ ),
916
+ sa.ForeignKeyConstraint(
917
+ ["step_attempt_id"],
918
+ ["step_attempts.id"],
919
+ name=op.f("fk_log_entries_step_attempt_id_step_attempts"),
920
+ ondelete="CASCADE",
921
+ ),
922
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_log_entries")),
923
+ )
924
+ with op.batch_alter_table("log_entries", schema=None) as batch_op:
925
+ batch_op.create_index("ix_log_entries_created_at", ["created_at"], unique=False)
926
+ batch_op.create_index("ix_log_entries_run_id_id", ["run_id", "id"], unique=False)
927
+
928
+
929
+ def downgrade() -> None:
930
+ """Drop all of it."""
931
+ with op.batch_alter_table("log_entries", schema=None) as batch_op:
932
+ batch_op.drop_index("ix_log_entries_run_id_id")
933
+ batch_op.drop_index("ix_log_entries_created_at")
934
+
935
+ op.drop_table("log_entries")
936
+ with op.batch_alter_table("artifact_refs", schema=None) as batch_op:
937
+ batch_op.drop_index(batch_op.f("ix_artifact_refs_run_id"))
938
+
939
+ op.drop_table("artifact_refs")
940
+ with op.batch_alter_table("step_attempts", schema=None) as batch_op:
941
+ batch_op.drop_index("ix_step_attempts_status_next_poll_at")
942
+ batch_op.drop_index("ix_step_attempts_status_available_at")
943
+ batch_op.drop_index(batch_op.f("ix_step_attempts_run_item_id"))
944
+ batch_op.drop_index(batch_op.f("ix_step_attempts_run_id"))
945
+ batch_op.drop_index("ix_step_attempts_lease_expires_at")
946
+
947
+ op.drop_table("step_attempts")
948
+ with op.batch_alter_table("webhook_deliveries", schema=None) as batch_op:
949
+ batch_op.drop_index("ix_webhook_deliveries_webhook_id_id")
950
+ batch_op.drop_index("ix_webhook_deliveries_created_at")
951
+
952
+ op.drop_table("webhook_deliveries")
953
+ with op.batch_alter_table("schedule_firings", schema=None) as batch_op:
954
+ batch_op.drop_index("ix_schedule_firings_schedule_id_id")
955
+ batch_op.drop_index("ix_schedule_firings_created_at")
956
+
957
+ op.drop_table("schedule_firings")
958
+ with op.batch_alter_table("run_items", schema=None) as batch_op:
959
+ batch_op.drop_index(batch_op.f("ix_run_items_status"))
960
+ batch_op.drop_index(batch_op.f("ix_run_items_run_id"))
961
+
962
+ op.drop_table("run_items")
963
+ with op.batch_alter_table("notifications", schema=None) as batch_op:
964
+ batch_op.drop_index("ix_notifications_status_available_at")
965
+ batch_op.drop_index(batch_op.f("ix_notifications_run_id"))
966
+ batch_op.drop_index("ix_notifications_created_at")
967
+ batch_op.drop_index(batch_op.f("ix_notifications_alert_rule_id"))
968
+
969
+ op.drop_table("notifications")
970
+ with op.batch_alter_table("runs", schema=None) as batch_op:
971
+ batch_op.drop_index(batch_op.f("ix_runs_status"))
972
+ batch_op.drop_index("ix_runs_pipeline_id_status_created_at")
973
+ batch_op.drop_index("ix_runs_finished_at")
974
+
975
+ op.drop_table("runs")
976
+ with op.batch_alter_table("webhook_triggers", schema=None) as batch_op:
977
+ batch_op.drop_index(batch_op.f("ix_webhook_triggers_trigger_document_id"))
978
+ batch_op.drop_index(batch_op.f("ix_webhook_triggers_pipeline_id"))
979
+
980
+ op.drop_table("webhook_triggers")
981
+ with op.batch_alter_table("schedules", schema=None) as batch_op:
982
+ batch_op.drop_index(batch_op.f("ix_schedules_trigger_document_id"))
983
+ batch_op.drop_index(batch_op.f("ix_schedules_pipeline_id"))
984
+ batch_op.drop_index("ix_schedules_paused_next_fire_at")
985
+
986
+ op.drop_table("schedules")
987
+ with op.batch_alter_table("trigger_documents", schema=None) as batch_op:
988
+ batch_op.drop_index(batch_op.f("ix_trigger_documents_pipeline_id"))
989
+ batch_op.drop_index(batch_op.f("ix_trigger_documents_digest"))
990
+
991
+ op.drop_table("trigger_documents")
992
+ with op.batch_alter_table("pipeline_versions", schema=None) as batch_op:
993
+ batch_op.drop_index(batch_op.f("ix_pipeline_versions_pipeline_id"))
994
+ batch_op.drop_index(batch_op.f("ix_pipeline_versions_digest"))
995
+
996
+ op.drop_table("pipeline_versions")
997
+ with op.batch_alter_table("api_tokens", schema=None) as batch_op:
998
+ batch_op.drop_index("ix_api_tokens_user_id_kind")
999
+
1000
+ op.drop_table("api_tokens")
1001
+ with op.batch_alter_table("alert_rules", schema=None) as batch_op:
1002
+ batch_op.drop_index(batch_op.f("ix_alert_rules_pipeline_id"))
1003
+ batch_op.drop_index(batch_op.f("ix_alert_rules_event"))
1004
+
1005
+ op.drop_table("alert_rules")
1006
+ with op.batch_alter_table("workers", schema=None) as batch_op:
1007
+ batch_op.drop_index(batch_op.f("ix_workers_last_seen_at"))
1008
+
1009
+ op.drop_table("workers")
1010
+ op.drop_table("users")
1011
+ op.drop_table("pipelines")
1012
+ op.drop_table("schemas")
1013
+ with op.batch_alter_table("connections", schema=None) as batch_op:
1014
+ batch_op.drop_index(batch_op.f("ix_connections_kind"))
1015
+
1016
+ op.drop_table("connections")