Public/Test-GitHubRepositoryMigration.ps1

function Test-GitHubRepositoryMigration {
    <#
    .SYNOPSIS
    Verifies a GitHub repository migration.

    .DESCRIPTION
    Performs read-only verification of a completed repository migration. Snapshot
    verification compares the source and destination default-branch Git trees
    and confirms the destination has the expected single-root-commit history
    shape. FullHistory verification compares ordinary branch and tag targets,
    reachable commit counts, branch-tip trees, the default branch, and reachable
    Git LFS object availability.

    FullHistory verification can additionally compare selected GitHub Releases and
    release assets by using -IncludeReleases. The release-selection parameters use
    the same semantics as Copy-GitHubRepository: stable non-draft releases are the
    default, tag include/exclude wildcard patterns can narrow the set, prereleases
    and drafts require explicit opt-in, and -ReleaseCount limits the newest matches.

    Release verification is read-only and compares the currently selected source
    release state with destination releases. It verifies tag commit identity,
    supported release metadata, asset metadata, and the Latest designation when the
    source Latest release is part of the selected set. Extra destination releases
    outside the selected source set do not cause verification failure.

    The default content mode is Snapshot. Use the same content mode that was used
    for the migration. The command does not repair a failed verification and does
    not mutate either repository. The current release line supports github.com only.

    .PARAMETER SourceRepository
    Specifies the source repository as owner/name. The repository must exist and
    be visible to the authenticated GitHub account.

    .PARAMETER DestinationRepository
    Specifies the destination repository as owner/name. This is the repository
    whose migrated content is verified against the source.

    .PARAMETER ContentMode
    Selects the verification contract. Snapshot is the default and verifies the
    default-branch tree plus the destination's single-root-commit history shape.
    FullHistory verifies branches, tags, reachable commit counts, branch-tip
    trees, the default branch, and reachable Git LFS object availability.

    .PARAMETER IncludeReleases
    Adds read-only GitHub Release verification to FullHistory verification. Stable,
    non-draft source releases are selected by default. Snapshot does not support
    GitHub Release verification.

    .PARAMETER ReleaseTag
    Includes only source GitHub Releases whose tag names match one or more
    PowerShell wildcard patterns. Exact tag names are valid patterns.

    .PARAMETER ReleaseExcludeTag
    Excludes source GitHub Releases whose tag names match one or more PowerShell
    wildcard patterns after include filtering.

    .PARAMETER IncludePrerelease
    Includes GitHub Releases marked as prereleases. Prereleases are excluded by
    default.

    .PARAMETER IncludeDraftReleases
    Includes draft GitHub Releases. Draft releases are excluded by default.

    .PARAMETER ReleaseCount
    Limits release verification to the newest N releases remaining after the other
    release filters are applied. Ordering uses publication time and then creation
    time.

    .PARAMETER HostName
    Specifies the GitHub host used for discovery and authentication. The default
    is github.com. The current release line supports github.com only.

    .EXAMPLE
    Test-GitHubRepositoryMigration `
        -SourceRepository infoconex/source `
        -DestinationRepository infoconex/destination

    Verifies a Snapshot migration, which is the default content mode.

    .EXAMPLE
    Test-GitHubRepositoryMigration `
        -SourceRepository infoconex/source `
        -DestinationRepository infoconex/destination `
        -ContentMode FullHistory

    Verifies a FullHistory migration including ordinary branches, tags, reachable
    history, branch-tip trees, default branch, and reachable Git LFS objects.

    .EXAMPLE
    Test-GitHubRepositoryMigration `
        -SourceRepository infoconex/source `
        -DestinationRepository infoconex/destination `
        -ContentMode FullHistory `
        -IncludeReleases `
        -ReleaseTag 'v2.*' `
        -ReleaseCount 3

    Verifies FullHistory plus the three newest stable, non-draft source GitHub
    Releases whose tags match v2.*.

    .INPUTS
    None. This command does not accept pipeline input.

    .OUTPUTS
    CopyGitHubRepo.SnapshotVerificationResult or
    CopyGitHubRepo.FullHistoryVerificationResult. FullHistory results include a
    ReleaseVerification property when -IncludeReleases is requested. The returned
    IsSuccessful value reflects both Git/LFS and requested release verification.

    .LINK
    https://github.com/infoconex/copy-github-repo

    .LINK
    https://github.com/infoconex/copy-github-repo/blob/main/docs/product/product-contract.md
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidatePattern('^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$')]
        [string] $SourceRepository,

        [Parameter(Mandatory)]
        [ValidatePattern('^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$')]
        [string] $DestinationRepository,

        [ValidateSet('Snapshot', 'FullHistory')]
        [string] $ContentMode = 'Snapshot',

        [switch] $IncludeReleases,

        [string[]] $ReleaseTag,

        [string[]] $ReleaseExcludeTag,

        [switch] $IncludePrerelease,

        [switch] $IncludeDraftReleases,

        [ValidateRange(1, [int]::MaxValue)]
        [int] $ReleaseCount,

        [ValidateNotNullOrEmpty()]
        [string] $HostName = 'github.com'
    )

    Assert-CgrSupportedHostName -HostName $HostName

    $releaseFilterWasSpecified = $PSBoundParameters.ContainsKey('ReleaseTag') -or
    $PSBoundParameters.ContainsKey('ReleaseExcludeTag') -or
    $IncludePrerelease -or
    $IncludeDraftReleases -or
    $PSBoundParameters.ContainsKey('ReleaseCount')

    if ($releaseFilterWasSpecified -and -not $IncludeReleases) {
        $message = 'Release verification filter parameters require -IncludeReleases.'
        $exception = [System.InvalidOperationException]::new($message)
        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            $exception,
            'ReleaseFilterRequiresIncludeReleases',
            [System.Management.Automation.ErrorCategory]::InvalidArgument,
            'IncludeReleases'
        )
        $PSCmdlet.ThrowTerminatingError($errorRecord)
    }

    if ($IncludeReleases -and $ContentMode -ne 'FullHistory') {
        $message = 'GitHub Release verification is currently supported only with -ContentMode FullHistory.'
        $exception = [System.NotSupportedException]::new($message)
        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            $exception,
            'SnapshotReleaseVerificationNotImplemented',
            [System.Management.Automation.ErrorCategory]::NotImplemented,
            'IncludeReleases'
        )
        $PSCmdlet.ThrowTerminatingError($errorRecord)
    }

    $prerequisites = Get-CgrPrerequisiteStatus -HostName $HostName
    if (-not $prerequisites.Git.Found) {
        $exception = [System.InvalidOperationException]::new(
            "Git is required for repository migration verification. Install 'git' from https://git-scm.com/."
        )
        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            $exception,
            'GitNotFound',
            [System.Management.Automation.ErrorCategory]::ResourceUnavailable,
            'git'
        )
        $PSCmdlet.ThrowTerminatingError($errorRecord)
    }

    if (-not $prerequisites.GitHubCli.Found) {
        $exception = [System.InvalidOperationException]::new(
            "GitHub CLI is required for repository migration verification. Install 'gh' from https://cli.github.com/."
        )
        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            $exception,
            'GitHubCliNotFound',
            [System.Management.Automation.ErrorCategory]::ResourceUnavailable,
            'gh'
        )
        $PSCmdlet.ThrowTerminatingError($errorRecord)
    }

    if (-not $prerequisites.Authentication.Authenticated) {
        $exception = [System.InvalidOperationException]::new($prerequisites.Authentication.Message)
        $errorRecord = [System.Management.Automation.ErrorRecord]::new(
            $exception,
            'GitHubCliNotAuthenticated',
            [System.Management.Automation.ErrorCategory]::SecurityError,
            $HostName
        )
        $PSCmdlet.ThrowTerminatingError($errorRecord)
    }

    $source = Get-CgrRepository -Repository $SourceRepository -HostName $HostName
    $destination = Get-CgrRepository -Repository $DestinationRepository -HostName $HostName

    if ($ContentMode -eq 'FullHistory') {
        $result = Invoke-CgrRepositoryFullHistoryVerification `
            -SourceRepository $source `
            -DestinationRepository $destination

        if ($IncludeReleases) {
            $releaseVerificationParameters = @{
                SourceRepository = $source
                DestinationRepository = $destination
                ReleaseTag = $ReleaseTag
                ReleaseExcludeTag = $ReleaseExcludeTag
                IncludePrerelease = $IncludePrerelease
                IncludeDraftReleases = $IncludeDraftReleases
                HostName = $HostName
            }
            if ($PSBoundParameters.ContainsKey('ReleaseCount')) {
                $releaseVerificationParameters.ReleaseCount = $ReleaseCount
            }

            $gitContentSuccessful = [bool] $result.IsSuccessful
            $releaseVerification = Test-CgrGitHubReleaseMigration @releaseVerificationParameters
            $result | Add-Member -NotePropertyName GitContentSuccessful -NotePropertyValue $gitContentSuccessful -Force
            $result | Add-Member -NotePropertyName ReleaseVerification -NotePropertyValue $releaseVerification -Force
            $result | Add-Member -NotePropertyName ReleasesVerified -NotePropertyValue ([bool] $releaseVerification.IsSuccessful) -Force
            $result | Add-Member -NotePropertyName IsSuccessful -NotePropertyValue ([bool] ($gitContentSuccessful -and $releaseVerification.IsSuccessful)) -Force
        }

        return $result
    }

    Invoke-CgrRepositorySnapshotVerification `
        -SourceRepository $source `
        -DestinationRepository $destination
}