今天使用 Visual Studio Code 的 Remote Development 將一個 .NET 6 與 SQL Server 開發環境全部放到 Docker 容器中。雖然照著文件操作都很順利,但在專案加入版控之後,問題就出現了。這篇文章我想點出現今容器化、跨平台的時代中,換行符號其實不得不面對他,否則遇到問題會無所適從。
體驗在容器中開發的感覺
-
先安裝好 Docker Desktop 與 Remote Development 擴充套件
-
先建立基本 ASP.NET Core 6 專案並用 Visual Studio Code 開啟
dotnet new web -n devcontainer-demo1
code devcontainer-demo1
-
按下 F1
> Remote-Containers: Add Development Container Configuration Files
-
先選擇一個 Dev Container 範本: C# (.NET) and MS SQL
-
選擇 .NET 版本: 6.0-focal
-
選擇 Node.js 版本: none
-
選擇要在容器中使用的工具: 這裡我們先不選,可直接按下 OK
完成設定
這樣就設定完成了! 👍
-
接著我們進行 Git 版控
為了要進行版控,我先加入一個 .NET 專用的 .gitignore
檔案:
dotnet new gitignore
下圖是最終版本,基本上每個檔案你都可以點開來看:
建立版控
git init
git add .
git commit -m "Initial commit"
-
執行 F1
> Remote-Containers: Reopen in Container
即可自動啟動
這個動作的背後,會全自動建置所有需要的容器映象(image),然後自動啟動,自動初始化容器環境 (安裝必要的開發工具),開啟必要的網路連線埠轉向(Port Forwarding),然後就可以很順利的讓你在 Visual Studio Code 連接到 Container 容器中進行開發、測試、執行。
體驗在版控之後發生的問題
由於專案中的 .devcontainer/Dockerfile
檔案會負責建置 Dev Container 開發容器中的所有環境,其中包含了 SQL 相關工具 如 sqlcmd
, bcp
, sqlpackage
等等。
這個 .devcontainer/Dockerfile
內容如下:
#!/bin/bash
# [Choice] .NET version: 6.0-focal, 3.1-focal
ARG VARIANT="6.0-focal"
FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT}
# [Choice] Node.js version: none, lts/*, 18, 16, 14
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
# Install SQL Tools: SQLPackage and sqlcmd
COPY mssql/installSQLtools.sh installSQLtools.sh
RUN bash ./installSQLtools.sh \
&& apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
你可以發現到他有將 mssql/installSQLtools.sh
複製到容器中執行,重點是微軟所提供的 Dev Container 開發用容器是以 Ubuntu 20.04.4 LTS
為主要作業系統,而在執行這段命令時,會要求所有的 Shell Script 必須以 LF
為主要斷行符號,因此 Windows 常見的 CRLF
斷行符號是無法使用的,建置容器映像的過程會發生錯誤!
接著我們就來看一下這個發生錯誤的過程:
-
容器中並不會包含專案原始碼
事實上,微軟的 Dev Container 會用 volumn mapping 的方式將原始碼掛載到容器中使用,因此你只有「專案執行環境」在容器中而已!
-
判斷 ~/.gitconfig
設定
我們一開始在建立容器映像的時候,他預設會將 Windows 本機的 ~/.gitconfig
全域設定檔,自動複製到容器環境下的 ~/.gitconfig
,因此基本上在 Ubuntu 20.04.4 LTS
底下的 Git 設定是跟 Windows 完全相同的。
但是 Windows 底下有個 core.autocrlf=true
的預設值,這意味著當所有檔案被取出(Checkout)的時候,所有文字檔案都會自動以 CRLF
來當成換行字元,但這個問題就大了!
-
重新取出原始碼
由於 .devcontainer/mssql/installSQLtools.sh
檔案剛開始是以 LF
來保存的,只要重新取出原始碼,他就會自動轉成 CRLF
換行字元,所以我們可以預期 .devcontainer/Dockerfile
在進行建置時會出現錯誤!
你可以試著將 .devcontainer
整個移除,然後用 git reset --hard
重新取出檔案。
-
執行 F1
> Remote-Containers: Reopen Folder Locally
重新回到 Windows 開啟此專案
-
執行 F1
> Rebuild Without Cache and Reopen in Container
重建 Dev Container 的容器映像
此時你就可以看到錯誤訊息了:
如果從 Terminal (終端機) 來看,你可以看到相對詳細的錯誤訊息:
發生問題的當下,第一時間並不是很好理解,但你現在應該很清楚是換行符號造成的錯誤!
正確的解決方案
這個問題最標準的解決方法如下:
-
執行 F1
> Remote-Containers: Reopen Folder Locally
重新回到 Windows 開啟此專案
-
在 .devcontainer/mssql/
目錄下建立一個 .gitattributes
檔案,並加入以下內容:
*.sh text eol=lf
如此一來,就可以確保該目錄下所有的 *.sh
檔案,在 Git 取出檔案時一定會使用 LF
當成換行字元,
-
刪除 .devcontainer/mssql/*.sh
檔案,並使用 git checkout
或 git reset --hard
重新取出檔案
# 一定要先刪除檔案
rm .devcontainer/mssql/*.sh
# 刪除後執行取出檔案作業
git checkout -- .devcontainer/mssql/*.sh
-
執行 F1
> Rebuild Without Cache and Reopen in Container
重建 Dev Container 的容器映像
這次應該又可以順利建立容器了! 👍
總結
這個問題其實不太會在 macOS 或 Linux 發生,只有 Windows 用戶會遇到這個狀況,這不經讓我懷疑,是不是微軟的工程師沒有人在用 Windows 了啊? 😅
我特別為了這個問題也到 microsoft/vscode-remote-release 開了一個 Issue #7030 追蹤此議題,我認為這個檔案只要從微軟的範本中加入,就不會對 Windows 用戶帶來困擾了!
不過,我還是覺得只要你跟我一樣是愛用 Windows 的開發人員,都應該理解 CRLF
與 LF
的差異,這樣日後在容器化的過程中,也會少踩一些地雷。
相關連結