# Azure Databricks with Azure DevOps CI/CD
[TOC]
<!-- ## Prerequisite
- Install Python 3.8
- [Python 3.8.10 - Download Windows installer (64-bit)](https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe)
- Visual Studio Code
- Azure Databricks -->
## 0) Lab 環境設置
此章節會建立 Databricks 環境供後續部屬使用及 Azure DevOps Organization 及 Project 實作 CI/CD
### 建置 Databrick 環境
- 到 [Azure Portal](https://portal.azure.com/),搜尋 `databrick`,選擇 **Azure Databricks**

- 點選  建立新資源
- 完成資訊填寫
**Basic**
- Subscription: <XXXX'S SCBSCRIPTION>
- Resource Group: `rg-xxx-databricks-lab`
- Workspace Name: `workspace-xxx-lab`
- Region: `East US`
- Pricing Tier: Standard

**Networking**
- Deploy Azure Databricks workspace with Secure Cluster Connectivity (No Public IP): `No`
- Deploy Azure Databricks workspace in your own Virtual Network (VNet): `No`

- 點選 
- 確認資訊正確後點選 

- 等待部屬完成

### 建置 Azure Storgae Account 儲存分析用資料
- 選擇剛建立的資源群組,並點選 
- 選擇 Stroage Account

- 完成資訊填寫 **Basics** 的資訊填寫
- Region: East US
- Performance: Standard
- Redundancy: Locally-redundant storage (LRS)

- 完成後可直接點選 ,確認資訊正確後點選 Create
- 等待部署完成後點選 Go to resource

- 將 Storage Account 的名稱記下

- 點選左邊選單中 Data storage 區塊內的 Container,點選 ,將 container 命名為 `datasource` 後點選 Create 建立一個 container,記下 container 的名稱

- 開啟 CMD,下載今日使用之範例 [huier23/sample-databricks-lab](https://github.com/huier23/sample-databricks-lab.git),完成後確認在 datasource 內有兩個 csv 檔案分別為 *movies.csv* 及 *ratings.csv*
```
cd desktop
git clone https://github.com/huier23/sample-databricks-lab.git
```

- 回到 Azure Portal,點選剛剛建立的 storage container 進入
- 點選  > Select a file  > 選擇 *sample-databricks-lab/datasource/movies.csv* 和 *sample-databricks-lab/datasource/ratings.csv* 兩個檔案上傳,選擇完後點選 

- 確認已有上傳兩份檔案

- 點選左編選單中 Security + networking 中的 Access keys,選擇 將 key1 或 key2 的 Key 記下 (擇一即可)

### 建置 Azure DevOps Organization
#### 建立新組織
- 到 [Azure DevOps](https://dev.azure.com/) 頁面,選擇 開始免費使用

- 選擇 Taiwan

- 填寫你的組織名稱、地區(建議可以選擇 Central US)

- 點選 Continuous,開始進行服務的建立

- 完成後,會跳轉至以下畫面,建立第一個專案,選擇 Private

#### 第一個專案
- 建立完成後會看到一個開好的專案

- 點選左上角的  回到組織首頁
#### Billing 設定
:::info
:bulb: 因目前免費的一條 pipeline 須提前申請,因此需綁定 billing 才可使用 pipeline,免費 pipeline 申請方式請參考 [Reference](https://hackmd.io/@msazuredev/HkaaHy2AO)
:::
- 在首頁中,點選左下方的 Organization Setting

- 點選 Billing,選擇 Set up billing,選擇你可使用的訂閱 (免費試用/MSDN 訂閱不可使用)

- 將 MS Hosted CI/CD 數量調整為 1

#### 建置今日 Project 所使用之專案
- 回到首頁,點選右上角 

- 填寫 Project name `databrick-cicd-workshop`,Visibility 選擇 Private,完成後 Create

## 1) 使用 Databrick Workspace 進行開發
### Databricks workspace 設定
- 部屬完成後點選 
- 在 Azure Databricks Service 的頁面點選 

- 點選 Get Start 中的 Create a cluster

- 點選 

- 設定 Cluster 新增資訊
- Cluster Name: `cluster-xxx-lab`
- Cluster mode: `Standard`
- Databricks runtime version: `Runtime: 10.4 LTS`
- Autopilot options: Disable autoscaling
- Worker type: `Standard_DS3_v2`
- Workers: `2`

### Databrick 上 Source Control 設定
- 開啟 [Azure DevOps](https://dev.azure.com/) > Repos > New repository

- 新增 `databrick-lab` 的 repository,勾選 Add a README,完成後點選 Create

- 點選右上角 ,將 repository 位置記錄下

- 回到 Databrick Portal,點選  >  > Git integration

- 設定 Git provider 為 Azure DevOps Services (Azure Active Directory)

<!-- - 點選  > Admin Console > Workspace settings

- Under Workspace Settings, confirmed "Files in Repo" is enable
 -->
- 點選  > ,將剛建立的 repository 的 URL 貼上

### 在 Workspace 中撰寫程式碼
- 點選  > Users > xxxx@xxxx.com,右鍵選擇 Create > Floder

- 新增一個名為 `workspace` 的 folder,點選 Create Folder

- 點選,找到 `workspace` 資料夾,右鍵選擇 Create > Notebook

- 新增 `analysis` 檔案,點選 Create

- 添加程式碼
- Import Library 並執行 (Shift + Enter)
```python
from pyspark.sql.functions import split, explode, col, udf
from pyspark.sql.types import *
from pyspark.sql import SparkSession
```
- 加入以下程式碼設定資料來源,將 storage 資料修改為個人的環境
```python
spark = SparkSession.builder.appName('temps-demo').getOrCreate()
# Setting storage account connection
container_name = "datasource"
storage_account_name = "<STORAGE-NAME>"
storage_account_access_key = "<STROAGE-KEY>"
spark.conf.set("fs.azure.account.key." + storage_account_name +".blob.core.windows.net",storage_account_access_key)
```
- 新增一行,做資料的 loading,完成後驗證
```python
# Get stroage data location
ratingsLocation = "wasbs://" + container_name +"@" + storage_account_name + ".blob.core.windows.net/ratings.csv"
moviesLocation = "wasbs://" + container_name +"@" + storage_account_name +".blob.core.windows.net/movies.csv"
# Get ratings and movies data
ratings = spark.read.format("csv") \
.option("inferSchema", "true") \
.option("header", "true") \
.load(ratingsLocation)
movies = spark.read.format("csv") \
.option("inferSchema", "true") \
.option("header", "true") \
.load(moviesLocation)
```
- 新增一行做 movies 資料欄位的顯示
```python
movies.show()
```

- 新增一行做 ratings 資料欄位的顯示
```python
ratings.show()
```

### 將新增的檔案從 workspace 上進版控
- 右鍵點選 `workspace` Folder > Move

- 選擇剛加入的 Repos

- 雖檔案已加入 Repos 中,但此時還未 check-in 到 remote repository 中
- 點選上方的 
- 完成 commit message 後點選 

- 回到 [Azure DevOps](https://dev.azure.com/) 上確認檔案已 sync 上

## 2) 使用 Visual Studio Code 進行開發
### databricks-connect 環境設置
- 安裝 JRE 8:Java Runtime Environment (JRE) 8. The client has been tested with the OpenJDK 8 JRE. The client does not support Java 11.
- 安裝 Python 3.8
- Python 路徑需加入到環境變數: `C:\Users\AzureUser\AppData\Local\Programs\Python\Python38`
- PIP 路徑需加入到環境變數: `C:\Users\AzureUser\AppData\Local\Programs\Python\Python38\Scripts`
:::info
:bulb: 注意 python version 與 databricks runtime 版本

:::
- 安裝 virtualenv
```
pip install virtualenv
```
- 確認已有成功安裝 virtualenv
```
pip list
```

- 在桌面將 Azure Repo 上的檔案 clone 下來,並索引到資料夾下
```
cd desktop
git clone <Azure-Repo-URL>
cd databrick-lab
```
- 使用 virtualenv 在此目錄下建立虛擬環境
- 環境中僅有一個 python 版本
```
virtualenv .env
```
- 如環境中有多個 python 版本,指定 virtualenv 的 python 版本
```
virtualenv -p <PythonVersion> .env
```

- 啟動虛擬環境,成功啟動後會出現  (此指令為 windows 寫法)
```
.env\Scripts\activate
```

- 確認環境中無安裝 PySpark
- 確認
```
pip list
```

- 如有安裝需進行移除 (會與 databricks-connect 衝突)
```
pip uninstall PySpark
```
- 安裝 [databricks-connect](https://pypi.org/project/databricks-connect/10.4.0b0/) (須配合 Databricks Cluster 的版本)
```
pip install databricks-connect==10.4.0b0
```

- 回到 [Azure Portal](https://portal.azure.com/) 取得 Databrick URL 並記錄下來

- 回到 Databrick Portal 上,點選  並選取先前建立的 cluster

- 在 Configuration 的頁面選擇 JSON view,取得 cluster_id 並記錄下來

- 在網址列 `?o=<Organization-ID>#` 取得 Organization ID

- 取得 Databricks PAT
- Setting > User Setting

- 點選 ,資訊填寫完成後按 Generate

- 將產生的 PAT 記錄下來並按 Done
- 回到本機環境執行 CMD 進行 databricks-connect 的設定
```
databricks-connect configure
```
- Do you accept the above agreenment: `y`
- Databricks Host [no current value, must start with https://]: `<Databricks-URL>`
- Databricks Token [no current value]: `<Databricks-PAT>`
- Cluster ID (e.g., 0921-001415-jelly628) [no current value]: `<Databricks-Cluster-ID>`
- Org ID (Azure-only, see ?o=orgId in URL) [0]: `<Databricks-Organization-ID>`
- Port [15001]: 使用 default

- 驗證連線
```
databricks-connect test
```
- Fix Windows Hadoop Issue

- Hadoop 安裝參考 [Hadoop : How to install in 5 Steps in Windows 10](https://medium.com/analytics-vidhya/hadoop-how-to-install-in-5-steps-in-windows-10-61b0e67342f8)
- 需使用 7-zip 解壓縮 .tar 時,需用 admin 開啟
- Hadoop 會使用到 Java,確認環境變數中已添加 `JAVA_HOME`,並將 `HADOOP_HOME` 路經加入至環境變數 (JAVA_HOME 及 HADOOP_HOME 的路徑不可有空格)


- 將 winutil 手動加入 %HADOOP_HOME%/bin 底下

- **Windows 所需環境: [DatabricksConnectEnv-Win](https://1drv.ms/u/s!AtAJk4ApWmAFhJQDG8fkp2p4Q3T41A?e=5Ahf9N)**
### 使用 vscode + Juypter notebook extension 開發程式碼
- 開啟 vscode,點選 ,搜尋 `jupyter`,安裝 [Jupyter Extension](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter)

- 搜尋 `python`,安裝 [Python Extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python)

- 點選 Files > Open Folder

- 選擇建立的資料夾

- 在 EXPLORER 的空白區域點選右鍵 > New Folder

- 新增一個 `vscode` 資料夾

- 選擇上方的 Terminal > New Terminal

- 確認虛擬環境是否有被啟動,如未被啟動則輸入 `.env/Scripts/activate`

- 點選 **Shift + Ctrl + P**,再彈出的視窗中輸入 `jupyter` 並選擇 Create: New Jupyter Notebook

- 會建立一個 .ipynb 的檔案,確認右上角的環境是使用虛擬環境

- 如非為虛擬環境之設定,可點選進行修改

- 添加程式碼
- Import Library
```python
from pyspark.sql.functions import split, explode, col, udf
from pyspark.sql.types import *
from pyspark.sql import SparkSession
```
- 點選 執行
- 會跳出視窗提醒需要安裝 jupyter and notebook package,點選 Install

- 點選  可新增一行 code

- 加入以下程式碼設定資料來源,將 storage 資料修改為個人的環境
```python
spark = SparkSession.builder.appName('temps-demo').getOrCreate()
# Setting storage account connection
container_name = "datasource"
storage_account_name = "<STORAGE-NAME>"
storage_account_access_key = "<STROAGE-KEY>"
spark.conf.set("fs.azure.account.key." + storage_account_name +".blob.core.windows.net",storage_account_access_key)
```
- 完成後執行驗證

- 再新增一行 code,做資料的 loading,完成後驗證
```python
# Get stroage data location
ratingsLocation = "wasbs://" + container_name +"@" + storage_account_name + ".blob.core.windows.net/ratings.csv"
moviesLocation = "wasbs://" + container_name +"@" + storage_account_name +".blob.core.windows.net/movies.csv"
# Get ratings and movies data
ratings = spark.read.format("csv") \
.option("inferSchema", "true") \
.option("header", "true") \
.load(ratingsLocation)
movies = spark.read.format("csv") \
.option("inferSchema", "true") \
.option("header", "true") \
.load(moviesLocation)
```
- 新增一行做 movies 資料欄位的顯示
```python
movies.show()
```

- 新增一行做 ratings 資料欄位的顯示
```python
ratings.show()
```

- **Ctrl + S** 進行儲存,將目前的檔案儲存於 `vscode` 資料夾下, File name 設定為 `analysis.ipynb`,Save as type 設定為 `All Files`,完成後點選 Save

- 在目前的目錄下新增 `.gitignore` 檔案
```.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
# Distribution / packaging
.Python
build/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
sdist/
wheels/
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Jupyter Notebook
.ipynb_checkpoints
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
```

### 在本機將開發的檔案進行版控
- 在 vscode 中開啟終端機

- 使用 git
```
git add .
git commit -m "add vscode/analysis"
git push origin main
```
### Databricks 上查看更新的檔案
- 回到 Databrick Portal,點選 Repos > xxxx@xxxx.com > databrick-lab > 

- 選擇視窗右上角的 Pull > Confirm

- 顯示完成拉取

- 點選 databrick-lab 資料夾,檔案已更新

### Databricks 上匯入 .ipynb 檔案至 workspace 中
- 選擇 Workspace > Users > xxxx@xxxx.com,右鍵 > Import

- 選擇 File > browser,選擇剛建立的 .ipynb 檔案

- 完成後點選 Import

- 點選 Run All 執行

## 3) Configue Azure DevOps CI/CD
### Azure DevOps Service 在 MLOps 內扮演的腳色

- Source Control (Azure Repo - Git)
- Notebook Deployment
- Release to Multiple Environments
- Dev
- Staging (UAT)
- Production
- Artifact management
### Databrick CI/CD
- 回到 [Azure DevOps](https://dev.azure.com/),選擇 Pipeline > Release > 

- 選擇 

- 將 Stage 命名為 `vsocode`

- 點選 + Add an artifact

- 設定 Artifact 來源
- Source type: Azure Repo
- Project: `databrick-cicd-workshop`
- Source (repository): `databrick-lab`
- Default branch: `main`

- 點選 ,設定 Continuous deployment trigger

- 點選 vscode stage 的  設定部屬工作流程
- 在 Agent Job 中點選 +,搜尋 `databrick`,選擇 **Configure Databricks**

:::info
:bulb: **若未出現 tasks 可使用,在 Marketplace 的區塊尋找**
- 第一次使用需安裝 DevOps for Azure Databricks 套件

- 點選 Get it free
- 選擇你的組織,點選 install

:::
- 設定 **Configure Databricks**
- Workspace URL: `$(WORKSPACE_URL)`
- Access Token: `$(PAT)`

- 點選 +,搜尋 `databrick`,選擇 **Start a Databricks Cluster**

- 設定 **Start a Databricks Cluster**
- Start a Databricks Cluster: `$(CLUSTER_ID)`

- 點選 +,搜尋 `databrick`,選擇 **Deploy Databricks Notebooks**

- 設定 **Deploy Databricks Notebooks**
- Notebooks folder:`$(System.DefaultWorkingDirectory)/_databrick-lab`
- Workspace folder: `/Shared`
- 點選 +,搜尋 `databrick`,選擇 **Execute Databricks Notebook**

- 設定 **Execute Databricks Notebook**
- Notebook path (at workspace): `/Shared/$(System.StageDisplayName)/analysis`
- Existing Cluster ID: `$(CLUSTER_ID)`
- 點選 +,搜尋 `databrick`,選擇 **Wait for Databricks Notebook execution**

- 點選 Variables > Variable groups > Manage variable groups

- 點選 ,新增三個變數,完成後點選 
- WORKSPACE_URL: `<DATABRICK-WORKSPACE_URL>`
- PAT: `<DATABRICK-PAT>`
- CLUSTER_ID: `<DATABRICK-CLUSTER_ID>`

- 回到 release pipeline,點選  連結剛建立的 variable group

- 點選上方選單的 pipeline,在 vscode stage 的下方選擇 clone

- 點選 Copy of vscode 的 stage,將 Stage name 修改為 `workspace`

- 此處於未來應用可設定不同的 Cluster ID 或 Databrick URL 做不同環境的部屬
- 點選 workspace 的 ,將 Pre-deployment approvals 啟用並設置為 approver
- 修改 release pipeline 的名稱為 `Release to databricks`

- Save 並點選 Create Release
- 點選上方的狀態列,查看 Release 狀態

- 允許做 workspace stage 的部屬

- 查看 Stage Log

- 選擇 Wait for Notebook execution

- 捲至最下方,點選 For details, go to the execution page 的網址,可查看在 Databricks 中執行的狀況


### Python Package (Artifact Management)
- **PyPI** 是一個套件庫,位於 https://pypi.org,其中包含了各式各樣的 Python 套件,在開發應用程式的過程中,可以到這邊來搜尋是否有所需的功能套件,安裝後透過引用的方式來進行使用,藉此提升開發效率。
- **pip** 是一個全域環境的套件管理工具,用來安裝及管理 PyPI 上的 Python 套件,也就是說利用 pip 指令所安裝的 Python 套件,所有專案皆可使用。
#### Settup CI pipeline for build python package
- 下載範例程式碼 [huier23/py-packaging-sample](https://github.com/huier23/py-packaging-sample)
```
cd desktop
git clone https://github.com/huier23/py-packaging-sample.git
cd py-packaging-sample
```
- 在 Azure DevOps 中新增一個 repository,命名為 `count-lib`,不新增 README

- 將 Repo 的 URL 記下

- 回到 CMD,透過 git 將程式碼推送至 [Azure DevOps](https://dev.azure.com/) Repo
```
git remote add devops <Azure-Devops-Repo-URL>
git push devops master
```
- 確認完成推送

- 選擇 Pipeline > New Pipeline

- 選擇 Use the classic editor

- 設定來源
- Azure Repo Git: Select a source
- Repository: count-lib

- 選擇 

- 新增 taks,搜尋 `python`,選擇 Use Python version

- 設定 Use Python Version
- Version spec: `>= 3.6`

- 新增 task,搜尋 `command`,選擇 Command line

- 設定 Command line
- Display name: `Upgrade pip`
- Script: `python -m pip install --upgrade build`

- 新增 task,搜尋 `command`,選擇 Command line

- 設定 Command line
- Display name: `Build python package`
- Script: `python -m build`

- 新增 task,搜尋 `copy`,選擇 Copy files

- 設定 Copy files
- Source Folder: `$(Build.Repository.LocalPath)/dist`
- Target Folder: `$(Build.ArtifactStagingDirectory)`

- 新增 task,搜尋 `publish`,選擇 Publish build artifacts

- 設定 Publish build artifacts
- Path to publish: `$(Build.ArtifactStagingDirectory)`
- Artifact name: `dist`

- 點選 Triggers,設定 Enable continuous integration

- 修改 CI pipeline 名稱為 `count-lib-CI`,完成後點選 Save & queue

- 執行完成後點選此 run 的 summary,點選 

- 查看 dist 下的檔案

#### Settup CD pipeline for release to Artifact
<!-- - Support PyPI, Maven or universal package (NO CRAN) -->
- 點選 Artifacts > + Create Feed

- 設定 Create new feed
- Name: `databricks-lib`
- Visibility: Members of huier-teamservice
- Scope: Project: databrick-cicd-workshop (Recommended)

- 選擇 Release > + New > + New release pipeline

- 選擇 Empty job

- 修改 stage name 為 `Release to Artifact`

- 新增 Artifact
- Source type: Build
- Source (build pipeline): count-lib-CI

- 點選  設定 Continuous deployment trigger

- 點選 Release to Artifact 的 

- 新增 task,搜尋 `command`,點選 Command Line

- 設定 Command Line
- Display name:`pip install wheel & twine`
- Script:
```
pip install wheel
pip install twine
```

- 新增 task,搜尋 `python`,點選 Python twine upload authenticate

- 設定 Python twine upload authenticate
- My feed (select below): `databricks-lib`

- 新增 task,搜尋 `command`,點選 Command Line

- 設定 Command Line
- Display name:`twin upload package`
- Script:
`python -m twine upload --repository databricks-lib --config-file $(PYPIRC_PATH) $(System.DefaultWorkingDirectory)/**/dist/*.whl`

- 修改 release pipeline 名稱為 `Release to Artifact`,完成後點選 Save 後 Create release

- 查看部屬狀態

- 完成後到 Artifact 中查看

#### Get package
- 到 [Azure Portal](https://portal.azure.com/) 上使用 Azure Cloud Shell 環境驗證
- Update pip
```
python -m pip install --upgrade pip
```
- Install the keyring (Azure Cloud Shell 環境已安裝,可略過)
```
pip install keyring artifacts-keyring
```
- 建立新資料夾,並索引到新資料夾底下
```
mkdir hello_world
cd hello_world
```
- 建立 python 虛擬環境並啟用
```
virtualenv .env
source .env/bin/activate
```

- 索引到 `.env` 底下,新增 `pip.conf` 檔案
```
cd .env
code pip.conf
```
- 設定 pip repo 位置,將下方程式碼貼上後儲存
```conf=0
[global]
index-url=https://pkgs.dev.azure.com/<Organization-Name>/<Project-Name>/_packaging/<Feed-Name>/pypi/simple/
```

- 回到 [Azure DevOps](https://dev.azure.com/) 取得 PAT

- 提供 packaging **Read, write & manage** 權限,完成後將 PAT 記錄在記事本

- 回到 Azure Cloud Shell,使用 pip 安裝 private package
```
pip install count==0.0.1
```
- User for pkgs.dev.azure.com 為個人帳號,password 為 PAT

- 回到新資料夾目錄下,新增測試用的檔案 `hello_world.py`
```
cd ..
code hello_world.py
```
:::info
:bulb: 如使用 windows,會自動跳出 device 驗證,於 PAT 中可查看到 Azure DevOps Artifactes Credential Provider

:::
- 新增測試檔案 `test.py`
```python=0
from count import add
print(add.add_one(3))
```
- 執行驗證
```
python test.py
```

## Reference
- [Python Release for Windows](https://www.python.org/downloads/windows/)
- [Datbricks Connect | Microsoft Doc](https://docs.microsoft.com/en-us/azure/databricks/dev-tools/databricks-connect)
- [Databricks Connect | Databricks Doc](https://docs.databricks.com/dev-tools/databricks-connect.html)
- [Generate Databricks PAT](https://docs.microsoft.com/en-us/azure/databricks/dev-tools/api/latest/authentication#--generate-a-personal-access-token)
- [Databricks CLI | Microsoft Doc](https://docs.microsoft.com/en-us/azure/databricks/dev-tools/cli/)
- [Anaconda Install](https://www.anaconda.com/products/distribution)
- [Databricks CLI](https://docs.microsoft.com/en-us/azure/databricks/dev-tools/cli/)
- [Libraries](https://docs.microsoft.com/en-us/azure/databricks/libraries/)
- [Repos for Git integration](https://docs.microsoft.com/en-us/azure/databricks/repos/)
- [Config PyCharm](https://docs.microsoft.com/en-us/azure/databricks/dev-tools/databricks-connect#pycharm)
- [Azure DevOps Artifact | Python](https://docs.microsoft.com/en-us/azure/devops/artifacts/quickstarts/python-packages?view=azure-devops)
- [Azure Artifacts: best practices](https://docs.microsoft.com/en-us/azure/devops/artifacts/concepts/best-practices?view=azure-devops)
- [Packaging Python Projects](https://packaging.python.org/en/latest/tutorials/packaging-projects/#creating-the-package-files)
- [Build Python apps](https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/python?view=azure-devops)
- [Publish Python packages with Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/artifacts/pypi?bc=%2Fazure%2Fdevops%2Fartifacts%2Fbreadcrumb%2Ftoc.json&toc=%2Fazure%2Fdevops%2Fartifacts%2Ftoc.json&view=azure-devops&tabs=yaml)
- [Install custom Python Libraries from private PyPI on Databricks](https://towardsdatascience.com/install-custom-python-libraries-from-private-pypi-on-databricks-6a7669f6e6fd)
- [Databricks Script Deployment Task by Data Thirst by Data Thirst Ltd | Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=DataThirstLtd.databricksDeployScriptsTasks&targetId=26cd1db8-21ca-4faa-93dc-73fd5a63f717)
- [Databricks CLI](https://docs.databricks.com/dev-tools/cli/index.html)
- [Azure DevOps Use predefined variables | Microsoft Doc](https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml)
- https://docs.microsoft.com/zh-tw/azure/databricks/repos/