Skip to content

Commit 5fe31dc

Browse files
committed
docs: align README and docs with teckel-spec v3.0 and teckel-rs
Position teckel4s as the Scala/Spark reference implementation of the Teckel Specification, with cross-links to teckel-spec and teckel-rs. Expand pipeline structure documentation to cover all v3.0 top-level keys (config, secrets, hooks, templates, streaming I/O), variable substitution and secrets resolution syntax, and the V-001..V-008 validation rules. Update transformation count to 45+ and flag the group/order vs groupBy/orderBy naming divergence.
1 parent fcb49dd commit 5fe31dc

6 files changed

Lines changed: 168 additions & 85 deletions

File tree

README.md

Lines changed: 119 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,193 @@
11
# Teckel
22

3-
[![Release](https://github.com/rafafrdz/teckel/actions/workflows/release.yml/badge.svg?branch=master)](https://github.com/rafafrdz/teckel/actions/workflows/release.yml)
3+
[![Release](https://github.com/eff3ct0/teckel/actions/workflows/release.yml/badge.svg?branch=master)](https://github.com/eff3ct0/teckel/actions/workflows/release.yml)
44
[![codecov](https://codecov.io/gh/eff3ct0/teckel/graph/badge.svg?token=24E1IZ0K2H)](https://codecov.io/gh/eff3ct0/teckel)
55
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
66

7-
Teckel is a framework designed to simplify the creation of Apache Spark ETL (Extract, Transform,
8-
Load) processes using YAML configuration files. This tool aims to standardize and streamline ETL workflow creation by
9-
enabling the definition of data transformations in a declarative, user-friendly format without writing extensive code.
7+
Teckel is a **Scala / Apache Spark reference implementation** of the
8+
[Teckel Specification](https://github.com/eff3ct0/teckel-spec) — a declarative YAML language for
9+
defining data transformation pipelines. You describe *what* you want done; Teckel parses the YAML,
10+
builds the DAG, validates references, and executes it on Spark.
1011

1112
![Logo](./docs/images/teckel-banner.png)
1213

13-
This concept is further developed on my
14-
blog: [Big Data with Zero Code](https://blog.rafaelfernandez.dev/posts/big-data-with-zero-code/)
14+
Background reading: [Big Data with Zero Code](https://blog.rafaelfernandez.dev/posts/big-data-with-zero-code/).
1515

16-
## Features
17-
18-
- **Declarative ETL Configuration:** Define your ETL processes with simple YAML files.
19-
- **Support for Multiple Data Sources:** Easily integrate inputs in CSV, JSON, and Parquet formats.
20-
- **Flexible Transformations:** Perform joins, aggregations, and selections with clear syntax.
21-
- **Spark Compatibility:** Leverage the power of Apache Spark for large-scale data processing.
16+
## Sibling projects
2217

23-
## ETL Yaml Example
18+
| Project | Role |
19+
|---------|------|
20+
| [eff3ct0/teckel-spec](https://github.com/eff3ct0/teckel-spec) | Language-agnostic specification (current: **v3.0**). Defines syntax, semantics, expression grammar, validation rules, conformance levels, error catalog. |
21+
| **eff3ct0/teckel** *(this repo)* | Scala 2.13 + Apache Spark 3.5.x reference implementation. |
22+
| [eff3ct0/teckel-rs](https://github.com/eff3ct0/teckel-rs) | Rust implementation (parser + model layer for v3.0). |
2423

25-
Here's an example of a fully defined ETL configuration using a YAML file:
24+
## Features
2625

27-
### SQL ETL
26+
- **Declarative YAML pipelines** — sources, transformations, sinks defined as data, not code.
27+
- **45+ built-in transformations** — relational (`select`, `where`, `group`, `order`, `join`,
28+
`union`/`intersect`/`except`, `distinct`, `limit`), column ops (`addColumns`, `dropColumns`,
29+
`renameColumns`, `castColumns`), analytical (`window`, `pivot`, `unpivot`, `flatten`, `sample`,
30+
`rollup`, `cube`, `groupingSets`), warehousing (`scd2`, `merge`, `enrich`, `schemaEnforce`),
31+
control flow (`conditional`, `split`, `sql`), v3.0 additions (`offset`, `tail`, `fillNa`,
32+
`dropNa`, `replace`, `parse`, `asOfJoin`, `lateralJoin`, `transpose`, `describe`, `crosstab`,
33+
`hint`), and pluggable `custom` components.
34+
- **Variable substitution**`${VAR}` and `${VAR:default}` resolved from env vars.
35+
- **Secrets**`{{secrets.<alias>}}` placeholders backed by env vars or pluggable providers.
36+
- **Hooks**`preExecution` / `postExecution` shell commands around the pipeline run.
37+
- **Pipeline config** — backend selection, cache policy, notifications, custom component declarations.
38+
- **Streaming I/O**`streamingInput` / `streamingOutput` for Structured Streaming pipelines.
39+
- **Dry-run & validation** — preview the execution plan and catch broken references before Spark starts.
40+
- **Doc generation** — render any pipeline as Markdown.
41+
- **DAG visualization** — Mermaid, DOT, or ASCII output.
42+
- **Embedded REST server** — expose pipelines as HTTP endpoints with no extra dependencies.
43+
44+
> Naming note: this implementation uses `group` / `order` as the YAML keys (instead of the
45+
> spec-canonical `groupBy` / `orderBy`). All other top-level keys and operation names match the spec.
46+
47+
## Quick example
48+
49+
```yaml
50+
version: "3.0"
2851

29-
- Simple Example: [here](./docs/etl/simple.yaml)
30-
- Complex Example: [here](./docs/etl/complex.yaml)
31-
- Other Example: [here](./docs/etl/example.yaml)
52+
input:
53+
- name: employees
54+
format: csv
55+
path: 'data/employees.csv'
56+
options:
57+
header: true
58+
inferSchema: true
59+
60+
transformation:
61+
- name: active
62+
where:
63+
from: employees
64+
filter: "status = 'active'"
65+
66+
- name: by_dept
67+
group:
68+
from: active
69+
by: [department]
70+
agg:
71+
- "count(*) as headcount"
72+
- "avg(salary) as avg_salary"
3273

33-
### SQL Transformations
74+
output:
75+
- name: by_dept
76+
format: parquet
77+
mode: overwrite
78+
path: 'data/output/by_dept'
79+
```
3480
35-
- `Select` Example: [here](./docs/etl/select.yaml)
36-
- `Where` Example: [here](./docs/etl/where.yaml)
37-
- `Group By` Example: [here](./docs/etl/group-by.yaml)
38-
- `Order By` Example: [here](./docs/etl/order-by.yaml)
39-
- `Join` Example: [here](./docs/etl/join.yaml)
81+
More examples under [`docs/etl/`](./docs/etl/) — `simple.yaml`, `complex.yaml`, `join.yaml`,
82+
`window.yaml`, `merge.yaml`, `quality.yaml`, `secrets.yaml`, `streaming.yaml`, and many more.
4083

4184
## Getting Started
4285

4386
### Prerequisites
4487

45-
- **Apache Spark**: Ensure you have Apache Spark installed and properly configured.
46-
- **YAML files**: Create configuration files specifying your data sources and transformations.
88+
- **JDK 8 or 11** (Spark 3.5.x is not fully supported on Java 17 in all configurations).
89+
- **Apache Spark 3.5.x** at runtime (declared `provided` in the library artifacts).
90+
- **YAML pipeline files**.
4791

48-
#### Deployment on Docker or Kubernetes
92+
#### Spark on Docker / Kubernetes
4993

50-
In case of you don't have Apache Spark installed previously, you can deploy an Apache Spark cluster using the following
51-
docker image [
52-
`eff3ct/spark:latest`](https://hub.docker.com/r/eff3ct/spark) available in
53-
the [eff3ct0/spark-docker](https://github.com/eff3ct0/spark-docker) GitHub repository.
94+
If you don't already run Spark, the
95+
[`eff3ct/spark`](https://hub.docker.com/r/eff3ct/spark) image
96+
([eff3ct0/spark-docker](https://github.com/eff3ct0/spark-docker)) is a ready-to-use cluster.
5497

5598
### Installation
5699

57-
Clone the Teckel repository and integrate it with your existing Spark setup:
100+
Clone and build:
58101

59102
```bash
60-
git clone https://github.com/rafafrdz/teckel.git
103+
git clone https://github.com/eff3ct0/teckel.git
61104
cd teckel
105+
sbt cli/assembly
62106
```
63107

64-
#### Building the Teckel ETL Uber JAR
108+
The CLI uber JAR is `cli/target/scala-2.13/teckel-etl_2.13.jar` (Spark bundled in).
65109

66-
Build the Teckel ETL CLI into an Uber JAR using the following command:
110+
### CLI
67111

68-
```bash
69-
sbt cli/assembly
112+
```
113+
-f, --file <path> run the pipeline at <path>
114+
-c, --console read YAML from stdin
115+
--dry-run print the execution plan, no Spark
116+
--doc emit Markdown documentation for the pipeline
117+
--graph emit the DAG (Mermaid by default)
118+
--server [--port N] start the embedded REST server
70119
```
71120
72-
The resulting JAR, `teckel-etl_2.13.jar`, will be located in the `cli/target/scala-2.13/` directory.
121+
#### Run from a file
73122
74-
### Usage in Apache Spark Ecosystem with the CLI
123+
<details><summary>Demo - Teckel and Apache Spark by Yaml File</summary>
75124
76-
Once the `teckel-etl_2.13.jar`is ready, use it to execute ETL processes on Apache Spark with the following arguments:
125+
[![Teckel and Apache Spark by Yaml File](https://res.cloudinary.com/marcomontalbano/image/upload/v1735905159/video_to_markdown/images/youtube--eJwJIbNAtto-c05b58ac6eb4c4700831b2b3070cd403.jpg)](https://www.youtube.com/watch?v=eJwJIbNAtto "Teckel and Apache Spark by Yaml File")
77126
78-
- `-f` or `--file`: The path to the ETL file.
79-
- `-c` or `--console`: Run the ETL in the console.
127+
</details>
80128
81-
#### Example: Running ETL in Apache Spark using STDIN
129+
```bash
130+
/opt/spark/bin/spark-submit --class com.eff3ct.teckel.app.Main teckel-etl_2.13.jar -f /path/to/pipeline.yaml
131+
```
132+
133+
#### Run from stdin
82134

83135
<details><summary>Demo - Teckel and Apache Spark by STDIN</summary>
84136

85-
[![Teckel and Apache Spark by Yaml File](https://res.cloudinary.com/marcomontalbano/image/upload/v1735905159/video_to_markdown/images/youtube--eJwJIbNAtto-c05b58ac6eb4c4700831b2b3070cd403.jpg)](https://www.youtube.com/watch?v=V9PzMdZ6u2U "Teckel and Apache Spark by STDIN")
137+
[![Teckel and Apache Spark by STDIN](https://res.cloudinary.com/marcomontalbano/image/upload/v1735905159/video_to_markdown/images/youtube--eJwJIbNAtto-c05b58ac6eb4c4700831b2b3070cd403.jpg)](https://www.youtube.com/watch?v=V9PzMdZ6u2U "Teckel and Apache Spark by STDIN")
86138

87139
</details>
88140

89-
To run the ETL in the **console**, you can use the following command:
90-
91141
```bash
92-
cat << EOF | /opt/spark/bin/spark-submit --class com.eff3ct.teckel.app.Main teckel-etl_2.13.jar -c
142+
cat << 'EOF' | /opt/spark/bin/spark-submit --class com.eff3ct.teckel.app.Main teckel-etl_2.13.jar -c
143+
version: "3.0"
93144
input:
94-
- name: table1
145+
- name: t
95146
format: csv
96147
path: '/path/to/data/file.csv'
97148
options:
98149
header: true
99150
sep: '|'
100-
101-
102151
output:
103-
- name: table1
152+
- name: t
104153
format: parquet
105154
mode: overwrite
106155
path: '/path/to/output/'
107156
EOF
108157
```
109158

110-
#### Example: Running ETL in Apache Spark using a file
111-
112-
<details><summary>Demo - Teckel and Apache Spark by Yaml File</summary>
113-
114-
[![Teckel and Apache Spark by Yaml File](https://res.cloudinary.com/marcomontalbano/image/upload/v1735905159/video_to_markdown/images/youtube--eJwJIbNAtto-c05b58ac6eb4c4700831b2b3070cd403.jpg)](https://www.youtube.com/watch?v=eJwJIbNAtto "Teckel and Apache Spark by Yaml File")
115-
116-
</details>
117-
118-
To run the ETL from a **file**, you can use the following command:
119-
120-
```bash
121-
/opt/spark/bin/spark-submit --class com.eff3ct.teckel.app.Main teckel-etl_2.13.jar -f /path/to/etl/file.yaml
122-
```
123-
124159
> [!IMPORTANT]
125160
>
126161
> **Teckel CLI as dependency / Teckel ETL as framework.**
127162
>
128-
> The Teckel CLI is a standalone application that can be used as a dependency in your project. Notice that the uber jar
129-
> name is `teckel-etl` and not `teckel-cli` or `teckel-cli-assembly`. This is because
130-
> we want to distinguish between the Teckel CLI dependency and the ETL framework.
163+
> The Teckel CLI is also usable as a library dependency. The uber JAR is named `teckel-etl`
164+
> (not `teckel-cli`) precisely to distinguish the CLI from the framework when both end up on the
165+
> same classpath.
131166
>
132-
> Check the [Integration with Apache Spark](./docs/integration-apache-spark.md) documentation for more information.
167+
> See [Integration with Apache Spark](./docs/integration-apache-spark.md) for embedding details.
133168
134169
## Integration with Apache Spark
135170

136-
Teckel can be integrated with Apache Spark easily just adding either the Teckel CLI or Teckel Api as a
137-
dependency in your project or using it as a framework in your Apache Spark project.
171+
Teckel integrates with any existing Spark application — either as a CLI invoked via
172+
`spark-submit`, or as a library (`teckel-api`) embedded in your Scala code. Three entry points
173+
are exposed: `etl[F, O]` (polymorphic), `etlIO[O]` (fixes `F = IO`), and `unsafeETL[O]`
174+
(synchronous). See [Integration with Apache Spark](./docs/integration-apache-spark.md).
138175

139-
Check the [Integration with Apache Spark](./docs/integration-apache-spark.md) documentation for more information.
176+
## Documentation
177+
178+
The Docusaurus site under [`teckel-docs/`](./teckel-docs/) contains the full guide:
179+
Getting Started, Transformations, API, CLI, Plugins, and Examples. The canonical language
180+
reference lives in
181+
[teckel-spec v3.0](https://github.com/eff3ct0/teckel-spec/blob/master/spec/v3.0/teckel-spec.md).
140182

141183
## Development and Contribution
142184

143-
Contributions to Teckel are welcome. If you'd like to contribute, please fork the repository and create a pull request
144-
with your changes.
185+
Contributions welcome — fork the repository and open a pull request. For changes affecting the
186+
YAML surface, please cross-check against the
187+
[Teckel Specification](https://github.com/eff3ct0/teckel-spec) before submitting.
145188

146189
## License
147190

148-
Teckel is available under the MIT License. See the [LICENSE](./LICENSE) file for more details.
149-
150-
If you have any questions regarding the license, feel free to contact Rafael Fernandez.
191+
Teckel is available under the MIT License. See the [LICENSE](./LICENSE) file for details.
151192

152-
For any issues or questions, feel free to open an issue on the GitHub repository.
193+
For any issues or questions, open an issue on GitHub or contact Rafael Fernandez.

teckel-docs/docs/examples.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ A collection of complete pipelines illustrating common ETL patterns. Each exampl
77
The simplest possible pipeline: read a CSV and write it as Parquet. No transformations, just format conversion. A good starting point to verify that your environment works.
88

99
```yaml
10+
version: "3.0"
11+
1012
input:
1113
- name: raw_data
1214
format: csv

teckel-docs/docs/getting-started.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ Let's build something concrete: read a CSV file of employees, filter the active
4646
Create `my-pipeline.yaml`. Each transformation references its upstream asset by name, which is how Teckel builds the dependency graph: `employees``active_employees``department_summary``sorted_departments`.
4747

4848
```yaml
49+
version: "3.0"
50+
4951
input:
5052
- name: employees
5153
format: csv
@@ -111,19 +113,46 @@ unsafeETL[Unit](yaml)
111113

112114
## Pipeline Structure
113115

114-
Every Teckel pipeline YAML has three sections. Two are required:
116+
A Teckel pipeline YAML follows the [Teckel Specification v3.0](https://github.com/eff3ct0/teckel-spec/blob/master/spec/v3.0/teckel-spec.md). The minimum is `input` and `output`; the rest are optional and unlock additional capabilities:
115117

116118
```yaml
117-
input: # Data sources (required)
119+
version: "3.0" # Spec version (recommended)
120+
121+
config: # Pipeline-wide config: backend, cache, notifications, components
122+
...
123+
124+
secrets: # Secret aliases referenced as {{secrets.<alias>}}
125+
keys:
126+
...
127+
128+
hooks: # Lifecycle commands run before/after the pipeline
129+
preExecution: [...]
130+
postExecution: [...]
131+
132+
templates: # Reusable configuration fragments
133+
- ...
134+
135+
input: # Batch data sources (required, NonEmptyList)
136+
- ...
137+
138+
streamingInput: # Structured Streaming sources
118139
- ...
119140

120-
transformation: # Transformation DAG (optional)
141+
transformation: # Transformation DAG (optional)
121142
- ...
122143

123-
output: # Data sinks (required)
144+
output: # Batch data sinks (required, NonEmptyList)
145+
- ...
146+
147+
streamingOutput: # Structured Streaming sinks
124148
- ...
125149
```
126150
151+
Variable substitution `${VAR}` and `${VAR:default}` is applied to the raw YAML before parsing
152+
(env vars resolve them); `$$` escapes a literal `$`. Secrets use `{{secrets.<alias>}}` and are
153+
resolved from the configured provider or env vars (`TECKEL_SECRET__<ALIAS>`). See the spec for
154+
the full set of validation rules (`V-001`..`V-008`).
155+
127156
### Input
128157

129158
Each input defines a data source. The `name` field is the reference that downstream transformations use to refer to this dataset:
@@ -170,7 +199,7 @@ transformation:
170199

171200
## Next Steps
172201

173-
- [Transformations](transformations.md) — full reference for all 30+ operations
202+
- [Transformations](transformations.md) — full reference for all 45+ operations
174203
- [Plugins](plugins.md) — custom readers, transformers, and writers
175204
- [CLI](cli.md) — all command-line options
176205
- [API](api.md) — programmatic integration with Scala and Cats Effect

teckel-docs/docs/index.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ Writing ETL pipelines with Spark usually means a lot of boilerplate: read from h
99

1010
**Teckel** is a Scala framework that turns YAML files into complete Apache Spark ETL pipelines. You define *what* you want to do, not *how* to do it. The framework builds the DAG, validates asset references, and executes everything on Spark — you keep the business logic, not the scaffolding.
1111

12-
**[Getting Started](getting-started.md)** · **[Transformations](transformations.md)** · **[API](api.md)** · **[CLI](cli.md)**
12+
This repository is the **Scala / Spark reference implementation** of the
13+
[Teckel Specification](https://github.com/eff3ct0/teckel-spec) (current version: **v3.0**).
14+
A sibling Rust implementation lives at [eff3ct0/teckel-rs](https://github.com/eff3ct0/teckel-rs).
15+
16+
**[Getting Started](getting-started.md)** · **[Transformations](transformations.md)** · **[API](api.md)** · **[CLI](cli.md)** · **[Spec v3.0](https://github.com/eff3ct0/teckel-spec/blob/master/spec/v3.0/teckel-spec.md)**
1317

1418
## What's included
1519

@@ -33,6 +37,8 @@ libraryDependencies += "com.eff3ct" %% "teckel-api" % "@VERSION@"
3337
Write a pipeline:
3438

3539
```yaml
40+
version: "3.0"
41+
3642
input:
3743
- name: users
3844
format: csv

teckel-docs/docs/plugins.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Plugins
22

3-
Teckel's 30+ built-in transformations cover most cases, but there are always pipelines that need something specific: an ML model, a proprietary connector, business logic that doesn't fit in SQL. That's what the plugin system is for.
3+
Teckel's 45+ built-in transformations cover most cases, but there are always pipelines that need something specific: an ML model, a proprietary connector, business logic that doesn't fit in SQL. That's what the plugin system is for.
44

55
The idea is straightforward: define a class that implements one of Teckel's three interfaces, register it with a name, and reference it from YAML as if it were any other transformation. Teckel handles instantiation and passes the DataFrame at the right moment.
66

teckel-docs/docs/transformations.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
Transformations are the core of Teckel. Each one represents an operation over a Spark DataFrame, expressed as a typed YAML block. They chain together by name, forming a DAG: the output of one becomes the input of the next.
44

5+
The canonical names and semantics are defined in
6+
[Teckel Specification v3.0 §8](https://github.com/eff3ct0/teckel-spec/blob/master/spec/v3.0/teckel-spec.md).
7+
This implementation deviates in two key names: it accepts `group` and `order` as the YAML keys
8+
(the spec uses `groupBy` and `orderBy`); all other operations match the spec.
9+
510
Every transformation follows the same pattern: a `name` field that identifies the resulting asset, and an operation block with its specific parameters.
611

712
```yaml

0 commit comments

Comments
 (0)