Aracılığıyla paylaş


Dağıtım işleri

Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020

Önemli

  • İş ve aşama adları anahtar sözcük içeremez (örnek: deployment).
  • Bir aşamadaki her işin benzersiz bir adı olmalıdır.

YAML işlem hatlarında dağıtım adımlarınızı dağıtım işi olarak adlandırılan özel türde bir işe yerleştirmenizi öneririz. Dağıtım işi, ortama göre sıralı olarak çalıştırılacak adımlardan oluşan bir koleksiyondur. Dağıtım işi ve geleneksel bir aynı aşamada bulunabilir. Azure DevOps runOnce, rolling ve kanarya stratejilerini destekler.

Dağıtım işleri aşağıdaki avantajları sağlar:

  • Dağıtım geçmişi: Dağıtım geçmişini işlem hatları arasında belirli bir kaynağa ve denetim için dağıtımların durumuna kadar elde edersiniz.
  • Dağıtım stratejisi uygulama: Uygulamanızın nasıl dağıtılacağını tanımlarsınız.

Dağıtım işi kaynak depoyu otomatik olarak kopyalamaz. ile checkout: selfişinizin içindeki kaynak depoyu kullanıma alabilirsiniz.

Not

Bu makalede dağıtım işleri ile dağıtıma odaklanmaktadır. İşlem hatları ile Azure'a dağıtmayı öğrenmek için bkz . Azure'a dağıtmaya genel bakış.

Şema

Dağıtım işini belirtmek için tam söz dizimi aşağıdadır:

jobs:
- deployment: string   # name of the deployment job, A-Z, a-z, 0-9, and underscore. The word "deploy" is a keyword and is unsupported as the deployment name.
  displayName: string  # friendly name to display in the UI
  pool:                # not required for virtual machine resources
    name: string       # Use only global level variables for defining a pool name. Stage/job level variables are not supported to define pool name.
    demands: string | [ string ]
  workspace:
    clean: outputs | resources | all # what to clean up before the job runs
  dependsOn: string
  condition: string
  continueOnError: boolean                # 'true' if future jobs should run even if this job fails; defaults to 'false'
  container: containerReference # container to run this job inside
  services: { string: string | container } # container resources to run as a service container
  timeoutInMinutes: nonEmptyString        # how long to run the job before automatically cancelling
  cancelTimeoutInMinutes: nonEmptyString  # how much time to give 'run always even if cancelled tasks' before killing them
  variables: # several syntaxes, see specific section
  environment: string  # target environment name and optionally a resource name to record the deployment history; format: <environment-name>.<resource-name>
  strategy:
    runOnce:    #rolling, canary are the other strategies that are supported
      deploy:
        steps: [ script | bash | pwsh | powershell | checkout | task | templateReference ]

Özelliği için environment kullanabileceğiniz daha ayrıntılı ve alternatif bir söz dizimi vardır.

environment:
    name: string # Name of environment.
    resourceName: string # Name of resource.
    resourceId: string # Id of resource.
    resourceType: string # Type of environment resource.
    tags: string # List of tag filters.

Sanal makineler için havuz tanımlamanız gerekmez. Bir dağıtım işinde sanal makine kaynağıyla tanımladığınız tüm adımlar, havuzdaki aracıya karşı değil, bu sanal makinede çalıştırılır. Kubernetes gibi diğer kaynak türleri için, görevlerin bu makinede çalışabilmesi için bir havuz tanımlamanız gerekir.

Dağıtım stratejileri

Uygulama güncelleştirmelerini dağıtırken, güncelleştirmeyi teslim etmek için kullandığınız tekniğin şunları sağlaması önemlidir:

  • Başlatmayı etkinleştirin.
  • Güncelleştirmeyi dağıtın.
  • Trafiği güncelleştirilmiş sürüme yönlendirin.
  • Trafiği yönlendirdikten sonra güncelleştirilmiş sürümü test edin.
  • Hata durumunda, bilinen son iyi sürüme geri yükleme adımlarını çalıştırın.

