介紹
在醫療互通環境中,InterSystems Health Connect 通常包含生產、商業流程、運作、服務、實用類別、例程以及其他 ObjectScript 艺術品等關鍵組件。傳統上,這些組件的許多部署都是手動完成的,通過複製類別、導入 XML 或使用管理入口的管理工具。
雖然這種方法在初始階段可能有效,但在專案成長後,當多個開發者並行工作,或需要在開發、整合、預生產和生產等環境中進行重複部署時,維護起來就變得困難。
一個更穩健的替代方案是在持續整合中整合Health Connect。 流程,使用 Git 作為來源代碼倉庫,並以 Jenkins 為部署協調器。
本篇文章的目標是展示一個實際方法來:
- 在 GitHub 上版本化 Health Connect 代碼。
- 只檢測自上次部署以來已修改的文件。
- 將這些文件複製到預備資料夾。
- 加載並編譯變更到一個 Health Connect 命名空間。
- 透過 SSH 從 Jenkins 遠端執行整個流程.
架構
以我們的範例為例,我們已設定以下元素:
健康實例的 IRIS
我在 AWS 需求上部署了 InterSystems IRIS for Health,使用 RHEL10,並且有自己的 Apache 伺服器,透過 HTTP 和 SSH 啟用連線。
為開發,我已將 Visual Studio Code 設定為在本地 IRIS 實例上運作,我將在這上面進行程式碼變更,然後上傳到 GitHub.
GitHub 儲存庫
我們選擇 GitHub 作為我們的版本控制系統,利用 Visual Studio Code 中提供的擴充功能。這將允許我們在必要時使用分支。
這個元素對 CI/CD 流程至關重要,因為這裡是我們獲取最新部署代碼的地方。
Jenkins
對於那些不熟悉 Jenkins 的朋友來說,它是一個開源的自動化伺服器,廣泛用於持續整合流程,因為它擁有多種插件,可以讓任務變得更容易。
Jenkins 拥有一個 Groovy 脚本工具,允許我們實現整合過程所需的步驟。在這個例子中,我們不會變得過於複雜。
整合程序
在本範例中,我們假設正在進行一個與 DEVELOPMENT 實例(部署在 AWS 伺服器上)的互操作性專案,我們希望將開發者對其本地實例所做的變更部署到其中進行測試。步驟大致如下:
- 開發者在其本地實例中實現功能。
- 開發者將變更上傳到 GitHub 倉儲庫的對應分支。
- 負責部署的人員存取 Jenkins 並啟動流程.
- Jenkins透過 SSH 連接到 DEVELOPMENT 伺服器.
- 伺服器上正在執行 Linux 腳本.
- 該腳本使用 git pull 從儲存庫下載最新變更.
- 此腳本識別新檔案或已修改的檔案,並將其複製到伺服器目錄。
- 當檔案識別完成後,腳本會呼叫第二個 ObjectScript 腳本。
- 第二個腳本會載入並編譯檔案至 IRIS for Health 實例。
- 如果上傳成功,腳本會重新啟動生產。
如您所見,我們選擇了一個非常基本的操作,但這個操作卻非常有幫助。
現在我們來看看會使用 Jenkins 在我們的 DEVELOPMENT 伺服器上執行的腳本:
#!/usr/bin/env bash
set -euo pipefail
=========================
Configuration
=========================
REPO_URL="https://github.com/intersystems-ib/workshop-cicd-demo"
BRANCH="main"
Local clone used to compare commits
CACHE_REPO="/opt/git-cache/project_repo"
Folder to copy the files to be uploaded into Health Connect
EXPORT_DIR="/projectGit"
File with the latest processed commit
STATE_FILE="${CACHE_REPO}/.last_sync_commit"
CLean up EXPORT_DIR before to copy the new updates
CLEAN_EXPORT_DIR="true"
=========================
Validations
=========================
if ! command -v git >/dev/null 2>&1; then
echo "Error: git is not installed."
exit 1
fi
mkdir -p "${EXPORT_DIR}"
mkdir -p "$(dirname "${CACHE_REPO}")"
=========================
Clone or update cache folder
=========================
if [ ! -d "${CACHE_REPO}/.git" ]; then
echo "Cloning repository into cache..."
git clone --branch "${BRANCH}" "${REPO_URL}" "${CACHE_REPO}"
else
echo "Updating local cache..."
git -C "${CACHE_REPO}" fetch origin
git -C "${CACHE_REPO}" checkout "${BRANCH}"
git -C "${CACHE_REPO}" reset --hard "origin/${BRANCH}"
fi
REMOTE_COMMIT="$(git -C "${CACHE_REPO}" rev-parse HEAD)"
=========================
First execution
=========================
if [ ! -f "${STATE_FILE}" ]; then
echo "First execution."
echo "Copying all the contains from branch into ${EXPORT_DIR}..."
if [ "${CLEAN_EXPORT_DIR}" = "true" ]; then
find "${EXPORT_DIR}" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
fi
rsync -av --delete --exclude ".git" "${CACHE_REPO}/" "${EXPORT_DIR}/"
echo "${REMOTE_COMMIT}" > "${STATE_FILE}"
echo "First export finished."
exit 0
fi
LAST_COMMIT="$(cat "${STATE_FILE}")"
if [ "${LAST_COMMIT}" = "${REMOTE_COMMIT}" ]; then
echo "No updates."
exit 0
fi
echo "Comparing commits:"
echo " anterior: ${LAST_COMMIT}"
echo " actual: ${REMOTE_COMMIT}"
if [ "${CLEAN_EXPORT_DIR}" = "true" ]; then
echo "Cleaning up export folder..."
find "${EXPORT_DIR}" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
fi
=========================
Export just added or modified files
=========================
while IFS= read -r -d '' status && IFS= read -r -d '' path1; do
case "${status}" in
M|A)
echo "Exporting ${status}: ${path1}"
mkdir -p "${EXPORT_DIR}/$(dirname "${path1}")"
cp -f "${CACHE_REPO}/${path1}" "${EXPORT_DIR}/${path1}"
;;
D)
# Ignoring deletes
echo "Ignoring deleted: ${path1}"
;;
R*)
IFS= read -r -d '' path2
echo "Exporting renamed: ${path1} -> ${path2}"
mkdir -p "${EXPORT_DIR}/$(dirname "${path2}")"
cp -f "${CACHE_REPO}/${path2}" "${EXPORT_DIR}/${path2}"
;;
*)
echo "Change not automatically managed: ${status} ${path1}"
;;
esac
done < <(git -C "${CACHE_REPO}" diff --name-status -z "${LAST_COMMIT}" "${REMOTE_COMMIT}")
echo "${REMOTE_COMMIT}" > "${STATE_FILE}"
echo "Incremental export concluded in ${EXPORT_DIR}"
echo "Starting file upload and compile in Health Connect"
(echo '_system'; echo 'SYS'; cat iris.script) | iris session IRISHEALTH
echo "Compilation successfully finished"
如您所見,這個腳本會在我們的 GitHub 存放庫上執行 git pull,更新 DEVELOPMENT 伺服器上某個目錄的來源代碼,檢測與上次下載版本相比的變更,將它們提取到第二個目錄(/projectGit),最後呼叫 IRIS 腳本。
(echo '_system'; echo 'SYS'; cat iris.script) | iris session IRISHEALTH
頭兩個 回應 指令將允許我們將使用者名稱和密碼傳送至我們需要打開以執行我們的 ObjectScript 腳本:
zn "DEMO"
set sc = $SYSTEM.OBJ.LoadDir("/projectGit/src/Demo", "ck", , 1)
if '$SYSTEM.Status.IsOK(sc) do $SYSTEM.Status.DisplayError(sc) quit
set production = "Demo.Order.Production"
set ^Ens.Configuration("csp","LastProduction") = production
do ##class(Ens.Director).SetAutoStart(production)
do ##class(Ens.Director).StartProduction(production)
write !,"Produccion iniciada correctamente: ",production,!
這個腳本是哪裡我們導入我們已識別為修改或建立的類別並編譯它們。如果編譯成功,我們重新啟動我們 DEMO 命名空間的對應生產環境,以便實施變更。
太好了,我們有了腳本、開發伺服器以及 GitHub,讓我們設定我們的 Jenkins.
設定 Jenkins
在開始建立我們的管線之前,我們必須安裝一個外掛,它允許我們透過 SSH 連接到我們的開發伺服器,使用我們的主要用戶名和密碼.
從 Jenkins 的設定中,我們為開發伺服器創建了一個訪問憑據:
最後我們繼續建立 Pipeline
在 Pipeline 設定中,我們定義了以下腳本,它將允許我們部署:
pipeline {
agent any
parameters {
string(name: 'GIT_BRANCH', defaultValue: 'main', description: 'Repository branch')
string(name: 'REMOTE_HOST', defaultValue: 'ec2-**-**-***-**.**-*****.compute.amazonaws.com', description: 'Remote Host')
string(name: 'REMOTE_USER', defaultValue: 'ec2-user', description: 'Remote SSH user')
string(name: 'REMOTE_SCRIPT_NAME', defaultValue: 'shell_script.sh', description: 'Remote script name')
}
environment {
REPO_URL = 'https://github.com/intersystems-ib/workshop-cicd-demo'
SSH_CREDENTIALS_ID = 'ssh-healthconnect-remote'
}
stages {
stage('Checkout') {
steps {
git branch: "${params.GIT_BRANCH}", url: "${env.REPO_URL}"
}
}
stage('Validate script') {
steps {
sh '''
set -eu
test -f shell_script.sh
chmod +x shell_script.sh
'''
}
}
stage('Launch remote script') {
steps {
sshagent(credentials: ["${env.SSH_CREDENTIALS_ID}"]) {
sh '''
set -eu
ssh -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" \
"sudo sh '/${REMOTE_SCRIPT_NAME}'" | tee remote_execution.log
'''
}
}
}
}
post {
always {
archiveArtifacts artifacts: 'remote_execution.log', allowEmptyArchive: true
}
success {
echo 'Remote deployment successfully finished.'
}
failure {
echo 'Remote deployment failed. Check remote_execution.log.'
}
}
}
我們的腳本做什麼?非常簡單,它檢查我們的 GitHub 倉存是否存在及其相關分支,然後,透過 SSH,發送執行 Linux 腳本的指令,該腳本將負責下載和更新我們的實例。
我們用一個小例子來看看它的實際應用
運行該流程
我們的生產運行正常,我們想更改我們的其中一個組件,以便其中一個參數中顯示的默認值不同:

現在我們希望我們的 TenantId 參數需要設為 ZZZ-999,太好了,讓我們修正我們在 Visual Studio Code 的本地實例中的程式碼,並將變更上傳到我們的 GitHub.
當我們的變更現在已推送到我們的儲存庫時,我們可以從我們的 Jenkins 實例執行管線。讓我們看看管線的輸出:
一切都正確無誤;它已偵測到我們的變更並成功執行腳本。
讓我們確認參數已變更且生產已成功重新啟動.
我們就有我們新的 TenantId 了!完全且圓滿的成功!
結論與下一步.
你可能已注意到,IRIS 沒有任何技術限制參與持續整合流程。你只需要適合你日常操作的合適腳本。
在本篇文章中,我們看到了一個使用 IRIS for Health 進行持續整合的小範例,但這可以擴展到某些可以通過配置合併等特性進行部署的配置。
試試看吧!























