تخطَّ إلى المحتوى

Dagger

محرك CI/CD قابل للبرمجة يشغل خطوط الأنابيب في حاويات باستخدام حزم تطوير Go أو Python أو TypeScript.

تثبيت واجهة سطر الأوامر

Section titled “تثبيت واجهة سطر الأوامر”
أمروصف
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التهيئة في دليل فرعي

أوامر واجهة سطر الأوامر

Section titled “أوامر واجهة سطر الأوامر”
أمروصف
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)
}

تطوير خطوط الأنابيب (Python)

Section titled “تطوير خطوط الأنابيب (Python)”
أمروصف
@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}")
        )

تطوير خطوط الأنابيب (TypeScript)

Section titled “تطوير خطوط الأنابيب (TypeScript)”

مثال على خط أنابيب TypeScript

Section titled “مثال على خط أنابيب TypeScript”
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)
}

نمط البناء متعدد المراحل

Section titled “نمط البناء متعدد المراحل”
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"])طباعة المتغيرات البيئية
  1. استخدم وحدات التخزين المؤقت بكثرة — قم بتحميل ذاكرات مؤقتة لمديري الحزم (pip و npm و cargo ووحدات Go) لتقليل أوقات البناء بشكل كبير عبر التشغيلات.

  2. اجعل الوحدات صغيرة وقابلة للتركيب — قسّم خطوط الأنابيب الكبيرة إلى دوال قابلة لإعادة الاستخدام تقوم كل منها بمهمة واحدة بشكل جيد، ثم اجمعها معاً.

  3. استخدم الأسرار بدلاً من المتغيرات البيئية — لا تمرر القيم الحساسة كنصوص عادية أبداً؛ استخدم دائماً dag.SetSecret() أو مراجع الأسرار في واجهة سطر الأوامر.

  4. ثبّت إصدارات الصور الأساسية — استخدم وسوماً محددة مثل golang:1.22 بدلاً من latest للحصول على بناءات قابلة لإعادة الإنتاج.

  5. استفد من أنماط المراحل المتعددة — افصل تبعيات البناء عن بيئة التشغيل لإنتاج حاويات نهائية مصغرة.

  6. استخدم ربط الخدمات لاختبارات التكامل — قم بتشغيل قواعد البيانات والذاكرات المؤقتة والتبعيات الأخرى كخدمات بدلاً من محاكاتها.

  7. صدّر العناصر بشكل صريح — استخدم .Export() أو .Publish() لجعل المخرجات متاحة خارج محرك Dagger.

  8. فعّل Dagger Cloud — اتصل بـ Dagger Cloud للتخزين المؤقت لخطوط الأنابيب والتتبع والتصحيح التعاوني.

  9. استخدم --interactive للتصحيح — عند فشل خطوة، يضعك --interactive داخل الحاوية عند نقطة الفشل.

  10. اختبر محلياً قبل CI — تعمل خطوط أنابيب Dagger بشكل متطابق على أجهزة الكمبيوتر المحمولة وفي CI، لذا تحقق من كل شيء محلياً أولاً.