├── .gitattributes ├── .gitignore ├── Documentation └── Images │ ├── CICD.png │ └── sql.png ├── Example ├── CICD-Kicker.ps1 └── GitLabCI │ └── Non-Docker │ └── .gitlab-ci.yml ├── LICENSE.md ├── MSSQL-CICD-Helper.build.ps1 ├── MSSQL-CICD-Helper ├── MSSQL-CICD-Helper.psd1 ├── MSSQL-CICD-Helper.psm1 ├── Private │ ├── BuildDeploy │ │ └── Invoke-Cmd.ps1 │ └── Configuration │ │ ├── ImportConfig.ps1 │ │ └── currentversion.ps1 └── Public │ ├── BuildDeploy │ ├── Get-MSSQLCICDHelperFiletoBuildDeploy.ps1 │ ├── Invoke-MSSQLCICDHelperMSBuild.ps1 │ └── Invoke-MSSQLCICDHelperSQLPackage.ps1 │ └── Configuration │ ├── Get-MSSQLCICDHelperConfiguration.ps1 │ ├── Get-MSSQLCICDHelperPaths.ps1 │ └── Save-MSSQLCICDHelperConfiguration.ps1 ├── README.md ├── Tests ├── MSSQL-CICD-Helper.Module.Tests.ps1 ├── PSAnalyzer.Tests.ps1 └── Unit.Tests.ps1 ├── build.ps1 ├── fingerprint └── localpester.ps1 /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /output/** 3 | 4 | localbuild\.ps1 5 | -------------------------------------------------------------------------------- /Documentation/Images/CICD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Continuous-Data/MSSQL-CICD-Helper/b22f30438d0cdea6cf1155e07b4cbf66d5eed827/Documentation/Images/CICD.png -------------------------------------------------------------------------------- /Documentation/Images/sql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Continuous-Data/MSSQL-CICD-Helper/b22f30438d0cdea6cf1155e07b4cbf66d5eed827/Documentation/Images/sql.png -------------------------------------------------------------------------------- /Example/CICD-Kicker.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | $Function, 3 | $targetserver, 4 | $targetdatabase, 5 | $targetuser, 6 | $targetpw 7 | ) 8 | 9 | if(-not(Get-Module MSSQLCICDHelper)){ 10 | try{ 11 | $isGitInstalled = $null -ne ( (Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*) + (Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*) | Where-Object { $null -ne $_.DisplayName -and $_.Displayname.Contains('Git') }) 12 | if($isGitInstalled){ 13 | git clone https://github.com/tsteenbakkers/MSSQL-CICD-Helper.git 14 | } 15 | else{ 16 | Write-Error "Git is not installed. Make sure it is installed and try again!" 17 | } 18 | 19 | $curdir = Get-Location 20 | $modulefile = "{0}\MSSQL-CICD-Helper\MSSQLCICDHelper.psd1" -f $curdir 21 | 22 | Import-Module -name $modulefile -Verbose 23 | } 24 | catch{ 25 | write-error "something wnet wrong cloning or importing the MSSQL-CICD-helper module. please check and retry" 26 | Throw; 27 | } 28 | 29 | 30 | } 31 | 32 | switch ($function) { 33 | "build"{ 34 | Invoke-MSSQLCICDHelperMSBuild -Verbose -keeplogfiles 35 | } 36 | "Deploy"{ 37 | Invoke-MSSQLCICDHelperSQLPackage -Verbose -keeplogfiles -tsn $targetserver -tdn $targetdatabase -tu $targetuser -tp $targetpw 38 | } 39 | Default { 40 | Write-Error "invalid function chosen." 41 | break; 42 | } 43 | } -------------------------------------------------------------------------------- /Example/GitLabCI/Non-Docker/.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - deploy 4 | 5 | msbuild: 6 | stage: build 7 | artifacts: 8 | untracked: true 9 | script: 10 | - powershell -File .\CICD-Kicker.ps1 -function Build 11 | tags: [yourrunnertaghere] 12 | SSDTDeploy: 13 | stage: deploy 14 | artifacts: 15 | untracked: true 16 | script: 17 | - powershell -File .\CICD-Kicker.ps1 -function Deploy -TargetServer %yourciservervar% -TargetDatabase %yourcidbvar% -TargetUser %yourciuservar% -TargetPW %yourcipwvar% 18 | tags: [yourrunnertaghere] 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2018 Tobi Steenbakkers 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper.build.ps1: -------------------------------------------------------------------------------- 1 | #requires -Modules InvokeBuild, PSDeploy, BuildHelpers, PSScriptAnalyzer, PlatyPS, Pester 2 | $script:ModuleName = 'MSSQL-CICD-Helper' 3 | 4 | $script:Source = Join-Path $BuildRoot $ModuleName 5 | $script:Output = Join-Path $BuildRoot output 6 | $script:Destination = Join-Path $Output $ModuleName 7 | $script:ModulePath = "$Destination\$ModuleName.psm1" 8 | $script:ManifestPath = "$Destination\$ModuleName.psd1" 9 | $script:Imports = ( 'Private', 'Public' ) 10 | $script:TestFile = "$PSScriptRoot\output\TestResults_PS$PSVersion`_$TimeStamp.xml" 11 | $script:HelpRoot = Join-Path $Output 'help' 12 | 13 | function TaskX($Name, $Parameters) {task $Name @Parameters -Source $MyInvocation} 14 | 15 | Task Default Clean, Build, Pester, UpdateSource 16 | Task Build CopyToOutput, BuildPSM1, BuildPSD1 17 | Task Pester Build, ImportModule, UnitTests, FullTests 18 | Task LocalPester Build, ImportModule, UnitTests 19 | Task Local Build, Pester, UpdateSource 20 | 21 | Task Clean { 22 | 23 | If (Test-Path $Output) 24 | { 25 | $null = Remove-Item $Output -Recurse -ErrorAction Ignore 26 | } 27 | $null = New-Item -Type Directory -Path $Destination -ErrorAction Ignore 28 | } 29 | 30 | Task UnitTests { 31 | $TestResults = Invoke-Pester -Path Tests\*unit* -PassThru -Tag Build -ExcludeTag Slow 32 | if ($TestResults.FailedCount -gt 0) 33 | { 34 | Write-Error "Failed [$($TestResults.FailedCount)] Pester tests" 35 | } 36 | } 37 | 38 | Task FullTests { 39 | $TestResults = Invoke-Pester -Path Tests -PassThru -OutputFormat NUnitXml -OutputFile $testFile -Tag Build 40 | if ($TestResults.FailedCount -gt 0) 41 | { 42 | Write-Error "Failed [$($TestResults.FailedCount)] Pester tests" 43 | } 44 | } 45 | 46 | Task CopyToOutput { 47 | 48 | " Create Directory [$Destination]" 49 | $null = New-Item -Type Directory -Path $Destination -ErrorAction Ignore 50 | 51 | Get-ChildItem $source -File | 52 | where name -NotMatch "$ModuleName\.ps[dm]1" | 53 | Copy-Item -Destination $Destination -Force -PassThru | 54 | ForEach-Object { " Create [.{0}]" -f $_.fullname.replace($PSScriptRoot, '')} 55 | 56 | Get-ChildItem $source -Directory | 57 | where name -NotIn $imports | 58 | Copy-Item -Destination $Destination -Recurse -Force -PassThru | 59 | ForEach-Object { " Create [.{0}]" -f $_.fullname.replace($PSScriptRoot, '')} 60 | } 61 | 62 | TaskX BuildPSM1 @{ 63 | Inputs = (Get-Item "$source\*\*.ps1") 64 | Outputs = $ModulePath 65 | Jobs = { 66 | [System.Text.StringBuilder]$stringbuilder = [System.Text.StringBuilder]::new() 67 | foreach ($folder in $imports ) 68 | { 69 | [void]$stringbuilder.AppendLine( "Write-Verbose 'Importing from [$Source\$folder]'" ) 70 | if (Test-Path "$source\$folder") 71 | { 72 | $fileList = Get-ChildItem $source\$folder\ -Recurse -include *.ps1 | Where Name -NotLike '*.Tests.ps1' 73 | foreach ($file in $fileList) 74 | { 75 | $shortName = $file.fullname.replace($PSScriptRoot, '') 76 | " Importing [.$shortName]" 77 | [void]$stringbuilder.AppendLine( "# .$shortName" ) 78 | [void]$stringbuilder.AppendLine( [System.IO.File]::ReadAllText($file.fullname) ) 79 | } 80 | } 81 | } 82 | 83 | " Creating module [$ModulePath]" 84 | Set-Content -Path $ModulePath -Value $stringbuilder.ToString() 85 | } 86 | } 87 | 88 | TaskX BuildPSD1 @{ 89 | Inputs = (Get-ChildItem $Source -Recurse -File) 90 | Outputs = $ManifestPath 91 | Jobs = { 92 | 93 | Write-Output " Update [$ManifestPath]" 94 | Copy-Item "$source\$ModuleName.psd1" -Destination $ManifestPath 95 | 96 | $functions = Get-ChildItem $ModuleName\Public -Recurse -include *.ps1 | Where-Object { $_.name -notmatch 'Tests'} | Select-Object -ExpandProperty basename 97 | Set-ModuleFunctions -Name $ManifestPath -FunctionsToExport $functions 98 | 99 | Write-Output " Detecting semantic versioning" 100 | 101 | Import-Module ".\$ModuleName" 102 | $commandList = Get-Command -Module $ModuleName 103 | Remove-Module $ModuleName 104 | 105 | Write-Output " Calculating fingerprint" 106 | $fingerprint = foreach ($command in $commandList ) 107 | { 108 | foreach ($parameter in $command.parameters.keys) 109 | { 110 | '{0}:{1}' -f $command.name, $command.parameters[$parameter].Name 111 | $command.parameters[$parameter].aliases | Foreach-Object { '{0}:{1}' -f $command.name, $_} 112 | } 113 | } 114 | 115 | $fingerprint = $fingerprint | Sort-Object 116 | 117 | if (Test-Path .\fingerprint) 118 | { 119 | $oldFingerprint = Get-Content .\fingerprint 120 | } 121 | 122 | $bumpVersionType = 'Patch' 123 | ' Detecting new features' 124 | $fingerprint | Where {$_ -notin $oldFingerprint } | % {$bumpVersionType = 'Minor'; " $_"} 125 | ' Detecting breaking changes' 126 | $oldFingerprint | Where {$_ -notin $fingerprint } | % {$bumpVersionType = 'Major'; " $_"} 127 | 128 | Set-Content -Path .\fingerprint -Value $fingerprint 129 | 130 | # Bump the module version 131 | $version = [version] (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion') 132 | 133 | if ( $version -lt ([version]'1.0.0') ) 134 | { 135 | # Still in beta, don't bump major version 136 | if ( $bumpVersionType -eq 'Major' ) 137 | { 138 | $bumpVersionType = 'Minor' 139 | } 140 | else 141 | { 142 | $bumpVersionType = 'Patch' 143 | } 144 | } 145 | 146 | if(-not(Test-Path "$output\version.xml")){ 147 | " PSXML not found. Determining version with Get-NextPSGalleryVersion and creating PSXML" 148 | $galleryVersion = Get-NextPSGalleryVersion -Name $ModuleName 149 | $galleryVersion | Export-Clixml -Path "$output\version.xml" 150 | }else{ 151 | " PSXML found. Grabbing value from previous build." 152 | $galleryVersion = Import-Clixml -Path "$output\version.xml" 153 | } 154 | 155 | if ( $version -lt $galleryVersion ) 156 | { 157 | $version = $galleryVersion 158 | } 159 | 160 | Write-Output " Stepping [$bumpVersionType] version [$version]" 161 | $version = [version] (Step-Version $version -Type $bumpVersionType) 162 | Write-Output " Using version: $version" 163 | 164 | Update-Metadata -Path $ManifestPath -PropertyName ModuleVersion -Value $version 165 | } 166 | } 167 | 168 | Task UpdateSource { 169 | Copy-Item $ManifestPath -Destination "$source\$ModuleName.psd1" 170 | } 171 | 172 | Task ImportModule { 173 | if ( -Not ( Test-Path $ManifestPath ) ) 174 | { 175 | " Modue [$ModuleName] is not built, cannot find [$ManifestPath]" 176 | Write-Error "Could not find module manifest [$ManifestPath]. You may need to build the module first" 177 | } 178 | else 179 | { 180 | if (Get-Module $ModuleName) 181 | { 182 | " Unloading Module [$ModuleName] from previous import" 183 | Remove-Module $ModuleName 184 | } 185 | " Importing Module [$ModuleName] from [$ManifestPath]" 186 | Import-Module $ManifestPath -Force 187 | } 188 | } 189 | 190 | TaskX CreateHelp @{ 191 | Partial = $true 192 | Inputs = {Get-ChildItem "$ModuleName\Public\*.ps1"} 193 | Outputs = { 194 | process 195 | { 196 | Get-ChildItem $_ | % {'{0}\{1}.md' -f $HelpRoot, $_.basename} 197 | } 198 | } 199 | Jobs = 'ImportModule', { 200 | process 201 | { 202 | $null = New-Item -Path $HelpRoot -ItemType Directory -ErrorAction SilentlyContinue 203 | $mdHelp = @{ 204 | #Module = $script:ModuleName 205 | OutputFolder = $HelpRoot 206 | AlphabeticParamsOrder = $true 207 | Verbose = $true 208 | Force = $true 209 | Command = Get-Item $_ | % basename 210 | } 211 | New-MarkdownHelp @mdHelp | % fullname 212 | } 213 | } 214 | } 215 | 216 | TaskX PackageHelp @{ 217 | Inputs = {Get-ChildItem $HelpRoot -Recurse -File} 218 | Outputs = "$Destination\en-us\$ModuleName-help.xml" 219 | Jobs = 'CreateHelp', { 220 | New-ExternalHelp -Path $HelpRoot -OutputPath "$Destination\en-us" -force | % fullname 221 | } 222 | } 223 | 224 | task Install Uninstall, { 225 | $version = [version] (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion') 226 | 227 | $path = $env:PSModulePath.Split(';').Where( { 228 | $_ -like 'C:\Users\*' 229 | }, 'First', 1) 230 | 231 | if ($path -and (Test-Path -Path $path)) 232 | { 233 | "Using [$path] as base path..." 234 | $path = Join-Path -Path $path -ChildPath $ModuleName 235 | $path = Join-Path -Path $path -ChildPath $version 236 | 237 | "Creating directory at [$path]..." 238 | New-Item -Path $path -ItemType 'Directory' -Force -ErrorAction 'Ignore' 239 | 240 | "Copying items from [$Destination] to [$path]..." 241 | Copy-Item -Path "$Destination\*" -Destination $path -Recurse -Force 242 | } 243 | } 244 | 245 | task Uninstall { 246 | 'Unloading Modules...' 247 | Get-Module -Name $ModuleName -ErrorAction 'Ignore' | Remove-Module 248 | 249 | 'Uninstalling Module packages...' 250 | $modules = Get-Module $ModuleName -ErrorAction 'Ignore' -ListAvailable 251 | foreach ($module in $modules) 252 | { 253 | Uninstall-Module -Name $module.Name -RequiredVersion $module.Version -ErrorAction 'Ignore' 254 | } 255 | 256 | 'Cleaning up manually installed Modules...' 257 | $path = $env:PSModulePath.Split(';').Where( { 258 | $_ -like 'C:\Users\*' 259 | }, 'First', 1) 260 | 261 | $path = Join-Path -Path $path -ChildPath $ModuleName 262 | if ($path -and (Test-Path -Path $path)) 263 | { 264 | 'Removing files... (This may fail if any DLLs are in use.)' 265 | Get-ChildItem -Path $path -File -Recurse | 266 | Remove-Item -Force | ForEach-Object 'FullName' 267 | 268 | 'Removing folders... (This may fail if any DLLs are in use.)' 269 | Remove-Item $path -Recurse -Force 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/MSSQL-CICD-Helper.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'MSSQL-CICD-Helper' 3 | # 4 | # Generated by: Tobi Steenbakkers 5 | # 6 | # Generated on: 2018-04-01 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'MSSQL-CICD-Helper.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.0.4' 16 | 17 | # ID used to uniquely identify this module 18 | GUID = '2287837f-86ec-43ef-97a8-fee9f33a7c33' 19 | 20 | # Author of this module 21 | Author = 'Tobi Steenbakkers' 22 | 23 | # Company or vendor of this module 24 | # CompanyName = 'Unknown' 25 | 26 | # Copyright statement for this module 27 | Copyright = '(c) 2018 MSSQL-CICD-Helper. All rights reserved.' 28 | 29 | # Description of the functionality provided by this module 30 | Description = 'Set of Powershell functions to aid in CI/CD processes to build with MSBuild and deploying various *pac files used in MSSQL Database and SSIS deployment' 31 | 32 | # Minimum version of the Windows PowerShell engine required by this module 33 | #PowerShellVersion = '3' 34 | 35 | # Name of the Windows PowerShell host required by this module 36 | # PowerShellHostName = '' 37 | 38 | # Minimum version of the Windows PowerShell host required by this module 39 | # PowerShellHostVersion = '' 40 | 41 | # Minimum version of Microsoft .NET Framework required by this module 42 | # DotNetFrameworkVersion = '' 43 | 44 | # Minimum version of the common language runtime (CLR) required by this module 45 | # CLRVersion = '' 46 | 47 | # Processor architecture (None, X86, Amd64) required by this module 48 | # ProcessorArchitecture = '' 49 | 50 | # Modules that must be imported into the global environment prior to importing this module 51 | # RequiredModules = @() 52 | 53 | # Assemblies that must be loaded prior to importing this module 54 | # RequiredAssemblies = @() 55 | 56 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 57 | # ScriptsToProcess = @() 58 | 59 | # Type files (.ps1xml) to be loaded when importing this module 60 | # TypesToProcess = @() 61 | 62 | # Format files (.ps1xml) to be loaded when importing this module 63 | #FormatsToProcess = @('MSSQLCICDHelper.Format.ps1xml') 64 | 65 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 66 | # NestedModules = @() 67 | 68 | # Functions to export from this module 69 | FunctionsToExport = @('Get-MSSQLCICDHelperFiletoBuildDeploy','Invoke-MSSQLCICDHelperMSBuild','Invoke-MSSQLCICDHelperSQLPackage','Get-MSSQLCICDHelperConfiguration','Get-MSSQLCICDHelperPaths','Save-MSSQLCICDHelperConfiguration') 70 | 71 | # Cmdlets to export from this module 72 | #CmdletsToExport = '*' 73 | 74 | # Variables to export from this module 75 | #VariablesToExport = '*' 76 | 77 | # Aliases to export from this module 78 | #AliasesToExport = '*' 79 | 80 | # DSC resources to export from this module 81 | # DscResourcesToExport = @() 82 | 83 | # List of all modules packaged with this module 84 | # ModuleList = @() 85 | 86 | # List of all files packaged with this module 87 | # FileList = @() 88 | 89 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 90 | PrivateData = @{ 91 | 92 | PSData = @{ 93 | 94 | # Tags applied to this module. These help with module discovery in online galleries. 95 | Tags = @('MSSQL', 96 | 'Powershell', 97 | 'MSBuild', 98 | 'CI' 99 | 'ContinuousIntegration', 100 | 'CD', 101 | 'ContiuousDelivery', 102 | 'ContiuousDeployment', 103 | 'Dacpac', 104 | 'SQL', 105 | 'DTSPac' 106 | ) 107 | 108 | # A URL to the license for this module. 109 | LicenseUri = 'https://github.com/tsteenbakkers/MSSQL-CICD-Helper/blob/master/LICENSE' 110 | 111 | # A URL to the main website for this project. 112 | ProjectUri = 'https://github.com/tsteenbakkers/MSSQL-CICD-Helper' 113 | 114 | # A URL to an icon representing this module. 115 | # IconUri = '' 116 | 117 | # ReleaseNotes of this module 118 | # ReleaseNotes = '' 119 | 120 | } # End of PSData hashtable 121 | 122 | } # End of PrivateData hashtable 123 | 124 | # HelpInfo URI of this module 125 | # HelpInfoURI = '' 126 | 127 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 128 | # DefaultCommandPrefix = '' 129 | 130 | } 131 | 132 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/MSSQL-CICD-Helper.psm1: -------------------------------------------------------------------------------- 1 | #Get public and private function definition files. 2 | $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\ -Include *.ps1 -Recurse -ErrorAction SilentlyContinue ) 3 | $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\ -Include *.ps1 -Recurse -ErrorAction SilentlyContinue ) 4 | $Config = @( Get-ChildItem -Path $PSScriptRoot\Config\ -Include *.ps1 -Recurse -ErrorAction SilentlyContinue ) 5 | 6 | #Dot source the files 7 | Foreach($import in @($Private + $Public + $Config)) { 8 | Try { 9 | . $import.fullname 10 | } 11 | Catch { 12 | Write-Error -Message "Failed to import function $($import.fullname): $_" 13 | } 14 | } 15 | 16 | Export-ModuleMember -Function $Public.Basename 17 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Private/BuildDeploy/Invoke-Cmd.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-Cmd { 2 | <# 3 | .SYNOPSIS 4 | Executes a Invoke-Expression / CMD.Exe based on the Given Parameters. Allows for re-using part of program code and mocking 5 | 6 | .DESCRIPTION 7 | 8 | 9 | .PARAMETER Executable 10 | 11 | 12 | .PARAMETER Arguments 13 | Specifies the Arguments for which the Invoked Cmd should use. 14 | 15 | .PARAMETER logfile 16 | Specifies the Arguments for which the Invoked Cmd should use. 17 | 18 | .PARAMETER errorlogfile 19 | Specifies the Arguments for which the Invoked Cmd should use. 20 | 21 | .OUTPUTS 22 | A result set with the outcome of the process along with some metrics. 23 | 24 | .EXAMPLE 25 | Invoke-Cmd -Arguments 26 | 27 | Will execute the following command: 28 | 29 | .LINK 30 | Project home: https://github.com/tsteenbakkers/MSSQL-CICD-Helper 31 | 32 | .NOTES 33 | Name: MSSQLCICDHelper 34 | Author: Tobi Steenbakkers 35 | Version: 1.0.0 36 | #> 37 | [cmdletbinding()] 38 | param( 39 | [Parameter(Mandatory=$true, 40 | HelpMessage='What to find: *.sln, *.dacpac, *.publish.xml *.dtspac or *.sqlproject File. Options are: Solution, DacPac, DTSPac or Project', 41 | Position=0)] 42 | [ValidateNotNullOrEmpty()] 43 | $executable, 44 | 45 | [Parameter(Mandatory=$true, 46 | HelpMessage='Path where search for $typetofind should be started', 47 | Position=0)] 48 | [ValidateNotNullOrEmpty()] 49 | $arguments, 50 | 51 | [Parameter(Mandatory=$true, 52 | HelpMessage='Determines the basepath where logfiles should be stored. if empty the directory where the script is running will be used', 53 | Position=0)] 54 | [Alias("lf")] 55 | [ValidateNotNullOrEmpty()] 56 | [String] $logfile, 57 | 58 | [Parameter(Mandatory=$true, 59 | HelpMessage='Determines the basepath where logfiles should be stored. if empty the directory where the script is running will be used', 60 | Position=0)] 61 | [Alias("elf")] 62 | [ValidateNotNullOrEmpty()] 63 | [String] $errorlogfile 64 | ) 65 | $poutput = @{} 66 | $poutput.Output = [string]::Empty 67 | $poutput.ErrorOutput = [string]::Empty 68 | $poutput.Message = [string]::Empty 69 | $poutput.Duration = [TimeSpan]::Zero 70 | $poutput.Succeeded = $null 71 | $poutput.ExitCode = $null 72 | 73 | $logbase = Split-Path -path $logfile -Parent 74 | $errorlogbase = Split-Path -path $logfile -Parent 75 | 76 | if(-not(Test-Path -Path $logbase) -or -not(Test-Path -Path $errorlogbase)){ 77 | 78 | $poutput.Message = "Could not find parent of $logfile or $errorlogfile unable to proceed to execute Cmd." 79 | 80 | Write-Error "$($poutput.message)" 81 | return $result 82 | throw; 83 | } 84 | 85 | try{ 86 | 87 | $pinfo = New-Object System.Diagnostics.ProcessStartInfo 88 | $pinfo.FileName = $executable 89 | $pinfo.Arguments = $arguments 90 | 91 | #$pinfo.Passthru = $true 92 | $pinfo.RedirectStandardError = $true 93 | $pinfo.RedirectStandardOutput = $true 94 | $pinfo.UseShellExecute = $false 95 | 96 | if($debug){ 97 | $pinfo 98 | } 99 | #executing the command and storing the result inside $p: 100 | $p = New-Object System.Diagnostics.Process 101 | $p.StartInfo = $pinfo 102 | $p.Start() | Out-Null 103 | 104 | $poutput.output = $p.StandardOutput.ReadToEnd() 105 | $poutput.erroroutput = $p.StandardError.read() 106 | $poutput.Duration = $p.ExitTime - $p.StartTime 107 | $poutput.output | Out-file -literalpath $logfile -Force 108 | $poutput.erroroutput | Out-file -literalpath $errorlogfile -Force 109 | $poutput.ExitCode = $p.ExitCode 110 | 111 | return $poutput 112 | 113 | }catch{ 114 | $errorMessage = $_ 115 | $poutput.Message = "Unexpected error occurred while processing : $errorMessage" 116 | $poutput.Succeeded = $false 117 | Write-Error ($poutput.Message) 118 | return $poutput 119 | throw; 120 | } 121 | } -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Private/Configuration/ImportConfig.ps1: -------------------------------------------------------------------------------- 1 | Function ImportConfig { 2 | <# 3 | .Synopsis 4 | Check for configuration and output the information. 5 | .DESCRIPTION 6 | Check for configuration and output the information. Goes into the $env:appdata for the configuration file. 7 | .EXAMPLE 8 | ImportConfig 9 | #> 10 | $currentversion = CurrentVersion 11 | 12 | if ( $IsWindows -or ( [version]$PSVersionTable.PSVersion -lt [version]"5.99.0" ) ) { 13 | $ConfigFile = "{0}\MSSQLCICDHelper\MSSQLCICDHelperConfiguration.xml" -f $env:appdata 14 | } elseif ( $IsLinux ) { 15 | $ConfigFile = "{0}/.MSSQLCICDHelper/MSSQLCICDHelperConfiguration.xml" -f $HOME 16 | } else { 17 | Write-Error "Unknown Platform" 18 | } 19 | if (Test-Path $ConfigFile) { 20 | $config = Import-Clixml $ConfigFile 21 | #$config 22 | if($config.version -ne $currentversion){ 23 | Write-Warning "Warning! current version = $currentversion. found version in config is $($config.version). This might lead to incompatibility issues. Please Run Save-MSSQL-CICD-HelperConfiguration or update your software (https://github.com/tsteenbakkers/MSSQL-CICD-Helper) if you experience issues." 24 | return $config 25 | }else{ 26 | return $config 27 | } 28 | 29 | 30 | } else { 31 | Write-Warning 'No saved configuration information found. Run Save-MSSQL-CICD-HelperConfiguration.' 32 | Write-Warning "path which was looked for: $configfile" 33 | throw; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Private/Configuration/currentversion.ps1: -------------------------------------------------------------------------------- 1 | Function CurrentVersion { 2 | <# 3 | .Synopsis 4 | Returns version of the current software for checking config file etc. 5 | .DESCRIPTION 6 | Returns version of the current software for checking config file etc. 7 | .EXAMPLE 8 | ImportConfig 9 | #> 10 | 11 | return 'v1.0.0' 12 | } 13 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Public/BuildDeploy/Get-MSSQLCICDHelperFiletoBuildDeploy.ps1: -------------------------------------------------------------------------------- 1 | function Get-MSSQLCICDHelperFiletoBuildDeploy { 2 | <# 3 | .SYNOPSIS 4 | Searches the system recursively for the given rootpath for the given type to find. 5 | 6 | .DESCRIPTION 7 | Searches the system recursively for the given rootpath for the given type to find. 8 | The function returns a full filename of the chosen type for aid in building and deploying in CICD Scenario's. 9 | If multiple of the same type are found it will return an error since only one can be built at once. 10 | For building solutions with multiple projects specify the solution instead of the project 11 | 12 | .PARAMETER typetofind 13 | Determines the kind of file to find. Accepts Solution, Project, Dacpac, PublishProfile, DTSPac 14 | Mandatory 15 | 16 | .PARAMETER rootpath 17 | Specifies the path where the function needs to start looking for the $typetofind 18 | Mandatory 19 | 20 | .OUTPUTS 21 | A filename (full path) to the file the function is supposed to find based on its rootpath and type to find. 22 | 23 | .EXAMPLE 24 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath C:\ 25 | 26 | Will Search C:\ for *sln files 27 | 28 | .LINK 29 | Project home: https://github.com/tsteenbakkers/MSSQL-CICD-Helper 30 | 31 | .NOTES 32 | Name: MSSQLCICDHelper 33 | Author: Tobi Steenbakkers 34 | Version: 1.0.0 35 | #> 36 | [cmdletbinding()] 37 | param( 38 | [Parameter(Mandatory=$true, 39 | HelpMessage='What to find: *.sln, *.dacpac, *.publish.xml *.dtspac or *.sqlproject File. Options are: Solution, DacPac, DTSPac or Project', 40 | Position=0)] 41 | [ValidateNotNullOrEmpty()] 42 | $typetofind, 43 | 44 | [Parameter(Mandatory=$true, 45 | HelpMessage='Path where search for $typetofind should be started', 46 | Position=0)] 47 | [ValidateNotNullOrEmpty()] 48 | $rootpath 49 | ) 50 | 51 | switch ($typetofind) { 52 | "solution"{ 53 | $buildfilextension = '*.sln' 54 | } 55 | "project"{ 56 | $buildfilextension = '*.sqlproj' 57 | } 58 | "DacPac"{ 59 | $buildfilextension = '*.dacpac' 60 | } 61 | "PublishProfile"{ 62 | $buildfilextension = '*.publish.xml' 63 | } 64 | "DTSPac"{ 65 | $buildfilextension = '*.dtspac' 66 | } 67 | default { 68 | Write-Error "Invalid option given for input param -typetofind. valid options are: Solution, Project, dacpac, PublishProfile or dtspac" 69 | throw; 70 | } 71 | } 72 | 73 | if(-not(Test-Path $rootpath)){ 74 | Write-Error "$rootpath was not found" 75 | throw; 76 | } 77 | 78 | $results = Get-ChildItem -Path $rootpath -Filter $buildfilextension -Recurse -ErrorAction SilentlyContinue 79 | 80 | Write-Verbose "Found $($results.Count) $buildfilextension files in $rootpath" 81 | 82 | if($results.Count -lt 1){ 83 | Write-Error 'No Files found! Please check path and re-run Get-MSSQLCICDHelperFiletoBuild. Exiting' 84 | throw; 85 | } 86 | elseif($results.Count -gt 1){ 87 | Write-Verbose 'Found multiple files. will return the file with the most recent writedatetime.' 88 | $result = $results | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Get-ChildItem 89 | } 90 | elseif($results.Count -eq 1){ 91 | $result = $results 92 | } 93 | 94 | $result 95 | 96 | } -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Public/BuildDeploy/Invoke-MSSQLCICDHelperMSBuild.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-MSSQLCICDHelperMSBuild { 2 | <# 3 | .SYNOPSIS 4 | Builds the given Visual Studio solution or project file using MsBuild. 5 | 6 | .DESCRIPTION 7 | Executes the MsBuild.exe tool against the specified Visual Studio solution or project file. 8 | Returns a hash table with properties for determining if the build succeeded or not, as well as other information (see the OUTPUTS section for list of properties). 9 | 10 | .PARAMETER filename 11 | The path of the Visual Studio solution or project to build (e.g. a .sln or .csproj file). 12 | If left empty the Module will search Recursively for the nearest Solution file where the basepath will the path where the script is Ran (not to be confused with script location) 13 | 14 | .PARAMETER MSBuildArguments 15 | Additional parameters to pass to the MsBuild command-line tool. This can be any valid MsBuild command-line parameters except for the path of 16 | the solution/project to build. 17 | 18 | See http://msdn.microsoft.com/en-ca/library/vstudio/ms164311.aspx for valid MsBuild command-line parameters. 19 | 20 | .PARAMETER hidden 21 | Switch to use when output from the MSBuild command line tool needs to be hidden instead of shown. 22 | When using the Invoke-MSBuild feature you need to explicitly tell it to show info with "-ShowBuildOutputInCurrentWindow" or something similar given to Parameter -InvokeMSBuildParameters 23 | 24 | 25 | .PARAMETER keeplogfiles 26 | Switch to specify that log files should be deleted. only applies on successfull builds. 27 | 28 | 29 | .PARAMETER UseInvokeMSBuildModule 30 | Instead of using the MSBuildfeatures from MSSQL-CICD-Helper the more advanced Invoke-MSBuild can be called. 31 | 32 | .PARAMETER InvokeMSBuildParameters 33 | This Parameter is used as a string to pass additional parameters to the Invoke-MSBuild function. 34 | Default we pass -Path, -LogDirectory and -KeepBuildLogOnSuccessfulBuilds so keep away from those. 35 | 36 | .OUTPUTS 37 | a hashtable with the following details is returned (whether or not using Invoke-MSBuild as the executor): 38 | 39 | BuildSucceeded = $true if the build passed, $false if the build failed, and $null if we are not sure. 40 | BuildLogFilePath = The path to the build's log file. 41 | BuildErrorsLogFilePath = The path to the build's error log file. 42 | FiletoBuild = The item that MsBuild ran against. 43 | CommandUsedToBuild = The full command that was used to invoke MsBuild. This can be useful for inspecting what parameters are passed to MsBuild.exe. 44 | Message = A message describing any problems that were encoutered by Invoke-MsBuild. This is typically an empty string unless something went wrong. 45 | MsBuildProcess = The process that was used to execute MsBuild.exe. 46 | BuildDuration = The amount of time the build took to complete, represented as a TimeSpan. 47 | 48 | .EXAMPLE 49 | 50 | Invoke-MSSQLCICDHelperMSBuild 51 | 52 | Will Run Invoke-MSSQLCICDHelperMSBuild with the default settings 53 | Filename = Autodetect 54 | Non hidden 55 | Delete logfiles when successfull. 56 | 57 | .EXAMPLE 58 | 59 | Invoke-MSSQLCICDHelperMSBuild -Verbose 60 | 61 | Will Run Invoke-MSSQLCICDHelperMSBuild with the default settings but show verbose output. (this goes for other CMDLetbindings aswell) 62 | Filename = Autodetect 63 | Non hidden 64 | Delete logfiles when successfull. 65 | 66 | .EXAMPLE 67 | 68 | Invoke-MSSQLCICDHelperMSBuild -filename 69 | 70 | Will Run Invoke-MSSQLCICDHelperMSBuild with the default settings other than filename 71 | Filename = given file 72 | Non hidden 73 | Delete logfiles when successfull. 74 | 75 | .EXAMPLE 76 | 77 | Invoke-MSSQLCICDHelperMSBuild -KeepLogfiles -hidden 78 | 79 | Will Run Invoke-MSSQLCICDHelperMSBuild with not showing ouput and keeping logfiles when done. 80 | Filename = Autodetect 81 | hidden 82 | Don't Delete logfiles when successfull. 83 | 84 | .EXAMPLE 85 | 86 | Invoke-MSSQLCICDHelperMSBuild -MSBuildArguments "t:build" 87 | 88 | Will Run Invoke-MSSQLCICDHelperMSBuild with additional msbuild parameters. 89 | See http://msdn.microsoft.com/en-ca/library/vstudio/ms164311.aspx for valid MsBuild command-line parameters. 90 | 91 | Filename = Autodetect 92 | Non hidden 93 | Delete logfiles when successfull. 94 | 95 | .EXAMPLE 96 | 97 | Invoke-MSSQLCICDHelperMSBuild -UseInvokeMSBuildModule 98 | 99 | Will Run Invoke-MSBuild with the default settings. The Following parameters will automatically be supplied to Invoke-MSBuild: -Path $filename (or auto-detect), -LogDirectory Parent of $filename and -KeepBuildLogOnSuccessfulBuilds because we want to have files to check. 100 | Filename = Auto-Detect 101 | Hidden by default 102 | Delete logfiles when successfull. 103 | 104 | .EXAMPLE 105 | 106 | Invoke-MSSQLCICDHelperMSBuild -UseInvokeMSBuildModule -InvokeMSBuildParameters 107 | 108 | Will Run Invoke-MSBuild with the additional settings specified. keep away from below settings because we automatically feed them to the function and can't be specified twice 109 | The Following parameters will automatically be supplied to Invoke-MSBuild: -Path $filename (or auto-detect), -LogDirectory Parent of $filename and -KeepBuildLogOnSuccessfulBuilds because we want to have files to check. 110 | 111 | See https://github.com/deadlydog/Invoke-MsBuild for valid optional Parameters. 112 | 113 | Filename = Auto-Detect 114 | Hidden by default 115 | Delete logfiles when successfull. 116 | 117 | .LINK 118 | Project home: https://github.com/tsteenbakkers/MSSQL-CICD-Helper 119 | 120 | .NOTES 121 | Name: MSSQLCICDHelper 122 | Author: Tobi Steenbakkers (partly based on the Invoke-MSBuild Module by Daniel Schroeder https://github.com/deadlydog/Invoke-MsBuild) 123 | Version: 1.0.0 124 | #> 125 | 126 | [cmdletbinding()] 127 | param( 128 | [Parameter(Mandatory=$false, 129 | HelpMessage='Filename which should be used for building. If empty it will find the nearest Solution based on the directory it was invoked from.', 130 | Position=0)] 131 | 132 | [ValidateScript({Test-Path -Path $_ -PathType Leaf})] 133 | $filename, 134 | [Parameter(Mandatory=$false, 135 | HelpMessage='Provides Build Arguments. Example /target:clean;build', 136 | Position=0)] 137 | [Alias("Parameters","Params","P")] 138 | [ValidateNotNullOrEmpty()] 139 | [String] $MSBuildArguments, 140 | 141 | [Parameter(Mandatory=$false, 142 | HelpMessage='Switch to only retrieve the outcome of MSBuild. Hides the MSBuildProces on screen. When using Invoke-MSBuild the default setting for invoking the function will be hidden.', 143 | Position=0)] 144 | #[Alias("Parameters","Params","P")] 145 | [ValidateNotNullOrEmpty()] 146 | [switch] $hidden, 147 | 148 | [Parameter(Mandatory=$false, 149 | HelpMessage='Switch to keep the log files after checking them. for results.', 150 | Position=0)] 151 | #[Alias("Parameters","Params","P")] 152 | [ValidateNotNullOrEmpty()] 153 | [switch] $keeplogfiles, 154 | 155 | [Parameter(Mandatory=$false, 156 | HelpMessage='Switch to use the Invoke-MSBuild Module instead of built-in process.', 157 | Position=0)] 158 | #[Alias("Parameters","Params","P")] 159 | [ValidateNotNullOrEmpty()] 160 | [switch] $UseInvokeMSBuildModule, 161 | 162 | [Parameter(Mandatory=$false, 163 | HelpMessage='Provide the optional parameters for Invoke-MSBuild ($path will be provided from this script based on $filename)', 164 | Position=0)] 165 | #[Alias("Parameters","Params","P")] 166 | [ValidateNotNullOrEmpty()] 167 | [String] $InvokeMSBuildParameters 168 | ) 169 | 170 | $result = @{} 171 | $result.CommandUsedToBuild = [string]::Empty 172 | $result.MsBuildProcess = $null 173 | $result.BuildSucceeded = $null 174 | $result.Message = [string]::Empty 175 | $result.BuildDuration = [TimeSpan]::Zero 176 | $result.BuildLogFilePath = $null 177 | $result.BuildLogFile = $null 178 | $result.FiletoBuild = $null 179 | 180 | try{ 181 | 182 | 183 | if($UseInvokeMSBuildModule){ 184 | if(-not(Get-Module Invoke-MSBuild)){ 185 | Write-Error 'Invoke-MSBuild was not found on this system. Make sure it is installed with Install-Module Invoke-MSBuild' 186 | throw; 187 | } 188 | 189 | } 190 | 191 | $configfile = ImportConfig 192 | $curdir = Get-location 193 | 194 | if($null -eq $filename){ 195 | 196 | write-verbose "No filename given. Running Get-MSSQLCICDHelperFiletoBuildDeploy based to find the Solution in current script path $curdir" 197 | $filename = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind 'Solution' -RootPath $curdir | Get-ChildItem 198 | } 199 | else{ 200 | if(-not(Test-Path $filename)){ 201 | Write-Error "File $filename not found." 202 | throw; 203 | }else{ 204 | $filename = Get-ChildItem $filename 205 | } 206 | 207 | } 208 | 209 | Write-Verbose "The following file will be built: $($filename.Name) located in path $($filename.DirectoryName)" 210 | 211 | $logfile = "$($filename.FullName).msbuild.log" 212 | $logbase = Split-Path -path $logfile -Parent 213 | $result.BuildLogFilePath = $logbase 214 | $result.BuildLogFile = $logfile 215 | $result.FiletoBuild = $filename.FullName 216 | $processlogfile = "$($filename.FullName).msbuildlog.log" 217 | $processerrorlogfile = "$($filename.FullName).msbuilderrorlog.log" 218 | 219 | Write-Verbose "The following build arguments will be used: $MSBuildArguments" 220 | $configfile['MSBuildExe'] 221 | 222 | Write-Verbose "Constructing Command to build..." 223 | if(-not($UseInvokeMSBuildModule)){ 224 | 225 | $CommandtoExecute = "/k "" ""$($configfile['MSBuildExe'])"" ""$($filename.FullName)"" /fl /flp:logfile=""$($logfile)""" 226 | if ($MSBuildArguments){ 227 | $CommandtoExecute += " $($MSBuildArguments)" 228 | } 229 | $CommandtoExecute += " & Exit"" " 230 | #Write-Verbose "Command to be Executed is: cmd.exe $commandtoexecute" 231 | $result.CommandUsedToBuild = "Command to be Executed is: cmd.exe $commandtoexecute" 232 | 233 | $processoutput = Invoke-Cmd -executable 'cmd.exe' -arguments $CommandtoExecute -logfile $processlogfile -errorlogfile $processerrorlogfile 234 | 235 | if(!$hidden){ 236 | "Normal Output: " 237 | $processoutput.output 238 | "Error Output:" 239 | $processoutput.erroroutput 240 | } 241 | # if($hidden){ 242 | # Write-verbose "Starting MSBuild ..." 243 | # $result.MsBuildProcess = Start-Process cmd.exe -ArgumentList $CommandtoExecute -Wait -WindowStyle Hidden -PassThru 244 | # }else{ 245 | # Write-verbose "Starting MSBuild ..." 246 | # $result.MsBuildProcess = Start-Process cmd.exe -ArgumentList $CommandtoExecute -Wait -NoNewWindow -PassThru 247 | # } 248 | }else{ 249 | $CommandtoExecute = "Invoke-MSBuild -Path $($filename.FullName) -logdirectory $($logbase)" 250 | 251 | $CommandtoExecute += " -KeepBuildLogOnSuccessfulBuilds" 252 | 253 | if($MSBuildArguments){ 254 | 255 | $CommandtoExecute += " -MsBuildParameters ""$($MSBuildArguments)""" 256 | } 257 | 258 | if ($InvokeMSBuildParameters){ 259 | $CommandtoExecute += " $($InvokeMSBuildParameters)" 260 | } 261 | 262 | $result.CommandUsedToBuild = "Command to be Executed is: $commandtoexecute" 263 | Write-verbose "Starting MSBuild ..." 264 | 265 | $processoutput = Invoke-Cmd -executable 'powershell.exe' -arguments $CommandtoExecute -logfile $processlogfile -errorlogfile $processerrorlogfile 266 | 267 | if(!$hidden){ 268 | "Normal Output: " 269 | $processoutput.output 270 | "Error Output:" 271 | $processoutput.erroroutput 272 | } 273 | 274 | #$result.MsBuildProcess = Invoke-Expression $CommandtoExecute 275 | } 276 | }catch{ 277 | $errorMessage = $_ 278 | $result.Message = "Unexpected error occurred while building ""$Path"": $errorMessage" 279 | $result.BuildSucceeded = $false 280 | Write-Error ($result.Message) 281 | return $result 282 | throw; 283 | } 284 | 285 | Write-verbose "MSBuild Started. Continue Checking results..." 286 | 287 | 288 | if(!(Test-Path -Path $result.BuildLogFile)){ 289 | $Result.BuildSucceeded = $false 290 | $result.Message = "Could not find file at '$($result.BuildLogFile)' unable to check for correct build." 291 | 292 | Write-Error "$($result.message)" 293 | return $result 294 | throw; 295 | } 296 | 297 | if($UseInvokeMSBuildModule){ 298 | # [bool] $buildReturnedSuccessfulExitCode = $result.MsBuildProcess.MsBuildProcess.ExitCode -eq 0 299 | # $result.BuildDuration = $result.MsBuildProcess.MsBuildProcess.ExitTime - $result.MsBuildProcess.MsBuildProcess.StartTime 300 | [bool] $buildReturnedSuccessfulExitCode = $processoutput.ExitCode -eq 0 301 | $result.BuildDuration = $processoutput.Duration 302 | }else{ 303 | [bool] $buildReturnedSuccessfulExitCode = $processoutput.ExitCode -eq 0 304 | $result.BuildDuration = $processoutput.Duration 305 | } 306 | 307 | [bool] $buildOutputDoesNotContainFailureMessage = (Select-String -Path $($result.BuildLogFile) -Pattern "Build FAILED." -SimpleMatch) -eq $null 308 | 309 | $buildSucceeded = $buildOutputDoesNotContainFailureMessage -and $buildReturnedSuccessfulExitCode 310 | 311 | if ($buildSucceeded -eq $true){ 312 | $result.BuildSucceeded = $true 313 | $result.Message = "Build Passed Successfully" 314 | 315 | if (!$keeplogfiles) 316 | { 317 | if (Test-Path $($result.BuildLogFile) -PathType Leaf) { Remove-Item -Path $($result.BuildLogFile) -Force } 318 | 319 | $result.BuildLogFile = $null 320 | } 321 | 322 | 323 | }else{ 324 | $result.BuildSucceeded = $false 325 | $result.Message = "Building ""$($result.FiletoBuild)"" Failed! Please check ""$($result.BuildLogFile)"" " 326 | Write-Error "$($result.message)" 327 | return $result 328 | throw; 329 | } 330 | 331 | Write-Verbose "MSBuild passed. See results below..." 332 | $result 333 | 334 | } 335 | 336 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Public/BuildDeploy/Invoke-MSSQLCICDHelperSQLPackage.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-MSSQLCICDHelperSQLPackage { 2 | <# 3 | .SYNOPSIS 4 | publishes a given DacPac file to specified target. 5 | 6 | .DESCRIPTION 7 | Executes the SQLPackage.exe tool against the specified DacPac file. If no DacPac file is specified the tool will search for one from current directory (root of the Source Code.) 8 | Returns a hash table with properties for determining if the publish succeeded or not, as well as other information (see the OUTPUTS section for list of properties). 9 | 10 | .PARAMETER filename 11 | The path including file of the DacPac file to publish (e.g. a .dacpac file). 12 | If left empty the Module will search Recursively for the nearest Solution file where the basepath will the path where the script is Ran (not to be confused with script location) 13 | 14 | .PARAMETER AdditionalArguments 15 | Additional parameters to pass to the SQLPackage command-line tool. This can be any valid sqlpackage command-line parameter(s) except for the ones mentioned below. 16 | 17 | See https://msdn.microsoft.com/library/hh550080(vs.103).aspx#Publish%20Parameters,%20Properties,%20and%20SQLCMD%20Variables for valid SQLPackage command-line parameters. 18 | 19 | Please note that the following parameters are already used / reserverd and should not be used: 20 | 21 | /action 22 | /SourceFile 23 | /Profile 24 | /TargetConnectionString 25 | /TargetServerName 26 | /TargetDatabaseName 27 | /TargetUsername 28 | /TargetPassword 29 | 30 | .PARAMETER logfilepath 31 | Determines the basepath where logfileoutput will be stored. If left empty the directory will be used where the script is ran. 32 | 33 | .PARAMETER TargetConnectionString 34 | Identifies the Connectionstring to be used for the Target. Will overrule any other $Targetparameter. 35 | 36 | .PARAMETER PublishProfile 37 | Identifies the filepath for a Publishprofile to be used. 38 | If used in conjunction with a targetconnectionstring or any of the other target variables the settings in the Publish profile will be overuled as per default function of SQLPackage. 39 | Depending on your setup you will need to use custom credentials next to using a publishing profile. 40 | 41 | .PARAMETER DetectPublishProfile 42 | Switch to use a publishing profile and have the script detect it. Publishing profiles should always be named *.publish.xml in order to be detected. 43 | 44 | .PARAMETER TargetServerName 45 | Identifies the Server to be used for the Target. If $TargetConnectionString is used this parameter will be overruled by the connectionstring. 46 | 47 | .PARAMETER TargetDBName 48 | Identifies the Database Name to be used for the Target. If $TargetConnectionString is used this parameter will be overruled by the connectionstring. 49 | 50 | .PARAMETER TargetUserName 51 | Identifies the Username to be used for the Target. If $TargetConnectionString is used this parameter will be overruled by the connectionstring. 52 | 53 | .PARAMETER TargetPassword 54 | Identifies the password to be used for the Target. If $TargetConnectionString is used this parameter will be overruled by the connectionstring. 55 | 56 | .PARAMETER hidden 57 | Switch to use when output from the command line tool needs to be hidden instead of shown. 58 | 59 | .PARAMETER keeplogfiles 60 | Switch to specify that log files should be deleted. only applies on successfull output. 61 | 62 | 63 | .OUTPUTS 64 | a hashtable with the following details is returned: 65 | 66 | Succeeded = $true if the process Succeeded, $false if the Process failed, and $null if we are not sure. 67 | LogFilePath = The path to the process log file. 68 | Logfile = filename of logfile which was used in the process. 69 | ErrorLogFilePath = The path to the Process error log file. 70 | ErrorLogfile = filename of the errorlogfile. 71 | FiletoProcess = The item that SQLPackage ran against. 72 | CommandUsed = The full command that was used to invoke SQLPackage. This can be useful for inspecting what parameters are passed to SQLPackage.exe. 73 | Message = A message describing any problems that were encoutered by the process. This is typically an empty string unless something went wrong. 74 | Duration = The amount of time the process took to complete, represented as a TimeSpan. 75 | 76 | 77 | .EXAMPLE 78 | 79 | Invoke-MSSQLCICDHelperSQLPackage -filename -TargetconnectionString -logfilepath c:\logs\builds 80 | 81 | Will Run Invoke-MSSQLCICDHelperSQLPackage with the default settings other than filename and logfilepath 82 | Filename = given file 83 | logfiles will be stored in c:\logs\builds\ 84 | Non hidden 85 | Delete logfiles when successfull. 86 | Will use the designated Connection string. 87 | 88 | .EXAMPLE 89 | 90 | Invoke-MSSQLCICDHelperSQLPackage -Publishprofile C:\builds\.publish.xml -TargetServerName -TargetDBName myawesomedb -TargetUsername sa -targetPassword Very_Str0nPa$$W0rd01 91 | 92 | Will Run Invoke-MSSQLCICDHelperSQLPackage with the default settings 93 | Filename = will search for a dacpac from the current directory. 94 | will use the mentioned Publishing profile for non-credential settings 95 | Non hidden 96 | Delete logfiles when successfull. 97 | Will use the mentioned credentials 98 | 99 | .EXAMPLE 100 | 101 | Invoke-MSSQLCICDHelperSQLPackage -DetectPublishProfile 102 | 103 | Will Run Invoke-MSSQLCICDHelperSQLPackage with the default settings 104 | Filename = will search for a dacpac from the current directory. 105 | Publishing profile will be detected from the current directory. 106 | Non hidden 107 | Delete logfiles when successfull. 108 | Credentials are taken from the publishing profile. 109 | 110 | .EXAMPLE 111 | 112 | Invoke-MSSQLCICDHelperSQLPackage -TargetServerName -TargetDBName myawesomedb -TargetUsername sa -targetPassword Very_Str0nPa$$W0rd01 113 | 114 | Will Run Invoke-MSSQLCICDHelperSQLPackage with the default settings 115 | Filename = will search for a dacpac from the current directory. 116 | Non hidden 117 | Delete logfiles when successfull. 118 | Will use the mentioned credentials 119 | 120 | .EXAMPLE 121 | 122 | Invoke-MSSQLCICDHelperSQLPackage -KeepLogfiles -hidden -TargetServerName -TargetDBName myawesomedb -TargetUsername sa -targetPassword Very_Str0nPa$$W0rd01 123 | 124 | Will Run Invoke-MSSQLCICDHelperSQLPackage with the default settings 125 | Filename = will search for a dacpac from the current directory. 126 | hidden 127 | Don't Delete logfiles when successfull. 128 | Will use the mentioned credentials 129 | 130 | .EXAMPLE 131 | 132 | Invoke-MSSQLCICDHelperSQLPackage -Verbose -TargetServerName -TargetDBName myawesomedb -TargetUsername sa -targetPassword Very_Str0nPa$$W0rd01 133 | 134 | Will Run Invoke-MSSQLCICDHelperSQLPackage with the default settings with added verbosity in it's output. 135 | Filename = will search for a dacpac from the current directory. 136 | Not hidden 137 | Delete logfiles when successfull. 138 | Will use the mentioned credentials 139 | 140 | .EXAMPLE 141 | 142 | Invoke-MSSQLCICDHelperSQLPackage -AdditionalArguments "/TargetTimeout:600" -TargetServerName -TargetDBName myawesomedb -TargetUsername sa -targetPassword Very_Str0nPa$$W0rd01 143 | 144 | Will Run Invoke-MSSQLCICDHelperSQLPackage with additional SQLPackage parameters. 145 | See https://msdn.microsoft.com/library/hh550080(vs.103).aspx#Publish%20Parameters,%20Properties,%20and%20SQLCMD%20Variables for valid SQLPackage command-line parameters. 146 | 147 | Please note that the following parameters are already used / reserverd and should not be used: 148 | 149 | /action 150 | /SourceFile 151 | /Profile 152 | /TargetConnectionString 153 | /TargetServerName 154 | /TargetDatabaseName 155 | /TargetUsername 156 | /TargetPassword 157 | 158 | Filename = Autodetect 159 | Non hidden 160 | Delete logfiles when successfull. 161 | 162 | 163 | .LINK 164 | Project home: https://github.com/tsteenbakkers/MSSQL-CICD-Helper 165 | 166 | .NOTES 167 | Name: MSSQLCICDHelper 168 | Author: Tobi Steenbakkers 169 | Version: 1.0.0 170 | #> 171 | 172 | [cmdletbinding()] 173 | param( 174 | [Parameter(Mandatory=$false, 175 | HelpMessage='Filename which should be used for publish. If empty it will find the nearest Solution based on the directory it was invoked from.', 176 | Position=0)] 177 | 178 | [ValidateScript({Test-Path -Path $_ -PathType Leaf})] 179 | $filename, 180 | 181 | [Parameter(Mandatory=$false, 182 | HelpMessage='Provides additional Arguments. Example "/TargetTimeout:600"', 183 | Position=0)] 184 | [Alias("Parameters","Params","P")] 185 | [ValidateNotNullOrEmpty()] 186 | [String] $AdditionalArguments, 187 | 188 | [Parameter(Mandatory=$false, 189 | HelpMessage='Determines the basepath where logfiles should be stored. if empty the directory where the script is running will be used', 190 | Position=0)] 191 | [Alias("lfp")] 192 | [ValidateNotNullOrEmpty()] 193 | [String] $logfilepath, 194 | 195 | [Parameter(Mandatory=$false, 196 | HelpMessage='Determines Target for publishing based on a connectionstring', 197 | Position=0)] 198 | [Alias("tconstr")] 199 | [ValidateNotNullOrEmpty()] 200 | [String] $TargetConnectionString, 201 | 202 | [Parameter(Mandatory=$false, 203 | HelpMessage='input for path + filename for publishing profile to be used.', 204 | Position=0)] 205 | [Alias("pr")] 206 | [ValidateNotNullOrEmpty()] 207 | [String] $PublishProfile, 208 | 209 | [Parameter(Mandatory=$false, 210 | HelpMessage='Use this switch if you want to use a publish profile but have it detected by the software. Will override a given $PublishProfile.', 211 | Position=0)] 212 | [Alias("detectpr")] 213 | [ValidateNotNullOrEmpty()] 214 | [Switch] $DetectPublishProfile, 215 | 216 | 217 | [Parameter(Mandatory=$false, 218 | HelpMessage='Determines Target Server for publishing', 219 | Position=0)] 220 | [Alias("tsn", "TargetServer")] 221 | [ValidateNotNullOrEmpty()] 222 | [String] $TargetServerName, 223 | 224 | 225 | 226 | [Parameter(Mandatory=$false, 227 | HelpMessage='Determines Target Database for publishing', 228 | Position=0)] 229 | [Alias("tdn","TargetDB")] 230 | [ValidateNotNullOrEmpty()] 231 | [String] $TargetDBName, 232 | 233 | 234 | 235 | [Parameter(Mandatory=$false, 236 | HelpMessage='Determines Target Username for publishing', 237 | Position=0)] 238 | [Alias("tu", "TargetUser")] 239 | [ValidateNotNullOrEmpty()] 240 | [String] $TargetUserName, 241 | 242 | 243 | [Parameter(Mandatory=$false, 244 | HelpMessage='Determines Target Password for publishing', 245 | Position=0)] 246 | [Alias("tp", "TargetPass")] 247 | [ValidateNotNullOrEmpty()] 248 | [String] $TargetPassWord, 249 | 250 | [Parameter(Mandatory=$false, 251 | HelpMessage='Switch to only retrieve the outcome of sqlpackage. Hides the SQLPackage.exe on screen.', 252 | Position=0)] 253 | #[Alias("Parameters","Params","P")] 254 | [ValidateNotNullOrEmpty()] 255 | [switch] $hidden, 256 | 257 | [Parameter(Mandatory=$false, 258 | HelpMessage='Switch to keep the log files after checking them. for results.', 259 | Position=0)] 260 | #[Alias("Parameters","Params","P")] 261 | [ValidateNotNullOrEmpty()] 262 | [switch] $keeplogfiles 263 | ) 264 | 265 | $result = @{} 266 | $result.CommandUsed = [string]::Empty 267 | $result.Succeeded = $null 268 | $result.Message = [string]::Empty 269 | $result.Duration = [TimeSpan]::Zero 270 | $result.LogFilePath = $null 271 | $result.LogFile = $null 272 | $result.ErrorLogFilePath = $null 273 | $result.ErrorLogFile = $null 274 | $result.FiletoProcess = $null 275 | 276 | try{ 277 | 278 | 279 | $configfile = ImportConfig 280 | $curdir = Get-location 281 | 282 | if($null -eq $filename){ 283 | 284 | write-verbose "No filename given. Running Get-MSSQLCICDHelperFiletoBuildDeploy based to find the Solution in current script path $curdir" 285 | $filename = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind 'dacpac' -RootPath $curdir | Get-ChildItem 286 | } 287 | else{ 288 | $filename = Get-ChildItem $filename 289 | } 290 | #$filename 291 | write-Verbose "The following file will be built: $($filename.Name) located in path $($filename.DirectoryName)" 292 | 293 | if($DetectPublishProfile){ 294 | write-verbose "No filename given. Running Get-MSSQLCICDHelperFiletoBuildDeploy based to find the Solution in current script path $curdir" 295 | $PublishProfile = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind 'PublishProfile' -RootPath $curdir | Get-ChildItem 296 | } 297 | 298 | if($logfilepath){ 299 | $logfile = "{0}\$($filename.name).SQLPackage.log" -f $logfilepath 300 | $errorlogfile = "{0}\$($filename.name).SQLPackage.errors.log" -f $logfilepath 301 | }else{ 302 | $logfile = "{0}\$($filename.name).SQLPackage.log" -f $curdir 303 | $errorlogfile = "{0}\$($filename.name).SQLPackage.errors.log" -f $curdir 304 | } 305 | $logbase = Split-Path -path $logfile -Parent 306 | $result.LogFilePath = $logbase 307 | $result.LogFile = $logfile 308 | $result.ErrorLogFilePath = $logbase 309 | $result.ErrorLogFile = $errorlogfile 310 | $result.FiletoProcess = $filename.FullName 311 | 312 | 313 | 314 | 315 | Write-Verbose "Constructing Command to execute..." 316 | 317 | $arguments = "/k "" ""$($configfile['SQLPackageExe'])""" 318 | 319 | $arguments += " /a:Publish" 320 | $arguments += " /sf:""$($filename)""" 321 | 322 | if($TargetConnectionString){ 323 | 324 | $arguments += " /tcs:$TargetConnectionString" 325 | 326 | }else{ 327 | if($TargetServerName -and $TargetDBName -and $TargetUserName -and $TargetPassWord){ 328 | $shownarguments = "$arguments /tsn:$($targetservername) /tdn:$($TargetDBName) /tu:$($targetUsername) /tp:******" 329 | $arguments += " /tsn:$($targetservername) /tdn:$($TargetDBName) /tu:$($targetUsername) /tp:$($targetPassword)" 330 | 331 | }else{ 332 | Write-Error "Some of the target Credentials are not filled" 333 | throw; 334 | } 335 | } 336 | 337 | if($PublishProfile){ 338 | $arguments += " /pr:""$($PublishProfile)""" 339 | $shownarguments += " /pr:""$($PublishProfile)""" 340 | } 341 | 342 | if($AdditionalArguments){ 343 | Write-Verbose "The following additional arguments will be used: $AdditionalArguments" 344 | $arguments += " $additionalarguments" 345 | $shownarguments += " $additionalarguments" 346 | } 347 | 348 | #closing arguments with an exit statement to return to powershell 349 | $arguments += " & Exit"" " 350 | $shownarguments += " & Exit"" " 351 | 352 | Write-Verbose "The following Arguments will be used: $shownarguments" 353 | $result.CommandUsed = "cmd.exe $shownarguments" 354 | 355 | $processoutput = Invoke-Cmd -executable 'cmd.exe' -Arguments $arguments -logfile $result.LogFile -errorlogfile $result.ErrorLogFile 356 | # #constructing the process and the arguments to send: 357 | # $pinfo = New-Object System.Diagnostics.ProcessStartInfo 358 | # $pinfo.FileName = "cmd.exe" 359 | # $pinfo.Arguments = $arguments 360 | 361 | # #$pinfo.Passthru = $true 362 | # $pinfo.RedirectStandardError = $true 363 | # $pinfo.RedirectStandardOutput = $true 364 | # $pinfo.UseShellExecute = $false 365 | 366 | # if($debug){ 367 | # $pinfo 368 | # } 369 | # #executing the command and storing the result inside $p: 370 | # $p = New-Object System.Diagnostics.Process 371 | # $p.StartInfo = $pinfo 372 | # $p.Start() | Out-Null 373 | 374 | # $output = $p.StandardOutput.ReadToEnd() 375 | # $erroroutput = $p.StandardError.read() 376 | # $result.Duration = $p.ExitTime - $p.StartTime 377 | # $output | Out-file -literalpath $logfile -Force 378 | # $erroroutput | Out-file -literalpath $errorlogfile -Force 379 | 380 | 381 | }catch{ 382 | $errorMessage = $_ 383 | $result.Message = "Unexpected error occurred while processing ""$Path"": $errorMessage" 384 | $result.Succeeded = $false 385 | Write-Error ($result.Message) 386 | return $result 387 | throw; 388 | } 389 | 390 | Write-verbose "SQLPackage.exe Started. Continue Checking results..." 391 | if(!$hidden){ 392 | "Normal Output: " 393 | $processoutput.output 394 | "Error Output:" 395 | $processoutput.erroroutput 396 | } 397 | 398 | 399 | if(!(Test-Path -Path $result.LogFile)){ 400 | $result.Succeeded = $false 401 | $result.Message = "Could not find file at '$($result.LogFile)' unable to check for correct execution." 402 | 403 | Write-Error "$($result.message)" 404 | return $result 405 | throw; 406 | } 407 | 408 | 409 | [bool] $ProcessReturnedSuccessfulExitCode = $processoutput.ExitCode -eq 0 410 | [bool] $ProcessOutputDoesNotContainFailureMessage = (((Select-String -Path $($result.LogFile) -Pattern "Could not deploy package" -SimpleMatch) -eq $null) -or ((Select-String -Path $($result.LogFile) -Pattern "Initializing deployment (Failed)" -SimpleMatch) -eq $null)) 411 | [bool] $ProcessOutputDoesContainSuccesseMessage = (Select-String -Path $($result.LogFile) -Pattern "Successfully published database." -SimpleMatch -Quiet) -eq $true 412 | 413 | $ProcessSucceeded = $ProcessOutputDoesNotContainFailureMessage -and $ProcessReturnedSuccessfulExitCode -and $ProcessOutputDoesContainSuccesseMessage 414 | 415 | if ($ProcessSucceeded -eq $true){ 416 | 417 | $result.Succeeded = $true 418 | $result.Message = "command executed Successfully" 419 | $result.Duration = $processoutput.Duration 420 | 421 | if (!$keeplogfiles) 422 | { 423 | if (Test-Path $($result.LogFile) -PathType Leaf) { Remove-Item -Path $($result.LogFile) -Force } 424 | if (Test-Path $($result.ErrorLogFile) -PathType Leaf) { Remove-Item -Path $($result.ErrorLogFile) -Force } 425 | 426 | $result.LogFile = 'Deleted' 427 | $result.ErrorLogFile = 'Deleted' 428 | } 429 | 430 | 431 | }else{ 432 | 433 | $result.Succeeded = $false 434 | $result.Message = "Processing ""$($result.FiletoProcess)"" Failed! Please check ""$($result.LogFile)"" " 435 | $result.Duration = $processoutput.Duration 436 | $result 437 | Write-Error "$($result.message)" 438 | throw; 439 | 440 | } 441 | 442 | Write-Verbose "SQLPackage passed. See results below..." 443 | $result 444 | 445 | } 446 | 447 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Public/Configuration/Get-MSSQLCICDHelperConfiguration.ps1: -------------------------------------------------------------------------------- 1 | function Get-MSSQLCICDHelperConfiguration { 2 | <# 3 | .SYNOPSIS 4 | Retrieves the Stored configuration file. 5 | 6 | .DESCRIPTION 7 | Calls the private ImportConfig function and returns its values. if it fails it will return an error message. 8 | 9 | .OUTPUTS 10 | A hashtable with the saved-config file. 11 | 12 | .EXAMPLE 13 | Get-MSSQLCICDHelperConfiguration 14 | 15 | .LINK 16 | Project home: https://github.com/tsteenbakkers/MSSQL-CICD-Helper 17 | 18 | .NOTES 19 | Name: MSSQLCICDHelper 20 | Author: Tobi Steenbakkers 21 | Version: 1.0.0 22 | #> 23 | 24 | Write-Output ' This Function will only display the config file. To assign a variable use ImportConfig' 25 | try{ 26 | $ConfigFile = ImportConfig 27 | 28 | #Write-Output $ConfigFile 29 | return $ConfigFile 30 | }catch{ 31 | 32 | Write-Error "Could not import config. Make sure it exists or save a new config." 33 | throw; 34 | } 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Public/Configuration/Get-MSSQLCICDHelperPaths.ps1: -------------------------------------------------------------------------------- 1 | Function Get-MSSQLCICDHelperPaths { 2 | <# 3 | .SYNOPSIS 4 | Searches the system recursively for the given rootpath for the given type to find. 5 | 6 | .DESCRIPTION 7 | Searches the system recursively for the given rootpath for the given type to find. 8 | The function returns a full filename of the chosen type for aid in Save-MSSQLCICDHelperConfiguration 9 | if multiple occurrences are found for the chosen type it will return all. 10 | 11 | 12 | .PARAMETER typetofind 13 | Determines the kind of file to find. Accepts MSBuild, SQLPackage or Both as inputs 14 | Mandatory 15 | 16 | .PARAMETER rootpath 17 | Specifies the path where the function needs to start looking for the $typetofind 18 | Mandatory 19 | 20 | .OUTPUTS 21 | A filename (full path) to the file the function is supposed to find based on its rootpath and type to find. 22 | 23 | .EXAMPLE 24 | 25 | Get-MSSQLCICDHelperPaths -typetofind MSBuild -rootpath C:\ 26 | 27 | Will Search C:\ for MSBuild.exe. 28 | 29 | .EXAMPLE 30 | 31 | Get-MSSQLCICDHelperPaths -typetofind SQLPackage -rootpath C:\ 32 | 33 | Will Search C:\ for SQLPackage.exe. 34 | .EXAMPLE 35 | 36 | Get-MSSQLCICDHelperPaths -typetofind Both -rootpath C:\ 37 | 38 | Will Search C:\ for MSBuild.exe and SQLPackage.exe. 39 | 40 | .LINK 41 | Project home: https://github.com/tsteenbakkers/MSSQL-CICD-Helper 42 | 43 | .NOTES 44 | Name: MSSQLCICDHelper 45 | Author: Tobi Steenbakkers 46 | Version: 1.0.0 47 | #> 48 | [cmdletbinding()] 49 | [OutputType('mssqlcicd.Setting')] 50 | param( 51 | [Parameter(Mandatory=$true, 52 | HelpMessage='What to find: MSBuild, SQLPackage or Both', 53 | Position=0)] 54 | [ValidateNotNullOrEmpty()] 55 | $typetofind, 56 | 57 | [Parameter(Mandatory=$true, 58 | HelpMessage='Path where search for $typetofind should be started', 59 | Position=0)] 60 | [ValidateNotNullOrEmpty()] 61 | $rootpath 62 | ) 63 | 64 | $exestofind = @() 65 | $results = @() 66 | 67 | switch($typetofind){ 68 | "MSBuild"{ 69 | $exestofind += 'MSBuild.exe' 70 | } 71 | "SQLPackage"{ 72 | $exestofind += 'SQLPackage.exe' 73 | } 74 | "Both"{ 75 | $exestofind += 'MSBuild.exe' 76 | $exestofind += 'SQLPackage.exe' 77 | } 78 | default { 79 | Write-Error "Invalid option given for input param -typetofind. valid options are: MSBuild, SQLPackage or Both" 80 | throw; 81 | } 82 | } 83 | 84 | if(-not(Test-Path $rootpath)){ 85 | 86 | Write-Error "$rootpath was not found." 87 | throw; 88 | } 89 | 90 | Write-verbose "searching for $exestofind in $rootpath" 91 | 92 | 93 | $exestofind | ForEach-Object{ 94 | $results += Get-ChildItem -Path $rootpath -filter $_ -Recurse -ErrorAction SilentlyContinue 95 | } 96 | 97 | if($results.Count -lt 1){ 98 | Write-Error 'No Files found! Please check path and re-run. Exiting' 99 | throw; 100 | }Else{ 101 | Write-verbose 'Found the following full paths for given parameters. Please take note of these and use the desired path in Save-MSSQLCICDHelperConfiguration' 102 | $results.FullName 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /MSSQL-CICD-Helper/Public/Configuration/Save-MSSQLCICDHelperConfiguration.ps1: -------------------------------------------------------------------------------- 1 | Function Save-MSSQLCICDHelperConfiguration { 2 | <# 3 | .SYNOPSIS 4 | Saves the paths to MSBuild and SQLPackage executables for lates usage. 5 | 6 | .DESCRIPTION 7 | Stores the paths for both SQLPackage.Exe and MSBuild.Exe for later use. Both paths are mandatory to store. 8 | The function tests both paths given for existance and stores it in a XML file on APPDATA for windows or Home on Linux. 9 | 10 | 11 | .PARAMETER SQLPackageExePath 12 | Determines the kind of file to find. Accepts MSBuild, SQLPackage or Both as inputs 13 | Mandatory 14 | 15 | .PARAMETER MSBuildExePath 16 | Specifies the path where the function needs to start looking for the $typetofind 17 | Mandatory 18 | 19 | .OUTPUTS 20 | None 21 | 22 | .EXAMPLE 23 | Save-MSSQLCICDHelperConfiguration -SQLPackageExePath c:\SQLPackage.Exe 24 | 25 | Will Store c:\SQLPackage.Exe as the configured path for SQLPackage 26 | 27 | .EXAMPLE 28 | 29 | Save-MSSQLCICDHelperConfiguration -MSBuildExePath c:\MSBuild.Exe 30 | 31 | Will Store c:\SQLPackage.Exe as the configured path for SQLPackage 32 | 33 | .LINK 34 | Project home: https://github.com/tsteenbakkers/MSSQL-CICD-Helper 35 | 36 | .NOTES 37 | Name: MSSQLCICDHelper 38 | Author: Tobi Steenbakkers 39 | Version: 1.0.0 40 | #> 41 | [cmdletbinding()] 42 | param( 43 | [Parameter(Mandatory=$true, 44 | HelpMessage='Path can be with or without including *.exe file. You can run Get-MSSQL-CICD-HelperPaths to find the paths for MSBUild / SQLPackage', 45 | Position=0)] 46 | [ValidateNotNullOrEmpty()] 47 | $SQLPackageExePath, 48 | 49 | [Parameter(Mandatory=$true, 50 | HelpMessage='Path can be with or without including *.exe file. You can run Get-MSSQL-CICD-HelperPaths to find the paths for MSBUild / SQLPackage', 51 | Position=1)] 52 | [ValidateNotNullOrEmpty()] 53 | $MSBuildExePath 54 | 55 | ) 56 | Write-Output 'Config saving procedure started...' 57 | 58 | Write-Verbose 'Starting to append paths when input was w/o *.exe file.' 59 | 60 | Write-Output "debugging SQLPackageExePath $SQLPackageExePath" 61 | 62 | Write-Output "debugging SQLPackageExePath $MSBuildExePath" 63 | 64 | $currentversion = CurrentVersion 65 | 66 | $PATHS = @{ 67 | MSBuild = $MSBuildExePath 68 | SQLPackage = $SQLPackageExePath 69 | } 70 | 71 | $pathstoappend = @() 72 | #Write-Output "modify" 73 | $paths.Keys | ForEach-Object{ 74 | if(-not($paths[$_] -like '*.exe')){ 75 | $pathstoappend += $_ 76 | } 77 | } 78 | 79 | Write-Verbose "The following paths will be appended: $pathstoappend" 80 | 81 | $pathstoappend | ForEach-Object{ 82 | $paths[$_] = "$($paths[$_].trimend('\'))\$($_).exe" 83 | } 84 | 85 | Write-Verbose 'Testing if either appended paths exists with Get-ChildItem.' 86 | 87 | # testing if either path exists 88 | $paths.Keys | ForEach-Object { 89 | if(-not(Test-Path ($paths[$_]) ) ) { 90 | Write-Error "Directory: $($paths[$_]) did not contain either Msbuild or SqlPackage. Please rerun Save-MSSQLCICDHelperConfiguration with a correct path" 91 | 92 | throw; 93 | } 94 | } 95 | 96 | Write-Verbose 'Finalized paths are:' 97 | Write-Verbose "$paths" 98 | Write-Verbose 'Starting to save config' 99 | 100 | if ( $IsWindows -or ( [version]$PSVersionTable.PSVersion -lt [version]"5.99.0" ) ) { 101 | 102 | # $Parameters = @{ 103 | # Token=(ConvertTo-SecureString -string $Token -AsPlainText -Force) 104 | # Domain=$Domain; 105 | # APIVersion=$APIVersion; 106 | # } 107 | 108 | $Parameters = @{ 109 | MSBuildExe = $PATHS['MSBuild']; 110 | SQLPackageExe = $PATHS['SQLPackage']; 111 | Version = $currentversion 112 | } 113 | 114 | $ConfigFile = "$env:appdata\MSSQLCICDHelper\MSSQLCICDHelperConfiguration.xml" 115 | 116 | } elseif ( $IsLinux ) { 117 | 118 | 119 | $Parameters = @{ 120 | MSBuildExe = $PATHS['MSBuild']; 121 | SQLPackageExe = $PATHS['SQLPackage']; 122 | Version = $currentversion 123 | } 124 | 125 | $ConfigFile = "{0}/.MSSQLCICDHelper/MSSQLCICDHelperConfiguration.xml" -f $HOME 126 | 127 | } else { 128 | Write-Error "Unknown Platform" 129 | throw; 130 | } 131 | 132 | Write-Verbose 'Testing config path.' 133 | if (-not (Test-Path (Split-Path $ConfigFile))) { 134 | New-Item -ItemType Directory -Path (Split-Path $ConfigFile) | Out-Null 135 | 136 | }else{ 137 | Write-Verbose "Path $ConfigFile found. Overwriting existing file." 138 | } 139 | 140 | $Parameters | Export-Clixml -Path $ConfigFile 141 | Write-Output "Configuration saved in $ConfigFile" 142 | Remove-Variable Parameters 143 | } 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CICD Logo](/Documentation/Images/CICD.png "CICD Logo") 2 | ![SQL Logo](/Documentation/Images/sql.png "SQL Logo") 3 | 4 | # MSSQL-CICD-Helper 5 | 6 | [![release badge]][release] 7 | [![licence badge]][licence] 8 | [![stars badge]][stars] 9 | [![forks badge]][forks] 10 | [![issues badge]][issues] 11 | 12 | ## Dev Build 13 | 14 | [![devbuild badge]][devbuild] 15 | 16 | ## Master Build 17 | 18 | [![masterbuild badge]][masterbuild] 19 | 20 | [licence badge]:https://img.shields.io/badge/license-MIT-blue.svg 21 | [stars badge]:https://img.shields.io/github/stars/tsteenbakkers/MSSQL-CICD-Helper.svg 22 | [forks badge]:https://img.shields.io/github/forks/tsteenbakkers/MSSQL-CICD-Helper.svg 23 | [issues badge]:https://img.shields.io/github/issues/tsteenbakkers/MSSQL-CICD-Helper.svg 24 | [release badge]:https://img.shields.io/github/release/tsteenbakkers/MSSQL-CICD-Helper.svg 25 | 26 | [devbuild badge]:https://ci.appveyor.com/api/projects/status/l7vbq4q6x86rgmtd?svg=true&branch=DEV 27 | [masterbuild badge]:https://ci.appveyor.com/api/projects/status/l7vbq4q6x86rgmtd?svg=true&branch=master 28 | 29 | [licence]:https://github.com/tsteenbakkers/MSSQL-CICD-Helper/blob/master/LICENSE.md 30 | [stars]:https://github.com/tsteenbakkers/MSSQL-CICD-Helper/stargazers 31 | [forks]:https://github.com/tsteenbakkers/MSSQL-CICD-Helper/network 32 | [issues]:https://github.com/tsteenbakkers/MSSQL-CICD-Helper/issues 33 | [release]:https://github.com/tsteenbakkers/MSSQL-CICD-Helper/releases 34 | [devbuild]:https://ci.appveyor.com/api/projects/status/l7vbq4q6x86rgmtd?branch=DEV 35 | [masterbuild]:https://ci.appveyor.com/api/projects/status/l7vbq4q6x86rgmtd?branch=master 36 | 37 | - [Introduction](#introduction) 38 | - [Support / Contribution](#support--contribution) 39 | - [License / Warranty](#license--warranty) 40 | - [Installation](#installation) 41 | - [Configuration](#configuration) 42 | - [Functions](#functions) 43 | - [Example CI Scripts](#example-ci-scripts) 44 | 45 | ---- 46 | 47 | # Introduction 48 | 49 | This repo contains a powershell module which helps and aids in CI / CD processes specifically for MSSQL (Microsoft SQL Server). 50 | The module was born because not every CI / CD tool supports the quirks often presented when trying to implement CI / CD in combination with SQL Server Projects. 51 | 52 | The main issue is that most current CI systems do not help in discovery of files to build / deploy which makes it difficult to automate building / deploy processes because your pipeline code needs to be customized for each Solution / Database which you want running in your pipeline. 53 | This is most notable when first starting to work with CI systems and having to adjust your pipeline code for each database which is not according to the philosophy of make once, use many. It clutters your CI system and makes it less manageble. 54 | Also this is notable when switching from one CI system to another. Something which I often have to do at customers. 55 | 56 | As such I decided to make a powershell module / wrapper which is versatile and exchangable when you want to work with CI systems and MSSQL databases. 57 | 58 | MSSQL-CICD-Helper helps you automate further by not worrying how your SQL Solution is configured. Something i found which would often differ in each project / solution (definitely not a best practice ^^). 59 | 60 | ## Features 61 | 62 | - Find any \*.sln, \*.sqlproject, \*.dacpac, \*.publish.XML or \*.dtspac on a runner / container based on the pulled sourcecode (so no need to worry about hard-coded paths) 63 | - run MSBuild for SQLProjects / SLN files (with above mentioned auto-discovery) 64 | - supports SSDT, SSIS, SSAS, SSRS solutions (\*.sln files) 65 | - Supports SSDT Projects (\*.sqlproj files) 66 | - Call either built-in MSBuild function or use [Invoke-MSBuild](https://github.com/deadlydog/Invoke-MsBuild) 67 | - Support for adding custom arguments to MSBuild / Invoke-MSBuild 68 | - Deploy / Publish DacPac files (with above mentioned discovery) 69 | - Support for connectionstrings 70 | - Support for publishing profiles 71 | - Support for custom credentials 72 | - Support for adding any custom arguments to SQLPackage.exe 73 | - Discover and save MsBuild.exe / SQLPackage.exe on runner system 74 | 75 | ## Upcoming Features 76 | 77 | - Add example kicker script 78 | - Add example CI / Pipeline scripts 79 | - Gitlab 80 | - Jenkins 81 | - TeamCity 82 | - VSTS/TFS 83 | - Add ability to use Windows Credentials instead of SQL Authentication. 84 | - Enable deploying SSIS packages 85 | - Enable deploying SSAS packages 86 | - Enable deploying SSRS packages 87 | - Enable deploying Azure Data Factory code 88 | - Test Automation (such as [T-SQLT](http://tsqlt.org/)) 89 | - Support for Azure SQL Database / Datawarehouse (it does support Azure VMs with SQL installed on it) 90 | - Maintaining / exporting dacpac prior to deploy 91 | - Support for saving environments for deploying (which you should do in your CI system if possible) 92 | - Code improvement / refactoring / cleaning up 93 | 94 | ## Supported CI Systems 95 | 96 | The following CI systems were tested and are supported: 97 | 98 | - [Jenkins](https://jenkins.io/) 99 | - [Gitlab CI](https://about.gitlab.com/) 100 | - [Teamcity](https://www.jetbrains.com/teamcity/) 101 | - [TFS / VSTS](https://www.visualstudio.com/team-services/) 102 | 103 | Please [let me know](MSSQL-CICD-Helper@protonmail.com) if you have this module in place in another CI system so I can add it to the list! 104 | 105 | [⬆ back to top](#mssql-cicd-helper) 106 | 107 | ---- 108 | 109 | # Support / Contribution 110 | 111 | If you want features added or find an issue please let me know by raising an issue on github. You are welcome to contribute if you have additional features or want to refactor. Pull requests are welcome! 112 | 113 | By no means am I an experienced Powershell programmer so please point me towards good code convention if you feel my code lacks in some kind. I am eager to learn to improve my code. 114 | 115 | Also i'd like some help in automated Powershell testing (pester etc.). So if you can and want to help please let me know! 116 | 117 | You can always contact me in regards of this repo on MSSQL-CICD-Helper@protonmail.com 118 | 119 | [⬆ back to top](#mssql-cicd-helper) 120 | 121 | # License / Warranty 122 | 123 | This project is released under the [MIT License](https://github.com/tsteenbakkers/MSSQL-CICD-Helper/blob/master/LICENSE) 124 | 125 | CODE HERE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 126 | 127 | [⬆ back to top](#mssql-cicd-helper) 128 | 129 | ---- 130 | 131 | # Installation 132 | 133 | ## Prerequisites 134 | 135 | In order for this module to work, your SQL Data products must be maintained / developed in Visual Studio (previously known as SSDT / SQL Server Data Tools). DDL definitions must be defined in a solution (\*.sln) containing one or more Projects (\*.sqlproj) files. 136 | 137 | Obviously for best results a true CI system as mentioned aboved should be used alongside this module. However the module can be used on its own for starting out companies. Depending on your architecture a seperate packaging / deployment tool (like [Octopus Deploy](https://octopus.com/)) is advised. 138 | 139 | ## Download and install 140 | 141 | Either download this repo and save it with your source code (can be in or outside your solution) or make a (forked) git clone at runtime within your pipeline (especially needed when running in docker containers). 142 | 143 | A kicker script is recommended for orchestrating your pipeline (Not yet added, if you need help with this [contact](MSSQL-CICD-Helper@protonmail.com) me.) 144 | 145 | ### Downloading / Cloning the module 146 | 147 | ```git 148 | $ git clone https://github.com/tsteenbakkers/MSSQL-CICD-Helper.git 149 | ``` 150 | or download / clone a specific release from [Releases page](https://github.com/tsteenbakkers/MSSQL-CICD-Helper/releases) 151 | 152 | ```git 153 | $ git clone https://github.com/tsteenbakkers/MSSQL-CICD-Helper.git --branch v1.0.0 154 | ``` 155 | 156 | ### Importing the module 157 | 158 | After cloning (or if you store it with your database code) you need to import this module in order to make the functions available: 159 | 160 | ```Powershell 161 | Import-Module \MSSQL-CICD-Helper\MSSQL-CICD-Helper\MSSQL-CICD-Helper.psd1 162 | ``` 163 | 164 | If you add a -verbose switch it will also display all the functions exported 165 | 166 | Be advised that if you use a CI system or Docker that you need to clone / import at each seperate build (this is why you want a kicker script :) ). 167 | 168 | [⬆ back to top](#mssql-cicd-helper) 169 | 170 | ---- 171 | 172 | # Configuration 173 | 174 | MSSQL-CICD-Helper uses a config file which does not exist when you first install / import the module. 175 | 176 | In order to use the functions we need to generate and save a config file. 177 | 178 | If you use [Save-MSSQLCICDHelperConfiguration](#save-mssqlcicdhelperconfiguration) it will let you store the filepath to SQLPackage.exe and MSBuild.exe for later use. 179 | 180 | example: 181 | 182 | ```Powershell 183 | Save-MSSQLCICDHelperConfiguration -SQLPackageExePath c:\SQLPackage.Exe -MSBuildExePath c:\MSBuild.exe 184 | ``` 185 | 186 | This will store `c:\MSBuild.exe` for calling *MSBuild* and `C:\SQLPackage.exe` for calling *SQLPackage*. The configuration is stored in an PSXML file in the local users *AppData* folder called `MSSQLCICDHelperConfiguration.xml`. 187 | 188 | ```windows 189 | Example: 190 | 191 | C:\Users\\AppData\Roaming\MSSQLCICDHelper\MSSQLCICDHelperConfiguration.xml 192 | ``` 193 | 194 | You don't need to store both executables if you only want to partial functionality but it is advised to store them both. After you've saved the configuration you are set to go. 195 | 196 | If you are unsure where either MSBuild / SQLPackage is located on your system (or on the runners system) you can use [Get-MSSQLCICDHelperPaths](#get-mssqlcicdhelperpaths). 197 | 198 | To review your saved config file use [Get-MSSQLCICDHelperConfiguration](#get-mssqlcicdhelperconfiguration). You can also use this function to discover the path where MSSQL-CICD-Helper stores its config so you know where to inject a pre-made config file. 199 | 200 | *Note: when using docker (or any non persistant tooling) you need to inject your config file after you generate it with [Save-MSSQLCICDHelperConfiguration](#save-mssqlcicdhelperconfiguration). This is not covered by this readme but i am willing to help. just [contact](MSSQL-CICD-Helper@protonmail.com) me!* 201 | 202 | [⬆ back to top](#mssql-cicd-helper) 203 | 204 | ---- 205 | 206 | # Functions 207 | 208 | ## Configuration related functions 209 | 210 | - [Save-MSSQLCICDHelperConfiguration](#save-mssqlcicdhelperconfiguration) 211 | - [Get-MSSQLCICDHelperConfiguration](#get-mssqlcicdhelperconfiguration) 212 | - [Get-MSSQLCICDHelperPaths](#get-mssqlcicdhelperpaths) 213 | 214 | ## Build / Deploy related functions 215 | 216 | - [Get-MSSQLCICDHelperFiletoBuildDeploy](#get-mssqlcicdhelperfiletobuilddeploy) 217 | - [Invoke-MSSQLCICDHelperMSBuild](#invoke-mssqlcicdhelpermsbuild) 218 | - [Invoke-MSSQLCICDHelperSQLPackage](#invoke-mssqlcicdhelpersqlpackage) 219 | 220 | [⬆ back to top](#mssql-cicd-helper) 221 | 222 | ---- 223 | 224 | ## Save-MSSQLCICDHelperConfiguration 225 | 226 | #### Parameters 227 | 228 | *-SQLPackagePath (String) - Mandatory: False* 229 | 230 | `Usage: -SQLPackagePath C:\yourpath\SqlPackage.exe` 231 | 232 | *-MSBuildPath (String) - Mandatory: False* 233 | 234 | `Usage: -MSBuildPath C:\yourpath\MSBuild.exe` 235 | 236 | #### Usage 237 | 238 | ```Powershell 239 | Save-MSSQLCICDHelperConfiguration -MSBuildPath .msbuild.exe -SQLPackagePath SqlPackage.exe 240 | ``` 241 | 242 | #### Examples 243 | 244 | Save both MSBuild and SQLPackage Paths: 245 | 246 | ```Powershell 247 | Save-MSSQLCICDHelperConfiguration -MSBuildPath C:\yourpath\MSBuild.exe -SQLPackagePath C:\yourpath\SqlPackage.exe 248 | ``` 249 | 250 | Save just MSBuild path: 251 | 252 | ```Powershell 253 | Save-MSSQLCICDHelperConfiguration -MSBuildPath C:\yourpath\MSBuild.exe 254 | ``` 255 | 256 | [⬆ back to function overview](#functions) 257 | 258 | ---- 259 | 260 | ## Get-MSSQLCICDHelperConfiguration 261 | 262 | #### Parameters 263 | 264 | None 265 | 266 | #### Usage 267 | 268 | ```Powershell 269 | Get-MSSQLCICDHelperConfiguration 270 | ``` 271 | 272 | #### Examples 273 | 274 | Return saved configuration: 275 | 276 | ```Powershell 277 | Get-MSSQLCICDHelperConfiguration 278 | ``` 279 | [⬆ back to function overview](#functions) 280 | 281 | ---- 282 | 283 | ## Get-MSSQLCICDHelperPaths 284 | 285 | #### Parameters 286 | 287 | *-typetofind (String) - Mandatory: True* 288 | 289 | `Usage: -typetofind MSBuild` 290 | 291 | Values Allowed: 292 | 293 | - MSBuild -- searches for MSBuild.exe from -rootpath 294 | - SQLPackage -- searches for SQLPackage.exe from -rootpath 295 | - Both -- searches for MSBuild.exe & SQLPackage.exe from -rootpath 296 | 297 | *-rootpath (String) - Mandatory: True* 298 | 299 | `Usage: -SQLPackagePath C:\yourpath\` 300 | 301 | #### Usage 302 | 303 | ```Powershell 304 | Get-MSSQLCICDHelperPaths -typetofind -rootpath 305 | ``` 306 | 307 | #### Examples 308 | 309 | Search for both MSBuild and SQLPackage from c:\users 310 | ```Powershell 311 | Get-MSSQLCICDHelperPaths -typetofind Both -rootpath c:\users 312 | ``` 313 | [⬆ back to function overview](#functions) 314 | 315 | ---- 316 | 317 | ## Get-MSSQLCICDHelperFiletoBuildDeploy 318 | 319 | #### Parameters 320 | 321 | *-typetofind (String) - Mandatory: True* 322 | 323 | `Usage: -typetofind MSBuild` 324 | 325 | Values Allowed: 326 | 327 | - Solution -- searches for a *.sln file 328 | - Project -- searches for a *.sqlproj file 329 | - DacPac -- searches for a *.dacpac file 330 | - PublishProfile -- searches for a *.publish.xml file 331 | - DTSPac -- searches for a *.dtspac file 332 | 333 | *-rootpath (String) - Mandatory: True* 334 | 335 | `Usage: -SQLPackagePath C:\yourpath\` 336 | 337 | #### Usage 338 | 339 | ```Powershell 340 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind -rootpath 341 | ``` 342 | 343 | #### Examples 344 | 345 | Search for both MSBuild and SQLPackage from c:\users 346 | ```Powershell 347 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Both -rootpath c:\users 348 | ``` 349 | [⬆ back to function overview](#functions) 350 | 351 | ---- 352 | 353 | ## Invoke-MSSQLCICDHelperMSBuild 354 | 355 | #### Parameters 356 | 357 | *-filename (String) - Mandatory: False* 358 | 359 | `Usage: -filename c:\\filetobuild.sln` 360 | 361 | if left blank the module will find the closest \*.sln file from where the script is ran. 362 | 363 | *-MSBuildArguments (String) - Mandatory: False* 364 | 365 | `Usage: -MSBuildArguments '/action:' ` 366 | 367 | Values **NOT** allowed: 368 | 369 | - /fl 370 | - /flp 371 | 372 | U are welcome to create additional arguments. When using the [Invoke-MSBuild](https://github.com/deadlydog/Invoke-MsBuild) they will be passed to the -MsBuildParameters Parameter 373 | 374 | See http://msdn.microsoft.com/en-ca/library/vstudio/ms164311.aspx for valid MsBuild command-line parameters. 375 | 376 | *-hidden (Switch) - Mandatory: False* 377 | 378 | `Usage: -hidden` 379 | 380 | if this switch is used the output from MSBuild will be hidden from the screen and only outcome will be presented. 381 | 382 | *-Keeplogfiles (Switch) - Mandatory: False* 383 | 384 | `Usage: -Keeplogfiles` 385 | 386 | if this switch is used the logfiles (including errorlog files will not be deleted when the outcome is successfull.) 387 | 388 | *-UseInvokeMSBuildModule (Switch) - Mandatory: False* 389 | 390 | `Usage: -UseInvokeMSBuildModule` 391 | 392 | if this switch is used the module will test for availability of the [Invoke-MSBuild](https://github.com/deadlydog/Invoke-MsBuild) module and it use it instead to build the chosen filename. 393 | 394 | *-InvokeMSBuildParameters (String) - Mandatory: False* 395 | 396 | `Usage: -InvokeMSBuildParameters ` 397 | 398 | Input for passing additional Arguments to [Invoke-MSBuild](https://github.com/deadlydog/Invoke-MsBuild). Must be used in conjunction with -UseInvokeMSBuildModule 399 | 400 | The following values are **NOT** allowed since we pass them automatically already: 401 | 402 | - -Path (use -filename / auto discovery instead) 403 | - -LogDirectory (automatically passed) 404 | - -KeepBuildLogOnSuccessfulBuilds (use -KeepLogfiles instead) 405 | - -MsBuildParameters (use -MSBuildArguments instead) 406 | 407 | #### Usage 408 | 409 | ```Powershell 410 | Invoke-MSSQLCICDHelperMSBuild -keeplogfiles 411 | # or assign the output to a variable for later use 412 | $result = Invoke-MSSQLCICDHelperMSBuild -keeplogfiles -hidden 413 | ``` 414 | 415 | #### Examples 416 | 417 | Auto-discover Solution file and keep logfiles afterwards: 418 | 419 | ```Powershell 420 | Invoke-MSSQLCICDHelperMSBuild -keeplogfiles 421 | ``` 422 | 423 | Build a specific project file and only show output: 424 | 425 | ```Powershell 426 | Invoke-MSSQLCICDHelperMSBuild -filename c:\builds\32egeudsd\myawesomedatabase.sqlproj -hidden 427 | ``` 428 | 429 | Use [Invoke-MSBuild](https://github.com/deadlydog/Invoke-MsBuild) with autodiscovery and no additional arguments: 430 | 431 | ```Powershell 432 | Invoke-MSSQLCICDHelperMSBuild -UseInvokeMSBuildModule 433 | ``` 434 | 435 | Use [Invoke-MSBuild](https://github.com/deadlydog/Invoke-MsBuild) with autodiscovery and additional arguments: 436 | 437 | ```Powershell 438 | Invoke-MSSQLCICDHelperMSBuild -UseInvokeMSBuildModule -InvokeMSBuildParameters '-ShowBuildOutputInNewWindow -PromptForInputBeforeClosing -AutoLaunchBuildLogOnFailure' 439 | ``` 440 | [⬆ back to function overview](#functions) 441 | 442 | ---- 443 | 444 | ## Invoke-MSSQLCICDHelperSQLPackage 445 | 446 | #### Parameters 447 | 448 | *-filename (String) - Mandatory: False* 449 | 450 | `Usage: -filename c:\\filetodeploy.dacpac` 451 | 452 | if left blank the module will find the closest \*.dacpac file from where the script is ran. 453 | 454 | *-AditionalArguments (String) - Mandatory: False* 455 | 456 | `Usage: -AditionalArguments '/tec:True' ` 457 | 458 | Values **NOT** allowed: 459 | 460 | - /action 461 | - /SourceFile 462 | - /Profile 463 | - /TargetConnectionString 464 | - /TargetServerName 465 | - /TargetDatabaseName 466 | - /TargetUsername 467 | - /TargetPassword 468 | 469 | Additional parameters to pass to the SQLPackage command-line tool. This can be any valid sqlpackage command-line parameter(s) except for the ones mentioned above. 470 | 471 | See https://msdn.microsoft.com/library/hh550080(vs.103).aspx#Publish%20Parameters,%20Properties,%20and%20SQLCMD%20Variables for valid SQLPackage command-line parameters. 472 | 473 | *-logfilepath (String) - Mandatory: False* 474 | 475 | `Usage: -logfilepath c:\` 476 | 477 | If left blank the directory will be used where the script is being executed 478 | 479 | *-TargetConnectionString (String) - Mandatory: False* 480 | 481 | `Usage: -TargetConnectionString '' ` 482 | 483 | Specifies a connection string to use. If you use a connection string you can not also use the following parameters: 484 | 485 | - -TargetServerName 486 | - -TargetDBName 487 | - -TargetUsername 488 | - -TargetPassword 489 | 490 | *-PublishProfile (String) - Mandatory: False* 491 | 492 | `Usage: -PublishProfile c:\.publish.xml` 493 | 494 | Specifies a SQL Publishing profile to be used. if this parameter is used it will override results from `-DetectPublishProfile` 495 | 496 | *-DetectPublishProfile (Switch) - Mandatory: False* 497 | 498 | `Usage: -DetectPublishProfile` 499 | 500 | Triggers the Module to go and find the closest \*.publish.xml file and will use its options. Can be used in cojunction with the following parameters: 501 | 502 | - -TargetServerName 503 | - -TargetDBName 504 | - -TargetUsername 505 | - -TargetPassword 506 | 507 | If you do they will override any settings regarding the target inside the publishing profile. 508 | 509 | *-TargetServerName (String) - Mandatory: False* 510 | 511 | `Usage: -TargetServerName '' ` 512 | 513 | Specifies a Target server to which the SQL Database needs to be published. Can be local or Azure (just make sure you set up your firewall / NAT correctly when using outside connections) 514 | 515 | *-TargetDBName (String) - Mandatory: False* 516 | 517 | `Usage: -TargetDBName '' ` 518 | 519 | Specifies a Target Database to which the SQL Database needs to be published. 520 | 521 | *-TargetUserName (String) - Mandatory: False* 522 | 523 | `Usage: -TargetUserName '' ` 524 | 525 | Specifies a Target username (SQL Authentication) to which the SQL Database needs to be published. 526 | 527 | *-TargetServerName (String) - Mandatory: False* 528 | 529 | `Usage: -TargetPassword '' ` 530 | 531 | Specifies the password for `-TargetUsername` 532 | 533 | *-hidden (Switch) - Mandatory: False* 534 | 535 | `Usage: -hidden` 536 | 537 | if this switch is used the output from SQLPackage will be hidden from the screen and only outcome will be presented. 538 | 539 | *-Keeplogfiles (Switch) - Mandatory: False* 540 | 541 | `Usage: -Keeplogfiles` 542 | 543 | if this switch is used the logfiles (including errorlog files will not be deleted when the outcome is successfull.) 544 | 545 | #### Usage 546 | 547 | ```Powershell 548 | Invoke-MSSQLCICDHelperSQLPackage -keeplogfiles 549 | # or assign the output to a variable for later use 550 | $result = Invoke-MSSQLCICDHelperSQLPackage -keeplogfiles -hidden 551 | ``` 552 | 553 | #### Examples 554 | 555 | Auto discover dacpac and use Publishing Profile for actual deployment. keep logfiles when deploy is successful: 556 | 557 | ```Powershell 558 | Invoke-MSSQLCICDHelperSQLPackage -keeplogfiles -Detectpublishprofile 559 | ``` 560 | 561 | Manually assign a dacpac file. keep logfiles when deploy is successful. Hide the output from screen: 562 | 563 | ```Powershell 564 | Invoke-MSSQLCICDHelperSQLPackage -keeplogfiles -hidden -filename c:\builds\987wd9d93\myawesomedb.dacpac 565 | ``` 566 | 567 | Auto discover dacpac and use manual credentials for deployment. Delete log files after deploy is successful: 568 | 569 | ```Powershell 570 | Invoke-MSSQLCICDHelperSQLPackage -TargetServername myazure.northeurope.cloudapp.azure.com -TargetDBName myawesomedb -TargetUsername DeployServiceAccount -Targetpassword My_Sup3rStr0nPW! 571 | ``` 572 | 573 | Auto discover dacpac and, use publishingprofile for options and manual credentials for deployment. Delete log files after deploy is successful: 574 | 575 | ```Powershell 576 | Invoke-MSSQLCICDHelperSQLPackage -PublishProfile C:\builds\828dds3\myawesomedb.publish.xml -TargetServername myazure.northeurope.cloudapp.azure.com -TargetDBName myawesomedb -TargetUsername DeployServiceAccount -Targetpassword My_Sup3rStr0nPW! 577 | ``` 578 | 579 | Auto discover dacpac and use manual credentials for deployment. Delete log files after deploy is successful and specify additional arguments (Timeout = 600 seconds) for SQLPackage: 580 | 581 | ```Powershell 582 | Invoke-MSSQLCICDHelperSQLPackage -AdditionalArguments '/TargetTimeout:600' -TargetServername myazure.northeurope.cloudapp.azure.com -TargetDBName myawesomedb -TargetUsername DeployServiceAccount -Targetpassword My_Sup3rStr0nPW! 583 | ``` 584 | 585 | [⬆ back to function overview](#functions) 586 | 587 | # Example CI Scripts 588 | 589 | ### Work in Progress!!! 590 | 591 | In the [Example_Scripts](https://github.com/tsteenbakkers/MSSQL-CICD-Helper/tree/master/Example_Scripts) folder you will find the general CICD_Kicker.ps1 script and some pipeline scripts for various CI systems. all CI folders will have a Non-Docker and a Docker variant depending on your setupt 592 | 593 | #### CICD-Kicker.ps1 594 | 595 | This script will do the following: 596 | 597 | - test for local availability of the MSSQL-CICD-Helper module. 598 | - If it is not available it will test for a git installation and pull the MSSQL-CICD-Helper module 599 | - Import the MSSQL-CICD-Helper module 600 | - Depending on function start building / deploying your Database project. 601 | 602 | Since it is an example script it is fairly simple and will accept two functions: 603 | 604 | - build (discover a local \*.sln file and MSBuild it with -verbose -Keeplogfile) 605 | - deploy (discover a local \*.dacpac file and deploy it with -verbose -Keeplogfile & passed input parameters as target connection.) 606 | 607 | Adjust to your situation! 608 | 609 | #### Gitlab CI 610 | 611 | provided is a non-docker script which has 2 stages: 612 | 613 | - build 614 | - deploy 615 | 616 | Both stages will call the CICD-Kicker script with aforementioned functions and passed input variables from your CI configuration. 617 | 618 | [⬆ back to top](#mssql-cicd-helper) -------------------------------------------------------------------------------- /Tests/MSSQL-CICD-Helper.Module.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ModuleName = Split-Path (Resolve-Path "$PSScriptRoot\..\" ) -Leaf 2 | $ModuleManifest = Resolve-Path "$PSScriptRoot\..\$ModuleName\$ModuleName.psd1" 3 | 4 | #Get-Module $ModuleName | Remove-Module 5 | 6 | #Import-Module $ModuleManifest 7 | 8 | Describe 'Module Information' { 9 | 10 | $ModuleManifest = "$PSScriptRoot\..\MSSQL-CICD-Helper\MSSQL-CICD-Helper.psd1" 11 | 12 | Context 'Module Manifest' { 13 | $Script:Manifest = $null 14 | It 'Valid Manifest File' { 15 | { 16 | $Script:Manifest = Test-ModuleManifest -Path $ModuleManifest -ErrorAction Stop -WarningAction SilentlyContinue 17 | } | Should Not Throw 18 | } 19 | 20 | It 'Valid Manifest Root Module' { 21 | $Script:Manifest.RootModule | Should Be 'MSSQL-CICD-Helper.psm1' 22 | } 23 | 24 | It 'Valid Manifest Name' { 25 | $Script:Manifest.Name | Should be MSSQL-CICD-Helper 26 | } 27 | 28 | It 'Valid Manifest GUID' { 29 | $Script:Manifest.Guid | SHould be '2287837f-86ec-43ef-97a8-fee9f33a7c33' 30 | } 31 | 32 | It 'Valid Author' { 33 | $Script:Manifest.Author | SHould be 'Tobi Steenbakkers' 34 | } 35 | 36 | It 'Valid Manifest Version' { 37 | $Script:Manifest.Version -as [Version] | Should Not BeNullOrEmpty 38 | } 39 | 40 | It 'Valid Manifest Description' { 41 | $Script:Manifest.Description | Should Not BeNullOrEmpty 42 | } 43 | 44 | It 'Required Modules' { 45 | $Script:Manifest.RequiredModules | Should BeNullOrEmpty 46 | } 47 | 48 | It 'Non blank description' { 49 | $Script:Manifest.Description | Should not Benullorempty 50 | } 51 | 52 | } 53 | } 54 | 55 | #Remove-Module $ModuleName 56 | -------------------------------------------------------------------------------- /Tests/PSAnalyzer.Tests.ps1: -------------------------------------------------------------------------------- 1 | $projectRoot = Resolve-Path "$PSScriptRoot\.." 2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psd1") 3 | $moduleName = Split-Path $moduleRoot -Leaf 4 | 5 | Describe "PSScriptAnalyzer rule-sets" -Tag Build { 6 | 7 | $Rules = Get-ScriptAnalyzerRule 8 | $excludedRules = ( 9 | 'PSAvoidUsingConvertToSecureStringWithPlainText', # For private token information 10 | 'PSAvoidUsingUserNameAndPassWordParams' # this refers to gitlab users and passwords 11 | ) 12 | $scripts = Get-ChildItem $moduleRoot -Include *.ps1, *.psm1, *.psd1 -Recurse | where fullname -notmatch 'classes' 13 | 14 | foreach ( $Script in $scripts ) 15 | { 16 | Context "Script '$($script.FullName)'" { 17 | $results = Invoke-ScriptAnalyzer -Path $script.FullName -includeRule $Rules -Severity Error -ExcludeRule $excludedRules -Verbose:$false 18 | if ($results) 19 | { 20 | foreach ($rule in $results) 21 | { 22 | It $rule.RuleName { 23 | $message = "{0} Line {1}: {2}" -f $rule.Severity, $rule.Line, $rule.message 24 | $message | Should Be "" 25 | } 26 | 27 | } 28 | } 29 | else 30 | { 31 | It "Should not fail any rules" { 32 | $results | Should BeNullOrEmpty 33 | } 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Tests/Unit.Tests.ps1: -------------------------------------------------------------------------------- 1 | $projectRoot = Resolve-Path "$PSScriptRoot\.." 2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psd1") 3 | $moduleName = Split-Path $moduleRoot -Leaf 4 | $testRoot = Resolve-Path "$projectRoot\Tests" 5 | 6 | #Import-Module (Join-Path $moduleRoot "$moduleName.psm1") -force 7 | InModuleScope MSSQL-CICD-Helper { 8 | 9 | Describe "CurrentVersion" -Tags Build { 10 | $returnedversion = 'V1.0.0' 11 | 12 | Mock CurrentVersion {$returnedversion} 13 | 14 | It "Saves the configuration with expected values"{ 15 | # Act 16 | $version = currentversion 17 | 18 | # Assert 19 | $version | should be 'v1.0.0' 20 | Assert-MockCalled currentversion -Exactly 1 -Scope It 21 | } 22 | } 23 | 24 | Describe 'Save-MSSQLCICDHelperConfiguration' -Tags Build { 25 | 26 | # Prepare 27 | $FilePath = "$TestDrive\PesterTest.xml" 28 | 29 | $ExportCLIXML = Get-Command Export-Clixml 30 | $currentversion = currentversion 31 | 32 | Mock Export-Clixml { 33 | & $ExportCLIXML -InputObject $InputObject -Path $FilePath 34 | } 35 | 36 | Mock Test-Path {$true} 37 | 38 | It "Saves the configuration with expected values"{ 39 | # Act 40 | Save-MSSQLCICDHelperConfiguration -SQLPackageExePath 'C:\pestertest\SQLPackage.exe' -MSBuildExePath 'C:\pestertest\MSBuild.exe' -erroraction stop 41 | 42 | # Assert 43 | $results = Import-Clixml "$TestDrive\PesterTest.xml" 44 | $results.SQLPackageExe | Should be 'C:\pestertest\SQLPackage.exe' 45 | $results.MSBuildExe | Should be 'C:\pestertest\MSBuild.exe' 46 | $results.version | Should be "$currentversion" 47 | 48 | Assert-MockCalled Export-Clixml -Exactly 1 -Scope It 49 | Assert-MockCalled Test-Path -Exactly 3 -Scope It 50 | } 51 | 52 | It "Saves without throwing"{ 53 | # Act 54 | { 55 | Save-MSSQLCICDHelperConfiguration -SQLPackageExePath 'C:\pestertest\SQLPackage.exe' -MSBuildExePath 'C:\pestertest\MSBuild.exe' -erroraction stop 56 | } | Should not throw 57 | 58 | Assert-MockCalled Export-Clixml -Exactly 1 -Scope It 59 | Assert-MockCalled Test-Path -Exactly 3 -Scope It 60 | 61 | } 62 | # prepare fail 63 | Mock Test-Path {$false} 64 | 65 | It "Throws when Test-Path fails"{ 66 | # Act 67 | { 68 | Save-MSSQLCICDHelperConfiguration -SQLPackageExePath 'C:\pestertest\SQLPackage.exe' -MSBuildExePath 'C:\pestertest\MSBuild.exe' -erroraction stop 69 | } | Should throw 70 | 71 | Assert-MockCalled Test-Path -Exactly 1 -Scope It 72 | 73 | } 74 | } 75 | 76 | Describe "ImportConfig" -Tags Build { 77 | # Prepare 78 | 79 | Mock currentversion {'V1.0.0'} 80 | 81 | $FilePath = "$TestDrive\PesterTest.xml" 82 | $ImportCLIXML = Get-Command Import-Clixml 83 | $buildpath = 'C:\pestertest\MSBuild.exe' 84 | $sqlpath = 'C:\pestertest\SQLPackage.exe' 85 | $currentversion = currentversion 86 | 87 | $export = @{ 88 | MSBuildExe = $buildpath 89 | SQLPackageExe = $sqlpath 90 | Version = currentversion 91 | } 92 | 93 | $export | Export-Clixml -path $FilePath 94 | 95 | Mock Test-Path {$true} 96 | 97 | Mock Import-Clixml -Verifiable { 98 | & $ImportCLIXML $FilePath 99 | } 100 | 101 | It "imports the config with the correct values"{ 102 | # Assert 103 | $results = ImportConfig 104 | $results.SQLPackageExe | Should BeExactly $sqlpath 105 | $results.MSBuildExe | Should BeExactly $buildpath 106 | $results.version | Should BeExactly $currentversion 107 | 108 | Assert-MockCalled currentversion -Exactly 1 -Scope It 109 | Assert-MockCalled Import-Clixml -Exactly 1 -Scope It 110 | Assert-MockCalled Test-Path -Exactly 1 -Scope It 111 | } 112 | 113 | It "imports the config w/o throwing an error"{ 114 | 115 | { 116 | ImportConfig -erroraction stop 117 | } | Should not throw 118 | 119 | Assert-MockCalled currentversion -Exactly 1 -Scope It 120 | Assert-MockCalled Import-Clixml -Exactly 1 -Scope It 121 | Assert-MockCalled Test-Path -Exactly 1 -Scope It 122 | } 123 | 124 | # prepare fail 125 | Mock Test-Path {$false} 126 | 127 | It "Throws an error when path cannot be tested"{ 128 | 129 | { 130 | ImportConfig -erroraction stop 131 | } | Should throw 132 | 133 | Assert-MockCalled currentversion -Exactly 1 -Scope It 134 | Assert-MockCalled Test-Path -Exactly 1 -Scope It 135 | } 136 | 137 | } 138 | 139 | Describe "Get-MSSQLCICDHelperConfiguration" -Tags Build { 140 | # Prepare 141 | Mock currentversion {'V1.0.0'} 142 | 143 | $FilePath = "$TestDrive\PesterTest.xml" 144 | $ImportCLIXML = Get-Command Import-Clixml 145 | $buildpath = 'C:\pestertest\MSBuild.exe' 146 | $sqlpath = 'C:\pestertest\SQLPackage.exe' 147 | $currentversion = currentversion 148 | 149 | $export = @{ 150 | MSBuildExe = $buildpath 151 | SQLPackageExe = $sqlpath 152 | Version = currentversion 153 | } 154 | 155 | $export | Export-Clixml -path $FilePath 156 | 157 | $mockresult = Import-Clixml $FilePath 158 | 159 | Mock Test-Path {$true} 160 | 161 | Mock ImportConfig {$mockresult} 162 | 163 | 164 | 165 | It "Imports the configuration with expected values"{ 166 | # Assert 167 | $results = Get-MSSQLCICDHelperConfiguration $FilePath 168 | $results.SQLPackageExe | Should BeExactly $sqlpath 169 | $results.MSBuildExe | Should BeExactly $buildpath 170 | $results.version | Should BeExactly $currentversion 171 | 172 | Assert-MockCalled importconfig -Exactly 1 -Scope It 173 | } 174 | 175 | It "imports the config w/o throwing an error"{ 176 | 177 | { 178 | Get-MSSQLCICDHelperConfiguration -erroraction stop 179 | } | Should not throw 180 | 181 | Assert-MockCalled importconfig -Exactly 1 -Scope It 182 | } 183 | 184 | # prepare fail 185 | Mock importconfig {throw "Could not import config. Make sure it exists or save a new config."} 186 | 187 | It "Throws an error when nested function fails"{ 188 | 189 | { 190 | Get-MSSQLCICDHelperConfiguration -erroraction stop 191 | } | Should throw "Could not import config. Make sure it exists or save a new config." 192 | 193 | Assert-MockCalled importconfig -Exactly 1 -Scope It 194 | } 195 | 196 | } 197 | 198 | Describe "Get-MSSQLCICDHelperPaths" -Tags Build { 199 | 200 | #create 1 MSBuild.exe and 2 SQLPackage.exe 1 non existing file. 201 | 202 | New-Item -Path $TestDrive -Name ExePath1 -ItemType Directory 203 | New-Item -Path $TestDrive -Name ExePath2 -ItemType Directory 204 | New-Item -Path $TestDrive -Name EmptyFolder -ItemType Directory 205 | 206 | New-Item -Path $TestDrive\ExePath1\MSBuild.exe -ItemType File 207 | New-Item -Path $TestDrive\ExePath1\SQLPackage.exe -ItemType File 208 | New-Item -Path $TestDrive\ExePath2\SQLPackage.exe -ItemType File 209 | New-Item -Path $TestDrive\ExePath1\Itshouldignorethis.exe -ItemType File 210 | 211 | Context "Mandatory Parameters" { 212 | It "Parameter Typetofind should be mandatory"{ 213 | 214 | (Get-Command "Get-MSSQLCICDHelperPaths").Parameters['Typetofind'].Attributes.Mandatory | Should Be $true 215 | 216 | } 217 | 218 | It "Parameter Rootpath should be mandatory"{ 219 | 220 | (Get-Command "Get-MSSQLCICDHelperPaths").Parameters['Rootpath'].Attributes.Mandatory | Should Be $true 221 | 222 | } 223 | 224 | It "Should throw an error when no valid typetofind was entered" { 225 | { 226 | Get-MSSQLCICDHelperPaths -typetofind Pester -rootpath $TestDrive -erroraction stop 227 | } | Should Throw 228 | 229 | } 230 | 231 | It "Should throw an error when no valid rootpath was entered for MSBuild"{ 232 | { 233 | Get-MSSQLCICDHelperPaths -typetofind MSBuild -rootpath $TestDrive\NonExistingFolder -erroraction stop 234 | } | Should Throw 235 | 236 | } 237 | 238 | It "Should throw an error when no valid rootpath was entered for SQLPackage"{ 239 | { 240 | Get-MSSQLCICDHelperPaths -typetofind SQLPackage -rootpath $TestDrive\NonExistingFolder -erroraction stop 241 | } | Should Throw 242 | 243 | } 244 | } 245 | 246 | Context "No Files Found"{ 247 | It "Should throw an error when no files were found for MSBuild"{ 248 | { 249 | Get-MSSQLCICDHelperPaths -typetofind MSBuild -rootpath $TestDrive\NonExistingFolder -erroraction stop 250 | } | Should Throw 251 | 252 | } 253 | 254 | It "Should throw an error when no files were found for SQLPackage"{ 255 | { 256 | Get-MSSQLCICDHelperPaths -typetofind SQLPackage -rootpath $TestDrive\NonExistingFolder -erroraction stop 257 | } | Should Throw 258 | 259 | } 260 | } 261 | 262 | Context "File counts"{ 263 | 264 | It "Should find one MSBuild.exe when searching MSBuild"{ 265 | 266 | (Get-MSSQLCICDHelperPaths -typetofind MSBuild -rootpath $TestDrive).count | Should BeExactly 1 267 | 268 | } 269 | 270 | It "Should find two SQLPackage.exe when searching SQLPackage"{ 271 | 272 | (Get-MSSQLCICDHelperPaths -typetofind SQLPackage -rootpath $TestDrive).count | Should BeExactly 2 273 | 274 | } 275 | 276 | It "Should find three total *.exe when searching Both"{ 277 | 278 | (Get-MSSQLCICDHelperPaths -typetofind Both -rootpath $TestDrive).count | Should BeExactly 3 279 | 280 | } 281 | } 282 | 283 | Context "Correct file paths"{ 284 | It "Should find find the correct path to MSbuild.exe"{ 285 | 286 | $results = Get-MSSQLCICDHelperPaths -typetofind MSBuild -rootpath $TestDrive 287 | 288 | $results | Should contain "$TestDrive\exepath1\MSBuild.exe" 289 | 290 | } 291 | 292 | It "Should find find the correct paths to SQLPackage.exe"{ 293 | 294 | $results = Get-MSSQLCICDHelperPaths -typetofind SQLPackage -rootpath $TestDrive 295 | 296 | $results | Should contain "$TestDrive\exepath1\SQLPackage.exe" 297 | $results | Should contain "$TestDrive\exepath2\SQLPackage.exe" 298 | 299 | } 300 | 301 | It "Should find find the correct paths to Both *.exe"{ 302 | 303 | $results = Get-MSSQLCICDHelperPaths -typetofind Both -rootpath $TestDrive 304 | 305 | $results | Should contain "$TestDrive\exepath1\MSBuild.exe" 306 | $results | Should contain "$TestDrive\exepath1\SQLPackage.exe" 307 | $results | Should contain "$TestDrive\exepath2\SQLPackage.exe" 308 | 309 | } 310 | 311 | It "Should not contain SQLPackage elements when looking for MSBuild"{ 312 | 313 | $results = Get-MSSQLCICDHelperPaths -typetofind MSBuild -rootpath $TestDrive 314 | 315 | $results | Should not contain "$TestDrive\exepath1\SQLPackage.exe" 316 | $results | Should not contain "$TestDrive\exepath2\SQLPackage.exe" 317 | 318 | } 319 | 320 | It "Should not contain MSBuild elements when looking for SQLPackage"{ 321 | 322 | $results = Get-MSSQLCICDHelperPaths -typetofind SQLPackage -rootpath $TestDrive 323 | 324 | $results | Should not contain "$TestDrive\exepath1\MSBuild.exe" 325 | 326 | } 327 | 328 | It "Should never contain the dummy file when running MSBuild"{ 329 | 330 | $results = Get-MSSQLCICDHelperPaths -typetofind MSBuild -rootpath $TestDrive 331 | 332 | $results | Should Not contain "$TestDrive\ExePath1\Itshouldignorethis.exe" 333 | 334 | } 335 | 336 | It "Should never contain the dummy file when running SQLPackage"{ 337 | 338 | $results = Get-MSSQLCICDHelperPaths -typetofind SQLPackage -rootpath $TestDrive 339 | 340 | $results | Should Not contain "$TestDrive\ExePath1\Itshouldignorethis.exe" 341 | 342 | } 343 | 344 | It "Should never contain the dummy file when running Both"{ 345 | 346 | $results = Get-MSSQLCICDHelperPaths -typetofind Both -rootpath $TestDrive 347 | 348 | $results | Should Not contain "$TestDrive\ExePath1\Itshouldignorethis.exe" 349 | 350 | } 351 | } 352 | 353 | 354 | Context "Throws" { 355 | It "Should not Throw when searching MSBuild"{ 356 | 357 | {Get-MSSQLCICDHelperPaths -typetofind MSBuild -rootpath $TestDrive } | Should Not Throw 358 | 359 | } 360 | 361 | It "Should not Throw when searching SQLPackage"{ 362 | 363 | {Get-MSSQLCICDHelperPaths -typetofind SQLPackage -rootpath $TestDrive } | Should Not Throw 364 | 365 | } 366 | 367 | It "Should not Throw when searching Both"{ 368 | 369 | {Get-MSSQLCICDHelperPaths -typetofind Both -rootpath $TestDrive } | Should Not Throw 370 | 371 | } 372 | } 373 | 374 | 375 | 376 | } 377 | 378 | Describe "Get-MSSQLCICDHelperFiletoBuildDeploy" -Tags Build { 379 | 380 | #create 1 of each type, dummyfile, a dir with multiple of the same and an empty dir 381 | # folders 382 | New-Item -Path $TestDrive -Name Single -ItemType Directory 383 | New-Item -Path $TestDrive -Name Multiple -ItemType Directory 384 | New-Item -Path $TestDrive -Name Empty -ItemType Directory 385 | 386 | # Singles 387 | New-Item -Path $TestDrive\Single\Solution.sln -ItemType File 388 | New-Item -Path $TestDrive\Single\SQLProject.sqlproj -ItemType File 389 | New-Item -Path $TestDrive\Single\DBToDeploy.dacpac -ItemType File 390 | New-Item -Path $TestDrive\Single\DBToDeploy.publish.xml -ItemType File 391 | New-Item -Path $TestDrive\Single\SSISPackages.dtspac -ItemType File 392 | 393 | #To Ignore Singles 394 | New-Item -Path $TestDrive\Single\DBToIgnore.nonpublish.xml -ItemType File 395 | New-Item -Path $TestDrive\Single\Itshouldignorethis.exe -ItemType File 396 | 397 | # Multiple 398 | 399 | New-Item -Path $TestDrive\Multiple\Solution1.sln -ItemType File 400 | New-Item -Path $TestDrive\Multiple\SQLProject1.sqlproj -ItemType File 401 | New-Item -Path $TestDrive\Multiple\DBToDeploy1.dacpac -ItemType File 402 | New-Item -Path $TestDrive\Multiple\DBToDeploy1.publish.xml -ItemType File 403 | New-Item -Path $TestDrive\Multiple\SSISPackages1.dtspac -ItemType File 404 | 405 | $date = (Get-Date).AddDays(-1-$i) 406 | 407 | Set-ItemProperty -Path $TestDrive\Multiple\Solution1.sln -Name LastWriteTime -Value $date 408 | Set-ItemProperty -Path $TestDrive\Multiple\SQLProject1.sqlproj -Name LastWriteTime -Value $date 409 | Set-ItemProperty -Path $TestDrive\Multiple\DBToDeploy1.dacpac -Name LastWriteTime -Value $date 410 | Set-ItemProperty -Path $TestDrive\Multiple\DBToDeploy1.publish.xml -Name LastWriteTime -Value $date 411 | Set-ItemProperty -Path $TestDrive\Multiple\SSISPackages1.dtspac -Name LastWriteTime -Value $date 412 | 413 | New-Item -Path $TestDrive\Multiple\Solution2.sln -ItemType File 414 | New-Item -Path $TestDrive\Multiple\SQLProject2.sqlproj -ItemType File 415 | New-Item -Path $TestDrive\Multiple\DBToDeploy2.dacpac -ItemType File 416 | New-Item -Path $TestDrive\Multiple\DBToDeploy2.publish.xml -ItemType File 417 | New-Item -Path $TestDrive\Multiple\SSISPackages2.dtspac -ItemType File 418 | 419 | #To Ignore Multiple 420 | New-Item -Path $TestDrive\Multiple\DBToIgnore.nonpublish.xml -ItemType File 421 | New-Item -Path $TestDrive\Multiple\Itshouldignorethis.exe -ItemType File 422 | 423 | Context "Mandatory Paramaters"{ 424 | 425 | It "Parameter Typetofind should be mandatory"{ 426 | 427 | (Get-Command "Get-MSSQLCICDHelperFiletoBuildDeploy").Parameters['Typetofind'].Attributes.Mandatory | Should Be $true 428 | 429 | } 430 | 431 | It "Parameter Rootpath should be mandatory"{ 432 | 433 | (Get-Command "Get-MSSQLCICDHelperFiletoBuildDeploy").Parameters['Rootpath'].Attributes.Mandatory | Should Be $true 434 | 435 | } 436 | 437 | It "Should throw an error when no valid typetofind was entered"{ 438 | { 439 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Pester -rootpath $TestDrive -erroraction stop 440 | } | Should Throw 441 | 442 | } 443 | 444 | It "Should throw an error when a non-existing folder was entered for type Solution"{ 445 | { 446 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath $TestDrive\NonExistingFolder -erroraction stop 447 | } | Should Throw 448 | } 449 | 450 | It "Should throw an error when a non-existing folder was entered for type project"{ 451 | { 452 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Project -rootpath $TestDrive\NonExistingFolder -erroraction stop 453 | } | Should Throw 454 | } 455 | 456 | It "Should throw an error when a non-existing folder was entered for type DacPac"{ 457 | { 458 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DacPac -rootpath $TestDrive\NonExistingFolder -erroraction stop 459 | } | Should Throw 460 | } 461 | 462 | It "Should throw an error when a non-existing folder was entered for type PublishProfile"{ 463 | { 464 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind PublishProfile -rootpath $TestDrive\NonExistingFolder -erroraction stop 465 | } | Should Throw 466 | } 467 | 468 | It "Should throw an error when a non-existing folder was entered for type DTSPac"{ 469 | { 470 | Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DTSPac -rootpath $TestDrive\NonExistingFolder -erroraction stop 471 | } | Should Throw 472 | } 473 | } 474 | 475 | Context "No File Found" { 476 | 477 | It "Should throw an error when no file has been found for type Solution"{ 478 | 479 | { Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath $TestDrive\Empty -erroraction stop } | Should Throw 480 | 481 | } 482 | 483 | It "Should throw an error when no file has been found for type project"{ 484 | 485 | { Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind project -rootpath $TestDrive\Empty -erroraction stop } | Should Throw 486 | 487 | } 488 | 489 | It "Should throw an error when no file has been found for type DacPac"{ 490 | 491 | { Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DacPac -rootpath $TestDrive\Empty -erroraction stop } | Should Throw 492 | 493 | } 494 | 495 | It "Should throw an error when no file has been found for type PublishProfile"{ 496 | 497 | { Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind PublishProfile -rootpath $TestDrive\Empty -erroraction stop } | Should Throw 498 | 499 | } 500 | 501 | It "Should throw an error when no file has been found for type DTSPac"{ 502 | 503 | { Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DTSPac -rootpath $TestDrive\Empty -erroraction stop } | Should Throw 504 | 505 | } 506 | } 507 | 508 | Context "Folders with Single file" {} 509 | 510 | Context "File Counts" { 511 | #single files Count 512 | It "Should Find a single file for type Solution"{ 513 | 514 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath $TestDrive\Single).count | Should BeExactly 1 515 | 516 | } 517 | 518 | It "Should Find a single file for type project"{ 519 | 520 | 521 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind project -rootpath $TestDrive\Single).count | Should BeExactly 1 522 | 523 | } 524 | 525 | It "Should Find a single file for type DacPac"{ 526 | 527 | 528 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DacPac -rootpath $TestDrive\Single).count | Should BeExactly 1 529 | 530 | } 531 | 532 | It "Should Find a single file for type PublishProfile"{ 533 | 534 | 535 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind PublishProfile -rootpath $TestDrive\Single).count | Should BeExactly 1 536 | 537 | } 538 | 539 | It "Should Find a single file for type DTSPac"{ 540 | 541 | 542 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DTSPac -rootpath $TestDrive\Single).count | Should BeExactly 1 543 | 544 | } 545 | } 546 | Context "Correct File Paths" { 547 | #file matches 548 | It "Single Filename match for type Solution"{ 549 | 550 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath $TestDrive\Single 551 | 552 | $results.Fullname | Should contain "$TestDrive\Single\Solution.sln" 553 | 554 | } 555 | 556 | It "Single Filename match for type project"{ 557 | 558 | 559 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Project -rootpath $TestDrive\Single 560 | 561 | $results.Fullname | Should contain "$TestDrive\Single\SQLProject.sqlproj" 562 | 563 | } 564 | 565 | It "Single Filename match for type DacPac"{ 566 | 567 | 568 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DacPac -rootpath $TestDrive\Single 569 | 570 | $results.Fullname | Should contain "$TestDrive\Single\DBToDeploy.dacpac" 571 | 572 | } 573 | 574 | It "Single Filename match for type PublishProfile"{ 575 | 576 | 577 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind PublishProfile -rootpath $TestDrive\Single 578 | 579 | $results.Fullname | Should contain "$TestDrive\Single\DBToDeploy.publish.xml" 580 | 581 | } 582 | 583 | It "Single Filename match for type DTSPac"{ 584 | 585 | 586 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DTSPac -rootpath $TestDrive\Single 587 | 588 | $results.Fullname | Should contain "$TestDrive\Single\SSISPackages.dtspac" 589 | 590 | } 591 | } 592 | Context "Dummy Files" { 593 | 594 | #file matches 595 | It "Dummy Exclude Single for type Solution"{ 596 | 597 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath $TestDrive\Single 598 | 599 | $results.Fullname | Should not contain "$TestDrive\Single\DBToIgnore.nonpublish.xml" 600 | $results.Fullname | Should not contain "$TestDrive\Single\Itshouldignorethis.exe" 601 | 602 | } 603 | 604 | It "Dummy Exclude Single for type project"{ 605 | 606 | 607 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Project -rootpath $TestDrive\Single 608 | 609 | $results.Fullname | Should not contain "$TestDrive\Single\DBToIgnore.nonpublish.xml" 610 | $results.Fullname | Should not contain "$TestDrive\Single\Itshouldignorethis.exe" 611 | 612 | } 613 | 614 | It "Dummy Exclude Single for type DacPac"{ 615 | 616 | 617 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DacPac -rootpath $TestDrive\Single 618 | 619 | $results.Fullname | Should not contain "$TestDrive\Single\DBToIgnore.nonpublish.xml" 620 | $results.Fullname | Should not contain "$TestDrive\Single\Itshouldignorethis.exe" 621 | 622 | } 623 | 624 | It "Dummy Exclude Single for type PublishProfile"{ 625 | 626 | 627 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind PublishProfile -rootpath $TestDrive\Single 628 | 629 | $results.Fullname | Should not contain "$TestDrive\Single\DBToIgnore.nonpublish.xml" 630 | $results.Fullname | Should not contain "$TestDrive\Single\Itshouldignorethis.exe" 631 | 632 | } 633 | 634 | It "Dummy Exclude Single for type DTSPac"{ 635 | 636 | 637 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DTSPac -rootpath $TestDrive\Single 638 | 639 | $results.Fullname | Should not contain "$TestDrive\Single\DBToIgnore.nonpublish.xml" 640 | $results.Fullname | Should not contain "$TestDrive\Single\Itshouldignorethis.exe" 641 | 642 | 643 | 644 | } 645 | 646 | } 647 | 648 | Context "Folders with Multiple files" {} 649 | 650 | Context "File Counts" { 651 | #single files Count 652 | It "Should Find a single file in folder with Multiple files for type Solution"{ 653 | 654 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath $TestDrive\Multiple).count | Should BeExactly 1 655 | 656 | } 657 | 658 | It "Should Find a single file in folder with Multiple files for type project"{ 659 | 660 | 661 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind project -rootpath $TestDrive\Multiple).count | Should BeExactly 1 662 | 663 | } 664 | 665 | It "Should Find a single file in folder with Multiple files for type DacPac"{ 666 | 667 | 668 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DacPac -rootpath $TestDrive\Multiple).count | Should BeExactly 1 669 | 670 | } 671 | 672 | It "Should Find a single file in folder with Multiple files for type PublishProfile"{ 673 | 674 | 675 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind PublishProfile -rootpath $TestDrive\Multiple).count | Should BeExactly 1 676 | 677 | } 678 | 679 | It "Should Find a single file in folder with Multiple files for type DTSPac"{ 680 | 681 | 682 | (Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DTSPac -rootpath $TestDrive\Multiple).count | Should BeExactly 1 683 | 684 | } 685 | } 686 | Context "Correct File Paths" { 687 | #file matches 688 | It "Multiple Filename match for type Solution"{ 689 | 690 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath $TestDrive\Multiple 691 | 692 | $results.Fullname | Should contain "$TestDrive\Multiple\Solution2.sln" 693 | 694 | } 695 | 696 | It "Multiple Filename match for type project"{ 697 | 698 | 699 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Project -rootpath $TestDrive\Multiple 700 | 701 | $results.Fullname | Should contain "$TestDrive\Multiple\SQLProject2.sqlproj" 702 | 703 | } 704 | 705 | It "Multiple Filename match for type DacPac"{ 706 | 707 | 708 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DacPac -rootpath $TestDrive\Multiple 709 | 710 | $results.Fullname | Should contain "$TestDrive\Multiple\DBToDeploy2.dacpac" 711 | 712 | } 713 | 714 | It "Multiple Filename match for type PublishProfile"{ 715 | 716 | 717 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind PublishProfile -rootpath $TestDrive\Multiple 718 | 719 | $results.Fullname | Should contain "$TestDrive\Multiple\DBToDeploy2.publish.xml" 720 | 721 | } 722 | 723 | It "Multiple Filename match for type DTSPac"{ 724 | 725 | 726 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DTSPac -rootpath $TestDrive\Multiple 727 | 728 | $results.Fullname | Should contain "$TestDrive\Multiple\SSISPackages2.dtspac" 729 | 730 | } 731 | } 732 | Context "Dummy Files" { 733 | 734 | #file matches 735 | It "Multiple Dummy Exclude Single for type Solution"{ 736 | 737 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Solution -rootpath $TestDrive\Multiple 738 | 739 | $results.Fullname | Should not contain "$TestDrive\Multiple\DBToIgnore.nonpublish.xml" 740 | $results.Fullname | Should not contain "$TestDrive\Multiple\Itshouldignorethis.exe" 741 | 742 | } 743 | 744 | It "Multiple Dummy Exclude Single for type project"{ 745 | 746 | 747 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind Project -rootpath $TestDrive\Multiple 748 | 749 | $results.Fullname | Should not contain "$TestDrive\Multiple\DBToIgnore.nonpublish.xml" 750 | $results.Fullname | Should not contain "$TestDrive\Multiple\Itshouldignorethis.exe" 751 | 752 | } 753 | 754 | It "Multiple Dummy Exclude Single for type DacPac"{ 755 | 756 | 757 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DacPac -rootpath $TestDrive\Multiple 758 | 759 | $results.Fullname | Should not contain "$TestDrive\Multiple\DBToIgnore.nonpublish.xml" 760 | $results.Fullname | Should not contain "$TestDrive\Multiple\Itshouldignorethis.exe" 761 | 762 | } 763 | 764 | It "Multiple Dummy Exclude Single for type PublishProfile"{ 765 | 766 | 767 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind PublishProfile -rootpath $TestDrive\Multiple 768 | 769 | $results.Fullname | Should not contain "$TestDrive\Multiple\DBToIgnore.nonpublish.xml" 770 | $results.Fullname | Should not contain "$TestDrive\Multiple\Itshouldignorethis.exe" 771 | 772 | } 773 | 774 | It "Multiple Dummy Exclude Single for type DTSPac"{ 775 | 776 | 777 | $results = Get-MSSQLCICDHelperFiletoBuildDeploy -typetofind DTSPac -rootpath $TestDrive\Multiple 778 | 779 | $results.Fullname | Should not contain "$TestDrive\Multiple\DBToIgnore.nonpublish.xml" 780 | $results.Fullname | Should not contain "$TestDrive\Multiple\Itshouldignorethis.exe" 781 | 782 | 783 | 784 | } 785 | 786 | } 787 | 788 | } 789 | Describe "Invoke-Cmd" -Tags Build { 790 | 791 | #New-Item -Path $TestDrive\poutput.log -ItemType File 792 | #New-Item -Path $TestDrive\errorpoutput.log -ItemType File 793 | 794 | $executable = 'powershell.exe' 795 | $passingarguments = 'Write-Output ''Test sentence for assertion''' 796 | $passingAssertion = 'Test sentence for assertion' 797 | $failingarguments = 'throw' 798 | $FailingAssertion = '' 799 | $logfile = "$TestDrive\poutput.log" 800 | $errorlogfile = "$TestDrive\errorpoutput.log" 801 | 802 | It "Mandatory Parameters"{ 803 | (Get-Command "Invoke-Cmd").Parameters['Executable'].Attributes.Mandatory | Should Be $true 804 | (Get-Command "Invoke-Cmd").Parameters['Arguments'].Attributes.Mandatory | Should Be $true 805 | (Get-Command "Invoke-Cmd").Parameters['Logfile'].Attributes.Mandatory | Should Be $true 806 | (Get-Command "Invoke-Cmd").Parameters['errorlogfile'].Attributes.Mandatory | Should Be $true 807 | } 808 | 809 | It "Should not throw when all parameters are entered on a successfull command"{ 810 | { Invoke-Cmd -executable $executable -arguments $passingarguments -logfile $logfile -errorlogfile $errorlogfile } | Should not Throw 811 | } 812 | 813 | It "Should throw on an invalid logfile path parent"{ 814 | { Invoke-Cmd -executable $executable -arguments $passingarguments -logfile $testdrive\NonExistingFolder\file.log -errorlogfile $errorlogfile -erroraction stop } | Should Throw 815 | } 816 | 817 | It "Should throw on an invalid errorlogfile path parent"{ 818 | { Invoke-Cmd -executable $executable -arguments $passingarguments -logfile $logfile -errorlogfile $testdrive\NonExistingFolder\file.log -erroraction stop } | Should Throw 819 | } 820 | 821 | It "Should throw when a non existing executable is used"{ 822 | { Invoke-Cmd -executable 'failingassert.error' -arguments $passingarguments -logfile $logfile -errorlogfile $errorlogfile -erroraction stop } | Should Throw 823 | } 824 | 825 | It "Should produce the correct results when calling write-output command"{ 826 | $results = Invoke-Cmd -executable $executable -arguments $passingarguments -logfile $logfile -errorlogfile $errorlogfile 827 | #getting results 828 | $results.ExitCode | Should -BeExactly 0 829 | $results.ErrorOutput | Should -BeExactly -1 830 | $results.Output.Trim() | Should -BeExactly $passingAssertion 831 | #test created files 832 | Test-Path -Path $logfile | Should be $true 833 | Test-Path -Path $errorlogfile | Should be $true 834 | 835 | $producedlogfile = Get-Content $logfile 836 | $producederrorlogfile = Get-Content $errorlogfile 837 | 838 | $producedlogfile[0].Trim() | Should -BeExactly $passingAssertion 839 | $producederrorlogfile.Trim() | Should -BeExactly -1 840 | } 841 | 842 | It "Should produce the correct results when calling throw command"{ 843 | $results = Invoke-Cmd -executable $executable -arguments $failingarguments -logfile $logfile -errorlogfile $errorlogfile 844 | 845 | $results.ExitCode | Should -BeExactly 1 846 | $results.ErrorOutput | Should -BeExactly 83 847 | $results.Output | Should -BeExactly $FailingAssertion 848 | 849 | Test-Path -Path $logfile | Should be $true 850 | Test-Path -Path $errorlogfile | Should be $true 851 | 852 | $producedlogfile = Get-Content $logfile 853 | $producederrorlogfile = Get-Content $errorlogfile 854 | 855 | $producedlogfile | Should -BeExactly $FailingAssertion 856 | $producederrorlogfile.Trim() | Should -BeExactly 83 857 | } 858 | } 859 | 860 | Describe "Invoke-MSSQLCICDHelperMSBuild" -Tags Build { 861 | Context "Errors & Dependencies" { 862 | #Context "test nested context"{} 863 | ############################ 864 | 865 | ############################ 866 | mock Get-Module {$false} 867 | 868 | It "Should throw when Invoke-MSBuild is not Available"{ 869 | {Invoke-MSSQLCICDHelperMSBuild -UseInvokeMSBuildModule -erroraction stop} | Should throw 870 | 871 | Assert-MockCalled Get-Module -Exactly 1 -Scope It 872 | } 873 | 874 | New-Item -Path $TestDrive -Name Solution -ItemType Directory 875 | New-Item -Path $TestDrive -Name Empty -ItemType Directory 876 | 877 | New-Item -Path $TestDrive\MSBuild.exe -ItemType File 878 | New-Item -Path $TestDrive\SQLPackage.exe -ItemType File 879 | 880 | New-Item -Path $TestDrive\Solution\Solution.sln -ItemType File 881 | 882 | $filetobuild = "$TestDrive\Solution\Solution.sln" 883 | 884 | Mock currentversion {'V1.0.0'} 885 | 886 | $configpath = "$TestDrive\PesterTest.xml" 887 | #$ImportCLIXML = Get-Command Import-Clixml 888 | $buildpath = "$Testdrive\MSBuild.exe" 889 | $sqlpath = "$testdrive\SQLPackage.exe" 890 | $currentversion = currentversion 891 | 892 | $export = @{ 893 | MSBuildExe = $buildpath 894 | SQLPackageExe = $sqlpath 895 | Version = $currentversion 896 | } 897 | 898 | $export | Export-Clixml -path $configpath 899 | 900 | $mockresult = Import-Clixml $configpath 901 | 902 | Mock ImportConfig {$mockresult} 903 | 904 | it "Should throw an error on an empty path" { 905 | {Invoke-MSSQLCICDHelperMSBuild -filename $testRoot\NonExisting.sln -erroraction stop} | Should throw 906 | 907 | Assert-MockCalled importconfig -Exactly 0 -Scope It 908 | } 909 | 910 | mock Get-MSSQLCICDHelperFiletoBuildDeploy {throw} 911 | #mock Get-ChildItem {} 912 | mock Invoke-Expression {throw} 913 | 914 | # it "Should throw when an exception occurs with manual filename " { 915 | 916 | # {Invoke-MSSQLCICDHelperMSBuild -filename $filetobuild -erroraction stop} | Should throw 917 | # } 918 | 919 | it "Should throw when an exception occurs with Autodiscovery filename" { 920 | 921 | {Invoke-MSSQLCICDHelperMSBuild -erroraction stop} | Should throw 922 | 923 | Assert-MockCalled importconfig -Exactly 1 -Scope It 924 | Assert-MockCalled Get-MSSQLCICDHelperFiletoBuildDeploy -Exactly 1 -Scope It 925 | } 926 | } 927 | } 928 | 929 | Describe "Invoke-MSSQLCICDHelperSQLPackage" -Tags Build { 930 | 931 | } 932 | 933 | } 934 | 935 | 936 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Description 3 | Installs and loads all the required modules for the build. 4 | Derived from scripts written by Warren F. (RamblingCookieMonster) 5 | #> 6 | 7 | [cmdletbinding()] 8 | param ($Task = 'Default') 9 | "Starting build" 10 | 11 | # Grab nuget bits, install modules, set build variables, start build. 12 | " Install Dependent Modules" 13 | Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 14 | Install-Module InvokeBuild, BuildHelpers, PSDeploy, PSScriptAnalyzer, PlatyPS -force -Scope CurrentUser #PSDeploy, 15 | Install-Module Pester -Force -SkipPublisherCheck -Scope CurrentUser 16 | 17 | " Import Dependent Modules" 18 | Import-Module InvokeBuild, BuildHelpers, PSScriptAnalyzer 19 | 20 | Set-BuildEnvironment 21 | 22 | " InvokeBuild" 23 | Invoke-Build $Task -Result result 24 | if ($Result.Error) 25 | { 26 | exit 1 27 | } 28 | else 29 | { 30 | exit 0 31 | } -------------------------------------------------------------------------------- /fingerprint: -------------------------------------------------------------------------------- 1 | Get-MSSQLCICDHelperFiletoBuildDeploy:db 2 | Get-MSSQLCICDHelperFiletoBuildDeploy:Debug 3 | Get-MSSQLCICDHelperFiletoBuildDeploy:ea 4 | Get-MSSQLCICDHelperFiletoBuildDeploy:ErrorAction 5 | Get-MSSQLCICDHelperFiletoBuildDeploy:ErrorVariable 6 | Get-MSSQLCICDHelperFiletoBuildDeploy:ev 7 | Get-MSSQLCICDHelperFiletoBuildDeploy:infa 8 | Get-MSSQLCICDHelperFiletoBuildDeploy:InformationAction 9 | Get-MSSQLCICDHelperFiletoBuildDeploy:InformationVariable 10 | Get-MSSQLCICDHelperFiletoBuildDeploy:iv 11 | Get-MSSQLCICDHelperFiletoBuildDeploy:ob 12 | Get-MSSQLCICDHelperFiletoBuildDeploy:OutBuffer 13 | Get-MSSQLCICDHelperFiletoBuildDeploy:OutVariable 14 | Get-MSSQLCICDHelperFiletoBuildDeploy:ov 15 | Get-MSSQLCICDHelperFiletoBuildDeploy:PipelineVariable 16 | Get-MSSQLCICDHelperFiletoBuildDeploy:pv 17 | Get-MSSQLCICDHelperFiletoBuildDeploy:rootpath 18 | Get-MSSQLCICDHelperFiletoBuildDeploy:typetofind 19 | Get-MSSQLCICDHelperFiletoBuildDeploy:vb 20 | Get-MSSQLCICDHelperFiletoBuildDeploy:Verbose 21 | Get-MSSQLCICDHelperFiletoBuildDeploy:wa 22 | Get-MSSQLCICDHelperFiletoBuildDeploy:WarningAction 23 | Get-MSSQLCICDHelperFiletoBuildDeploy:WarningVariable 24 | Get-MSSQLCICDHelperFiletoBuildDeploy:wv 25 | Get-MSSQLCICDHelperPaths:db 26 | Get-MSSQLCICDHelperPaths:Debug 27 | Get-MSSQLCICDHelperPaths:ea 28 | Get-MSSQLCICDHelperPaths:ErrorAction 29 | Get-MSSQLCICDHelperPaths:ErrorVariable 30 | Get-MSSQLCICDHelperPaths:ev 31 | Get-MSSQLCICDHelperPaths:infa 32 | Get-MSSQLCICDHelperPaths:InformationAction 33 | Get-MSSQLCICDHelperPaths:InformationVariable 34 | Get-MSSQLCICDHelperPaths:iv 35 | Get-MSSQLCICDHelperPaths:ob 36 | Get-MSSQLCICDHelperPaths:OutBuffer 37 | Get-MSSQLCICDHelperPaths:OutVariable 38 | Get-MSSQLCICDHelperPaths:ov 39 | Get-MSSQLCICDHelperPaths:PipelineVariable 40 | Get-MSSQLCICDHelperPaths:pv 41 | Get-MSSQLCICDHelperPaths:rootpath 42 | Get-MSSQLCICDHelperPaths:typetofind 43 | Get-MSSQLCICDHelperPaths:vb 44 | Get-MSSQLCICDHelperPaths:Verbose 45 | Get-MSSQLCICDHelperPaths:wa 46 | Get-MSSQLCICDHelperPaths:WarningAction 47 | Get-MSSQLCICDHelperPaths:WarningVariable 48 | Get-MSSQLCICDHelperPaths:wv 49 | Invoke-MSSQLCICDHelperMSBuild:db 50 | Invoke-MSSQLCICDHelperMSBuild:Debug 51 | Invoke-MSSQLCICDHelperMSBuild:ea 52 | Invoke-MSSQLCICDHelperMSBuild:ErrorAction 53 | Invoke-MSSQLCICDHelperMSBuild:ErrorVariable 54 | Invoke-MSSQLCICDHelperMSBuild:ev 55 | Invoke-MSSQLCICDHelperMSBuild:filename 56 | Invoke-MSSQLCICDHelperMSBuild:hidden 57 | Invoke-MSSQLCICDHelperMSBuild:infa 58 | Invoke-MSSQLCICDHelperMSBuild:InformationAction 59 | Invoke-MSSQLCICDHelperMSBuild:InformationVariable 60 | Invoke-MSSQLCICDHelperMSBuild:InvokeMSBuildParameters 61 | Invoke-MSSQLCICDHelperMSBuild:iv 62 | Invoke-MSSQLCICDHelperMSBuild:keeplogfiles 63 | Invoke-MSSQLCICDHelperMSBuild:MSBuildArguments 64 | Invoke-MSSQLCICDHelperMSBuild:ob 65 | Invoke-MSSQLCICDHelperMSBuild:OutBuffer 66 | Invoke-MSSQLCICDHelperMSBuild:OutVariable 67 | Invoke-MSSQLCICDHelperMSBuild:ov 68 | Invoke-MSSQLCICDHelperMSBuild:P 69 | Invoke-MSSQLCICDHelperMSBuild:Parameters 70 | Invoke-MSSQLCICDHelperMSBuild:Params 71 | Invoke-MSSQLCICDHelperMSBuild:PipelineVariable 72 | Invoke-MSSQLCICDHelperMSBuild:pv 73 | Invoke-MSSQLCICDHelperMSBuild:UseInvokeMSBuildModule 74 | Invoke-MSSQLCICDHelperMSBuild:vb 75 | Invoke-MSSQLCICDHelperMSBuild:Verbose 76 | Invoke-MSSQLCICDHelperMSBuild:wa 77 | Invoke-MSSQLCICDHelperMSBuild:WarningAction 78 | Invoke-MSSQLCICDHelperMSBuild:WarningVariable 79 | Invoke-MSSQLCICDHelperMSBuild:wv 80 | Invoke-MSSQLCICDHelperSQLPackage:AdditionalArguments 81 | Invoke-MSSQLCICDHelperSQLPackage:db 82 | Invoke-MSSQLCICDHelperSQLPackage:Debug 83 | Invoke-MSSQLCICDHelperSQLPackage:detectpr 84 | Invoke-MSSQLCICDHelperSQLPackage:DetectPublishProfile 85 | Invoke-MSSQLCICDHelperSQLPackage:ea 86 | Invoke-MSSQLCICDHelperSQLPackage:ErrorAction 87 | Invoke-MSSQLCICDHelperSQLPackage:ErrorVariable 88 | Invoke-MSSQLCICDHelperSQLPackage:ev 89 | Invoke-MSSQLCICDHelperSQLPackage:filename 90 | Invoke-MSSQLCICDHelperSQLPackage:hidden 91 | Invoke-MSSQLCICDHelperSQLPackage:infa 92 | Invoke-MSSQLCICDHelperSQLPackage:InformationAction 93 | Invoke-MSSQLCICDHelperSQLPackage:InformationVariable 94 | Invoke-MSSQLCICDHelperSQLPackage:iv 95 | Invoke-MSSQLCICDHelperSQLPackage:keeplogfiles 96 | Invoke-MSSQLCICDHelperSQLPackage:lfp 97 | Invoke-MSSQLCICDHelperSQLPackage:logfilepath 98 | Invoke-MSSQLCICDHelperSQLPackage:ob 99 | Invoke-MSSQLCICDHelperSQLPackage:OutBuffer 100 | Invoke-MSSQLCICDHelperSQLPackage:OutVariable 101 | Invoke-MSSQLCICDHelperSQLPackage:ov 102 | Invoke-MSSQLCICDHelperSQLPackage:P 103 | Invoke-MSSQLCICDHelperSQLPackage:Parameters 104 | Invoke-MSSQLCICDHelperSQLPackage:Params 105 | Invoke-MSSQLCICDHelperSQLPackage:PipelineVariable 106 | Invoke-MSSQLCICDHelperSQLPackage:pr 107 | Invoke-MSSQLCICDHelperSQLPackage:PublishProfile 108 | Invoke-MSSQLCICDHelperSQLPackage:pv 109 | Invoke-MSSQLCICDHelperSQLPackage:TargetConnectionString 110 | Invoke-MSSQLCICDHelperSQLPackage:TargetDB 111 | Invoke-MSSQLCICDHelperSQLPackage:TargetDBName 112 | Invoke-MSSQLCICDHelperSQLPackage:TargetPass 113 | Invoke-MSSQLCICDHelperSQLPackage:TargetPassWord 114 | Invoke-MSSQLCICDHelperSQLPackage:TargetServer 115 | Invoke-MSSQLCICDHelperSQLPackage:TargetServerName 116 | Invoke-MSSQLCICDHelperSQLPackage:TargetUser 117 | Invoke-MSSQLCICDHelperSQLPackage:TargetUserName 118 | Invoke-MSSQLCICDHelperSQLPackage:tconstr 119 | Invoke-MSSQLCICDHelperSQLPackage:tdn 120 | Invoke-MSSQLCICDHelperSQLPackage:tp 121 | Invoke-MSSQLCICDHelperSQLPackage:tsn 122 | Invoke-MSSQLCICDHelperSQLPackage:tu 123 | Invoke-MSSQLCICDHelperSQLPackage:vb 124 | Invoke-MSSQLCICDHelperSQLPackage:Verbose 125 | Invoke-MSSQLCICDHelperSQLPackage:wa 126 | Invoke-MSSQLCICDHelperSQLPackage:WarningAction 127 | Invoke-MSSQLCICDHelperSQLPackage:WarningVariable 128 | Invoke-MSSQLCICDHelperSQLPackage:wv 129 | Save-MSSQLCICDHelperConfiguration:db 130 | Save-MSSQLCICDHelperConfiguration:Debug 131 | Save-MSSQLCICDHelperConfiguration:ea 132 | Save-MSSQLCICDHelperConfiguration:ErrorAction 133 | Save-MSSQLCICDHelperConfiguration:ErrorVariable 134 | Save-MSSQLCICDHelperConfiguration:ev 135 | Save-MSSQLCICDHelperConfiguration:infa 136 | Save-MSSQLCICDHelperConfiguration:InformationAction 137 | Save-MSSQLCICDHelperConfiguration:InformationVariable 138 | Save-MSSQLCICDHelperConfiguration:iv 139 | Save-MSSQLCICDHelperConfiguration:MSBuildExePath 140 | Save-MSSQLCICDHelperConfiguration:ob 141 | Save-MSSQLCICDHelperConfiguration:OutBuffer 142 | Save-MSSQLCICDHelperConfiguration:OutVariable 143 | Save-MSSQLCICDHelperConfiguration:ov 144 | Save-MSSQLCICDHelperConfiguration:PipelineVariable 145 | Save-MSSQLCICDHelperConfiguration:pv 146 | Save-MSSQLCICDHelperConfiguration:SQLPackageExePath 147 | Save-MSSQLCICDHelperConfiguration:vb 148 | Save-MSSQLCICDHelperConfiguration:Verbose 149 | Save-MSSQLCICDHelperConfiguration:wa 150 | Save-MSSQLCICDHelperConfiguration:WarningAction 151 | Save-MSSQLCICDHelperConfiguration:WarningVariable 152 | Save-MSSQLCICDHelperConfiguration:wv 153 | -------------------------------------------------------------------------------- /localpester.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Description 3 | Installs and loads all the required modules for the build. 4 | Derived from scripts written by Warren F. (RamblingCookieMonster) 5 | #> 6 | 7 | [cmdletbinding()] 8 | param ($Task = 'LocalPester') 9 | "Starting build" 10 | # " Installing latest NuGet Provider" 11 | #Install-PackageProvider -name NuGet -Force 12 | # Grab nuget bits, install modules, set build variables, start build. 13 | " Install Dependent Modules" 14 | Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 15 | Install-Module InvokeBuild, BuildHelpers, PSDeploy, PlatyPS -force -Scope CurrentUser 16 | Install-Module PSScriptAnalyzer, Pester -Force -SkipPublisherCheck -Scope CurrentUser 17 | 18 | " Import Dependent Modules" 19 | $modules = New-Object System.Collections.ArrayList 20 | $modules.add('InvokeBuild') 21 | $modules.add('BuildHelpers') 22 | $modules.add('PSScriptAnalyzer') 23 | 24 | foreach($module in $modules) { 25 | if(Get-Module $$module -ErrorAction 'Ignore' -ListAvailable){ 26 | "Module $module is installed. skipping install" 27 | }else{ 28 | "Installing $module ..." 29 | Import-Module $module 30 | } 31 | 32 | } 33 | 34 | Set-BuildEnvironment 35 | 36 | " InvokeBuild" 37 | Invoke-Build $Task -Result result -Verbose 38 | if ($Result.Error) 39 | { 40 | exit 1 41 | } 42 | else 43 | { 44 | exit 0 45 | } --------------------------------------------------------------------------------