Skip to content
Open
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
80 changes: 80 additions & 0 deletions Actions/.Modules/CompileFromWorkspace.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -846,10 +846,90 @@ function New-BuildOutputFile {
return $buildOutputPath
}

<#
.SYNOPSIS
Generates AppSourceCop.json files for app folders with baseline version information.
.DESCRIPTION
For each app folder, creates an AppSourceCop.json file containing the previous version
of the app as a baseline for breaking change detection. Also includes mandatory affixes
and obsolete tag settings from the project settings.
.PARAMETER AppFolders
Array of app folder paths to generate AppSourceCop.json for.
.PARAMETER PreviousApps
Array of file paths to previous release .app files.
.PARAMETER CompilerFolder
Path to the compiler folder containing the AL tool.
.PARAMETER Settings
Hashtable containing the build settings with AppSourceCop configuration.
#>
function New-AppSourceCopJson {
param(
[Parameter(Mandatory = $true)]
[string[]] $AppFolders,
[Parameter(Mandatory = $false)]
[string[]] $PreviousApps = @(),
[Parameter(Mandatory = $true)]
[string] $CompilerFolder,
[Parameter(Mandatory = $true)]
[hashtable] $Settings
)

# Extract version info from previous apps using the AL tool
$previousAppVersions = @{}
$alToolPath = Get-ALTool -CompilerFolder $CompilerFolder
foreach ($appFile in $PreviousApps) {
try {
$appInfo = RunAndCheck $alToolPath GetPackageManifest $appFile | ConvertFrom-Json
$key = "$($appInfo.Publisher)_$($appInfo.Name)"
$previousAppVersions[$key] = $appInfo.Version.ToString()
}
catch {
OutputWarning -message "Failed to read manifest from '$appFile': $($_.Exception.Message)"
}
}

# Create AppSourceCop.json for each app folder with the previous version as baseline and settings from the project configuration
foreach ($folder in $AppFolders) {
$appSourceCopJsonFile = Join-Path $folder "AppSourceCop.json"
$appSourceCopJson = @{}

# Set mandatory affixes if specified in settings
if ($Settings.appSourceCopMandatoryAffixes -and $Settings.appSourceCopMandatoryAffixes.Count -gt 0) {
$appSourceCopJson["mandatoryAffixes"] = @() + $Settings.appSourceCopMandatoryAffixes
}

# Set obsolete tag minimum allowed major.minor version if specified in settings
if ($Settings.obsoleteTagMinAllowedMajorMinor) {
$appSourceCopJson["obsoleteTagMinAllowedMajorMinor"] = $Settings.obsoleteTagMinAllowedMajorMinor
}

# Match previous app version by Publisher_Name
$appJsonPath = Join-Path $folder "app.json"
if (Test-Path $appJsonPath) {
$appJson = Get-Content -Path $appJsonPath -Raw | ConvertFrom-Json
$key = "$($appJson.Publisher)_$($appJson.Name)"
if ($previousAppVersions.ContainsKey($key)) {
$appSourceCopJson["Publisher"] = $appJson.Publisher
$appSourceCopJson["Name"] = $appJson.Name
$appSourceCopJson["Version"] = $previousAppVersions[$key]
}
}

if ($appSourceCopJson.Count -gt 0) {
Write-Host "Creating AppSourceCop.json for $folder"
$appSourceCopJson | ConvertTo-Json -Depth 99 | Set-Content -Encoding UTF8 $appSourceCopJsonFile
}
elseif (Test-Path $appSourceCopJsonFile) {
Remove-Item $appSourceCopJsonFile -Force
}
}
}

Export-ModuleMember -Function Build-AppsInWorkspace
Export-ModuleMember -Function New-BuildOutputFile
Export-ModuleMember -Function Get-BuildMetadata
Export-ModuleMember -Function Get-CodeAnalyzers
Export-ModuleMember -Function Get-CustomAnalyzers
Export-ModuleMember -Function Get-AssemblyProbingPaths
Export-ModuleMember -Function Update-AppJsonProperties
Export-ModuleMember -Function New-AppSourceCopJson
37 changes: 31 additions & 6 deletions Actions/CompileApps/Compile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ Param(
[Parameter(HelpMessage = "RunId of the baseline workflow run", Mandatory = $false)]
[string] $baselineWorkflowRunId = '0',
[Parameter(HelpMessage = "SHA of the baseline workflow run", Mandatory = $false)]
[string] $baselineWorkflowSHA = ''
[string] $baselineWorkflowSHA = '',
[Parameter(HelpMessage = "Path to folder containing previous release apps for AppSourceCop baseline", Mandatory = $false)]
[string] $previousAppsPath = ''
)

