UDE ile Git Repository Yapısı ve Metadata Yönetimi
UDE ile Git Repository Yapısı ve Metadata Yönetimi
Giriş
Serinin önceki bölümlerinde UDE ortamımızı oluşturduk, gerekli araçları kurduk, Visual Studio’yu yapılandırdık ve ilk X++ geliştirmemizi yaptık.
Buraya kadar daha çok geliştirme ortamını ayağa kaldırmaya odaklandık. Gerçek bir projede ise kodu yazmak işin sadece bir kısmı. Kodun ekip içinde nasıl paylaşılacağı, hangi değişikliğin kim tarafından yapıldığı, gerektiğinde eski bir sürüme nasıl dönüleceği ve build sürecinin hangi kaynaktan besleneceği de en az geliştirme kadar önemli.
Tam bu noktada Git ve Azure DevOps Repository yapısı devreye giriyor.
UDE tarafında Git kullanımı ayrıca önemli. Çünkü custom metadata artık lokal makinemizde duruyor ve birden fazla geliştirici aynı UDE ortamına bağlanabiliyor. Bu nedenle kaynak kodun tek ve izlenebilir bir referansta tutulması gerekiyor.
Bu yazıda ağırlıklı olarak şu konulara bakacağız:
- Custom metadata’nın repository içinde nerede tutulacağı
- Repository klasör yapısının nasıl kurgulanabileceği
- Metadata, Visual Studio projeleri, build dosyaları ve .gitignore yaklaşımı
- Projenin büyüklüğüne göre branch yapısı seçenekleri
- Pull Request, branch policy ve hotfix yaklaşımı
- Bir geliştiricinin günlük Git akışı ve metadata conflict’leri
UDE ile Birlikte Değişen Metadata Yaklaşımı
Klasik Dynamics 365 Finance & Operations development VM’lerinde Microsoft standart metadata’sı ile bizim geliştirdiğimiz custom metadata aynı PackagesLocalDirectory yapısı altında duruyordu.
Örneğin yıllardır görmeye alıştığımız klasör şuydu:
Bu klasörün içinde Microsoft paketleri, ISV çözümleri ve bizim custom modellerimiz yan yana bulunabiliyordu.
UDE ile birlikte bu konu daha net ayrılıyor.
Visual Studio’daki metadata configuration içinde iki ayrı konum tanımlıyoruz:
- Folder for your own custom metadata
- Folders for reference metadata
İlk alan kendi geliştirdiğimiz X++ modellerinin bulunduğu klasörü, ikinci alan ise Microsoft standart metadata’sını ve varsa diğer referans modelleri gösteriyor.
Bana göre UDE’nin en faydalı taraflarından biri de bu ayrım.
Pratikte bize şu avantajları sağlıyor:
- Microsoft standart metadata’sı repository’nin parçası olmuyor.
- Repository’yi yalnızca bize ait custom kod ve proje dosyalarıyla sınırlayabiliyoruz.
- Branch değiştirme ve kod senkronizasyonu daha temiz hale geliyor.
- Microsoft güncellemeleri ile custom kod birbirinden daha net ayrılıyor.
- Build pipeline’ın hangi dosyaları kullanacağı daha kontrollü hale geliyor.
Benim burada kullandığım temel kural şu:
Önerilen Yerel Klasör Yapısı
Önceki bölümlerde custom metadata için aşağıdaki klasörü kullanmıştık:
İstersek bu klasörü doğrudan repository kökü olarak da kullanabiliriz. Ben biraz daha düzenli olması için repository kökü ile asıl metadata klasörünü ayırmayı tercih ediyorum.
Örneğin şöyle bir yapı kullanılabilir:
C:\CustomXppMetadata
│
├── Metadata
├── Projects
├── Build
├── Scripts
├── Documentation
├── .gitignore
└── README.md
Visual Studio’daki Folder for your own custom metadata alanını da aşağıdaki klasöre yönlendirebiliriz:
Böylece repository kökü ile Visual Studio’nun çalıştığı custom metadata klasörü birbirinden ayrılmış olur.
Klasörler Ne İçin Kullanılıyor?
Metadata
Bu klasörde geliştirdiğimiz Dynamics 365 F&O package ve modelleri bulunur.
Örneğin:
Metadata
│
└── DMRCustomizations
│
├── Descriptor
│ └── DMRCustomizations.xml
│
└── DMRCustomizations
├── AxClass
├── AxTable
├── AxForm
├── AxMenuItemDisplay
├── AxSecurityPrivilege
└── AxLabelFile
Burada ilk DMRCustomizations klasörü package yapısını, içteki DMRCustomizations klasörü ise model metadata’sını temsil ediyor.
Özellikle Descriptor klasörünü unutmamak gerekiyor. Modelin adı, publisher bilgisi, layer ve bağımlılıkları gibi tanımlar burada tutuluyor ve build tarafında da bu bilgiye ihtiyaç duyuluyor.
Bu nedenle model metadata’sı ile descriptor dosyasını birlikte kaynak kod yönetimine almak önemli.
Projects
Bu klasörde Visual Studio solution ve X++ proje dosyalarını tutabiliriz.
Örneğin:
Projects
│
└── DMR_FD_AITest1
├── DMR_FD_AITest1.sln
└── DMR_FD_AITest1.rnrproj
X++ kaynak kodunun asıl karşılığı Metadata altındaki XML dosyalarıdır. Buna rağmen solution ve .rnrproj dosyalarını da repository’de tutmak ekip çalışmasını ve build pipeline kurulumunu ciddi şekilde kolaylaştırıyor.
Yeni bir geliştirici repository’yi aldığında aynı solution yapısıyla doğrudan çalışmaya başlayabiliyor.
Ayrıca otomatik build tarafında build edilecek package’lar için ilgili .rnrproj dosyalarının repository’de bulunması gerekiyor.
Build
Azure DevOps pipeline ve build yapılandırma dosyalarını bu klasörde tutabiliriz.
Örneğin:
Build
├── azure-pipelines.yml
├── packages.config
└── nuget.config
Bir sonraki bölümde build pipeline’ı kurarken bu klasörü zaten daha detaylı kullanacağız.
Scripts
Repository kurulumu, yardımcı geliştirme işlemleri veya build adımları için kullandığımız PowerShell scriptlerini burada tutabiliriz.
Scripts
├── Initialize-DeveloperEnvironment.ps1
├── Update-Metadata.ps1
└── Build-Model.ps1
Documentation
Projeye ait teknik notlar, geliştirme standartları ve mimari kararlar için ayrı bir Documentation klasörü oldukça işe yarıyor.
Documentation
├── DevelopmentStandards.md
├── BranchStrategy.md
└── DeploymentProcess.md
README.md
README.md benim için repository’nin giriş sayfası gibi.
En azından şu bilgilerin burada olmasını faydalı buluyorum:
- Projenin kısa açıklaması
- Kullanılan Dynamics 365 sürümü
- Custom metadata klasörü
- Gerekli model bağımlılıkları
- Visual Studio solution bilgisi
- Branch yapısı
- İlk kurulum adımları
- Build pipeline bilgisi
- Geliştirme standartlarına bağlantı
Yeni bir geliştirici projeye başladığında ilk bakacağı yerin README.md olması işleri ciddi şekilde kolaylaştırıyor. Aşağıdaki ekran görüntüsü kendi makinemden. Bire bir burada anlattığım yapıyla aynı değil; zaten amaç tek bir klasör standardı dayatmak değil. Projeye göre Tools veya Licenses gibi ek klasörler de olabilir.
Repository İçin Önerilen Genel Yapı
Repository’nin tamamını bir arada düşünürsek örnek yapı şöyle olabilir:
D365FO-UDE
│
├── Metadata
│ │
│ ├── DMRCustomizations
│ │ ├── Descriptor
│ │ │ └── DMRCustomizations.xml
│ │ │
│ │ └── DMRCustomizations
│ │ ├── AxClass
│ │ ├── AxTable
│ │ ├── AxForm
│ │ ├── AxMenuItemDisplay
│ │ ├── AxSecurityPrivilege
│ │ └── AxLabelFile
│ │
│ └── DMRIntegrations
│ ├── Descriptor
│ │ └── DMRIntegrations.xml
│ │
│ └── DMRIntegrations
│ ├── AxClass
│ ├── AxDataEntityView
│ └── AxService
│
├── Projects
│ ├── DMRCustomizations
│ │ ├── DMRCustomizations.sln
│ │ └── DMRCustomizations.rnrproj
│ │
│ └── DMRIntegrations
│ ├── DMRIntegrations.sln
│ └── DMRIntegrations.rnrproj
│
├── Build
│ ├── azure-pipelines.yml
│ ├── packages.config
│ └── nuget.config
│
├── Scripts
│ └── Initialize-DeveloperEnvironment.ps1
│
├── Documentation
│ ├── DevelopmentStandards.md
│ └── BranchStrategy.md
│
├── .gitignore
└── README.md
Aynı repository içinde birden fazla model ve package bulunabilir.
Benim genel yaklaşımım, aynı Dynamics 365 projesine ve aynı yayın döngüsüne ait custom modelleri tek repository içinde tutmak.
Her model için ayrı repository ilk bakışta daha düzenli görünebilir. Fakat modeller birbirine bağımlıysa, aynı sürümde birlikte yayınlanıyorsa ve aynı ekip tarafından geliştiriliyorsa bu ayrım çoğu zaman işleri kolaylaştırmak yerine zorlaştırıyor.
Hangi Dosyalar Git’e Alınmalı?
Ben repository’ye hangi dosyaların gireceğini kabaca üç grupta düşünüyorum: custom metadata, geliştirme/build tanımları ve gerçekten gerekli referans dosyaları.
Custom model metadata dosyaları
Örneğin:
AxClass
AxTable
AxForm
AxView
AxQuery
AxDataEntityView
AxMenuItemDisplay
AxMenuItemAction
AxSecurityRole
AxSecurityDuty
AxSecurityPrivilege
AxLabelFile
Bunlar uygulamanın gerçek X++ kaynak kodunu ve metadata tanımlarını oluşturuyor.
Model descriptor dosyaları
Örneğin:
Descriptor dosyasının özellikle ilk model eklenirken unutulduğunu sık görüyorum.
Metadata klasörü repository’de olsa bile descriptor eksik olduğunda başka bir geliştirici modeli doğru şekilde göremeyebilir veya build tarafı model bilgisini çözemeyebilir.
Visual Studio solution ve proje dosyaları
*.sln
*.rnrproj
Bunlar hem yeni geliştiricilerin hızlı başlaması hem de otomatik build için önemli.
Pipeline dosyaları
azure-pipelines.yml
packages.config
nuget.config
Build tanımlarının da kodla birlikte versiyonlanması gerekir.
packages.config hangi build paketlerinin ve sürümlerinin kullanılacağını, nuget.config ise bu paketlerin hangi feed’den alınacağını tanımlar. İkisini de repository’de, mümkün olduğunca kolay bulunabilecek bir yerde tutmayı tercih ediyorum.
Buradaki kritik nokta şu: nuget.config içine kullanıcı adı, parola, PAT veya erişim anahtarı gibi bir bilgi yazmamalıyız. Kimlik bilgileri pipeline/service connection veya güvenli değişkenler üzerinden yönetilmeli.
Geliştirme scriptleri ve dokümanlar
*.ps1
*.md
*.json
*.yml
*.yaml
Projeyi kurmak, build etmek veya geliştirme standartlarını açıklamak için kullandığımız script ve dokümanların da kodla birlikte versiyonlanması faydalı.
Gerekli üçüncü taraf DLL dosyaları
Genel olarak binary dosyaları repository’ye koymamayı tercih ederim. Fakat bazı ISV çözümleri veya özel entegrasyonlar build sırasında belirli referans DLL’lerine ihtiyaç duyabiliyor.
Bu nedenle .gitignore içine doğrudan *.dll yazmak bazen beklenmedik build hatalarına neden olabilir.
Eğer bu DLL’ler NuGet veya başka bir paket feed’i üzerinden yönetilemiyorsa, gerçekten gerekli olanları kontrollü şekilde repository’de tutmak gerekebilir.
Hangi Dosyalar Git’e Alınmamalı?
Kaynak kod olmayan, yeniden üretilebilen veya makineye özel dosyaları ise repository dışında bırakmak gerekiyor.
Microsoft standart metadata’sı
UDE bağlantısı sırasında indirilen aşağıdaki Microsoft metadata içeriğini repository’ye almıyoruz:
PackagesLocalDirectory
PackagesLocalDirectory.zip
Bu klasör Microsoft’un uygulama ve platform metadata’sını içeriyor. Boyutu büyük ve bağlı olduğumuz uygulama sürümüne göre değişiyor.
Her geliştirici gerekli reference metadata’yı kendi ortamı için indirip kullanabilir.
Visual Studio geçici dosyaları
.vs
*.user
*.suo
Bunlar geliştiricinin kendi yerel Visual Studio ayarlarıdır.
Build çıktıları
bin
obj
output
artifacts
TestResults
Bu klasörler derleme sırasında yeniden üretildiği için kaynak kod yönetimine alınmalarına gerek yok.
Log ve geçici dosyalar
*.log
*.tmp
*.temp
*.cache
Cross-reference veritabanı dosyaları
DYNAMICSXREFDB.bak
*.mdf
*.ldf
Cross-reference veritabanı geliştirici makinesine özgüdür ve gerektiğinde yeniden oluşturulabilir.
NuGet paketleri ve indirilen build paketleri
*.nupkg
packages
.nuget
NuGet paketlerinin kendisini Git’te tutmak yerine Azure Artifacts gibi bir feed üzerinden yönetmek daha sağlıklı.
Deployment çıktıları
*.zip
*.axdeployablepackage
DeployablePackages
UnifiedPackages
Build sonucunda oluşan deployable/unified package dosyalarını da source repository’ye commit etmek yerine pipeline artifact’i olarak saklamak daha doğru.
Örnek .gitignore Dosyası
Repository kökünde başlangıç için aşağıdaki gibi bir .gitignore kullanabiliriz:
# Visual Studio
.vs/
*.user
*.suo
*.userosscache
*.sln.docstates
# Build output
**/bin/
**/obj/
**/output/
**/artifacts/
**/TestResults/
# Logs and temporary files
*.log
*.tmp
*.temp
*.cache
*.bak
# Local databases
*.mdf
*.ldf
DYNAMICSXREFDB.bak
# Microsoft reference metadata
PackagesLocalDirectory/
PackagesLocalDirectory.zip
# Downloaded development assets
Microsoft.Dynamics.FinOps.ToolsVS2022.vsix
TraceParser.msi
# NuGet local content
.nuget/
packages/
*.nupkg
# Deployment outputs
DeployablePackages/
UnifiedPackages/
*.axdeployablepackage
# Operating system files
Thumbs.db
Desktop.ini
.DS_Store
# Secrets / private files
*.pfx
.env
Burada özellikle şu satırı eklemediğimize dikkat edelim:
Çünkü build için gerçekten ihtiyaç duyduğumuz bazı üçüncü taraf DLL’leri olabilir. Bu yüzden DLL’leri otomatik olarak tamamen dışarıda bırakmıyorum.
Bir başka önemli nokta da .gitignore dosyasını en başta oluşturmaktır. Git’in daha önce takip etmeye başladığı bir dosyayı sonradan .gitignore’a eklemek o dosyayı otomatik olarak takipten çıkarmaz. Gerekirse git rm –cached ile index’ten ayrıca çıkarmak gerekir.
Visual Studio’daki Git Changes ekranından yaptığımız ignore işlemleri de repository kökündeki .gitignore dosyasına kural ekler. Yani ekip için ortak ignore standardını yine bu dosyada yönetmiş oluruz.
Azure DevOps Repository Oluşturma
Azure DevOps projemizin içinde yeni bir Git repository oluşturuyoruz.
Örnek isim:
Repository oluştuktan sonra yerel makinemize clone edebiliriz.
Örnek:
cd C:\
git clone https://dev.azure.com/<Organization>/<Project>/_git/D365FO-UDE CustomXppMetadata
Bu örnekte repository aşağıdaki klasöre gelir:
Ardından temel klasörleri oluşturabiliriz:
cd C:\CustomXppMetadata
New-Item -ItemType Directory -Path Metadata
New-Item -ItemType Directory -Path Projects
New-Item -ItemType Directory -Path Build
New-Item -ItemType Directory -Path Scripts
New-Item -ItemType Directory -Path Documentation
Sonra Visual Studio’da şu menüye gidiyoruz:
Extensions
> Dynamics 365
> Configure Metadata
Custom metadata klasörünü aşağıdaki şekilde tanımlıyoruz:
Reference metadata tarafında ise UDE’den indirdiğimiz Microsoft metadata klasörü bulunuyor.
Örneğin:
Bu yapı sayesinde custom kodumuz ile Microsoft standardını fiziksel olarak da ayırmış oluyoruz.
İlk Commit’in Oluşturulması
Klasör yapısı, .gitignore ve README.md hazır olduğunda ilk commit’i atabiliriz.
git status
git add .
git commit -m "Initialize D365 F&O UDE repository structure"
git push origin main
İlk commit’ten sonra Azure DevOps üzerinden repository içeriğine bir kez bakmakta fayda var.
Ben özellikle şu noktaları kontrol ediyorum:
- Microsoft standart metadata’sı repository’ye girmiş mi?
- Büyük ZIP veya NuGet dosyaları eklenmiş mi?
- Descriptor dosyaları mevcut mu?
- Visual Studio solution ve proje dosyaları eklenmiş mi?
- Parola veya bağlantı bilgisi içeren bir dosya bulunuyor mu?
- Gereksiz build çıktıları repository’ye alınmış mı?
Yanlışlıkla büyük veya hassas bir dosya commit edildiyse yalnızca yerel klasörden silmek her zaman yeterli olmaz; dosya Git geçmişinde kalabilir. Bu yüzden ilk commit öncesi kontrol etmek en temiz yöntem.
Azure DevOps bağlantısını Visual Studio üzerinden de yapabilirsiniz. Aşağıdaki ekran görüntüsünde kendi kullandığım bağlantı yapısını görebilirsiniz.
Repository klasörünü Git’e ekledikten sonra yaptığınız değişiklikler Git Changes ekranında görünmeye başlar. Buradan commit, push ve branch işlemlerini Visual Studio’dan yönetebilirsiniz.
Branch Yapısı Nasıl Olmalı?
Branch tarafında tek bir doğru şablon yok. Ekip büyüklüğü, aynı anda kaç geliştirme yapıldığı, UAT süresi ve ne kadar sık canlıya çıkıldığı yapıyı doğrudan değiştiriyor.
Ben branch stratejisini mümkün olduğunca basit tutmaya çalışıyorum. Çünkü gereğinden fazla branch de en az yetersiz branch yapısı kadar operasyon yükü oluşturabiliyor.
Yine de gerçek bir F&O projesinde en azından şu kod durumlarını birbirinden ayırabilmek gerekiyor:
- Henüz tamamlanmamış geliştirmeler
- Geliştirmesi tamamlanmış ve entegrasyona alınmış kod
- Test/UAT sürecindeki sürüm
- Production’da çalışan kod
Bunun nasıl ayrılacağı projenin ölçeğine göre değişebilir. Aşağıda benim kullandığım üç seviyeli yaklaşımı göstereceğim.
Seçenek 1: Basit main + dev Yapısı
Tek geliştiricili deneme projelerinde veya çok küçük ekiplerde iki branch ile başlamak yeterli olabilir:
main
└── dev
main
- Stabil ve production’a çıkabilecek kodu içerir.
- Canlıya çıkmış veya çıkmaya hazır sürümü temsil eder.
- Doğrudan günlük geliştirme yapılmaz.
- Değişiklikler dev branch’inden Pull Request ile alınır.
dev
- Günlük entegrasyon branch’idir.
- Tamamlanan geliştirmeler burada birleştirilir.
- Build ve temel kontroller bu branch üzerinde çalıştırılabilir.
Akış aşağıdaki gibi olur:
Geliştirme
↓
dev
↓ Pull Request
main
Bu yapı küçük ve yayın süreci basit projelerde iş görebilir.
Seçenek 2: feature + dev + main Yapısı
Ekipte birden fazla geliştirici varsa benim daha çok tercih ettiğim başlangıç yapısı bu:
main
└── dev
├── feature/1234-customer-integration
├── feature/1250-sales-order-control
└── feature/1280-invoice-report
Her iş için dev branch’inden kısa ömürlü bir feature branch oluşturuyoruz.
Örneğin:
git checkout dev
git pull
git checkout -b feature/1234-customer-integration
Geliştirme bittiğinde branch’i remote repository’ye gönderiyoruz:
git add .
git commit -m "AB#1234 Add customer integration service"
git push -u origin feature/1234-customer-integration
Ardından Azure DevOps üzerinde feature branch’ten dev’e bir Pull Request açıyoruz:
feature/1234-customer-integration
↓
dev
Kod review ve gerekli build kontrollerinden sonra feature branch dev’e alınır.
Bu yapının pratikte en önemli faydaları:
- Her geliştirme birbirinden izole kalır.
- Tamamlanmamış kod doğrudan dev’e taşınmaz.
- Kod review Pull Request üzerinden yapılır.
- Work Item ile commit/PR arasında bağlantı kurulabilir.
- Sorunlu bir değişikliği ayırmak ve geri almak kolaylaşır.
Seçenek 3: feature + dev + release + main Yapısı
UAT süresinin uzun olduğu, production ortamının aktif olduğu veya aynı anda bir sonraki sürümün geliştirmesinin devam ettiği projelerde ayrı bir release branch faydalı oluyor. Ancak bunu her projeye otomatik olarak eklememek lazım; branch sayısı arttıkça DevOps süreci de ağırlaşıyor.
Örnek yapı:
main
│
├── hotfix/1.0.1-critical-invoice-error
│
└── release/1.1.0
↑
dev
↑
feature/*
Kod akışı kabaca şöyle olur:
feature/*
↓
dev
↓
release/1.1.0
↓
main
feature branch’leri
Her geliştirme için dev üzerinden oluşturulur.
Örnekler:
feature/1234-customer-integration
feature/1250-credit-limit-control
feature/1280-bank-interface
Mümkün olduğunca kısa ömürlü tutmak önemli.
dev
Tamamlanan geliştirmelerin birleştiği entegrasyon branch’idir.
Burada örneğin şu kontroller çalıştırılabilir:
- Geliştirici testleri
- Model build
- Best Practice kontrolleri
- Otomatik testler
- Genel entegrasyon kontrolleri
Bu kontrollerin hangilerinin otomatik olacağı projenin pipeline olgunluğuna göre değişir.
release branch
Fonksiyonel test veya UAT’a alınacak belirli bir sürümü temsil eder.
Örneğin:
release/1.1.0
release/2026.09
release/sprint-12
Ben sürekli yaşayan tek bir release branch yerine, sürüm bazlı release branch’leri kullanmayı daha anlaşılır buluyorum.
Örneğin:
Bu branch dev üzerinden belirli bir noktada oluşturulur. UAT sırasında yalnızca o sürüme ait hata düzeltmeleri buraya alınır.
Bu sırada yeni geliştirmeler dev üzerinde devam edebilir.
Özellikle UAT birkaç hafta sürüyorsa bu ayrım ciddi rahatlık sağlıyor. Test edilen sürüm sabit kalırken ekip bir sonraki geliştirmeleri bekletmek zorunda kalmıyor.
main
Production’da çalışan veya production’a çıkmaya hazır kodu temsil eder.
Release testleri tamamlandığında release branch’ten main’e Pull Request açılır:
release/1.1.0
↓
main
Merge sonrasında production sürümünü işaretlemek için ilgili commit’e tag vermek faydalı:
Örneğin:
git tag -a v1.1.0 -m "Production release 1.1.0"
git push origin v1.1.0
Böylece production’da hangi commit’in çalıştığını geriye dönük çok daha rahat takip edebiliriz.
Hotfix Yapısı
Production’da acil bir hata çıktığında hotfix branch’ini main üzerinden oluşturmak en temiz yaklaşım.
git checkout main
git pull
git checkout -b hotfix/1.1.1-invoice-posting-error
Düzeltme tamamlandığında değişikliği main’e alıyoruz:
hotfix/1.1.1-invoice-posting-error
↓
main
Ama iş burada bitmiyor.
Aynı düzeltmeyi dev’e ve açıksa ilgili release branch’ine de taşımak gerekiyor:
hotfix
├── main
├── dev
└── release/1.2.0
Aksi halde production’da düzelttiğimiz hata bir sonraki sürümle tekrar geri gelebilir.
Branch Naming Standardı
Branch isimleri için ekip içinde ortak bir standart belirlemek küçük ama çok faydalı bir iş.
Örneğin:
feature/<work-item-id>-<short-description>
bugfix/<work-item-id>-<short-description>
hotfix/<version>-<short-description>
release/<version>
Örnekler:
feature/1234-vendor-integration
feature/1280-sales-report
bugfix/1325-tax-calculation
hotfix/1.0.1-invoice-posting
release/1.2.0
Ben branch adlarında şu kuralları kullanıyorum:
- Türkçe karakter kullanılmaması
- Boşluk kullanılmaması
- Kısa ve anlaşılır açıklama yazılması
- Azure DevOps Work Item numarasının eklenmesi
Bu kadar basit bir standart bile Azure DevOps üzerinde arama ve takip işini kolaylaştırıyor.
Branch Policy Ayarları
Özellikle main, dev ve release branch’lerinde minimum bir branch policy seti kullanmayı öneriyorum.
main branch için
- Direct push kapatılmalı
- Pull Request zorunlu olmalı
- En az bir reviewer olmalı
- Kendi Pull Request’ini onaylama kapatılmalı
- Build validation zorunlu olmalı
- Açık yorumlar çözülmeden merge yapılmamalı
- Work Item bağlantısı zorunlu olmalı
dev branch için
- Pull Request zorunlu olmalı
- En az bir reviewer olmalı
- Build validation çalışmalı
- Merge conflict kontrol edilmeli
release branch için
- Yeni feature kabul edilmemeli
- Yalnızca test hata düzeltmeleri alınmalı
- Build validation zorunlu olmalı
- Düzeltmelerin dev branch’ine geri taşındığı kontrol edilmeli
Burada asıl amaç branch’i kilitlemek değil; production’a giden değişikliğin en azından bir review ve build kontrolünden geçtiğini garanti altına almak.
Squash Merge Kullanımı
Feature branch üzerinde geliştirme yaparken doğal olarak çok sayıda küçük commit oluşabiliyor:
Fix compilation
Update label
Correct method
Fix build
Final correction
Bu commit’lerin tamamını dev geçmişine ayrı ayrı taşımak bir süre sonra repository geçmişini gereksiz şekilde gürültülü hale getiriyor.
Bu yüzden feature branch’leri dev’e alırken çoğu projede Squash Merge kullanmayı tercih ediyorum.
Örneğin feature branch’te on küçük commit olsa bile dev tarafında tek ve anlamlı bir commit kalabilir:
Hotfix ve release merge’lerinde ise sürüm geçmişini daha görünür tutmak için ekip standardına göre normal merge tercih edilebilir.
Burada tek bir zorunlu yöntem yok; önemli olan ekibin aynı merge standardını tutarlı şekilde uygulaması.
Bir Geliştiricinin Günlük Git Akışı
Bunu günlük çalışma akışına çevirirsek örnek bir geliştirici günü kabaca şöyle ilerleyebilir.
1. dev branch’ini güncelleme
git checkout dev
git pull origin dev
2. Feature branch oluşturma
3. Geliştirmeyi yapma
Visual Studio’da X++ geliştirmemizi yapıyoruz.
Commit öncesinde değişiklikleri kontrol etmek için:
git status
git diff
4. Değişiklikleri commit etme
git add .
git commit -m "AB#1450 Add sales order control"
5. Branch’i güncel tutma
Geliştirme birkaç gün sürdüyse dev’deki güncel değişiklikleri kendi feature branch’imize almak iyi olur:
git fetch origin
git merge origin/dev
Ekip standardına göre rebase de tercih edilebilir:
Ekip Git konusunda çok deneyimli değilse başlangıçta merge yaklaşımı daha anlaşılır ve daha az sürprizli olabilir.
6. Remote repository’ye gönderme
7. Pull Request oluşturma
Azure DevOps üzerinde feature branch’ten dev’e Pull Request açıyoruz:
feature/1450-sales-order-control
↓
dev
Ben Pull Request açıklamasında en azından şu bilgilerin olmasını bekliyorum:
- Yapılan geliştirme
- İlgili Work Item
- Etkilenen modül ve süreçler
- Test senaryosu
- Varsa teknik riskler
- Varsa veri tabanı senkronizasyon ihtiyacı
- Varsa yeni security object bilgileri
Metadata Conflict’lerine Dikkat
Dynamics 365 Finance & Operations metadata dosyaları XML formatında saklanıyor.
İki geliştirici aynı anda aynı form, tablo veya sınıf üzerinde çalıştığında klasik Git merge conflict’iyle karşılaşabiliriz.
Özellikle şu nesnelerde conflict ihtimali daha yüksek:
- Formlar
- Tablolar
- Data entity’ler
- Security role ve duty’ler
- Label dosyaları
- Büyük sınıflar
Conflict riskini azaltmak için birkaç basit alışkanlık çok işe yarıyor:
- Feature branch’leri mümkün olduğunca kısa tutmak
- dev’deki değişiklikleri düzenli olarak feature branch’e almak
- Büyük işleri daha küçük geliştirme parçalarına bölmek
- Aynı nesne üzerinde çalışacak geliştiricilerin önceden haberleşmesi
- Her Pull Request’in mümkün olduğunca tek bir iş ihtiyacını kapsaması
- Gereksiz format veya sıralama değişikliklerinden kaçınmak
Metadata conflict’i çözüldüğünde yalnızca Git’in 'merge tamamlandı' demesine güvenmemek gerekiyor. İlgili nesneyi Visual Studio/Application Explorer tarafında da açıp kontrol etmekte fayda var.
XML teknik olarak merge edilmiş olabilir ama ortaya çıkan metadata’nın gerçekten doğru build edildiği ve beklediğimiz davranışı verdiği ayrıca doğrulanmalı.
Sık Yaptığımız Hatalar
1. Microsoft standart metadata’sını repository’ye almak
Bu hem repository boyutunu gereksiz büyütür hem de branch/sync işlemlerini ağırlaştırır.
PackagesLocalDirectory reference metadata olarak lokal makinede kalmalı.
2. Descriptor dosyasını unutmak
Model metadata’sını ekleyip Descriptor klasörünü atlamak başka makinelerde ve build sürecinde sorun çıkarabilir.
3. Metadata ile proje dosyalarını birlikte yönetmemek
Sadece .rnrproj dosyasını almak yeterli değil; asıl kaynak kod Metadata altındaki XML dosyalarıdır.
Tersi de problemli. Yalnızca metadata’yı alıp solution/.rnrproj dosyalarını eklemezsek yeni geliştiricinin projeyi açması ve pipeline’ın build etmesi zorlaşır.
4. Tüm DLL dosyalarını otomatik olarak ignore etmek
Gerçekten gerekli bir üçüncü taraf referans DLL’i de yanlışlıkla repository dışında kalabilir.
5. Herkesin doğrudan dev branch’inde çalışması
Küçük ekipte bir süre idare edebilir ama geliştirme sayısı arttıkça tamamlanmamış işler ve conflict’ler birbirine karışmaya başlar.
6. main ve hotfix akışını kontrolsüz bırakmak
main’e doğrudan commit yapmak review izini kaybettirir. Aynı şekilde production hotfix’ini sadece main’de bırakmak da düzeltmenin sonraki sürümde kaybolmasına neden olabilir.
7. Repository’ye parola, token veya bağlantı bilgisi eklemek
Connection string, parola, PAT, token veya private certificate gibi bilgiler kaynak kod repository’sinde tutulmamalıdır.
Sonuç
UDE ile Git tarafını birlikte düşününce asıl değişiklik sadece klasör yolunun değişmesi değil. Custom metadata artık geliştiricinin lokal makinesinde daha bağımsız duruyor ve bu da kaynak kod yönetimini çok daha merkezi bir hale getiriyor.
Bana göre sağlıklı bir UDE repository’sinde amaç mümkün olduğunca basit: custom kodu, Visual Studio projelerini ve build tanımlarını aynı versiyon kontrolü altında tutmak; Microsoft metadata’sı ile geçici dosyaları bunun dışında bırakmak.
Özet repository yapımız kabaca şöyle:
Repository
├── Custom Metadata
├── Visual Studio Projects
├── Build Definitions
├── Scripts
└── Documentation
Microsoft standart metadata’sı, Visual Studio’nun geçici dosyaları, build çıktıları ve lokal veritabanları repository dışında kalmalı.
Branch yapısını da projenin gerçek ihtiyacına göre büyütmek en doğrusu:
Küçük proje:
dev → main
Orta ölçekli proje:
feature → dev → main
UAT/release süreci olan proje:
feature → dev → release → main
hotfix: main → düzeltme → main + dev (+ açık release)
Ben yapıyı gereğinden fazla karmaşıklaştırmadan şu üç prensibi korumaya çalışıyorum:
- Geliştirmeleri mümkün olduğunca feature branch’lerde izole etmek.
- Production kodunu temsil eden main branch’ine doğrudan geliştirme yapmamak.
- Custom metadata, Visual Studio projeleri ve build tanımlarını aynı versiyon kontrol sürecinde yönetmek.
Bu noktadan sonra geliştirmelerimiz yalnızca lokal makinede duran dosyalar olmaktan çıkıyor; ekip tarafından izlenebilir, review edilebilir ve otomatik build sürecine bağlanabilir hale geliyor.
Serinin bir sonraki bölümünde bu repository yapısını kullanarak Azure DevOps üzerinde X++ Build Pipeline oluşturacağız. NuGet paketleri, packages.config/nuget.config, YAML pipeline ve unified package üretimini adım adım ele alacağız.
Selamlar.
Fatih Demirci










No comments yet.