Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Actions/TrackPRDeployment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# TrackPRDeployment

Track PR deployments against the PR branch instead of the trigger branch (e.g. main).

When deploying a PR build via PublishToEnvironment, the workflow runs on main but deploys artifacts built from a PR branch. GitHub's `environment:` key auto-creates a deployment record against main, which is misleading. This action deactivates that record and creates a new deployment against the actual PR branch, so the deployment shows correctly on the PR.

## INPUT

### ENV variables

None

### Parameters

| Name | Required | Description | Default value |
| :-- | :-: | :-- | :-- |
| shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell |
| token | | The GitHub token running the action | github.token |
| environmentsMatrixJson | Yes | JSON string with the environments matrix from Initialization | |
| deployResult | Yes | The result of the Deploy job (success or failure) | |
| artifactsVersion | Yes | Artifacts version (PR\_\<number>) | |
| sha | | The commit SHA of the workflow run, used to identify the correct auto-created deployment | github.sha |

## OUTPUT

None
163 changes: 163 additions & 0 deletions Actions/TrackPRDeployment/TrackPRDeployment.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
Param(
[Parameter(Mandatory = $false)]
[string] $token = $ENV:GITHUB_TOKEN,
[Parameter(Mandatory = $true)]
[string] $environmentsMatrixJson,
[Parameter(Mandatory = $true)]
[string] $deployResult,
[Parameter(Mandatory = $true)]
[string] $artifactsVersion,
[Parameter(Mandatory = $false)]
[string] $sha = $ENV:GITHUB_SHA
)

. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve)

<#
.SYNOPSIS
Deactivate auto-created deployments and retrieve the environment URL.
.DESCRIPTION
The environment: key on the Deploy job auto-creates a deployment against the trigger ref (e.g. main).
This function finds those deployments, retrieves the environment URL from the latest status, and
deactivates them so they no longer show as active.
#>
function DeactivateAutoDeployments {
Param(
[hashtable] $headers,
[string] $repository,
[string] $environmentName,
[string] $triggerRef,
[string] $sha
)

$apiBase = "https://api.github.com/repos/$repository"
$encodedEnv = [System.Uri]::EscapeDataString($environmentName)
$envUrl = $null

$logUrl = $null

$listUri = "$apiBase/deployments?environment=$encodedEnv&ref=$triggerRef&sha=$sha&per_page=1"
OutputDebug "GET $listUri"
$existingDeps = (InvokeWebRequest -Headers $headers -Uri $listUri).Content | ConvertFrom-Json
if ($existingDeps -and @($existingDeps).Count -gt 0) {
$dep = @($existingDeps)[0]
OutputDebug "Found auto-created deployment $($dep.id) (ref: $($dep.ref), sha: $($dep.sha))"
$statusesUri = "$apiBase/deployments/$($dep.id)/statuses?per_page=1"
OutputDebug "GET $statusesUri"
$statuses = (InvokeWebRequest -Headers $headers -Uri $statusesUri).Content | ConvertFrom-Json
if ($statuses -and @($statuses).Count -gt 0) {
OutputDebug "Latest status: state=$(@($statuses)[0].state), environment_url=$(@($statuses)[0].environment_url), log_url=$(@($statuses)[0].log_url)"
if (@($statuses)[0].environment_url) {
$envUrl = @($statuses)[0].environment_url
}
if (@($statuses)[0].log_url) {
$logUrl = @($statuses)[0].log_url
}
if (@($statuses)[0].state -ne 'inactive') {
Write-Host "Deactivating auto-created deployment $($dep.id) (ref: $($dep.ref))"
$deactivateUri = "$apiBase/deployments/$($dep.id)/statuses"
OutputDebug "POST $deactivateUri (state: inactive)"
InvokeWebRequest -Headers $headers -Method 'POST' -Uri $deactivateUri -Body '{"state":"inactive"}' | Out-Null
}
else {
Write-Host "Auto-created deployment $($dep.id) is already inactive, skipping"
}
}
}
else {
OutputDebug "No auto-created deployment found for environment=$environmentName, ref=$triggerRef, sha=$sha"
}

return @{
EnvironmentUrl = $envUrl
LogUrl = $logUrl
}
}