. (Join-Path -Path $PSScriptRoot -ChildPath "..\AL-Go-Helper.ps1" -Resolve)
Expand All @@ -30,7 +32,7 @@ $settings = AnalyzeRepo -settings $settings -baseFolder $baseFolder -project $pr
$settings = CheckAppDependencyProbingPaths -settings $settings -token $token -baseFolder $baseFolder -project $project

# Check if there are any app folders or test app folders to compile
if ($settings.appFolders.Count -eq 0 -and $settings.testFolders.Count -eq 0) {
if ($settings.appFolders.Count -eq 0 -and $settings.testFolders.Count -eq 0 -and $settings.bcptTestFolders.Count -eq 0) {
Write-Host "No app folders or test app folders specified for compilation. Skipping compilation step."
return
}
Expand Down Expand Up @@ -117,13 +119,24 @@ try {
Write-Host "Incremental builds based on modified apps is not yet implemented."
}

if ((-not $settings.skipUpgrade) -and $settings.enableAppSourceCop) {
# TODO: Missing implementation of around using latest release as a baseline (skipUpgrade) / Appsourcecop.json baseline implementation (AB#620310)
Write-Host "Checking for required upgrades using AppSourceCop..."
$previousApps = @()
if ($previousAppsPath -and (Test-Path $previousAppsPath)) {
$previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName })
if ($previousApps.Count -gt 0) {
# Copy previous apps to the package cache so AppSourceCop can resolve them
$previousApps | ForEach-Object {
Copy-Item -Path $_ -Destination $packageCachePath -Force
}
}
}

if ($settings.enableAppSourceCop) {
# Generate AppSourceCop.json files for app folders with baseline version and settings
New-AppSourceCopJson -AppFolders $settings.appFolders -PreviousApps $previousApps -CompilerFolder $compilerFolder -Settings $settings
}

# Update the app jsons with version number (and other properties) from the app manifest files
Update-AppJsonProperties -Folders ($settings.appFolders + $settings.testFolders) `
Update-AppJsonProperties -Folders ($settings.appFolders + $settings.testFolders + $settings.bcptTestFolders) `
-MajorMinorVersion $versionNumber.MajorMinorVersion -BuildNumber $versionNumber.BuildNumber -RevisionNumber $versionNumber.RevisionNumber `
-BuildBy $buildMetadata.BuildBy -BuildUrl $buildMetadata.BuildUrl

Expand Down Expand Up @@ -174,6 +187,18 @@ try {
-AppType 'testApp'
}

if ($settings.bcptTestFolders.Count -gt 0) {
if (-not ($settings.enableCodeAnalyzersOnTestApps)) {
$buildParams.Analyzers = @()
}

# Compile BCPT Test Apps
$testAppFiles += Build-AppsInWorkspace @buildParams `
-Folders $settings.bcptTestFolders `
-OutFolder $testAppOutputFolder `
-AppType 'bcptApp'
}

} finally {
New-BuildOutputFile -BuildArtifactFolder $buildArtifactFolder -BuildOutputPath (Join-Path $projectFolder "BuildOutput.txt") -DisplayInConsole -FailOn $settings.failOn
}
Expand Down
7 changes: 6 additions & 1 deletion Actions/CompileApps/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ inputs:
description: SHA of the baseline workflow run
required: false
default: ''
previousAppsPath:
description: Path to folder containing previous release apps for AppSourceCop baseline
required: false
default: ''
runs:
using: composite
steps:
Expand All @@ -51,9 +55,10 @@ runs:
_dependencyTestAppsJson: ${{ inputs.dependencyTestAppsJson }}
_baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }}
_baselineWorkflowSHA: ${{ inputs.baselineWorkflowSHA }}
_previousAppsPath: ${{ inputs.previousAppsPath }}
run: |
${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "Compile Apps" -Action {
${{ github.action_path }}/Compile.ps1 -token $ENV:_token -artifact $ENV:_artifact -project $ENV:_project -buildMode $ENV:_buildMode -dependencyAppsJson $ENV:_dependencyAppsJson -dependencyTestAppsJson $ENV:_dependencyTestAppsJson -baselineWorkflowRunId $ENV:_baselineWorkflowRunId -baselineWorkflowSHA $ENV:_baselineWorkflowSHA
${{ github.action_path }}/Compile.ps1 -token $ENV:_token -artifact $ENV:_artifact -project $ENV:_project -buildMode $ENV:_buildMode -dependencyAppsJson $ENV:_dependencyAppsJson -dependencyTestAppsJson $ENV:_dependencyTestAppsJson -baselineWorkflowRunId $ENV:_baselineWorkflowRunId -baselineWorkflowSHA $ENV:_baselineWorkflowSHA -previousAppsPath $ENV:_previousAppsPath
}
branding:
icon: terminal
Expand Down
38 changes: 38 additions & 0 deletions Actions/DownloadPreviousRelease/DownloadPreviousRelease.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Param(
[Parameter(HelpMessage = "The GitHub token running the action", Mandatory = $false)]
[string] $token,
[Parameter(HelpMessage = "Project folder", Mandatory = $false)]
[string] $project = ""
)

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

$baseFolder = (Get-BasePath)
$projectFolder = Join-Path $baseFolder $project
$previousAppsPath = Join-Path $projectFolder ".previousRelease"
New-Item $previousAppsPath -ItemType Directory -Force | Out-Null

OutputGroupStart -Message "Locating previous release"
try {
$branchForRelease = if ($ENV:GITHUB_BASE_REF) { $ENV:GITHUB_BASE_REF } else { $ENV:GITHUB_REF_NAME }
$latestRelease = GetLatestRelease -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -ref $branchForRelease
if ($latestRelease) {
Write-Host "Using $($latestRelease.name) (tag $($latestRelease.tag_name)) as previous release"
DownloadRelease -token $token -projects $project -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $latestRelease -path $previousAppsPath -mask "Apps" -unpack
$previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName })
Write-Host "Downloaded $($previousApps.Count) previous release app(s)"
}
else {
OutputWarning -message "No previous release found"
}
}
catch {
OutputError -message "Error trying to locate previous release. Error was $($_.Exception.Message)"
exit
}
finally {
OutputGroupEnd
}