Dağıtım sırasında adımları çalıştırabilen yaşam döngüsü kancalarını kullanarak bunu başarıyoruz. Yaşam döngüsü kancalarının her biri, özniteliğine bağlı pool olarak bir aracı işi veya sunucu işi (ya da gelecekte bir kapsayıcı veya doğrulama işi) olarak çözümlenmektedir. Varsayılan olarak, yaşam döngüsü kancaları iş tarafından deployment belirtilen öğesini pool devralır.

Dağıtım işleri sistem değişkenini $(Pipeline.Workspace) kullanır.

Yaşam döngüsü kancalarının açıklamaları

preDeploy: Uygulama dağıtımı başlamadan önce kaynakları başlatan adımları çalıştırmak için kullanılır.

deploy: Uygulamanızı dağıtan adımları çalıştırmak için kullanılır. Yapıt indirme görevi yalnızca dağıtım işleri için kancaya deploy otomatik olarak eklenecektir. Yapıtları indirmeyi durdurmak için İşlem Hattı Yapıtlarını İndir görevini belirterek indirilmesi gereken belirli yapıtları kullanın - download: none veya seçin.

routeTraffic: Güncelleştirilmiş sürüme gelen trafiğe hizmet eden adımları çalıştırmak için kullanılır.

postRouteTraffic: Trafik yönlendirildikten sonra adımları çalıştırmak için kullanılır. Bu görevler genellikle tanımlı aralık için güncelleştirilmiş sürümün durumunu izler.

on: failure veya on: success: Geri alma eylemleri veya temizleme adımlarını çalıştırmak için kullanılır.

RunOnce dağıtım stratejisi

runOnce, ve postRouteTrafficgibi preDeploy deployrouteTraffictüm yaşam döngüsü kancalarının bir kez yürütüldüğü en basit dağıtım stratejisidir. Ardından veya on: success on: failure yürütülür.

strategy: 
    runOnce:
      preDeploy:        
        pool: [ server | pool ] # See pool schema.        
        steps:
        - script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
      deploy:          
        pool: [ server | pool ] # See pool schema.        
        steps:
        ...
      routeTraffic:         
        pool: [ server | pool ]         
        steps:
        ...        
      postRouteTraffic:          
        pool: [ server | pool ]        
        steps:
        ...
      on:
        failure:         
          pool: [ server | pool ]           
          steps:
          ...
        success:          
          pool: [ server | pool ]           
          steps:
          ...

Şirket içinde barındırılan aracılar kullanıyorsanız, dağıtım çalışma alanınızı temizlemek için çalışma alanı temizleme seçeneklerini kullanabilirsiniz.

  jobs:
  - deployment: MyDeploy
    pool:
      vmImage: 'ubuntu-latest'
    workspace:
      clean: all
    environment: staging

Sıralı dağıtım stratejisi

Sıralı dağıtım, bir uygulamanın önceki sürümünün örneklerini, her yinelemedeki sabit bir sanal makine kümesindeki (sıralı küme) uygulamanın yeni sürümünün örnekleriyle değiştirir.

Şu anda yalnızca VM kaynakları için sıralı stratejiyi destekliyoruz.

Örneğin, sıralı dağıtım genellikle sonraki dağıtım kümesine geçmeden önce her sanal makine kümesindeki dağıtımların tamamlanmasını bekler. Her yinelemeden sonra bir sistem durumu denetimi yapabilirsiniz ve önemli bir sorun oluşursa sıralı dağıtım durdurulabilir.

Sıralı dağıtımlar, düğümün altında strategy: anahtar sözcük rolling: belirtilerek yapılandırılabilir. strategy.name değişkeni, stratejinin adını alan bu strateji bloğunda kullanılabilir. Bu durumda, yuvarlanıyor.

strategy:
  rolling:
    maxParallel: [ number or percentage as x% ]
    preDeploy:        
      steps:
      - script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
    deploy:          
      steps:
      ...
    routeTraffic:         
      steps:
      ...        
    postRouteTraffic:          
      steps:
      ...
    on:
      failure:         
        steps:
        ...
      success:          
        steps:
        ...

Tüm yaşam döngüsü kancaları desteklenir ve her VM'de çalıştırılacak yaşam döngüsü kanca işleri oluşturulur.

preDeploy, deploy, routeTrafficve postRouteTraffic tarafından tanımlanan maxParalleltoplu iş boyutu başına bir kez yürütülür. Ardından veya on: success on: failure yürütülür.

