پڑھنے کا وقت : 18 minutes

کیا آپ ڈاکر کمانڈز دستی طور پر ٹائپ کرتے ہیں جب کہ آپ سب کچھ گرایڈل ٹاسکوں میں کاپسل کر سکتے ہیں؟ یہاں وہ طریقہ جس نے میری ڈویل‑اولاما پرو کالنفیگ کو بیahrī شیل سکرپٹ کے بغیر صنعتی بنایا۔ اسٹارٹ، اسٹاپ، پہچان، سائن‑إن، پل — سب کچھ یہاں سے گزرتا ہے`./gradlew`.

تعارف

پچھلے مضامین میں، میں نے دکھایا کہ دو Ollama Pro اکاؤنٹس کو ایک ہی مشین پر ساتھ رہائش میں رکھنا کیسے کر سکتا ہوں، دوسری مثال کو ڈاکر کنٹینر میں الگ کر کے۔ سیٹ اپ کام کرتا ہے، لیکن روزمرہ کا انتظام جلد بھریلا ہو جاتا ہے: کنٹینر کا نام یاد رکھنا، مناسب کامینڈ چلانا`docker exec`, چیکر کون Sá porter ہے فاعل…​

کیا آپ وہ وقت جانتے ہیں جب آپ کے پاس 14 ٹرمینل کھلے ہوتے ہیں جو آپ ٹائپ کرتے ہیں`docker exec ollama-instance-b ollama list`چہارویں بار کے لیے، اور آپ خود سے کہتے ہیں «ضرور کوئی صاف ستھرہ طریقہ ہونا چاہیے»؟

یہ طریقہ، گریڈل ہے۔

صرف کوڈ بنانے کے لیے نہیں۔ Gradle، مقامی انفرastructure کے اورکِسٹریٹر جیسا کام کرتا ہے۔ اس کے کاموں میں فراہمی، تصدیق، ٹیسٹنگ اور صفائی شامل ہیں۔ سب کچھ ورژنڈ ایک میں`build.gradle.kts`جو اس کے پاس رہتا ہے`docker-compose.yml`.

@startuml
!theme plain

skinparam handwritten false
skinparam shadowing false
skinparam BoxPadding 8
skinparam DefaultFontSize 11
skinparam ArrowThickness 1.5

left to right direction

actor "ڈویلپر" as Dev

package "build.gradle.kts" as GradleFile {

  usecase "startOllamaB" as Start
  usecase "stopOllamaB" as Stop
  usecase "checkIdentity" as Check
  usecase "signInB" as SignIn
  usecase "pullModelB" as Pull
  usecase "testOllamaB" as Test
  usecase "statusOllamaAll" as Status
  usecase "cleanOllamaB" as Clean
}

package "ڈاکر ڈیمن (Docker Daemon)" as DockerDaemon {

  node "ollama-instance-b" as ContainerB {
    component ":11435" as Port
    database "~/ollama-b-data" as Volume
  }
}

package "اولاما نیٹو (Ollama Native)" as Native {
  component ":11434" as NatPort
}

Dev --> Start
Dev --> SignIn
Dev --> Pull
Dev --> Test
Dev --> Status
Dev --> Check
Dev --> Stop
Dev --> Clean

Start ..> ContainerB : docker run -d
Stop ..> ContainerB : docker stop
Check ..> Volume : cat id_ed25519.pub
SignIn ..> ContainerB : ollama signin
Pull ..> ContainerB : ollama pull
Test ..> ContainerB : curl /v1/chat/completions
Status ..> ContainerB : docker ps
Status ..> Native : curl :11434
Clean ..> ContainerB : docker rm -f

note bottom of GradleFile
کوئی بیرونی bash اسکرپٹ نہیں۔
سب کچھ `build.gradle.kts` میں ہے،
جو پروجیکٹ کے ساتھ ورژن شدہ ہے۔
end note

@enduml

انفراستrukچر کے لیے گریڈل کیوں؟

سوال جائز ہے۔ Gradle ایک بیلڈ ٹول ہے۔عام طور پر، یہ کامپائل کرتا ہے، ٹیسٹ کرتا ہے، پیجیکج کرتا ہے۔ یہ ڈاکر کنٹینرز نہیں چلاتا۔

