mainsequence 6.0.22__tar.gz → 6.0.24__tar.gz

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 (126) hide show
  1. {mainsequence-6.0.22/mainsequence.egg-info → mainsequence-6.0.24}/PKG-INFO +1 -1
  2. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +5 -21
  3. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/base.py +0 -1
  4. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/models_helpers.py +28 -111
  5. {mainsequence-6.0.22 → mainsequence-6.0.24/mainsequence.egg-info}/PKG-INFO +1 -1
  6. {mainsequence-6.0.22 → mainsequence-6.0.24}/pyproject.toml +1 -1
  7. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_filter_normalization.py +120 -53
  8. {mainsequence-6.0.22 → mainsequence-6.0.24}/LICENSE +0 -0
  9. {mainsequence-6.0.22 → mainsequence-6.0.24}/README.md +0 -0
  10. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/AGENTS.md +0 -0
  11. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
  12. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  13. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  14. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  15. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
  16. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  17. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  18. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/maintenance/project-maintenance/SKILL.md +0 -0
  19. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  20. {mainsequence-6.0.22 → mainsequence-6.0.24}/agent_scaffold/skills/sdk_project_execution/SKILL.md +0 -0
  21. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/__init__.py +0 -0
  22. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/__main__.py +0 -0
  23. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/bootstrap.py +0 -0
  24. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/__init__.py +0 -0
  25. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/api.py +0 -0
  26. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/browser_auth.py +0 -0
  27. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/cli.py +0 -0
  28. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/config.py +0 -0
  29. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/docker_utils.py +0 -0
  30. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/doctor.py +0 -0
  31. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/local_ops.py +0 -0
  32. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/migrations.py +0 -0
  33. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/model_filters.py +0 -0
  34. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/project_status.py +0 -0
  35. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/pydantic_cli.py +0 -0
  36. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/sdk_utils.py +0 -0
  37. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/ssh_utils.py +0 -0
  38. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/cli/ui.py +0 -0
  39. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/__init__.py +0 -0
  40. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/agent_runtime_models.py +0 -0
  41. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/client.py +0 -0
  42. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/compute_validation.py +0 -0
  43. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  44. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  45. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
  46. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
  47. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/dtype_codec.py +0 -0
  48. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/exceptions.py +0 -0
  49. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/fastapi/__init__.py +0 -0
  50. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/fastapi/auth.py +0 -0
  51. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/metatables/__init__.py +0 -0
  52. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/metatables/core.py +0 -0
  53. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/models_foundry.py +0 -0
  54. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/models_user.py +0 -0
  55. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/client/utils.py +0 -0
  56. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/defaults.py +0 -0
  57. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/instrumentation/__init__.py +0 -0
  58. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/instrumentation/utils.py +0 -0
  59. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/logconf.py +0 -0
  60. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/__init__.py +0 -0
  61. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/__main__.py +0 -0
  62. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
  63. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
  64. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  65. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  66. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
  67. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  68. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  69. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
  70. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  71. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  72. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/future_registry.py +0 -0
  73. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/hashing.py +0 -0
  74. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/__init__.py +0 -0
  75. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/alembic.py +0 -0
  76. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/env.py +0 -0
  77. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/provider.py +0 -0
  78. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/registry.py +0 -0
  79. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
  80. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
  81. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
  82. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
  83. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  84. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/schema_names.py +0 -0
  85. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  86. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/project_skills.py +0 -0
  87. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/runtime_context.py +0 -0
  88. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/runtime_flags.py +0 -0
  89. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence/scaffold_skills.py +0 -0
  90. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence.egg-info/SOURCES.txt +0 -0
  91. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence.egg-info/dependency_links.txt +0 -0
  92. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence.egg-info/entry_points.txt +0 -0
  93. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence.egg-info/requires.txt +0 -0
  94. {mainsequence-6.0.22 → mainsequence-6.0.24}/mainsequence.egg-info/top_level.txt +0 -0
  95. {mainsequence-6.0.22 → mainsequence-6.0.24}/setup.cfg +0 -0
  96. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_auth_precedence.py +0 -0
  97. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_build_operations_hashing.py +0 -0
  98. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_canonical_api_route_contract.py +0 -0
  99. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_cli.py +0 -0
  100. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_cli_browser_auth.py +0 -0
  101. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_cli_migrations.py +0 -0
  102. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_client.py +0 -0
  103. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_data_access_mixin_dimension_audit.py +0 -0
  104. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_data_node_storage_dimension_queries.py +0 -0
  105. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_data_node_update_flow.py +0 -0
  106. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_dependency_extras.py +0 -0
  107. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_duckdb_interface_dimensions.py +0 -0
  108. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_instrumentation.py +0 -0
  109. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_logconf.py +0 -0
  110. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_meta_table_migrations.py +0 -0
  111. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_meta_tables_client_models.py +0 -0
  112. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
  113. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_models_user_request_bound_auth.py +0 -0
  114. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_pod_project_resolution.py +0 -0
  115. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_project_batch_jobs_from_file.py +0 -0
  116. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_project_branch_contract.py +0 -0
  117. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_project_skills.py +0 -0
  118. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_run_configuration.py +0 -0
  119. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_scaffold_skills.py +0 -0
  120. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_schema_names.py +0 -0
  121. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_secret_client_model.py +0 -0
  122. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_source_table_configuration.py +0 -0
  123. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_sqlite_interface_dimensions.py +0 -0
  124. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_update_runner_uid_runtime.py +0 -0
  125. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_update_statistics.py +0 -0
  126. {mainsequence-6.0.22 → mainsequence-6.0.24}/tests/test_update_uid_guards.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 6.0.22