ile maxParallel: <# or % of VMs>, paralel olarak dağıtılacak sanal makine hedeflerinin sayısını/yüzdesini denetleyebilirsiniz. Bu, uygulamanın bu makinelerde çalışmasını ve dağıtım diğer makinelerde gerçekleşirken istekleri işleyebilmesini sağlar ve bu da genel kapalı kalma süresini azaltır.

Not

Bu özellikte bilinen birkaç boşluk vardır. Örneğin, bir aşamayı yeniden denediğinizde, dağıtımı yalnızca başarısız hedeflerde değil tüm VM'lerde yeniden çalıştırır.

Kanarya dağıtımı stratejisi

Kanarya dağıtım stratejisi, uygulamaların yeni sürümlerinin kullanıma sunulma riskini azaltmaya yardımcı olan gelişmiş bir dağıtım stratejisidir. Bu stratejiyi kullanarak, değişiklikleri önce sunucuların küçük bir alt kümesine dağıtabilirsiniz. Yeni sürüme daha fazla güven kazandıkça, altyapınızdaki daha fazla sunucuya yayınlayabilir ve daha fazla trafik yönlendirebilirsiniz.

strategy: 
    canary:
      increments: [ number ]
      preDeploy:        
        pool: [ server | pool ] # See pool schema.        
        steps:
        - script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
      deploy:          
        pool: [ server | pool ] # See pool schema.        
        steps:
        ...
      routeTraffic:         
        pool: [ server | pool ]         
        steps:
        ...        
      postRouteTraffic:          
        pool: [ server | pool ]        
        steps:
        ...
      on:
        failure:         
          pool: [ server | pool ]           
          steps:
          ...
        success:          
          pool: [ server | pool ]           
          steps:
          ...

Kanarya dağıtım stratejisi yaşam döngüsü kancasını preDeploy destekler (bir kez yürütülür) ve , routeTrafficve postRouteTraffic yaşam döngüsü kancalarıyla yinelenirdeploy. Ardından veya failure kancasıyla success çıkar.

Bu stratejide aşağıdaki değişkenler kullanılabilir:

strategy.name: Stratejinin adı. Örneğin kanarya.
strategy.action: Kubernetes kümesinde gerçekleştirilecek eylem. Örneğin, dağıtın, yükseltin veya reddedin.
strategy.increment: Geçerli etkileşimde kullanılan artış değeri. Bu değişken yalnızca deploy, routeTrafficve postRouteTraffic yaşam döngüsü kancalarında kullanılabilir.

Örnekler

RunOnce dağıtım stratejisi

Aşağıdaki örnek YAML kod parçacığı, dağıtım stratejisini kullanarak runOnce basit bir dağıtım işi kullanımını gösterir. Örnek, bir kullanıma alma adımı içerir.


jobs:
  # Track deployments on the environment.
- deployment: DeployWeb
  displayName: deploy Web App
  pool:
    vmImage: 'ubuntu-latest'
  # Creates an environment if it doesn't exist.
  environment: 'smarthotel-dev'
  strategy:
    # Default deployment strategy, more coming...
    runOnce:
      deploy:
        steps:
        - checkout: self 
        - script: echo my first deployment

Bu işin her çalıştırmasıyla, dağıtım geçmişi ortama göre smarthotel-dev kaydedilir.

Not

  • Ayrıca, önceki örnekte gösterildiği gibi boş kaynaklarla bir ortam oluşturmak ve bunu dağıtım geçmişini kaydetmek için soyut kabuk olarak kullanmak da mümkündür.

Sonraki örnekte işlem hattının bir dağıtım işi için hedef olarak kullanılacak bir ortama ve kaynağa nasıl başvurabileceği gösterilmektedir.