مگر Gradle بھی انحصاری گراف انجن ہے — اور یہ وہ جگہ ہے جہاں یہ ہماری صورت‑حالت کے لیے دلچسپ بن جاتا ہے

چاہتے ہیں :

  1. کونٹینر سائن‑این سے پہلے چلا دیا جائے

  2. مодеل pull سے پہلے شناخت کی تصدیق کی جائے

  3. اگر کنٹینر ڈاؤن ہو جائے تو ایک API ٹیسٹ خاموشی سے ناکام ہو جاتا ہے

خالص انحصار گراف۔ Gradle اس کے لیے تیار کیا گیا ہے۔ Kotlin DSL کام لکھنے کو fluent بناتا ہے، اور`doLast`سِسٹم کے کمانڈز کو ایپلیکیشن کے کوڈ کے طور پر چلانے کی اجازت دیتا ہے۔

اور بالخصوص: bashrc کو براہ راست رکھنے کی ضرورت نہیں، اور اس میں بکھرے ہوئے سکرپٹ نہیں`~/bin`, ڈاکر-کامپوز کے ساتھ Makefile نہیں۔ سب چیز ایک ہی فائل میں ہے، projects کے کوڈ کے ساتھ ایک ہی جگہ پر۔

کامل build.gradle.kts

یہ وہ فائل ہے جو میں نے لکھنا ختم کر لیا۔ یہ تمام چیزیں کرتا ہے، وائلوم کی فراہمی سے curl کی تثبیت تک، انٹریکٹو سائن-إن کے ذریعے۔

import java.io.ByteArrayOutputStream

plugins {
    base
}

// -------------------------------------------------------------------------
// Configuration
// -------------------------------------------------------------------------
val instanceName   = "ollama-instance-b"
val instancePort   = 11435
val instanceImage  = "ollama/ollama:0.20.2"
val dataDir        = file("${System.getProperty("user.home")}/ollama-b-data")
val nativePort     = 11434

// Modèles à gérer — cloud Pro + un modèle local gratuit pour les tests
val proModels      = listOf("deepseek-v4-pro:cloud", "gemma4:31b-cloud")
val freeModels     = listOf("qwen3:0.6b")

// -------------------------------------------------------------------------
// Tâche préparatoire — création du volume si absent
// -------------------------------------------------------------------------
val prepareVolume by tasks.registering {
    group       = "ollama"
    description = "Crée le répertoire de volume pour l'instance B si absent"
    doLast {
        if (!dataDir.exists()) {
            dataDir.mkdirs()
            println("Volume créé : ${dataDir.absolutePath}")
        } else {
            println("Volume existant : ${dataDir.absolutePath}")
        }
    }
}

// -------------------------------------------------------------------------
// Démarrage du conteneur Docker
// -------------------------------------------------------------------------
val startOllamaB by tasks.registering(Exec::class) {
    group       = "ollama"
    description = "Lance le conteneur Docker pour l'instance Ollama B"
    dependsOn(prepareVolume)

    commandLine(
        "docker", "run", "-d",
        "--name", instanceName,
        "-p", "$instancePort:11434",
        "-v", "${dataDir.absolutePath}:/root/.ollama",
        "-e", "OLLAMA_HOST=0.0.0.0",
        "--restart", "always",
        instanceImage
    )
    isIgnoreExitValue = true

    doLast {
        val alreadyRunning = executionResult.get().exitValue != 0
        if (alreadyRunning) {
            logger.lifecycle("Le conteneur '$instanceName' existe déjà — tentative de démarrage")
        } else {
            logger.lifecycle("Conteneur '$instanceName' lancé")
        }
    }
}

// -------------------------------------------------------------------------
// Arrêt du conteneur
// -------------------------------------------------------------------------
val stopOllamaB by tasks.registering(Exec::class) {
    group       = "ollama"
    description = "Arrête le conteneur Docker de l'instance B"

    commandLine("docker", "stop", instanceName)
    isIgnoreExitValue = true

    doLast {
        logger.lifecycle("Conteneur '$instanceName' arrêté")
    }
}

