Skip to content

Commit 23b5dde

Browse files
OleWunschmannOle WunschmennmazhelezCopilot
authored
Add trigger 'workflow_call' to workflow 'UpdateGitHubGoSystemFiles' for reusability (#2031)
### ❔What, Why & How What: This change enables the update workflow to be called by other workflows as a reusable workflow. Why: Creating the second (or subsequent) project using one of the app creation workflows results in an incorrect “.AL-Go” directory in the created project. This creates an even bigger problem for our custom template, as our template contains additional files that are missing from the “.AL-Go” directories of new projects. As discussed in #1855, there should be an actual solution that requires a revision of the current logic for changing and committing files. In the short term, however, we want to call the update workflow from a custom job in each app creation workflow. Related to issue: #1855 ### ✅ Checklist - [x] Add/Update unit tests - [x] Update RELEASENOTES.md - [x] Update documentation --------- Co-authored-by: Ole Wunschmenn <owunschmann4046@cosmoconsult.com> Co-authored-by: Maria Zhelezova <43066499+mazhelez@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 5c35401 commit 23b5dde

File tree

6 files changed

+688
-491
lines changed

6 files changed

+688
-491
lines changed

Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1

Lines changed: 146 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -330,146 +330,180 @@ function ApplyWorkflowDefaultInputs {
330330
return
331331
}
332332

333+
# Get workflow_call inputs
334+
$workflowCallInputs = $yaml.Get('on:/workflow_call:/inputs:/')
335+
333336
# Apply defaults to matching inputs
334-
foreach ($default in $repoSettings.workflowDefaultInputs) {
335-
$inputName = $default.name
336-
$defaultValue = $default.value
337-
338-
# Check if this input exists in the workflow
339-
$inputSection = $inputs.Get("$($inputName):/")
340-
if (-not $inputSection) {
341-
# Input is not present in the workflow
342-
continue
337+
$workflowDispatchInputsModified = $false
338+
$workflowCallInputsModified = $false
339+
340+
foreach ($defaultInput in $repoSettings.workflowDefaultInputs) {
341+
# Apply to workflow_dispatch inputs and only update workflow_call if dispatch was modified
342+
if (ApplyWorkflowDefaultInput -workflowName $workflowName -inputs $inputs -defaultInput $defaultInput) {
343+
$workflowDispatchInputsModified = $true
344+
345+
# Only apply to workflow_call inputs if the workflow_dispatch input was modified
346+
if ($workflowCallInputs) {
347+
if (ApplyWorkflowDefaultInput -workflowName $workflowName -inputs $workflowCallInputs -defaultInput $defaultInput) {
348+
$workflowCallInputsModified = $true
349+
}
350+
}
343351
}
352+
}
344353

345-
# Get the input type from the YAML if specified
346-
$inputType = $null
347-
$typeStart = 0
348-
$typeCount = 0
349-
if ($inputSection.Find('type:', [ref] $typeStart, [ref] $typeCount)) {
350-
$typeLine = $inputSection.content[$typeStart].Trim()
351-
if ($typeLine -match 'type:\s*(.+)') {
352-
$inputType = $matches[1].Trim()
353-
}
354+
# Update the workflow_dispatch section if modified
355+
if ($workflowDispatchInputsModified) {
356+
$workflowDispatch.Replace('inputs:/', $inputs.content)
357+
$yaml.Replace('on:/workflow_dispatch:/', $workflowDispatch.content)
358+
}
359+
360+
# Update workflow_call inputs if modified
361+
if ($workflowCallInputsModified) {
362+
$yaml.Replace('on:/workflow_call:/inputs:/', $workflowCallInputs.content)
363+
}
364+
}
365+
366+
function ApplyWorkflowDefaultInput {
367+
Param(
368+
[string] $workflowName,
369+
[Yaml] $inputs,
370+
[hashtable] $defaultInput
371+
)
372+
373+
$inputName = $defaultInput.name
374+
$defaultValue = $defaultInput.value
375+
376+
# Check if this input exists in the inputs collection
377+
$inputSection = $inputs.Get("$($inputName):/")
378+
if (-not $inputSection) {
379+
# Input is not present in the workflow
380+
return $false
381+
}
382+
383+
# Get the input type from the YAML if specified
384+
$inputType = $null
385+
$typeStart = 0
386+
$typeCount = 0
387+
if ($inputSection.Find('type:', [ref] $typeStart, [ref] $typeCount)) {
388+
$typeLine = $inputSection.content[$typeStart].Trim()
389+
if ($typeLine -match 'type:\s*(.+)') {
390+
$inputType = $matches[1].Trim()
354391
}
392+
}
355393

356-
# Validate that the value type matches the input type
357-
$validationError = $null
358-
if ($inputType) {
359-
switch ($inputType) {
360-
'boolean' {
361-
if ($defaultValue -isnot [bool]) {
362-
$validationError = "Workflow '$workflowName', input '$inputName': Expected boolean value, but got $($defaultValue.GetType().Name). Please use `$true or `$false."
363-
}
394+
# Validate that the value type matches the input type
395+
$validationError = $null
396+
if ($inputType) {
397+
switch ($inputType) {
398+
'boolean' {
399+
if ($defaultValue -isnot [bool]) {
400+
$validationError = "Workflow '$workflowName', input '$inputName': Expected boolean value, but got $($defaultValue.GetType().Name). Please use `$true or `$false."
364401
}
365-
'number' {
366-
if ($defaultValue -isnot [int] -and $defaultValue -isnot [long] -and $defaultValue -isnot [double]) {
367-
$validationError = "Workflow '$workflowName', input '$inputName': Expected number value, but got $($defaultValue.GetType().Name)."
368-
}
402+
}
403+
'number' {
404+
if ($defaultValue -isnot [int] -and $defaultValue -isnot [long] -and $defaultValue -isnot [double]) {
405+
$validationError = "Workflow '$workflowName', input '$inputName': Expected number value, but got $($defaultValue.GetType().Name)."
369406
}
370-
'string' {
371-
if ($defaultValue -isnot [string]) {
372-
$validationError = "Workflow '$workflowName', input '$inputName': Expected string value, but got $($defaultValue.GetType().Name)."
373-
}
407+
}
408+
'string' {
409+
if ($defaultValue -isnot [string]) {
410+
$validationError = "Workflow '$workflowName', input '$inputName': Expected string value, but got $($defaultValue.GetType().Name)."
374411
}
375-
'choice' {
376-
# Choice inputs accept strings and must match one of the available options (case-sensitive)
377-
if ($defaultValue -isnot [string]) {
378-
$validationError = "Workflow '$workflowName', input '$inputName': Expected string value for choice input, but got $($defaultValue.GetType().Name)."
379-
}
380-
else {
381-
# Validate that the value is one of the available options
382-
$optionsStart = 0
383-
$optionsCount = 0
384-
if ($inputSection.Find('options:', [ref] $optionsStart, [ref] $optionsCount)) {
385-
$availableOptions = @()
386-
# Parse the options from the YAML (they are indented list items starting with "- ")
387-
for ($i = $optionsStart + 1; $i -lt ($optionsStart + $optionsCount); $i++) {
388-
$optionLine = $inputSection.content[$i].Trim()
389-
if ($optionLine -match '^-\s*(.+)$') {
390-
$availableOptions += $matches[1].Trim()
391-
}
412+
}
413+
'choice' {
414+
# Choice inputs accept strings and must match one of the available options (case-sensitive)
415+
if ($defaultValue -isnot [string]) {
416+
$validationError = "Workflow '$workflowName', input '$inputName': Expected string value for choice input, but got $($defaultValue.GetType().Name)."
417+
}
418+
else {
419+
# Validate that the value is one of the available options
420+
$optionsStart = 0
421+
$optionsCount = 0
422+
if ($inputSection.Find('options:', [ref] $optionsStart, [ref] $optionsCount)) {
423+
$availableOptions = @()
424+
# Parse the options from the YAML (they are indented list items starting with "- ")
425+
for ($i = $optionsStart + 1; $i -lt ($optionsStart + $optionsCount); $i++) {
426+
$optionLine = $inputSection.content[$i].Trim()
427+
if ($optionLine -match '^-\s*(.+)$') {
428+
$availableOptions += $matches[1].Trim()
392429
}
430+
}
393431

394-
if ($availableOptions.Count -gt 0 -and $availableOptions -cnotcontains $defaultValue) {
395-
$validationError = "Workflow '$workflowName', input '$inputName': Value '$defaultValue' is not a valid choice (case-sensitive match required). Available options: $($availableOptions -join ', ')."
396-
}
432+
if ($availableOptions.Count -gt 0 -and $availableOptions -cnotcontains $defaultValue) {
433+
$validationError = "Workflow '$workflowName', input '$inputName': Value '$defaultValue' is not a valid choice (case-sensitive match required). Available options: $($availableOptions -join ', ')."
397434
}
398435
}
399436
}
400437
}
401438
}
402-
else {
403-
# If no type is specified in the workflow, it defaults to string
404-
if ($defaultValue -isnot [string]) {
405-
OutputWarning "Workflow '$workflowName', input '$inputName': No type specified in workflow (defaults to string), but configured value is $($defaultValue.GetType().Name). This may cause issues."
406-
}
439+
}
440+
else {
441+
# If no type is specified in the workflow, it defaults to string
442+
if ($defaultValue -isnot [string]) {
443+
OutputWarning "Workflow '$workflowName', input '$inputName': No type specified in workflow (defaults to string), but configured value is $($defaultValue.GetType().Name). This may cause issues."
407444
}
445+
}
408446

409-
if ($validationError) {
410-
throw $validationError
411-
}
447+
if ($validationError) {
448+
throw $validationError
449+
}
412450

413-
# Convert the default value to the appropriate YAML format
414-
$yamlValue = $defaultValue
415-
if ($defaultValue -is [bool]) {
416-
$yamlValue = $defaultValue.ToString().ToLower()
417-
}
418-
elseif ($defaultValue -is [string]) {
419-
# Quote strings and escape single quotes per YAML spec
420-
$escapedValue = $defaultValue.Replace("'", "''")
421-
$yamlValue = "'$escapedValue'"
422-
}
451+
# Convert the default value to the appropriate YAML format
452+
$yamlValue = $defaultValue
453+
if ($defaultValue -is [bool]) {
454+
$yamlValue = $defaultValue.ToString().ToLower()
455+
}
456+
elseif ($defaultValue -is [string]) {
457+
# Quote strings and escape single quotes per YAML spec
458+
$escapedValue = $defaultValue.Replace("'", "''")
459+
$yamlValue = "'$escapedValue'"
460+
}
423461

424-
# Find and replace the default: line in the input section
425-
$start = 0
426-
$count = 0
427-
if ($inputSection.Find('default:', [ref] $start, [ref] $count)) {
428-
# Replace existing default value
429-
$inputSection.Replace('default:', "default: $yamlValue")
462+
# Find and replace the default: line in the input section
463+
$start = 0
464+
$count = 0
465+
466+
if ($inputSection.Find('default:', [ref] $start, [ref] $count)) {
467+
# Replace existing default value
468+
$inputSection.Replace('default:', "default: $yamlValue")
469+
}
470+
else {
471+
# Add default value - find the best place to insert it
472+
# Insert after type, required, or description (whichever comes last)
473+
$insertAfter = -1
474+
$typeLine = 0
475+
$typeCount = 0
476+
$requiredLine = 0
477+
$requiredCount = 0
478+
$descLine = 0
479+
$descCount = 0
480+
481+
if ($inputSection.Find('type:', [ref] $typeLine, [ref] $typeCount)) {
482+
$insertAfter = $typeLine + $typeCount
430483
}
431-
else {
432-
# Add default value - find the best place to insert it
433-
# Insert after type, required, or description (whichever comes last)
434-
$insertAfter = -1
435-
$typeLine = 0
436-
$typeCount = 0
437-
$requiredLine = 0
438-
$requiredCount = 0
439-
$descLine = 0
440-
$descCount = 0
441-
442-
if ($inputSection.Find('type:', [ref] $typeLine, [ref] $typeCount)) {
443-
$insertAfter = $typeLine + $typeCount
484+
if ($inputSection.Find('required:', [ref] $requiredLine, [ref] $requiredCount)) {
485+
if (($requiredLine + $requiredCount) -gt $insertAfter) {
486+
$insertAfter = $requiredLine + $requiredCount
444487
}
445-
if ($inputSection.Find('required:', [ref] $requiredLine, [ref] $requiredCount)) {
446-
if (($requiredLine + $requiredCount) -gt $insertAfter) {
447-
$insertAfter = $requiredLine + $requiredCount
448-
}
449-
}
450-
if ($inputSection.Find('description:', [ref] $descLine, [ref] $descCount)) {
451-
if (($descLine + $descCount) -gt $insertAfter) {
452-
$insertAfter = $descLine + $descCount
453-
}
454-
}
455-
456-
if ($insertAfter -eq -1) {
457-
# No other properties, insert at position 1 (after the input name)
458-
$insertAfter = 1
488+
}
489+
if ($inputSection.Find('description:', [ref] $descLine, [ref] $descCount)) {
490+
if (($descLine + $descCount) -gt $insertAfter) {
491+
$insertAfter = $descLine + $descCount
459492
}
493+
}
460494

461-
$inputSection.Insert($insertAfter, "default: $yamlValue")
495+
if ($insertAfter -eq -1) {
496+
# No other properties, insert at position 1 (after the input name)
497+
$insertAfter = 1
462498
}
463499

464-
# Update the inputs section with the modified input
465-
$inputs.Replace("$($inputName):/", $inputSection.content)
500+
$inputSection.Insert($insertAfter, "default: $yamlValue")
466501
}
467502

468-
# Update the workflow_dispatch section with modified inputs
469-
$workflowDispatch.Replace('inputs:/', $inputs.content)
503+
# Update the inputs collection with the modified input section
504+
$inputs.Replace("$($inputName):/", $inputSection.content)
470505

471-
# Update the on: section with modified workflow_dispatch
472-
$yaml.Replace('on:/workflow_dispatch:/', $workflowDispatch.content)
506+
return $true
473507
}
474508

475509
function GetWorkflowContentWithChangesFromSettings {

RELEASENOTES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
- Issue 2055 When using versioningStrategy 3+16, you get an error when building
44
- AL-Go repositories with large amounts of projects may run into issues with too large environment variables
5+
- Discussion 1855 Add trigger 'workflow_call' to workflow 'Update AL-Go System Files' for reusability
6+
7+
### Set default values for workflow inputs
8+
9+
The `workflowDefaultInputs` setting now also applies to `workflow_call` inputs when an input with the same name exists for `workflow_dispatch`.
10+
This ensures consistent default values across both manual workflow runs and reusable workflow calls.
11+
12+
Read more at [workflowDefaultInputs](https://aka.ms/algosettings#workflowDefaultInputs).
513

614
### AL-Go Telemetry updates
715

0 commit comments

Comments
 (0)