Table des matières
পড়ার সময় : 11 minutes

আপনি একটি Gradle প্লাগইন সংগ্রহস্থল ক্লোন করেন। আপনি একটি টার্মিনাল খুলেন। আপনি কি টাইপ করেন? পরে?./gradlew tasks, অবশ্যই. কিন্তু কোন ফোল্ডারে? রুট? সাবমডিউল? কি প্রথমে একটি README পড়া দরকার হচ্ছে বিল্ড করতে কীভাবে জানার জন্য? যদি আপনি হিচকিচ্ছেন, না কি…​ শুধুমাত্র একটি সেকেন্ড, প্রকল্পের আর্কিটেকচার ভাঙ্গে গেছে।

এখানে বলা যাচ্ছে যে আমি কিভাবে এই সমস্যাটি একবারেই সমাধান করেছি — এবং কেন প্যাটার্ন আজ আমার সব Gradle প্লাগনের স্বাক্ষর মধ্যে`foundry/public/`.

সমস্যা: তিনটি আর্কিটেকচার যা আমাকে সময় বर्बাদ করতে বाध्य

বর্তমান প্যাটার্নে সংযোজনের আগে, আমি তিনটি পদ্ধতির মধ্যে ভুলভুলি grades for organizing a Gradle plugin. Each had a disqualifying flaw.

বিকল্প ১ : আইসোলেটেড প্লাগইন (কukaanি না)

প্রকল্পটি শুধুমাত্র প্লাগইন ধারণ করে। ব্যাবহারের কোনো উদাহরণ নেই। না প্রকল্প যা এতে ব্যায়াম করে। এইটিকে পরীক্ষা করতে, একটি বাহ্যিক প্রকল্প তৈরি করতে হবে, সেখানে প্লাগইনকে মাধ্যমে রেফার`mavenLocal`অথবা একটি কমপজিট বিল্ড, এবং শুধু সেখানে পরীক্ষা কর যে এটি কাজ করে।

$ git clone mon-plugin
$ cd mon-plugin
$ ./gradlew build          # le plugin compile
$ # ... et maintenant ? comment je l'essaie ?

কনসুমশন উদাহরণহীন গ্রেডল প্লাগইন, একটি লাইব্রেরি মতো এकीকরণ পরীক্ষা। আপনি কখনও জানতে পারবেন না যে শেষ পরিবর্তন ব্যবহারকারীর অভিজ্ঞতা টুটিয়ে গেছে।

বিকল্প ২ : ক্লাসিক মোনোরেপো (include(":plugin"))

Gradle`init`উত্পন্ন করে`settings.gradle.kts`সাথে`include("plugin")`। রুট এবং সাব-মডিউল একই ডায়েমন এবং একই কনফিগারেশন শেয়어 করে, এই একই ক্যাটালগগুলো. ব্যাবহারিক, কিন্তু সংযুক্ত.

.
├── settings.gradle.kts   → include("plugin")
├── build.gradle.kts      → plugins { id("mon-plugin") }
├── plugin/
│   └── build.gradle.kts  → java-gradle-plugin
└── gradle/
    └── libs.versions.toml

কী সমস্যা:

  • রুটকরতে হবেসাব-মডিউলের মতো গ্র্যাডলের একই সংস্করণ থাকা

  • `libs.versions.toml`বাগশে — সাধারণ সংস্করণ ক্যাটালগ, নির্ভরতা

যারা একটি মডিউল থেকে অন্য মডিউলে পালাচ্ছেন।

  • প্লাগইনকে রুট থেকে স্বাধীনভাবে বিল্ড করা অসম্ভব।

  • CI-এর দুটি মডিউল তৈরি করতে হবে, যদিও শুধুমাত্র প্লাগইন পরিবর্তিত হয়েছে।

বিকল্প ৩ : কম্পোজিট বিল্ড (includeBuild())