<#
.SYNOPSIS
Create a deployment record against the PR branch and set its status.
#>
function CreatePRDeployment {
Param(
[hashtable] $headers,
[string] $repository,
[string] $prRef,
[string] $prNumber,
[string] $environmentName,
[string] $environmentUrl,
[string] $logUrl,
[string] $state
)

$apiBase = "https://api.github.com/repos/$repository"

$deployBody = @{
ref = $prRef
environment = $environmentName
auto_merge = $false
required_contexts = @()
description = "Deployed via PublishToEnvironment (PR #$prNumber)"
} | ConvertTo-Json -Compress

$createUri = "$apiBase/deployments"
OutputDebug "POST $createUri (ref: $prRef, environment: $environmentName)"
$deployment = (InvokeWebRequest -Headers $headers -Method 'POST' -Uri $createUri -Body $deployBody).Content | ConvertFrom-Json
Write-Host "Created deployment $($deployment.id) against $prRef"

$statusBody = @{
state = $state
environment = $environmentName
description = "Deployed PR #$prNumber to $environmentName"
}
if ($environmentUrl) { $statusBody['environment_url'] = $environmentUrl }
if ($logUrl) { $statusBody['log_url'] = $logUrl }
$statusJson = $statusBody | ConvertTo-Json -Compress

$statusUri = "$apiBase/deployments/$($deployment.id)/statuses"
OutputDebug "POST $statusUri (state: $state)"
InvokeWebRequest -Headers $headers -Method 'POST' -Uri $statusUri -Body $statusJson | Out-Null
Write-Host "Deployment status set to $state for $environmentName"
}

# Main
$prNumber = $artifactsVersion.Substring(3)
$repo = $ENV:GITHUB_REPOSITORY
$triggerRef = $ENV:GITHUB_REF_NAME
$state = if ($deployResult -eq 'success') { 'success' } else { 'failure' }

OutputDebug "PR number: $prNumber, repository: $repo, triggerRef: $triggerRef, sha: $sha, deployResult: $deployResult"

$headers = GetHeaders -token $token

# Get PR branch ref using existing helper from Deploy.psm1
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath "..\Deploy\Deploy.psm1" -Resolve)
$prRef = GetHeadRefFromPRId -repository $repo -prId $prNumber -token $token
if (-not $prRef) {
throw "Could not determine PR branch for PR #$prNumber"
}
Write-Host "PR #$prNumber branch: $prRef"

# Parse environments from the matrix JSON
$matrix = $environmentsMatrixJson | ConvertFrom-Json
$environments = @($matrix.matrix.include | ForEach-Object { $_.environment })
OutputDebug "Environments to process: $($environments -join ', ')"

foreach ($envName in $environments) {
Write-Host "Tracking deployment for environment: $envName"

$deploymentInfo = $null
try {
$deploymentInfo = DeactivateAutoDeployments -headers $headers -repository $repo -environmentName $envName -triggerRef $triggerRef -sha $sha
}
catch {
OutputWarning -message "Could not deactivate auto-created deployment for $envName`: $($_.Exception.Message)"
}

try {
CreatePRDeployment -headers $headers -repository $repo -prRef $prRef -prNumber $prNumber -environmentName $envName -environmentUrl $deploymentInfo.EnvironmentUrl -logUrl $deploymentInfo.LogUrl -state $state
}
catch {
OutputWarning -message "Failed to create PR deployment for $envName`: $($_.Exception.Message)"
}
}
42 changes: 42 additions & 0 deletions Actions/TrackPRDeployment/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: TrackPRDeployment
author: Microsoft Corporation
inputs:
shell:
description: Shell in which you want to run the action (powershell or pwsh)
required: false
default: powershell
token:
description: The GitHub token running the action
required: false
default: ${{ github.token }}
environmentsMatrixJson:
description: JSON string with the environments matrix from Initialization
required: true
deployResult:
description: The result of the Deploy job (success or failure)
required: true
artifactsVersion:
description: Artifacts version (PR_<number>)
required: true
sha:
description: The commit SHA of the workflow run, used to identify the correct auto-created deployment
required: false
default: ${{ github.sha }}
runs:
using: composite
steps:
- name: run
shell: ${{ inputs.shell }}
env:
_token: ${{ inputs.token }}
_environmentsMatrixJson: ${{ inputs.environmentsMatrixJson }}
_deployResult: ${{ inputs.deployResult }}
_artifactsVersion: ${{ inputs.artifactsVersion }}
_sha: ${{ inputs.sha }}
run: |
${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "TrackPRDeployment" -Action {
${{ github.action_path }}/TrackPRDeployment.ps1 -token $ENV:_token -environmentsMatrixJson $ENV:_environmentsMatrixJson -deployResult $ENV:_deployResult -artifactsVersion $ENV:_artifactsVersion -sha $ENV:_sha
}
branding:
icon: terminal
color: blue
5 changes: 5 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

- Issue 2107 Publish a specific build mode to an environment
- Issue 1915 CICD fails on releases/26.x branch - '26.x' cannot be recognized as a semantic version string
- Issue 2118 Deployments from "Publish To Environment" not tracked against PR branch

### PR deployment tracking

When deploying a PR build via "Publish To Environment", the deployment is now correctly tracked against the PR branch instead of the trigger branch (e.g. main). Previously, GitHub would show the deployment against the latest commit on main, which was misleading. A new `TrackPRDeployment` action runs after the deploy job to deactivate the auto-created deployment and create one pointing to the actual PR branch.

### The default pull request trigger is changing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ on:
permissions:
actions: read
contents: read
deployments: write
id-token: write
pull-requests: read
checks: read
Expand Down Expand Up @@ -195,6 +196,22 @@ jobs:
artifactsFolder: '.artifacts'
deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }}