3
+ Version: 6.0.24
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: LicenseRef-MainSequence-Proprietary
@@ -209,14 +209,14 @@ Validate the deployment path, not the Streamlit design.
209
209
 
210
210
  `automatic_deployment` is the automated deployment opt-in flag on a `ResourceRelease`. It means repository synchronization can rotate an existing release to the latest synced project commit for the same resource path.
211
211
 
212
- When `automatic_deployment=True`, repository-sync events may create a `ResourceReleaseAutomaticDeploymentRun` with source `repository_event`. That run:
212
+ When `automatic_deployment=True`, repository-sync events may create a unified `DeploymentRun` with `target_type="resource_release"` and source `repository_event`. That run:
213
213
 
214
214
  - reads the project's current synced commit
215
215
  - resolves the current project resource at the release's existing resource path
216
216
  - resolves the current adjacent `README.md` when the release kind requires one, such as Streamlit dashboards
217
217
  - creates or resolves the project image for that commit
218
218
  - redeploys the existing release to the current resource, README, and project image
219
- - records status, current step, revision context, image/resource context, result, and errors on the deployment run
219
+ - records state, phase, outcome, revision context, artifact context, steps, logs, result, and errors on the deployment run
220
220
 
221
221
  This is not a local development shortcut. It does not deploy unpushed local files. The repository must be pushed, the project repository must be synced, and project resource discovery must find the resource at the same path for the current commit.
222
222
 
@@ -247,28 +247,12 @@ The SDK surface also accepts:
247
247
  - `ProjectResource.create_dashboard(..., automatic_deployment=True)`
248
248
  - `ProjectResource.create_fastapi(..., automatic_deployment=True)`
249
249
  - `ResourceRelease.create(..., automatic_deployment=True)`
250
- - `ResourceRelease.deploy_current_version()` for an SDK-triggered manual deployment run
250
+ - `ResourceRelease.deploy_current_version()` for an SDK-triggered manual deployment run; it returns `DeploymentRun`
251
+ - `DeploymentRun.filter(target_type="resource_release", target_uid=release.uid)` to inspect runs for one release
251
252
 
252
253
  Do not claim there is a CLI command for `deploy_current_version` unless the local CLI actually exposes one. In this SDK, the manual detail action is available through the client model.
253
254
 
