cartage 0.1.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.
Files changed (70) hide show
  1. cartage-0.1.0/.github/workflows/publish.yml +67 -0
  2. cartage-0.1.0/.gitignore +5 -0
  3. cartage-0.1.0/PKG-INFO +188 -0
  4. cartage-0.1.0/README.md +161 -0
  5. cartage-0.1.0/docs/cartage-demo.gif +0 -0
  6. cartage-0.1.0/docs/cartage-demo.tape +66 -0
  7. cartage-0.1.0/docs/superpowers/plans/2026-09-30-cartage-v0.1.md +4680 -0
  8. cartage-0.1.0/docs/superpowers/specs/2026-09-30-cartage-v0.1-design.md +438 -0
  9. cartage-0.1.0/pyproject.toml +52 -0
  10. cartage-0.1.0/src/cartage/__init__.py +1 -0
  11. cartage-0.1.0/src/cartage/adapters/__init__.py +0 -0
  12. cartage-0.1.0/src/cartage/adapters/destinations/__init__.py +0 -0
  13. cartage-0.1.0/src/cartage/adapters/destinations/sap/__init__.py +0 -0
  14. cartage-0.1.0/src/cartage/adapters/destinations/sap/bapi_meta/BAPI_MATERIAL_SAVEDATA.yaml +24 -0
  15. cartage-0.1.0/src/cartage/adapters/destinations/sap/destination.py +64 -0
  16. cartage-0.1.0/src/cartage/adapters/destinations/sap/mapping.py +82 -0
  17. cartage-0.1.0/src/cartage/adapters/destinations/sap/meta.py +59 -0
  18. cartage-0.1.0/src/cartage/adapters/destinations/sap/transports/__init__.py +12 -0
  19. cartage-0.1.0/src/cartage/adapters/destinations/sap/transports/mock.py +163 -0
  20. cartage-0.1.0/src/cartage/adapters/destinations/sap/transports/rfc.py +8 -0
  21. cartage-0.1.0/src/cartage/adapters/engines/__init__.py +0 -0
  22. cartage-0.1.0/src/cartage/adapters/engines/dlt.py +80 -0
  23. cartage-0.1.0/src/cartage/adapters/engines/python.py +26 -0
  24. cartage-0.1.0/src/cartage/adapters/orchestrators/__init__.py +0 -0
  25. cartage-0.1.0/src/cartage/adapters/orchestrators/airflow.py +115 -0
  26. cartage-0.1.0/src/cartage/adapters/orchestrators/templates/cartage/airflow_dag.py.j2 +47 -0
  27. cartage-0.1.0/src/cartage/adapters/sources/__init__.py +0 -0
  28. cartage-0.1.0/src/cartage/adapters/sources/dlt.py +57 -0
  29. cartage-0.1.0/src/cartage/adapters/sources/filesystem.py +82 -0
  30. cartage-0.1.0/src/cartage/adapters/sources/s3.py +122 -0
  31. cartage-0.1.0/src/cartage/checks.py +44 -0
  32. cartage-0.1.0/src/cartage/cli.py +250 -0
  33. cartage-0.1.0/src/cartage/config.py +245 -0
  34. cartage-0.1.0/src/cartage/core.py +91 -0
  35. cartage-0.1.0/src/cartage/project_template/cartage.yaml +18 -0
  36. cartage-0.1.0/src/cartage/project_template/connections.yaml +29 -0
  37. cartage-0.1.0/src/cartage/project_template/data/materials/materials.csv +21 -0
  38. cartage-0.1.0/src/cartage/project_template/gitignore +1 -0
  39. cartage-0.1.0/src/cartage/project_template/pipelines/materials.yaml +28 -0
  40. cartage-0.1.0/src/cartage/project_template/transforms/__init__.py +0 -0
  41. cartage-0.1.0/src/cartage/project_template/transforms/materials.py +22 -0
  42. cartage-0.1.0/src/cartage/registry.py +34 -0
  43. cartage-0.1.0/src/cartage/runner.py +124 -0
  44. cartage-0.1.0/src/cartage/scaffold.py +32 -0
  45. cartage-0.1.0/src/cartage/secrets.py +79 -0
  46. cartage-0.1.0/src/cartage/state.py +79 -0
  47. cartage-0.1.0/src/cartage/transforms.py +160 -0
  48. cartage-0.1.0/src/cartage/ui.py +218 -0
  49. cartage-0.1.0/tests/conftest.py +15 -0
  50. cartage-0.1.0/tests/test_airflow.py +135 -0
  51. cartage-0.1.0/tests/test_airflow_integration.py +29 -0
  52. cartage-0.1.0/tests/test_cli_misc.py +59 -0
  53. cartage-0.1.0/tests/test_cli_run.py +90 -0
  54. cartage-0.1.0/tests/test_cli_validate_plan.py +83 -0
  55. cartage-0.1.0/tests/test_config.py +111 -0
  56. cartage-0.1.0/tests/test_core.py +64 -0
  57. cartage-0.1.0/tests/test_dlt_engine.py +50 -0
  58. cartage-0.1.0/tests/test_dlt_source.py +90 -0
  59. cartage-0.1.0/tests/test_filesystem.py +78 -0
  60. cartage-0.1.0/tests/test_registry.py +32 -0
  61. cartage-0.1.0/tests/test_runner.py +66 -0
  62. cartage-0.1.0/tests/test_s3.py +101 -0
  63. cartage-0.1.0/tests/test_sap_destination.py +73 -0
  64. cartage-0.1.0/tests/test_sap_mapping.py +64 -0
  65. cartage-0.1.0/tests/test_sap_mock.py +136 -0
  66. cartage-0.1.0/tests/test_scaffold.py +22 -0
  67. cartage-0.1.0/tests/test_secrets.py +99 -0
  68. cartage-0.1.0/tests/test_state.py +63 -0
  69. cartage-0.1.0/tests/test_transforms.py +160 -0
  70. cartage-0.1.0/uv.lock +4278 -0