// -------------------------------------------------------------------------
// Affichage de la clé publique (Device Key)
// -------------------------------------------------------------------------
val checkIdentity by tasks.registering(Exec::class) {
    group       = "ollama"
    description = "Affiche la clé publique SSH de l'instance B"
    dependsOn(startOllamaB)

    commandLine("docker", "exec", instanceName, "cat", "/root/.ollama/id_ed25519.pub")
    standardOutput = ByteArrayOutputStream()

    doLast {
        val pubKey = standardOutput.toString().trim()
        println("=".repeat(60))
        println("Device Key (Instance B)")
        println("=".repeat(60))
        println(pubKey)
        println("=".repeat(60))
        println("→ Enregistre cette clé sur https://ollama.com/settings/keys")
        println("→ Puis exécute : ./gradlew signInB")
        println("=".repeat(60))
    }
}

// -------------------------------------------------------------------------
// Sign-in interactif (Ouvrira le navigateur)
// -------------------------------------------------------------------------
val signInB by tasks.registering(Exec::class) {
    group       = "ollama"
    description = "Lance l'authentification interactive Ollama pour le compte B"
    dependsOn(startOllamaB)

    standardInput  = System.`in`
    standardOutput = System.out
    errorOutput    = System.err

    commandLine("docker", "exec", "-it", instanceName, "ollama", "signin")

    doFirst {
        logger.lifecycle("Authentification pour le Compte Pro B...")
        logger.lifecycle("Ouvre ton navigateur et connecte-toi avec l'email du compte B")
    }
}

// -------------------------------------------------------------------------
// Pull d'un modèle (paramétrable via propriété Gradle)
// -------------------------------------------------------------------------
val pullModelB by tasks.registering(Exec::class) {
    group       = "ollama"
    description = "Pull un modèle sur l'instance B. Usage : ./gradlew pullModelB -Pmodel=nom:tag"
    dependsOn(startOllamaB)

    val modelName = project.findProperty("model") as? String ?: "qwen3:0.6b"

    commandLine("docker", "exec", instanceName, "ollama", "pull", modelName)

    doFirst {
        logger.lifecycle("Pull du modèle '$modelName' sur l'instance B...")
    }

    doLast {
        logger.lifecycle("Modèle '$modelName' pullé avec succès")
    }
}

// -------------------------------------------------------------------------
// Pull de tous les modèles Pro (batch)
// -------------------------------------------------------------------------
val pullAllProModels by tasks.registering {
    group       = "ollama"
    description = "Pull tous les modèles cloud Pro sur l'instance B"
    dependsOn(startOllamaB)

    doLast {
        proModels.forEach { model ->
            logger.lifecycle("→ Pull $model...")
            val process = ProcessBuilder("docker", "exec", instanceName, "ollama", "pull", model)
                .inheritIO()
                .start()
            process.waitFor()
        }
        logger.lifecycle("Tous les modèles Pro pullés")
    }
}

// -------------------------------------------------------------------------
// Pull de tous les modèles gratuits (batch / première install)
// -------------------------------------------------------------------------
val pullAllFreeModels by tasks.registering {
    group       = "ollama"
    description = "Pull tous les modèles locaux gratuits sur l'instance B"
    dependsOn(startOllamaB)

    doLast {
        freeModels.forEach { model ->
            logger.lifecycle("→ Pull $model...")
            val process = ProcessBuilder("docker", "exec", instanceName, "ollama", "pull", model)
                .inheritIO()
                .start()
            process.waitFor()
        }
        logger.lifecycle("Tous les modèles gratuits pullés")
    }
}

// -------------------------------------------------------------------------
// Liste des modèles disponibles sur l'instance B
// -------------------------------------------------------------------------
val listModelsB by tasks.registering(Exec::class) {
    group       = "ollama"
    description = "Liste les modèles disponibles sur l'instance B"
    dependsOn(startOllamaB)

    commandLine("docker", "exec", instanceName, "ollama", "list")
}