254
- Automatic deployment runs can have these statuses:
255
-
256
- - `pending`
257
- - `running`
258
- - `waiting_project_image`
259
- - `waiting_runtime_ready`
260
- - `no_action`
261
- - `deployed`
262
- - `skipped`
263
- - `blocked`
264
- - `failed`
265
-
266
- Important blocked/skipped cases include:
267
-
268
- - `automatic_deployment_disabled`: repository-event rotation skipped because the release is not opted in
269
- - `project_current_version_missing`: the project has no current synced version
270
- - `resource_missing_for_current_commit`: no resource exists at the release path for the latest synced commit
271
- - `readme_missing_for_current_commit`: a dashboard-style release needs an adjacent README for the current commit
255
+ Inspect the unified run's `state`, `phase`, `outcome`, `steps`, `logs`, and `error` fields. Do not use legacy resource-release deployment status fields or filters.
272
256
 
273
257
  ## Review Rules
274
258
 
@@ -100,7 +100,6 @@ class BaseObjectOrm:
100
100
  "GithubOrganization": "github-organizations",
101
101
  "ProjectResource": "project-resources",
102
102
  "ResourceRelease": "resource-releases",
103
- "ResourceReleaseAutomaticDeploymentRun": "deployment-runs",
104
103
  "DeploymentRun": "deployment-runs",
105
104
  "Bucket": "buckets",
106
105
  }
@@ -963,6 +963,24 @@ class ResourceReleaseKind(str, Enum):
963
963
  STATIC_SITE = "static_site"
964
964
 
965
965
 
966
+ class AutomaticRedeploymentPolicy(BaseModel):
967
+ tag_regex: str | None = Field(
968
+ ...,
969
+ title="Tag Regex",
970
+ description=(
971
+ "Regular expression matched against immutable repository tags. "
972
+ "Null enables redeployment for every commit."
973
+ ),
974
+ examples=[None, r"^v(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)$"],
975
+ )
976
+ policy_revision: PositiveInt = Field(
977
+ ...,
978
+ title="Policy Revision",
979
+ description="Immutable revision of the current automatic redeployment policy.",
980
+ examples=[1, 3],
981
+ )
982
+
983
+
966
984
  class ResourceRelease(ShareableObjectMixin, BaseObjectOrm, BasePydanticModel):
967
985
  FILTERSET_FIELDS: ClassVar[dict[str, list[str]]] = {
968
986
  "uid": ["exact", "in"],
@@ -1065,6 +1083,14 @@ class ResourceRelease(ShareableObjectMixin, BaseObjectOrm, BasePydanticModel):
1065
1083
  ),
1066
1084
  examples=[False, True],
1067
1085
  )
1086
+ automatic_redeployment_policy: AutomaticRedeploymentPolicy | None = Field(
1087
+ default=None,
1088
+ title="Automatic Redeployment Policy",
1089
+ description=(
1090
+ "Backend-owned tag matching policy and its current immutable revision. "
1091
+ "The revision is returned by the API and is not part of create requests."
1092
+ ),
1093
+ )
1068
1094
 
1069
1095
  @classmethod
1070
1096
  def create(
@@ -1148,7 +1174,7 @@ class ResourceRelease(ShareableObjectMixin, BaseObjectOrm, BasePydanticModel):
1148
1174
  self,
1149
1175
  *,
1150
1176
  timeout: int | float | tuple[float, float] | None = None,
1151
- ) -> ResourceReleaseAutomaticDeploymentRun:
1177
+ ) -> DeploymentRun:
1152
1178
  data = self._request_detail_action(
1153
1179
  r_type="POST",
1154
1180
  action_name="deploy-current-version",
@@ -1156,116 +1182,7 @@ class ResourceRelease(ShareableObjectMixin, BaseObjectOrm, BasePydanticModel):
1156
1182
  timeout=timeout,
1157
1183
  expected_statuses=(200, 202),
1158
1184
  )
