pipewright-mlops 1.0.0__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.
- pipewright_mlops-1.0.0/.dockerignore +5 -0
- pipewright_mlops-1.0.0/.gitattributes +6 -0
- pipewright_mlops-1.0.0/.github/workflows/ci.yml +64 -0
- pipewright_mlops-1.0.0/.github/workflows/release.yml +132 -0
- pipewright_mlops-1.0.0/.gitignore +36 -0
- pipewright_mlops-1.0.0/CHANGELOG.md +128 -0
- pipewright_mlops-1.0.0/CONTEXT.md +1052 -0
- pipewright_mlops-1.0.0/CONTRIBUTING.md +52 -0
- pipewright_mlops-1.0.0/LICENSE +202 -0
- pipewright_mlops-1.0.0/NOTICE +7 -0
- pipewright_mlops-1.0.0/PKG-INFO +549 -0
- pipewright_mlops-1.0.0/README.md +510 -0
- pipewright_mlops-1.0.0/SECURITY.md +24 -0
- pipewright_mlops-1.0.0/STABILITY.md +41 -0
- pipewright_mlops-1.0.0/docs/FEATURES.md +126 -0
- pipewright_mlops-1.0.0/docs/ONBOARDING.md +149 -0
- pipewright_mlops-1.0.0/docs/images/pipewright-architecture.drawio +1 -0
- pipewright_mlops-1.0.0/docs/images/pipewright-architecture.png +0 -0
- pipewright_mlops-1.0.0/docs/images/pipewright-gke-architecture.drawio +1 -0
- pipewright_mlops-1.0.0/docs/images/pipewright-gke-architecture.png +0 -0
- pipewright_mlops-1.0.0/examples/churn/components/champion.py +51 -0
- pipewright_mlops-1.0.0/examples/churn/components/churn_net.py +83 -0
- pipewright_mlops-1.0.0/examples/churn/components/evaluate.py +90 -0
- pipewright_mlops-1.0.0/examples/churn/components/ingest.py +61 -0
- pipewright_mlops-1.0.0/examples/churn/components/mlkit.py +369 -0
- pipewright_mlops-1.0.0/examples/churn/components/monitor.py +216 -0
- pipewright_mlops-1.0.0/examples/churn/components/prepare.py +50 -0
- pipewright_mlops-1.0.0/examples/churn/components/record.py +51 -0
- pipewright_mlops-1.0.0/examples/churn/components/register.py +114 -0
- pipewright_mlops-1.0.0/examples/churn/components/requirements.txt +19 -0
- pipewright_mlops-1.0.0/examples/churn/components/score.py +63 -0
- pipewright_mlops-1.0.0/examples/churn/components/torchserve_handler.py +63 -0
- pipewright_mlops-1.0.0/examples/churn/components/train.py +80 -0
- pipewright_mlops-1.0.0/examples/churn/components/tune.py +75 -0
- pipewright_mlops-1.0.0/examples/churn/components/validate.py +40 -0
- pipewright_mlops-1.0.0/examples/churn/config/data.yaml +32 -0
- pipewright_mlops-1.0.0/examples/churn/config/evaluation.yaml +31 -0
- pipewright_mlops-1.0.0/examples/churn/config/operations.yaml.tmpl +33 -0
- pipewright_mlops-1.0.0/examples/churn/config/runtime.yaml.tmpl +14 -0
- pipewright_mlops-1.0.0/examples/churn/config/synthetic.yaml +22 -0
- pipewright_mlops-1.0.0/examples/churn/config/tables.yaml.tmpl +28 -0
- pipewright_mlops-1.0.0/examples/churn/config/training.yaml +21 -0
- pipewright_mlops-1.0.0/examples/churn/config/triggers.yaml.tmpl +10 -0
- pipewright_mlops-1.0.0/examples/churn/data/generate_sample.py +85 -0
- pipewright_mlops-1.0.0/examples/churn/data/sample_customers.csv +5001 -0
- pipewright_mlops-1.0.0/examples/churn/data/scoring_customers.csv +1001 -0
- pipewright_mlops-1.0.0/examples/churn/data/scoring_drifted.csv +1001 -0
- pipewright_mlops-1.0.0/examples/churn/environments/dev.yaml +8 -0
- pipewright_mlops-1.0.0/examples/churn/inference_pipeline.py +61 -0
- pipewright_mlops-1.0.0/examples/churn/schemas/churn_outcomes.json +17 -0
- pipewright_mlops-1.0.0/examples/churn/schemas/model_metrics.json +127 -0
- pipewright_mlops-1.0.0/examples/churn/schemas/model_monitoring.json +82 -0
- pipewright_mlops-1.0.0/examples/churn/schemas/predictions.json +10 -0
- pipewright_mlops-1.0.0/examples/churn/schemas/raw_customers.json +72 -0
- pipewright_mlops-1.0.0/examples/churn/tests/requirements.txt +4 -0
- pipewright_mlops-1.0.0/examples/churn/tests/test_mlkit.py +149 -0
- pipewright_mlops-1.0.0/examples/churn/training_pipeline.py +93 -0
- pipewright_mlops-1.0.0/examples/churn/usecase.yaml.tmpl +41 -0
- pipewright_mlops-1.0.0/examples/features/config/data.yaml.tmpl +33 -0
- pipewright_mlops-1.0.0/examples/features/data/customer_profile.csv +15001 -0
- pipewright_mlops-1.0.0/examples/features/data/entities.csv +5001 -0
- pipewright_mlops-1.0.0/examples/features/data/load_tables.sh +21 -0
- pipewright_mlops-1.0.0/examples/features/data/support_activity.csv +10001 -0
- pipewright_mlops-1.0.0/examples/features/example.yaml +12 -0
- pipewright_mlops-1.0.0/examples/features/usecase.yaml.tmpl +49 -0
- pipewright_mlops-1.0.0/examples/minimal/config/data.yaml +21 -0
- pipewright_mlops-1.0.0/examples/minimal/example.yaml +9 -0
- pipewright_mlops-1.0.0/examples/minimal/usecase.yaml.tmpl +39 -0
- pipewright_mlops-1.0.0/examples/ray/components/ray_tune.py +56 -0
- pipewright_mlops-1.0.0/examples/ray/components/ray_tune_job.py +66 -0
- pipewright_mlops-1.0.0/examples/ray/components/requirements.txt +22 -0
- pipewright_mlops-1.0.0/examples/ray/config/data.yaml +21 -0
- pipewright_mlops-1.0.0/examples/ray/example.yaml +9 -0
- pipewright_mlops-1.0.0/examples/ray/ray-requirements.txt +5 -0
- pipewright_mlops-1.0.0/examples/ray/training_pipeline.py +84 -0
- pipewright_mlops-1.0.0/examples/ray/usecase.yaml.tmpl +57 -0
- pipewright_mlops-1.0.0/images/launcher.Dockerfile +35 -0
- pipewright_mlops-1.0.0/pyproject.toml +77 -0
- pipewright_mlops-1.0.0/scripts/check_package.py +64 -0
- pipewright_mlops-1.0.0/scripts/smoke_test.sh +40 -0
- pipewright_mlops-1.0.0/src/pipewright/__init__.py +10 -0
- pipewright_mlops-1.0.0/src/pipewright/adapters/__init__.py +23 -0
- pipewright_mlops-1.0.0/src/pipewright/adapters/kfp.py +149 -0
- pipewright_mlops-1.0.0/src/pipewright/adapters/vertex.py +65 -0
- pipewright_mlops-1.0.0/src/pipewright/alerts/__init__.py +0 -0
- pipewright_mlops-1.0.0/src/pipewright/alerts/sync.py +246 -0
- pipewright_mlops-1.0.0/src/pipewright/build/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/build/compile.py +165 -0
- pipewright_mlops-1.0.0/src/pipewright/build/images.py +226 -0
- pipewright_mlops-1.0.0/src/pipewright/build/spec_patch.py +81 -0
- pipewright_mlops-1.0.0/src/pipewright/build/templates/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/build/templates/components.Dockerfile +32 -0
- pipewright_mlops-1.0.0/src/pipewright/build/templates/pipewright_runtime.py +682 -0
- pipewright_mlops-1.0.0/src/pipewright/build/templates/ray.Dockerfile +31 -0
- pipewright_mlops-1.0.0/src/pipewright/build/templates/synthetic-engine-requirements.txt +9 -0
- pipewright_mlops-1.0.0/src/pipewright/build/templates/synthetic-engine.Dockerfile +40 -0
- pipewright_mlops-1.0.0/src/pipewright/build/templates/synthetic-evaluation-requirements.txt +10 -0
- pipewright_mlops-1.0.0/src/pipewright/build/templates/synthetic-evaluation.Dockerfile +35 -0
- pipewright_mlops-1.0.0/src/pipewright/checks/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/checks/doctor.py +666 -0
- pipewright_mlops-1.0.0/src/pipewright/checks/kfp_version.py +94 -0
- pipewright_mlops-1.0.0/src/pipewright/checks/ray_version.py +61 -0
- pipewright_mlops-1.0.0/src/pipewright/cli/__init__.py +5 -0
- pipewright_mlops-1.0.0/src/pipewright/cli/main.py +786 -0
- pipewright_mlops-1.0.0/src/pipewright/config/__init__.py +22 -0
- pipewright_mlops-1.0.0/src/pipewright/config/document.py +97 -0
- pipewright_mlops-1.0.0/src/pipewright/config/environment.py +156 -0
- pipewright_mlops-1.0.0/src/pipewright/config/io.py +38 -0
- pipewright_mlops-1.0.0/src/pipewright/config/merge.py +29 -0
- pipewright_mlops-1.0.0/src/pipewright/config/ml.py +287 -0
- pipewright_mlops-1.0.0/src/pipewright/config/models.py +140 -0
- pipewright_mlops-1.0.0/src/pipewright/config/reference.py +274 -0
- pipewright_mlops-1.0.0/src/pipewright/config/references.py +51 -0
- pipewright_mlops-1.0.0/src/pipewright/config/resolve.py +378 -0
- pipewright_mlops-1.0.0/src/pipewright/config/schema.py +110 -0
- pipewright_mlops-1.0.0/src/pipewright/config/schemas/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/config/schemas/environment.schema.json +52 -0
- pipewright_mlops-1.0.0/src/pipewright/config/schemas/manifest.schema.json +728 -0
- pipewright_mlops-1.0.0/src/pipewright/config/schemas/reference.yaml +326 -0
- pipewright_mlops-1.0.0/src/pipewright/config/schemas/usecase.schema.json +816 -0
- pipewright_mlops-1.0.0/src/pipewright/data/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/data/erase.py +134 -0
- pipewright_mlops-1.0.0/src/pipewright/data/erase_gcs.py +56 -0
- pipewright_mlops-1.0.0/src/pipewright/errors.py +18 -0
- pipewright_mlops-1.0.0/src/pipewright/gcp/__init__.py +6 -0
- pipewright_mlops-1.0.0/src/pipewright/gcp/bigquery.py +63 -0
- pipewright_mlops-1.0.0/src/pipewright/gcp/session.py +154 -0
- pipewright_mlops-1.0.0/src/pipewright/gcp/storage.py +56 -0
- pipewright_mlops-1.0.0/src/pipewright/kube.py +89 -0
- pipewright_mlops-1.0.0/src/pipewright/launcher/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/launcher/app.py +323 -0
- pipewright_mlops-1.0.0/src/pipewright/launcher/pull.py +138 -0
- pipewright_mlops-1.0.0/src/pipewright/launcher/registry.py +91 -0
- pipewright_mlops-1.0.0/src/pipewright/launcher/routing.py +169 -0
- pipewright_mlops-1.0.0/src/pipewright/models/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/models/deploy.py +172 -0
- pipewright_mlops-1.0.0/src/pipewright/models/gates.py +113 -0
- pipewright_mlops-1.0.0/src/pipewright/models/kserve.py +302 -0
- pipewright_mlops-1.0.0/src/pipewright/models/registry.py +128 -0
- pipewright_mlops-1.0.0/src/pipewright/monitoring/__init__.py +0 -0
- pipewright_mlops-1.0.0/src/pipewright/monitoring/status.py +61 -0
- pipewright_mlops-1.0.0/src/pipewright/observability/__init__.py +0 -0
- pipewright_mlops-1.0.0/src/pipewright/observability/events.py +69 -0
- pipewright_mlops-1.0.0/src/pipewright/pipelines_ui.py +97 -0
- pipewright_mlops-1.0.0/src/pipewright/plugins.py +43 -0
- pipewright_mlops-1.0.0/src/pipewright/ray_clusters/__init__.py +5 -0
- pipewright_mlops-1.0.0/src/pipewright/ray_clusters/service.py +407 -0
- pipewright_mlops-1.0.0/src/pipewright/ray_clusters/settings.py +186 -0
- pipewright_mlops-1.0.0/src/pipewright/release/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/release/contract.py +208 -0
- pipewright_mlops-1.0.0/src/pipewright/release/promote.py +509 -0
- pipewright_mlops-1.0.0/src/pipewright/release/publish.py +113 -0
- pipewright_mlops-1.0.0/src/pipewright/release/service.py +227 -0
- pipewright_mlops-1.0.0/src/pipewright/release/verify.py +103 -0
- pipewright_mlops-1.0.0/src/pipewright/retraining/__init__.py +21 -0
- pipewright_mlops-1.0.0/src/pipewright/retraining/cases.py +272 -0
- pipewright_mlops-1.0.0/src/pipewright/retraining/client.py +145 -0
- pipewright_mlops-1.0.0/src/pipewright/retraining/identity.py +73 -0
- pipewright_mlops-1.0.0/src/pipewright/retraining/service.py +243 -0
- pipewright_mlops-1.0.0/src/pipewright/retraining/store.py +99 -0
- pipewright_mlops-1.0.0/src/pipewright/retraining/summary.py +137 -0
- pipewright_mlops-1.0.0/src/pipewright/runs/__init__.py +0 -0
- pipewright_mlops-1.0.0/src/pipewright/runs/launch.py +106 -0
- pipewright_mlops-1.0.0/src/pipewright/runs/sources.py +36 -0
- pipewright_mlops-1.0.0/src/pipewright/scaffold/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/scaffold/ci.py +128 -0
- pipewright_mlops-1.0.0/src/pipewright/scaffold/ci_templates/action.yml +62 -0
- pipewright_mlops-1.0.0/src/pipewright/scaffold/ci_templates/pipewright-operate.yml +123 -0
- pipewright_mlops-1.0.0/src/pipewright/scaffold/ci_templates/pipewright-pr.yml +95 -0
- pipewright_mlops-1.0.0/src/pipewright/scaffold/ci_templates/pipewright-release.yml +66 -0
- pipewright_mlops-1.0.0/src/pipewright/scaffold/from_bq.py +123 -0
- pipewright_mlops-1.0.0/src/pipewright/scaffold/new_usecase.py +101 -0
- pipewright_mlops-1.0.0/src/pipewright/schedules/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/schedules/fire.py +47 -0
- pipewright_mlops-1.0.0/src/pipewright/schedules/kubernetes.py +143 -0
- pipewright_mlops-1.0.0/src/pipewright/schedules/sync.py +144 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/__init__.py +20 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/config.py +277 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/pipeline.py +59 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/__init__.py +5 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/engines.py +170 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/evaluation.py +234 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/gates.py +89 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/relational.py +151 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/runtime/rules.py +183 -0
- pipewright_mlops-1.0.0/src/pipewright/synthetic/synthetic_steps.py +317 -0
- pipewright_mlops-1.0.0/src/pipewright/tables/__init__.py +1 -0
- pipewright_mlops-1.0.0/src/pipewright/tables/external.py +85 -0
- pipewright_mlops-1.0.0/src/pipewright/tables/plan.py +157 -0
- pipewright_mlops-1.0.0/src/pipewright/tables/sync.py +103 -0
- pipewright_mlops-1.0.0/src/pipewright/testing/__init__.py +0 -0
- pipewright_mlops-1.0.0/src/pipewright/testing/runner.py +29 -0
- pipewright_mlops-1.0.0/tests/__init__.py +0 -0
- pipewright_mlops-1.0.0/tests/conftest.py +134 -0
- pipewright_mlops-1.0.0/tests/test_alerts.py +132 -0
- pipewright_mlops-1.0.0/tests/test_ci_and_run.py +100 -0
- pipewright_mlops-1.0.0/tests/test_config.py +191 -0
- pipewright_mlops-1.0.0/tests/test_config_ml.py +191 -0
- pipewright_mlops-1.0.0/tests/test_features.py +357 -0
- pipewright_mlops-1.0.0/tests/test_gates_and_names.py +104 -0
- pipewright_mlops-1.0.0/tests/test_kfp.py +301 -0
- pipewright_mlops-1.0.0/tests/test_kfp_version.py +50 -0
- pipewright_mlops-1.0.0/tests/test_kserve.py +136 -0
- pipewright_mlops-1.0.0/tests/test_launcher.py +180 -0
- pipewright_mlops-1.0.0/tests/test_launcher_pull.py +62 -0
- pipewright_mlops-1.0.0/tests/test_manifest_compat.py +82 -0
- pipewright_mlops-1.0.0/tests/test_ops.py +98 -0
- pipewright_mlops-1.0.0/tests/test_outputs.py +144 -0
- pipewright_mlops-1.0.0/tests/test_pipelines_ui.py +48 -0
- pipewright_mlops-1.0.0/tests/test_platform_hooks.py +257 -0
- pipewright_mlops-1.0.0/tests/test_promote.py +311 -0
- pipewright_mlops-1.0.0/tests/test_ray.py +449 -0
- pipewright_mlops-1.0.0/tests/test_release_parts.py +220 -0
- pipewright_mlops-1.0.0/tests/test_retraining.py +344 -0
- pipewright_mlops-1.0.0/tests/test_scaffold.py +69 -0
- pipewright_mlops-1.0.0/tests/test_schedules_kubernetes.py +121 -0
- pipewright_mlops-1.0.0/tests/test_sources.py +46 -0
- pipewright_mlops-1.0.0/tests/test_synthetic.py +478 -0
- pipewright_mlops-1.0.0/tests/test_tables_and_compile.py +223 -0
- pipewright_mlops-1.0.0/tests/test_yaml_help.py +183 -0
- pipewright_mlops-1.0.0/uv.lock +2120 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Pipewright product CI: every pull request and push to main, and the first stage of release.yml.
|
|
2
|
+
# Lint and tests, the example's tests, the wheel (contents checked, installed fresh, smoke tested), the launcher
|
|
3
|
+
# image built from that wheel (not pushed). The wheel is kept as the `dist` artifact for release.yml.
|
|
4
|
+
name: ci
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
pull_request:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
workflow_call:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: astral-sh/setup-uv@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.11"
|
|
23
|
+
- run: uv sync --all-extras
|
|
24
|
+
- run: uv run ruff check .
|
|
25
|
+
- run: uv run pytest -q
|
|
26
|
+
- name: Example use case tests
|
|
27
|
+
run: uv run --no-project --with-requirements examples/churn/tests/requirements.txt python -m pytest examples/churn/tests -q
|
|
28
|
+
|
|
29
|
+
package:
|
|
30
|
+
needs: test
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: astral-sh/setup-uv@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.11"
|
|
37
|
+
- name: Build wheel and sdist
|
|
38
|
+
run: uv build --out-dir dist
|
|
39
|
+
- name: Check wheel contents
|
|
40
|
+
run: uv run --no-project python scripts/check_package.py dist
|
|
41
|
+
- name: Install the wheel in a fresh environment and smoke test it
|
|
42
|
+
run: |
|
|
43
|
+
python3 -m venv "$RUNNER_TEMP/smoke"
|
|
44
|
+
"$RUNNER_TEMP/smoke/bin/pip" install "$(ls dist/pipewright_mlops-*-py3-none-any.whl)[gcp]"
|
|
45
|
+
bash scripts/smoke_test.sh "$RUNNER_TEMP/smoke/bin/pipewright"
|
|
46
|
+
- uses: actions/upload-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: dist
|
|
49
|
+
path: dist/
|
|
50
|
+
if-no-files-found: error
|
|
51
|
+
|
|
52
|
+
launcher-image:
|
|
53
|
+
needs: package
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v4
|
|
57
|
+
- uses: actions/download-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
- name: Build the launcher image from the wheel (not pushed)
|
|
62
|
+
run: |
|
|
63
|
+
rm -f dist/*.tar.gz
|
|
64
|
+
docker build -f images/launcher.Dockerfile -t pipewright-launcher:ci .
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Pipewright release: push a tag v<version> (the version in pyproject.toml) to publish that version on GitHub.
|
|
2
|
+
#
|
|
3
|
+
# ci the whole of ci.yml: tests, wheel, smoke test, launcher image build
|
|
4
|
+
# version the tag must be v<pyproject version>, or v<pyproject version>-rehearsal.<n> for a rehearsal
|
|
5
|
+
# publish-launcher the launcher image, built from the released wheel -> GitHub Container Registry
|
|
6
|
+
# (ghcr.io/<owner>/pipewright-launcher:<version>), linked to this repository so it inherits the
|
|
7
|
+
# repository's access; platform teams copy it by digest into their environment's own registry
|
|
8
|
+
# github-release the GitHub release: the wheel and sdist as files, and the launcher image digest
|
|
9
|
+
# publish-pypi the same wheel and sdist to PyPI (pipewright-mlops), real versions only, after the GitHub
|
|
10
|
+
# release; PyPI Trusted Publishing, so no API token is stored anywhere
|
|
11
|
+
#
|
|
12
|
+
# Keyless: GitHub's built-in token publishes the image and the release, and PyPI trusts this workflow through
|
|
13
|
+
# OpenID Connect (a trusted publisher for repository fluxorsoft/pipewright, workflow release.yml, environment pypi). A rehearsal
|
|
14
|
+
# (v<version>-rehearsal.<n>) publishes a pre-release and an image tagged <version>-rehearsal.<n>, to be deleted
|
|
15
|
+
# afterwards. A real version is published once: do not delete and re-create its release.
|
|
16
|
+
name: release
|
|
17
|
+
|
|
18
|
+
on:
|
|
19
|
+
push:
|
|
20
|
+
tags: ["v*"]
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: release
|
|
27
|
+
cancel-in-progress: false
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
ci:
|
|
31
|
+
uses: ./.github/workflows/ci.yml
|
|
32
|
+
|
|
33
|
+
version:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
outputs:
|
|
36
|
+
version: ${{ steps.v.outputs.version }}
|
|
37
|
+
image_tag: ${{ steps.v.outputs.image_tag }}
|
|
38
|
+
prerelease: ${{ steps.v.outputs.prerelease }}
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- id: v
|
|
42
|
+
env:
|
|
43
|
+
TAG: ${{ github.ref_name }}
|
|
44
|
+
run: |
|
|
45
|
+
version="$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
|
|
46
|
+
case "$TAG" in
|
|
47
|
+
"v$version") image_tag="$version"; prerelease=false ;;
|
|
48
|
+
"v$version"-rehearsal.[0-9]*) image_tag="${TAG#v}"; prerelease=true ;;
|
|
49
|
+
*) echo "::error::tag $TAG is neither v$version nor v$version-rehearsal.<n> (pyproject.toml version $version)"
|
|
50
|
+
exit 1 ;;
|
|
51
|
+
esac
|
|
52
|
+
case "$version" in *dev*|*a*|*b*|*rc*) prerelease=true ;; esac
|
|
53
|
+
{ echo "version=$version"; echo "image_tag=$image_tag"; echo "prerelease=$prerelease"; } >> "$GITHUB_OUTPUT"
|
|
54
|
+
|
|
55
|
+
publish-launcher:
|
|
56
|
+
needs: [ci, version]
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
permissions:
|
|
59
|
+
contents: read
|
|
60
|
+
packages: write
|
|
61
|
+
outputs:
|
|
62
|
+
image: ${{ steps.push.outputs.image }}
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
- uses: actions/download-artifact@v4
|
|
66
|
+
with:
|
|
67
|
+
name: dist
|
|
68
|
+
path: dist/
|
|
69
|
+
- name: Build from the released wheel and push to GitHub Container Registry
|
|
70
|
+
id: push
|
|
71
|
+
env:
|
|
72
|
+
VERSION: ${{ needs.version.outputs.version }}
|
|
73
|
+
TAG: ${{ needs.version.outputs.image_tag }}
|
|
74
|
+
TOKEN: ${{ github.token }}
|
|
75
|
+
run: |
|
|
76
|
+
rm -f dist/*.tar.gz
|
|
77
|
+
image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/pipewright-launcher"
|
|
78
|
+
echo "$TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin
|
|
79
|
+
docker build -f images/launcher.Dockerfile --build-arg VERSION="$VERSION" \
|
|
80
|
+
--label "org.opencontainers.image.source=https://github.com/$GITHUB_REPOSITORY" -t "$image:$TAG" .
|
|
81
|
+
docker push "$image:$TAG"
|
|
82
|
+
ref="$(docker inspect --format='{{index .RepoDigests 0}}' "$image:$TAG")"
|
|
83
|
+
echo "image=$ref" >> "$GITHUB_OUTPUT"
|
|
84
|
+
echo "Launcher image: \`$ref\`" >> "$GITHUB_STEP_SUMMARY"
|
|
85
|
+
|
|
86
|
+
github-release:
|
|
87
|
+
needs: [version, publish-launcher]
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
permissions:
|
|
90
|
+
contents: write
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/download-artifact@v4
|
|
93
|
+
with:
|
|
94
|
+
name: dist
|
|
95
|
+
path: dist/
|
|
96
|
+
- name: Create the GitHub release
|
|
97
|
+
env:
|
|
98
|
+
GH_TOKEN: ${{ github.token }}
|
|
99
|
+
VERSION: ${{ needs.version.outputs.version }}
|
|
100
|
+
IMAGE: ${{ needs.publish-launcher.outputs.image }}
|
|
101
|
+
PRERELEASE: ${{ needs.version.outputs.prerelease }}
|
|
102
|
+
run: |
|
|
103
|
+
cat > notes.md <<EOF
|
|
104
|
+
Pipewright $VERSION
|
|
105
|
+
|
|
106
|
+
- **Python package:** download the wheel below (\`gh release download $GITHUB_REF_NAME -R $GITHUB_REPOSITORY\`)
|
|
107
|
+
and install it, or pin \`pipewright-mlops[gcp]==$VERSION\` from PyPI or your organisation's package index.
|
|
108
|
+
- **Launcher image:** copy it into your environment's own registry by digest, then pin that digest in the
|
|
109
|
+
platform's Terraform:
|
|
110
|
+
\`$IMAGE\`
|
|
111
|
+
|
|
112
|
+
What changed: CHANGELOG.md. What stays stable: STABILITY.md.
|
|
113
|
+
EOF
|
|
114
|
+
flag=""; [ "$PRERELEASE" = true ] && flag="--prerelease"
|
|
115
|
+
gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "Pipewright $GITHUB_REF_NAME" \
|
|
116
|
+
--notes-file notes.md $flag
|
|
117
|
+
|
|
118
|
+
publish-pypi:
|
|
119
|
+
needs: [version, github-release]
|
|
120
|
+
if: needs.version.outputs.prerelease == 'false' # rehearsals never reach PyPI: a version there is final
|
|
121
|
+
runs-on: ubuntu-latest
|
|
122
|
+
environment:
|
|
123
|
+
name: pypi
|
|
124
|
+
url: https://pypi.org/project/pipewright-mlops/${{ needs.version.outputs.version }}/
|
|
125
|
+
permissions:
|
|
126
|
+
id-token: write # PyPI Trusted Publishing
|
|
127
|
+
steps:
|
|
128
|
+
- uses: actions/download-artifact@v4
|
|
129
|
+
with:
|
|
130
|
+
name: dist
|
|
131
|
+
path: dist/
|
|
132
|
+
- uses: pypa/gh-action-pypi-publish@v1.14.2 # twine 7: accepts metadata 2.5 (hatchling)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv/
|
|
6
|
+
venv/
|
|
7
|
+
|
|
8
|
+
# Build output. Anchored: src/pipewright/build/ is a package and must stay tracked.
|
|
9
|
+
/dist/
|
|
10
|
+
/build/
|
|
11
|
+
|
|
12
|
+
# Tool caches
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
|
|
19
|
+
# Pipewright's per-use-case build output (compiled templates, image contexts)
|
|
20
|
+
.pipewright/
|
|
21
|
+
|
|
22
|
+
# Local settings and secrets: never commit credentials, tokens or CA bundles
|
|
23
|
+
.env
|
|
24
|
+
.env.*
|
|
25
|
+
*.pem
|
|
26
|
+
*.key
|
|
27
|
+
credentials*.json
|
|
28
|
+
*-key.json
|
|
29
|
+
service-account*.json
|
|
30
|
+
|
|
31
|
+
# Editors and operating systems
|
|
32
|
+
.idea/
|
|
33
|
+
.vscode/
|
|
34
|
+
*.swp
|
|
35
|
+
.DS_Store
|
|
36
|
+
Thumbs.db
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Pipewright. The format follows [Keep a Changelog](https://keepachangelog.com/), and versions
|
|
4
|
+
follow [semantic versioning](https://semver.org/); [STABILITY.md](STABILITY.md) says what it covers.
|
|
5
|
+
|
|
6
|
+
## [1.0.0] - 2026-10-18
|
|
7
|
+
|
|
8
|
+
First release, on GitHub and on PyPI (`pipewright-mlops`). First published on GitHub on 2026-10-17 and re-cut to
|
|
9
|
+
include synthetic data and, for PyPI, a README whose links work outside the repository.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Help with the YAML.** `pipewright explain <key>` (description, type, allowed values, default, whether it may vary
|
|
14
|
+
per environment); editor schemas in `.pipewright-schemas/` and a `yaml-language-server` line in every generated
|
|
15
|
+
YAML file, for completion, hover help and underlined mistakes (`pipewright schemas` for existing repos); every use
|
|
16
|
+
case key described in `config/schemas/reference.yaml`.
|
|
17
|
+
- **Minimal example** (`new-usecase --from minimal`): the churn training code with only the required configuration.
|
|
18
|
+
- **Data contract from BigQuery** (`new-usecase --from-bq <project.dataset.table> --label <column>`): drafts
|
|
19
|
+
`config/data.yaml` and the raw table's schema from a table or view.
|
|
20
|
+
- **Evaluation gates checked by Pipewright.** `model approve` and `deploy-model` recompute the metric gates, slice
|
|
21
|
+
gates and champion margin from the version's evaluation record (`evaluation.json`) and refuse a version that fails;
|
|
22
|
+
`model list` shows each version's result.
|
|
23
|
+
- **Write-once release contracts** and component image tags derived from the release, so re-releases are
|
|
24
|
+
idempotent and immutable tags work. Contract metadata (owner, objective, description, contacts, model) for tools
|
|
25
|
+
without a repository checkout.
|
|
26
|
+
- **Per-tenant pipeline roots.** Runs write their artifacts under `pipeline-root/<tenant>/<usecase>/` of the
|
|
27
|
+
artifacts bucket, so each tenant's runner can be granted its own prefix only.
|
|
28
|
+
- **`pipewright promote`.** A tested release, and optionally its approved model, from one environment to the next:
|
|
29
|
+
destination policies first, images by digest, templates checked equal (or an accepted, recorded difference), model
|
|
30
|
+
files verified, the contract written once with `promoted_from`, the pointer moved last; idempotent; a
|
|
31
|
+
`release.promoted` event. Models carry the release that trained them (a `release` label).
|
|
32
|
+
- **Retrain decisions.** Drift or a performance drop opens a decision case instead of retraining: modes `off`,
|
|
33
|
+
`propose` and `candidate`; a decision pack (what drifted, the trend, data checks, the champion, what retraining
|
|
34
|
+
would use); approver groups with a quorum; snoozes and declines that expire; re-opening when things get worse;
|
|
35
|
+
escalation and a weekly digest; decision events and an opt-in per-tenant feed; `pipewright retrain list | show |
|
|
36
|
+
approve | decline | snooze`; launcher routes `/retraining/propose`, `/retraining/decide`, `/retraining/tick`.
|
|
37
|
+
- **Features from feature tables.** `data.features_from` names BigQuery feature tables and their features; the
|
|
38
|
+
run's source is then the entity table and `read_source` joins each feature as it was at the row's cut-off
|
|
39
|
+
(the newest value at or before it), in one BigQuery query with the environment's key. Scoring without the
|
|
40
|
+
cut-off column uses the latest values. A `data.features.resolved` event, `validate` and `doctor` checks, and
|
|
41
|
+
`new-usecase --from features`.
|
|
42
|
+
- **Feature groups as a feature source.** A `data.features_from` source may name a Feature Store feature group; the
|
|
43
|
+
Feature Registry gives its table and columns at each run. The manifest can list a tenant's feature datasets and
|
|
44
|
+
require feature tables (`features.required`); `validate`, `doctor` and `read_source` enforce both.
|
|
45
|
+
- **Ray on Agent Platform steps.** `steps.<step>.ray` gives a step a Ray cluster for one run, within the platform's
|
|
46
|
+
limits (manifest `ray`, per environment and tenant), created by the launcher as the runner identity with the
|
|
47
|
+
environment's key and Private Service Connect network attachment, and always deleted (by the step, or by the
|
|
48
|
+
launcher's tick). `pipewright release` builds the use case's Ray image; `ray_cluster()` and `ray_job()` in the
|
|
49
|
+
component runtime; a Ray client version check; `new-usecase --from ray`; a `ray.cluster.created` event records
|
|
50
|
+
what each cluster used.
|
|
51
|
+
- **Private-environment support.** The manifest `security` block (CMEK key, BigQuery key, network, reserved IP
|
|
52
|
+
ranges) is applied to every pipeline job, model, endpoint and BigQuery query; run requests over Pub/Sub
|
|
53
|
+
(`launcher.run_requests_topic`, `/pubsub/run`); events published through the regional Pub/Sub endpoint.
|
|
54
|
+
- **Manifest forward compatibility.** Manifest keys this version does not know are ignored with a warning
|
|
55
|
+
(`validate`, `doctor`, the launcher's log) instead of failing; an optional `requires` (for example `">=1.1"`)
|
|
56
|
+
makes an older version refuse the manifest with a clear message.
|
|
57
|
+
- **`predict` in the generated operate workflow** (`pipewright ci init`): runs `pipewright model predict` against the
|
|
58
|
+
use case's endpoint or InferenceService with the `instances` input, inline JSON or a JSON file in the repository
|
|
59
|
+
(for example `usecases/<name>/tests/predict.json`); the predictions go to the job summary.
|
|
60
|
+
- **CLI plugins** through the `pipewright.commands` entry point group.
|
|
61
|
+
|
|
62
|
+
### Added: private GKE
|
|
63
|
+
|
|
64
|
+
- **Orchestrator `kfp`: Kubeflow Pipelines on private GKE.** The same use case YAML, compiled templates and release
|
|
65
|
+
contracts run on Kubeflow Pipelines inside the cluster, submitted through the Kubernetes API (no exposed endpoint)
|
|
66
|
+
as each tenant's Kubernetes service account, bound to its runner identity. Tasks inside `dsl.OneOf` branches run
|
|
67
|
+
uncached on Kubeflow (it cannot resolve a cached branch).
|
|
68
|
+
- **Ray on KubeRay.** On GKE a step's cluster is a RayCluster per run on the platform's Ray node pool, reached
|
|
69
|
+
inside the cluster; or a tenant's **standing** cluster (`ray.mode: standing`), long-lived and autoscaling, to
|
|
70
|
+
which steps submit with their release's code. The release records the mode it was built for.
|
|
71
|
+
- **Online serving on KServe** (`model.serving.target: kserve`): the registered container and its Agent Platform
|
|
72
|
+
environment as an InferenceService in the tenant's serving namespace, so requests and responses match Agent
|
|
73
|
+
Platform endpoints; gates, approvals, rolling updates, undeploy; exposure in the cluster or an internal load
|
|
74
|
+
balancer. `pipewright model predict` for either platform.
|
|
75
|
+
- **Schedules as Kubernetes CronJobs** where the manifest says `schedules.backend: kubernetes`.
|
|
76
|
+
- **The launcher as a pull worker** (`launcher.runtime: gke`): the same routes fed from the same subscriptions,
|
|
77
|
+
for a launcher inside the cluster; `launcher.audiences` so releases keep working across a switch.
|
|
78
|
+
- **`pipewright pipelines-ui`**: the Kubeflow Pipelines UI on localhost, read only, through the cluster's API.
|
|
79
|
+
- A small Kubernetes client (`pipewright.kube`); credentials ask for `userinfo.email` (GKE names callers by email).
|
|
80
|
+
|
|
81
|
+
### Added: synthetic data
|
|
82
|
+
|
|
83
|
+
- **Off until a team switches it on.** A `synthetic` section in the use case turns it on, per environment; without
|
|
84
|
+
it nothing is built, compiled or released for it, and templates and digests are unchanged.
|
|
85
|
+
- **Learned inside the environment.** A built-in `synthetic` pipeline (`pipewright run --pipeline synthetic`, or a
|
|
86
|
+
schedule with `pipeline: synthetic`): the source is read as training reads it, a real holdout is kept back,
|
|
87
|
+
candidates are generated in parallel (by default the MOSTLY AI Synthetic Data SDK in local mode, which makes no
|
|
88
|
+
network calls, at sampling temperatures 1.0 and 1.3; SDV as an engine a team installs itself), and each is scored
|
|
89
|
+
on fidelity, utility (a model trained on synthetic data, tested on the real holdout) and privacy (exact copies,
|
|
90
|
+
distance to the closest record, singling-out, linkability and inference attacks). The best candidate that passes
|
|
91
|
+
every gate is published; otherwise nothing is.
|
|
92
|
+
- **Gates per purpose** (`test-data`, `sharing`), recomputed by Pipewright; `pipewright synthetic gates` lists them;
|
|
93
|
+
a use case may make privacy gates stricter, never looser. The platform's `policies.synthetic` in the manifest sets
|
|
94
|
+
the allowed purposes and engines, purposes that need differential privacy, gate floors and required approvers.
|
|
95
|
+
- **Never learned:** keys and personal-data columns get new values from column rules.
|
|
96
|
+
- **Relational and sequential** (`mode: relational`): the entity table with the feature tables of
|
|
97
|
+
`data.features_from` and the event tables in `synthetic.sequences`, learned together. Keys stay consistent across
|
|
98
|
+
tables, each history keeps its timing against the cut-off, and the gates are measured on the point-in-time join of
|
|
99
|
+
the synthetic tables; each table goes to its own output table.
|
|
100
|
+
- **Without real data:** `pipewright synthetic generate` writes rows from the data contract and the column rules,
|
|
101
|
+
for development, CI and demos.
|
|
102
|
+
- **Released like any pipeline:** `pipewright release` builds two images (the engine, and the evaluation), recorded
|
|
103
|
+
in the contract by digest, verified, and copied by `promote`. `doctor` checks the section against the live
|
|
104
|
+
environment. Events `synthetic.evaluated`, `synthetic.rejected` and `synthetic.generated`.
|
|
105
|
+
|
|
106
|
+
### Changed
|
|
107
|
+
|
|
108
|
+
Compared with the pre-release builds early users ran:
|
|
109
|
+
|
|
110
|
+
- `retraining.enabled: true` now means `retraining.mode: propose`: drift no longer requests training directly.
|
|
111
|
+
- Decision emails show the summary as a code block, so line breaks and commands are kept.
|
|
112
|
+
- `.yml` configuration files are refused with a message to rename them to `.yaml`, instead of being ignored.
|
|
113
|
+
- Licensed under the Apache License 2.0.
|
|
114
|
+
- Distribution name `pipewright-mlops` (PyPI); the import and the command stay `pipewright`.
|
|
115
|
+
|
|
116
|
+
### Fixed
|
|
117
|
+
|
|
118
|
+
- **`alerts sync` keeps its own notification channels.** Another email channel for the same address (a platform's
|
|
119
|
+
own alerts) made every sync try to repoint the use case's policies; the channel Pipewright made now wins.
|
|
120
|
+
- **Steps with only `retries` or `cache`** no longer get an empty `resources` block in the compiled template: Agent
|
|
121
|
+
Platform ran them on its sized-step backend, where the metadata server issues no ID tokens.
|
|
122
|
+
- **A release reuses a contract only when its images and compiled templates are unchanged**, so a Pipewright
|
|
123
|
+
upgrade that changes how templates compile is released (as `<commit>-<hash>`) instead of silently reusing the old
|
|
124
|
+
template.
|
|
125
|
+
- **Generated CI workflows fail when a command fails.** Steps run as `bash -eo pipefail`, so a `pipewright ...`
|
|
126
|
+
piped to `tee` no longer passes when the command fails.
|
|
127
|
+
- A pipeline job that cannot join its Agent Platform experiment (for example in a new project) is reported as
|
|
128
|
+
submitted with a warning, instead of failing the trigger and being retried as a duplicate.
|