jobs:
- deployment: DeployWeb
  displayName: deploy Web App
  pool:
    vmImage: 'ubuntu-latest'
  # Records deployment against bookings resource - Kubernetes namespace.
  environment: 'smarthotel-dev.bookings'
  strategy: 
    runOnce:
      deploy:
        steps:
          # No need to explicitly pass the connection details.
        - task: KubernetesManifest@0
          displayName: Deploy to Kubernetes cluster
          inputs:
            action: deploy
            namespace: $(k8sNamespace)
            manifests: |
              $(System.ArtifactsDirectory)/manifests/*
            imagePullSecrets: |
              $(imagePullSecret)
            containers: |
              $(containerRegistry)/$(imageRepository):$(tag)

Bu yöntemin aşağıdaki faydaları vardır:

  • Ortamın içindeki tüm kaynaklarda geçmişi kaydetmek yerine, ortam içindeki belirli bir kaynakta dağıtım geçmişini kaydeder.
  • Dağıtım işi ortama bağlı olduğundan dağıtım işindeki adımlar kaynağın bağlantı ayrıntılarını (bu örnekte kubernetes ad alanısmarthotel-dev.bookings) otomatik olarak devralır. Bu, aynı bağlantı ayrıntılarının işin birden çok adımı için ayarlandığı durumlarda kullanışlıdır.

Not

Özel bir AKS kümesi kullanıyorsanız, API sunucusu uç noktası genel IP adresi aracılığıyla gösterilmediğinden kümenin sanal ağına bağlı olduğunuzdan emin olun.

Azure Pipelines, kümenin sanal ağına erişimi olan bir sanal ağ içinde şirket içinde barındırılan bir aracı ayarlamanızı önerir. Ayrıntılar için bkz . Özel kümeye bağlanma seçenekleri.

Sıralı dağıtım stratejisi

VM'ler için sıralı strateji, her yinelemede en fazla beş hedef güncelleştirir. maxParallel , paralel olarak dağıtılacak hedef sayısını belirler. Seçim, dağıtılmakta olan hedefler hariç olmak üzere herhangi bir zamanda kullanılabilir durumda kalması gereken hedeflerin mutlak sayısını veya yüzdesini hesaplar. Ayrıca dağıtım sırasında başarı ve başarısızlık koşullarını belirlemek için de kullanılır.

jobs: 
- deployment: VMDeploy
  displayName: web
  environment:
    name: smarthotel-dev
    resourceType: VirtualMachine
  strategy:
    rolling:
      maxParallel: 5  #for percentages, mention as x%
      preDeploy:
        steps:
        - download: current
          artifact: drop
        - script: echo initialize, cleanup, backup, install certs
      deploy:
        steps:
        - task: IISWebAppDeploymentOnMachineGroup@0
          displayName: 'Deploy application to Website'
          inputs:
            WebSiteName: 'Default Web Site'
            Package: '$(Pipeline.Workspace)/drop/**/*.zip'
      routeTraffic:
        steps:
        - script: echo routing traffic
      postRouteTraffic:
        steps:
        - script: echo health check post-route traffic
      on:
        failure:
          steps:
          - script: echo Restore from backup! This is on failure
        success:
          steps:
          - script: echo Notify! This is on success

Kanarya dağıtımı stratejisi

Sonraki örnekte, AKS için kanarya stratejisi değişiklikleri önce yüzde 10 pod ile ve ardından yüzde 20 ile dağıtırken, sırasındaki postRouteTrafficsistem durumunu izler. Her şey yolunda giderse, yüzde 100'e yükseltilecektir.

jobs: 
- deployment: 
  environment: smarthotel-dev.bookings
  pool: 
    name: smarthotel-devPool
  strategy:                  
    canary:      
      increments: [10,20]  
      preDeploy:                                     
        steps:           
        - script: initialize, cleanup....   
      deploy:             
        steps: 
        - script: echo deploy updates... 
        - task: KubernetesManifest@0 
          inputs: 
            action: $(strategy.action)       
            namespace: 'default' 
            strategy: $(strategy.name) 
            percentage: $(strategy.increment) 
            manifests: 'manifest.yml' 
      postRouteTraffic: 
        pool: server 
        steps:           
        - script: echo monitor application health...   
      on: 
        failure: 
          steps: 
          - script: echo clean-up, rollback...   
        success: 
          steps: 
          - script: echo checks passed, notify... 

Adımları otomatik olarak eklemek için işlem hattı dekoratörlerini kullanma