আমরা দুটিকে আলাদা Gradle বিল্ডে ভাগ করে এবং এদেরকে সংযুক্ত করি includeBuild("mon-plugin")`মধ্যে`settings.gradle.kts.

এটি ইতিমধ্যে ভালো। প্লাগইনের বিল্ডটি পৃথক করা হয়েছে। কিন্তু ব্যবহারকারী এক্সটার্নাল বিল্ডকে স্পষ্টভাবে উল্লেখ করতে হবে — এবং আউটপুটের `./gradlew tasks`রুটের সঠিক কনফিগারেশনের উপর নির্ভর করে সম্প্রিত। ক্লোনিং নয় শূন্য কনফিগারেশন: জানা দরকার যে প্লাগইনটি একটি আলাদা ফোল্ডারে আছে, রুট এটি উল্লেখ করে, ইত্যাদি।

.
├── settings.gradle.kts   → includeBuild("plugin-build/")
├── build.gradle.kts      → plugins { id("mon-plugin") }
└── plugin-build/
    ├── settings.gradle.kts
    └── build.gradle.kts

আমি ভালো খোঁজছিলাম। খুব ভালো।

সমাধান : দুই Builds স্বাধীন, একটি ভোক্তা মূল

এখানে প্যাটার্নটি যা আমি শেষে গ্রহণ করলাম:

.
├── settings.gradle.kts          ← racine consommateur
├── build.gradle.kts              ← 3 lignes : apply plugin + dogfood
├── gradle/
│   └── libs.versions.toml        ← catalogue du consommateur
├── {name}-plugin/                ← BUILD INDÉPENDANT
│   ├── gradlew                    ← son propre wrapper
│   ├── settings.gradle.kts        ← rootProject.name = "{name}-plugin"
│   ├── build.gradle.kts           ← java-gradle-plugin, signing, publish
│   ├── gradle/
│   │   ├── libs.versions.toml     ← catalogue du plugin
│   │   └── wrapper/
│   ├── src/                       ← sources du plugin
│   ├── .agents/                   ← gouvernance agent
│   └── *.adoc                     ← AGENT, PROMPT_REPRISE, snapshot, etc.
└── site.yml / slides-context.yml / ...   ← configs dogfood

চabi:`{name}-plugin/`এটি একটি সম্পূর্ণ এবং স্বায়ত্ত Gradle প্রকল্প। তার নিজের র্যাপার, তার নিজের সেটিংস, তার নিজের ক্যাটালগের সংস্করণ। এটি নিজেই ক্লোন করে, বিল্ড করে, টেস্ট করে এবং প্রকাশ করে, রুট ছাড়া। তার অস্তিত্বে সতর্ক থাকুক।

রুট, সে, শুধুমাত্র একটি কাজ করে : প্লাগইন প্রয়োগ করে।

plugins {
    alias(libs.plugins.bakery)
}

repositories {
    mavenLocal()
    mavenCentral()
}

bakery { configPath = file("site.yml").absolutePath }

কেসের জন্য তিনটি লাইন`bakery-gradle`আর কিছু না. শূন্য`include(), শূন্য`includeBuild(), শূন্য উপ-প্রকল্প। একটি ক্লাসিক Gradle বিল্ড যেটি একটি প্লাগইন প্রয়োগ করে zoals যে কোনো কোনো ভোগকারী তা করবে?

কর্মপ্রবাহ

Failed to generate image: PlantUML preprocessing failed: [From <input> (line 32) ]

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12

left to right direction

package "মূল (ভোক্তা)" #CCFFCC {
    usecase "রিপোজিটরিটি ক্লোন করুন" as Clone
    usecase "./gradlew tasks" as Tasks
    usecase "./gradlew bake" as Dogfood
    note bottom of Dogfood
        Exerce le plugin
        Feedback immédiat
        Zéro config
    end note
}