# Output variable with path to previous apps for use in subsequent steps
Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "PreviousAppsPath=$previousAppsPath"
26 changes: 26 additions & 0 deletions Actions/DownloadPreviousRelease/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Download Previous Release

Downloads the latest release apps for use as a baseline in AppSourceCop validation and upgrade testing.

## INPUT

### ENV variables

| Name | Description |
| :-- | :-- |
| Settings | env.Settings must be set by a prior call to the ReadSettings Action |

### Parameters

| Name | Required | Description | Default value |
| :-- | :-: | :-- | :-- |
| shell | | The shell (powershell or pwsh) in which the PowerShell script in this action should run | powershell |
| project | | The AL-Go project for which to download previous release apps | '.' |

## OUTPUT

### OUTPUT variables

| Name | Description |
| :-- | :-- |
| PreviousAppsPath | Path to the folder containing the downloaded previous release apps. |
35 changes: 35 additions & 0 deletions Actions/DownloadPreviousRelease/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Download Previous Release
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 }}
project:
description: Project folder
required: false
default: '.'
outputs:
PreviousAppsPath:
description: Path to the folder containing the downloaded previous release apps.
value: ${{ steps.DownloadPreviousRelease.outputs.PreviousAppsPath }}
runs:
using: composite
steps:
- name: run
shell: ${{ inputs.shell }}
id: DownloadPreviousRelease
env:
_token: ${{ inputs.token }}
_project: ${{ inputs.project }}
run: |
${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "DownloadPreviousRelease" -Action {
${{ github.action_path }}/DownloadPreviousRelease.ps1 -token $ENV:_token -project $ENV:_project
}
branding:
icon: terminal
color: blue
56 changes: 18 additions & 38 deletions Actions/RunPipeline/RunPipeline.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ Param(
[Parameter(HelpMessage = "RunId of the baseline workflow run", Mandatory = $false)]
[string] $baselineWorkflowRunId = '0',
[Parameter(HelpMessage = "SHA of the baseline workflow run", Mandatory = $false)]
[string] $baselineWorkflowSHA = ''
[string] $baselineWorkflowSHA = '',
[Parameter(HelpMessage = "Path to folder containing previous release apps for upgrade testing", Mandatory = $false)]
[string] $previousAppsPath = ''
)

$containerBaseFolder = $null
Expand Down Expand Up @@ -138,17 +140,12 @@ try {
OutputDebug -message "Build artifacts folder $buildArtifactFolder already exists. Previous build artifacts might interfere with the current build."
}

# When using workspace compilation, apps are already compiled - pass empty folders to Run-AlPipeline
if ($settings.workspaceCompilation.enabled) {
$appFolders = @()
$testFolders = @()
$bcptTestFolders = $settings.bcptTestFolders
}
else {
$appFolders = $settings.appFolders
$testFolders = $settings.testFolders
$bcptTestFolders = $settings.bcptTestFolders
}
# When using workspace compilation, apps are already compiled in .buildartifacts.
# Pass appFolders/testFolders to Run-AlPipeline so its prebuilt detection picks them up
# and publishes them through the proper upgrade testing path.
$appFolders = $settings.appFolders
$testFolders = $settings.testFolders
$bcptTestFolders = $settings.bcptTestFolders

if ((-not $settings.workspaceCompilation.enabled) -and $baselineWorkflowSHA -and $baselineWorkflowRunId -ne '0' -and $settings.incrementalBuilds.mode -eq 'modifiedApps') {
# Incremental builds are enabled and we are only building modified apps
Expand Down Expand Up @@ -275,33 +272,16 @@ try {

$previousApps = @()
if (!$settings.skipUpgrade) {
if ($settings.workspaceCompilation.enabled) {
OutputWarning -message "skipUpgrade is ignored when workspaceCompilation is enabled." # TODO: Missing implementation when workspace compilation is enabled (AB#620310)
} else {
OutputGroupStart -Message "Locating previous release"
try {
$branchForRelease = if ($ENV:GITHUB_BASE_REF) { $ENV:GITHUB_BASE_REF } else { $ENV:GITHUB_REF_NAME }
$latestRelease = GetLatestRelease -token $token -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -ref $branchForRelease
if ($latestRelease) {
Write-Host "Using $($latestRelease.name) (tag $($latestRelease.tag_name)) as previous release"
$artifactsFolder = Join-Path $baseFolder "artifacts"
if(-not (Test-Path $artifactsFolder)) {
New-Item $artifactsFolder -ItemType Directory | Out-Null
}
DownloadRelease -token $token -projects $project -api_url $ENV:GITHUB_API_URL -repository $ENV:GITHUB_REPOSITORY -release $latestRelease -path $artifactsFolder -mask "Apps"
$previousApps += @(Get-ChildItem -Path $artifactsFolder | ForEach-Object { $_.FullName })
}
else {
OutputWarning -message "No previous release found"
}
}
catch {
OutputError -message "Error trying to locate previous release. Error was $($_.Exception.Message)"
exit
}
finally {
OutputGroupEnd
if ($previousAppsPath -and (Test-Path $previousAppsPath)) {
# Use previously downloaded release apps
$previousApps = @(Get-ChildItem -Path $previousAppsPath -Recurse -Filter "*.app" | ForEach-Object { $_.FullName })
if ($previousApps.Count -gt 0) {
Write-Host "Using $($previousApps.Count) previous release app(s) from $previousAppsPath"
} else {
OutputWarning -message "No .app files found in '$previousAppsPath' for upgrade testing."
}
} else {
OutputWarning -message "Could not locate previous release apps for upgrade testing."
}
}

Expand Down
7 changes: 6 additions & 1 deletion Actions/RunPipeline/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ inputs:
description: SHA of the baseline workflow run
required: false
default: ''
previousAppsPath:
description: Path to folder containing previous release apps for upgrade testing
required: false
default: ''
runs:
using: composite
steps:
Expand All @@ -51,9 +55,10 @@ runs:
_installTestAppsJson: ${{ inputs.installTestAppsJson }}
_baselineWorkflowRunId: ${{ inputs.baselineWorkflowRunId }}
_baselineWorkflowSHA: ${{ inputs.baselineWorkflowSHA }}
_previousAppsPath: ${{ inputs.previousAppsPath }}
run: |
${{ github.action_path }}/../Invoke-AlGoAction.ps1 -ActionName "RunPipeline" -Action {
${{ github.action_path }}/RunPipeline.ps1 -token $ENV:_token -artifact $ENV:_artifact -project $ENV:_project -buildMode $ENV:_buildMode -installAppsJson $ENV:_installAppsJson -installTestAppsJson $ENV:_installTestAppsJson -baselineWorkflowRunId $ENV:_baselineWorkflowRunId -baselineWorkflowSHA $ENV:_baselineWorkflowSHA
${{ github.action_path }}/RunPipeline.ps1 -token $ENV:_token -artifact $ENV:_artifact -project $ENV:_project -buildMode $ENV:_buildMode -installAppsJson $ENV:_installAppsJson -installTestAppsJson $ENV:_installTestAppsJson -baselineWorkflowRunId $ENV:_baselineWorkflowRunId -baselineWorkflowSHA $ENV:_baselineWorkflowSHA -previousAppsPath $ENV:_previousAppsPath
}
branding:
icon: terminal
Expand Down
Loading
Loading