1159
- return ResourceReleaseAutomaticDeploymentRun(**data)
1160
-
1161
-
1162
- class ResourceReleaseAutomaticDeploymentRun(BaseObjectOrm, BasePydanticModel):
1163
- FILTERSET_FIELDS: ClassVar[dict[str, list[str]]] = {
1164
- "resource_release__uid": ["exact", "in"],
1165
- "status": ["exact", "in"],
1166
- "release_kind": ["exact", "in"],
1167
- "automatic_deployment_source": ["exact", "in"],
1168
- }
1169
- FILTER_VALUE_NORMALIZERS: ClassVar[dict[str, str]] = {
1170
- "resource_release__uid": "uid",
1171
- "status": "str",
1172
- "release_kind": "str",
1173
- "automatic_deployment_source": "str",
1174
- }
1175
-
1176
- STATUS_PENDING: ClassVar[str] = "pending"
1177
- STATUS_RUNNING: ClassVar[str] = "running"
1178
- STATUS_WAITING_PROJECT_IMAGE: ClassVar[str] = "waiting_project_image"
1179
- STATUS_WAITING_RUNTIME_READY: ClassVar[str] = "waiting_runtime_ready"
1180
- STATUS_NO_ACTION: ClassVar[str] = "no_action"
1181
- STATUS_DEPLOYED: ClassVar[str] = "deployed"
1182
- STATUS_SKIPPED: ClassVar[str] = "skipped"
1183
- STATUS_BLOCKED: ClassVar[str] = "blocked"
1184
- STATUS_FAILED: ClassVar[str] = "failed"
1185
-
1186
- SOURCE_MANUAL: ClassVar[str] = "manual"
1187
- SOURCE_REPOSITORY_EVENT: ClassVar[str] = "repository_event"
1188
-
1189
- uid: str | None = Field(
1190
- None,
1191
- title="Resource Release Automatic Deployment Run UID",
1192
- description="Public UID of the automatic deployment run.",
1193
- )
1194
- resource_release_uid: str | None = Field(
1195
- None,
1196
- title="Resource Release UID",
1197
- description="Public UID snapshot of the resource release targeted by this run.",
1198
- )
1199
- resource_release_name: str = Field(
1200
- default="",
1201
- title="Resource Release Name",
1202
- description="Display-name snapshot of the targeted resource release.",
1203
- )
1204
- release_kind: ResourceReleaseKind | str | None = Field(
1205
- None,
1206
- title="Release Kind",
1207
- description="Resource release kind targeted by this run.",
1208
- )
1209
- status: str | None = Field(
1210
- None,
1211
- title="Status",
1212
- description="Deployment run status.",
1213
- )
1214
- current_step: str = Field(
1215
- default="",
1216
- title="Current Step",
1217
- description="Current orchestration step key.",
1218
- )
1219
- automatic_deployment_source: str | None = Field(
1220
- None,
1221
- title="Automatic Deployment Source",
1222
- description="Run trigger source, for example manual or repository_event.",
1223
- )
1224
- revision_context: dict[str, Any] = Field(
1225
- default_factory=dict,
1226
- title="Revision Context",
1227
- description="Stable project/revision/resource context for the run.",
1228
- )
1229
- trigger_context: dict[str, Any] = Field(
1230
- default_factory=dict,
1231
- title="Trigger Context",
1232
- description="Repository event or manual trigger correlation metadata.",
1233
- )
1234
- image_artifact_context: dict[str, Any] = Field(
1235
- default_factory=dict,
1236
- title="Image Artifact Context",
1237
- description="Project image and resource snapshots captured by the run.",
1238
- )
1239
- cleanup_context: dict[str, Any] = Field(
1240
- default_factory=dict,
1241
- title="Cleanup Context",
1242
- description="Cleanup outcomes captured by the run.",
1243
- )
1244
- started_at: datetime.datetime | None = Field(
1245
- None,
1246
- title="Started At",
1247
- description="Timestamp when execution started.",
1248
- )
1249
- finished_at: datetime.datetime | None = Field(
1250
- None,
1251
- title="Finished At",
1252
- description="Timestamp when execution finished.",
1253
- )
1254
- result: dict[str, Any] = Field(
1255
- default_factory=dict,
1256
- title="Result",
1257
- description="Terminal or intermediate deployment result payload.",
1258
- )
1259
- error_code: str = Field(
1260
- default="",
1261
- title="Error Code",
1262
- description="Machine-readable error code when the run failed or was blocked.",
1263
- )
1264
- error_detail: str = Field(
1265
- default="",
1266
- title="Error Detail",
1267
- description="Human-readable error detail when the run failed or was blocked.",
1268
- )
1185
+ return DeploymentRun(**data)
1269
1186
 
1270
1187
 