package "{name}-plugin/ (স্বতন্ত্র বিল্ড)" #CCE5FF {
    usecase "./gradlew build" as BuildPlugin
    usecase "./gradlew publishToMavenLocal" as MavenLocal
    usecase "./gradlew test" as TestPlugin
    note bottom of BuildPlugin
        Cycle de vie isolé
        Tests unitaires + Cucumber
        CI dédiée possible
    end note
}

Clone -down-> Tasks
Tasks -down-> Dogfood
Dogfood ..> MavenLocal : "প্লাগ인의 উপর নির্ভর করে
স্থানীয়ভাবে প্রকাশিত"
^^^^^
 Syntax Error? (Assumed diagram type: component)

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12

left to right direction

package "মূল (ভোক্তা)" #CCFFCC {
    usecase "রিপোজিটরিটি ক্লোন করুন" as Clone
    usecase "./gradlew tasks" as Tasks
    usecase "./gradlew bake" as Dogfood
    note bottom of Dogfood
        Exerce le plugin
        Feedback immédiat
        Zéro config
    end note
}

package "{name}-plugin/ (স্বতন্ত্র বিল্ড)" #CCE5FF {
    usecase "./gradlew build" as BuildPlugin
    usecase "./gradlew publishToMavenLocal" as MavenLocal
    usecase "./gradlew test" as TestPlugin
    note bottom of BuildPlugin
        Cycle de vie isolé
        Tests unitaires + Cucumber
        CI dédiée possible
    end note
}

Clone -down-> Tasks
Tasks -down-> Dogfood
Dogfood ..> MavenLocal : "প্লাগ인의 উপর নির্ভর করে
স্থানীয়ভাবে প্রকাশিত"
BuildPlugin -up-> MavenLocal
TestPlugin -up-> BuildPlugin

@enduml

বাস্তব কর্মপ্রবাহ

# 1. Builder le plugin
$ cd codebase-plugin
$ ./gradlew publishToMavenLocal

# 2. L'exercer depuis la racine
$ cd ..
$ ./gradlew indexCodebase queryCodebase snapshot

# La boucle est fermée. Le plugin est testé dans des conditions
# réelles de consommation, par le projet même qui l'héberge.

এই আর্কিটেকচার কেন আমাকে জয় নিয়েছে

তিনটি সুবিধা, যেগুলো সম্মিলিত হলে, "duplication" -এর খরচের বদিলে:

১. নেটিভ ডগফিডিং, তৎক্ষণিক ফিডব্যাক

একটি Gradle প্লাগইনকে পরীক্ষা করার সবচেয়ে ভালো উপায় হলো এটি ব্যবহার করা। একটি মক করা ইউনিট টেস্ট নেই। একটি না`GradleRunner`একটি প্রকল্প সঙ্গে টেস্টের. একটি vrai বিল্ড যে প্লাগইনটি প্রয়োগ করে বাস্তব ফাইলগুলো।

$ git clone bakery-gradle
$ cd bakery-gradle
$ ./gradlew bake    # ← le plugin est exercé immédiatement

যদি প্লাগইনটি ভাঙ্গে, মূল বিল্ডটি এটি বলে। যেতে দরকার নেই বহিরাগত পরীক্ষা প্রকল্প খুঁজে বের করা। ডগফুডিং হল প্রথম নতুন অবদানকারী চালানো টাস্ক। এটি সর্বশেষ ধোঁয়া পরীক্ষা।

নিয়মটি সহজ: যদি মূলটি কম্পাইল হয় এবং`./gradlew tasks` আপনার প্লাগইনের কাজগুলি দেখানো হচ্ছে, প্লাগইনটি কার্যকর। কোন আশ্চর্য নয়। উৎপাদনে.

2. জিরো-কনফিগ ক্লোনিং

Un `git clone && ./gradlew tasks`এবং নতুন আসন্ন সমস্ত কিছু দেখে হাঁটা কোনো কনফিগার না করে। এ`build.gradle.kts`মূল আছে প্লাগইনের ব্যবহারের জীবিত ডকুমেন্টেশন। লে`site.yml`পাশে প্রত্যাশিত কনফিগারেশন দেখায়।

