From 2d22fdebc051e870cfb854617903ba79c1f49b21 Mon Sep 17 00:00:00 2001 From: JosephPilov-MSFT <23519517+PiJoCoder@users.noreply.github.com> Date: Sat, 19 Apr 2025 16:06:35 -0500 Subject: [PATCH] #397 create release scripts --- ReleaseProcess/FinalRelease.ps1 | 189 ++++++++++++++++ ReleaseProcess/PrepareFilesForSigning.ps1 | 225 +++++++++++++++++++ RowsetImportEngine/RowsetImportEngine.csproj | 4 +- 3 files changed, 416 insertions(+), 2 deletions(-) create mode 100644 ReleaseProcess/FinalRelease.ps1 create mode 100644 ReleaseProcess/PrepareFilesForSigning.ps1 diff --git a/ReleaseProcess/FinalRelease.ps1 b/ReleaseProcess/FinalRelease.ps1 new file mode 100644 index 0000000..0ab764a --- /dev/null +++ b/ReleaseProcess/FinalRelease.ps1 @@ -0,0 +1,189 @@ +param ( + [string]$ReleasePath +) + + + +# Check if the script is being run with the correct number of parameters +if ($PSBoundParameters.Count -ne 1) { + Write-Host "Usage: .\FinalRelease.ps1 -InitialPath " -ForegroundColor Red + exit 1 +} + +# Check if the current user is an administrator +$principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) +if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + Write-Host "The script must be run as an administrator. Please restart PowerShell with elevated permissions." + exit 1 +} + +# Validate the destination root folder +if (-not (Test-Path -Path $ReleasePath)) { + Write-Host "The provided initial path does not exist: '$ReleasePath'" -ForegroundColor Red + exit 1 +} + +$SignedFilesToCopy = @( + "\SQLNexus\Bin\Release\BulkLoadEx.dll", + "\SQLNexus\Bin\Release\LinuxPerfImporter.dll", + "\SQLNexus\Bin\Release\NexusInterfaces.dll", + "\SQLNexus\Bin\Release\PerfmonImporter.dll", + "\SQLNexus\Bin\Release\ReadTraceNexusImporter.dll", + "\SQLNexus\Bin\Release\RowsetImportEngine.dll", + "\SQLNexus\Bin\Release\sqlnexus.exe" +) + +# Define the paths to the folders +$filesToSignFolder = Join-Path -Path $ReleasePath -ChildPath "FilesToSign" +$signedFilesFolder = Join-Path -Path $ReleasePath -ChildPath "SignedFiles" +$releaseSignedPrepFolder = Join-Path -Path $ReleasePath -ChildPath "Release_Signed_Prep" +$releaseSignedFolder = Join-Path -Path $ReleasePath -ChildPath "Release_Signed" +$releaseUnsignedFolder = Join-Path -Path $ReleasePath -ChildPath "Release_Unsigned" + +Write-Host "Release Signed Prep Folder: $releaseSignedPrepFolder" + +# Validate the existence of all three folders in a single check +if (-not (Test-Path -Path $filesToSignFolder) -or + -not (Test-Path -Path $signedFilesFolder) -or + -not (Test-Path -Path $releaseSignedPrepFolder)) { + Write-Host "One or more required folders do not exist. Please ensure that '\FilesToSign', '\SignedFiles', and '\Release_Signed_Prep' folders are present." -ForegroundColor Red + exit 1 +} + +# Get the list of files in each folder +$filesToSign = Get-ChildItem -Path $filesToSignFolder -File +$signedFiles = Get-ChildItem -Path $signedFilesFolder -File + + +# Compare the number of files +if (($filesToSign.Count) -ne ($signedFiles.Count)) { + Write-Host "The number of files in '\FilesToSign' ($($filesToSign.Count)) and '\SignedFiles' ($($signedFiles.Count))) folders do not match." -ForegroundColor Red +} +else { + Write-Host "The number of files in '\FilesToSign' ($($filesToSign.Count)) and '\SignedFiles' ($($signedFiles.Count)) folders match." +} + +# Compare the signed files and filestosign filenames +$filesToSignNames = $filesToSign.Name +$signedFilesNames = $signedFiles.Name +$missingFiles = $filesToSignNames | Where-Object { $_ -notin $signedFilesNames } +if ($missingFiles.Count -gt 0) { + Write-Host "The following file(s) are missing in the '\SignedFiles' folder: `r`n $($missingFiles -join "`r`n ")" -ForegroundColor Red + exit 1 +} + +# Validate that the files in SignedFiles are digitally signed +$invalidCount = 0 +foreach ($file in $signedFiles) { + $signature = Get-AuthenticodeSignature $file.FullName + if ($signature.Status -ne 'Valid') { + Write-Host "The file '$($file.FullName)' is not properly signed. Status: $($signature.StatusMessage)" -ForegroundColor Red + $invalidCount++ + } +} + +# Check if any files were found to be invalidly signed and exit with an error if so +if ($invalidCount -gt 0) { + Write-Host "$invalidCount file(s) in the '\SignedFiles' folder are not properly signed. Exiting." + exit 1 +} +else { + Write-Host "All files in the '\SignedFiles' folder are properly signed." -ForegroundColor Green +} + +# Validate that there are no extra files in Release_Signed_Prep that are not in SignedFilesToCopy and vice versa + +# Get files in Release_Signed_Prep folder and filter to files that are to be overwritten with signed counterparts +# Extract just the filenames from SignedFilesToCopy +$SignedFileNamesToCopy = $SignedFilesToCopy | ForEach-Object { Split-Path -Path $_ -Leaf } + +# Use the extracted filenames in the Where-Object filter +$releaseSignedPrepFiles = Get-ChildItem -Path $releaseSignedPrepFolder -Recurse -File | Where-Object { $_.Name -in $SignedFileNamesToCopy } + + +# Check if there are any files in Release_Signed_Prep folder +if ($releaseSignedPrepFiles.Count -eq 0) { + Write-Host "No files found in the '\Release_Signed_Prep' folder. Please ensure that the folder contains the expected signed files." -ForegroundColor Red + exit 1 +} + +# Get the full file names from Release_Signed_Prep folder and remove the folder path to get just the relative file names for comparison +$releaseSignedPrepFileNames = $releaseSignedPrepFiles.FullName | ForEach-Object { $_.Substring($releaseSignedPrepFolder.Length) } + +$releaseSignedPrepFileNames + +# Check for files in Release_Signed_Prep that are not in SignedFilesToCopy +$missingSignedFilesToCopy = $releaseSignedPrepFileNames | Where-Object { $_ -notin $SignedFilesToCopy } + +# Check for files in SignedFilesToCopy array that are not in Release_Signed_Prep +$missingReleaseSignedPrepFiles = $SignedFilesToCopy | Where-Object {$_ -notin $releaseSignedPrepFileNames} + +# If there are any missing files in either direction, report them and exit with an error +# This ensures that the files in SignedFilesToCopy are present in Release_Signed_Prep and vice versa + +if ($missingSignedFilesToCopy.Count -gt 0 -or $missingReleaseSignedPrepFiles.Count -gt 0) { + if ($missingSignedFilesToCopy.Count -gt 0) { + Write-Host "Files missing in SignedFilesToCopy array:`r`n $($missingSignedFilesToCopy -join "`r`n ")" -ForegroundColor Red + } + if ($missingReleaseSignedPrepFiles.Count -gt 0) { + Write-Host "Files missing in Release_Signed_Prep directory:`r`n $($missingReleaseSignedPrepFiles -join "`r`n ")" -ForegroundColor Red + } + exit 1 +} + +# Validate that each file in SignedFilesToCopy exists in Release_Signed_Prep with matching subdirectory structure +$copiedFilesCount = 0 + +foreach ($file in $SignedFilesToCopy) { + # Construct the full path to the source file in the SignedFiles folder. + # They are expected to be in the one flat folder structure, so we just need to join the signedFilesFolder with the file name only. + $sourceFile = Join-Path -Path $signedFilesFolder -ChildPath $file.Split("\")[-1] + + # Check if it exists in the Release_Signed_Prep folder with the subdirectory structure coming from $SignedFilesToCopy + # Use TrimStart to remove the leading backslash from the file path to avoid issues with Join-Path + + if (-not (Test-Path -Path (Join-Path -Path $releaseSignedPrepFolder -ChildPath $file.TrimStart("\")))) + { + # If the file does not exist in the Release_Signed_Prep folder, report an error and exit + Write-Host "The file '$sourceFile' does not exist in the '\Release_Signed_Prep' folder with matching subdirectory structure." -ForegroundColor Red + exit 1 + } + else + { + # Copy the file to the Release_Signed_Prep folder, preserving the subdirectory structure + $destinationFile = Join-Path -Path $releaseSignedPrepFolder -ChildPath $file.TrimStart("\") + Copy-Item -Path $sourceFile -Destination $destinationFile -Force -ErrorAction Stop + + $copiedFilesCount++ + # Output the copied file information + Write-Host "Copied '$sourceFile' to '$destinationFile'" + } +} + +# Output the total number of copied files and expected to copy files +Write-Host "Total of $($SignedFilesToCopy.Count) file(s) expected to be copied from SignedFilesToCopy array" +Write-Host "Total of $copiedFilesCount file(s) actually copied to '$releaseSignedPrepFolder' " + + + +# Final step: Rename the Release_Signed_Prep folder to Release_Signed +if (Test-Path -Path $releaseSignedFolder) { + Write-Host "The destination folder '$releaseSignedFolder' already exists. Please remove or rename it before running the script." -ForegroundColor Red + exit 1 +} +try { + $seconds_to_wait = 15 + Write-Host "Waiting $seconds_to_wait seconds for files to be processed by Anti-Virus and OneDrive sync..." + Start-Sleep -Seconds $seconds_to_wait + Rename-Item -Path $releaseSignedPrepFolder -NewName $releaseSignedFolder -ErrorAction Stop +} catch { + Write-Host "Failed to rename '$releaseSignedPrepFolder' to '$releaseSignedFolder'. Error: $_" -ForegroundColor Red +} + +#check if the folder rename was successful +if (Test-Path -Path $releaseSignedFolder) { + Write-Host "The folder '$releaseSignedFolder' was successfully renamed from '$releaseSignedPrepFolder' and is ready for release." +} else { + Write-Host "The folder '$releaseSignedFolder' was not found after renaming. Please check the rename operation." -ForegroundColor Red +} +# End of script \ No newline at end of file diff --git a/ReleaseProcess/PrepareFilesForSigning.ps1 b/ReleaseProcess/PrepareFilesForSigning.ps1 new file mode 100644 index 0000000..d3a904b --- /dev/null +++ b/ReleaseProcess/PrepareFilesForSigning.ps1 @@ -0,0 +1,225 @@ +param ( + [string]$DestinationRootFolder +) + + +try { + + # Define the source binary files to be signed + $SourceFiles = @( + "BulkLoadEx.dll", + "LinuxPerfImporter.dll", + "NexusInterfaces.dll", + "PerfmonImporter.dll", + "ReadTraceNexusImporter.dll", + "RowsetImportEngine.dll", + "sqlnexus.exe" + ) + + # Define an exclusion list for files and folders that should not be included in the project directory + $ExcludeFilesUnsignedArray = @( + "PrepareFilesForSigning.ps1", + "FinalRelease.ps1", + "azuredevops-pipelines.yml", + ".gitignore" + + ) + + # Define an exclusion list for the Signed_Prep folder, which includes all files in $ExcludeFilesUnsigned and additional files +$ExcludeFilesSignedArray = $ExcludeFilesUnsignedArray + @( + "BulkLoadEx.pdb", + "LinuxPerfImporter.pdb", + "Microsoft.Data.SqlClient.SNI.arm64.pdb", + "Microsoft.Data.SqlClient.SNI.x64.pdb", + "Microsoft.Data.SqlClient.SNI.x86.pdb", + "NexusInterfaces.pdb", + "PerfmonImporter.pdb", + "ReadTraceNexusImporter.pdb", + "RowsetImportEngine.pdb", + "sqlnexus.pdb" + ) + + # exclude these folders from the project directory + $ExcludeFoldersUnsignedArr = @( + ".vscode" + ) + + $ExcludeFoldersSignedArr = $ExcludeFoldersUnsignedArr + @( + "app.publish" + ) + # Go to the root solution directory + $Nexus_RootDir = (Get-Item (Get-Location)).Parent.FullName + + # Validate the destination root folder + if (-not (Test-Path -Path $DestinationRootFolder)) { + Write-Error "The provided destination root folder path does not exist: '$DestinationRootFolder'" + exit 1 + } + + Write-Host "Before folder creation: $(Get-Date)" + + # Create required folders + $ReleaseUnsignedFolder = Join-Path -Path $DestinationRootFolder -ChildPath "Release_Unsigned" + $FilesToSignFolder = Join-Path -Path $DestinationRootFolder -ChildPath "FilesToSign" + $SignedFilesFolder = Join-Path -Path $DestinationRootFolder -ChildPath "SignedFiles" + $ReleaseSignedFolder = Join-Path -Path $DestinationRootFolder -ChildPath "Release_Signed_Prep" + + New-Item -ItemType Directory -Path $ReleaseUnsignedFolder -Force + New-Item -ItemType Directory -Path $FilesToSignFolder -Force + New-Item -ItemType Directory -Path $SignedFilesFolder -Force + New-Item -ItemType Directory -Path $ReleaseSignedFolder -Force + + + Write-Host "Before Get-ChildItem: $(Get-Date)" + + # Get all project folders excluding the specified folders + $projectFilesUnsigned = @() + $projectFilesSigned = @() + + + # Get all items in the SQLNexus \Bin\Release directory + $releaseBinaries = $Nexus_RootDir + "\SQLNexus\Bin\Release" + $allItems = Get-ChildItem -Path $releaseBinaries -Recurse + + # Loop through each item and exclude folders and their contents, as well as specific files + foreach ($item in $allItems) { + + # filter out folders and files that are in the unsigned ExcludeFolders and ExcludeFiles arrays + # and add them to the projectFilesUnsigned array + $excludeUnSig = $false + foreach ($excludeFolder in $ExcludeFoldersUnsignedArr) { + if ($item.FullName -like "*$excludeFolder*") { + $excludeUnSig = $true + break + } + } + foreach ($excludeFile in $ExcludeFilesUnsignedArray) { + if ($item.Name -eq $excludeFile) { + $excludeUnSig = $true + break + } + } + if (-not $excludeUnSig) { + $projectFilesUnsigned += $item + } + + + # filter out folders and files that are in the signed ExcludeFolders and ExcludeFiles arrays + # and add them to the projectFilesSigned array + $excludeSig = $false + foreach ($excludeFolder in $ExcludeFoldersSignedArr) { + if ($item.FullName -like "*$excludeFolder*") { + $excludeSig = $true + break + } + } + foreach ($excludeFile in $ExcludeFilesSignedArray) { + if ($item.Name -eq $excludeFile) { + $excludeSig = $true + break + } + } + if (-not $excludeSig) { + $projectFilesSigned += $item + } + } + + # Get only the name of the binaries from the SQLNexus project + # Filter project files to include only those in the SourceFiles array + $nexusProjectBinFiles = $projectFilesUnsigned | Where-Object { $_.Name -in $SourceFiles } + $projectFilesName = $nexusProjectBinFiles.Name + + + # Check for files that are in the SourceFiles array but not in the project directory + # and files that are in the project directory but not in the SourceFiles array + $missingInSourceFiles = $projectFilesName | Where-Object { $_ -notin $SourceFiles } + $missingInProjectFiles = $SourceFiles | Where-Object { -not (Test-Path (Join-Path -Path $releaseBinaries -ChildPath $_)) } + + if ($missingInSourceFiles -or $missingInProjectFiles) { + if ($missingInSourceFiles) { + Write-Host "Files missing in SourceFiles array:`r`n $($missingInSourceFiles -join "`r`n ")" -ForegroundColor Red + } + if ($missingInProjectFiles) { + Write-Host "Files missing in project directory:`r`n $($missingInProjectFiles -join "`r`n ")" -ForegroundColor Red + } + exit 1 + } + + + + $beforeCopy = $(Get-Date) + Write-Host "Before Copy-Item in FilesToSign: $beforeCopy" + + # Copy each of the SQLNexus project binaries that need signing to the FilesToSign folder + $nexusProjectBinFiles | ForEach-Object { Copy-Item -Path $_.FullName -Destination $FilesToSignFolder } + + Write-Host "`r`nTotal files copied to the 'FilesToSign' folder: $($nexusProjectBinFiles.Count)" + + Write-Host "`r`nStarting to copy files to the 'Release_Unsigned' folders..." + Write-Host "`----------------------------------------------------------------" + + Write-Host "Before Copy-Item in Signed and Unsigned folders: $(Get-Date)" + $copiedFilesCount = 0 + # Copy all files to the Release_Unsigned folder, preserving the folder structure + $projectFilesUnsigned | ForEach-Object { + + # extract the relative path from the parent directory to the destination folder + # and create the corresponding destination path for Release_Signed_Prep and Release_Unsigned folders + $relativePath = $_.FullName.Substring($Nexus_RootDir.TrimEnd('\').Length + 1) + $destReleaseUnsignedPath = Join-Path -Path $ReleaseUnsignedFolder -ChildPath $relativePath + + # copy to Release_Unsigned folder as well + Write-Host "Copying file: '$($_.FullName)' to '$destReleaseUnsignedPath'" + Copy-Item -Path $_.FullName -Destination $destReleaseUnsignedPath -Force + + # Increment the copied files count for each file copied + $copiedFilesCount++ + } + + Write-Host "`r`nTotal files copied to the 'Release_Unsigned' folder': $copiedFilesCount" + + Write-Host "`r`nStarting to copy files to the 'Release_Signed_Prep' folders..." + Write-Host "`----------------------------------------------------------------" + + # reset the copied files count for the next copy operation + $copiedSignedFilesCount = 0 + + # Copy all files to the Release_Signed_Prep folder, preserving the folder structure + $projectFilesSigned | ForEach-Object { + + # extract the relative path from the parent directory to the destination folder + # and create the corresponding destination path for Release_Signed_Prep and Release_Unsigned folders + $relativePath = $_.FullName.Substring($Nexus_RootDir.Length + 1) + $destReleaseSignedPrepPath = Join-Path -Path $ReleaseSignedFolder -ChildPath $relativePath + + # copy to Release_Signed_Prep folder, preserving the folder structure + Write-Host "Copying file: '$($_.FullName)' to '$destReleaseSignedPrepPath'" + Copy-Item -Path $_.FullName -Destination $destReleaseSignedPrepPath -Force + + # Increment the copied files count for each file copied + $copiedSignedFilesCount++ + } + + Write-Host "`r`nTotal files copied to the 'Release_Signed_Prep' folder: $copiedFilesCount" + + + Write-Host "After Copy-Item: $(Get-Date). It took $((New-TimeSpan -Start $beforeCopy -End (Get-Date)).TotalSeconds) seconds to copy files." + Write-Host "`r`nList of files in the '$FilesToSignFolder' folder:" + Get-ChildItem -Path $FilesToSignFolder | Select-Object -Property Name, LastWriteTime, @{Name="LengthKB";Expression={[math]::Round($_.Length / 1KB, 2)}} | Format-Table -AutoSize + +} +catch { + Write-Host "An error occurred: $($_.Exception.Message)" -ForegroundColor Red + Write-Host "Script line number: $($_.InvocationInfo.ScriptLineNumber)" -ForegroundColor Red + + exit 1 +} +finally { + Write-Host "`nSummary:" + Write-Host "==========" + Write-Host "Total files from source folder: $($allItems.Count)" + Write-Host "Total files copied to 'FilesToSign': $($nexusProjectBinFiles.Count)" + Write-Host "Total files copied to 'Release_Unsigned': $copiedFilesCount" + Write-Host "Total files copied to 'Release_Signed': $copiedSignedFilesCount" + Write-Host "`r`nScript execution completed." +} \ No newline at end of file diff --git a/RowsetImportEngine/RowsetImportEngine.csproj b/RowsetImportEngine/RowsetImportEngine.csproj index f349ad7..61d01eb 100644 --- a/RowsetImportEngine/RowsetImportEngine.csproj +++ b/RowsetImportEngine/RowsetImportEngine.csproj @@ -83,7 +83,7 @@ TRACE - false + true 4096 false @@ -93,7 +93,7 @@ false false 4 - none + pdbonly prompt AnyCPU AllRules.ruleset