İşlem hattı dekoratörleri , her dağıtım işinin her yaşam döngüsü kanca yürütmesine herhangi bir özel adımı (örneğin güvenlik açığı tarayıcısı) otomatik olarak eklemek için dağıtım işlerinde kullanılabilir. İşlem hattı dekoratörleri bir kuruluştaki tüm işlem hatlarına uygulanabildiğinden, bu, güvenli dağıtım uygulamalarını zorlamanın bir parçası olarak uygulanabilir.

Ayrıca dağıtım işleri, tanımlanırsa hizmetler yan araçla birlikte bir kapsayıcı işi olarak çalıştırılabilir.

Çıkış değişkenleri desteği

Dağıtım işinin yaşam döngüsü kancalarında çıkış değişkenlerini tanımlayın ve bunları aynı aşamadaki diğer aşağı akış adımlarında ve işlerinde kullanın.

Değişkenleri aşamalar arasında paylaşmak için, bir aşamada bir yapıt çıktısı alın ve bunu sonraki bir aşamada kullanın veya değişkenlerde açıklanan söz dizimini kullanınstageDependencies.

Dağıtım stratejilerini yürütürken, aşağıdaki söz dizimini kullanarak çıkış değişkenlerine işler arasında erişebilirsiniz.

  • runOnce stratejisi için: $[dependencies.<job-name>.outputs['<job-name>.<step-name>.<variable-name>']] (örneğin, $[dependencies.JobA.outputs['JobA.StepA.VariableA']])
  • runOnce stratejisi ve resourceType için: $[dependencies.<job-name>.outputs['Deploy_<resource-name>.<step-name>.<variable-name>']]. (örneğin, $[dependencies.JobA.outputs['Deploy_VM1.StepA.VariableA']])
  • Kanarya stratejisi için:$[dependencies.<job-name>.outputs['<lifecycle-hookname>_<increment-value>.<step-name>.<variable-name>']]
  • Sıralı strateji için:$[dependencies.<job-name>.outputs['<lifecycle-hookname>_<resource-name>.<step-name>.<variable-name>']]
# Set an output variable in a lifecycle hook of a deployment job executing canary strategy.
- deployment: A
  pool:
    vmImage: 'ubuntu-latest'
  environment: staging
  strategy:                  
    canary:      
      increments: [10,20]  # Creates multiple jobs, one for each increment. Output variable can be referenced with this.
      deploy:
        steps:
        - bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
          name: setvarStep
        - bash: echo $(setvarStep.myOutputVar)
          name: echovar

# Map the variable from the job.
- job: B
  dependsOn: A
  pool:
    vmImage: 'ubuntu-latest'
  variables:
    myVarFromDeploymentJob: $[ dependencies.A.outputs['deploy_10.setvarStep.myOutputVar'] ]
  steps:
  - script: "echo $(myVarFromDeploymentJob)"
    name: echovar

Bir runOnce iş için, yaşam döngüsü kancası yerine işin adını belirtin:

# Set an output variable in a lifecycle hook of a deployment job executing runOnce strategy.
- deployment: A
  pool:
    vmImage: 'ubuntu-latest'
  environment: staging
  strategy:                  
    runOnce:
      deploy:
        steps:
        - bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
          name: setvarStep
        - bash: echo $(setvarStep.myOutputVar)
          name: echovar

# Map the variable from the job.
- job: B
  dependsOn: A
  pool:
    vmImage: 'ubuntu-latest'
  variables:
    myVarFromDeploymentJob: $[ dependencies.A.outputs['A.setvarStep.myOutputVar'] ]
  steps:
  - script: "echo $(myVarFromDeploymentJob)"
    name: echovar

Dağıtım işinde bir ortam tanımladığınızda, çıktı değişkeninin söz dizimi ortamın nasıl tanımlandığına bağlı olarak değişir. Bu örnekte, env1 kısaltma gösterimini kullanır ve env2 tanımlı bir kaynak türüyle tam söz dizimini içerir.

