محرك CI/CD قابل للبرمجة يشغل خطوط الأنابيب في حاويات باستخدام حزم تطوير Go أو Python أو TypeScript.
| أمر | وصف |
|---|
curl -fsSL https://dl.dagger.io/dagger/install.sh | sh | تثبيت Dagger CLI على Linux/macOS |
brew install dagger/tap/dagger | التثبيت على macOS باستخدام Homebrew |
choco install dagger | التثبيت على Windows باستخدام Chocolatey |
scoop install dagger | التثبيت على Windows باستخدام Scoop |
nix profile install nixpkgs#dagger | التثبيت على NixOS/باستخدام Nix |
dagger version | عرض إصدار Dagger المثبت |
| أمر | وصف |
|---|
dagger init | تهيئة وحدة Dagger جديدة |
dagger init --sdk=python | تهيئة الوحدة باستخدام حزمة تطوير Python |
dagger init --sdk=typescript | تهيئة الوحدة باستخدام حزمة تطوير TypeScript |
dagger init --sdk=go | تهيئة الوحدة باستخدام حزمة تطوير Go |
dagger init --name=my-pipeline | تهيئة الوحدة باسم مخصص |
dagger init --sdk=go --source=./ci | التهيئة في دليل فرعي |
| أمر | وصف |
|---|
dagger call | استدعاء دالة من الوحدة الحالية |
dagger call build | استدعاء دالة البناء |
dagger call test --source=. | استدعاء دالة الاختبار مع وسيط المصدر |
dagger call build --source=. export --path=./dist | البناء وتصدير المخرجات محلياً |
dagger call lint --source=. stdout | تشغيل فحص الكود وطباعة المخرجات |
dagger call publish --source=. --tag=v1.0 | استدعاء دالة النشر مع وسم |
dagger functions | عرض الدوال المتاحة في الوحدة |
dagger shell | فتح واجهة Dagger التفاعلية |
dagger query | تنفيذ استعلام GraphQL مباشر على المحرك |
dagger run cmd | تشغيل أمر داخل جلسة Dagger |
| أمر | وصف |
|---|
dagger login | المصادقة مع Dagger Cloud |
dagger logout | إزالة المصادقة |
dagger config | عرض إعدادات الوحدة |
dagger cloud traces | عرض تتبع خطوط الأنابيب في Dagger Cloud |
DAGGER_CLOUD_TOKEN=token dagger call build | التشغيل مع رمز Dagger Cloud |
| أمر | وصف |
|---|
dagger install github.com/org/repo | تثبيت وحدة تبعية |
dagger install github.com/org/repo@v1.0 | تثبيت إصدار محدد |
dagger install github.com/org/repo/subpath | تثبيت وحدة من دليل فرعي |
dagger develop | إنشاء/تحديث ربط حزمة التطوير |
dagger develop --sdk=python | تبديل الوحدة إلى حزمة تطوير مختلفة |
dagger mod sync | مزامنة تبعيات الوحدة |
dagger mod update | تحديث تبعيات الوحدة |
dagger publish ttl.sh/my-module | نشر الوحدة إلى السجل |
dagger publish ghcr.io/org/module:v1.0 | النشر إلى GitHub Container Registry |
| أمر | وصف |
|---|
func (m *MyModule) Build(ctx context.Context, src *dagger.Directory) *dagger.Container | تعريف دالة البناء |
dag.Container().From("golang:1.22") | إنشاء حاوية من صورة |
container.WithDirectory("/src", src) | تحميل دليل في الحاوية |
container.WithExec([]string{"go", "build", "."}) | تنفيذ أمر في الحاوية |
container.WithEnvVariable("KEY", "value") | تعيين متغير بيئي |
container.File("/app/binary") | الحصول على ملف من الحاوية |
container.Directory("/app/dist") | الحصول على دليل من الحاوية |
container.WithServiceBinding("db", dbSvc) | ربط تبعية خدمة |
container.WithWorkdir("/src") | تعيين دليل العمل |
container.WithUser("nonroot") | تعيين مستخدم الحاوية |
container.Publish(ctx, "ttl.sh/my-image") | نشر الحاوية كصورة |
container.Export(ctx, "./output.tar") | تصدير الحاوية كملف مضغوط |
package main
import (
"context"
"dagger/my-pipeline/internal/dagger"
)
type MyPipeline struct{}
func (m *MyPipeline) Build(ctx context.Context, src *dagger.Directory) *dagger.Container {
goCache := dag.CacheVolume("go-mod")
buildCache := dag.CacheVolume("go-build")
return dag.Container().
From("golang:1.22-alpine").
WithMountedCache("/go/pkg/mod", goCache).
WithMountedCache("/root/.cache/go-build", buildCache).
WithDirectory("/src", src).
WithWorkdir("/src").
WithExec([]string{"go", "mod", "download"}).
WithExec([]string{"go", "build", "-o", "/app/server", "./cmd/server"})
}
func (m *MyPipeline) Test(ctx context.Context, src *dagger.Directory) (string, error) {
return m.Build(ctx, src).
WithExec([]string{"go", "test", "-v", "./..."}).
Stdout(ctx)
}
func (m *MyPipeline) Publish(ctx context.Context, src *dagger.Directory, tag string) (string, error) {
build := m.Build(ctx, src)
return dag.Container().
From("cgr.dev/chainguard/static:latest").
WithFile("/app/server", build.File("/app/server")).
WithEntrypoint([]string{"/app/server"}).
Publish(ctx, "ttl.sh/my-app:"+tag)
}
| أمر | وصف |
|---|
@function | تزيين الدالة لعرضها في Dagger |
async def build(self, src: dagger.Directory) -> dagger.Container | تعريف دالة بناء غير متزامنة |
dag.container().from_("python:3.12") | إنشاء حاوية من صورة |
container.with_directory("/src", src) | تحميل دليل |
container.with_exec(["pip", "install", "-r", "requirements.txt"]) | تشغيل pip install |
await container.stdout() | الحصول على مخرجات الأمر |
container.with_workdir("/app") | تعيين دليل العمل |
container.with_user("nonroot") | تعيين مستخدم الحاوية |
await container.publish("ttl.sh/my-app") | نشر الحاوية كصورة |
import dagger
from dagger import dag, function, object_type
@object_type
class MyPipeline:
@function
async def build(self, src: dagger.Directory) -> dagger.Container:
pip_cache = dag.cache_volume("pip")
return (
dag.container()
.from_("python:3.12-slim")
.with_mounted_cache("/root/.cache/pip", pip_cache)
.with_directory("/app", src)
.with_workdir("/app")
.with_exec(["pip", "install", "--no-cache-dir", "-r", "requirements.txt"])
.with_exec(["pip", "install", "."])
)
@function
async def test(self, src: dagger.Directory) -> str:
return await (
self.build(src)
.with_exec(["pytest", "-v", "--tb=short"])
.stdout()
)
@function
async def lint(self, src: dagger.Directory) -> str:
return await (
self.build(src)
.with_exec(["ruff", "check", "."])
.stdout()
)
@function
async def publish(self, src: dagger.Directory, tag: str) -> str:
build = self.build(src)
return await (
dag.container()
.from_("cgr.dev/chainguard/python:latest")
.with_directory("/app", build.directory("/app"))
.with_entrypoint(["python", "-m", "myapp"])
.publish(f"ttl.sh/my-python-app:{tag}")
)
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"
@object()
class MyPipeline {
@func()
build(src: Directory): Container {
const nodeCache = dag.cacheVolume("node-modules")
return dag
.container()
.from("node:20-slim")
.withMountedCache("/app/node_modules", nodeCache)
.withDirectory("/app", src)
.withWorkdir("/app")
.withExec(["npm", "ci"])
.withExec(["npm", "run", "build"])
}
@func()
async test(src: Directory): Promise<string> {
return this.build(src)
.withExec(["npm", "test"])
.stdout()
}
@func()
async lint(src: Directory): Promise<string> {
return this.build(src)
.withExec(["npm", "run", "lint"])
.stdout()
}
@func()
async publish(src: Directory, tag: string): Promise<string> {
return dag
.container()
.from("cgr.dev/chainguard/node:latest")
.withDirectory("/app", this.build(src).directory("/app/dist"))
.withEntrypoint(["node", "/app/index.js"])
.publish(`ttl.sh/my-node-app:${tag}`)
}
}
| أمر | وصف |
|---|
container.WithMountedCache("/go/pkg", dag.CacheVolume("gomod")) | تحميل وحدة تخزين مؤقت دائمة (Go) |
container.with_mounted_cache("/root/.cache/pip", dag.cache_volume("pip")) | تحميل ذاكرة pip المؤقتة (Python) |
dag.CacheVolume("node-modules") | إنشاء وحدة تخزين مؤقت مسماة |
container.WithMountedCache("/root/.cache/go-build", dag.CacheVolume("gobuild")) | تخزين عناصر بناء Go مؤقتاً |
container.WithMountedCache("/root/.npm", dag.CacheVolume("npm")) | تخزين حزم npm مؤقتاً |
container.WithMountedCache("/root/.cargo/registry", dag.CacheVolume("cargo")) | تخزين تبعيات Rust مؤقتاً |
| أمر | وصف |
|---|
container.WithEnvVariable("CACHE_BUSTER", time.Now().String()) | إبطال التخزين المؤقت عند الحاجة |
dagger call --focus=false build | التشغيل بدون التركيز/التخزين المؤقت التلقائي |
container.WithoutEnvVariable("CACHE_BUSTER") | إزالة متغير إبطال التخزين المؤقت |
| أمر | وصف |
|---|
dag.SetSecret("token", os.Getenv("API_TOKEN")) | إنشاء سر من متغير بيئي |
container.WithSecretVariable("API_KEY", secret) | تعيين متغير بيئي من سر |
container.WithMountedSecret("/run/secrets/key", secret) | تحميل سر كملف |
dagger call deploy --token=env:API_TOKEN | تمرير سر من البيئة عبر واجهة سطر الأوامر |
dagger call deploy --token=file:./token.txt | تمرير سر من ملف عبر واجهة سطر الأوامر |
dagger call deploy --token=cmd:"vault read -field=token secret/app" | تمرير سر من أمر |
| أمر | وصف |
|---|
container.WithServiceBinding("db", dbSvc) | ربط خدمة بحاوية |
container.WithExposedPort(8080) | كشف منفذ من حاوية |
container.AsService() | تحويل حاوية إلى خدمة |
dag.Container().From("postgres:16").WithExposedPort(5432).AsService() | إنشاء خدمة قاعدة بيانات |
service.Start(ctx) | بدء خدمة بشكل صريح |
service.Stop(ctx) | إيقاف خدمة قيد التشغيل |
func (m *MyPipeline) IntegrationTest(ctx context.Context, src *dagger.Directory) (string, error) {
postgres := dag.Container().
From("postgres:16-alpine").
WithEnvVariable("POSTGRES_PASSWORD", "test").
WithEnvVariable("POSTGRES_DB", "testdb").
WithExposedPort(5432).
AsService()
redis := dag.Container().
From("redis:7-alpine").
WithExposedPort(6379).
AsService()
return m.Build(ctx, src).
WithServiceBinding("db", postgres).
WithServiceBinding("cache", redis).
WithEnvVariable("DATABASE_URL", "postgres://postgres:test@db:5432/testdb").
WithEnvVariable("REDIS_URL", "redis://cache:6379").
WithExec([]string{"go", "test", "-v", "-tags=integration", "./..."}).
Stdout(ctx)
}
func (m *MyPipeline) BuildProduction(ctx context.Context, src *dagger.Directory) *dagger.Container {
// المرحلة 1: بناء التبعيات
deps := dag.Container().
From("golang:1.22-alpine").
WithDirectory("/src", src, dagger.ContainerWithDirectoryOpts{
Include: []string{"go.mod", "go.sum"},
}).
WithWorkdir("/src").
WithMountedCache("/go/pkg/mod", dag.CacheVolume("gomod")).
WithExec([]string{"go", "mod", "download"})
// المرحلة 2: بناء الملف التنفيذي
build := deps.
WithDirectory("/src", src).
WithExec([]string{"go", "build", "-ldflags=-s -w", "-o", "/app/server", "./cmd/server"})
// المرحلة 3: بيئة تشغيل مصغرة
return dag.Container().
From("cgr.dev/chainguard/static:latest").
WithFile("/app/server", build.File("/app/server")).
WithExposedPort(8080).
WithEntrypoint([]string{"/app/server"})
}
name: CI with Dagger
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Dagger
run: |
curl -fsSL https://dl.dagger.io/dagger/install.sh | sh
sudo mv ./bin/dagger /usr/local/bin/
- name: Run pipeline
env:
DAGGER_CLOUD_TOKEN: ${{ secrets.DAGGER_CLOUD_TOKEN }}
run: |
dagger call build --source=.
dagger call test --source=.
dagger call lint --source=.
- name: Publish
if: github.ref == 'refs/heads/main'
run: |
dagger call publish --source=. --tag=${{ github.sha }}
stages:
- build
- test
variables:
DAGGER_CLOUD_TOKEN: $DAGGER_CLOUD_TOKEN
build:
stage: build
image: alpine:latest
before_script:
- apk add curl
- curl -fsSL https://dl.dagger.io/dagger/install.sh | sh
- export PATH=$PWD/bin:$PATH
script:
- dagger call build --source=.
- dagger call test --source=.
| أمر | وصف |
|---|
dagger call --debug build | التشغيل مع تسجيل التصحيح |
dagger call build --interactive | الدخول إلى واجهة تفاعلية عند الفشل |
container.WithExec(["sh", "-c", "cat /etc/os-release"]) | تصحيح محتويات الحاوية |
container.Terminal() | فتح طرفية في الحاوية (Go) |
export DAGGER_LOG_LEVEL=debug | تعيين التسجيل المفصل |
dagger query '{ container { from(address:"alpine") { exec(args:["ls"]) { stdout } } } }' | التصحيح باستخدام GraphQL مباشر |
dagger call build 2>&1 | tee dagger-output.log | حفظ المخرجات في ملف سجل |
container.WithExec(["ls", "-la", "/app"]) | عرض الملفات للتصحيح |
container.WithExec(["env"]) | طباعة المتغيرات البيئية |
-
استخدم وحدات التخزين المؤقت بكثرة — قم بتحميل ذاكرات مؤقتة لمديري الحزم (pip و npm و cargo ووحدات Go) لتقليل أوقات البناء بشكل كبير عبر التشغيلات.
-
اجعل الوحدات صغيرة وقابلة للتركيب — قسّم خطوط الأنابيب الكبيرة إلى دوال قابلة لإعادة الاستخدام تقوم كل منها بمهمة واحدة بشكل جيد، ثم اجمعها معاً.
-
استخدم الأسرار بدلاً من المتغيرات البيئية — لا تمرر القيم الحساسة كنصوص عادية أبداً؛ استخدم دائماً dag.SetSecret() أو مراجع الأسرار في واجهة سطر الأوامر.
-
ثبّت إصدارات الصور الأساسية — استخدم وسوماً محددة مثل golang:1.22 بدلاً من latest للحصول على بناءات قابلة لإعادة الإنتاج.
-
استفد من أنماط المراحل المتعددة — افصل تبعيات البناء عن بيئة التشغيل لإنتاج حاويات نهائية مصغرة.
-
استخدم ربط الخدمات لاختبارات التكامل — قم بتشغيل قواعد البيانات والذاكرات المؤقتة والتبعيات الأخرى كخدمات بدلاً من محاكاتها.
-
صدّر العناصر بشكل صريح — استخدم .Export() أو .Publish() لجعل المخرجات متاحة خارج محرك Dagger.
-
فعّل Dagger Cloud — اتصل بـ Dagger Cloud للتخزين المؤقت لخطوط الأنابيب والتتبع والتصحيح التعاوني.
-
استخدم --interactive للتصحيح — عند فشل خطوة، يضعك --interactive داخل الحاوية عند نقطة الفشل.
-
اختبر محلياً قبل CI — تعمل خطوط أنابيب Dagger بشكل متطابق على أجهزة الكمبيوتر المحمولة وفي CI، لذا تحقق من كل شيء محلياً أولاً.