বিকল্পের সাথে তুলনা করুন: একটি তিন-অনুচ্ছেদ README যা বিল্ড কিভাবে প্লাগিন তৈরি করবেন এবং বিল্ড কিভাবে প্রজেক্ট তৈরি করবেন টেস্টের. একটি নতুন অবদানকারী README-কে ঝকঝক পড়ে, ভুল করে, একটি ইসু খুলে — Whereas তথ্য devenir চালনযোগ্য হওয়া

সর্বশক্তিশালী নথিকাঠামো সেটি নয় যা পড়া হয়। এটি সে যেচালায়. মূল বিল্ড হল ডকুমেন্টেশন প্লাগইনের এক্সিকিউটেবল

3. Constructions isolées, CI indépendants

প্লাগইনের নিজস্ব Gradle wrapper és নিজস্ব লাইফসাইকেল আছে, তাদের নিজস্ব পরীক্ষা। আপনি পারেন:

  • প্লাগইনে Gradle আপগ্রেড করুন, রুটকে ছুঁই না

  • প্লাগইনে একটি নির্ভরতা যোগ করুন যাতে তা রুটে প্রবেশ না করে

  • প্লাগইনটি ভাঙ্গে দিন, মূল বিল্ডকে প্রভাবিত না করেই (যতক্ষণ না আপনি

টুটি সংস্করণটি প্রকাশ করবেন না)

  • একটি CI থাকবে যা প্লাগইনকে বিল্ড/টেস্ট করে, আরেকটি যা এটি ব্যবহার করে

root — স্বতন্ত্রভাবে

.github/workflows/
├── test-plugin.yml      → codebase-plugin/.gradlew build
├── test-root.yml        → .gradlew tasks (vérifie que le plugin est consommable)
└── publish.yml          → codebase-plugin/.gradlew publish