// -------------------------------------------------------------------------
// Health check — test API sur les deux instances
// -------------------------------------------------------------------------
val testOllamaB by tasks.registering {
    group       = "ollama"
    description = "Teste la connectivité API de l'instance B avec un simple chat"
    dependsOn(startOllamaB)

    doLast {
        val model = project.findProperty("model") as? String ?: "qwen3:0.6b"
        logger.lifecycle("Test de l'instance B avec le modèle '$model'...")

        val payload = """
            {
              "model": "$model",
              "messages": [{"role": "user", "content": "Réponds uniquement par OK"}],
              "max_tokens": 5
            }
        """.trimIndent()

        val process = ProcessBuilder(
            "curl", "-s", "http://localhost:$instancePort/v1/chat/completions",
            "-H", "Content-Type: application/json",
            "-d", payload
        ).start()

        val response = process.inputStream.bufferedReader().readText()
        process.waitFor()

        if (response.contains("\"id\":\"chatcmpl") || response.contains("\"choices\"")) {
            println("✓ Instance B OK — port $instancePort répond")
            println("  Response: ${response.take(120)}...")
        } else {
            println("✗ Instance B INACTIVE — vérifie le conteneur avec './gradlew statusOllamaAll'")
            println("  Raw: $response")
        }
    }
}

// -------------------------------------------------------------------------
// Health check sur les deux instances simultanément
// -------------------------------------------------------------------------
val statusOllamaAll by tasks.registering {
    group       = "ollama"
    description = "Vérifie le statut des deux instances Ollama (native + Docker)"

    doLast {
        // Instance native
        runCatching {
            val process = ProcessBuilder(
                "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
                "http://localhost:$nativePort/api/tags"
            ).start()
            val code = process.inputStream.bufferedReader().readText().trim()
            process.waitFor()
            println(if (code == "200") "✓ Instance Native   — port $nativePort OK (HTTP $code)"
                    else "✗ Instance Native — port $nativePort (HTTP $code)")
        }.getOrElse {
            println("✗ Instance Native — injoignable sur le port $nativePort")
        }

        // Instance Docker
        runCatching {
            val process = ProcessBuilder(
                "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
                "http://localhost:$instancePort/api/tags"
            ).start()
            val code = process.inputStream.bufferedReader().readText().trim()
            process.waitFor()
            println(if (code == "200") "✓ Instance Docker B — port $instancePort OK (HTTP $code)"
                    else "✗ Instance Docker B — port $instancePort (HTTP $code)")
        }.getOrElse {
            println("✗ Instance Docker B — injoignable sur le port $instancePort")
        }

        // Info conteneur
        runCatching {
            val process = ProcessBuilder("docker", "ps", "--filter", "name=$instanceName",
                "--format", "table {{.Names}}\\t{{.Status}}\\t{{.Ports}}").start()
            val status = process.inputStream.bufferedReader().readText()
            process.waitFor()
            println()
            println("Conteneur Docker :")
            println(status)
        }
    }
}

// -------------------------------------------------------------------------
// Nettoyage complet
// -------------------------------------------------------------------------
val cleanOllamaB by tasks.registering(Exec::class) {
    group       = "ollama"
    description = "Arrête et supprime le conteneur Docker de l'instance B"
    dependsOn(stopOllamaB)

    commandLine("docker", "rm", instanceName)
    isIgnoreExitValue = true

    doLast {
        logger.lifecycle("Conteneur '$instanceName' supprimé")
        logger.lifecycle("Le volume ${dataDir.absolutePath} est conservé (identité persistante)")
    }
}

// -------------------------------------------------------------------------
// Affichage des instructions
// -------------------------------------------------------------------------
val helpOllama by tasks.registering {
    group       = "ollama"
    description = "Affiche l'aide des tâches Ollama disponibles"

    doLast {
        println("""
        ╔══════════════════════════════════════════════════════════════╗
        ║         Tâches Gradle — Pilotage Dual Ollama Pro            ║
        ╠══════════════════════════════════════════════════════════════╣
        ║  ./gradlew startOllamaB      Démarrer l'instance Docker B   ║
        ║  ./gradlew stopOllamaB       Arrêter l'instance Docker B    ║
        ║  ./gradlew checkIdentity     Afficher la clé SSH publique   ║
        ║  ./gradlew signInB           Sign-in interactif compte B    ║
        ║  ./gradlew pullModelB        Pull un modèle (-Pmodel=...)   ║
        ║  ./gradlew pullAllProModels  Pull tous les modèles Pro      ║
        ║  ./gradlew pullAllFreeModels Pull tous les modèles gratuits ║
        ║  ./gradlew listModelsB       Lister les modèles instance B  ║
        ║  ./gradlew testOllamaB       Test API avec un chat simple   ║
        ║  ./gradlew statusOllamaAll   Santé des 2 instances          ║
        ║  ./gradlew cleanOllamaB      Supprimer le conteneur         ║
        ║  ./gradlew helpOllama        Cette aide                     ║
        ╚══════════════════════════════════════════════════════════════╝
        """.trimIndent())
    }
}

