慣性聚合 高效追蹤和閱讀你感興趣的部落格、新聞、科技資訊
閱讀原文 在慣性聚合中打開

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
IRIS 中的持續整合使用 Git 和 Jenkins
InterSystems · 2026-05-24 · via DEV Community

介紹

在醫療互通環境中,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 進行持續整合的小範例,但這可以擴展到某些可以通過配置合併等特性進行部署的配置。

試試看吧!