AzsBuildUtilities.psm1

function WriteMessage(
        [Parameter(Mandatory=$true)] [string] $Message
    )
{
    Write-Host ("[{0}]: {1}" -f (Get-Date), $Message)
}

function GetBuildTrackerPageWithRetry(
        [Parameter(Mandatory=$true)] [string] $BuildTrackerUrl
    )
{
    $attempt = 1
    do {        
        $response = Invoke-WebRequest -Uri $BuildTrackerUrl -UseDefaultCredential
        $responseCode = $response.StatusCode
        $attempt += 1
        if ($responseCode -ne 200)
        {
            WriteMessage ("[WARNING] Unexpected response code from BuildTracker: {0}. Retrying..." -f $responseCode)
        }
    } while ($responseCode -ne 200 -and $attempt -lt 3)

    if ($responseCode -ne 200 -and $attempt -ge 3) {
        throw "Error getting build tracker page with 3 retries."
    }

    return $response.Content
}

function GetJobStatus(
        [Parameter(Mandatory=$true)] [string] $BuildTrackerPageContent
    )
{
    $statusRegex = [regex]"<B>Status: </B><font color='\w+'><b>(\w+)</b></font>"
    $match = [regex]::Match($BuildTrackerPageContent, $statusRegex).captures.groups[1].value
    if ($match -eq $null)
    {
        throw "Could not find job status on BuildTracker page"
    }
    return $match
}

function GetReddogPath(
        [Parameter(Mandatory=$true)] [string] $BuildTrackerPageContent
    )
{
    $reddogRegex = [regex]"<BR><B>Drop: </B><a href='(.+)'>"
    $reddogPath = [regex]::Match($BuildTrackerPageContent, $reddogRegex).captures.groups[1].value
    if ($reddogPath -eq $null)
    {
        throw "Could not find reddog location on BuildTracker page"
    }
    return $reddogPath
}

function CopyBuildToShare(
        [Parameter(Mandatory=$true)] [string] $ShareRootPath,
        [Parameter(Mandatory=$true)] [string] $BuildTrackerPageContent
    )
{
    WriteMessage "Finding build packages..."
    $reddogPath = GetReddogPath($BuildTrackerPageContent)
    $packagesPath = $reddogPath + "\packages"
    WriteMessage ("reddog packages path: {0}" -f $packagesPath)
    $shareOutputPath = $packagesPath -replace "\\\\reddog",$ShareRootPath
    WriteMessage "Generating share output path..."
    WriteMessage ("Share output path: {0}" -f $shareOutputPath)
    if (Test-Path $shareOutputPath)
    {
        WriteMessage "Share output path already exists! Removing..."
        Remove-Item -Recurse -Path $shareOutputPath
    }
    WriteMessage "Copying to share..."
    Copy-Item -Recurse -Force -Path $packagesPath -Destination $shareOutputPath
    return $shareOutputPath
}

function GetShareLatestPath(
        [Parameter(Mandatory=$true)] [string] $ShareOutputPath
    )
{
    $latestPath = $ShareOutputPath -replace "\\[^\\]+\\packages", "_latest"
    if ($latestPath -eq $null)
    {
        throw "Could not find generate latest path"
    }
    return $latestPath
}

function UpdateLatestPath(
        [Parameter(Mandatory=$true)] [string] $ShareOutputPath
    )
{
    $latestPath = GetShareLatestPath($ShareOutputPath)
    $latestPackagesPath = $latestPath + "\packages"
    WriteMessage ("Latest packages path: {0}" -f $latestPackagesPath)
    if (Test-Path $latestPath)
    {
        WriteMessage "Latest path already exists! Removing..."
        Remove-Item -Recurse -Path $latestPath
    }

    WriteMessage "Copying to latest packages path..."
    Copy-Item -Recurse -Force -Path $ShareOutputPath -Destination $latestPackagesPath
    return $latestPackagesPath
}

function IsJobInProgress(
        [Parameter(Mandatory=$true)] [string] $JobStatus
    )
{
    return $JobStatus -eq "Queued" -or $JobStatus -eq "Submitted" -or $JobStatus -eq "Sequenced" -or $JobStatus -eq "Pending" -or $JobStatus -eq "Running"
}

function IsJobCompleted(
        [Parameter(Mandatory=$true)] [string] $JobStatus
    )
{
    return $JobStatus -eq "Completed"
}

function IsJobFailed(
        [Parameter(Mandatory=$true)] [string] $JobStatus
    )
{
    return $JobStatus -eq "Failed" -or $JobStatus -eq "Aborted"
}