یہ ہے۔ بارہ کام، ایک منفرد داخل کرنے کی نقطہ، اور بے رکاوٹ۔

مهم کاموں کی تشریح

startOllamaB` — لانچر

کسی جادو نہیں : ایک`docker run -d`درست پیرامیٹرز کے ساتھ. چال اندر ہے`isIgnoreExitValue = true`.

isIgnoreExitValue = true

اگر کونٹینر پہلے سے موجود ہے، کمانڈ`docker run`یہ (exit code ≠ 0) میں ناکام ہو جاتا ہے۔ اس خاصیت کے بغیر، Gradle پوری بنائندگی (build) کو غلطی پر رک دے گا۔ یہاں، ناکامی خاموش ہے — اور`doLast`پات لگتا ہے کہ آیا یہ ایک 'پہلے سے موجود' تھا یا ایک سچئی غلطی۔

Le `doLast`ضروری ہے : یہ après کمانڈ کے تنفیذ کے بعد چلتا ہے، جس سے آپ کا تفتیش کرنے کی امکانیت ہوتی ہے`executionResult.get().exitValue`اور مناسب پیغام لاگ کریں۔

signInB` — انٹرایکٹ

یہ صرف ایک کام ہے جسے انسانی تعامل کی ضرورت ہوتی ہے۔ Gradle منتقل کرتا ہے۔`System.in`پروسیس میں پرومپٹ کے لیے`ollama signin`نورمال طور پر دکھایا جا سکتا ہے:

standardInput  = System.`in`
standardOutput = System.out
errorOutput    = System.err

بina اس کے، sign-in بلاک ہو جائے گا کیونکہ stdin بند ہو جائے گا اور آپ کبھی بھی پرامپٹ نہیں دیکھیں گے۔

pullModelB` — قابلِ تنظیم

ایک عام کام جو ایک پیرامیٹر کے ذریعے قبول کرتا ہے`-P`:

./gradlew pullModelB -Pmodel=deepseek-v4-pro:cloud
./gradlew pullModelB                           # fallback : qwen3:0.6b

fallback اہم ہے :`project.findProperty("model") as? String ?: "qwen3:0.6b"`یہ یقین دلاتا ہے کہ کام پھٹے گا نہیں اگر ہم پیرامیٹر بھول جائیں۔

statusOllamaAll` — تشخیص

دو`curl`بندرگاہوں پر صحت`11434` et 11435, ایک بیکاپ میں`runCatching`ایک ان اسٹینس ڈاؤن ہونے پر کراشر ہونے سے بچنے کے لیے نتیجہ منظم ہے :

✓ Instance Native   — port 11434 OK (HTTP 200)
✓ Instance Docker B — port 11435 OK (HTTP 200)

Conteneur Docker :
NAMES               STATUS         PORTS
ollama-instance-b   Up 3 hours     0.0.0.0:11435->11434/tcp

ایک نگاه، اور تم بالکل جان لیتے ہو کہ کون سا چلتا ہے اور کون سا غلط ہے۔

