diff --git a/.build.ps1 b/.build.ps1 new file mode 100644 index 0000000..4bd2139 --- /dev/null +++ b/.build.ps1 @@ -0,0 +1,36 @@ +<# +.Synopsis + Build script + +.Example + # Create module from source. + Invoke-Build Build + +.Example + # Add doc templates for new command. + # BE CAREFUL! Existing documents will be overwritten and must be discarded using git. + Invoke-Build Doc.Init -ForceDocInit + +#> + +param( + [ValidateSet('Debug', 'Release')] + [string] $Configuration = 'Debug', + + [string] $NuGetApiKey = $env:nuget_apikey, + + # Overwrite published versions + [switch] $ForcePublish, + + # Add doc templates for new command. + [switch] $ForceDocInit, + + # Version suffix to prereleases + [int] $BuildNumber +) + +$ModuleName = 'PsSmo' + +. $PSScriptRoot\tasks\Build.Tasks.ps1 +. $PSScriptRoot\tasks\Dependencies.Tasks.ps1 +. $PSScriptRoot\tasks\PsBuild.Tasks.ps1 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..5cb6205 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,16 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/dotnet/.devcontainer/base.Dockerfile + +# [Choice] .NET version: 6.0, 3.1, 6.0-bullseye, 3.1-bullseye, 6.0-focal, 3.1-focal +ARG VARIANT="6.0-bullseye-slim" +FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT} + +# [Choice] Node.js version: none, lts/*, 18, 16, 14 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..cd636b3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,62 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/dotnet +{ + "name": "C# (.NET)", + "build": { + "dockerfile": "Dockerfile", + "args": { + // Update 'VARIANT' to pick a .NET Core version: 3.1, 6.0 + // Append -bullseye or -focal to pin to an OS version. + "VARIANT": "6.0-bullseye", + // Options + "NODE_VERSION": "none" + } + }, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-dotnettools.csharp" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [5000, 5001], + + // [Optional] To reuse of your local HTTPS dev cert: + // + // 1. Export it locally using this command: + // * Windows PowerShell: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // * macOS/Linux terminal: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // + // 2. Uncomment these 'remoteEnv' lines: + // "remoteEnv": { + // "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere", + // "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx", + // }, + // + // 3. Do one of the following depending on your scenario: + // * When using GitHub Codespaces and/or Remote - Containers: + // 1. Start the container + // 2. Drag ~/.aspnet/https/aspnetapp.pfx into the root of the file explorer + // 3. Open a terminal in VS Code and run "mkdir -p /home/vscode/.aspnet/https && mv aspnetapp.pfx /home/vscode/.aspnet/https" + // + // * If only using Remote - Containers with a local container, uncomment this line instead: + // "mounts": [ "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/vscode/.aspnet/https,type=bind" ], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "dotnet restore", + + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "features": { + "docker-from-docker": "latest", + "powershell": "latest" + } +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..2090df5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [abbgrade] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..d0b302e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "nuget" # See documentation for possible values + directory: "/src/PsSmo" # Location of package manifests + schedule: + interval: "daily" + target-branch: "develop" diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml new file mode 100644 index 0000000..396ec9e --- /dev/null +++ b/.github/workflows/build-validation.yml @@ -0,0 +1,9 @@ +on: + push: + branches: [ develop, bugfix/*, feature/*, release/* ] + pull_request: + branches: [ develop ] + +jobs: + build-validation: + uses: abbgrade/PsBuildTasks/.github/workflows/build-validation-matrix.yml@1.4.0 diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 0000000..0b1fc58 --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,12 @@ +on: + push: + branches: [ release/* ] + workflow_dispatch: + +jobs: + pre-release: + uses: abbgrade/PsBuildTasks/.github/workflows/pre-release-windows.yml@1.4.0 + with: + module-name: PsSmo + secrets: + ps-gallery-key: ${{ secrets.PS_GALLERY_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..6793b1f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,12 @@ +on: + push: + branches: [ main ] + workflow_dispatch: + +jobs: + release: + uses: abbgrade/PsBuildTasks/.github/workflows/release-windows.yml@1.4.0 + with: + module-name: PsSmo + secrets: + ps-gallery-key: ${{ secrets.PS_GALLERY_KEY }} diff --git a/.gitignore b/.gitignore index a248c98..771fd8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +publish bin obj coverage.xml diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..5c47120 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-vscode.powershell", + "vector-of-bool.gitflow" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 26a164f..d4cd7e8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,6 @@ "files.insertFinalNewline": true, "editor.tabSize": 4, "editor.formatOnSave": false, - "powershell.codeFormatting.addWhitespaceAroundPipe": true + "powershell.codeFormatting.addWhitespaceAroundPipe": true, + "cSpell.language": "en" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 060e1a5..f95aa31 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,6 +1,6 @@ -// Do not edit! This file is generated by New-VSCodeTask.ps1 -// Modify the build script or tasks-merge.json and recreate. { + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format "version": "2.0.0", "windows": { "options": { @@ -8,9 +8,8 @@ "executable": "pwsh.exe", "args": [ "-NoProfile", + "-ExecutionPolicy Bypass", "-NonInteractive", - "-ExecutionPolicy", - "Bypass", "-Command" ] } @@ -20,10 +19,7 @@ "options": { "shell": { "executable": "/usr/bin/pwsh", - "args": [ - "-NoProfile", - "-Command" - ] + "args": [ "-NoProfile", "-Command" ] } } }, @@ -31,83 +27,50 @@ "options": { "shell": { "executable": "/usr/local/bin/pwsh", - "args": [ - "-NoProfile", - "-Command" - ] + "args": [ "-NoProfile", "-Command" ] } } }, "tasks": [ { - "label": "build", - "group": "build", - "type": "shell", - "command": "Invoke-Build", - "args": ["build"], - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - } - }, - { - "label": "clean", + "label": "Build", "type": "shell", - "command": "Invoke-Build", - "args": ["clean"], - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - } + "group": "build", + "command": "Invoke-Build Build", + "problemMatcher": [] }, { - "label": "test", - "group": "test", + "label": "Clean", "type": "shell", - "command": "Invoke-Pester", - "args": ["-Output", "Detailed"], - "options": { - "cwd": "test" - }, - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "dependsOn": "build" + "command": "Invoke-Build Clean", + "problemMatcher": [] }, { - "label": "install", + "label": "Install", "type": "shell", - "command": "Invoke-Build", - "args": ["install", "-Configuration", "Release"], - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - } + "command": "Invoke-Build Install", + "problemMatcher": [] }, { - "label": ".", + "label": "Update docs", "type": "shell", - "command": "Invoke-Build -Task .", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - } + "command": "Invoke-Build UpdateDocs", + "problemMatcher": [] }, { - "label": "?", + "label": "Test", + "group": "test", "type": "shell", - "command": "Invoke-Build -Task ?", + "command": "Invoke-Pester", + "options": { + "cwd": "${workspaceFolder}/test" + }, "problemMatcher": "$msCompile", "presentation": { "echo": false, "showReuseMessage": false - } + }, + "dependsOn": "Build" } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..45d2251 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,68 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.0.0] - 2022-08-18 + +### Added + +- Added Get-Table command. +- Added output to all commands. + +### Changed + +- Updated to net6.0 +- Updated from System.Data.SqlClient to Microsoft.Data.SqlClient. +- Updated from PowerShellStandard.Library to System.Management.Automation +- Updated Microsoft.SqlServer.SqlManagementObjects +- Added pipeline input for `Disconnect-Instance` + +### Fixed + +- ErrorAction parameter works for `Invoke-Command`. + +## [0.5.0] - 2021-10-15 + +### Added + +- Added sql command output handler. +- Added support for line commends in scripts. + +### Fixed + +- Fixed variables with quoted values. + +## [0.4.0] - 2021-09-27 + +### Added + +- Added more parameter sets for `Connect-SmoInstance`. + +### Fixed + +- Fixed connection exception for Azure SQL. + +## [0.3.0] - 2021-09-16 + +### Added + +- Added `$(Variable)` and `:setvar Variable Value` support. + +## [0.2.0] - 2021-09-15 + +### Added + +- Added `Invoke-SmoCommand` with batch support (`GO` statement). + +## [0.1.0] - 2021-09-12 + +### Added + +- Added `Connect-SmoInstance` and `Disconnect-SmoInstance` commands. + + diff --git a/PsSmo.build.ps1 b/PsSmo.build.ps1 deleted file mode 100644 index f16aca3..0000000 --- a/PsSmo.build.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -<# -.Synopsis - Build script - -.Example - Invoke-Build Publish -Configuration Release -NuGetApiKey xyz -#> - -param( - [ValidateSet('Debug', 'Release')] - [string] $Configuration = 'Debug', - - [string] $NuGetApiKey = $env:nuget_apikey -) - -. $PSScriptRoot\tasks\Build.Tasks.ps1 - -# Synopsis: Default task. -task . Build diff --git a/Readme.md b/Readme.md index 0191a8c..e57cc9b 100644 --- a/Readme.md +++ b/Readme.md @@ -42,28 +42,7 @@ Execute SQLCMD scripts like those created by [DacFX](https://github.com/microsof ## Changelog -### 0.1.0 - -- Added `Connect-SmoInstance` and `Disconnect-SmoInstance` commands. - -### 0.2.0 - -- Added `Invoke-SmoCommand` with batch support (`GO` statement). - -### 0.3.0 - -- Added `$(Variable)` and `:setvar Variable Value` support. - -### 0.4.0 - -- Added more parameter sets for `Connect-SmoInstance`. -- Fixed connection exception for Azure SQL. - -### 0.5.0 - -- Added sql command output handler. -- Fixed variables with quoted values. -- Added support for line commends in scripts. +See the [changelog](./CHANGELOG.md) file. ## Development @@ -72,6 +51,13 @@ Execute SQLCMD scripts like those created by [DacFX](https://github.com/microsof - Build automation is based on [InvokeBuild](https://github.com/nightroman/Invoke-Build) - Test automation is based on [Pester](https://pester.dev) - Commands are named based on [Approved Verbs for PowerShell Commands](https://docs.microsoft.com/de-de/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands) +- This project uses [git-flow](https://github.com/nvie/gitflow). +- This project uses [keep a changelog](https://keepachangelog.com/en/1.0.0/). +- This project uses [PsBuildTasks](https://github.com/abbgrade/PsBuildTasks). + +### Status + +[![.github/workflows/build-validation.yml](https://github.com/abbgrade/PsSmo/actions/workflows/build-validation.yml/badge.svg?branch=develop)](https://github.com/abbgrade/PsSmo/actions/workflows/build-validation.yml) ### Build @@ -88,9 +74,12 @@ The InvokeBuild test tasks are for CI and do not generate console output. ### Release -1. Create release branch using gitflow. -2. Update version number in psd1 file. -3. Update changelog in this readme file. -4. Uninstall old versions. -5. publish release using `Invoke-Build Publish`. -6. finish release using gitflow. +1. Create a release branch using git-flow. +2. Update the version number in the module manifest. +3. Extend the changelog in this readme. +4. If you want to create a pre-release. + 1. Push the release branch to github, to publish the pre-release to PsGallery. +5. Finish release using git-flow. +6. Check if tags are not pushed to github. +7. Check if the release branch is deleted on github. +8. Create the release on github. diff --git a/docs/Connect-SmoInstance.md b/docs/Connect-SmoInstance.md new file mode 100644 index 0000000..d1a2f70 --- /dev/null +++ b/docs/Connect-SmoInstance.md @@ -0,0 +1,173 @@ +--- +external help file: PsSmo.dll-Help.xml +Module Name: PsSmo +online version: +schema: 2.0.0 +--- + +# Connect-SmoInstance + +## SYNOPSIS +{{ Fill in the Synopsis }} + +## SYNTAX + +### SqlClient +``` +Connect-SmoInstance -Connection [] +``` + +### ConnectionString +``` +Connect-SmoInstance [-ConnectionString] [] +``` + +### Properties_IntegratedSecurity +``` +Connect-SmoInstance [-DataSource] [[-InitialCatalog] ] [-AccessToken ] + [] +``` + +### Properties_SQLServerAuthentication +``` +Connect-SmoInstance [-DataSource] [[-InitialCatalog] ] [-UserId] + [-Password] [] +``` + +## DESCRIPTION +{{ Fill in the Description }} + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> {{ Add example code here }} +``` + +{{ Add example description here }} + +## PARAMETERS + +### -AccessToken +{{ Fill AccessToken Description }} + +```yaml +Type: String +Parameter Sets: Properties_IntegratedSecurity +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Connection +{{ Fill Connection Description }} + +```yaml +Type: SqlConnection +Parameter Sets: SqlClient +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -ConnectionString +{{ Fill ConnectionString Description }} + +```yaml +Type: String +Parameter Sets: ConnectionString +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) +Accept wildcard characters: False +``` + +### -DataSource +{{ Fill DataSource Description }} + +```yaml +Type: String +Parameter Sets: Properties_IntegratedSecurity, Properties_SQLServerAuthentication +Aliases: Server, ServerName, ServerInstance + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -InitialCatalog +{{ Fill InitialCatalog Description }} + +```yaml +Type: String +Parameter Sets: Properties_IntegratedSecurity, Properties_SQLServerAuthentication +Aliases: Database, DatabaseName + +Required: False +Position: 1 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Password +{{ Fill Password Description }} + +```yaml +Type: SecureString +Parameter Sets: Properties_SQLServerAuthentication +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) +Accept wildcard characters: False +``` + +### -UserId +{{ Fill UserId Description }} + +```yaml +Type: String +Parameter Sets: Properties_SQLServerAuthentication +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### Microsoft.Data.SqlClient.SqlConnection + +### System.String + +### System.Security.SecureString + +## OUTPUTS + +### Microsoft.SqlServer.Management.Smo.Server + +## NOTES + +## RELATED LINKS diff --git a/docs/Disconnect-SmoInstance.md b/docs/Disconnect-SmoInstance.md new file mode 100644 index 0000000..d7e453c --- /dev/null +++ b/docs/Disconnect-SmoInstance.md @@ -0,0 +1,61 @@ +--- +external help file: PsSmo.dll-Help.xml +Module Name: PsSmo +online version: +schema: 2.0.0 +--- + +# Disconnect-SmoInstance + +## SYNOPSIS +{{ Fill in the Synopsis }} + +## SYNTAX + +``` +Disconnect-SmoInstance [-Instance ] [] +``` + +## DESCRIPTION +{{ Fill in the Description }} + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> {{ Add example code here }} +``` + +{{ Add example description here }} + +## PARAMETERS + +### -Instance +{{ Fill Instance Description }} + +```yaml +Type: Server +Parameter Sets: (All) +Aliases: Connection + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### Microsoft.SqlServer.Management.Smo.Server + +## NOTES + +## RELATED LINKS diff --git a/docs/Get-SmoTable.md b/docs/Get-SmoTable.md new file mode 100644 index 0000000..49962e9 --- /dev/null +++ b/docs/Get-SmoTable.md @@ -0,0 +1,76 @@ +--- +external help file: PsSmo.dll-Help.xml +Module Name: PsSmo +online version: +schema: 2.0.0 +--- + +# Get-SmoTable + +## SYNOPSIS +{{ Fill in the Synopsis }} + +## SYNTAX + +``` +Get-SmoTable [-Name ] [-Instance ] [] +``` + +## DESCRIPTION +{{ Fill in the Description }} + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> {{ Add example code here }} +``` + +{{ Add example description here }} + +## PARAMETERS + +### -Instance +{{ Fill Instance Description }} + +```yaml +Type: Server +Parameter Sets: (All) +Aliases: Connection + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name +{{ Fill Name Description }} + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### Microsoft.SqlServer.Management.Smo.Table + +## NOTES + +## RELATED LINKS diff --git a/docs/Invoke-SmoCommand.md b/docs/Invoke-SmoCommand.md new file mode 100644 index 0000000..6bf244f --- /dev/null +++ b/docs/Invoke-SmoCommand.md @@ -0,0 +1,113 @@ +--- +external help file: PsSmo.dll-Help.xml +Module Name: PsSmo +online version: +schema: 2.0.0 +--- + +# Invoke-SmoCommand + +## SYNOPSIS +{{ Fill in the Synopsis }} + +## SYNTAX + +### Text +``` +Invoke-SmoCommand -Text [-Variables ] [-Instance ] [] +``` + +### File +``` +Invoke-SmoCommand -InputFile [-Variables ] [-Instance ] [] +``` + +## DESCRIPTION +{{ Fill in the Description }} + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> {{ Add example code here }} +``` + +{{ Add example description here }} + +## PARAMETERS + +### -InputFile +{{ Fill InputFile Description }} + +```yaml +Type: FileInfo +Parameter Sets: File +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Instance +{{ Fill Instance Description }} + +```yaml +Type: Server +Parameter Sets: (All) +Aliases: Connection + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Text +{{ Fill Text Description }} + +```yaml +Type: String +Parameter Sets: Text +Aliases: Command + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Variables +{{ Fill Variables Description }} + +```yaml +Type: Hashtable +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### System.String + +### System.IO.FileInfo + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..fff4ab9 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..d9f8841 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,9 @@ +# Index + +
    +{% for page in site.pages %} +{% if page.title and page.title != 'Index' %} +
  • {{ page.title }}
  • +{% endif %} +{% endfor %} +