1271
1188
  class DeploymentRunTarget(BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 6.0.22
3
+ Version: 6.0.24
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: LicenseRef-MainSequence-Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "6.0.22"
7
+ version = "6.0.24"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -2041,7 +2041,7 @@ def test_resource_release_model_accepts_collection_payload_without_runtime_child
2041
2041
  assert release.related_job_uid is None
2042
2042
 
2043
2043
 
2044
- def test_resource_release_models_support_automatic_deployment_payloads():
2044
+ def test_resource_release_model_supports_automatic_deployment_payloads():
2045
2045
  release_uid = "2f4c4c3d-5669-4da5-9d86-b84633c1e6ed"
2046
2046
  resource_uid = "857bec7b-dd77-4272-aecd-13fc2138eacc"
2047
2047
  job_uid = "7d0ab07c-d1c0-4b7f-9c69-3c1a41c0a4da"
@@ -2055,40 +2055,30 @@ def test_resource_release_models_support_automatic_deployment_payloads():
2055
2055
  "related_job_uid": job_uid,
2056
2056
  "release_kind": "fastapi",
2057
2057
  "automatic_deployment": True,
2058
+ "automatic_redeployment_policy": {
2059
+ "tag_regex": None,
2060
+ "policy_revision": 3,
2061
+ },
2058
2062
  }
2059
2063
  )
2060
2064
 
2061
2065
  assert release.uid == release_uid
2062
2066
  assert release.release_kind == models_helpers_mod.ResourceReleaseKind.FAST_API
2063
2067
  assert release.automatic_deployment is True
2068
+ assert release.automatic_redeployment_policy is not None
2069
+ assert release.automatic_redeployment_policy.tag_regex is None
2070
+ assert release.automatic_redeployment_policy.policy_revision == 3
2064
2071
 
2065
- run = models_helpers_mod.ResourceReleaseAutomaticDeploymentRun.model_validate(
2066
- {
2067
- "uid": "11111111-1111-4111-8111-111111111111",
2068
- "resource_release_uid": release_uid,
2069
- "resource_release_name": "analytics-123",
2070
- "release_kind": "fastapi",
2071
- "status": "waiting_project_image",
2072
- "current_step": "wait_project_image",
2073
- "automatic_deployment_source": "manual",
2074
- "revision_context": {"current_commit_sha": "abc123"},
2075
- "trigger_context": {},
2076
- "image_artifact_context": {"project_image": {"state": "waiting"}},
2077
- "cleanup_context": {},
2078
- "started_at": "2026-01-01T00:00:00Z",
2079
- "finished_at": None,
2080
- "result": {},
2081
- "error_code": "",
2082
- "error_detail": "",
2083
- }
2084
- )
2085
2072
 
2086
- assert run.resource_release_uid == release_uid
2087
- assert (
2088
- run.status
2089
- == models_helpers_mod.ResourceReleaseAutomaticDeploymentRun.STATUS_WAITING_PROJECT_IMAGE
2090
- )
2091
- assert run.revision_context["current_commit_sha"] == "abc123"
2073
+ def test_legacy_resource_release_deployment_run_model_is_not_exported():
2074
+ import mainsequence.client as client_package
2075
+
2076
+ legacy_name = "ResourceReleaseAutomaticDeploymentRun"
2077
+
2078
+ assert not hasattr(models_helpers_mod, legacy_name)
2079
+ assert not hasattr(client_package, legacy_name)
2080
+ with pytest.raises(KeyError, match=legacy_name):
2081
+ models_helpers_mod.get_model_class(legacy_name)
2092
2082
 
2093
2083
 
2094
2084
  def test_resource_release_create_sends_automatic_deployment(monkeypatch):
@@ -2110,6 +2100,10 @@ def test_resource_release_create_sends_automatic_deployment(monkeypatch):
2110
2100
  "related_job_uid": "7d0ab07c-d1c0-4b7f-9c69-3c1a41c0a4da",
2111
2101
  "release_kind": "fastapi",
2112
2102
  "automatic_deployment": True,
2103
+ "automatic_redeployment_policy": {
2104
+ "tag_regex": "^v[0-9]+\\.[0-9]+\\.[0-9]+$",
2105
+ "policy_revision": 1,
2106
+ },
2113
2107
  }