@@ -0,0 +1,67 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ name: Test and build distributions
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Check out source
16
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
+ with:
18
+ persist-credentials: false
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
22
+ with:
23
+ enable-cache: true
24
+ cache-dependency-glob: uv.lock
25
+
26
+ - name: Install Python
27
+ run: uv python install 3.11
28
+
29
+ - name: Install test dependencies
30
+ run: uv sync --locked --python 3.11 --group dev --extra dlt
31
+
32
+ - name: Run tests
33
+ run: uv run --locked pytest
34
+
35
+ - name: Build distributions
36
+ run: uv build --no-sources
37
+
38
+ - name: Upload distributions
39
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
40
+ with:
41
+ name: python-package-distributions
42
+ path: dist/
43
+ if-no-files-found: error
44
+
45
+ publish:
46
+ name: Publish distributions
47
+ needs: build
48
+ runs-on: ubuntu-latest
49
+ environment:
50
+ name: pypi
51
+ url: https://pypi.org/p/cartage
52
+ permissions:
53
+ id-token: write
54
+ steps:
55
+ - name: Install uv
56
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
57
+ with:
58
+ enable-cache: false
59
+
60
+ - name: Download distributions
61
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
62
+ with:
63
+ name: python-package-distributions
64
+ path: dist/
65
+
66
+ - name: Publish to PyPI
67
+ run: uv publish
@@ -0,0 +1,5 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ .pytest_cache/
cartage-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.5
2
+ Name: cartage
3
+ Version: 0.1.0
4
+ Summary: Declarative data migrations: YAML pipelines, Python transforms, any engine, any orchestrator.
5
+ License-Expression: Apache-2.0
6
+ Requires-Python: >=3.11.4
7
+ Requires-Dist: jinja2>=3.1
8
+ Requires-Dist: pydantic>=2.6
9
+ Requires-Dist: rich>=13.7
10
+ Requires-Dist: ruamel-yaml>=0.18
11
+ Requires-Dist: typer>=0.12
12
+ Provides-Extra: airflow
13
+ Requires-Dist: apache-airflow-providers-standard>=1.0; extra == 'airflow'
14
+ Requires-Dist: apache-airflow>=3.0; extra == 'airflow'
15
+ Requires-Dist: kubernetes>=29; extra == 'airflow'
16
+ Provides-Extra: all
17
+ Requires-Dist: apache-airflow-providers-standard>=1.0; extra == 'all'
18
+ Requires-Dist: apache-airflow>=3.0; extra == 'all'
19
+ Requires-Dist: boto3>=1.34; extra == 'all'
20
+ Requires-Dist: dlt>=1.20; extra == 'all'
21
+ Requires-Dist: kubernetes>=29; extra == 'all'
22
+ Provides-Extra: dlt
23
+ Requires-Dist: dlt>=1.20; extra == 'dlt'
24
+ Provides-Extra: s3
25
+ Requires-Dist: boto3>=1.34; extra == 's3'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Cartage
29
+
30
+ Declarative data migrations. Describe sources, destinations and pipelines in YAML, put custom logic in plain
31
+ Python, run locally to test, and generate thin Airflow DAGs for production.
32
+
33
+ ![Cartage terminal demo](docs/cartage-demo.gif)
34
+
35
+ - **Sources:** local CSV folders, S3, and any [dlt](https://dlthub.com) source.
36
+ - **Destinations:** SAP via BAPIs (v0.1 ships a mock SAP; RFC is planned).
37
+ - **Engines:** `python` (a plain loop) and `dlt`.
38
+ - **Orchestrators:** Airflow. The DAG only calls `cartage run`, so local and production run the same code.
39
+
40
+ ## Quickstart
41
+
42
+ ```bash
43
+ pip install "cartage[dlt]"
44
+ cartage init demo && cd demo
45
+ cartage validate
46
+ cartage plan materials # dry run: records before/after transforms and the BAPI payloads
47
+ cartage run materials # reads 20 rows, filters 2, sends 18: 16 load, 2 fail on purpose (exit 1)
48
+ cartage run materials --advance-state
49
+ cartage run materials # incremental: nothing new to load
50
+ cartage generate # dags/materials_to_sap.py
51
+ ```
52
+
53
+ Split-screen demo: run `cartage sap mock` in one terminal, set `url: http://localhost:8765` on `sap_erp.dev`
54
+ in `connections.yaml`, and run the pipeline in another terminal.
55
+
56
+ ## Project layout
57
+
58
+ | Path | Purpose |
59
+ |---|---|
60
+ | `cartage.yaml` | environments, default engine, state location, orchestrator settings |
61
+ | `connections.yaml` | named connections with settings per environment — secrets only as references |
62
+ | `pipelines/*.yaml` | source → transforms → destination (+ schedule) |
63
+ | `transforms/*.py` | `map` / `filter` / `batch` functions referenced as `module:function` |
64
+ | `templates/airflow/dag.py.j2` | optional DAG template override (`{% extends "cartage/airflow_dag.py.j2" %}`) |
65
+ | `.cartage/` | git-ignored: `secrets.yaml`, `state/`, `rejects/` |
66
+
67
+ ## YAML configuration
68
+
69
+ Cartage uses three YAML layers: `cartage.yaml` sets project-wide defaults, `connections.yaml` defines named services
70
+ per environment, and each `pipelines/*.yaml` file describes one source-to-destination flow.
71
+
72
+ ### `cartage.yaml`
73
+
74
+ ```yaml
75
+ project: inventory
76
+ environments: [dev, prd]
77
+ default_env: dev
78
+
79
+ defaults:
80
+ engine: dlt # or python
81
+
82
+ state:
83
+ dev: { path: .cartage/state }
84
+
85
+ orchestrators:
86
+ airflow:
87
+ dags_dir: dags
88
+ default_args: { owner: data-team, retries: 1 }
89
+ ```
90
+
91
+ The `default_env` must be listed in `environments`. A pipeline can override the default engine with its own `engine`.
92
+ State settings are optional; when omitted, Cartage stores local state under `.cartage/state`.
93
+
94
+ ### `connections.yaml`
95
+
96
+ Connections have a `type` and an `envs` map. Put service-specific settings under the environment where they apply;
97
+ pipelines refer to the connection by name. Keep credentials out of the file and use secret or environment references.
98
+
99
+ ```yaml
100
+ connections:
101
+ local_files:
102
+ type: filesystem
103
+ envs:
104
+ dev: { path: ./data }
105
+
106
+ sap_erp:
107
+ type: sap
108
+ envs:
109
+ dev: { transport: mock, client: "100" }
110
+ prd:
111
+ transport: rfc
112
+ ashost: sap.example.com
113
+ sysnr: "00"
114
+ client: "100"
115
+ user: "${secret:sap.user}"
116
+ passwd: "${secret:sap.passwd}"
117
+ ```
118
+
119
+ `${secret:key}` resolves from `CARTAGE_SECRET__<KEY>` (dots become double underscores and names are uppercased),
120
+ then `.cartage/secrets.yaml`. `${env:NAME}` reads an environment variable directly. See [Secrets](#secrets) for details.
121
+
122
+ ### `pipelines/*.yaml`
123
+
124
+ Each pipeline names a source connection, applies an ordered list of transforms, and writes to a destination. A step
125
+ must have exactly one of `map`, `filter`, or `batch`; each reference uses the `module:function` format.
126
+
127
+ ```yaml
128
+ name: materials_to_sap
129
+ source:
130
+ connection: local_files
131
+ format: csv
132
+ path: materials/*.csv
133
+ incremental: true
134
+
135
+ transforms:
136
+ - map: transforms.materials:normalize_uom
137
+ - filter: transforms.materials:is_active
138
+ - batch: transforms.materials:dedupe
139
+ with: { key: material }
140
+
141
+ destination:
142
+ connection: sap_erp
143
+ bapi: BAPI_MATERIAL_SAVEDATA
144
+ mapping:
145
+ material: HEADDATA.MATERIAL
146
+ description: MATERIALDESCRIPTION[].MATL_DESC
147
+ uom: CLIENTDATA.BASE_UOM
148
+ constants:
149
+ MATERIALDESCRIPTION[].LANGU_ISO: EN
150
+ commit: per_record
151
+
152
+ schedule:
153
+ airflow:
154
+ schedule: "0 3 * * *"
155
+ tags: [sap, materials]
156
+ ```
157
+
158
+ Source and destination fields other than `connection` are adapter options. Transform functions live in your `transforms/`
159
+ package; `with` passes keyword arguments to the function. The optional `schedule.airflow` block controls DAG generation
160
+ with `cartage generate`. Run `cartage validate` after editing YAML to check the project and references.
161
+
162
+ ## Batches
163
+
164
+ - `batch` transforms see one source batch at a time (default 100 rows, `batch_size` source option), never across files.
165
+ - `commit: per_batch` commits each batch the engine hands to the destination (the dlt engine re-chunks at 100).
166
+
167
+ ## Secrets
168
+
169
+ `${secret:sap.passwd}` reads `CARTAGE_SECRET__SAP__PASSWD`, then `.cartage/secrets.yaml`. `${env:VAR}` reads an
170
+ environment variable. Resolved values are never printed.
171
+
172
+ ## State and rejects
173
+
174
+ Incremental state is saved only when a run has no record errors (or with `--advance-state`). Rejected records
175
+ go to `.cartage/rejects/<pipeline>/<run_id>.jsonl`. `cartage state show|reset <pipeline>` inspects or clears state.
176
+
177
+ ## Exit codes
178
+
179
+ `0` ok · `1` record errors · `2` configuration error · `3` fatal run error (connection, transport, `on_error: fail`).
180
+
181
+ ## Extending
182
+
183
+ Adapters are entry points in the groups `cartage.sources`, `cartage.destinations`, `cartage.engines` and
184
+ `cartage.orchestrators`. `cartage plugins` lists what is installed.
185
+
186
+ ## License
187
+
188
+ Apache-2.0
@@ -0,0 +1,161 @@
1
+ # Cartage
2
+
3
+ Declarative data migrations. Describe sources, destinations and pipelines in YAML, put custom logic in plain
4
+ Python, run locally to test, and generate thin Airflow DAGs for production.
5
+
6
+ ![Cartage terminal demo](docs/cartage-demo.gif)
7
+
8
+ - **Sources:** local CSV folders, S3, and any [dlt](https://dlthub.com) source.
9
+ - **Destinations:** SAP via BAPIs (v0.1 ships a mock SAP; RFC is planned).
10
+ - **Engines:** `python` (a plain loop) and `dlt`.
11
+ - **Orchestrators:** Airflow. The DAG only calls `cartage run`, so local and production run the same code.
12
+
13
+ ## Quickstart
14
+
15
+ ```bash
16
+ pip install "cartage[dlt]"
17
+ cartage init demo && cd demo
18
+ cartage validate
19
+ cartage plan materials # dry run: records before/after transforms and the BAPI payloads
20
+ cartage run materials # reads 20 rows, filters 2, sends 18: 16 load, 2 fail on purpose (exit 1)
21
+ cartage run materials --advance-state
22
+ cartage run materials # incremental: nothing new to load
23
+ cartage generate # dags/materials_to_sap.py
24
+ ```
25
+
26
+ Split-screen demo: run `cartage sap mock` in one terminal, set `url: http://localhost:8765` on `sap_erp.dev`
27
+ in `connections.yaml`, and run the pipeline in another terminal.
28
+
29
+ ## Project layout
30
+
31
+ | Path | Purpose |
32
+ |---|---|
33
+ | `cartage.yaml` | environments, default engine, state location, orchestrator settings |
34
+ | `connections.yaml` | named connections with settings per environment — secrets only as references |
35
+ | `pipelines/*.yaml` | source → transforms → destination (+ schedule) |
36
+ | `transforms/*.py` | `map` / `filter` / `batch` functions referenced as `module:function` |
37
+ | `templates/airflow/dag.py.j2` | optional DAG template override (`{% extends "cartage/airflow_dag.py.j2" %}`) |
38
+ | `.cartage/` | git-ignored: `secrets.yaml`, `state/`, `rejects/` |
39
+
40
+ ## YAML configuration
41
+
42
+ Cartage uses three YAML layers: `cartage.yaml` sets project-wide defaults, `connections.yaml` defines named services
43
+ per environment, and each `pipelines/*.yaml` file describes one source-to-destination flow.
44
+
45
+ ### `cartage.yaml`
46
+
47
+ ```yaml
48
+ project: inventory
49
+ environments: [dev, prd]
50
+ default_env: dev
51
+
52
+ defaults:
53
+ engine: dlt # or python
54
+
55
+ state:
56
+ dev: { path: .cartage/state }
57
+
58
+ orchestrators:
59
+ airflow:
60
+ dags_dir: dags
61
+ default_args: { owner: data-team, retries: 1 }
62
+ ```
63
+
64
+ The `default_env` must be listed in `environments`. A pipeline can override the default engine with its own `engine`.
65
+ State settings are optional; when omitted, Cartage stores local state under `.cartage/state`.
66
+
67
+ ### `connections.yaml`
68
+
69
+ Connections have a `type` and an `envs` map. Put service-specific settings under the environment where they apply;
70
+ pipelines refer to the connection by name. Keep credentials out of the file and use secret or environment references.
71
+
72
+ ```yaml
73
+ connections:
74
+ local_files:
75
+ type: filesystem
76
+ envs:
77
+ dev: { path: ./data }
78
+
79
+ sap_erp:
80
+ type: sap
81
+ envs:
82
+ dev: { transport: mock, client: "100" }
83
+ prd:
84
+ transport: rfc
85
+ ashost: sap.example.com
86
+ sysnr: "00"
87
+ client: "100"
88
+ user: "${secret:sap.user}"
89
+ passwd: "${secret:sap.passwd}"
90
+ ```
91
+
92
+ `${secret:key}` resolves from `CARTAGE_SECRET__<KEY>` (dots become double underscores and names are uppercased),
93
+ then `.cartage/secrets.yaml`. `${env:NAME}` reads an environment variable directly. See [Secrets](#secrets) for details.
94
+
95
+ ### `pipelines/*.yaml`
96
+
97
+ Each pipeline names a source connection, applies an ordered list of transforms, and writes to a destination. A step
98
+ must have exactly one of `map`, `filter`, or `batch`; each reference uses the `module:function` format.
99
+
100
+ ```yaml
101
+ name: materials_to_sap
102
+ source:
103
+ connection: local_files
104
+ format: csv
105
+ path: materials/*.csv
106
+ incremental: true
107
+
108
+ transforms:
109
+ - map: transforms.materials:normalize_uom
110
+ - filter: transforms.materials:is_active
111
+ - batch: transforms.materials:dedupe
112
+ with: { key: material }
113
+
114
+ destination:
115
+ connection: sap_erp
116
+ bapi: BAPI_MATERIAL_SAVEDATA
117
+ mapping:
118
+ material: HEADDATA.MATERIAL
119
+ description: MATERIALDESCRIPTION[].MATL_DESC
120
+ uom: CLIENTDATA.BASE_UOM
121
+ constants:
122
+ MATERIALDESCRIPTION[].LANGU_ISO: EN
123
+ commit: per_record
124
+
125
+ schedule:
126
+ airflow:
127
+ schedule: "0 3 * * *"
128
+ tags: [sap, materials]
129
+ ```
130
+
131
+ Source and destination fields other than `connection` are adapter options. Transform functions live in your `transforms/`
132
+ package; `with` passes keyword arguments to the function. The optional `schedule.airflow` block controls DAG generation
133
+ with `cartage generate`. Run `cartage validate` after editing YAML to check the project and references.
134
+
135
+ ## Batches
136
+
137
+ - `batch` transforms see one source batch at a time (default 100 rows, `batch_size` source option), never across files.
138
+ - `commit: per_batch` commits each batch the engine hands to the destination (the dlt engine re-chunks at 100).
139
+
140
+ ## Secrets
141
+
142
+ `${secret:sap.passwd}` reads `CARTAGE_SECRET__SAP__PASSWD`, then `.cartage/secrets.yaml`. `${env:VAR}` reads an
143
+ environment variable. Resolved values are never printed.
144
+
145
+ ## State and rejects
146
+
147
+ Incremental state is saved only when a run has no record errors (or with `--advance-state`). Rejected records
148
+ go to `.cartage/rejects/<pipeline>/<run_id>.jsonl`. `cartage state show|reset <pipeline>` inspects or clears state.
149
+
150
+ ## Exit codes
151
+
152
+ `0` ok · `1` record errors · `2` configuration error · `3` fatal run error (connection, transport, `on_error: fail`).
153
+
154
+ ## Extending
155
+
156
+ Adapters are entry points in the groups `cartage.sources`, `cartage.destinations`, `cartage.engines` and
157
+ `cartage.orchestrators`. `cartage plugins` lists what is installed.
158
+
159
+ ## License
160
+
161
+ Apache-2.0
Binary file
@@ -0,0 +1,66 @@
1
+ Output docs/cartage-demo.gif
2
+ Require uv
3
+ Set Shell "zsh"
4
+ Set FontSize 14
5
+ Set Width 1100
6
+ Set Height 680
7
+ Set TypingSpeed 25ms
8
+ Set Theme "Catppuccin Mocha"
9
+ Set LoopOffset 4%
10
+
11
+ Hide
12
+ Type `CARTAGE_ROOT="$PWD"; DEMO_DIR="$(mktemp -d /tmp/cartage-demo.XXXXXX)"; trap 'rm -rf "$DEMO_DIR"' EXIT; uv run cartage init "$DEMO_DIR" >/dev/null && source "$CARTAGE_ROOT/.venv/bin/activate" && export PATH="$CARTAGE_ROOT/.venv/bin:$PATH" && hash -r && cd "$DEMO_DIR" && clear`
13
+ Enter
14
+ Show
15
+ Sleep 700ms
16
+
17
+ Type "Let's see what Cartage can do..."
18
+ Sleep 1s
19
+ Backspace 32
20
+ Type "cartage --help"
21
+ Enter
22
+ Wait+Line />$/
23
+ Sleep 3s
24
+
25
+ Type "First, let's validate the demo project."
26
+ Sleep 1s
27
+ Backspace 39
28
+ Type "cartage validate"
29
+ Enter
30
+ Wait+Line />$/
31
+ Sleep 2s
32
+
33
+ Type "Watch the UOM conversion: KGS to KG."
34
+ Sleep 1s
35
+ Backspace 36
36
+ Type "cartage plan materials -n 3"
37
+ Enter
38
+ Wait+Line />$/
39
+ ScrollUp 30
40
+ Sleep 1s
41
+ ScrollDown@100ms 30
42
+ Sleep 2s
43
+
44
+ Type "Now generate the production Airflow DAG."
45
+ Sleep 1s
46
+ Backspace 40
47
+ Type "cartage generate"
48
+ Enter
49
+ Wait+Line />$/
50
+ Sleep 2s
51
+
52
+ Type "Let's read the generated Airflow DAG."
53
+ Sleep 1s
54
+ Backspace 37
55
+ Type "cat dags/materials_to_sap.py"
56
+ Enter
57
+ Wait+Line />$/
58
+ Sleep 4s
59
+
60
+ Type "Finally, run the sample materials pipeline."
61
+ Sleep 1s
62
+ Backspace 43
63
+ Type "cartage run materials"
64
+ Enter
65
+ Wait+Line />$/
66
+ Sleep 4s