cleanOllamaB` — جِس صفائی خود شناخت کو محفوظ رکھتی ہے

commandLine("docker", "rm", instanceName)
isIgnoreExitValue = true

کنٹینر حذف کر دیا گیا، لیکن حجم`~/ollama-b-data`ہےمحفوظ. SSH کی پہچان صافائی کے بعد بھی برقرار رہتی ہے. اگلا`startOllamaB`, اس سے مطلب یہ ہے کہ ویسی ہی کلیدیں دوبارہ استعمال کی جائیں گی — سائن ان کرنے یا ڈیوائس کی دوبارہ ریکارڈ کرنے کی ضرورت نہیں ہے۔

cleanOllamaB`آواز کو مت چھوئے۔ اگر آپ ب کی شناخت حذف کرنا چاہتے ہیں ( اور صفر سے نئی شروع کرنا ) تو آپ کو حذف کرنا ہوگا~/ollama-b-data`دستی. یہ اختیاری — ایک`clean`credentials کو تَراشنا نہیں چاہیے.

مکمل ورک فلور — صفر سے دو جلسات تک

یہ وہ ایدیل ترتیب ہے جس کے لئے کسی شخص کی ضرورت ہوتی ہے جو پروجيٽ پر آتا ہے اور کوئی دستاویز کھولے بغیر سب چیزیں تیار کرنا چاہتا ہے:

# 1. Premier contact — que faire ?
$ ./gradlew helpOllama

# 2. On démarre l'instance B
$ ./gradlew startOllamaB

# 3. On récupère la Device Key et on l'enregistre sur ollama.com
$ ./gradlew checkIdentity
# → copier-coller de la clé sur https://ollama.com/settings/keys

# 4. Authentification interactive
$ ./gradlew signInB

# 5. Pull d'un petit modèle gratuit pour tester la plomberie
$ ./gradlew pullModelB

# 6. Test API
$ ./gradlew testOllamaB

# 7. Si tout est vert, pull des modèles Pro
$ ./gradlew pullAllProModels

# 8. Vérification finale des deux instances
$ ./gradlew statusOllamaAll

دو منٹ سے کم میں، سب چیز فراہم کر دی گئی ہے۔ کوئی README پڑھنے کی ضرورت نہیں، کوئی ماحولیاتی متغیرات سیٹ کرنے کی ضرورت نہیں۔ کار`helpOllama`یہ ملحق دستاویزات کے طور پر کام کرتا ہے۔

@startuml
!theme plain
skinparam DefaultFontSize 11

actor Développeur as Dev
participant "Gradle\n(build.gradle.kts)" as Gradle
participant "Docker\nDaemon" as Docker
participant "Ollama\nInstance B" as OllamaB
participant "Ollama\nCloud" as Cloud

== Provisionnement ==
Dev -> Gradle : helpOllama
Gradle --> Dev : Aide et instructions

Dev -> Gradle : startOllamaB
Gradle -> Docker : docker run -d ollama/ollama:0.20.2
Docker -> OllamaB : Génération clés SSH (id_ed25519)
Docker --> Gradle : conteneur lancé
Gradle --> Dev : OK

Dev -> Gradle : checkIdentity
Gradle -> Docker : cat id_ed25519.pub
Gradle --> Dev : Clé publique SSH

Dev -> Cloud : Enregistrement clé SSH\n(sur ollama.com/settings/keys)

== Authentification ==
Dev -> Gradle : signInB
Gradle -> Docker : ollama signin (interactif)
Dev <-> Cloud : Login navigateur (compte B)
Docker -> Cloud : Échange token ⇔ Device Key
Gradle --> Dev : Authentifié

== Vérification ==
Dev -> Gradle : pullModelB
Gradle -> Docker : ollama pull qwen3:0.6b
Gradle --> Dev : Modèle pullé

Dev -> Gradle : testOllamaB
Gradle -> Docker : curl /v1/chat/completions
Gradle --> Dev : ✓ Instance B OK

== Production ==
Dev -> Gradle : pullAllProModels
Gradle -> Docker : ollama pull deepseek-v4-pro:cloud
Gradle -> Docker : ollama pull gemma4:31b-cloud
Gradle --> Dev : Modèles Pro pullés

Dev -> Gradle : statusOllamaAll
Gradle --> Dev : ✓ Native ✓ Docker B
@enduml

کیا ہم اسے واقعی گرینڈل پلاگَن نہیں بناتے؟

معتبر سوال. یہ نکالیں`build.gradle.kts`ایک خود‑مستقل گریڈیل پلاگین میں فوائد ہوں گے: پروژوں میں دوبارہ استعمال، اعلانیاتی تنظیم، DSL کا اضافہ۔

لیکن ابھی تک، اس کا کوئی معنی نہیں ہے۔ اس بِلڈ میں کوئی پیچیدہ کاروباری منطق نہیں ہے — یہ ڈاکر اور curl کی命令 کے ارد گرد ایک وریپر ہے۔ پوری گریڈل پلاگijn (extension، یونٹ ٹیسٹس، نشر کے ساتھ) کا اوورلیے 12 utility tasks کے لیے ناموزوں ہوگا۔

Le `build.gradle.kts`inline ضرورت والا پروジェکٹ میں رہتا ہے۔ یہ دوسری جگہ پر کاپی کرنا آسان ہے۔ اگر ایک دن مجھے تین پروジェکٹ مل جائیں جنہیں اس پر منحصَر ہوں، تو میں اسے پلاگین میں بدل دوں گا۔ حالانہ، KISS.

وہ دن جہاں تم تین رکھتے ہو`build.gradle.kts`تین پروژیکٹس میں مساوی ایک اشارہ ہے کہ پلاگن نکالنا چاہیے۔ اس سے پہلے، ڈپلیکیٹنگ abstraction سے کم خرچ والی ہوتی ہے۔ ڈویلپر کی حکمت۔

سیکھا گیا

  1. Gradle صرف ایک بِلڈ ٹول نہیں ہے— اعتمادی گراف انجن بنا دیتا ہے ایک خوفناک انفراسٹریکٹ اورکیسٹریٹر۔doLast, isIgnoreExitValue, dependsOn: ان primitives کافی ہیں تاکہ ایک ڈاکر کنٹینر کو سی‑آئی pipeline کے مطابق اعتماد کے ساتھ کنٹرول کیا جا سکے۔

  2. isIgnoreExitValue` تمہارا دوست ہے— مثالی دنیا میں,docker run`موجودہ کونٹینر پر warning دے گا، error کُوڈ exit کے بجائے۔ حقیقی دنیا میں، تم`isIgnoreExitValue = true`و تم تشخیص کا انتظام کر رہے ہو`doLast.

  3. پارامٹر ایبل کام ابھاؤتی کو قتل کر دیتے ہیں۔ — Un pullModelB`جینریک ساتھ-Pmodel=…​`یہ N مخصوص کاموں کی جگہ لیتا ہے۔ ایک ہی کام، ایک ہی doc، ایک ہی سلوک۔

  4. runCatching` صحت کے چیک کے لیے— جب آپ N endpoints ٹیسٹ کریں، پہلا کراش دوسرے کو ٹیسٹ کرنے سے نہ روکنا چاہیے`runCatching` + `getOrElse`ہر ایینڈ پوانٹ کے لیے، اور تشخیص مکمل رہتی ہے، جزوی خرابی کی صورت میں بھی۔

  5. کام helpOllama مثل مستندات قابل اجرا — Un `println()`بہتر شکل سے Formatted ایک تین پاراگرافوں کاREADME بدلتا ہے۔ یہ خود‑دستاویزی، ہمیشہ اپ‑to‑date، اور ٹرمنل سے باہر نکلے بغیر قابل رسائی ہے۔

نتیجہ

ہم ہاتھ سے ڈاکر کمانڈز ٹائپ کرنے سے ایک مکمل، ورژن شدہ، قابل استعمال گریڈیل ورک فلو تک پہنچ گئے ہیں۔ بارہ ٹاسک pure life cycle کو coverage کرتی ہیں: فراہمی، شناخت، سائن‑ان، پول، ٹیسٹ، تشخیص، صافکاری۔

ڈاکر‑کمپوز + build.gradle.kts + opencode.json ایک صاف trinitī بناتا ہے: ڈاکر Isolation کے لیے، Gradle Orchestration کے لیے، OpenCode Consumption کے لیے۔ ہر لايے کی اپنی ذمہ داری ہوتی ہے، اور کوئی بھی لايے دوسری لايے پر اوورلاں نہیں ہوتی۔

اگلا قدم؟ سی اِی میں عمر کو خودکار بنائیں تاکہ Ollama کے انスタンス مؤقّت dev کے ماحولیات میں دستیاب ہوں۔ مگر یہ ایک دیگر مضمون ہے۔

متعلقہ مضامین