function GetBuildTrackerUrl(
        [Parameter(Mandatory=$true)] [Object[]] $BuildreqOutput
    )
{
    $lines = $BuildreqOutput.Split([Environment]::NewLine)    
    $buildUrlRegex = [regex]"Build (http://wabt/BuildTracker/Jobs/LegSummary.aspx\?Id=\d+) started$"

    # Find the build URL, checking each line of output in reverse
    for ($i = $lines.Count - 1; $i -ge 0; $i--)
    {
        $captures = [regex]::Match($lines[$i], $buildUrlRegex).captures
        if ($captures -ne $null -and $captures.Groups.Count -ge 2)
        {
            $buildUrl = $captures.Groups[1].Value
            # Greedily take the first match
            if ($buildUrl -ne $null)
            {
                return $buildUrl
            }
        }        
    }

    # If we reach here, we never found a build URL
    throw "Could not find BuildTracker URL in buildreq output"    
}

function Watch-Build(
        [Parameter(Mandatory=$true)] [string] $BuildTrackerUrl
    )
{
    WriteMessage ("Watching build tracker job @ `"{0}`"..." -f $BuildTrackerUrl)
    do
    {
        $pageContent = GetBuildTrackerPageWithRetry($BuildTrackerUrl)
        $status = GetJobStatus($pageContent)
        WriteMessage ("Current job status: {0}" -f $status)
        $continue = IsJobInProgress($status)
        if ($continue)
        {
            Start-Sleep -Seconds 60
        }
    } while ($continue)

    if ($(IsJobFailed($status)) -eq $true) 
    {
        Write-Error "Job failed!"
        return $false
    }
    if ($(IsJobCompleted($status)) -eq $false)
    {
        Write-Error "Job did not complete."
        return $false
    }
    return $true
}

function Watch-BuildAndCopyToShare(
        [Parameter(Mandatory=$true)] [string] $BuildTrackerUrl,
        [string] $ShareRootPath  = ("\\ecg\teams\Solutions\AzureStack\Data\{0}" -f $env:UserName),
        [Switch] $ShutdownOnCompletion
    )
{    
    WriteMessage ("Output share root path: `"{0}`"..." -f $ShareRootPath)
    if ($(Watch-Build($BuildTrackerUrl)) -eq $false)
    {
        return $false
    }
    WriteMessage "Job completed!"

    $pageContent = GetBuildTrackerPageWithRetry($BuildTrackerUrl)
    $outputPath = CopyBuildToShare -ShareRootPath $ShareRootPath -BuildTrackerPageContent $pageContent
    if ($outputPath -eq $null)
    {
        Write-Error ("Error copying to share: {0}!" -f $ShareRootPath)
        return $false
    }

    WriteMessage "Updating latest path..."
    UpdateLatestPath($outputPath)

    WriteMessage "Openning share output path..."
    Start $outputPath

    if ($ShutdownOnCompletion -eq $true)
    {
        WriteMessage "Shutdown on completion is enabled. Shutting down in 60 seconds..."
        Shutdown /f /s /t 60
    }
    return $true
}

function Start-BuildAndCopyToShare(
        [string] $ShareRootPath = ("\\ecg\teams\Solutions\AzureStack\Data\{0}" -f $env:UserName),
        [Switch] $Publish,
        [Switch] $Sign,
        [Switch] $ShutdownOnCompletion
    )
{
    WriteMessage ("Building repo...")
    $buildReqCommand = "buildreq"
    if ($Sign -eq $true)
    {
        WriteMessage "Code signing enabled."
        $buildReqCommand += " --signing"
    }
    else 
    {
        WriteMessage "Code signing disabled."
    }

    if ($Publish -eq $true)
    {
        WriteMessage "Code publishing enabled."
        $buildReqCommand += " --publish"
        if ($Sign -eq $false) 
        {
            WriteMessage "[WARNING] Are you sure you want to publish unsigned code?"    
        }        
    }    
    else
    {
        WriteMessage "Code publishing disabled."
    }

    WriteMessage ("Calling buildreq with command `"{0}`"..." -f $buildReqCommand)
    $buildReqOutput = Invoke-Expression $buildReqCommand
    Write-Host $buildReqOutput
    $buildTrackerUrl = GetBuildTrackerUrl($buildReqOutput)

    return $(Watch-BuildAndCopyToShare -ShutdownOnCompletion:$ShutdownOnCompletion -BuildTrackerUrl $buildTrackerUrl -ShareRootPath $ShareRootPath)
}

Export-ModuleMember Watch-Build, Watch-BuildAndCopyToShare, Start-BuildAndCopyToShare