ساختن Workflow چندمرحلهای از فرایند دستی تا Workflow فرض کنید میخواهید یک برنامهٔ وب را deploy کنید. فرایند دستی شما این است:
بررسی کنید که کد جدید روی Git هست تستها را اجرا کنید اگر تستها موفق بودند، build بگیرید پیکربندی production را آماده کنید سرویس قدیمی را متوقف کنید فایلهای جدید را جایگزین کنید سرویس جدید را بالا بیاورید health check انجام دهید اگر سرویس سالم نیست، rollback کنید این یک workflow است — مجموعهای از مراحل مرتبط که باید بهترتیب اجرا شوند. حالا میخواهیم آن را خودکار کنیم.
اصول ساختن Workflow 1. هر مرحله یک وظیفهٔ مشخص بهتر است هر مرحله را در یک تابع یا اسکریپت جداگانه قرار دهید:
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 run_tests() {
5 echo "Running tests..."
6 cd /opt/myapp || exit 1
7 npm test
8 }
9
10 build_app() {
11 echo "Building application..."
12 cd /opt/myapp || exit 1
13 npm run build
14 }
15
16 deploy_app() {
17 echo "Deploying application..."
18 systemctl stop myapp
19 cp -r /opt/myapp/dist/* /var/www/html/
20 systemctl start myapp
21 }
22
23 health_check() {
24 echo "Running health check..."
25 sleep 3
26 curl -f http://localhost/ > /dev/null
27 } 2. مراحل باید مستقل باشند هر مرحله باید بتواند بهتنهایی اجرا شود و نتیجهٔ خودش را گزارش دهد:
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 check_dependencies() {
5 local missing = 0
6
7 for cmd in git npm systemctl curl; do
8 if ! command -v " $cmd " & >/dev/null; then
9 echo "ERROR: $cmd not found" >& 2
10 missing = 1
11 fi
12 done
13
14 return $missing
15 }
16
17 # میتوانید فقط این مرحله را تست کنید
18 if check_dependencies; then
19 echo "All dependencies present"
20 else
21 echo "Some dependencies are missing"
22 exit 1
23 fi 3. مدیریت وابستگی بین مراحل بعضی مراحل به خروجی مراحل قبلی نیاز دارند:
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 # مرحله 1: گرفتن آخرین کد
5 fetch_code() {
6 echo "Fetching latest code..."
7 cd /opt/myapp || return 1
8 git pull origin main
9 git rev-parse --short HEAD # خروجی: commit hash
10 }
11
12 # مرحله 2: ساختن image با commit hash
13 build_image() {
14 local commit_hash = " $1 "
15 echo "Building image with tag: $commit_hash "
16 docker build -t "myapp: $commit_hash " .
17 }
18
19 # اجرای workflow
20 main() {
21 local commit_hash
22
23 commit_hash = $( fetch_code) || {
24 echo "ERROR: Failed to fetch code" >& 2
25 exit 1
26 }
27
28 build_image " $commit_hash " || {
29 echo "ERROR: Failed to build image" >& 2
30 exit 1
31 }
32
33 echo "Build complete: myapp: $commit_hash "
34 }
35
36 main Workflow کامل: Deployment Pipeline یک مثال واقعیتر که این اصول را رعایت میکند:
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 readonly APP_DIR = "/opt/myapp"
5 readonly BACKUP_DIR = "/backups"
6 readonly LOG_FILE = "/var/log/deploy.log"
7
8 log() {
9 echo "[ $( date +'%Y-%m-%d %H:%M:%S' ) ] $* " | tee -a " $LOG_FILE "
10 }
11
12 create_backup() {
13 local backup_path = " $BACKUP_DIR /app- $( date +%s) "
14 log "Creating backup at $backup_path "
15
16 mkdir -p " $BACKUP_DIR "
17 cp -a " $APP_DIR " " $backup_path "
18 echo " $backup_path "
19 }
20
21 fetch_latest_code() {
22 log "Fetching latest code"
23 cd " $APP_DIR " || return 1
24
25 git fetch origin
26 local latest_commit
27 latest_commit = $( git rev-parse origin/main)
28
29 git checkout " $latest_commit "
30 echo " $latest_commit "
31 }
32
33 run_tests() {
34 log "Running tests"
35 cd " $APP_DIR " || return 1
36 npm test
37 }
38
39 build_application() {
40 log "Building application"
41 cd " $APP_DIR " || return 1
42 npm run build
43 }
44
45 deploy_new_version() {
46 log "Deploying new version"
47 systemctl stop myapp
48 systemctl start myapp
49 }
50
51 verify_deployment() {
52 log "Verifying deployment"
53 sleep 5
54
55 if systemctl is-active --quiet myapp && \
56 curl -f http://localhost:3000/health > /dev/null 2>& 1; then
57 return 0
58 else
59 return 1
60 fi
61 }
62
63 rollback() {
64 local backup_path = " $1 "
65 log "Rolling back to $backup_path "
66
67 systemctl stop myapp
68 rm -rf " $APP_DIR "
69 cp -a " $backup_path " " $APP_DIR "
70 systemctl start myapp
71 }
72
73 main() {
74 log "Starting deployment pipeline"
75
76 # مرحله 1: backup
77 local backup_path
78 backup_path = $( create_backup) || {
79 log "ERROR: Backup failed"
80 exit 1
81 }
82
83 # مرحله 2: fetch code
84 local commit_hash
85 commit_hash = $( fetch_latest_code) || {
86 log "ERROR: Failed to fetch code"
87 exit 1
88 }
89
90 log "Deploying commit: $commit_hash "
91
92 # مرحله 3: tests
93 if ! run_tests; then
94 log "ERROR: Tests failed"
95 exit 1
96 fi
97
98 # مرحله 4: build
99 if ! build_application; then
100 log "ERROR: Build failed"
101 exit 1
102 fi
103
104 # مرحله 5: deploy
105 if ! deploy_new_version; then
106 log "ERROR: Deployment failed, rolling back"
107 rollback " $backup_path "
108 exit 1
109 fi
110
111 # مرحله 6: verify
112 if ! verify_deployment; then
113 log "ERROR: Verification failed, rolling back"
114 rollback " $backup_path "
115 exit 1
116 fi
117
118 log "Deployment successful"
119 rm -rf " $backup_path "
120 }
121
122 main " $@ " Workflow قابل پیکربندی بهجای hardcode کردن مقادیر، از environment variable استفاده کنید:
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 # پیکربندی از محیط
5 readonly ENVIRONMENT = " ${ DEPLOY_ENV :- staging } "
6 readonly APP_DIR = " ${ APP_DIR :- /opt/myapp } "
7 readonly SKIP_TESTS = " ${ SKIP_TESTS :- false } "
8
9 log() {
10 echo "[ $( date +'%Y-%m-%d %H:%M:%S' ) ] [ $ENVIRONMENT ] $* "
11 }
12
13 main() {
14 log "Starting deployment to $ENVIRONMENT "
15
16 # اگر production است، tests را skip نکن
17 if [[ " $ENVIRONMENT " == "production" ]] ; then
18 SKIP_TESTS = false
19 fi
20
21 if [[ " $SKIP_TESTS " == "false" ]] ; then
22 run_tests || exit 1
23 else
24 log "Skipping tests (not recommended)"
25 fi
26
27 # بقیه workflow...
28 }
29
30 main " $@ " استفاده:
1 # برای staging
2 DEPLOY_ENV = staging ./deploy.sh
3
4 # برای production
5 DEPLOY_ENV = production ./deploy.sh
6
7 # برای development با skip tests (فقط برای تست محلی!)
8 DEPLOY_ENV = dev SKIP_TESTS = true ./deploy.shتصویرسازی Workflow مطابق با دانستههای فعلی، یک workflow خوب را میتوان بهصورت یک نمودار ساده تصور کرد:
1 ┌─────────────────┐
2 │ Create Backup │
3 └────────┬────────┘
4 │
5 ▼
6 ┌─────────────────┐
7 │ Fetch Code │
8 └────────┬────────┘
9 │
10 ▼
11 ┌─────────────────┐
12 │ Run Tests │────── fail ──┐
13 └────────┬────────┘ │
14 │ pass │
15 ▼ │
16 ┌─────────────────┐ │
17 │ Build │────── fail ───┤
18 └────────┬────────┘ │
19 │ success │
20 ▼ │
21 ┌─────────────────┐ │
22 │ Deploy │────── fail ───┤
23 └────────┬────────┘ │
24 │ success │
25 ▼ │
26 ┌─────────────────┐ │
27 │ Health Check │────── fail ───┤
28 └────────┬────────┘ │
29 │ pass │
30 ▼ │
31 ┌─────────────────┐ │
32 │ Success │ │
33 └─────────────────┘ │
34 │
35 ┌────────────────────────┘
36 │
37 ▼
38 ┌─────────────────┐
39 │ Rollback │
40 └─────────────────┘ هر مرحله میتواند موفق یا ناموفق باشد. اگر ناموفق شد، به مسیر rollback میرود.
منابع یادگیری مستندات و مقالات