From c16c374d1c378b3167b9f9fd78e75963dbb7864e Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 13:04:35 +0000 Subject: [PATCH 01/24] first commit --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..33bb4f1 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# repositorio-teste From 0f6d71f1e198e63110b5a5aa18adaa2940309c25 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 13:08:07 +0000 Subject: [PATCH 02/24] first commit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33bb4f1..cd0bd6c 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# repositorio-teste +# repositorio-teste - From 5e326f317e737b76ef7ee91a4a1871668ad62fac Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 13:14:30 +0000 Subject: [PATCH 03/24] Removendo dab_test do GitHub mas mantendo localmente --- dab_test | 1 - 1 file changed, 1 deletion(-) delete mode 160000 dab_test diff --git a/dab_test b/dab_test deleted file mode 160000 index 2850ef9..0000000 --- a/dab_test +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2850ef9a66e0c9446329cbf7c384c69139185c2e From 2191879b0a5d3d9a54b50e308d32d8020eb62cf7 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 13:15:37 +0000 Subject: [PATCH 04/24] first commit --- README.md | 2 +- dab_test/.vscode/__builtins__.pyi | 3 + dab_test/.vscode/extensions.json | 7 + dab_test/.vscode/settings.json | 39 +++++ dab_test/AGENTS.md | 25 +++ dab_test/CLAUDE.md | 6 + dab_test/README.md | 71 +++++++++ dab_test/databricks.yml | 48 ++++++ dab_test/fixtures/.gitkeep | 9 ++ dab_test/pyproject.toml | 31 ++++ dab_test/resources/dab_test_job.yml | 36 +++++ dab_test/src/dab_test/__init__.py | 0 dab_test/src/dab_test/main.py | 14 ++ dab_test/src/dab_test_etl/README.md | 20 +++ .../explorations/sample_exploration.ipynb | 63 ++++++++ .../transformations/sample_trips_dab_test.py | 12 ++ .../transformations/sample_zones_dab_test.py | 17 ++ dab_test/src/notebooks/demo_notebooks.py | 30 ++++ dab_test/src/sample_notebook.ipynb | 149 ++++++++++++++++++ dab_test/tests/job_config_test.py | 51 ++++++ dab_test/tests/main_test.py | 6 + 21 files changed, 638 insertions(+), 1 deletion(-) create mode 100644 dab_test/.vscode/__builtins__.pyi create mode 100644 dab_test/.vscode/extensions.json create mode 100644 dab_test/.vscode/settings.json create mode 100644 dab_test/AGENTS.md create mode 100644 dab_test/CLAUDE.md create mode 100644 dab_test/README.md create mode 100644 dab_test/databricks.yml create mode 100644 dab_test/fixtures/.gitkeep create mode 100644 dab_test/pyproject.toml create mode 100644 dab_test/resources/dab_test_job.yml create mode 100644 dab_test/src/dab_test/__init__.py create mode 100644 dab_test/src/dab_test/main.py create mode 100644 dab_test/src/dab_test_etl/README.md create mode 100644 dab_test/src/dab_test_etl/explorations/sample_exploration.ipynb create mode 100644 dab_test/src/dab_test_etl/transformations/sample_trips_dab_test.py create mode 100644 dab_test/src/dab_test_etl/transformations/sample_zones_dab_test.py create mode 100644 dab_test/src/notebooks/demo_notebooks.py create mode 100644 dab_test/src/sample_notebook.ipynb create mode 100644 dab_test/tests/job_config_test.py create mode 100644 dab_test/tests/main_test.py diff --git a/README.md b/README.md index cd0bd6c..146bb9e 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# repositorio-teste - +# repositorio-teste diff --git a/dab_test/.vscode/__builtins__.pyi b/dab_test/.vscode/__builtins__.pyi new file mode 100644 index 0000000..0edd518 --- /dev/null +++ b/dab_test/.vscode/__builtins__.pyi @@ -0,0 +1,3 @@ +# Typings for Pylance in Visual Studio Code +# see https://github.com/microsoft/pyright/blob/main/docs/builtins.md +from databricks.sdk.runtime import * diff --git a/dab_test/.vscode/extensions.json b/dab_test/.vscode/extensions.json new file mode 100644 index 0000000..b958aac --- /dev/null +++ b/dab_test/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "charliermarsh.ruff", + "databricks.databricks", + "redhat.vscode-yaml" + ] +} diff --git a/dab_test/.vscode/settings.json b/dab_test/.vscode/settings.json new file mode 100644 index 0000000..d73c73b --- /dev/null +++ b/dab_test/.vscode/settings.json @@ -0,0 +1,39 @@ +{ + "jupyter.interactiveWindow.cellMarker.codeRegex": "^# COMMAND ----------|^# Databricks notebook source|^(#\\s*%%|#\\s*\\|#\\s*In\\[\\d*?\\]|#\\s*In\\[ \\])", + "jupyter.interactiveWindow.cellMarker.default": "# COMMAND ----------", + "python.testing.pytestArgs": [ + "." + ], + "files.exclude": { + "**/*.egg-info": true, + "**/__pycache__": true, + ".pytest_cache": true, + "dist": true, + }, + "files.associations": { + "**/.gitkeep": "markdown" + }, + + // Pylance settings (VS Code) + // Set typeCheckingMode to "basic" to enable type checking! + "python.analysis.typeCheckingMode": "off", + "python.analysis.extraPaths": ["src", "lib", "resources"], + "python.analysis.diagnosticMode": "workspace", + "python.analysis.stubPath": ".vscode", + + // Pyright settings (Cursor) + // Set typeCheckingMode to "basic" to enable type checking! + "cursorpyright.analysis.typeCheckingMode": "off", + "cursorpyright.analysis.extraPaths": ["src", "lib", "resources"], + "cursorpyright.analysis.diagnosticMode": "workspace", + "cursorpyright.analysis.stubPath": ".vscode", + + // General Python settings + "python.defaultInterpreterPath": "./.venv/bin/python", + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.formatOnSave": true, + }, +} diff --git a/dab_test/AGENTS.md b/dab_test/AGENTS.md new file mode 100644 index 0000000..fdcca98 --- /dev/null +++ b/dab_test/AGENTS.md @@ -0,0 +1,25 @@ +# Declarative Automation Bundles Project + +This project uses Declarative Automation Bundles (DABs) for deployment. Add project-specific instructions below. + +## For AI Agents: Use Databricks AI Tools + +**BEFORE any other action, read the `databricks-core` skill.** + +It sets you up to work with this project reliably: CLI authentication, profile +selection, data discovery, and the bundle deployment workflow. Without it, +results are often slower and less accurate. + +If this skill is not available (Databricks AI Tools are not installed), you can install them for your coding agent in seconds: + +```bash +databricks aitools install +``` + +If the CLI is not installed, see: https://docs.databricks.com/dev-tools/cli/install + +--- + +## Project Instructions + + diff --git a/dab_test/CLAUDE.md b/dab_test/CLAUDE.md new file mode 100644 index 0000000..5612c9b --- /dev/null +++ b/dab_test/CLAUDE.md @@ -0,0 +1,6 @@ +# CLAUDE.md + +Project guidance for AI agents lives in AGENTS.md. +Claude Code loads it via the import below. + +@AGENTS.md diff --git a/dab_test/README.md b/dab_test/README.md new file mode 100644 index 0000000..bdf9c44 --- /dev/null +++ b/dab_test/README.md @@ -0,0 +1,71 @@ +# dab_test + +The 'dab_test' project was generated by using the default-python template. + +* `src/`: Python source code for this project. + * `src/dab_test/`: Shared Python code that can be used by jobs and pipelines. +* `resources/`: Resource configurations (jobs, pipelines, etc.) +* `tests/`: Unit tests for the shared Python code. +* `fixtures/`: Fixtures for data sets (primarily used for testing). + + +## Getting started + +Choose how you want to work on this project: + +(a) Directly in your Databricks workspace, see + https://docs.databricks.com/dev-tools/bundles/workspace. + +(b) Locally with an IDE like Cursor or VS Code, see + https://docs.databricks.com/dev-tools/vscode-ext.html. + +(c) With command line tools, see https://docs.databricks.com/dev-tools/cli/databricks-cli.html + +If you're developing with an IDE, dependencies for this project should be installed using uv: + +* Make sure you have the UV package manager installed. + It's an alternative to tools like pip: https://docs.astral.sh/uv/getting-started/installation/. +* Run `uv sync --dev` to install the project's dependencies. + + +# Using this project using the CLI + +The Databricks workspace and IDE extensions provide a graphical interface for working +with this project. It's also possible to interact with it directly using the CLI: + +1. Authenticate to your Databricks workspace, if you have not done so already: + ``` + $ databricks configure + ``` + +2. To deploy a development copy of this project, type: + ``` + $ databricks bundle deploy --target dev + ``` + (Note that "dev" is the default target, so the `--target` parameter + is optional here.) + + This deploys everything that's defined for this project. + For example, the default template would deploy a pipeline called + `[dev yourname] dab_test_etl` to your workspace. + You can find that resource by opening your workpace and clicking on **Jobs & Pipelines**. + +3. Similarly, to deploy a production copy, type: + ``` + $ databricks bundle deploy --target prod + ``` + Note the default template has a includes a job that runs the pipeline every day + (defined in resources/sample_job.job.yml). The schedule + is paused when deploying in development mode (see + https://docs.databricks.com/dev-tools/bundles/deployment-modes.html). + +4. To run a job or pipeline, use the "run" command: + ``` + $ databricks bundle run + ``` + +5. Finally, to run tests locally, use `pytest`: + ``` + $ uv run pytest + ``` +video 01 diff --git a/dab_test/databricks.yml b/dab_test/databricks.yml new file mode 100644 index 0000000..ad36d9e --- /dev/null +++ b/dab_test/databricks.yml @@ -0,0 +1,48 @@ +# This is a Declarative Automation Bundle definition for dab_test. +# See https://docs.databricks.com/dev-tools/bundles/index.html for documentation. +bundle: + name: dab_test + uuid: 348afa13-f395-4806-9fcf-f5fc52935756 + +include: + - resources/jobs/*.yml + - resources/pipelines/*.yml + - resources/schemas/*.yml + +artifacts: + python_artifact: + type: whl + build: uv build --wheel + +# Variable declarations. These variables are assigned in the dev/prod targets below. +variables: + catalog: + description: The catalog to use + schema: + description: The schema to use + +targets: + dev: + # The default target uses 'mode: development' to create a development copy. + # - Deployed resources get prefixed with '[dev my_user_name]' + # - Any job schedules and triggers are paused by default. + # See also https://docs.databricks.com/dev-tools/bundles/deployment-modes.html. + mode: development + default: true + workspace: + host: https://dbc-39ee3252-8e93.cloud.databricks.com + variables: + catalog: dev + schema: dev + prod: + mode: production + workspace: + host: https://dbc-39ee3252-8e93.cloud.databricks.com + # We explicitly deploy to /Workspace/Users/rubencruzh@gmail.com to make sure we only have a single copy. + root_path: /Workspace/Users/rubencruzh@gmail.com/.bundle/${bundle.name}/${bundle.target} + variables: + catalog: dev + schema: prod + permissions: + - user_name: rubencruzh@gmail.com + level: CAN_MANAGE diff --git a/dab_test/fixtures/.gitkeep b/dab_test/fixtures/.gitkeep new file mode 100644 index 0000000..77a9066 --- /dev/null +++ b/dab_test/fixtures/.gitkeep @@ -0,0 +1,9 @@ +# Test fixtures directory + +Add JSON or CSV files here. In tests, use them with `load_fixture()`: + +``` +def test_using_fixture(load_fixture): + data = load_fixture("my_data.json") + assert len(data) >= 1 +``` diff --git a/dab_test/pyproject.toml b/dab_test/pyproject.toml new file mode 100644 index 0000000..8fee306 --- /dev/null +++ b/dab_test/pyproject.toml @@ -0,0 +1,31 @@ +[project] +name = "dab_test" +version = "0.0.1" +authors = [{ name = "rubencruzh@gmail.com" }] +requires-python = ">=3.10,<3.13" +dependencies = [ + # Any dependencies for jobs and pipelines in this project can be added here + # See also https://docs.databricks.com/dev-tools/bundles/library-dependencies + # + # LIMITATION: for pipelines, dependencies are cached during development; + # add dependencies to the 'environment' section of your pipeline.yml file instead +] + +[dependency-groups] +dev = [ + "pytest", + "ruff", + "databricks-dlt", + "databricks-connect>=15.4,<15.5", + "ipykernel", +] + +[project.scripts] +main = "dab_test.main:main" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.ruff] +line-length = 120 diff --git a/dab_test/resources/dab_test_job.yml b/dab_test/resources/dab_test_job.yml new file mode 100644 index 0000000..209811c --- /dev/null +++ b/dab_test/resources/dab_test_job.yml @@ -0,0 +1,36 @@ +# Job do treinamento para executar o notebook demo. +resources: + jobs: + dab_test_job: + name: dab_test_job + tags: + treinamento: dab + ambiente: dev + area: engenharia + description: Job de treinamento que ingere usuários de exemplo com parâmetros dinâmicos. + parameters: + - name: catalog_name + default: ${var.catalog_name} + - name: user_id + default: "3" + - name: user_name + default: "Anselmo" + email_notifications: + on_failure: + - anselmoborges@gmail.com + timeout_seconds: 900 + schedule: + quartz_cron_expression: "0 0 8 ? * TUE *" + timezone_id: America/Sao_Paulo + tasks: + - task_key: ingestao_usuarios + description: Ingestão de usuários de exemplo via notebook. + notebook_task: + notebook_path: ../../src/notebooks/demo_notebook.py + base_parameters: + catalog_name: "{{job.parameters.catalog_name}}" + user_id: "{{job.parameters.user_id}}" + user_name: "{{job.parameters.user_name}}" + queue: + enabled: true + performance_target: ${var.performance_target} \ No newline at end of file diff --git a/dab_test/src/dab_test/__init__.py b/dab_test/src/dab_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dab_test/src/dab_test/main.py b/dab_test/src/dab_test/main.py new file mode 100644 index 0000000..aadae50 --- /dev/null +++ b/dab_test/src/dab_test/main.py @@ -0,0 +1,14 @@ +from databricks.sdk.runtime import spark +from pyspark.sql import DataFrame + + +def find_all_taxis() -> DataFrame: + return spark.read.table("samples.nyctaxi.trips") + + +def main(): + find_all_taxis().show(5) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dab_test/src/dab_test_etl/README.md b/dab_test/src/dab_test_etl/README.md new file mode 100644 index 0000000..30f31d3 --- /dev/null +++ b/dab_test/src/dab_test_etl/README.md @@ -0,0 +1,20 @@ +# dab_test + +This folder defines all source code for the dab_test pipeline: + +- `explorations/`: Ad-hoc notebooks used to explore the data processed by this pipeline. +- `transformations/`: All dataset definitions and transformations. +- `utilities/` (optional): Utility functions and Python modules used in this pipeline. +- `data_sources/` (optional): View definitions describing the source data for this pipeline. + +## Getting Started + +To get started, go to the `transformations` folder -- most of the relevant source code lives there: + +* By convention, every dataset under `transformations` is in a separate file. +* Take a look at the sample called "sample_trips_dab_test.py" to get familiar with the syntax. + Read more about the syntax at https://docs.databricks.com/dlt/python-ref.html. +* If you're using the workspace UI, use `Run file` to run and preview a single transformation. +* If you're using the CLI, use `databricks bundle run dab_test_etl --refresh sample_trips_dab_test` to run a single transformation. + +For more tutorials and reference material, see https://docs.databricks.com/dlt. diff --git a/dab_test/src/dab_test_etl/explorations/sample_exploration.ipynb b/dab_test/src/dab_test_etl/explorations/sample_exploration.ipynb new file mode 100644 index 0000000..6b3bc19 --- /dev/null +++ b/dab_test/src/dab_test_etl/explorations/sample_exploration.ipynb @@ -0,0 +1,63 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": {}, + "inputWidgets": {}, + "nuid": "19a992e9-55e0-49e4-abc7-8c92c420dd5b", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "source": [ + "### Example Exploratory Notebook\n", + "\n", + "Use this notebook to explore the data generated by the pipeline in your preferred programming language.\n", + "\n", + "**Note**: This notebook is not executed as part of the pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": {}, + "inputWidgets": {}, + "nuid": "1b0a82fa-3c6a-4f29-bb43-ded1c4fd77c6", + "showTitle": false, + "tableResultSettingsMap": {}, + "title": "" + } + }, + "outputs": [], + "source": [ + "# !!! Before performing any data analysis, make sure to run the pipeline to materialize the sample datasets. The tables referenced in this notebook depend on that step.\n", + "\n", + "display(spark.sql(\"SELECT * FROM dev.rubencruzh.sample_trips_dab_test\"))" + ] + } + ], + "metadata": { + "application/vnd.databricks.v1+notebook": { + "computePreferences": null, + "dashboards": [], + "environmentMetadata": null, + "inputWidgetPreferences": null, + "language": "python", + "notebookMetadata": { + "pythonIndentUnit": 2 + }, + "notebookName": "sample_exploration", + "widgets": {} + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/dab_test/src/dab_test_etl/transformations/sample_trips_dab_test.py b/dab_test/src/dab_test_etl/transformations/sample_trips_dab_test.py new file mode 100644 index 0000000..c739b02 --- /dev/null +++ b/dab_test/src/dab_test_etl/transformations/sample_trips_dab_test.py @@ -0,0 +1,12 @@ +from pyspark import pipelines as dp +from pyspark.sql.functions import col + + +# This file defines a sample transformation. +# Edit the sample below or add new transformations +# using "+ Add" in the file browser. + + +@dp.table +def sample_trips_dab_test(): + return spark.read.table("samples.nyctaxi.trips") diff --git a/dab_test/src/dab_test_etl/transformations/sample_zones_dab_test.py b/dab_test/src/dab_test_etl/transformations/sample_zones_dab_test.py new file mode 100644 index 0000000..a8c936e --- /dev/null +++ b/dab_test/src/dab_test_etl/transformations/sample_zones_dab_test.py @@ -0,0 +1,17 @@ +from pyspark import pipelines as dp +from pyspark.sql.functions import col, sum + + +# This file defines a sample transformation. +# Edit the sample below or add new transformations +# using "+ Add" in the file browser. + + +@dp.table +def sample_zones_dab_test(): + # Read from the "sample_trips" table, then sum all the fares + return ( + spark.read.table(f"sample_trips_dab_test") + .groupBy(col("pickup_zip")) + .agg(sum("fare_amount").alias("total_fare")) + ) diff --git a/dab_test/src/notebooks/demo_notebooks.py b/dab_test/src/notebooks/demo_notebooks.py new file mode 100644 index 0000000..a56304d --- /dev/null +++ b/dab_test/src/notebooks/demo_notebooks.py @@ -0,0 +1,30 @@ +# Databricks notebook source +# DBTITLE 1,Configura widgets de entrada +dbutils.widgets.text("catalog", "dev", "Catalog Name") +dbutils.widgets.text("user_id", "3", "User ID") +dbutils.widgets.text("user_name", "Anselmo", "User Name") + +# COMMAND ---------- +# DBTITLE 1,Obtém valores informados +catalog_name = dbutils.widgets.get("catalog") +user_id = int(dbutils.widgets.get("user_id")) +user_name = dbutils.widgets.get("user_name") +print(f"Using Catalog: {catalog_name}") +print(f"Inserting user_id={user_id}, user_name={user_name}") + +# COMMAND ---------- +# DBTITLE 1,Cria tabela e insere dados de exemplo + parâmetro +spark.sql( + f"CREATE TABLE IF NOT EXISTS {catalog_name}.rescue_b.users (id INT, name STRING)" +) +spark.sql( + f"INSERT OVERWRITE {catalog_name}.rescue_b.users VALUES (1, 'Alice'), (2, 'Bob')" +) +spark.sql( + f"INSERT INTO {catalog_name}.rescue_b.users VALUES ({user_id}, '{user_name}')" +) + +# COMMAND ---------- +# DBTITLE 1,Lê os dados inseridos +result_df = spark.sql(f"SELECT * FROM {catalog_name}.rescue_b.users ORDER BY id") +display(result_df) \ No newline at end of file diff --git a/dab_test/src/sample_notebook.ipynb b/dab_test/src/sample_notebook.ipynb new file mode 100644 index 0000000..1f32293 --- /dev/null +++ b/dab_test/src/sample_notebook.ipynb @@ -0,0 +1,149 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": {}, + "inputWidgets": {}, + "nuid": "ee353e42-ff58-4955-9608-12865bd0950e", + "showTitle": false, + "title": "" + } + }, + "source": [ + "# Default notebook\n", + "\n", + "This default notebook is executed using a Lakeflow job as defined in resources/sample_job.job.yml." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": {}, + "inputWidgets": {}, + "nuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "showTitle": false, + "title": "" + } + }, + "outputs": [], + "source": [ + "# Set default catalog and schema\n", + "catalog = dbutils.widgets.get(\"catalog\")\n", + "schema = dbutils.widgets.get(\"schema\")\n", + "spark.sql(f\"USE CATALOG {catalog}\")\n", + "spark.sql(f\"USE SCHEMA {schema}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "application/vnd.databricks.v1+cell": { + "cellMetadata": { + "byteLimit": 2048000, + "rowLimit": 10000 + }, + "inputWidgets": {}, + "nuid": "6bca260b-13d1-448f-8082-30b60a85c9ae", + "showTitle": false, + "title": "" + } + }, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "sys.path.append(\"../src\")\n", + "from dab_test import taxis\n", + "\n", + "taxis.find_all_taxis().show(10)" + ] + } + ], + "metadata": { + "application/vnd.databricks.v1+notebook": { + "dashboards": [], + "environmentMetadata": { + "base_environment": "", + "dependencies": [ + "--editable .." + ], + "environment_version": "4" + }, + "language": "python", + "notebookMetadata": { + "pythonIndentUnit": 2 + }, + "notebookName": "notebook", + "widgets": { + "catalog": { + "currentValue": "dev", + "nuid": "c4t4l0g-w1dg-3t12-3456-789012345678", + "typedWidgetInfo": { + "autoCreated": false, + "defaultValue": "dev", + "label": "Catalog", + "name": "catalog", + "options": { + "validationRegex": null, + "widgetDisplayType": "Text" + }, + "parameterDataType": "String" + }, + "widgetInfo": { + "defaultValue": "dev", + "label": "Catalog", + "name": "catalog", + "options": { + "autoCreated": false, + "validationRegex": null, + "widgetType": "text" + }, + "widgetType": "text" + } + }, + "schema": { + "currentValue": "rubencruzh", + "nuid": "5ch3m4-w1dg-3t98-7654-321098765432", + "typedWidgetInfo": { + "autoCreated": false, + "defaultValue": "default", + "label": "Schema", + "name": "schema", + "options": { + "validationRegex": null, + "widgetDisplayType": "Text" + }, + "parameterDataType": "String" + }, + "widgetInfo": { + "defaultValue": "default", + "label": "Schema", + "name": "schema", + "options": { + "autoCreated": false, + "validationRegex": null, + "widgetType": "text" + }, + "widgetType": "text" + } + } + } + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/dab_test/tests/job_config_test.py b/dab_test/tests/job_config_test.py new file mode 100644 index 0000000..921bf3a --- /dev/null +++ b/dab_test/tests/job_config_test.py @@ -0,0 +1,51 @@ +from pathlib import Path +import yaml + +ROOT = Path(__file__).resolve().parents[1] +JOB_FILE = ROOT / "resources" / "jobs" / "dab_treinamento.job.yml" + + +def _load_job(): + data = yaml.safe_load(JOB_FILE.read_text()) + job = data["resources"]["jobs"]["dab_treinamento_job"] + return job + + +def test_job_has_description_and_tags(): + job = _load_job() + print("Validando descrição do job...") + assert job.get("description"), "Job description must be set" + print("Descrição OK!") + tags = job.get("tags", {}) + print(f"Validando tags obrigatórias: {list(tags.keys())}") + for tag_key in ("treinamento", "ambiente", "area"): + assert tag_key in tags, f"Tag '{tag_key}' está ausente" + print("Tags obrigatórias OK!") + + +def test_job_has_schedule_and_timeout(): + job = _load_job() + print("Validando agendamento e timeout...") + schedule = job.get("schedule") + assert schedule, "Job schedule deve estar configurado" + print(f"Agendamento encontrado: {schedule['quartz_cron_expression']} ({schedule['timezone_id']})") + assert schedule["quartz_cron_expression"].lower() == "0 0 8 ? * tue *" + assert schedule["timezone_id"] == "America/Sao_Paulo" + assert job.get("timeout_seconds") == 900 + print("Agendamento e timeout OK!") + + +def test_job_parameters_exposed(): + job = _load_job() + params = {p["name"]: p["default"] for p in job.get("parameters", [])} + print(f"Validando parâmetros expostos: {list(params.keys())}") + for expected in ("catalog_name", "user_id", "user_name"): + assert expected in params, f"Parâmetro '{expected}' não configurado" + print("Parâmetros obrigatórios OK!") + + +def test_job_uses_variable_for_performance_target(): + job = _load_job() + print("Validando uso da variável performance_target...") + assert job.get("performance_target") == "${var.performance_target}" + print("performance_target OK!") \ No newline at end of file diff --git a/dab_test/tests/main_test.py b/dab_test/tests/main_test.py new file mode 100644 index 0000000..240f1d9 --- /dev/null +++ b/dab_test/tests/main_test.py @@ -0,0 +1,6 @@ +from dab_test import main + + +def test_find_all_taxis(): + taxis = main.find_all_taxis() + assert taxis.count() > 5 \ No newline at end of file From e73a4fda1178819e8b2eb1dd6a64d32b39d1273e Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 13:50:15 +0000 Subject: [PATCH 05/24] update resource --- dab_test/src/dab_test/__init__.py | 0 dab_test/src/dab_test/main.py | 14 ----- dab_test/src/dab_test_etl/README.md | 20 ------ .../explorations/sample_exploration.ipynb | 63 ------------------- .../transformations/sample_trips_dab_test.py | 12 ---- .../transformations/sample_zones_dab_test.py | 17 ----- 6 files changed, 126 deletions(-) delete mode 100644 dab_test/src/dab_test/__init__.py delete mode 100644 dab_test/src/dab_test/main.py delete mode 100644 dab_test/src/dab_test_etl/README.md delete mode 100644 dab_test/src/dab_test_etl/explorations/sample_exploration.ipynb delete mode 100644 dab_test/src/dab_test_etl/transformations/sample_trips_dab_test.py delete mode 100644 dab_test/src/dab_test_etl/transformations/sample_zones_dab_test.py diff --git a/dab_test/src/dab_test/__init__.py b/dab_test/src/dab_test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dab_test/src/dab_test/main.py b/dab_test/src/dab_test/main.py deleted file mode 100644 index aadae50..0000000 --- a/dab_test/src/dab_test/main.py +++ /dev/null @@ -1,14 +0,0 @@ -from databricks.sdk.runtime import spark -from pyspark.sql import DataFrame - - -def find_all_taxis() -> DataFrame: - return spark.read.table("samples.nyctaxi.trips") - - -def main(): - find_all_taxis().show(5) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/dab_test/src/dab_test_etl/README.md b/dab_test/src/dab_test_etl/README.md deleted file mode 100644 index 30f31d3..0000000 --- a/dab_test/src/dab_test_etl/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# dab_test - -This folder defines all source code for the dab_test pipeline: - -- `explorations/`: Ad-hoc notebooks used to explore the data processed by this pipeline. -- `transformations/`: All dataset definitions and transformations. -- `utilities/` (optional): Utility functions and Python modules used in this pipeline. -- `data_sources/` (optional): View definitions describing the source data for this pipeline. - -## Getting Started - -To get started, go to the `transformations` folder -- most of the relevant source code lives there: - -* By convention, every dataset under `transformations` is in a separate file. -* Take a look at the sample called "sample_trips_dab_test.py" to get familiar with the syntax. - Read more about the syntax at https://docs.databricks.com/dlt/python-ref.html. -* If you're using the workspace UI, use `Run file` to run and preview a single transformation. -* If you're using the CLI, use `databricks bundle run dab_test_etl --refresh sample_trips_dab_test` to run a single transformation. - -For more tutorials and reference material, see https://docs.databricks.com/dlt. diff --git a/dab_test/src/dab_test_etl/explorations/sample_exploration.ipynb b/dab_test/src/dab_test_etl/explorations/sample_exploration.ipynb deleted file mode 100644 index 6b3bc19..0000000 --- a/dab_test/src/dab_test_etl/explorations/sample_exploration.ipynb +++ /dev/null @@ -1,63 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": {}, - "inputWidgets": {}, - "nuid": "19a992e9-55e0-49e4-abc7-8c92c420dd5b", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "source": [ - "### Example Exploratory Notebook\n", - "\n", - "Use this notebook to explore the data generated by the pipeline in your preferred programming language.\n", - "\n", - "**Note**: This notebook is not executed as part of the pipeline." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": {}, - "inputWidgets": {}, - "nuid": "1b0a82fa-3c6a-4f29-bb43-ded1c4fd77c6", - "showTitle": false, - "tableResultSettingsMap": {}, - "title": "" - } - }, - "outputs": [], - "source": [ - "# !!! Before performing any data analysis, make sure to run the pipeline to materialize the sample datasets. The tables referenced in this notebook depend on that step.\n", - "\n", - "display(spark.sql(\"SELECT * FROM dev.rubencruzh.sample_trips_dab_test\"))" - ] - } - ], - "metadata": { - "application/vnd.databricks.v1+notebook": { - "computePreferences": null, - "dashboards": [], - "environmentMetadata": null, - "inputWidgetPreferences": null, - "language": "python", - "notebookMetadata": { - "pythonIndentUnit": 2 - }, - "notebookName": "sample_exploration", - "widgets": {} - }, - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/dab_test/src/dab_test_etl/transformations/sample_trips_dab_test.py b/dab_test/src/dab_test_etl/transformations/sample_trips_dab_test.py deleted file mode 100644 index c739b02..0000000 --- a/dab_test/src/dab_test_etl/transformations/sample_trips_dab_test.py +++ /dev/null @@ -1,12 +0,0 @@ -from pyspark import pipelines as dp -from pyspark.sql.functions import col - - -# This file defines a sample transformation. -# Edit the sample below or add new transformations -# using "+ Add" in the file browser. - - -@dp.table -def sample_trips_dab_test(): - return spark.read.table("samples.nyctaxi.trips") diff --git a/dab_test/src/dab_test_etl/transformations/sample_zones_dab_test.py b/dab_test/src/dab_test_etl/transformations/sample_zones_dab_test.py deleted file mode 100644 index a8c936e..0000000 --- a/dab_test/src/dab_test_etl/transformations/sample_zones_dab_test.py +++ /dev/null @@ -1,17 +0,0 @@ -from pyspark import pipelines as dp -from pyspark.sql.functions import col, sum - - -# This file defines a sample transformation. -# Edit the sample below or add new transformations -# using "+ Add" in the file browser. - - -@dp.table -def sample_zones_dab_test(): - # Read from the "sample_trips" table, then sum all the fares - return ( - spark.read.table(f"sample_trips_dab_test") - .groupBy(col("pickup_zip")) - .agg(sum("fare_amount").alias("total_fare")) - ) From d218c40ee1f9f02ce04d87ec47bb6a9d1ca7bb71 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 13:52:59 +0000 Subject: [PATCH 06/24] update resource --- dab_test/resources/dab_test_job.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dab_test/resources/dab_test_job.yml b/dab_test/resources/dab_test_job.yml index 209811c..4b3191f 100644 --- a/dab_test/resources/dab_test_job.yml +++ b/dab_test/resources/dab_test_job.yml @@ -17,7 +17,7 @@ resources: default: "Anselmo" email_notifications: on_failure: - - anselmoborges@gmail.com + - rubencruzh@gmail.com timeout_seconds: 900 schedule: quartz_cron_expression: "0 0 8 ? * TUE *" From 1ceacfd645870503d5b45c9cc514307fdab95a65 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 14:05:44 +0000 Subject: [PATCH 07/24] update notebooks --- dab_test/src/sample_notebook.ipynb | 149 ----------------------------- 1 file changed, 149 deletions(-) delete mode 100644 dab_test/src/sample_notebook.ipynb diff --git a/dab_test/src/sample_notebook.ipynb b/dab_test/src/sample_notebook.ipynb deleted file mode 100644 index 1f32293..0000000 --- a/dab_test/src/sample_notebook.ipynb +++ /dev/null @@ -1,149 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": {}, - "inputWidgets": {}, - "nuid": "ee353e42-ff58-4955-9608-12865bd0950e", - "showTitle": false, - "title": "" - } - }, - "source": [ - "# Default notebook\n", - "\n", - "This default notebook is executed using a Lakeflow job as defined in resources/sample_job.job.yml." - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": {}, - "inputWidgets": {}, - "nuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", - "showTitle": false, - "title": "" - } - }, - "outputs": [], - "source": [ - "# Set default catalog and schema\n", - "catalog = dbutils.widgets.get(\"catalog\")\n", - "schema = dbutils.widgets.get(\"schema\")\n", - "spark.sql(f\"USE CATALOG {catalog}\")\n", - "spark.sql(f\"USE SCHEMA {schema}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 0, - "metadata": { - "application/vnd.databricks.v1+cell": { - "cellMetadata": { - "byteLimit": 2048000, - "rowLimit": 10000 - }, - "inputWidgets": {}, - "nuid": "6bca260b-13d1-448f-8082-30b60a85c9ae", - "showTitle": false, - "title": "" - } - }, - "outputs": [], - "source": [ - "import sys\n", - "\n", - "sys.path.append(\"../src\")\n", - "from dab_test import taxis\n", - "\n", - "taxis.find_all_taxis().show(10)" - ] - } - ], - "metadata": { - "application/vnd.databricks.v1+notebook": { - "dashboards": [], - "environmentMetadata": { - "base_environment": "", - "dependencies": [ - "--editable .." - ], - "environment_version": "4" - }, - "language": "python", - "notebookMetadata": { - "pythonIndentUnit": 2 - }, - "notebookName": "notebook", - "widgets": { - "catalog": { - "currentValue": "dev", - "nuid": "c4t4l0g-w1dg-3t12-3456-789012345678", - "typedWidgetInfo": { - "autoCreated": false, - "defaultValue": "dev", - "label": "Catalog", - "name": "catalog", - "options": { - "validationRegex": null, - "widgetDisplayType": "Text" - }, - "parameterDataType": "String" - }, - "widgetInfo": { - "defaultValue": "dev", - "label": "Catalog", - "name": "catalog", - "options": { - "autoCreated": false, - "validationRegex": null, - "widgetType": "text" - }, - "widgetType": "text" - } - }, - "schema": { - "currentValue": "rubencruzh", - "nuid": "5ch3m4-w1dg-3t98-7654-321098765432", - "typedWidgetInfo": { - "autoCreated": false, - "defaultValue": "default", - "label": "Schema", - "name": "schema", - "options": { - "validationRegex": null, - "widgetDisplayType": "Text" - }, - "parameterDataType": "String" - }, - "widgetInfo": { - "defaultValue": "default", - "label": "Schema", - "name": "schema", - "options": { - "autoCreated": false, - "validationRegex": null, - "widgetType": "text" - }, - "widgetType": "text" - } - } - } - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.12" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} From bae918b754e8950d1d71a44256b0d5316f57e985 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 14:17:18 +0000 Subject: [PATCH 08/24] update readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 146bb9e..9d6bd3c 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# repositorio-teste +# repositorio-teste video - From a8746b8652c259552b607efe096d86e619909d33 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 14:51:28 +0000 Subject: [PATCH 09/24] update main --- dab_test/pyproject.toml | 4 +++ dab_test/src/dab_test/__init__.py | 0 dab_test/src/dab_test/main.py | 57 +++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 dab_test/src/dab_test/__init__.py create mode 100644 dab_test/src/dab_test/main.py diff --git a/dab_test/pyproject.toml b/dab_test/pyproject.toml index 8fee306..1cbaedc 100644 --- a/dab_test/pyproject.toml +++ b/dab_test/pyproject.toml @@ -15,6 +15,7 @@ dependencies = [ dev = [ "pytest", "ruff", + "pyyaml", "databricks-dlt", "databricks-connect>=15.4,<15.5", "ipykernel", @@ -27,5 +28,8 @@ main = "dab_test.main:main" requires = ["hatchling"] build-backend = "hatchling.build" +[tool.hatch.build.targets.wheel] +packages = ["dab_test"] + [tool.ruff] line-length = 120 diff --git a/dab_test/src/dab_test/__init__.py b/dab_test/src/dab_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dab_test/src/dab_test/main.py b/dab_test/src/dab_test/main.py new file mode 100644 index 0000000..cdcd27a --- /dev/null +++ b/dab_test/src/dab_test/main.py @@ -0,0 +1,57 @@ +"""This file configures pytest.""" + +import os, sys, pathlib +from contextlib import contextmanager + + +try: + from databricks.connect import DatabricksSession + from databricks.sdk import WorkspaceClient + from pyspark.sql import SparkSession + import pytest +except ImportError: + raise ImportError("Test dependencies not found.\n\nRun tests using 'uv run pytest'. See http://docs.astral.sh/uv to learn more about uv.") + + +def enable_fallback_compute(): + """Enable serverless compute if no compute is specified.""" + conf = WorkspaceClient().config + if conf.serverless_compute_id or conf.cluster_id or os.environ.get("SPARK_REMOTE"): + return + + url = "https://docs.databricks.com/dev-tools/databricks-connect/cluster-config" + print("☁️ no compute specified, falling back to serverless compute", file=sys.stderr) + print(f" see {url} for manual configuration", file=sys.stderr) + + os.environ["DATABRICKS_SERVERLESS_COMPUTE_ID"] = "auto" + + +@contextmanager +def allow_stderr_output(config: pytest.Config): + """Temporarily disable pytest output capture.""" + capman = config.pluginmanager.get_plugin("capturemanager") + if capman: + with capman.global_and_fixture_disabled(): + yield + else: + yield + + +def pytest_configure(config: pytest.Config): + """Configure pytest session.""" + with allow_stderr_output(config): + enable_fallback_compute() + + # Initialize Spark session eagerly, so it is available even when + # SparkSession.builder.getOrCreate() is used. For DB Connect 15+, + # we validate version compatibility with the remote cluster. + if hasattr(DatabricksSession.builder, "validateSession"): + DatabricksSession.builder.validateSession().getOrCreate() + else: + DatabricksSession.builder.getOrCreate() + + +@pytest.fixture(scope="session") +def spark() -> SparkSession: + """Provide a SparkSession fixture for tests.""" + return DatabricksSession.builder.getOrCreate() \ No newline at end of file From e00d32dc434c24e7a3f525d23601cb27d61b9563 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 15:53:19 +0000 Subject: [PATCH 10/24] =?UTF-8?q?feat:=20salvando=20altera=C3=A7=C3=B5es?= =?UTF-8?q?=20antes=20de=20sincronizar=20com=20dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dab_test/pyproject.toml | 3 +- dab_test/resources/{ => job}/dab_test_job.yml | 0 .../dab_test/{__init__.py => ____init__.py} | 0 dab_test/src/dab_test/main.py | 59 +++---------------- dab_test/tests/job_config_test.py | 2 +- 5 files changed, 11 insertions(+), 53 deletions(-) rename dab_test/resources/{ => job}/dab_test_job.yml (100%) rename dab_test/src/dab_test/{__init__.py => ____init__.py} (100%) diff --git a/dab_test/pyproject.toml b/dab_test/pyproject.toml index 1cbaedc..24abc54 100644 --- a/dab_test/pyproject.toml +++ b/dab_test/pyproject.toml @@ -28,8 +28,9 @@ main = "dab_test.main:main" requires = ["hatchling"] build-backend = "hatchling.build" + [tool.hatch.build.targets.wheel] -packages = ["dab_test"] +packages = ["src/dab_test"] [tool.ruff] line-length = 120 diff --git a/dab_test/resources/dab_test_job.yml b/dab_test/resources/job/dab_test_job.yml similarity index 100% rename from dab_test/resources/dab_test_job.yml rename to dab_test/resources/job/dab_test_job.yml diff --git a/dab_test/src/dab_test/__init__.py b/dab_test/src/dab_test/____init__.py similarity index 100% rename from dab_test/src/dab_test/__init__.py rename to dab_test/src/dab_test/____init__.py diff --git a/dab_test/src/dab_test/main.py b/dab_test/src/dab_test/main.py index cdcd27a..aadae50 100644 --- a/dab_test/src/dab_test/main.py +++ b/dab_test/src/dab_test/main.py @@ -1,57 +1,14 @@ -"""This file configures pytest.""" +from databricks.sdk.runtime import spark +from pyspark.sql import DataFrame -import os, sys, pathlib -from contextlib import contextmanager +def find_all_taxis() -> DataFrame: + return spark.read.table("samples.nyctaxi.trips") -try: - from databricks.connect import DatabricksSession - from databricks.sdk import WorkspaceClient - from pyspark.sql import SparkSession - import pytest -except ImportError: - raise ImportError("Test dependencies not found.\n\nRun tests using 'uv run pytest'. See http://docs.astral.sh/uv to learn more about uv.") +def main(): + find_all_taxis().show(5) -def enable_fallback_compute(): - """Enable serverless compute if no compute is specified.""" - conf = WorkspaceClient().config - if conf.serverless_compute_id or conf.cluster_id or os.environ.get("SPARK_REMOTE"): - return - url = "https://docs.databricks.com/dev-tools/databricks-connect/cluster-config" - print("☁️ no compute specified, falling back to serverless compute", file=sys.stderr) - print(f" see {url} for manual configuration", file=sys.stderr) - - os.environ["DATABRICKS_SERVERLESS_COMPUTE_ID"] = "auto" - - -@contextmanager -def allow_stderr_output(config: pytest.Config): - """Temporarily disable pytest output capture.""" - capman = config.pluginmanager.get_plugin("capturemanager") - if capman: - with capman.global_and_fixture_disabled(): - yield - else: - yield - - -def pytest_configure(config: pytest.Config): - """Configure pytest session.""" - with allow_stderr_output(config): - enable_fallback_compute() - - # Initialize Spark session eagerly, so it is available even when - # SparkSession.builder.getOrCreate() is used. For DB Connect 15+, - # we validate version compatibility with the remote cluster. - if hasattr(DatabricksSession.builder, "validateSession"): - DatabricksSession.builder.validateSession().getOrCreate() - else: - DatabricksSession.builder.getOrCreate() - - -@pytest.fixture(scope="session") -def spark() -> SparkSession: - """Provide a SparkSession fixture for tests.""" - return DatabricksSession.builder.getOrCreate() \ No newline at end of file +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/dab_test/tests/job_config_test.py b/dab_test/tests/job_config_test.py index 921bf3a..9e30f35 100644 --- a/dab_test/tests/job_config_test.py +++ b/dab_test/tests/job_config_test.py @@ -2,7 +2,7 @@ import yaml ROOT = Path(__file__).resolve().parents[1] -JOB_FILE = ROOT / "resources" / "jobs" / "dab_treinamento.job.yml" +JOB_FILE = ROOT / "resources" / "jobs" / "dab_treinamento_job.yml" def _load_job(): From b2085c1d6818f0343ea9a7907f74520114e54d8c Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 16:25:43 +0000 Subject: [PATCH 11/24] update --- README.md | 2 +- dab_test/tests/job_config_test.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9d6bd3c..9fb08b5 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# repositorio-teste video - +# repositorio-teste video diff --git a/dab_test/tests/job_config_test.py b/dab_test/tests/job_config_test.py index 9e30f35..fd9becf 100644 --- a/dab_test/tests/job_config_test.py +++ b/dab_test/tests/job_config_test.py @@ -2,12 +2,12 @@ import yaml ROOT = Path(__file__).resolve().parents[1] -JOB_FILE = ROOT / "resources" / "jobs" / "dab_treinamento_job.yml" +JOB_FILE = ROOT / "resources" / "jobs" / "dab_test_job.yml" def _load_job(): data = yaml.safe_load(JOB_FILE.read_text()) - job = data["resources"]["jobs"]["dab_treinamento_job"] + job = data["resources"]["jobs"]["dab_test_job"] return job From 0a847df2b9b070c43c24688af6c51806fd8f9106 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 16:30:41 +0000 Subject: [PATCH 12/24] update jobs --- dab_test/resources/{job => jobs}/dab_test_job.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dab_test/resources/{job => jobs}/dab_test_job.yml (100%) diff --git a/dab_test/resources/job/dab_test_job.yml b/dab_test/resources/jobs/dab_test_job.yml similarity index 100% rename from dab_test/resources/job/dab_test_job.yml rename to dab_test/resources/jobs/dab_test_job.yml From 5ea721a64f695796ae37fbc52d2bbc909b370475 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 17:09:59 +0000 Subject: [PATCH 13/24] update main --- dab_test/tests/main_test.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/dab_test/tests/main_test.py b/dab_test/tests/main_test.py index 240f1d9..38ed1b0 100644 --- a/dab_test/tests/main_test.py +++ b/dab_test/tests/main_test.py @@ -1,6 +1,18 @@ +from unittest.mock import MagicMock, patch + from dab_test import main def test_find_all_taxis(): - taxis = main.find_all_taxis() - assert taxis.count() > 5 \ No newline at end of file + mock_df = MagicMock() + mock_reader = MagicMock() + mock_spark = MagicMock() + + mock_spark.read = mock_reader + mock_reader.table.return_value = mock_df + + with patch.object(main, "spark", mock_spark): + taxis = main.find_all_taxis() + + mock_reader.table.assert_called_once_with("samples.nyctaxi.trips") + assert taxis == mock_df \ No newline at end of file From a120ad935ddaba7fd1900973c442b135074e0d5e Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 17:26:05 +0000 Subject: [PATCH 14/24] update main --- dab_test/databricks.yml | 3 +++ dab_test/tests/job_config_test.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dab_test/databricks.yml b/dab_test/databricks.yml index ad36d9e..be7a0a5 100644 --- a/dab_test/databricks.yml +++ b/dab_test/databricks.yml @@ -20,6 +20,9 @@ variables: description: The catalog to use schema: description: The schema to use + catalog_name: + description: "Catalog used by the job" + default: dev targets: dev: diff --git a/dab_test/tests/job_config_test.py b/dab_test/tests/job_config_test.py index fd9becf..3421ea0 100644 --- a/dab_test/tests/job_config_test.py +++ b/dab_test/tests/job_config_test.py @@ -48,4 +48,7 @@ def test_job_uses_variable_for_performance_target(): job = _load_job() print("Validando uso da variável performance_target...") assert job.get("performance_target") == "${var.performance_target}" - print("performance_target OK!") \ No newline at end of file + print("performance_target OK!") + +spark = None + From 241d3f628256f0f3d8250394dca39a26e0c05c7d Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 17:37:26 +0000 Subject: [PATCH 15/24] update --- README.md | 2 +- dab_test/databricks.yml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9fb08b5..9d6bd3c 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# repositorio-teste video +# repositorio-teste video - diff --git a/dab_test/databricks.yml b/dab_test/databricks.yml index be7a0a5..5048f15 100644 --- a/dab_test/databricks.yml +++ b/dab_test/databricks.yml @@ -23,7 +23,9 @@ variables: catalog_name: description: "Catalog used by the job" default: dev - + performance_target: + description: "Performance target for the job" + default: "10" targets: dev: # The default target uses 'mode: development' to create a development copy. From 6f45c522e898180a702827abcc6a18b4fc631a15 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 17:46:01 +0000 Subject: [PATCH 16/24] update --- dab_test/databricks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dab_test/databricks.yml b/dab_test/databricks.yml index 5048f15..104975f 100644 --- a/dab_test/databricks.yml +++ b/dab_test/databricks.yml @@ -25,7 +25,7 @@ variables: default: dev performance_target: description: "Performance target for the job" - default: "10" + default: "STANDARD" targets: dev: # The default target uses 'mode: development' to create a development copy. From f3215e258907a6ad2210194da25c9b4f53732455 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 18:00:22 +0000 Subject: [PATCH 17/24] update --- dab_test/src/notebooks/{demo_notebooks.py => demo_notebook.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dab_test/src/notebooks/{demo_notebooks.py => demo_notebook.py} (100%) diff --git a/dab_test/src/notebooks/demo_notebooks.py b/dab_test/src/notebooks/demo_notebook.py similarity index 100% rename from dab_test/src/notebooks/demo_notebooks.py rename to dab_test/src/notebooks/demo_notebook.py From 52edfd1ce71ebfc2050ba65e5e24839ef6cfdcd2 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 22:25:01 +0000 Subject: [PATCH 18/24] update video --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d6bd3c..4aada1d 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# repositorio-teste video - +# repositorio-teste video - p From 8bbeb40086e854b2023f913b15a2d8141e6454aa Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 22:36:46 +0000 Subject: [PATCH 19/24] update --- .github/workflows/deploy_prd.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy_prd.yml b/.github/workflows/deploy_prd.yml index edee33e..51d2b35 100644 --- a/.github/workflows/deploy_prd.yml +++ b/.github/workflows/deploy_prd.yml @@ -37,19 +37,19 @@ jobs: env: DATABRICKS_CONFIG_PROFILE: PRD run: | - cd dab_treinamento + cd dab_test databricks bundle validate --target prd - name: Deploy bundle Databricks env: DATABRICKS_CONFIG_PROFILE: PRD run: | - cd dab_treinamento + cd dab_test databricks bundle deploy --target prd - name: Executar job Databricks env: DATABRICKS_CONFIG_PROFILE: PRD run: | - cd dab_treinamento - databricks bundle run dab_treinamento_job --target prd \ No newline at end of file + cd dab_test + databricks bundle run dab_test_job --target prd \ No newline at end of file From 7419a23b970fdcdef7ed81f9438345359c7f48f6 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 22:42:37 +0000 Subject: [PATCH 20/24] update dev --- .github/workflows/deploy_prd.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/deploy_prd.yml b/.github/workflows/deploy_prd.yml index 51d2b35..8ba25f0 100644 --- a/.github/workflows/deploy_prd.yml +++ b/.github/workflows/deploy_prd.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: branches: - - main + - prod jobs: deploy-prd: @@ -35,21 +35,21 @@ jobs: - name: Validar bundle Databricks env: - DATABRICKS_CONFIG_PROFILE: PRD + DATABRICKS_CONFIG_PROFILE: PROD run: | cd dab_test - databricks bundle validate --target prd + databricks bundle validate --target prod - name: Deploy bundle Databricks env: - DATABRICKS_CONFIG_PROFILE: PRD + DATABRICKS_CONFIG_PROFILE: PROD run: | cd dab_test - databricks bundle deploy --target prd + databricks bundle deploy --target prod - name: Executar job Databricks env: - DATABRICKS_CONFIG_PROFILE: PRD + DATABRICKS_CONFIG_PROFILE: PROD run: | cd dab_test - databricks bundle run dab_test_job --target prd \ No newline at end of file + databricks bundle run dab_test_job --target prod \ No newline at end of file From 5148dfb510d4daee1d435ba257c281fbdb4ad08b Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 22:55:40 +0000 Subject: [PATCH 21/24] update dev --- .github/workflows/deploy_prd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_prd.yml b/.github/workflows/deploy_prd.yml index 8ba25f0..1945ced 100644 --- a/.github/workflows/deploy_prd.yml +++ b/.github/workflows/deploy_prd.yml @@ -1,4 +1,4 @@ -name: Deploy Databricks Bundle PRD +name: Deploy Databricks Bundle PROD on: workflow_dispatch: From 3dee5a8574f6968e1b4790370fa35039bc8399b1 Mon Sep 17 00:00:00 2001 From: "rubencruzh@gmail.com" Date: Tue, 11 Aug 2026 23:08:52 +0000 Subject: [PATCH 22/24] update dev --- dab_test/databricks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dab_test/databricks.yml b/dab_test/databricks.yml index 104975f..8cb2b66 100644 --- a/dab_test/databricks.yml +++ b/dab_test/databricks.yml @@ -46,7 +46,7 @@ targets: # We explicitly deploy to /Workspace/Users/rubencruzh@gmail.com to make sure we only have a single copy. root_path: /Workspace/Users/rubencruzh@gmail.com/.bundle/${bundle.name}/${bundle.target} variables: - catalog: dev + catalog: prod schema: prod permissions: - user_name: rubencruzh@gmail.com From 3a7ebf40465edeb66ea570fa7933b1ceea5eb7fb Mon Sep 17 00:00:00 2001 From: "Ruben Cruz H." Date: Wed, 12 Aug 2026 10:45:55 -0300 Subject: [PATCH 23/24] Revise README for Databricks Asset Bundle project Updated project title and detailed project overview, architecture, technology stack, repository structure, and deployment workflows for Databricks Asset Bundle. --- README.md | 974 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 973 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4aada1d..e7ae401 100644 --- a/README.md +++ b/README.md @@ -1 +1,973 @@ -# repositorio-teste video - p +# Databricks Asset Bundle — Python Data Engineering + +A practical Databricks Data Engineering project demonstrating how to develop, test, package, deploy, and execute Python workloads using **Databricks Asset Bundles (DAB)** and **GitHub Actions CI/CD**. + +The project implements separate **DEV and PROD environments**, Python Wheel packaging, automated testing, parameterized Databricks Jobs, OAuth Service Principal authentication, and a reproducible development environment using VS Code Dev Containers. + +--- + +## Architecture + +```text + GitHub Repository + │ + ┌───────────┴───────────┐ + │ │ + dev prod + │ │ + ▼ ▼ + deploy_dev.yml deploy_prd.yml + │ │ + ▼ ▼ + GitHub Actions GitHub Actions + │ │ + ┌─────┴─────┐ ┌─────┴─────┐ + │ │ │ │ + Tests Validate Validate Deploy + │ │ │ │ + └─────┬─────┘ └─────┬─────┘ + │ │ + ▼ ▼ + DAB Bundle DAB Bundle + │ │ + ▼ ▼ + Databricks DEV Databricks PROD + │ │ + └───────────┬───────────┘ + │ + ▼ + dab_test_job + │ + ▼ + demo_notebook.py + │ + ▼ + Unity Catalog + catalog.schema.users +``` + +--- + +# Project Overview + +This project is based on the **Databricks Asset Bundle default Python project structure** and has been extended to demonstrate a more complete Data Engineering deployment workflow. + +The main workload demonstrates: + +1. Python application development. +2. Databricks notebook execution. +3. Parameterized Databricks Jobs. +4. Python Wheel packaging. +5. Databricks Asset Bundle configuration. +6. DEV and PROD environments. +7. Automated testing. +8. GitHub Actions CI/CD. +9. OAuth Service Principal authentication. +10. Containerized local development. + +--- + +# Technology Stack + +| Technology | Purpose | +| ------------------------ | ------------------------------------------- | +| Python 3.10–3.12 | Application development | +| PySpark | Distributed data processing | +| Databricks | Data Engineering platform | +| Databricks Asset Bundles | Deployment and resource management | +| Databricks CLI | Bundle validation, deployment and execution | +| Unity Catalog | Catalog/schema/table organization | +| uv | Python dependency management and build | +| Hatchling | Python Wheel build backend | +| Pytest | Automated testing | +| Ruff | Python linting | +| GitHub Actions | CI/CD | +| Docker | Development environment | +| VS Code Dev Containers | Reproducible development environment | +| YAML | Databricks and CI/CD configuration | +| TOML | Python project configuration | + +--- + +# Repository Structure + +```text +. +├── .devcontainer/ +│ ├── .env +│ ├── Dockerfile +│ ├── devcontainer.json +│ └── requirements.txt +│ +├── .github/ +│ └── workflows/ +│ ├── deploy_dev.yml +│ └── deploy_prd.yml +│ +├── dab_test/ +│ ├── .vscode/ +│ │ +│ ├── fixtures/ +│ │ +│ ├── resources/ +│ │ └── jobs/ +│ │ └── dab_test_job.yml +│ │ +│ ├── src/ +│ │ ├── dab_test/ +│ │ │ ├── __init__.py +│ │ │ └── main.py +│ │ │ +│ │ └── notebooks/ +│ │ └── demo_notebook.py +│ │ +│ ├── tests/ +│ │ ├── job_config_test.py +│ │ └── main_test.py +│ │ +│ ├── AGENTS.md +│ ├── CLAUDE.md +│ ├── README.md +│ ├── databricks.yml +│ └── pyproject.toml +│ +└── README.md +``` + +--- + +# Databricks Asset Bundle + +The core of the project is the `databricks.yml` file. + +The Bundle is named: + +```yaml +bundle: + name: dab_test +``` + +The configuration includes resource definitions from: + +```text +resources/jobs/*.yml +resources/pipelines/*.yml +resources/schemas/*.yml +``` + +It also defines a Python Wheel artifact: + +```yaml +artifacts: + python_artifact: + type: whl + build: uv build --wheel +``` + +This means the deployment process can build the Python application into a `.whl` package and make it available to the Databricks workload. + +--- + +# Environments + +The project defines two Databricks Bundle targets. + +## DEV + +The `dev` target uses: + +```yaml +mode: development +``` + +and is the default Bundle target. + +It uses the following configuration: + +```text +Catalog: dev +Schema: rescue_b +``` + +The DEV workflow is intended for development, testing, validation, deployment, and execution. + +--- + +## PROD + +The `prod` target uses: + +```yaml +mode: production +``` + +with: + +```text +Catalog: prod +Schema: rescue_b +``` + +The production deployment is associated with the `prod` Git branch. + +The production workflow can be triggered by: + +* a push to the `prod` branch; +* manual GitHub Actions execution. + +--- + +# Python Application + +The main Python package is located under: + +```text +dab_test/src/dab_test/ +``` + +The main application contains a simple Spark workload that reads the Databricks sample NYC Taxi dataset: + +```python +def find_all_taxis() -> DataFrame: + return spark.read.table("samples.nyctaxi.trips") +``` + +The `main()` function displays the first five records. + +The project therefore provides a minimal Python/Spark workload that can be packaged and executed through the Databricks environment. + +--- + +# Demo Notebook + +The project also contains: + +```text +dab_test/src/notebooks/demo_notebook.py +``` + +The notebook demonstrates parameterized Databricks execution using widgets: + +```text +catalog +user_id +user_name +``` + +The notebook: + +1. Receives runtime parameters. +2. Creates a `users` table if it does not exist. +3. Inserts sample users. +4. Inserts the parameterized user. +5. Reads the resulting table. +6. Displays the resulting DataFrame. + +The target table follows the pattern: + +```text +.rescue_b.users +``` + +For example: + +```text +dev.rescue_b.users +prod.rescue_b.users +``` + +--- + +# Databricks Job + +The Job is defined in: + +```text +dab_test/resources/jobs/dab_test_job.yml +``` + +The Job is called: + +```text +dab_test_job +``` + +It contains a task named: + +```text +ingestao_usuarios +``` + +which executes: + +```text +src/notebooks/demo_notebook.py +``` + +The Job exposes parameters including: + +```text +catalog_name +user_id +user_name +``` + +This allows the same notebook to be reused with different runtime values instead of hard-coding the input data. + +--- + +# Job Schedule + +The Job configuration defines a Quartz cron schedule: + +```text +Every Tuesday at 08:00 +Timezone: America/Sao_Paulo +``` + +The Job also has a timeout of: + +```text +900 seconds +``` + +Failure notifications are configured through Databricks Job email notifications. + +--- + +# Dynamic Parameters + +One of the main concepts demonstrated by this project is the separation between: + +### Bundle variables + +Defined in: + +```text +databricks.yml +``` + +Example: + +```text +catalog +schema +catalog_name +performance_target +``` + +and: + +### Job parameters + +Defined in: + +```text +resources/jobs/dab_test_job.yml +``` + +Example: + +```text +catalog_name +user_id +user_name +``` + +The values can flow through the deployment configuration into the notebook at runtime. + +Conceptually: + +```text +databricks.yml + │ + │ ${var.catalog_name} + ▼ +Databricks Job + │ + │ {{job.parameters.catalog_name}} + ▼ +demo_notebook.py + │ + ▼ +Unity Catalog +``` + +--- + +# Python Packaging + +The Python project is configured through: + +```text +dab_test/pyproject.toml +``` + +The project supports: + +```text +Python >= 3.10 +Python < 3.13 +``` + +The build system uses: + +```text +Hatchling +``` + +The Wheel package is built using: + +```bash +uv build --wheel +``` + +The resulting package can be used by the Databricks deployment. + +--- + +# Development Dependencies + +The project includes development dependencies for: + +* Pytest +* Ruff +* PyYAML +* Databricks DLT +* Databricks Connect +* IPython Kernel + +Install the development dependencies using: + +```bash +uv sync --dev +``` + +--- + +# Testing + +Tests are located under: + +```text +dab_test/tests/ +``` + +Current test modules include: + +```text +main_test.py +job_config_test.py +``` + +Run the tests locally with: + +```bash +uv run pytest +``` + +The DEV CI/CD workflow also executes: + +```bash +uv run pytest -s +``` + +before Bundle validation and deployment. + +--- + +# CI/CD + +The repository contains two GitHub Actions workflows: + +```text +.github/workflows/ +├── deploy_dev.yml +└── deploy_prd.yml +``` + +## DEV Pipeline + +The DEV workflow is triggered by pushes to: + +```text +dev +``` + +It performs: + +```text +Checkout + │ + ▼ +Install Databricks CLI + │ + ▼ +Install uv + │ + ▼ +Configure Databricks OAuth + │ + ▼ +Run Pytest + │ + ▼ +Validate Bundle + │ + ▼ +Deploy Bundle + │ + ▼ +Run Databricks Job +``` + +--- + +# PROD Pipeline + +The production workflow is triggered by: + +```text +push to prod +``` + +or manually through: + +```text +workflow_dispatch +``` + +The production workflow performs: + +```text +Checkout + │ + ▼ +Install Databricks CLI + │ + ▼ +Install uv + │ + ▼ +Configure PROD OAuth Service Principal + │ + ▼ +Validate Bundle + │ + ▼ +Deploy Bundle + │ + ▼ +Run Databricks Job +``` + +The PROD workflow deploys using: + +```bash +databricks bundle deploy --target prod +``` + +and executes: + +```bash +databricks bundle run dab_test_job --target prod +``` + +--- + +# Authentication + +GitHub Actions authenticates to Databricks using an **OAuth Service Principal**. + +The workflows obtain the following values from GitHub Secrets: + +```text +DATABRICKS_HOST +DATABRICKS_CLIENT_ID +DATABRICKS_CLIENT_SECRET +``` + +Environment-specific secrets are used for DEV and PROD. + +For example: + +```text +DATABRICKS_HOST_DEV +DATABRICKS_CLIENT_ID_DEV +DATABRICKS_CLIENT_SECRET_DEV +``` + +and: + +```text +DATABRICKS_HOST_PRD +DATABRICKS_CLIENT_ID_PRD +DATABRICKS_CLIENT_SECRET_PRD +``` + +No credentials should be committed to the repository. + +--- + +# Local Development + +## Prerequisites + +Recommended tools: + +* Git +* Docker +* Visual Studio Code +* VS Code Dev Containers extension +* Python 3.10–3.12 +* uv +* Databricks CLI + +--- + +## Dev Container + +The project provides a development container under: + +```text +.devcontainer/ +``` + +with: + +```text +Dockerfile +devcontainer.json +requirements.txt +.env +``` + +Open the repository in VS Code and select: + +```text +Dev Containers: Reopen in Container +``` + +This provides a reproducible development environment. + +> Never commit production credentials, access tokens, client secrets, or other sensitive information to `.env` or the repository. + +--- + +# Databricks CLI + +Authenticate to Databricks using your preferred authentication method. + +For a local profile: + +```bash +databricks configure +``` + +Verify the CLI: + +```bash +databricks --version +``` + +--- + +# Validate the Bundle + +From the project directory: + +```bash +cd dab_test +``` + +Validate DEV: + +```bash +databricks bundle validate --target dev +``` + +Validate PROD: + +```bash +databricks bundle validate --target prod +``` + +Validation should be performed before deployment. + +--- + +# Deploy to DEV + +```bash +cd dab_test + +databricks bundle deploy --target dev +``` + +After deployment, execute the Job: + +```bash +databricks bundle run dab_test_job --target dev +``` + +--- + +# Deploy to PROD + +Production deployment should preferably occur through the GitHub Actions workflow. + +The equivalent CLI commands are: + +```bash +cd dab_test + +databricks bundle validate --target prod + +databricks bundle deploy --target prod + +databricks bundle run dab_test_job --target prod +``` + +--- + +# End-to-End Deployment Flow + +The complete workflow is: + +```text +Developer + │ + │ git push + ▼ +GitHub + │ + ├───────────────┐ + │ │ + dev prod + │ │ + ▼ ▼ +DEV Workflow PROD Workflow + │ │ + ▼ ▼ +Pytest Validation + │ │ + ▼ ▼ +Validation Deployment + │ │ + ▼ ▼ +Deployment Job Execution + │ │ + ▼ ▼ +Databricks DEV Databricks PROD +``` + +--- + +# Configuration Files + +## `databricks.yml` + +Defines the Databricks Asset Bundle. + +Responsible for: + +* Bundle name +* Resources +* Variables +* Artifacts +* DEV target +* PROD target +* Workspace configuration + +--- + +## `pyproject.toml` + +Defines the Python project. + +Responsible for: + +* Python version +* Project metadata +* Dependencies +* Development dependencies +* Entry point +* Build system +* Wheel packaging +* Ruff configuration + +--- + +## `resources/jobs/dab_test_job.yml` + +Defines the Databricks Job. + +Responsible for: + +* Job name +* Job parameters +* Schedule +* Timeout +* Notifications +* Notebook task +* Runtime parameters +* Performance configuration + +--- + +## `.github/workflows/deploy_dev.yml` + +Defines the DEV CI/CD pipeline. + +Responsible for: + +* Installing tooling +* Authentication +* Running tests +* Bundle validation +* Deployment +* Job execution + +--- + +## `.github/workflows/deploy_prd.yml` + +Defines the PROD CI/CD pipeline. + +Responsible for: + +* Installing tooling +* PROD authentication +* Bundle validation +* Production deployment +* Job execution + +--- + +# Key Engineering Concepts + +This project demonstrates the following Data Engineering and DevOps practices: + +### Infrastructure as Code + +Databricks resources are defined as code rather than being created manually through the Databricks UI. + +### Environment Separation + +The same Bundle supports: + +```text +DEV +PROD +``` + +using different targets. + +### Python Packaging + +The application is packaged as a Python Wheel before deployment. + +### Automated Testing + +Pytest is integrated into the DEV deployment pipeline. + +### CI/CD + +GitHub Actions automates the deployment lifecycle. + +### Parameterized Workloads + +The Databricks Job passes runtime parameters into the notebook. + +### Service Principal Authentication + +CI/CD uses OAuth-based Service Principal authentication rather than personal credentials. + +### Reproducible Development + +The Dev Container provides a consistent development environment. + +--- + +# Project Status + +Current implementation: + +* [x] Databricks Asset Bundle +* [x] Python project structure +* [x] PySpark application +* [x] Databricks notebook +* [x] Parameterized Databricks Job +* [x] Unity Catalog catalog/schema configuration +* [x] DEV target +* [x] PROD target +* [x] Python Wheel packaging +* [x] uv dependency management +* [x] Hatchling build system +* [x] Pytest tests +* [x] Ruff configuration +* [x] VS Code Dev Container +* [x] GitHub Actions DEV pipeline +* [x] GitHub Actions PROD pipeline +* [x] Databricks OAuth Service Principal authentication +* [x] Automated Databricks Job execution + +--- + +# Future Improvements + +Potential extensions include: + +* [ ] Add Delta Lake ingestion and transformation layers +* [ ] Add Bronze/Silver/Gold architecture +* [ ] Add data quality validation +* [ ] Add integration tests against Databricks +* [ ] Add structured logging +* [ ] Add monitoring and alerting +* [ ] Add Unity Catalog permissions management +* [ ] Add CI quality gates for Ruff +* [ ] Add pull-request validation +* [ ] Add deployment approval gates for PROD +* [ ] Add infrastructure documentation +* [ ] Add job run monitoring +* [ ] Add data lineage documentation + +--- + +# Learning Objectives + +This project was created to demonstrate practical knowledge of: + +```text +Python + │ + ├── Packaging + ├── Testing + └── PySpark + │ + ▼ + Databricks + │ + ├── Asset Bundles + ├── Jobs + ├── Notebooks + └── Unity Catalog + │ + ▼ + DevOps + │ + ├── Git + ├── GitHub Actions + ├── CI/CD + └── Service Principal +``` + +The main objective is to demonstrate how a Data Engineering workload can move from **local development to a controlled production deployment** using modern software engineering practices. + +--- + +# Author + +**Ruben Cruz** + +Data Engineering · Data Integration · Python · PySpark · Databricks · CI/CD From eed02ed5197ee17e2c4bdc9117fc1b11a1292182 Mon Sep 17 00:00:00 2001 From: "Ruben Cruz H." Date: Wed, 12 Aug 2026 16:27:32 -0300 Subject: [PATCH 24/24] Update README with detailed project description Expanded project description to include CI/CD reference implementation. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index e7ae401..5d9096e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -# Databricks Asset Bundle — Python Data Engineering - +# Databricks Asset Bundle — Python Data Engineering - Databricks platform engineering / CI/CD reference implementation. A practical Databricks Data Engineering project demonstrating how to develop, test, package, deploy, and execute Python workloads using **Databricks Asset Bundles (DAB)** and **GitHub Actions CI/CD**. The project implements separate **DEV and PROD environments**, Python Wheel packaging, automated testing, parameterized Databricks Jobs, OAuth Service Principal authentication, and a reproducible development environment using VS Code Dev Containers.