Private/Invoke-WorkflowCompilerPack.ps1

function Invoke-WorkflowCompilerPack {
<#
.SYNOPSIS
    Packs a UiPath project by invoking UiPath.WorkflowCompiler `build` directly.
 
.DESCRIPTION
    This is the same engine uipcli and `uip rpa pack` both drive internally,
    invoked without either wrapper in between. Driving it directly is what makes
    --include-sources controllable: the compiler's own default is true, uipcli
    hardcodes true and exposes no flag for it, so any package packed through
    uipcli ships every .cs file in plaintext under content/.
 
    Package identity travels in a pack-options JSON file rather than on the
    command line, so values containing spaces or quotes survive the process
    boundary intact.
 
    Boolean flags take a separate 'true'/'false' argument — '--flag=true' is a
    parse error for this tool.
 
    Returns the process exit code. Stream discipline matches Invoke-UipcliPack:
    output goes to Verbose on success and to Warning on failure.
 
.PARAMETER WorkflowCompiler
    Hashtable from Resolve-CpmfUipsWorkflowCompiler (FilePath, ArgumentPrefix).
 
.PARAMETER ProjectDir
    Project directory containing project.json.
 
.PARAMETER OutputDir
    Directory the compiler writes the package into.
 
.PARAMETER PackOptions
    Package identity fields written to the pack-options JSON file.
 
.PARAMETER PackSettings
    Pack-time policy: IncludeSources, SkipAnalyze, SkipValidate, NoOptimizeDeps,
    SplitPackages, ExcludeConfiguredSources, LogLevel, PolicyFileType, PolicyFilePath.
 
.PARAMETER ExtraArgs
    Additional arguments appended verbatim.
#>

    [CmdletBinding()]
    [OutputType([int])]
    param(
        [Parameter(Mandatory)]
        [hashtable]$WorkflowCompiler,

        [Parameter(Mandatory)]
        [string]   $ProjectDir,

        [Parameter(Mandatory)]
        [string]   $OutputDir,

        [hashtable]$PackOptions  = @{},
        [hashtable]$PackSettings = @{},
        [string[]] $ExtraArgs    = @()
    )

    function ConvertTo-CompilerBool {
        param([object]$Value)
        if ($null -eq $Value) { return 'false' }
        if ([bool]$Value) { return 'true' } else { return 'false' }
    }

    $packOptionsFile = Join-Path ([System.IO.Path]::GetTempPath()) "cpmf-packoptions-$([System.Guid]::NewGuid()).json"

    try {
        # Only non-empty fields are written; the compiler treats an empty string
        # as a real value and would stamp it into the nuspec.
        $payload = [ordered]@{}
        foreach ($key in @('id', 'version', 'author', 'description', 'releaseNotes', 'tags',
                           'iconUrl', 'projectUrl', 'repositoryType', 'repositoryUrl',
                           'repositoryBranch', 'repositoryCommit')) {
            if ($PackOptions.ContainsKey($key) -and -not [string]::IsNullOrWhiteSpace([string]$PackOptions[$key])) {
                $payload[$key] = [string]$PackOptions[$key]
            }
        }

        $json = $payload | ConvertTo-Json -Compress -Depth 4
        [System.IO.File]::WriteAllText($packOptionsFile, $json, (New-Object System.Text.UTF8Encoding $false))
        Write-Verbose "[workflowcompiler] pack-options: $json"

        $logLevel = if ($PackSettings.ContainsKey('LogLevel') -and -not [string]::IsNullOrWhiteSpace([string]$PackSettings['LogLevel'])) {
            [string]$PackSettings['LogLevel']
        } else {
            'Warning'
        }

        $buildArgs = @($WorkflowCompiler.ArgumentPrefix) + @(
            'build'
            '-p', $ProjectDir
            '-o', $OutputDir
            '--log-level', $logLevel
            '--format-logs', 'true'
            '--skip-analyze', (ConvertTo-CompilerBool $PackSettings['SkipAnalyze'])
            '--skip-validate', (ConvertTo-CompilerBool $PackSettings['SkipValidate'])
            '--no-optimize-deps', (ConvertTo-CompilerBool $PackSettings['NoOptimizeDeps'])
            '--include-sources', (ConvertTo-CompilerBool $PackSettings['IncludeSources'])
            '--split-packages', (ConvertTo-CompilerBool $PackSettings['SplitPackages'])
            '--exclude-configured-sources', (ConvertTo-CompilerBool $PackSettings['ExcludeConfiguredSources'])
            '--pack-options-file', $packOptionsFile
        )

        if ($PackOptions.ContainsKey('version') -and -not [string]::IsNullOrWhiteSpace([string]$PackOptions['version'])) {
            $buildArgs += @('--build-version', [string]$PackOptions['version'])
        }
        if ($PackOptions.ContainsKey('id') -and -not [string]::IsNullOrWhiteSpace([string]$PackOptions['id'])) {
            $buildArgs += @('--package-name', [string]$PackOptions['id'])
        }
        if ($PackSettings.ContainsKey('PolicyFileType') -and -not [string]::IsNullOrWhiteSpace([string]$PackSettings['PolicyFileType'])) {
            $buildArgs += @('--policy-file-type', [string]$PackSettings['PolicyFileType'])
        }
        if ($PackSettings.ContainsKey('PolicyFilePath') -and -not [string]::IsNullOrWhiteSpace([string]$PackSettings['PolicyFilePath'])) {
            $buildArgs += @('--policy-file', [string]$PackSettings['PolicyFilePath'])
        }
        if ($ExtraArgs.Count -gt 0) { $buildArgs += $ExtraArgs }

        Write-Verbose "[workflowcompiler] $($WorkflowCompiler.FilePath) $($buildArgs -join ' ')"

        $capture  = Invoke-NativeCommandCapture -FilePath $WorkflowCompiler.FilePath -ArgumentList $buildArgs
        $output   = @($capture.StdOutLines) + @($capture.StdErrLines)
        $exitCode = $capture.ExitCode

        # The compiler emits a JSON result envelope as its last stdout line.
        $summary = $null
        foreach ($line in @($capture.StdOutLines)) {
            $str = "$line".Trim()
            if ($str.StartsWith('{') -and $str.EndsWith('}')) {
                try {
                    $parsed = $str | ConvertFrom-Json
                    if ($parsed.PSObject.Properties.Name -contains 'Type' -or
                        $parsed.PSObject.Properties.Name -contains 'Success') {
                        $summary = $parsed
                    }
                } catch {
                    Write-Verbose "[workflowcompiler] Unparsed JSON-looking line: $str"
                }
            }
        }

        if ($exitCode -ne 0) {
            foreach ($line in $output) { Write-Warning "[workflowcompiler] $line" }
            if ($summary -and $summary.PSObject.Properties.Name -contains 'Summary') {
                Write-Warning "[workflowcompiler] $($summary.Summary)"
            }
        } else {
            foreach ($line in $output) { Write-Verbose "[workflowcompiler] $line" }
        }

        return $exitCode
    } finally {
        Remove-Item -LiteralPath $packOptionsFile -Force -ErrorAction SilentlyContinue
    }
}