TrackPRDeployment:
needs: [ Initialization, Deploy ]
if: always() && (needs.Deploy.result == 'success' || needs.Deploy.result == 'failure') && startsWith(github.event.inputs.appVersion, 'PR_')
runs-on: [ windows-latest ]
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Track PR Deployment
uses: microsoft/AL-Go-Actions/TrackPRDeployment@main
with:
shell: powershell
environmentsMatrixJson: ${{ needs.Initialization.outputs.environmentsMatrixJson }}
deployResult: ${{ needs.Deploy.result }}
artifactsVersion: ${{ github.event.inputs.appVersion }}

PostProcess:
needs: [ Initialization, Deploy ]
if: always()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ on:
permissions:
actions: read
contents: read
deployments: write
id-token: write
pull-requests: read
checks: read
Expand Down Expand Up @@ -195,6 +196,22 @@ jobs:
artifactsFolder: '.artifacts'
deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }}

TrackPRDeployment:
needs: [ Initialization, Deploy ]
if: always() && (needs.Deploy.result == 'success' || needs.Deploy.result == 'failure') && startsWith(github.event.inputs.appVersion, 'PR_')
runs-on: [ windows-latest ]
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Track PR Deployment
uses: microsoft/AL-Go-Actions/TrackPRDeployment@main
with:
shell: powershell
environmentsMatrixJson: ${{ needs.Initialization.outputs.environmentsMatrixJson }}
deployResult: ${{ needs.Deploy.result }}
artifactsVersion: ${{ github.event.inputs.appVersion }}

PostProcess:
needs: [ Initialization, Deploy ]
if: always()
Expand Down
28 changes: 28 additions & 0 deletions Tests/TrackPRDeployment.Action.Test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Get-Module TestActionsHelper | Remove-Module -Force
Import-Module (Join-Path $PSScriptRoot 'TestActionsHelper.psm1')
$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0

Describe "TrackPRDeployment Action Tests" {
BeforeAll {
$actionName = "TrackPRDeployment"
$scriptRoot = Join-Path $PSScriptRoot "..\Actions\$actionName" -Resolve
$scriptName = "$actionName.ps1"
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptPath', Justification = 'False positive.')]
$scriptPath = Join-Path $scriptRoot $scriptName
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'actionScript', Justification = 'False positive.')]
$actionScript = GetActionScript -scriptRoot $scriptRoot -scriptName $scriptName
}

It 'Compile Action' {
Invoke-Expression $actionScript
}

It 'Test action.yaml matches script' {
$outputs = [ordered]@{
}
YamlTest -scriptRoot $scriptRoot -actionName $actionName -actionScript $actionScript -outputs $outputs
}

# Call action

}
Loading