সাব-ফোল্ডারের অ্যানাটমি `{name}-plugin/

আরো বিস্তারিতವಾಗಿ দেখা করা যাক, প্লাগইন ফোল্ডারের মধ্যে যা বাস করে :

codebase-plugin/
├── gradlew                           ← wrapper indépendant
├── settings.gradle.kts               ← @Suppress("UnstableApiUsage")
│                                       + foobar-resolver-convention
├── build.gradle.kts                  ← java-gradle-plugin + signing + publish
├── gradle/
│   ├── libs.versions.toml            ← catalogue complet (langchain4j, pgvector...)
│   └── wrapper/
├── buildSrc/                         ← classes utilitaires buildSrc
│   ├── build.gradle.kts
│   └── src/main/kotlin/
│       ├── codebase/                 ← CodebaseYmlAnonymizer, CodebaseConfiguration
│       ├── benchmark/                ← BenchmarkConfig, BenchmarkProtocol
│       ├── readme/                   ← ReadmeYmlAnonymizer
│       ├── site/                     ← SiteYmlAnonymizer
│       ├── slider/                   ← SliderYmlAnonymizer
│       └── snapshot/                 ← SnapshotManager
├── src/
│   ├── main/kotlin/codebase/
│   │   ├── CodebasePlugin.kt         ← class Plugin<Project>
│   │   ├── rag/                      ← pgvector, embedding, anonymization...
│   │   ├── benchmark/                ← BenchmarkRunner, export, comparison...
│   │   └── walker/                   ← WorkspaceWalker
│   └── test/
│       ├── kotlin/codebase/scenarios/ ← steps Cucumber
│       ├── features/                  ← .feature files
│       └── resources/datasets/        ← fixtures .adoc, .yml, .json
├── .agents/                          ← gouvernance agent (INDEX, SESSIONS, etc.)
├── AGENT.adoc                        ← règles agent
├── PROMPT_REPRISE.adoc              ← mission session
├── BACKLOG.adoc                      ← backlog produit
├── snapshot.adoc                     ← snapshot auto-généré du projet
└── embeds.yml                        ← config RAG embeds

সবকিছু এখানে আছে। মূল এবং সাবফোল্ডারের মধ্যে কোনো ছড়ানয় নেই। প্লাগইনে কাজ করার ডেভেলপার কখনও দরকার নেই প্রস্থান করা`codebase-plugin/`. ডেভেলপার যে প্লাগইন ব্যবহার করে শুধু মূলটি দেখে — এবং`build.gradle.kts`3 লাইনের তিনি তাকে যা জানার দরকার তা বলেন।

বার্সন ক্যাটালগ : দুটি পৃথক ফাইল

gradle/libs.versions.toml(মূল)

{name}-plugin/gradle/libs.versions.toml

নির্ভরতাগুলোর সংখ্যা

2-3 (plugin + readme সম্ভবত)

30+ (langchain4j, pgvector, cucumber…​)

ভুমিকা

প্লাগইন ব্যবহার করুন

বিল্ডার প্লাগইনটি

যে তা পড়ে

প্লাগইন ব্যবহারকারী

প্লাগইন ডেভেলপার

রুটের ইচ্ছামত সীমিত একটি ক্যাটালগ আছে। প্লাগইনের একটি ক্যাটালগ আছে। সম্পূর্ণ। বিলঙ্কন অসম্ভব : প্রতিটি বিল্ডের নিজস্ব স্কোপ আছে। নির্ভরতাগুলোর.

�যদি আপনি ইতিমধ্যে একটি নির্ভরতা সংঘর্ষকে ডিবাগ করতে এক ঘণ্টা খরচ করেছেন আপনার প্লাগইন এবং আপনার টেস্ট প্রকল্পের মধ্যে, আপনি তার মূল্য বুঝেন এই বিভাজন। স্বাধীন ক্যাটালগগুলো এই সমস্যা দূরে করে। निर्मাণে।

যা আমরা করি না

এই প্যাটার্নটি জাদুকরী নয়। এটি একটি সীমাবদ্ধতা প্রয়োগ করে যা আমি সে খুশি করে আমাকে কষ্ট দেয় :

রুট প্লাগইন তৈরি করে না। আপনাকে`publishToMavenLocal` ou রুট এটি ব্যবহার করতে পারার আগে একটি রিপোজিটরিতে স্থাপন করুন।

এটি একটি ন্যूनতম খরচ, এবং এটি সঠিক সীমাবদ্ধতা। রুট প্লাগইনকে বাহ্যিক ক্লায়েন্টের रूपে ব্যবহার করুন — Mavenের মাধ্যমে. সঠিক যেমন একটি তৃতীয় পক্ষের প্রকল্প করবে। যদি প্লাগইন প্রকাশযোগ্য না হয়, root আপনকে এই-та বলে অবিলম্বে।

# La seule "friction" du pattern
$ cd codebase-plugin && ./gradlew publishToMavenLocal && cd ..
$ ./gradlew tasks --group=codebase

তুলনা : তিনটি আর্কিটেকচার ডগফুডিং-এর মুখে

আলাদা প্লাগইন

Monorepo`include()`

সামগ্রিক`includeBuild()`

রুট + স্বাধীন প্লাগইন.

`git clone && gradlew tasks`প্লাগইনের কাজগুলো দেয়

�❌

(Empty)

✅

[No French text was provided for translation, so the output is empty.]

রুট থেকে স্বাধীন প্লাগিন তৈরি করুন

(Empty output)

❌

✅

✅

বিভক্ত সংস্করণ ক্যাটালগ

✅

�❌

✅

�✅

না`include()` ni includeBuild()

✅

�❌

�❌

সব ব্যাটিক কোড স্পান (…​) অপরিবর্তিত রাখুন — কখনো ব্যাটিক কন্টেন্ট, স্পেসিং বা পজিশন পরিবর্তন করবেন না।

ন্যাটিভ Dogfood কনফিগ ছাড়া

�❌

[No output]

⚠️

✅

CI plugin স্বাধীন

�✅

�❌

✅

�✅

রুট একটি বাস্তব ভোগের উদাহরণ।

❌

⚠️

✅

✅

ডান কলাম সকল বক্স চেক করে। এই কারণে আমি না আমি আরও পিছনে ফিরব।

ড্যাগ কনট্র্যাক্ট : একটি রুট বিল্ড কখনও না একটি সাবফোল্ডার থেকে

এই আর্কিটেকচার স্বাভাবিকভাবে DAG N0→N3-এ একত্রিত আমার workspsace- এর. প্যাটার্ন হলো: N2 প্লাগইন হুব নিজের নিজের নির্ভরতাগুলির, N3 রুট একটি টার্মিনাল যে হাবস প্রয়োগ করে

contrat dag architecture

Le codebase-gradle/build.gradle.kts`6 লাইন তৈরি করে. না`src/, না`buildSrc/, না`gradle/rag-bench.gradle.kts. শুধু plugins { alias(libs.plugins.codebase) }`এবং রিপোজিটরিগুলো। সব জটিলতা বাস করে`codebase-plugin/.

সারাংশ : প্রত্যাশিত ডুপ্লিকেশন যা সময় বাঁচায়

�যখন আমি এই কাঠামো কাউকে দেখাই, প্রথম প্রতিক্রিয়া সাধারণত : « কিন্তু তুমি দুটি আছে`gradlew`, ২`settings.gradle.kts`, দুই`libs.versions.toml`— এটা নকল!

হ্যাঁ। এবং না।

ডুপ্লিকেশন », মানে একই তথ্য দুটি স্থানেই পুনরাবৃত্তি করা। এখানে, দুটি বিভিন্ন ফাইল দুটি বিভিন্ন usagesকে ব্যবহৃত হয় : প্লাগিনের ক্যাটালগ (30+ বিল্ডারের জন্য নির্ভরতা) এবং ক্যাটালগ মূল থেকে (২-৩ নির্ভরতাগুলি খচ করতে)। প্লাগইনের র্যাপার (বিকাশের জন্য লকড সংস্করণ) এবং রুট র‍্যাপার (সম্ভবত ভিন্ন সংস্করণ, প্লাগইনের অনুশীলনের জন্য).

এটি ডুপ্লিকেশন নয়। এটি বিচ্ছেদের দায়িত্ব বিল্ড সিস্টেমে প্রয়োগ করা হয়েছে। প্রতিটি বিল্ড একটি কাজ করে, এবং শুধু এটাই। মূলটি ভোগ করে। প্লাগইনটি তৈরি হয়।

খরচ ? একটি অর্ডার`publishToMavenLocal`প্লাগিনের নির্মাণের মধ্যে এবং রুটের বিল্ড। লাভ? একটি স্থapat্য স্পষ্টতা যে অগ্রসর ডিবাগিং ঘন্টা সরিয়ে ফেলে।

এই প্যাটার্নটি প্রয়োগ করলেই`bakery-gradle`, plantuml-gradle, codebase-gradle, এবং অন্যান্য প্লাগইনের`foundry/public/, আমি নাই একটি টার্মিনাল খোলার সময় আমার একটি রিপোজিটরিতে আবার হিজ্জত করা হয়নি।এটি প্রথম প্রতিক্রিয়া —./gradlew tasks`— চল সবসময়, দে সবসময় ভালো কাজগুলো, এবং তাৎক্ষণে বলে যে সব ঠিক আছে।

এইই, ভাল আর্কিটেকচার। এটি README-এ পড়া যায় না। এটি টার্মিনালে দশ সেকেন্ডের কমে পরীক্ষিত হয়।

Articles connexes