Skip to content

Commit 032dced

Browse files
committed
Merge branch 'develop' into feature/served-pipelines
2 parents 06d6732 + 244c489 commit 032dced

File tree

14 files changed

+332
-260
lines changed

14 files changed

+332
-260
lines changed

.coderabbit.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ early_access: false
33
reviews:
44
high_level_summary: true
55
poem: true
6-
review_status: true
6+
review_status: false
77
collapse_walkthrough: true
88
path_filters:
99
- "!**/.xml"

docs/book/getting-started/zenml-pro/projects.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ This command sets the "default" project as your active project. All subsequent Z
4545
Best practice is to set your active project right after running `zenml init`, just like you would set an active stack. This ensures all your resources are properly organized within the project.
4646
{% endhint %}
4747

48+
You can also set the project to be used by your client via an environment variable:
49+
50+
```bash
51+
export ZENML_ACTIVE_PROJECT_ID=<PROJECT_ID>
52+
```
53+
4854
### Setting a default project
4955

5056
The default project is something that each user can configure. This project will be automatically set as the active project when you connect your local Python client to a ZenML Pro workspace.

docs/book/reference/environment-variables.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,12 @@ For more information on server configuration, see the [ZenML Server documentatio
135135

136136
## Client configuration
137137

138-
Setting the `ZENML_STORE_URL` and `ZENML_STORE_API_KEY` environment variables automatically connects your ZenML Client to the specified server. This method is particularly useful when you are using the ZenML client in an automated CI/CD workload environment like GitHub Actions or GitLab CI or in a containerized environment like Docker or Kubernetes:
138+
Setting the `ZENML_STORE_URL`, `ZENML_STORE_API_KEY` and `ZENML_ACTIVE_PROJECT_ID` environment variables automatically connects your ZenML Client to the specified server for a specific project. This method is particularly useful when you are using the ZenML client in an automated CI/CD workload environment like GitHub Actions or GitLab CI or in a containerized environment like Docker or Kubernetes:
139139

140140
```bash
141141
export ZENML_STORE_URL=https://...
142142
export ZENML_STORE_API_KEY=<API_KEY>
143+
export ZENML_ACTIVE_PROJECT_ID=<PROJECT_ID>
143144
```
144145

145146
<figure><img src="https://static.scarf.sh/a.png?x-pxid=f0b4f458-0a54-4fcd-aa95-d5ee424815bc" alt="ZenML Scarf"><figcaption></figcaption></figure>

docs/link_checker.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,32 @@
7979
except ImportError:
8080
HAS_REQUESTS = False
8181

82+
# ------------------------------------------------------------------------------
83+
# Centralized exceptions for HTTP status codes we consider acceptable for
84+
# specific URLs or domains during link validation.
85+
#
86+
# How to extend:
87+
# - To allow particular statuses for a *single* URL, add an entry to
88+
# EXEMPT_URL_STATUS with the normalized URL (no trailing slash) and a set
89+
# of allowed status codes.
90+
# - To allow particular statuses for an entire *domain*, add an entry to
91+
# EXEMPT_DOMAIN_STATUS with the domain suffix (e.g. "example.com") and a
92+
# set of allowed status codes. Matching uses host.endswith(domain).
93+
# ------------------------------------------------------------------------------
94+
95+
EXEMPT_URL_STATUS: Dict[str, set] = {
96+
# Requires authentication; often returns 403 to unauthenticated HEAD/GET.
97+
"https://huggingface.co/settings/tokens": {403},
98+
}
99+
100+
EXEMPT_DOMAIN_STATUS: Dict[str, set] = {
101+
# Some HashiCorp properties rate-limit automated checks.
102+
"hashicorp.com": {429},
103+
"developer.hashicorp.com": {429},
104+
"terraform.io": {429},
105+
"www.terraform.io": {429},
106+
}
107+
82108
# Default policies for troublesome domains that frequently rate-limit automated traffic.
83109
# These defaults can be extended via CLI flags.
84110
DEFAULT_IGNORE_429_DOMAINS = {
@@ -96,6 +122,37 @@
96122
)
97123

98124

125+
def is_exception_status(cleaned_url: str, status_code: int) -> bool:
126+
"""
127+
Return True if (cleaned_url, status_code) should be treated as valid based
128+
on configured exemptions.
129+
130+
Rules:
131+
- Exact-URL exemptions: compared after stripping any trailing slash.
132+
- Domain exemptions: suffix match against the URL hostname.
133+
"""
134+
if not cleaned_url.startswith(("http://", "https://")):
135+
return False
136+
137+
# Exact URL exemptions (normalize by removing trailing slash)
138+
normalized = cleaned_url.rstrip("/")
139+
allowed = EXEMPT_URL_STATUS.get(normalized)
140+
if allowed and status_code in allowed:
141+
return True
142+
143+
# Domain exemptions
144+
try:
145+
host = urlparse(cleaned_url).hostname or ""
146+
except Exception:
147+
host = ""
148+
149+
for domain, codes in EXEMPT_DOMAIN_STATUS.items():
150+
if host.endswith(domain) and status_code in codes:
151+
return True
152+
153+
return False
154+
155+
99156
def find_markdown_files(directory: str) -> List[str]:
100157
"""Find all markdown files in the given directory and its subdirectories."""
101158
markdown_files = []
@@ -334,7 +391,11 @@ def check_link_validity(
334391
cleaned_url, timeout=timeout, allow_redirects=True
335392
)
336393

337-
# Soft-pass 429 for configured domains
394+
# Check configured exception rules (domain- and URL-specific)
395+
if is_exception_status(cleaned_url, response.status_code):
396+
return (url, True, None, response.status_code)
397+
398+
# Also check for 429 soft-pass for configured domains (backward compatibility)
338399
if response.status_code == 429 and ignore_429:
339400
return (
340401
url,

src/zenml/models/v2/core/pipeline_run.py

Lines changed: 82 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -207,32 +207,6 @@ class PipelineRunResponseBody(ProjectScopedResponseBody):
207207
default=None,
208208
title="The reason for the status of the pipeline run.",
209209
)
210-
stack: Optional["StackResponse"] = Field(
211-
default=None, title="The stack that was used for this run."
212-
)
213-
pipeline: Optional["PipelineResponse"] = Field(
214-
default=None, title="The pipeline this run belongs to."
215-
)
216-
build: Optional["PipelineBuildResponse"] = Field(
217-
default=None, title="The pipeline build that was used for this run."
218-
)
219-
schedule: Optional["ScheduleResponse"] = Field(
220-
default=None, title="The schedule that was used for this run."
221-
)
222-
code_reference: Optional["CodeReferenceResponse"] = Field(
223-
default=None, title="The code reference that was used for this run."
224-
)
225-
snapshot_id: Optional[UUID] = Field(
226-
default=None, title="The snapshot that was used for this run."
227-
)
228-
trigger_execution: Optional["TriggerExecutionResponse"] = Field(
229-
default=None, title="The trigger execution that triggered this run."
230-
)
231-
model_version_id: Optional[UUID] = Field(
232-
title="The ID of the model version that was "
233-
"configured by this pipeline run explicitly.",
234-
default=None,
235-
)
236210

237211
model_config = ConfigDict(protected_namespaces=())
238212

@@ -290,10 +264,6 @@ class PipelineRunResponseMetadata(ProjectScopedResponseMetadata):
290264
description="DEPRECATED: Template used for the pipeline run.",
291265
deprecated=True,
292266
)
293-
source_snapshot_id: Optional[UUID] = Field(
294-
default=None,
295-
description="Source snapshot used for the pipeline run.",
296-
)
297267
is_templatable: bool = Field(
298268
default=False,
299269
description="Whether a template can be created from this run.",
@@ -304,6 +274,25 @@ class PipelineRunResponseResources(ProjectScopedResponseResources):
304274
"""Class for all resource models associated with the pipeline run entity."""
305275

306276
snapshot: Optional["PipelineSnapshotResponse"] = None
277+
source_snapshot: Optional["PipelineSnapshotResponse"] = None
278+
stack: Optional["StackResponse"] = Field(
279+
default=None, title="The stack that was used for this run."
280+
)
281+
pipeline: Optional["PipelineResponse"] = Field(
282+
default=None, title="The pipeline this run belongs to."
283+
)
284+
build: Optional["PipelineBuildResponse"] = Field(
285+
default=None, title="The pipeline build that was used for this run."
286+
)
287+
schedule: Optional["ScheduleResponse"] = Field(
288+
default=None, title="The schedule that was used for this run."
289+
)
290+
code_reference: Optional["CodeReferenceResponse"] = Field(
291+
default=None, title="The code reference that was used for this run."
292+
)
293+
trigger_execution: Optional["TriggerExecutionResponse"] = Field(
294+
default=None, title="The trigger execution that triggered this run."
295+
)
307296
model_version: Optional[ModelVersionResponse] = None
308297
tags: List[TagResponse] = Field(
309298
title="Tags associated with the pipeline run.",
@@ -387,78 +376,6 @@ def status(self) -> ExecutionStatus:
387376
"""
388377
return self.get_body().status
389378

390-
@property
391-
def stack(self) -> Optional["StackResponse"]:
392-
"""The `stack` property.
393-
394-
Returns:
395-
the value of the property.
396-
"""
397-
return self.get_body().stack
398-
399-
@property
400-
def pipeline(self) -> Optional["PipelineResponse"]:
401-
"""The `pipeline` property.
402-
403-
Returns:
404-
the value of the property.
405-
"""
406-
return self.get_body().pipeline
407-
408-
@property
409-
def build(self) -> Optional["PipelineBuildResponse"]:
410-
"""The `build` property.
411-
412-
Returns:
413-
the value of the property.
414-
"""
415-
return self.get_body().build
416-
417-
@property
418-
def schedule(self) -> Optional["ScheduleResponse"]:
419-
"""The `schedule` property.
420-
421-
Returns:
422-
the value of the property.
423-
"""
424-
return self.get_body().schedule
425-
426-
@property
427-
def trigger_execution(self) -> Optional["TriggerExecutionResponse"]:
428-
"""The `trigger_execution` property.
429-
430-
Returns:
431-
the value of the property.
432-
"""
433-
return self.get_body().trigger_execution
434-
435-
@property
436-
def code_reference(self) -> Optional["CodeReferenceResponse"]:
437-
"""The `schedule` property.
438-
439-
Returns:
440-
the value of the property.
441-
"""
442-
return self.get_body().code_reference
443-
444-
@property
445-
def snapshot_id(self) -> Optional["UUID"]:
446-
"""The `snapshot_id` property.
447-
448-
Returns:
449-
the value of the property.
450-
"""
451-
return self.get_body().snapshot_id
452-
453-
@property
454-
def model_version_id(self) -> Optional[UUID]:
455-
"""The `model_version_id` property.
456-
457-
Returns:
458-
the value of the property.
459-
"""
460-
return self.get_body().model_version_id
461-
462379
@property
463380
def run_metadata(self) -> Dict[str, MetadataType]:
464381
"""The `run_metadata` property.
@@ -568,15 +485,6 @@ def template_id(self) -> Optional[UUID]:
568485
"""
569486
return self.get_metadata().template_id
570487

571-
@property
572-
def source_snapshot_id(self) -> Optional[UUID]:
573-
"""The `source_snapshot_id` property.
574-
575-
Returns:
576-
the value of the property.
577-
"""
578-
return self.get_metadata().source_snapshot_id
579-
580488
@property
581489
def is_templatable(self) -> bool:
582490
"""The `is_templatable` property.
@@ -595,6 +503,69 @@ def snapshot(self) -> Optional["PipelineSnapshotResponse"]:
595503
"""
596504
return self.get_resources().snapshot
597505

506+
@property
507+
def source_snapshot(self) -> Optional["PipelineSnapshotResponse"]:
508+
"""The `source_snapshot` property.
509+
510+
Returns:
511+
the value of the property.
512+
"""
513+
return self.get_resources().source_snapshot
514+
515+
@property
516+
def stack(self) -> Optional["StackResponse"]:
517+
"""The `stack` property.
518+
519+
Returns:
520+
the value of the property.
521+
"""
522+
return self.get_resources().stack
523+
524+
@property
525+
def pipeline(self) -> Optional["PipelineResponse"]:
526+
"""The `pipeline` property.
527+
528+
Returns:
529+
the value of the property.
530+
"""
531+
return self.get_resources().pipeline
532+
533+
@property
534+
def build(self) -> Optional["PipelineBuildResponse"]:
535+
"""The `build` property.
536+
537+
Returns:
538+
the value of the property.
539+
"""
540+
return self.get_resources().build
541+
542+
@property
543+
def schedule(self) -> Optional["ScheduleResponse"]:
544+
"""The `schedule` property.
545+
546+
Returns:
547+
the value of the property.
548+
"""
549+
return self.get_resources().schedule
550+
551+
@property
552+
def trigger_execution(self) -> Optional["TriggerExecutionResponse"]:
553+
"""The `trigger_execution` property.
554+
555+
Returns:
556+
the value of the property.
557+
"""
558+
return self.get_resources().trigger_execution
559+
560+
@property
561+
def code_reference(self) -> Optional["CodeReferenceResponse"]:
562+
"""The `schedule` property.
563+
564+
Returns:
565+
the value of the property.
566+
"""
567+
return self.get_resources().code_reference
568+
598569
@property
599570
def model_version(self) -> Optional[ModelVersionResponse]:
600571
"""The `model_version` property.

0 commit comments

Comments
 (0)