> For the complete documentation index, see [llms.txt](https://jadelab.gitbook.io/jadegit/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jadelab.gitbook.io/jadegit/0.18.0/ci-cd/pipeline/deploy.md).

# Deploy

This stage applies changes to a database within a specified environment, as opposed to the testing stage which uses an ephemeral database within the pipeline agent workspace.

The overall flow follows the fundamental steps [described previously](https://jadelab.gitbook.io/jadegit/developers/deployment), retrieving the current version information from the target database, building a deployment, and applying it. In this context, these steps are distributed across two agents: the database agent, which interacts directly with the target database, and the cloud agent, which handles the build.

```mermaid
sequenceDiagram
    participant Pipeline
    participant Agent as Database Agent
    participant Database

    Pipeline->>Agent: Retrieve current version
    Agent->>Database: Extract registry
    Database-->>Agent: Registry
    Agent-->>Pipeline: Registry

    Note over Pipeline: Build deployment

    Pipeline->>Agent: Apply deployment
    Agent->>Database: Load deployment
    Database-->>Agent: Result
    Agent-->>Pipeline: Result
```

## Online

An online deployment occurs while the database is active, and is only suitable for changes that can be safely applied without exclusive access.

{% hint style="warning" icon="screwdriver-wrench" %}
The build process needs improvement to distinguish what is safe for online deployment. In the meantime, applications need to be stopped manually beforehand.
{% endhint %}

## Offline

An offline deployment requires the database to be taken offline while changes are applied.

{% hint style="info" %}
The templates referenced by the example below to stop and restart the database are not covered as their implementation depends on how databases are run.
{% endhint %}

## Example

The `deploy.yml` template handles the full deployment stage for each database, running three jobs:

* An initialisation job that retrieves the current version from the database agent
* A build job that runs on a cloud agent to prepare the deployment
* A deployment job that applies the changes to the database

The initialisation and deployment jobs make use of the environment variables defined by the [database agent](https://jadelab.gitbook.io/jadegit/ci-cd/database-agents).

When multiple databases are specified, subsequent databases wait for the first to complete, which ensures at least one remains available.

```yaml
parameters:
- name: environment
  type: string
- name: databases
  type: object
- name: online
  type: boolean
  default: false
- name: merge
  type: boolean
  default: false

jobs:
- ${{ each database in parameters.databases }}:
  - deployment: init_${{ database }}
    displayName: Initialize (${{ database }})
    environment:
      name: ${{ parameters.environment }}
      resourceName: ${{ database }}
    strategy:
      runOnce:
        deploy:
          steps:
            - checkout: none

            - powershell: |
                $verinfo = (& $env:JADE_BIN\jverinfo out=stdout binmask=jade.exe sysmask=. datmask=. libmask=. noThinClients noExtraDirs) | Select-String -Pattern '(?m)^jade\.exe\s*(?<version>\d+\.\d+\.\d+)\.\d+.*-(?<charset>ansi|unicode).*$'
                $version = $verinfo | ForEach-Object { return $($_.Matches[0].Groups['version'].Value); }
                $charset = $verinfo | ForEach-Object { return $($_.Matches[0].Groups['charset'].Value); }
                Write-Host "##vso[task.setvariable variable=version]$version"
                Write-Host "##vso[task.setvariable variable=charset]$charset"
              displayName: "Get Version Info"

            - task: UniversalPackages@0
              displayName: "Download JadeGit"
              inputs:
                command: download
                vstsFeed: '$(jade.feed)'
                vstsFeedPackage: 'jadegit-dev-$(version)-$(charset)'
                vstsPackageVersion: '$(jadegit.version)'
                downloadDirectory: $(JADE_BIN)
            
            - powershell: |
                & $env:JADE_BIN\jadegit -p "$env:JADE_PATH" -i "$env:JADE_INI" -m registry extract "$(Build.StagingDirectory)\current.jgr"
              displayName: "Extract Registry"

            - publish: $(Build.StagingDirectory)
              artifact: "${{ parameters.environment }}-${{ database }}-registry"
              displayName: "Publish Registry"

  - job: build_${{ database }}
    displayName: Build (${{ database }})
    dependsOn: init_${{ database }}
    pool:
      vmImage: 'windows-2022'
    steps:
    - checkout: self
      persistCredentials: true
    
    - download: current
      artifact: "${{ parameters.environment }}-${{ database }}-registry"
      displayName: "Download Registry"

    - task: UniversalPackages@0
      displayName: "Download JadeGit"
      inputs:
        command: download
        vstsFeed: '$(jade.feed)'
        vstsFeedPackage: 'jadegit-console'
        vstsPackageVersion: '$(jadegit.version)'
        downloadDirectory: $(Agent.TempDirectory)

    - powershell: |
        & $(Agent.TempDirectory)\jadegit registry show "$(Pipeline.Workspace)\${{ parameters.environment }}-${{ database }}-registry\current.jgr"
        $options = @()
        if ("${{ parameters.merge }}" -eq $true)
        {
          $options += "--merge"
        }

        # TODO: Specify alternate build format parameters based on pipeline parameters and/or environmental factors
        $format = @("pwsh", "$(Build.StagingDirectory)")

        & $(Agent.TempDirectory)\jadegit --log-progress-format "##vso[task.setprogress value={}]" build --registry "$(Pipeline.Workspace)\${{ parameters.environment }}-${{ database }}-registry\current.jgr" $options $format
      displayName: "Build Deployment"

    - publish: $(Build.StagingDirectory)
      artifact: "${{ parameters.environment }}-${{ database }}-deployment"
      displayName: "Publish Deployment"

  - deployment: deployment_${{ database }}
    ${{ if eq(parameters.online, 'True') }}:
      displayName: Online Deployment (${{ database }})
    ${{ else }}:
      displayName: Offline Deployment (${{ database }})
    dependsOn:
    - init_${{ database }}
    - build_${{ database }}
    - ${{ if ne(parameters.databases[0], database) }}:
      - deployment_${{ parameters.databases[0] }}
    variables:
      server: $[ iif(${{ parameters.online }}, 'multiUser', 'singleUser') ]
    environment:
      name: ${{ parameters.environment }}
      resourceName: ${{ database }}
    workspace:
      clean: all
    timeoutInMinutes: 0
    strategy:
      runOnce:
        preDeploy:
          steps:
            - checkout: none
            - template: ..\database\stop.yml
              parameters:
                condition: and(succeeded(), not(${{ parameters.online }}))

        deploy:
          steps:
            - checkout: none
            - powershell: |
                & "$(Pipeline.Workspace)\${{ parameters.environment }}-${{ database }}-deployment\deploy" -bin "$env:JADE_BIN" -path "$env:JADE_PATH" -ini "$env:JADE_INI" -server $(server)
              displayName: "Run Deployment Script"

        on:
          success:
            steps:
              - checkout: none
              - template: ..\database\start.yml
                parameters:
                  condition: and(succeeded(), not(${{ parameters.online }}))
                  
          failure:
            steps:
              - checkout: none
              - template: ..\database\start.yml
                parameters:
                  condition: and(succeeded(), not(${{ parameters.online }}))
```

### Deploy Pipeline

Like the main CI pipeline, each code repository may have a minimal deploy pipeline definition that extends the shared template, specifying its associated databases to be deployed to within each environment.

As this pipeline is triggered manually and doesn't depend on tests passing first, users have direct control over when and where a feature branch is deployed during development and testing.

```yaml
trigger: none

parameters:
- name: environment
  type: string
  displayName: Target Environment

- name: online
  type: boolean
  displayName: Online
  default: true

- name: merge
  type: boolean
  displayName: Merge
  default: true

resources:
  repositories:
  - repository: admin
    type: git
    name: Jade.Admin

variables:
- template: ci/variables.yml@admin

extends:
  template: ci/deploy.yml@admin
  parameters:
    environment: ${{ parameters.environment }}
    online: ${{ parameters.online }}
    merge: ${{ parameters.merge }}
    databases:
    - delorean
```