2114
2108
 
2115
2109
  def _fake_make_request(*, s, loaders, r_type, url, payload, time_out=None):
@@ -2132,6 +2126,9 @@ def test_resource_release_create_sends_automatic_deployment(monkeypatch):
2132
2126
  )
2133
2127
 
2134
2128
  assert release.automatic_deployment is True
2129
+ assert release.automatic_redeployment_policy is not None
2130
+ assert release.automatic_redeployment_policy.tag_regex == "^v[0-9]+\\.[0-9]+\\.[0-9]+$"
2131
+ assert release.automatic_redeployment_policy.policy_revision == 1
2135
2132
  assert captured["r_type"] == "POST"
2136
2133
  assert str(captured["url"]).endswith("/api/v1/resource-releases/")
2137
2134
  assert captured["payload"]["json"]["automatic_deployment"] is True
@@ -2209,21 +2206,31 @@ def test_resource_release_deploy_current_version_posts_detail_action(monkeypatch
2209
2206
  def json():
2210
2207
  return {
2211
2208
  "uid": run_uid,
2212
- "resource_release_uid": release_uid,
2213
- "resource_release_name": "analytics-123",
2214
- "release_kind": "fastapi",
2215
- "status": "pending",
2216
- "current_step": "resolve_eligibility",
2217
- "automatic_deployment_source": "manual",
2209
+ "target_type": "resource_release",
2210
+ "target": {"uid": release_uid, "name": "analytics-123", "kind": "fastapi"},
2211
+ "project_branch_uid": "33333333-3333-4333-8333-333333333333",
2212
+ "operation": "build_and_deploy",
2213
+ "source": "manual",
2214
+ "commit_sha": "a" * 40,
2215
+ "configuration_revision": 4,
2216
+ "state": "succeeded",
2217
+ "phase": "completed",
2218
+ "outcome": "deployed",
2219
+ "created_at": "2026-07-19T12:00:00Z",
2220
+ "started_at": "2026-07-19T12:00:01Z",
2221
+ "finished_at": "2026-07-19T12:00:05Z",
2218
2222
  "revision_context": {},
2219
2223
  "trigger_context": {},
2220
- "image_artifact_context": {},
2224
+ "artifact_context": {},
2221
2225
  "cleanup_context": {},
2222
- "started_at": None,
2223
- "finished_at": None,
2224
2226
  "result": {},
2225
- "error_code": "",
2226
- "error_detail": "",
2227
+ "steps": [],
2228
+ "logs": {
2229
+ "state": "available",
2230
+ "url": f"/api/v1/deployment-runs/{run_uid}/logs/",
2231
+ "retention_expires_at": None,
2232
+ },
2233
+ "error": None,
2227
2234
  }
2228
2235
 
2229
2236
  def _fake_make_request(*, s, loaders, r_type, url, payload, time_out=None):
@@ -2238,7 +2245,13 @@ def test_resource_release_deploy_current_version_posts_detail_action(monkeypatch
2238
2245
  run = release.deploy_current_version(timeout=9)
2239
2246
 
2240
2247
  assert run.uid == run_uid
2241
- assert run.status == models_helpers_mod.ResourceReleaseAutomaticDeploymentRun.STATUS_PENDING
2248
+ assert isinstance(run, models_helpers_mod.DeploymentRun)
2249
+ assert run.target.uid == release_uid
2250
+ assert run.state == "succeeded"
2251
+ assert run.phase == "completed"
2252
+ assert run.outcome == "deployed"
2253
+ assert run.logs.state == "available"
2254
+ assert run.error is None
2242
2255
  assert captured == {
2243
2256
  "r_type": "POST",
2244
2257
  "url": (
@@ -2250,24 +2263,78 @@ def test_resource_release_deploy_current_version_posts_detail_action(monkeypatch
2250
2263
  }
2251
2264
 
2252
2265
 
2253
- def test_resource_release_automatic_deployment_run_filters_are_uid_based():
2254
- release_uid = " 2f4c4c3d-5669-4da5-9d86-b84633c1e6ed "
2266
+ def test_deployment_run_collection_and_detail_use_unified_resource_release_contract(monkeypatch):
2267
+ captured = []
2268
+ run_uid = "11111111-1111-4111-8111-111111111111"
2269
+ release_uid = "2f4c4c3d-5669-4da5-9d86-b84633c1e6ed"
2270
+ response_payload = {
2271
+ "uid": run_uid,
2272
+ "target_type": "resource_release",
2273
+ "target": {"uid": release_uid, "name": "analytics-123", "kind": "fastapi"},
2274
+ "project_branch_uid": "33333333-3333-4333-8333-333333333333",
2275
+ "operation": "build_and_deploy",
2276
+ "source": "repository_event",
2277
+ "commit_sha": "a" * 40,
2278
+ "configuration_revision": 4,
2279
+ "state": "succeeded",
2280
+ "phase": "completed",
2281
+ "outcome": "deployed",
2282
+ "created_at": "2026-07-19T12:00:00Z",
2283
+ "started_at": "2026-07-19T12:00:01Z",
2284
+ "finished_at": "2026-07-19T12:00:05Z",
2285
+ "revision_context": {},
2286
+ "trigger_context": {},
2287
+ "artifact_context": {},
2288
+ "cleanup_context": {},
2289
+ "result": {},
2290
+ "steps": [],
2291
+ "logs": {
2292
+ "state": "available",
2293
+ "url": f"/api/v1/deployment-runs/{run_uid}/logs/",
2294
+ "retention_expires_at": None,
2295
+ },
2296
+ "error": None,
2297
+ }
2298
+
2299
+ class FakeResponse:
2300
+ status_code = 200
2301
+
2302
+ def __init__(self, payload):
2303
+ self.payload = payload
2255
2304
 
2256
- normalized = models_helpers_mod.ResourceReleaseAutomaticDeploymentRun._normalize_filter_kwargs(
2257
- {
2258
- "resource_release__uid": release_uid,
2259
- "status__in": [" deployed ", " failed "],
2260
- "release_kind": " fastapi ",
2261
- "automatic_deployment_source": " manual ",
2262
- }
2305
+ def json(self):
2306
+ return self.payload
2307
+
2308
+ def _fake_make_request(*, s, loaders, r_type, url, payload, time_out=None):
2309
+ captured.append({"r_type": r_type, "url": url, "payload": payload})
2310
+ if url.rstrip("/") == models_helpers_mod.DeploymentRun.get_object_url():
2311
+ return FakeResponse({"results": [dict(response_payload)], "next": None})
2312
+ return FakeResponse(dict(response_payload))
2313
+
2314
+ monkeypatch.setattr(
2315
+ models_helpers_mod.DeploymentRun,
2316
+ "build_session",
2317
+ classmethod(lambda cls: object()),
2263
2318
  )
2319
+ monkeypatch.setattr(base_mod, "make_request", _fake_make_request)
2264
2320
 
2265
- assert normalized == {
2266
- "resource_release__uid": release_uid.strip(),
2267
- "status__in": ["deployed", "failed"],
2268
- "release_kind": "fastapi",
2269
- "automatic_deployment_source": "manual",
2321
+ runs = models_helpers_mod.DeploymentRun.filter(
2322
+ target_type="resource_release",
2323
+ target_uid=release_uid,
2324
+ )
2325
+ detail = models_helpers_mod.DeploymentRun.get(pk=run_uid)
2326
+
2327
+ assert len(runs) == 1
2328
+ assert runs[0].target.uid == release_uid
2329
+ assert detail.state == "succeeded"
2330
+ assert detail.phase == "completed"
2331
+ assert detail.outcome == "deployed"
2332
+ assert detail.logs.state == "available"
2333
+ assert detail.error is None
2334
+ assert captured[0]["payload"] == {
2335
+ "params": {"target_type": "resource_release", "target_uid": release_uid}
2270
2336
  }
2337
+ assert captured[1]["url"].endswith(f"/deployment-runs/{run_uid}/")
2271
2338
 
2272
2339
 
2273
2340
  def test_unified_deployment_run_models_and_filters(monkeypatch):
File without changes
File without changes
File without changes