diff --git a/src/PsSmo/ClientCommand.cs b/src/PsSmo/ClientCommand.cs new file mode 100644 index 0000000..0bec67d --- /dev/null +++ b/src/PsSmo/ClientCommand.cs @@ -0,0 +1,78 @@ +using Microsoft.SqlServer.Management.Common; +using Microsoft.SqlServer.Management.Smo; +using Microsoft.Data.SqlClient; +using System.Management.Automation; + +namespace PsSmo +{ + public abstract class ClientCommand : PSCmdlet + { + [Parameter()] + [Alias("Connection")] + public Server Instance { get; set; } = ConnectInstanceCommand.Instance; + + protected override void BeginProcessing() + { + base.BeginProcessing(); + + Instance.ConnectionContext.InfoMessage += ConnectionContext_InfoMessage; + Instance.ConnectionContext.RemoteLoginFailed += ConnectionContext_RemoteLoginFailed; + Instance.ConnectionContext.ServerMessage += ConnectionContext_ServerMessage; + Instance.ConnectionContext.StateChange += ConnectionContext_StateChange; + Instance.ConnectionContext.StatementExecuted += ConnectionContext_StatementExecuted; + } + + protected override void EndProcessing() + { + base.EndProcessing(); + + Instance.ConnectionContext.InfoMessage -= ConnectionContext_InfoMessage; + Instance.ConnectionContext.RemoteLoginFailed -= ConnectionContext_RemoteLoginFailed; + Instance.ConnectionContext.ServerMessage -= ConnectionContext_ServerMessage; + Instance.ConnectionContext.StateChange -= ConnectionContext_StateChange; + Instance.ConnectionContext.StatementExecuted -= ConnectionContext_StatementExecuted; + } + + protected override void ProcessRecord() + { + base.ProcessRecord(); + + if (Instance == null) + throw new PSArgumentNullException(nameof(Instance), $"run Connect-SmoInstance"); + } + + private void ConnectionContext_RemoteLoginFailed(object sender, ServerMessageEventArgs e) + { + WriteWarning(e.ToString()); + } + + private void ConnectionContext_StatementExecuted(object sender, StatementEventArgs e) + { + WriteInformation(messageData: e.SqlStatement, tags: new string[] { "SqlStatement" }); + } + + private void ConnectionContext_StateChange(object sender, System.Data.StateChangeEventArgs e) + { + WriteVerbose($"Database state changed from {e.OriginalState} to {e.CurrentState}."); + } + + private void ConnectionContext_ServerMessage(object sender, ServerMessageEventArgs e) + { + switch (e.Error.Class) + { + case 0: + break; // handled in ConnectionContext_InfoMessage + default: + WriteWarning($"{e.Error.Class}: {e.Error}"); + break; + } + } + + private void ConnectionContext_InfoMessage(object sender, SqlInfoMessageEventArgs e) + { + var message = e.Message.Trim(); + if (message.Length > 0) + WriteVerbose(message); + } + } +} diff --git a/src/PsSmo/ConnectInstanceCommand.cs b/src/PsSmo/ConnectInstanceCommand.cs index 0108eaa..f2f2189 100644 --- a/src/PsSmo/ConnectInstanceCommand.cs +++ b/src/PsSmo/ConnectInstanceCommand.cs @@ -2,7 +2,7 @@ using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Common; using System.Management.Automation; -using System.Data.SqlClient; +using Microsoft.Data.SqlClient; using Microsoft.Azure.Services.AppAuthentication; using System.Security; @@ -14,6 +14,8 @@ public class ConnectInstanceCommand : PSCmdlet { internal static Server Instance { get; set; } + #region Parameters + [Parameter( Mandatory = true, ValueFromPipeline = true, @@ -88,8 +90,11 @@ public class ConnectInstanceCommand : PSCmdlet [ValidateNotNullOrEmpty()] public SecureString Password { get; set; } + #endregion + protected override void ProcessRecord() { + base.ProcessRecord(); switch (ParameterSetName) { @@ -168,49 +173,7 @@ protected override void ProcessRecord() break; } - Instance.ConnectionContext.InfoMessage += ConnectionContext_InfoMessage; - Instance.ConnectionContext.RemoteLoginFailed += ConnectionContext_RemoteLoginFailed; - Instance.ConnectionContext.ServerMessage += ConnectionContext_ServerMessage; - Instance.ConnectionContext.StateChange += ConnectionContext_StateChange; - Instance.ConnectionContext.StatementExecuted += ConnectionContext_StatementExecuted; - WriteObject(Instance); } - - private void ConnectionContext_RemoteLoginFailed(object sender, ServerMessageEventArgs e) - { - WriteWarning(e.ToString()); - } - - private void ConnectionContext_StatementExecuted(object sender, StatementEventArgs e) - { - WriteInformation(messageData: e.SqlStatement, tags: new string[] { "SqlStatement" }); - } - - private void ConnectionContext_StateChange(object sender, System.Data.StateChangeEventArgs e) - { - WriteVerbose($"Database state changed from {e.OriginalState} to {e.CurrentState}."); - } - - private void ConnectionContext_ServerMessage(object sender, ServerMessageEventArgs e) - { - switch(e.Error.Class) - { - case 0: - break; // handled in ConnectionContext_InfoMessage - default: - WriteWarning($"{e.Error.Class}: {e.Error}"); - break; - } - - } - - private void ConnectionContext_InfoMessage(object sender, SqlInfoMessageEventArgs e) - { - var message = e.Message.Trim(); - if (message.Length > 0) - WriteVerbose(message); - } - } } diff --git a/src/PsSmo/DisconnectInstanceCommand.cs b/src/PsSmo/DisconnectInstanceCommand.cs index 51634c1..031ef9c 100644 --- a/src/PsSmo/DisconnectInstanceCommand.cs +++ b/src/PsSmo/DisconnectInstanceCommand.cs @@ -1,30 +1,26 @@  using Microsoft.SqlServer.Management.Smo; -using Microsoft.SqlServer.Management.Common; using System.Management.Automation; -using System.Data.SqlClient; namespace PsSmo { [Cmdlet(VerbsCommunications.Disconnect, "Instance")] [OutputType(typeof(Server))] - public class DisconnectInstanceCommand : PSCmdlet + public class DisconnectInstanceCommand : ClientCommand { + [Parameter( + Position = 0, ValueFromPipeline = true )] - public Server Instance { get; set; } - - protected override void BeginProcessing() - { - base.BeginProcessing(); - - if (Instance == null) - Instance = ConnectInstanceCommand.Instance; - } + [ValidateNotNullOrEmpty()] + [Alias("Connection")] + public Server Instance { get; set; } = ConnectInstanceCommand.Instance; protected override void ProcessRecord() { + base.ProcessRecord(); + Instance.ConnectionContext.Disconnect(); } diff --git a/src/PsSmo/GetTableCommand.cs b/src/PsSmo/GetTableCommand.cs new file mode 100644 index 0000000..185d92c --- /dev/null +++ b/src/PsSmo/GetTableCommand.cs @@ -0,0 +1,26 @@ +using System.Management.Automation; +using System.Linq; +using Microsoft.SqlServer.Management.Smo; +using System.Collections.Generic; + +namespace PsSmo +{ + [Cmdlet(VerbsCommon.Get, "Table")] + [OutputType(typeof(Table))] + public class GetTableCommand : ClientCommand + { + [Parameter()] + public string Name { get; set; } + + protected override void ProcessRecord() + { + base.ProcessRecord(); + + foreach (Table table in Instance.Databases[Instance.ConnectionContext.CurrentDatabase].Tables) + { + if (string.IsNullOrEmpty(Name) || table.Name == Name) + WriteObject(table); + } + } + } +} diff --git a/src/PsSmo/InvokeCommandCommand.cs b/src/PsSmo/InvokeCommandCommand.cs index 61dac88..93a5bce 100644 --- a/src/PsSmo/InvokeCommandCommand.cs +++ b/src/PsSmo/InvokeCommandCommand.cs @@ -10,13 +10,9 @@ namespace PsSmo { [Cmdlet(VerbsLifecycle.Invoke, "Command")] - public class InvokeCommandCommand : PSCmdlet + public class InvokeCommandCommand : ClientCommand { - [Parameter( - Mandatory = false - )] - [Alias("Connection")] - public Server Instance { get; set; } = ConnectInstanceCommand.Instance; + #region Parameters [Parameter( ParameterSetName = "Text", @@ -36,6 +32,8 @@ public class InvokeCommandCommand : PSCmdlet [Parameter()] public Hashtable Variables { get; set; } = new Hashtable(); + #endregion + protected override void ProcessRecord() { base.ProcessRecord(); @@ -43,16 +41,24 @@ protected override void ProcessRecord() switch (ParameterSetName) { case "Text": + WriteVerbose("Execute SQL script from text."); break; case "File": + WriteVerbose("Execute SQL script from file."); Text = File.ReadAllText(InputFile.FullName); break; default: throw new NotImplementedException($"ParameterSetName {ParameterSetName} is not implemented"); } - Instance.ConnectionContext.ExecuteNonQuery(sqlCommand: processSqlCmdText(Text, processVariables(Variables))); + try + { + Instance.ConnectionContext.ExecuteNonQuery(sqlCommand: processSqlCmdText(Text, processVariables(Variables))); + } catch (Exception ex) + { + WriteError(new ErrorRecord(ex, ex.GetType().Name, ErrorCategory.NotSpecified, Text)); + } } private Dictionary processVariables(Hashtable variables) diff --git a/src/PsSmo/PsSmo.csproj b/src/PsSmo/PsSmo.csproj index d25d1ef..eba3e2c 100644 --- a/src/PsSmo/PsSmo.csproj +++ b/src/PsSmo/PsSmo.csproj @@ -1,15 +1,15 @@ - netcoreapp2.1 + net6.0 PsSmo - - - - + + + + diff --git a/src/PsSmo/PsSmo.psd1 b/src/PsSmo/PsSmo.psd1 index 9640e53..543507f 100644 --- a/src/PsSmo/PsSmo.psd1 +++ b/src/PsSmo/PsSmo.psd1 @@ -1,28 +1,136 @@ +# +# Module manifest for module 'PsSmo' +# +# Generated by: Steffen Kampmann +# +# Generated on: 3/23/2022 +# + @{ - RootModule = 'PsSmo.dll' - ModuleVersion = '0.5.0' - DefaultCommandPrefix = 'Smo' - PowerShellVersion = '7.0' - - GUID = 'e800d6be-fb78-4e78-8c9e-a80fce7a139c' - Author = 'Steffen Kampmann' - Copyright = '(c) 2021 Steffen Kampmann. Alle Rechte vorbehalten.' - Description = 'The PowerShell SQL Client module replaces the SQL Server utilities SQLCMD with native PowerShell commands.' - - CmdletsToExport = @( - 'Connect-Instance', - 'Disconnect-Instance', - 'Invoke-Command' - ) - - PrivateData = @{ - - PSData = @{ - Category = 'Databases' - Tags = @('sql', 'sqlserver', 'sqlclient') - LicenseUri = 'https://github.com/abbgrade/PsSmo/blob/main/LICENSE' - ProjectUri = 'https://github.com/abbgrade/PsSmo' - IsPrerelease = 'True' - } - } + +# Script module or binary module file associated with this manifest. +RootModule = 'PsSmo.dll' + +# Version number of this module. +ModuleVersion = '1.0.0' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = 'e800d6be-fb78-4e78-8c9e-a80fce7a139c' + +# Author of this module +Author = 'Steffen Kampmann' + +# Company or vendor of this module +CompanyName = '' + +# Copyright statement for this module +Copyright = '(c) 2021 Steffen Kampmann. Alle Rechte vorbehalten.' + +# Description of the functionality provided by this module +Description = 'The PowerShell SQL Client module replaces the SQL Server utilities SQLCMD with native PowerShell commands.' + +# Minimum version of the PowerShell engine required by this module +PowerShellVersion = '7.0' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = '*' + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = 'Connect-Instance', 'Disconnect-Instance', 'Get-Table', + 'Invoke-Command' + +# Variables to export from this module +# VariablesToExport = @() + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = '*' + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# 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. +PrivateData = @{ + + #Category of this module + Category = 'Databases' + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'sql','sqlserver','sqlclient' + + # A URL to the license for this module. + LicenseUri = 'https://github.com/abbgrade/PsSmo/blob/main/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/abbgrade/PsSmo' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +DefaultCommandPrefix = 'Smo' + } + diff --git a/tasks/Build.Tasks.ps1 b/tasks/Build.Tasks.ps1 index 0f12ac4..26bbbf0 100644 --- a/tasks/Build.Tasks.ps1 +++ b/tasks/Build.Tasks.ps1 @@ -1,44 +1,76 @@ -requires Configuration - -[System.IO.FileInfo] $global:Manifest = "$PSScriptRoot/../src/PsSmo/bin/$Configuration/netcoreapp2.1/publish/PsSmo.psd1" - -# Synopsis: Build project. -task Build { - exec { dotnet publish ./src/PsSmo -c $Configuration } -} - -# Synopsis: Remove temporary files. -task Clean { - remove src/PsSmo/bin, src/PsSmo/obj -} - -# Synopsis: Generate documentation. -task Docs -Jobs Build, { - Import-Module $global:Manifest - - if ( Test-Path ./docs -PathType Container ) { - Update-MarkdownHelp ./docs - } else { - New-MarkdownHelp -Module PsSmo -OutputFolder ./docs - } -} - -# Synopsis: Install the module. -task Install -Jobs Build, { - $info = Import-PowerShellDataFile $global:Manifest.FullName - $version = ([System.Version] $info.ModuleVersion) - $name = $global:Manifest.BaseName - $defaultModulePath = $env:PsModulePath -split ';' | Select-Object -First 1 - Write-Verbose "install $name $version to $defaultModulePath" - $installPath = Join-Path $defaultModulePath $name $version.ToString() - New-Item -Type Directory $installPath -Force | Out-Null - Get-ChildItem $global:Manifest.Directory | Copy-Item -Destination $installPath -Recurse -Force -} - -# Synopsis: Publish the module to PSGallery. -task Publish -Jobs Install, { - - assert ( $Configuration -eq 'Release' ) - - Publish-Module -Name PsSmo -NuGetApiKey $NuGetApiKey -} +requires Configuration +requires ModuleName + +[System.IO.DirectoryInfo] $PublishDirectory = "$PSScriptRoot/../publish" +[System.IO.DirectoryInfo] $SourceDirectory = "$PSScriptRoot/../src" +[System.IO.DirectoryInfo] $DocumentationDirectory = "$PSScriptRoot/../docs" +[System.IO.DirectoryInfo] $ModulePublishDirectory = "$PublishDirectory/$ModuleName" +[System.IO.DirectoryInfo] $ModuleSourceDirectory = "$SourceDirectory/$ModuleName" +[System.IO.DirectoryInfo] $BinaryDirectory = "$ModuleSourceDirectory/bin" +[System.IO.DirectoryInfo] $ObjectDirectory = "$ModuleSourceDirectory/obj" + +# Synopsis: Set the prerelease in the manifest based on the build number. +task SetPrerelease -If $BuildNumber { + $Global:PreRelease = "alpha$( '{0:d4}' -f $BuildNumber )" + Update-ModuleManifest -Path $Global:Manifest -Prerelease $Global:PreRelease +} + +# Synopsis: Build the dll with the module commands. +task Build.Dll -Jobs { + exec { dotnet publish $ModuleSourceDirectory -c $Configuration -o $ModulePublishDirectory } + $Global:Manifest = Get-Item $ModulePublishDirectory/$ModuleName.psd1 +}, SetPrerelease + +# Synopsis: Import the module. +task Import -Jobs Build.Dll, { + Import-Module $Global:Manifest +} + +# Synopsis: Import platyPs. +task Import.platyPs -Jobs { + Import-Module platyPs +} + +# Synopsis: Initialize the documentation. +task Doc.Init -If { $DocumentationDirectory.Exists -eq $false -Or $ForceDocInit -eq $true } -Jobs Import, Import.platyPs, { + New-Item $DocumentationDirectory -ItemType Directory -ErrorAction SilentlyContinue + New-MarkdownHelp -Module $ModuleName -OutputFolder $DocumentationDirectory -Force:$ForceDocInit -ErrorAction Stop +} + +# Synopsis: Update the markdown documentation. +task Doc.Update -Jobs Import, Import.platyPs, Doc.Init, { + Update-MarkdownHelp -Path $DocumentationDirectory +} + +# Synopsis: Build the XML help based on the markdown documentation. +task Build.Help -Jobs Import.platyPs, Doc.Update, { + New-ExternalHelp -Path $DocumentationDirectory -OutputPath $ModulePublishDirectory\en-US\ -Force +} + +# Synopsis: Build the module. +task Build -Job Build.Dll, Build.Help + +# Synopsis: Remove all temporary files. +task Clean { + remove $BinaryDirectory, $ObjectDirectory, $PublishDirectory +} + +# Synopsis: Install the module. +task Install -Jobs Build, { + $info = Import-PowerShellDataFile $Global:Manifest + $version = ([System.Version] $info.ModuleVersion) + $defaultModulePath = $env:PsModulePath -split ';' | Select-Object -First 1 + Write-Verbose "install $ModuleName $version to $defaultModulePath" + $installPath = Join-Path $defaultModulePath $ModuleName $version.ToString() + New-Item -Type Directory $installPath -Force | Out-Null + Get-ChildItem $Global:Manifest.Directory | Copy-Item -Destination $installPath -Recurse -Force +} + +# Synopsis: Publish the module to PSGallery. +task Publish -Jobs Clean, Build, { + if ( -Not $Global:PreRelease ) { + assert ( $Configuration -eq 'Release' ) + Update-ModuleManifest -Path $Global:Manifest -Prerelease '' + } + Publish-Module -Path $Global:Manifest.Directory -NuGetApiKey $NuGetApiKey -Force:$ForcePublish +} diff --git a/tasks/Dependencies.Tasks.ps1 b/tasks/Dependencies.Tasks.ps1 new file mode 100644 index 0000000..201b88e --- /dev/null +++ b/tasks/Dependencies.Tasks.ps1 @@ -0,0 +1,13 @@ +task InstallBuildDependencies -Jobs { + Install-Module platyPs -Scope CurrentUser -ErrorAction Stop -Verbose +} + +task InstallTestDependencies -Jobs { + Install-Module PsSqlClient -Scope CurrentUser -ErrorAction Stop -AllowPrerelease -AllowClobber -Verbose + Install-Module PsSqlLocalDb -Scope CurrentUser -AllowPrerelease -Verbose + Install-Module psdocker -Scope CurrentUser -AllowPrerelease -Verbose + Install-Module PsSqlTestServer -Scope CurrentUser -AllowPrerelease -Verbose +} + +task InstallReleaseDependencies -Jobs { +} diff --git a/tasks/PsBuild.Tasks.ps1 b/tasks/PsBuild.Tasks.ps1 new file mode 100644 index 0000000..98a60cc --- /dev/null +++ b/tasks/PsBuild.Tasks.ps1 @@ -0,0 +1,88 @@ +#region InvokeBuild + +task UpdateBuildTasks { + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/DotNet/Build.Tasks.ps1' ` + -OutFile "$PSScriptRoot\Build.Tasks.ps1" +} + +#endregion +#region GitHub Actions + +task UpdateValidationWorkflow { + [System.IO.FileInfo] $file = "$PSScriptRoot/../.github/workflows/build-validation.yml" + New-Item -Type Directory $file.Directory -ErrorAction SilentlyContinue + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/GitHub/build-validation-matrix.yml' ` + -OutFile $file +} + +task UpdatePreReleaseWorkflow { + requires ModuleName + [System.IO.FileInfo] $file = "$PSScriptRoot\..\.github\workflows\pre-release.yml" + New-Item -Type Directory $file.Directory -ErrorAction SilentlyContinue + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/GitHub/pre-release-windows.yml' | + ForEach-Object { $_ -replace 'MyModuleName', $ModuleName } | + Out-File $file -NoNewline +} + +task UpdateReleaseWorkflow { + requires ModuleName + [System.IO.FileInfo] $file = "$PSScriptRoot\..\.github\workflows\release.yml" + New-Item -Type Directory $file.Directory -ErrorAction SilentlyContinue + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/GitHub/release-windows.yml' | + ForEach-Object { $_ -replace 'MyModuleName', $ModuleName } | + Out-File $file -NoNewline +} + +#endregion +#region GitHub Pages + +task UpdateIndexPage { + New-Item -Type Directory "$PSScriptRoot\..\docs" -ErrorAction SilentlyContinue + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/docs/index.md' ` + -OutFile "$PSScriptRoot\..\docs\index.md" + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/docs/_config.yml' ` + -OutFile "$PSScriptRoot\..\docs\_config.yml" +} + +#endregion +#region GitHub Dependabot + +task UpdateDependabotConfig { + requires ModuleName + [System.IO.FileInfo] $file = "$PSScriptRoot\..\.github\dependabot.yml" + New-Item -Type Directory $file.Directory -ErrorAction SilentlyContinue + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/dependabot/dependabot.yml' | + ForEach-Object { $_ -replace 'MyModuleName', $ModuleName } | + Out-File $file -NoNewline +} + +#endregion +#region VsCode + +task UpdateVsCodeTasks { + [System.IO.FileInfo] $file = "$PSScriptRoot\..\.vscode\tasks.json" + New-Item -Type Directory $file.Directory -ErrorAction SilentlyContinue + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/VsCode/tasks.json' ` + -OutFile $file +} + +#endregion +#region PsBuildTasks + +task UpdatePsBuildTasksTasks { + Invoke-WebRequest ` + -Uri 'https://raw.githubusercontent.com/abbgrade/PsBuildTasks/main/tasks/Dotnet-Matrix.Tasks.ps1' ` + -OutFile "$PSScriptRoot\PsBuild.Tasks.ps1" +} + +#endregion + +task UpdatePsBuildTasks -Jobs UpdateBuildTasks, UpdateValidationWorkflow, UpdatePreReleaseWorkflow, UpdateReleaseWorkflow, UpdateIndexPage, UpdateDependabotConfig, UpdateVsCodeTasks, UpdatePsBuildTasksTasks diff --git a/test/Connect-Instance.Tests.ps1 b/test/Connect-Instance.Tests.ps1 index f1ad357..f495014 100644 --- a/test/Connect-Instance.Tests.ps1 +++ b/test/Connect-Instance.Tests.ps1 @@ -1,84 +1,48 @@ -#Requires -Modules @{ ModuleName='Pester'; ModuleVersion='5.0.0' } +#Requires -Modules @{ ModuleName='Pester'; ModuleVersion='5.0.0' }, @{ ModuleName='PsSqlTestServer'; ModuleVersion='1.2.0' } -Describe 'Connect-Instance' { - - BeforeDiscovery { - $script:missingSqlclient = $true - $local:psSqlclient = Get-Module -ListAvailable -Name PsSqlClient - if ( $local:psSqlclient ) { - Import-Module PsSqlClient - $script:missingSqlclient = $false - } - } +Describe Connect-Instance { BeforeAll { - Import-Module -Name $PSScriptRoot/../src/PsSmo/bin/Debug/netcoreapp2.1/publish/PsSmo.psd1 -Force -ErrorAction 'Stop' + Import-Module $PSScriptRoot/../publish/PsSmo/PsSmo.psd1 -Force -ErrorAction Stop } - Context 'LocalDb' -Tag LocalDb { + Context SqlInstance { BeforeAll { - $script:missingLocalDb = $true - foreach( $version in Get-ChildItem -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server Local DB\Installed Versions' | Sort-Object Name -Descending ) { - if ( $script:missingLocalDb ) { - switch ( $version.PSChildName ) { - '11.0' { - $script:DataSource = '(localdb)\v11.0' - $script:missingLocalDb = $false - break; - } - '13.0' { - $script:DataSource = '(LocalDb)\MSSQLLocalDB' - $script:missingLocalDb = $false - break; - } - '15.0' { - $script:DataSource = '(LocalDb)\MSSQLLocalDB' - $script:missingLocalDb = $false - break; - } - Default { - Write-Warning "LocalDb version $_ is not implemented." - } - } - } - } + $SqlInstance = New-SqlTestInstance -ErrorAction Stop + $SqlInstanceConnection = $SqlInstance | Connect-TSqlInstance } - AfterEach { - if ( $script:Instance ) { - $script:Instance | Disconnect-SmoInstance + AfterAll { + if ( $SqlInstance ) { + $SqlInstance | Remove-SqlTestInstance } - } - - Context 'SqlClient' -Skip:$script:missingSqlclient { - - BeforeAll { - $script:Connection = Connect-TSqlInstance -DataSource $script:DataSource + if ( $SqlInstanceConnection ) { + Disconnect-TSqlInstance -Connection $SqlInstanceConnection -ErrorAction Continue } + } - AfterAll { - if ( $script:Connection ) { - $script:Connection | Disconnect-TSqlInstance - } - } + It 'Returns a connection by pipeline input' { + $SmoConnection = $SqlInstanceConnection | Connect-SmoInstance - It 'Returns a connection' { - $script:Instance = $script:Connection | Connect-SmoInstance + $SmoConnection | Should -Not -BeNullOrEmpty + $SmoConnection.Refresh() + $SmoConnection.Edition | Should -Not -BeNullOrEmpty + $SmoConnection.ConnectionContext.IsOpen | Should -be $true + } - $script:Instance | Should -Not -BeNullOrEmpty - $script:Instance.Refresh() - $script:Instance.Edition | Should -Not -BeNullOrEmpty - $script:Instance.ConnectionContext.IsOpen | Should -be $true - } + It 'Returns a connection by property' { + $SmoConnection = Connect-SmoInstance -Connection $SqlInstanceConnection - It 'Returns a connection by property' { - $script:Instance = Connect-SmoInstance -Connection $script:Connection + $SmoConnection | Should -Not -BeNullOrEmpty + $SmoConnection.Refresh() + $SmoConnection.Edition | Should -Not -BeNullOrEmpty + $SmoConnection.ConnectionContext.IsOpen | Should -be $true + } - $script:Instance | Should -Not -BeNullOrEmpty - $script:Instance.Refresh() - $script:Instance.Edition | Should -Not -BeNullOrEmpty - $script:Instance.ConnectionContext.IsOpen | Should -be $true + AfterEach { + if ( $SmoConnection ) { + Disconnect-SmoInstance -Instance $SmoConnection } } } diff --git a/test/Get-Table.Tests.ps1 b/test/Get-Table.Tests.ps1 new file mode 100644 index 0000000..d1f7213 --- /dev/null +++ b/test/Get-Table.Tests.ps1 @@ -0,0 +1,66 @@ +#Requires -Modules @{ ModuleName='Pester'; ModuleVersion='5.0.0' }, @{ ModuleName='PsSqlTestServer'; ModuleVersion='1.2.0' } + +Describe Get-Table { + + BeforeAll { + Import-Module $PSScriptRoot/../publish/PsSmo/PsSmo.psd1 -Force -ErrorAction Stop + } + + Context SqlInstance { + + BeforeAll { + $SqlInstance = New-SqlTestInstance -ErrorAction Stop + $SqlInstanceConnection = $SqlInstance | Connect-TSqlInstance + } + + AfterAll { + if ( $SqlInstance ) { + $SqlInstance | Remove-SqlTestInstance + } + } + + Context SqlDatabase { + + BeforeAll { + $SqlDatabase = New-SqlTestDatabase -Instance $SqlInstance -InstanceConnection $SqlInstanceConnection -ErrorAction Stop + $SqlDatabaseConnection = $SqlDatabase | Connect-TSqlInstance + } + + AfterAll { + Disconnect-TSqlInstance -Connection $SqlDatabaseConnection + $SqlDatabase | Remove-SqlTestDatabase + } + + Context SmoInstance { + BeforeAll { + $SmoConnection = $SqlDatabaseConnection | Connect-SmoInstance -ErrorAction Stop + } + + AfterAll { + if ( $SmoConnection ) { + Disconnect-SmoInstance -Instance $SmoConnection + } + } + + Context Table { + BeforeAll { + Invoke-TSqlCommand 'CREATE TABLE MyTable ( [Id] INT NOT NULL PRIMARY KEY )' -Connection $SqlDatabaseConnection -ErrorAction Stop + } + + It 'Returns the table' { + $table = Get-SmoTable -Connection $SmoConnection + $table | Should -Not -BeNullOrEmpty + $table.Name | Should -Be 'MyTable' + } + + It 'Returns the table by name' { + $table = Get-SmoTable -Name 'MyTable' -Connection $SmoConnection + $table | Should -Not -BeNullOrEmpty + $table.Name | Should -Be 'MyTable' + } + } + } + } + } +} + diff --git a/test/Invoke-Command.Tests.ps1 b/test/Invoke-Command.Tests.ps1 index ac4fdbf..82f2ff0 100644 --- a/test/Invoke-Command.Tests.ps1 +++ b/test/Invoke-Command.Tests.ps1 @@ -1,107 +1,76 @@ -Describe 'Invoke-Command' { - - BeforeDiscovery { - $script:missingSqlclient = $true - $local:psSqlclient = Get-Module -ListAvailable -Name PsSqlClient - if ( $local:psSqlclient ) { - Import-Module PsSqlClient - $script:missingSqlclient = $false - } - } +#Requires -Modules @{ ModuleName='Pester'; ModuleVersion='5.0.0' }, @{ ModuleName='PsSqlTestServer'; ModuleVersion='1.2.0' } + +Describe Invoke-Command { BeforeAll { - Import-Module $PSScriptRoot/../src/PsSmo/bin/Debug/netcoreapp2.1/publish/PsSmo.psd1 -Force -ErrorAction 'Stop' + Import-Module $PSScriptRoot/../publish/PsSmo/PsSmo.psd1 -Force -ErrorAction Stop } - Context 'LocalDb' -Tag LocalDb { + Context SqlInstance { BeforeAll { - $script:missingLocalDb = $true - foreach( $version in Get-ChildItem -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server Local DB\Installed Versions' | Sort-Object Name -Descending ) { - if ( $script:missingLocalDb ) { - switch ( $version.PSChildName ) { - '11.0' { - $script:DataSource = '(localdb)\v11.0' - $script:missingLocalDb = $false - break; - } - '13.0' { - $script:DataSource = '(LocalDb)\MSSQLLocalDB' - $script:missingLocalDb = $false - break; - } - '15.0' { - $script:DataSource = '(LocalDb)\MSSQLLocalDB' - $script:missingLocalDb = $false - break; - } - Default { - Write-Warning "LocalDb version $_ is not implemented." - } - } - } - } + $Script:SqlInstance = New-SqlTestInstance -ErrorAction Stop + $Script:SqlInstanceConnection = $Script:SqlInstance | Connect-TSqlInstance } - Context 'SqlClient' -Skip:$script:missingSqlclient { + AfterAll { + if ( $Script:SqlInstance ) { + $Script:SqlInstance | Remove-SqlTestInstance + } + if ( $Script:SqlInstanceConnection ) { + Disconnect-TSqlInstance -Connection $Script:SqlInstanceConnection -ErrorAction Continue + } + } + Context SmoInstance { BeforeAll { - $script:Connection = Connect-TSqlInstance -DataSource $script:DataSource + $Script:SmoConnection = $Script:SqlInstanceConnection | Connect-SmoInstance -ErrorAction Stop } AfterAll { - if ( $script:Connection ) { - $script:Connection | Disconnect-TSqlInstance + if ( $Script:SmoConnection ) { + Disconnect-SmoInstance -Instance $Script:SmoConnection } } - Context 'SmoInstance' { - BeforeAll { - $script:Instance = $script:Connection | Connect-SmoInstance - } - - AfterAll { - $script:Instance | Disconnect-SmoInstance - } - - It 'throws on error' { - { - Invoke-SmoCommand -Command 'SELECT 1/0' - } | Should -Throw - } + It 'throws on error' { + { + Invoke-SmoCommand -Command 'SELECT 1/0' -ErrorAction Stop + } | Should -Throw + } - Context 'SQLCMD' { + Context 'SQLCMD' { - It 'works with separator' { - Invoke-SmoCommand -Command @' + It 'works with separator' { + Invoke-SmoCommand -Command @' PRINT 'foo' GO PRINT 'bar' GO '@ - } + } - It 'throws with undefined variables' { - { - Invoke-SmoCommand -Command 'PRINT ''$(foo)''' - } | Should -Throw - } + It 'throws with undefined variables' { + { + Invoke-SmoCommand -Command 'PRINT ''$(foo)''' -ErrorAction Stop + } | Should -Throw + } - It 'works with defined variables' { - Invoke-SmoCommand -Command 'PRINT ''$(foo)''' -Variables @{ foo = 'bar' } -Verbose - } + It 'works with defined variables' { + Invoke-SmoCommand -Command 'PRINT ''$(foo)''' -Variables @{ foo = 'bar' } -Verbose + } - It 'works with :on error' { - Invoke-SmoCommand -Command @' + It 'works with :on error' { + Invoke-SmoCommand -Command @' GO :on error exit GO '@ - } + } - It 'works with :setvar' { - Invoke-SmoCommand -Command @' + It 'works with :setvar' { + Invoke-SmoCommand -Command @' :setvar __IsSqlCmdEnabled "True" GO IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True' @@ -110,26 +79,24 @@ IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True' SET NOEXEC ON; END '@ - } + } - It 'ignores line comments' { - Invoke-SmoCommand -Command @' + It 'ignores line comments' { + Invoke-SmoCommand -Command @' -- :setvar foo $(foo) PRINT '$(foo)' '@ -Variables @{ foo = 'bar' } - } + } - It 'ignores block comments' { - Invoke-SmoCommand -Command @' + It 'ignores block comments' { + Invoke-SmoCommand -Command @' /* :setvar foo $(foo) */ PRINT '$(foo)' '@ -Variables @{ foo = 'bar' } - } } } } } - }