stages:
- stage: StageA
  jobs:
  - deployment: A1
    pool:
      vmImage: 'ubuntu-latest'
    environment: env1
    strategy:                  
      runOnce:
        deploy:
          steps:
          - bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
            name: setvarStep
          - bash: echo $(System.JobName)
  - deployment: A2
    pool:
      vmImage: 'ubuntu-latest'
    environment: 
      name: env2
      resourceName: vmsfortesting
      resourceType: virtualmachine
    strategy:                  
      runOnce:
        deploy:
          steps:
          - script: echo "##vso[task.setvariable variable=myOutputVarTwo;isOutput=true]this is the second deployment variable value"
            name: setvarStepTwo
  
  - job: B1
    dependsOn: A1
    pool:
      vmImage: 'ubuntu-latest'
    variables:
      myVarFromDeploymentJob: $[ dependencies.A1.outputs['A1.setvarStep.myOutputVar'] ]
      
    steps:
    - script: "echo $(myVarFromDeploymentJob)"
      name: echovar
 
  - job: B2
    dependsOn: A2
    pool:
      vmImage: 'ubuntu-latest'
    variables:
      myVarFromDeploymentJob: $[ dependencies.A2.outputs['A2.setvarStepTwo.myOutputVarTwo'] ]
      myOutputVarTwo: $[ dependencies.A2.outputs['Deploy_vmsfortesting.setvarStepTwo.myOutputVarTwo'] ]
    
    steps:
    - script: "echo $(myOutputVarTwo)"
      name: echovartwo

Birinci aşamadaki bir işten bir değişken çıkardığınızda, sonraki aşamada bir dağıtım işinden bu değişkene başvuruda bulunmak, bir değişken ayarlamak veya bunu aşama için koşul olarak kullanmak istediğinize bağlı olarak farklı söz dizimi kullanır.

stages:
- stage: StageA
  jobs:
  - job: A1
    steps:
      - pwsh: echo "##vso[task.setvariable variable=RunStageB;isOutput=true]true"
        name: setvarStep
      - bash: echo $(System.JobName)

- stage: StageB
  dependsOn: 
    - StageA
 
  # when referring to another stage, stage name is included in variable path
  condition: eq(dependencies.StageA.outputs['A1.setvarStep.RunStageB'], 'true')
  
  # Variables reference syntax differs slightly from inter-stage condition syntax
  variables:
    myOutputVar: $[stageDependencies.StageA.A1.outputs['setvarStep.RunStageB']]
  jobs:
  - deployment: B1
    pool:
      vmImage: 'ubuntu-latest'
    environment: envB
    strategy:                  
      runOnce:
        deploy:
          steps:
          - bash: echo $(myOutputVar)

Bir dağıtım işinden bir değişken çıkardığınızda, sonraki aşamadan (örneğin, $[stageDependencies.<stage-name>.<job-name>.outputs[Deploy_<resource-name>.<step-name>.<variable-name>]]) başvurmak için stageDependencies söz dizimlerini kullanın.

stages:
- stage: StageA
  jobs:
    - deployment: A1
      environment: 
        name:  env1
        resourceName: DevEnvironmentV
        resourceType: virtualMachine
      strategy:
        runOnce:
          deploy:
            steps:
              - script: echo "##vso[task.setvariable variable=myVar;isOutput=true]true"
                name: setvarStep
              - script: |
                  echo "Value of myVar in the same Job : $(setVarStep.myVar)"
                displayName: 'Verify variable in StageA'
- stage: StageB
  dependsOn: StageA

  # Full Variables syntax for inter-stage jobs
  variables:
    myOutputVar: $[stageDependencies.StageA.A1.outputs['Deploy_DevEnvironmentV.setvarStep.myVar']]
  jobs:
  - deployment: B1
    pool:
      vmImage: 'ubuntu-latest'
    environment: envB
    strategy:                  
      runOnce:
        deploy:
          steps:
          - bash: echo $(myOutputVar)

Çok işli çıkış değişkeni ayarlama hakkında daha fazla bilgi edinin

SSS

İşlem hattım "İş bekliyor..." iletisiyle takılıyor. Bunu nasıl düzeltebilirim?

İki iş arasında bir ad çakışması olduğunda bu durum oluşabilir. Aynı aşamadaki dağıtım işlerinin benzersiz bir ada sahip olduğunu ve iş ve aşama adlarının anahtar sözcük içermediğini doğrulayın. Yeniden adlandırma sorunu çözmezse işlem hattı çalıştırmalarının sorunlarını giderme makalesini gözden geçirin.

Dekoratörler dağıtım gruplarında destekleniyor mu?

Hayır Dağıtım gruplarında dekoratörleri kullanamazsınız.