├── .devcontainer ├── Dockerfile ├── devcontainer.json └── library-scripts │ └── common-debian.sh ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CI ├── CI.ps1 ├── Dockerfile ├── InstallPowerShell.ps1 ├── PS-CI.ps1 ├── Publish.ps1 ├── Test-SingleFunctions.ps1 ├── build.ps1 └── pipeline.yml ├── ContinuousIntegration └── ContinuousIntegration.ps1 ├── ConvertFrom-DIB.ps1 ├── ConvertFrom-IPYNB.ps1 ├── ConvertFromNotebookToMarkdown.ps1 ├── ConvertMarkdownToNoteBook.ps1 ├── ConvertToDib.ps1 ├── ConvertToPowerShellNoteBook.ps1 ├── ConvertToSQLNoteBook.ps1 ├── Examples └── ShowGraphInBrowser.ps1 ├── ExportAsPowerShellNotebook.ps1 ├── ExportNotebookToPowerShellScript.ps1 ├── ExportNotebookToSqlScript.ps1 ├── FindParameterizedCell.ps1 ├── Get-CredentialFromWindowsCredentialManager.ps1 ├── Get-DIBBlock.ps1 ├── GetNotebook.ps1 ├── GetNotebookContent.ps1 ├── GetNotebookDisplayData.ps1 ├── GetParameterInsertionIndex.ps1 ├── InstallModule.ps1 ├── InvokeExecuteNotebook.ps1 ├── InvokePowerShellNotebook.ps1 ├── LICENSE ├── MarkdownExamples ├── demo.ipynb ├── demo.md ├── multiplePSLines.ipynb └── multiplePSLines.md ├── New-InteractiveNotebook.ps1 ├── NewCodeCell.ps1 ├── NewGistNotebook.ps1 ├── Open-InteractiveNotebook.ps1 ├── PowerShellNotebook.psd1 ├── PowerShellNotebook.psm1 ├── PowerShellNotebookDSL.ps1 ├── PublishToGallery.ps1 ├── README.md ├── SetNotebookToPs.ps1 ├── TestAzureBlobStorageUrl.ps1 ├── TestHasParameterizedCell.ps1 ├── TestUri.ps1 ├── Watch-Directory.ps1 ├── __tests__ ├── ChartNotebooks │ └── charts.ipynb ├── ConvertFromNotebookToMarkdown.tests.ps1 ├── ConvertMarkdownToNotebook.tests.ps1 ├── ConvertToPowerShellNotebook.tests.ps1 ├── ConvertToSQLNotebook.tests.ps1 ├── DemoFiles │ ├── AdventureWorksMultiStatementSBatch_NoGO2.sql │ ├── GetParsedSqlOffsets.ps1 │ ├── ParsingExamples.ps1 │ ├── demo.sql │ ├── demo.txt │ ├── demo_SingleCommentSingleLineCodeBlock.ps1 │ └── demo_w3GOs.sql ├── DotNetInteractiveNotebooks │ ├── AllDotNetInteractive.ipynb │ ├── ExportNotebookToPowerShellScript.tests.ps1 │ ├── PSInteractive.ipynb │ └── TestWithInvokePS.ipynb ├── ExportAsPowerShellNotebook.tests.ps1 ├── GetNotebook.tests.ps1 ├── GetNotebookContent.tests.ps1 ├── GetParsedSqlOffsets.tests.ps1 ├── GoodConvertedNotebooks │ ├── AdventureWorksMultiStatementSBatch_NoGO2.ipynb │ ├── GetParsedSqlOffsets.ipynb │ ├── ParsingExamples.ipynb │ ├── demo.ipynb │ └── sqlTestConverted.ipynb ├── GoodNotebooks │ ├── SimpleNotebookToTestConvertToMarkdown.ipynb │ ├── SingleCodeBlock.ipynb │ ├── UsedForCellNumbers.ipynb │ ├── demo.ipynb │ ├── testPSExcel.ipynb │ ├── testPSNb1.ipynb │ └── testPSNb2.ipynb ├── InvokeExecuteNotebook.tests.ps1 ├── InvokePowerShellNotebook.tests.ps1 ├── MultiLineSourceNotebooks │ ├── MultiLineSourceAsArray.ipynb │ └── MultiLineSourceAsString.ipynb ├── MultipleChapters │ └── MultipleChapters.md ├── MultiplePSFiles │ ├── a1.ps1 │ ├── a2.ps1 │ └── a3.ps1 ├── NewCodeCell.tests.ps1 ├── NewGistNotebook.tests.ps1 ├── NoNotebooks │ └── NotANotebok.txt ├── NoteBookName ├── NotebooksForUseWithInvokeOutfile │ ├── CellHasAnError.ipynb │ ├── ComboGoodAndErrorCells.ipynb │ ├── Hello-PowerShell.ipynb │ ├── NotebookMoreThanOneParameterCell.ipynb │ ├── NotebookNoParameterCells.ipynb │ ├── VariablesAcrossCells.ipynb │ ├── parameters.ipynb │ └── testFile1.ipynb ├── OneNotebook │ └── testPSNb.ipynb ├── PSNBDSL.tests.ps1 ├── PSNotebookRunspace.tests.ps1 ├── SQLNotebooks │ ├── ExportNotebookToSqlScript.tests.ps1 │ ├── Simple_SELECTs.ipynb │ └── sys_databases.ipynb ├── TestHasParameterizedCell.tests.ps1 └── samplemarkdown │ ├── demo.md │ ├── demoPowerShellFenceBlock.md │ └── excludeResults.md ├── azure-pipelines.yml ├── changelog.ipynb ├── changelog.md ├── input.ipynb ├── media ├── ADSPowerShellNoteBook.png ├── ConvertMarkdownToNotebook.png ├── ConvertedFromDemoText.png ├── CreateNotebookUsingTheDSL.png ├── CvtFromMarkdown.png ├── EnableLanguageForDSL.png ├── InvokePowerShellNotebook.png ├── InvokePowerShellNotebookAsExcel.png ├── IsParameterCell.png └── ParametersTag.gif └── samplenotebook ├── Chapter01code.ipynb ├── MDandCodeSrcAsArray.ipynb ├── SimpleNotebookToTestConvertToMarkdown.ipynb ├── SingleCodeBlock.ipynb ├── csharp.ipynb ├── fsharp.ipynb ├── powershell.ipynb └── python.ipynb /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/powershell:lts-debian-10 2 | 3 | # [Option] Install zsh 4 | ARG INSTALL_ZSH="true" 5 | # [Option] Upgrade OS packages to their latest versions 6 | ARG UPGRADE_PACKAGES="false" 7 | 8 | # Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies. 9 | ARG USERNAME=vscode 10 | ARG USER_UID=1000 11 | ARG USER_GID=$USER_UID 12 | COPY library-scripts/*.sh /tmp/library-scripts/ 13 | RUN apt-get update \ 14 | && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" \ 15 | && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts 16 | 17 | # [Optional] Uncomment this section to install additional packages. 18 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 19 | # && apt-get -y install --no-install-recommends 20 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.140.1/containers/powershell 3 | { 4 | "name": "PowerShell", 5 | "dockerFile": "Dockerfile", 6 | // Set *default* container specific settings.json values on container create. 7 | "settings": { 8 | "terminal.integrated.shell.linux": "/usr/bin/pwsh" 9 | }, 10 | // Add the IDs of extensions you want installed when the container is created. 11 | "extensions": [ 12 | "ms-vscode.powershell" 13 | ], 14 | "build": { 15 | "args": { 16 | "INSTALL_ZSH": "false" 17 | } 18 | }, 19 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 20 | // "forwardPorts": [], 21 | // Uncomment the next line to run commands after the container is created. This gets run in bash which is why we call `pwsh`. 22 | "postCreateCommand": "pwsh -c 'install-module pester -force'; pwsh -c 'install-module sqlserver -force'; pwsh -c 'install-module importexcel -force'" 23 | 24 | // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. 25 | // "remoteUser": "vscode" 26 | } -------------------------------------------------------------------------------- /.devcontainer/library-scripts/common-debian.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #------------------------------------------------------------------------------------------------------------- 3 | # Copyright (c) Microsoft Corporation. All rights reserved. 4 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 5 | #------------------------------------------------------------------------------------------------------------- 6 | 7 | # Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag] 8 | 9 | INSTALL_ZSH=${1:-"true"} 10 | USERNAME=${2:-"vscode"} 11 | USER_UID=${3:-1000} 12 | USER_GID=${4:-1000} 13 | UPGRADE_PACKAGES=${5:-"true"} 14 | 15 | set -e 16 | 17 | if [ "$(id -u)" -ne 0 ]; then 18 | echo -e 'Script must be run a root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' 19 | exit 1 20 | fi 21 | 22 | # Treat a user name of "none" as root 23 | if [ "${USERNAME}" = "none" ] || [ "${USERNAME}" = "root" ]; then 24 | USERNAME=root 25 | USER_UID=0 26 | USER_GID=0 27 | fi 28 | 29 | # Load markers to see which steps have already run 30 | MARKER_FILE="/usr/local/etc/vscode-dev-containers/common" 31 | if [ -f "${MARKER_FILE}" ]; then 32 | echo "Marker file found:" 33 | cat "${MARKER_FILE}" 34 | source "${MARKER_FILE}" 35 | fi 36 | 37 | # Ensure apt is in non-interactive to avoid prompts 38 | export DEBIAN_FRONTEND=noninteractive 39 | 40 | # Function to call apt-get if needed 41 | apt-get-update-if-needed() 42 | { 43 | if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then 44 | echo "Running apt-get update..." 45 | apt-get update 46 | else 47 | echo "Skipping apt-get update." 48 | fi 49 | } 50 | 51 | # Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies 52 | if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then 53 | apt-get-update-if-needed 54 | 55 | PACKAGE_LIST="apt-utils \ 56 | git \ 57 | openssh-client \ 58 | gnupg2 \ 59 | iproute2 \ 60 | procps \ 61 | lsof \ 62 | htop \ 63 | net-tools \ 64 | psmisc \ 65 | curl \ 66 | wget \ 67 | rsync \ 68 | ca-certificates \ 69 | unzip \ 70 | zip \ 71 | nano \ 72 | vim-tiny \ 73 | less \ 74 | jq \ 75 | lsb-release \ 76 | apt-transport-https \ 77 | dialog \ 78 | libc6 \ 79 | libgcc1 \ 80 | libgssapi-krb5-2 \ 81 | libicu[0-9][0-9] \ 82 | liblttng-ust0 \ 83 | libstdc++6 \ 84 | zlib1g \ 85 | locales \ 86 | sudo \ 87 | ncdu \ 88 | man-db" 89 | 90 | # Install libssl1.1 if available 91 | if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then 92 | PACKAGE_LIST="${PACKAGE_LIST} libssl1.1" 93 | fi 94 | 95 | # Install appropriate version of libssl1.0.x if available 96 | LIBSSL=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '') 97 | if [ "$(echo "$LIBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then 98 | if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then 99 | # Debian 9 100 | PACKAGE_LIST="${PACKAGE_LIST} libssl1.0.2" 101 | elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then 102 | # Ubuntu 18.04, 16.04, earlier 103 | PACKAGE_LIST="${PACKAGE_LIST} libssl1.0.0" 104 | fi 105 | fi 106 | 107 | echo "Packages to verify are installed: ${PACKAGE_LIST}" 108 | apt-get -y install --no-install-recommends ${PACKAGE_LIST} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 ) 109 | 110 | PACKAGES_ALREADY_INSTALLED="true" 111 | fi 112 | 113 | # Get to latest versions of all packages 114 | if [ "${UPGRADE_PACKAGES}" = "true" ]; then 115 | apt-get-update-if-needed 116 | apt-get -y upgrade --no-install-recommends 117 | apt-get autoremove -y 118 | fi 119 | 120 | # Ensure at least the en_US.UTF-8 UTF-8 locale is available. 121 | # Common need for both applications and things like the agnoster ZSH theme. 122 | if [ "${LOCALE_ALREADY_SET}" != "true" ]; then 123 | echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen 124 | locale-gen 125 | LOCALE_ALREADY_SET="true" 126 | fi 127 | 128 | # Create or update a non-root user to match UID/GID - see https://aka.ms/vscode-remote/containers/non-root-user. 129 | if id -u $USERNAME > /dev/null 2>&1; then 130 | # User exists, update if needed 131 | if [ "$USER_GID" != "$(id -G $USERNAME)" ]; then 132 | groupmod --gid $USER_GID $USERNAME 133 | usermod --gid $USER_GID $USERNAME 134 | fi 135 | if [ "$USER_UID" != "$(id -u $USERNAME)" ]; then 136 | usermod --uid $USER_UID $USERNAME 137 | fi 138 | else 139 | # Create user 140 | groupadd --gid $USER_GID $USERNAME 141 | useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME 142 | fi 143 | 144 | # Add add sudo support for non-root user 145 | if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then 146 | echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME 147 | chmod 0440 /etc/sudoers.d/$USERNAME 148 | EXISTING_NON_ROOT_USER="${USERNAME}" 149 | fi 150 | 151 | # .bashrc/.zshrc snippet 152 | RC_SNIPPET="$(cat << EOF 153 | export USER=\$(whoami) 154 | 155 | export PATH=\$PATH:\$HOME/.local/bin 156 | 157 | if [[ \$(which code-insiders 2>&1) && ! \$(which code 2>&1) ]]; then 158 | alias code=code-insiders 159 | fi 160 | EOF 161 | )" 162 | 163 | # Ensure ~/.local/bin is in the PATH for root and non-root users for bash. (zsh is later) 164 | if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then 165 | echo "${RC_SNIPPET}" >> /etc/bash.bashrc 166 | RC_SNIPPET_ALREADY_ADDED="true" 167 | fi 168 | 169 | # Optionally install and configure zsh 170 | if [ "${INSTALL_ZSH}" = "true" ] && [ ! -d "/root/.oh-my-zsh" ] && [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then 171 | apt-get-update-if-needed 172 | apt-get install -y zsh 173 | curl -fsSLo- https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh | bash 2>&1 174 | echo "${RC_SNIPPET}" >> /etc/zsh/zshrc 175 | echo -e "DEFAULT_USER=\$USER\nprompt_context(){}" >> /root/.zshrc 176 | cp -fR /root/.oh-my-zsh /etc/skel 177 | cp -f /root/.zshrc /etc/skel 178 | sed -i -e "s/\/root\/.oh-my-zsh/\/home\/\$(whoami)\/.oh-my-zsh/g" /etc/skel/.zshrc 179 | if [ "${USERNAME}" != "root" ]; then 180 | cp -fR /etc/skel/.oh-my-zsh /etc/skel/.zshrc /home/$USERNAME 181 | chown -R $USER_UID:$USER_GID /home/$USERNAME/.oh-my-zsh /home/$USERNAME/.zshrc 182 | fi 183 | ZSH_ALREADY_INSTALLED="true" 184 | fi 185 | 186 | # Write marker file 187 | mkdir -p "$(dirname "${MARKER_FILE}")" 188 | echo -e "\ 189 | PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\ 190 | LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\ 191 | EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\ 192 | RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\ 193 | ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}" 194 | 195 | echo "Done!" -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | 3 | workflow_dispatch: 4 | 5 | push: 6 | branches: 7 | - master 8 | 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | validate: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [windows-latest, ubuntu-18.04, macos-latest] 19 | steps: 20 | - uses: actions/checkout@v1 21 | - name: Run Continuous Integration 22 | run : ./ContinuousIntegration/ContinuousIntegration.ps1 23 | shell: pwsh -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | debug.log 2 | *\.ipynb_checkpoints* 3 | -------------------------------------------------------------------------------- /CI/Dockerfile: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM mcr.microsoft.com/powershell:latest 3 | #LABEL maintainer="" 4 | #RUN apt-get -y update && apt-get install -y --no-install-recommends libgdiplus libc6-dev 5 | SHELL ["pwsh","-Command"] 6 | ADD Modules /root/.local/share/powershell/Modules/ -------------------------------------------------------------------------------- /CI/InstallPowerShell.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Installs PowerShell Core on Windows. 4 | #> 5 | [CmdLetBinding()] 6 | Param 7 | ( 8 | # Version to install in the format from the .msi, for example "7.0.0-preview.1" 9 | [String]$Version 10 | ) 11 | $ErrorActionPreference = 'Stop' 12 | 13 | if (-not $Version) { 14 | $Version = (Invoke-RestMethod https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/metadata.json).StableReleaseTag 15 | } 16 | $Version = $Version -replace "^v","" 17 | 18 | '[Progress] Downloading PowerShell Core.' 19 | $MsiPath = Join-Path $env:TEMP "PowerShell-$Version-win-x64.msi" 20 | [System.Net.WebClient]::new().DownloadFile("https://github.com/PowerShell/PowerShell/releases/download/v$Version/PowerShell-$Version-win-x64.msi", $MsiPath) 21 | 22 | '[Progress] Installing PowerShell Core.' 23 | Start-Process 'msiexec.exe' -Wait -ArgumentList "/i $MsiPath /quiet" 24 | Remove-Item -Path $MsiPath 25 | $PowerShellFolder = $Version[0] 26 | if ($Version -like "*preview*") { 27 | $PowerShellFolder += '-preview' 28 | } 29 | $env:Path = "$env:ProgramFiles\PowerShell\$PowerShellFolder;$env:Path" 30 | '[Progress] PowerShell Core Installed.' -------------------------------------------------------------------------------- /CI/Publish.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploy module to PowerShellGallery. 4 | #> 5 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "Success")] 6 | [CmdletBinding(DefaultParameterSetName = 'ModuleName')] 7 | Param 8 | ( 9 | # The name of the installed module to be deployed, if not provided the name of the .psm1 file in the parent folder is used. 10 | [Parameter(ParameterSetName = 'ModuleName')] 11 | [ValidateNotNullOrEmpty()] 12 | [String]$ModuleName, 13 | 14 | # Publish module from path (module folder), if not provided -ModuleName is used. 15 | [Parameter(Mandatory, ParameterSetName = 'Path')] 16 | [ValidateNotNullOrEmpty()] 17 | [String]$Path, 18 | 19 | # Key for PowerShellGallery deployment, if not provided $env:NugetApiKey is used. 20 | [ValidateNotNullOrEmpty()] 21 | [String]$NugetApiKey, 22 | 23 | # Skip Version verification for PowerShellGallery deployment, can be used for first release. 24 | [Switch]$Force 25 | ) 26 | $ErrorActionPreference = 'Stop' 27 | 28 | if ($Path) { 29 | $Path = Resolve-Path -Path $Path 30 | if ($Path.Count -ne 1) { 31 | throw ('Invalid Path, $Path.Count: {0}.' -f $Path.Count) 32 | } 33 | $Psd1Path = (Get-ChildItem -File -Filter *.psd1 -Path $Path -Recurse)[0].FullName 34 | $ModuleName = [System.IO.Path]::GetFileNameWithoutExtension($Psd1Path) 35 | $VersionLocal = (. ([Scriptblock]::Create((Get-Content -Path $Psd1Path | Out-String)))).ModuleVersion 36 | } 37 | else { 38 | # Get Script Root 39 | if ($PSScriptRoot) { 40 | $ScriptRoot = $PSScriptRoot 41 | } 42 | elseif ($psISE.CurrentFile.IsUntitled -eq $false) { 43 | $ScriptRoot = Split-Path -Path $psISE.CurrentFile.FullPath 44 | } 45 | elseif ($null -ne $psEditor.GetEditorContext().CurrentFile.Path -and $psEditor.GetEditorContext().CurrentFile.Path -notlike 'untitled:*') { 46 | $ScriptRoot = Split-Path -Path $psEditor.GetEditorContext().CurrentFile.Path 47 | } 48 | else { 49 | $ScriptRoot = '.' 50 | } 51 | 52 | # Get Module Info 53 | if (!$ModuleName) { 54 | $ModuleName = [System.IO.Path]::GetFileNameWithoutExtension((Get-ChildItem -File -Filter *.psm1 -Name -Path (Split-Path $ScriptRoot))) 55 | } 56 | $VersionLocal = ((Get-Module -Name $ModuleName -ListAvailable).Version | Measure-Object -Maximum).Maximum 57 | } 58 | 59 | "[Progress] Deploy Script Start for Module: $ModuleName, Version: $VersionLocal." 60 | 61 | # Deploy to PowerShell Gallery if run locally OR from AppVeyor & GitHub master 62 | if (!$env:APPVEYOR -or $env:APPVEYOR_REPO_BRANCH -eq 'master') { 63 | if ($env:APPVEYOR) { 64 | $Success = $true 65 | $AppVeyorProject = Invoke-RestMethod -Uri "https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG" 66 | $AppVeyorProject.build.jobs | ForEach-Object { 67 | '[Info] AppVeyor job name: "{0}", Id: {1}, Status: {2}.' -f $_.name, $_.jobId, $_.status 68 | if ($_.jobId -ne $env:APPVEYOR_JOB_ID -and $_.status -ne "success") { 69 | $Success = $false 70 | } 71 | } 72 | if (!$Success) { 73 | '[Info] There are filed jobs skipping PowerShell Gallery deploy.' 74 | break 75 | } 76 | } 77 | try { 78 | $VersionGallery = (Find-Module -Name $ModuleName -ErrorAction Stop).Version 79 | } 80 | catch { 81 | if ($_.Exception.Message -notlike 'No match was found for the specified search criteria*' -or !$Force) { 82 | throw $_ 83 | } 84 | } 85 | 86 | "[Info] PowerShellGallery. $ModuleName, VersionGallery: $VersionGallery, VersionLocal: $VersionLocal." 87 | if ($VersionGallery -lt $VersionLocal -or $Force) { 88 | if (!$NugetApiKey) { 89 | $NugetApiKey = $env:NugetApiKey 90 | } 91 | "[Info] PowerShellGallery. Deploying $ModuleName version $VersionLocal." 92 | if ($Path) { 93 | Publish-Module -NuGetApiKey $NugetApiKey -Path $Path 94 | } 95 | else { 96 | Publish-Module -NuGetApiKey $NugetApiKey -Name $ModuleName -RequiredVersion $VersionLocal 97 | } 98 | } 99 | else { 100 | '[Info] PowerShellGallery Deploy Skipped (Version Check).' 101 | } 102 | } 103 | else { 104 | '[Info] PowerShellGallery Deploy Skipped.' 105 | } 106 | '[Progress] Deploy Ended.' -------------------------------------------------------------------------------- /CI/Test-SingleFunctions.ps1: -------------------------------------------------------------------------------- 1 |  2 | function Test-SingleFunction { 3 | param ( 4 | [parameter(ValueFromPipeline=$true)] 5 | $path ) 6 | begin { 7 | $psd = Get-Content -Raw "$PSScriptRoot\..\ImportExcel.psd1" 8 | $exportedFunctions = (Invoke-Command ([scriptblock]::Create($psd))).functionsToExport 9 | $reg = [Regex]::new(@" 10 | function\s*[-\w]+\s*{ # The function name and opening '{' 11 | (?: 12 | [^{}]+ # Match all non-braces 13 | | 14 | (? { ) # Match '{', and capture into 'open' 15 | | 16 | (?<-open> } ) # Match '}', and delete the 'open' capture 17 | )* 18 | (?(open)(?!)) # Fails if 'open' stack isn't empty 19 | } # Functions closing '}' 20 | "@, 57) # 41 = compile ignore case and white space. 21 | $reg2 = [Regex]::new(@" 22 | ^function\s*[-\w]+\s*{ # The function name and opening '{' 23 | ( 24 | \#.*?[\r\n]+ # single line comment 25 | | # or 26 | \s*<\#.*?\#> # <#comment block#> 27 | | # or 28 | \s*\[.*?\] # [attribute tags] 29 | )* 30 | "@, 57) 31 | # 43 = compile, multi-line, ignore case and white space. 32 | } 33 | process { 34 | $item = Get-item $Path 35 | $name = $item.Name -replace "\.\w+$","" 36 | Write-Verbose $name 37 | $file = Get-Content $item -Raw 38 | $m = $reg.Matches($file) 39 | 40 | #based on https://stackoverflow.com/questions/7898310/using-regex-to-balance-match-parenthesis 41 | if ($m.Count -eq 0) {return "Could not find $name function in $($item.name)"} 42 | elseif ($m.Count -ge 2) {return "Multiple functions in $($item.name)"} 43 | elseif ($exportedFunctions -cnotcontains $name) {return "$name not exported (or in the wrong case)"} 44 | elseif ($m[0] -cnotmatch "^\w+\s+$name") {return "function $name in wrong case"} 45 | $m2 = [regex]::Match($m[0],"param",[System.Text.RegularExpressions.RegexOptions]::IgnoreCase) 46 | if (-not $m2.Success) {return "No param block in $name"} 47 | # elseif ($m[0] -inotmatch "(?s)^function\s*$name\s*{(\s*<\#.*?\#>|\s*\[.*?\])*\s*param") 48 | # elseif ($reg2.IsMatch($m[0].Value)) {return "function $name has comment-based help"} 49 | elseif ($m[0] -inotmatch "\[CmdletBinding\(" -and 50 | $m[0] -inotmatch "\[parameter\(" ) {return "$name has is not an advanced function"} 51 | #elseif (-not (& $Name -?).synopsis) {return "$name has no help"} 52 | else {Write-Verbose "$name OK"} 53 | } 54 | } -------------------------------------------------------------------------------- /CI/build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(DefaultParameterSetName = 'Default')] 2 | param( 3 | # Path to install the module to, if not provided -Scope used. 4 | [Parameter(Mandatory, ParameterSetName = 'ModulePath')] 5 | [ValidateNotNullOrEmpty()] 6 | [String]$ModulePath, 7 | 8 | # Path to install the module to, PSModulePath "CurrentUser" or "AllUsers", if not provided "CurrentUser" used. 9 | [Parameter(Mandatory, ParameterSetName = 'Scope')] 10 | [ValidateSet('CurrentUser', 'AllUsers')] 11 | [string] 12 | $Scope = 'CurrentUser', 13 | [switch]$Passthru 14 | ) 15 | 16 | if ($PSScriptRoot) { Push-Location "$PSScriptRoot\.." } 17 | 18 | $psdpath = Get-Item "*.psd1" 19 | if (-not $psdpath -or $psdpath.count -gt 1) { 20 | throw "Did not find a unique PSD file " 21 | } 22 | else { 23 | $ModuleName = $psdpath.Name -replace '\.psd1$' , '' 24 | $Settings = $(& ([scriptblock]::Create(($psdpath | Get-Content -Raw)))) 25 | } 26 | 27 | try { 28 | Write-Verbose -Message 'Module installation started' 29 | 30 | if (!$ModulePath) { 31 | if ($IsLinux -or $IsMacOS) {$ModulePathSeparator = ':' } 32 | else {$ModulePathSeparator = ';' } 33 | 34 | if ($Scope -eq 'CurrentUser') { $dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile) } 35 | else { $dir = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles) } 36 | $ModulePath = ($env:PSModulePath -split $ModulePathSeparator).where({$_ -like "$dir*"},"First",1) 37 | $ModulePath = Join-Path -Path $ModulePath -ChildPath $ModuleName 38 | $ModulePath = Join-Path -Path $ModulePath -ChildPath $Settings.ModuleVersion 39 | } 40 | 41 | # Create Directory 42 | if (-not (Test-Path -Path $ModulePath)) { 43 | $null = New-Item -Path $ModulePath -ItemType Directory -ErrorAction Stop 44 | Write-Verbose -Message ('Created module folder: "{0}"' -f $ModulePath) 45 | } 46 | 47 | Write-Verbose -Message ('Copying files to "{0}"' -f $ModulePath) 48 | $outputFile = $psdpath | Copy-Item -Destination $ModulePath -PassThru 49 | Foreach ($file in $Settings.FileList) { 50 | if ($file -like '.\*') { 51 | $dest = ($file -replace '\.\\',"$ModulePath\") 52 | if (-not (Test-Path -PathType Container (Split-Path -Parent $dest))) { 53 | $null = New-item -Type Directory -Path (Split-Path -Parent $dest) 54 | } 55 | } 56 | else {$dest = $ModulePath } 57 | Copy-Item $file -Destination $dest -Force -Recurse 58 | } 59 | 60 | if (Test-Path -PathType Container "mdHelp") { 61 | if (-not (Get-Module -ListAvailable platyPS)) { 62 | Write-Verbose-Message ('Installing Platyps to build help files') 63 | Install-Module -Name platyPS -Force -SkipPublisherCheck 64 | } 65 | Import-Module platyPS 66 | Get-ChildItem .\mdHelp -Directory | ForEach-Object { 67 | New-ExternalHelp -Path $_.FullName -OutputPath (Join-Path $ModulePath $_.Name) -Force -Verbose 68 | } 69 | } 70 | $env:PSNewBuildModule = $ModulePath 71 | 72 | if ($Passthru) {$outputFile} 73 | } 74 | catch { 75 | throw ('Failed installing module "{0}". Error: "{1}" in Line {2}' -f $ModuleName, $_, $_.InvocationInfo.ScriptLineNumber) 76 | } 77 | finally { 78 | if ($PSScriptRoot) { Pop-Location } 79 | Write-Verbose -Message 'Module installation end' 80 | } -------------------------------------------------------------------------------- /CI/pipeline.yml: -------------------------------------------------------------------------------- 1 | # https://aka.ms/yaml 2 | 3 | trigger: 4 | branches: 5 | include: 6 | - '*' 7 | # - master 8 | # - releases/* 9 | paths: 10 | exclude: 11 | - README.md 12 | - CHANGELOG.md 13 | 14 | jobs: 15 | - job: 'Windows_PowerShell_all_options' 16 | pool: 17 | vmImage: 'windows-latest' 18 | steps: 19 | - powershell: 'Install-Module -Name Pester -Force -SkipPublisherCheck' 20 | displayName: 'Update Pester' 21 | - powershell: './CI/PS-CI.ps1 ' 22 | displayName: 'Check Build Check Pack Test' 23 | - task: PublishTestResults@2 24 | inputs: 25 | testResultsFormat: 'NUnit' 26 | testResultsFiles: '**/TestResults*.xml' 27 | failTaskOnFailedTests: true 28 | - task: PublishPipelineArtifact@1 29 | inputs: 30 | targetPath: '$(Build.ArtifactStagingDirectory)/ImportExcel' 31 | artifact: 'ImportExcel' 32 | 33 | - job: 'PowerShell_Core_on_Windows_Build_and_Pester_only' 34 | pool: 35 | vmImage: 'windows-latest' 36 | steps: 37 | - pwsh: 'Install-Module -Name Pester -Force' 38 | displayName: 'Update Pester' 39 | - pwsh: '.\CI\PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks' 40 | displayName: 'Install and Test' 41 | - task: PublishTestResults@2 42 | inputs: 43 | testResultsFormat: 'NUnit' 44 | testResultsFiles: '**/TestResults*.xml' 45 | failTaskOnFailedTests: true 46 | 47 | - job: 'Ubuntu_Build_and_Pester_Only' 48 | pool: 49 | vmImage: 'ubuntu-latest' 50 | steps: 51 | - powershell: 'Install-Module -Name Pester -Force' 52 | displayName: 'Update Pester' 53 | - powershell: './CI/PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks ' 54 | displayName: 'Install and Test' 55 | - task: PublishTestResults@2 56 | inputs: 57 | testResultsFormat: 'NUnit' 58 | testResultsFiles: '**/TestResults*.xml' 59 | failTaskOnFailedTests: true 60 | 61 | - job: 'macOS_Build_and_Pester_Only' 62 | pool: 63 | vmImage: 'macOS-latest' 64 | steps: 65 | - script: brew install mono-libgdiplus 66 | displayName: 'Install mono-libgdiplus' 67 | - powershell: 'Install-Module -Name Pester -Force' 68 | displayName: 'Update Pester' 69 | - powershell: './CI/PS-CI.ps1 -SkipPreChecks -SkipHelp -SkipPostChecks' 70 | displayName: 'Install and Test' 71 | - task: PublishTestResults@2 72 | inputs: 73 | testResultsFormat: 'NUnit' 74 | testResultsFiles: '**/TestResults*.xml' 75 | failTaskOnFailedTests: true 76 | -------------------------------------------------------------------------------- /ContinuousIntegration/ContinuousIntegration.ps1: -------------------------------------------------------------------------------- 1 | $PSVersionTable 2 | 3 | $modules = @("Pester", "ImportExcel", "SQLServer", "PSScriptAnalyzer") 4 | 5 | foreach ($module in $modules) { 6 | Write-Host "Installing $module" -ForegroundColor Cyan 7 | Install-Module $module -Force -SkipPublisherCheck 8 | Import-Module $module -Force -PassThru 9 | } 10 | 11 | $pesterResults = Invoke-Pester -Output Detailed -PassThru 12 | 13 | if ($pesterResults.FailedCount -gt 0) { 14 | 15 | '[Progress] Pester Results Failed' 16 | $pesterResults.Failed | Out-String 17 | 18 | '[Progress] Pester Results FailedBlocks' 19 | $pesterResults.FailedBlocks | Out-String 20 | 21 | '[Progress] Pester Results FailedContainers' 22 | $pesterResults.FailedContainers | Out-String 23 | 24 | Throw "Tests failed" 25 | } -------------------------------------------------------------------------------- /ConvertFrom-DIB.ps1: -------------------------------------------------------------------------------- 1 | function ConvertFrom-DIB { 2 | param( 3 | [Switch]$AsText, 4 | [Parameter(ValueFromPipelineByPropertyName)] 5 | [Alias("FullName")] 6 | $Path 7 | ) 8 | 9 | Process { 10 | $psnParams = @{DNI = $true } 11 | 12 | if ($AsText) { 13 | $psnParams['AsText'] = $true 14 | } 15 | else { 16 | $NoteBookName = (Split-Path $Path -Leaf) -replace 'dib', 'ipynb' 17 | $psnParams['NoteBookName'] = $NoteBookName 18 | } 19 | 20 | New-PSNotebook @psnParams { 21 | switch (Get-DIBBlock $Path) { 22 | { $_.Type -eq '#!markdown' } { 23 | Add-NotebookMarkdown $_.Content 24 | } 25 | { $_.Type -eq '#!pwsh' } { 26 | Add-NotebookCode $_.Content -NoGUID 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /ConvertFromNotebookToMarkdown.ps1: -------------------------------------------------------------------------------- 1 | function ConvertFrom-NotebookToMarkdown { 2 | <# 3 | .SYNOPSIS 4 | Take an exiting PowerShell Notebook and convert it to markdown 5 | #> 6 | param( 7 | [Parameter(Mandatory,Position=0)] 8 | [Alias('NotebookName','NoteBookFullName','Fullname')] 9 | $Path, 10 | $Destination = $pwd, 11 | [Switch]$AsText, 12 | [Switch]$Includeoutput 13 | ) 14 | 15 | $NotebookProperties = Get-Notebook $Path 16 | $text = $( 17 | switch (Get-NotebookContent -Path $Path -Includeoutput:$Includeoutput) { 18 | { $_.Type -eq 'markdown' } { $_.Source } 19 | { $_.Type -eq 'code' } { 20 | #if present Convert .NetInteractive magic commands into "linguist" Names by github to the render code in markup 21 | switch -Regex ($_.source) { 22 | '^#!csharp|^#!c#' {$format = 'C#'} 23 | '^#!fsharp|^#!f#' {$format = 'F#'} 24 | '^#!pwsh|^#!PowerShell' {$format = 'PowerShell'} 25 | '^#!js|^#!JavaScript' {$format = 'JavaScript'} 26 | '^#!html' {$format = 'html'} 27 | '^#!markdown' {$format = 'MarkDown'} 28 | default {$format = $NotebookProperties.FormatStyle} 29 | } 30 | '```'+ $format + "`n" + $_.Source + "`n" + '```' + "`n" 31 | #There will only be output if we specified the -IncludeOutput and we will ignore output which isn't a string or a single HTML block. 32 | if ($_.Output -is [string]) { 33 | '```' + "`n" + $_.Output.trim() + "`n" + '```' + "`n" 34 | } 35 | elseif ($_.output.count -eq 1 -and $_.output.'text/html') {$_.output.'text/html' + "`n"} 36 | } 37 | } 38 | ) 39 | 40 | if ($AsText) { return $text } 41 | 42 | if (Test-Path -PathType Container -Path $Destination) { 43 | $Destination = join-path $Destination -ChildPath( (Split-Path -Leaf $Path) -replace 'ipynb$', 'md') 44 | } 45 | $text | Set-Content -Encoding UTF8 $Destination 46 | 47 | Get-Item -Path $Destination 48 | } -------------------------------------------------------------------------------- /ConvertMarkdownToNoteBook.ps1: -------------------------------------------------------------------------------- 1 | function Convert-MarkdownToNoteBook { 2 | <# 3 | .SYNOPSIS 4 | Convert a markdown file to an interactive PowerShell Notebook 5 | 6 | .Description 7 | 8 | .Example 9 | # converts .\demo.md to demo.ipynb 10 | Convert-MarkdownToNoteBook .\demo.md 11 | 12 | .Example 13 | # converts .\demo.md to demo.ipynb and watches the file for changes and automatically converts it again 14 | Convert-MarkdownToNoteBook .\demo.md -watch 15 | #> 16 | param( 17 | $filename, 18 | [Switch]$watch 19 | ) 20 | 21 | function DoWatch { 22 | param( 23 | $targetFile, 24 | [scriptblock]$sb 25 | ) 26 | 27 | "Watching - Press Ctl-C to stop" 28 | $targetFile = Resolve-Path $targetFile 29 | $sb = { 30 | foreach ($entry in $args[0].GetEnumerator()) { 31 | if ($entry.Key -eq $targetFile -and $entry.Value -eq "Changed") { 32 | & $sb 33 | } 34 | } 35 | }.GetNewClosure() 36 | 37 | &"$PSScriptRoot\Watch-Directory.ps1" -Path . -TestSeconds .5 -WaitSeconds 1 -Command $sb 38 | } 39 | 40 | "[{0}] Converting {1}" -f (Get-Date), $filename 41 | 42 | $content = Get-Content $filename 43 | 44 | $chapters = [ordered]@{ } 45 | $chapterIndex = 1 46 | 47 | switch ($content) { 48 | "" { 49 | $inChapter = $false 50 | $chapterIndex += 1 51 | } 52 | 53 | { $inChapter } { 54 | $currentChapter = "Chapter {0}" -f $chapterIndex 55 | if (!$chapters.$currentChapter) { 56 | $chapters.$currentChapter = @() 57 | } 58 | 59 | $chapters.$currentChapter += $_ 60 | } 61 | 62 | "" { $inChapter = $true } 63 | } 64 | 65 | 66 | $code = @() 67 | $markDown = @() 68 | 69 | New-PSNotebook -NoteBookName ($filename -replace '.md', '.ipynb') -IncludeCodeResults { 70 | 71 | foreach ($chapter in $chapters.Keys) { 72 | 73 | Add-NotebookMarkdown -markdown ("# $($chapter)") 74 | 75 | $inCodeBlock = $false 76 | 77 | switch ($chapters.$chapter) { 78 | { $_ -eq '```ps' -or $_ -eq '```powershell' } { 79 | Add-NotebookMarkdown -markdown (-join $markDown) 80 | $code = @() 81 | $inCodeBlock = $true 82 | } 83 | '```' { 84 | Add-NotebookCode -code (-join $code) 85 | $markDown = @() 86 | $inCodeBlock = $false 87 | } 88 | default { 89 | if ($inCodeBlock) { 90 | $code += $_ + "`r`n" 91 | } 92 | else { 93 | $markDown += $_ + "`r`n" 94 | } 95 | } 96 | } 97 | 98 | if ($markDown) { 99 | Add-NotebookMarkdown -markdown (-join $markDown) 100 | $markDown = @() 101 | } 102 | } 103 | } 104 | 105 | "[{0}] Finished {1}" -f (Get-Date), $filename 106 | 107 | # if ($Watch) { DoWatch $filename { .\ConvertMarkdownToNoteBook.ps1 -filename $filename } } 108 | if ($Watch) { DoWatch $filename { Convert-MarkdownToNoteBook -filename $filename } } 109 | } -------------------------------------------------------------------------------- /ConvertToDib.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Dib { 2 | <# 3 | .Synopsis 4 | #> 5 | 6 | param( 7 | [Parameter(ValueFromPipelineByPropertyName)] 8 | $FullName 9 | ) 10 | 11 | Process { 12 | if ($FullName.EndsWith('.ipynb')) { 13 | $dibName = $FullName.Replace('.ipynb', '.dib') 14 | 15 | foreach ($block in Get-NotebookContent $FullName) { 16 | $prefix = $null 17 | if ($block.Type -eq 'markdown') { 18 | $prefix = "#!markdown`n" 19 | } 20 | 21 | $prefix + $block.Source | Add-Content $dibName -Encoding utf8 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /ConvertToPowerShellNoteBook.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-PowerShellNoteBook { 2 | <# 3 | .Synopsis 4 | Convert PowerShell scripts (ps1 files) to interactive notebooks (ipynb files) 5 | 6 | .Description 7 | Convert PowerShell scripts on disk or the internet to interactive notebooks that can be run in Azure Data Studio or with `Invoke-ExecuteMethod` 8 | 9 | .Example 10 | ConvertTo-PowerShellNoteBook -InputFileName c:\Temp\demo.txt -OutputNotebookName c:\Temp\demo.ipynb 11 | 12 | .Example 13 | ConvertTo-PowerShellNoteBook 'https://raw.githubusercontent.com/dfinke/PowerShellNotebook/master/__tests__/DemoFiles/demo_SingleCommentSingleLineCodeBlock.ps1' 14 | 15 | .Example 16 | $( 17 | 'https://raw.githubusercontent.com/dfinke/PowerShellNotebook/master/__tests__/DemoFiles/demo_SingleCommentSingleLineCodeBlock.ps1' 18 | dir *.ps1 19 | ) | ConvertTo-PowerShellNoteBook 20 | #> 21 | param( 22 | [parameter(ValueFromPipelineByPropertyName, ValueFromPipeline)] 23 | [Alias('FullName', 'Path')] 24 | $InputFileName, 25 | $OutputNotebookName 26 | ) 27 | 28 | Process { 29 | #region Parsing section. 30 | 31 | Write-Progress -Activity "Converting PowerShell file to Notebook" -Status "Converting $($InputFileName)" 32 | 33 | if ([System.Uri]::IsWellFormedUriString($InputFileName, [System.UriKind]::Absolute)) { 34 | $s = Invoke-RestMethod -Uri $InputFileName 35 | } 36 | else { 37 | $InputFileName = Resolve-Path $InputFileName 38 | $s = Get-Content -Raw $InputFileName 39 | } 40 | 41 | # if no $OutputNotebookName, grab the filename and replace the ps1 42 | # should work with both filenames and uri's 43 | if (!$OutputNotebookName) { 44 | $OutputNotebookName = (Split-Path -leaf $InputFileName) -replace '.ps1', '.ipynb' 45 | $OutputNotebookName = $pwd.Path + "\" + $OutputNotebookName 46 | } 47 | 48 | try { 49 | # $CommentRanges = [System.Management.Automation.PSParser]::Tokenize((Get-Content -Raw $InputFileName), [ref]$null).Where( { $_.Type -eq 'Comment' }) | 50 | $CommentRanges = [System.Management.Automation.PSParser]::Tokenize($s, [ref]$null).Where( { $_.Type -eq 'Comment' }) | 51 | Select-Object -Property Start, Length, Type, Content 52 | } 53 | Catch { 54 | "This is not a valid PowerShell file" 55 | } 56 | 57 | $BlocksWitGaps = @() 58 | $Previous = $null 59 | foreach ($CommentBlock in $CommentRanges ) { 60 | $BlockOffsets = [ordered]@{ 61 | Start = $CommentBlock.Start; 62 | StopOffset = $CommentBlock.Start + $CommentBlock.Length; 63 | Length = $CommentBlock.Length; 64 | GapLength = [int] $CommentBlock.Start - $Previous.StopOffset; 65 | PreviousStart = $Previous.Start; 66 | PreviousStopOffset = $Previous.StopOffset; 67 | Type = 'Code'; 68 | GapText = IF ($CommentBlock.Start - $Previous.StopOffset -gt 1) { [string] $s.Substring($Previous.StopOffset, ($CommentBlock.Start - $Previous.StopOffset)).trimstart() }else { [string] '' }; 69 | Content = $CommentBlock.Content 70 | } 71 | 72 | $Previous = $BlockOffsets 73 | $BlocksWitGaps += [pscustomobject] $BlockOffsets 74 | } 75 | 76 | 77 | $AllBlocks = @() 78 | $Previous = $null 79 | $AllBlocks = $CommentRanges 80 | <# Catch anything missed from the tail of the file, add it to $AllBlocks #> 81 | if ($BlocksWitGaps.Count -eq 1) { 82 | <# This step handles ading the psobjects together if $CommentBlock isn't an array. #> 83 | $AllBlocks = @($CommentBlock; [pscustomobject][Ordered]@{ 84 | Start = ($CommentBlock.Start + $CommentBlock.Length); 85 | Length = $s.Length - ($CommentBlock.Start + $CommentBlock.Length); 86 | Type = 'Gap'; 87 | Content = $s.Substring(($CommentBlock.Start + $CommentBlock.Length), ($s.Length - ($CommentBlock.Start + $CommentBlock.Length))).trimstart() 88 | }) 89 | } 90 | else { 91 | if (($CommentBlock.Start + $CommentBlock.Length) -lt $s.Length) { 92 | $AllBlocks += [pscustomobject][ordered]@{ 93 | Start = ($CommentBlock.Start + $CommentBlock.Length); 94 | Length = $s.Length - ($CommentBlock.Start + $CommentBlock.Length); 95 | Type = 'Gap'; 96 | Content = $s.Substring(($CommentBlock.Start + $CommentBlock.Length), ($s.Length - ($CommentBlock.Start + $CommentBlock.Length))).trimstart() 97 | } 98 | } 99 | } 100 | 101 | foreach ($GapBlock in $BlocksWitGaps ) { 102 | $GapOffsets = [ordered]@{ 103 | Start = $GapBlock.PreviousStopOffset; 104 | StopOffset = $GapBlock.Start; 105 | Length = $GapBlock.GapLength; 106 | Type = 'Gap'; 107 | Content = $GapBlock.GapText.trimstart() 108 | } 109 | 110 | $Previous = $GapOffsets 111 | $AllBlocks += if ($GapOffsets.Length -gt 0) { [pscustomobject] $GapOffsets } 112 | } 113 | #endregion 114 | #region Notebook creation section. 115 | New-PSNotebook -NoteBookName $OutputNotebookName -DotNetInteractive { 116 | 117 | foreach ($Block in $AllBlocks | Sort-Object Start ) { 118 | 119 | switch ($Block.Type) { 120 | 'Comment' { 121 | $TextBlock = $s.Substring($Block.Start, $Block.Length) -replace ("\n", " `n ") 122 | 123 | if ($TextBlock.Trim().length -gt 0) { 124 | Add-NotebookMarkdown -markdown (-join $TextBlock) 125 | } 126 | } 127 | 'Gap' { 128 | $GapBlock = $s.Substring($Block.Start, $Block.Length) 129 | 130 | if ($GapBlock.Trim().length -gt 0) { 131 | Add-NotebookCode -code (-join $GapBlock.trimstart().trimend()) # -replace ("\n", " `n ") 132 | } 133 | } 134 | } 135 | } 136 | } 137 | # Set $OutputNotebookName to $null otherwise if the $InputFileName is being piped to, it won't get reset 138 | $OutputNotebookName = $null 139 | #endregion 140 | } 141 | } -------------------------------------------------------------------------------- /Examples/ShowGraphInBrowser.ps1: -------------------------------------------------------------------------------- 1 | Import-Module ..\PowerShellNotebook.psd1 -Force 2 | 3 | $htmlFile = "$env:TEMP\test.html" 4 | 5 | $result = Get-NotebookDisplayData -NoteBookFullName "$PSScriptRoot\..\__tests__\ChartNotebooks\charts.ipynb" 6 | $result.Display > $htmlFile 7 | 8 | Invoke-Item $htmlFile -------------------------------------------------------------------------------- /ExportAsPowerShellNotebook.ps1: -------------------------------------------------------------------------------- 1 | function Export-AsPowerShellNotebook { 2 | <# 3 | .Synopsis 4 | Takes strings of PowerShell and creates and interactive Jupyter Notebook. Try exporting your PowerShell history to a notebook. Check the examples 5 | 6 | .Example 7 | Get-History | % command* | Out-ConsoleGridView | Export-AsPowerShellNotebook -OutputNotebook .\temp\testthis.ipynb 8 | 9 | .Example 10 | Get-History 7,14 | % comm* | Export-AsPowerShellNotebook -OutputNotebook d:\temp\testthis.ipynb 11 | #> 12 | param( 13 | $OutputNotebook, 14 | [Parameter(ValueFromPipeline)] 15 | $PowerShellText 16 | ) 17 | 18 | Begin { 19 | if (!$OutputNotebook) { 20 | throw '$OutputNotebook not specified' 21 | } 22 | $psCode = @() 23 | } 24 | 25 | Process { 26 | $psCode += $PowerShellText 27 | } 28 | 29 | End { 30 | $count = $psCode.Count 31 | if ($count -gt 0) { 32 | New-PSNotebook -NoteBookName $OutputNotebook { 33 | for ($idx = 0; $idx -lt $count; $idx++) { 34 | $currentText = $psCode[$idx] 35 | if ($currentText.Trim().StartsWith('#')) { 36 | Add-NotebookMarkdown -markdown $currentText 37 | } 38 | else { 39 | Add-NotebookCode -code $currentText 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /ExportNotebookToPowerShellScript.ps1: -------------------------------------------------------------------------------- 1 | function Export-NotebookToPowerShellScript { 2 | <# 3 | .SYNOPSIS 4 | Exports all code blocks from a PowerShell Notebook to a PowerShell script 5 | 6 | .DESCRIPTION 7 | Exports from either a local notebook or one on the internet 8 | 9 | .Example 10 | Export-NotebookToPowerShellScript .\TestPS.ipynb 11 | Get-Content .\TestPS.ps1 12 | 13 | .Example 14 | Export-NotebookToPowerShellScript "https://raw.githubusercontent.com/dfinke/PowerShellNotebook/AddJupyterNotebookMetaInfo/samplenotebook/powershell.ipynb" 15 | Get-Content .\powershell.ps1 16 | 17 | .Example 18 | Export-NotebookToPowerShellScript .\TestPS.ipynb -IncludeTextCells 19 | Get-Content .\TestPS.ps1 20 | 21 | Include exporting the the Text cells from the .IPYNB file to the .PS1 file. 22 | #> 23 | [CmdletBinding()] 24 | param( 25 | [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=0)] 26 | $FullName, 27 | [Alias("OutPath")] 28 | $Destination = $PWD, 29 | [switch]$IncludeTextCells, 30 | [switch]$AsText 31 | ) 32 | Process { 33 | Write-Progress -Activity "Exporting PowerShell Notebook" -Status $FullName 34 | 35 | if (Test-Path $Destination -PathType Container) { 36 | #split-path works for well from URIs as well as filesystem paths 37 | $outFile = (Split-Path -Leaf $FullName) -replace ".ipynb", ".ps1" 38 | $Destination = Join-Path -Path $Destination -ChildPath $outFile 39 | } 40 | 41 | #ensure date is formated for local culture. 42 | $result = , (@' 43 | <# 44 | Created from: {1} 45 | 46 | Created by: Export-NotebookToPowerShellScript 47 | Created on: {0:D} {0:t} 48 | #> 49 | 50 | '@ -f (Get-Date), $FullName) 51 | 52 | if ($IncludeTextCells) {$sourceBlocks = Get-NotebookContent $FullName} 53 | else {$sourceBlocks = Get-NotebookContent $FullName -JustCode} 54 | 55 | #if the last cell is empty don't output it 56 | if ($sourceBlocks.count -gt 1 -and [string]::IsNullOrEmpty($sourceBlocks[-1].source)) { 57 | $sourceBlocks = $sourceBlocks[0..($sourceBlocks.count -2)] 58 | } 59 | 60 | $prevCode = $false 61 | $result += switch ($sourceBlocks) { 62 | {$_.type -eq 'code'} { 63 | if ($prevCode) {"<# #>"} #Avoid concatenating Code cells. 64 | ($_.Source.trimend() ) 65 | $prevCode = $true 66 | } 67 | default { 68 | "<#`r`n"+ $_.Source.TrimEnd() +"`r`n#>" 69 | $prevCode = $false 70 | } 71 | } 72 | if ($AsText) {return $result} 73 | else { 74 | $result| Set-Content $Destination 75 | Get-item $Destination 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /ExportNotebookToSqlScript.ps1: -------------------------------------------------------------------------------- 1 | function Export-NotebookToSqlScript { 2 | <# 3 | .SYNOPSIS 4 | Exports all code blocks from a SQL Notebook to a SQL script 5 | 6 | .DESCRIPTION 7 | Exports from either a local notebook or one on the internet 8 | 9 | .Example 10 | Export-NotebookToSqlScript .\BPCheck.ipynb 11 | Get-Content .\BPCheck.SQL 12 | 13 | Converts a local copy of the BPCheck.ipynb Jupyter Notebook into a .SQL file, and gets the content of the 14 | resulting .SQL file. 15 | 16 | .Example 17 | Export-NotebookToSqlScript "https://raw.githubusercontent.com/microsoft/tigertoolbox/master/BPCheck/BPCheck.ipynb" 18 | Get-Content .\BPCheck.sql 19 | 20 | Downloads the latest version of the BPCheck Jupyter Notebook from the TigerToolbox repository, converts it 21 | into a .SQL file (named BPCheck.SQL), and gets the content. 22 | 23 | .Example 24 | Export-NotebookToSqlScript "https://raw.githubusercontent.com/microsoft/tigertoolbox/master/BPCheck/BPCheck.ipynb" 25 | Open-EditorFile .\BPCheck.sql 26 | 27 | Downloads the latest version of the BPCheck Jupyter Notebook from the TigerToolbox repository, converts it 28 | into a .SQL file (named BPCheck.SQL), and when run from the PowerShell Integrated Console (in either VS Code or 29 | Azure Data Studio), opens it as a new Notebook window. 30 | #> 31 | [CmdletBinding()] 32 | param( 33 | $FullName, 34 | $outPath = "./", 35 | $IncludeTextCells=$true 36 | ) 37 | Write-Progress -Activity "Exporting SQL Notebook" -Status $FullName 38 | 39 | if ([System.Uri]::IsWellFormedUriString($FullName, [System.UriKind]::Absolute)) { 40 | $outFile = $FullName.split('/')[-1] 41 | } 42 | else { 43 | $outFile = (Split-Path -Leaf $FullName) 44 | } 45 | 46 | $outFile = $outFile -replace ".ipynb", ".sql" 47 | $fullOutFileName = $outPath + $outFile 48 | 49 | $heading = @" 50 | /* 51 | Created from: $($FullName) 52 | 53 | Created by: Export-NotebookToSqlScript 54 | Created on: {0:D} {0:t} 55 | */ 56 | 57 | "@ -f (Get-Date) 58 | 59 | $heading | Set-Content $fullOutFileName 60 | if($IncludeTextCells -eq $false) 61 | {$sourceBlocks = Get-NotebookContent $FullName -JustCode} 62 | else{$sourceBlocks = Get-NotebookContent $FullName} 63 | 64 | $result = foreach ($sourceBlock in $sourceBlocks) 65 | { 66 | switch ($sourceBlock.Type) { 67 | 'code' {($sourceBlock.Source)} 68 | 'markdown' {"/* "+($sourceBlock.Source)+" */"} 69 | } 70 | "" 71 | } 72 | 73 | $result | Add-Content $fullOutFileName 74 | 75 | Write-Verbose "$($outFile) created" 76 | } -------------------------------------------------------------------------------- /FindParameterizedCell.ps1: -------------------------------------------------------------------------------- 1 | function Find-ParameterizedCell { 2 | <# 3 | .Synopsis 4 | Reads a Jupyter Notebook and returns all cells with a tag -eq to 'parameters' 5 | .Example 6 | Invoke-ExecuteNotebook -InputNotebook .\test.ipynb "abs://$($account)/$($containerName)/test.ipynb?$($sasToken)" 7 | #> 8 | param( 9 | [Parameter(Mandatory)] 10 | $InputNotebook 11 | ) 12 | 13 | if ([System.Uri]::IsWellFormedUriString($InputNotebook, [System.UriKind]::Absolute)) { 14 | $data = Invoke-RestMethod $InputNotebook 15 | } 16 | else { 17 | $json = Get-Content $InputNotebook 18 | $data = $json | ConvertFrom-Json 19 | } 20 | 21 | for ($idx = 0; $idx -lt $data.cells.Count; $idx++) { 22 | $currentCell = $data.cells[$idx] 23 | if ($currentCell.metadata.tags -eq 'parameters') { 24 | $idx 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Get-DIBBlock.ps1: -------------------------------------------------------------------------------- 1 | function Get-DIBBlock { 2 | param( 3 | [Parameter(Mandatory)] 4 | $fileName 5 | ) 6 | 7 | $content = Get-Content $fileName 8 | 9 | $lineNumber = -1 10 | 11 | $locations = foreach ($line in $content) { 12 | $lineNumber++ 13 | if ($line.Startswith('#!')) { 14 | $lineNumber 15 | } 16 | } 17 | 18 | for ($idx = 0; $idx -lt $locations.Count; $idx++) { 19 | $startBlock = $locations[$idx] + 1 20 | if ($idx + 1 -eq $locations.Count) { 21 | $endBlock = $content.Count - 1 22 | } 23 | else { 24 | $endBlock = $locations[$idx + 1] - 1 25 | } 26 | 27 | [pscustomobject][ordered]@{ 28 | FileName = $fileName 29 | Block = $idx 30 | Range = '{0}..{1}' -f $startBlock, $endBlock 31 | Type = $content[$locations[$idx]] 32 | Content = $content[$startBlock..$endBlock] -join "`n" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /GetNotebook.ps1: -------------------------------------------------------------------------------- 1 | function Get-Notebook { 2 | <# 3 | .SYNOPSIS 4 | Get-Notebook reads the metadata of Jupyter Notebooks 5 | 6 | .Example 7 | Get-Notebook .\samplenotebook\Chapter01code.ipynb 8 | 9 | NoteBookName : Chapter01code.ipynb 10 | KernelName : powershell 11 | CodeBlocks : 83 12 | MarkdownBlocks : 23 13 | FullName : C:\Users\Douglas\Documents\GitHub\MyPrivateGit\PowerShellNotebook\samplenotebook\Chapter01code.ipynb 14 | FormatStyle : PowerShell 15 | HasParameterizedCell : False 16 | 17 | .Example 18 | Get-Notebook .\samplenotebook\| Format-Table 19 | 20 | NoteBookName KernelName CodeBlocks MarkdownBlocks FullName 21 | ------------ ---------- ---------- -------------- ---------------- 22 | Chapter01code.ipynb powershell 83 23 C:\Users\Douglas\Documents\GitHub\MyPrivateGit\Power... 23 | csharp.ipynb .net-csharp 1 0 C:\Users\Douglas\Documents\GitHub\MyPrivateGit\Power... 24 | fsharp.ipynb .net-fsharp 1 0 C:\Users\Douglas\Documents\GitHub\MyPrivateGit\Power... 25 | powershell.ipynb .net-powershell 1 0 C:\Users\Douglas\Documents\GitHub\MyPrivateGit\Power... 26 | python.ipynb python3 1 0 C:\Users\Douglas\Documents\GitHub\MyPrivateGit\Power... 27 | SingleCodeBlock.ipynb powershell 1 0 C:\Users\Douglas\Documents\GitHub\MyPrivateGit\Power 28 | 29 | .Example 30 | Get-Notebook -recurse | group KernelName 31 | 32 | Count Name Group 33 | ----- ---- ----- 34 | 98 powershell {@{NoteBookName=Aaron_CopyWorkspace.ipynb; KernelName=powershell; CodeBlocks=14; Mar... 35 | 2 not found {@{NoteBookName=BPCheck.ipynb; KernelName=not found; CodeBlocks=0; MarkdownBlocks=0;... 36 | 36 SQL {@{NoteBookName=BPCheck.ipynb; KernelName=SQL; C... 37 | 29 python3 {@{NoteBookName=python install powershell_kernel.ipynb; KernelName=python3; CodeBloc... 38 | 781 .net-powershell {@{NoteBookName=Using_ConvertTo-SQLNoteBook.ipynb; KernelName=.net-powershell; CodeB... 39 | 3 pyspark3kernel {@{NoteBookName=load-sample-data-into-bdc.ipynb; KernelName=pyspark3kernel; C... 40 | 41 | This command will allow you to serch through a directory & all sub directories to find Jupyter Notebooks & group them by Kernel used in each of those Notebook. 42 | #> 43 | param( 44 | [Parameter(ValueFromPipelineByPropertyName=$true)] 45 | [Alias('Fullname')] 46 | $Path = $pwd, 47 | $NoteBookName = '*', 48 | [switch]$Recurse 49 | ) 50 | begin { 51 | $linguistNames = @{ #used by github to the render code in markup ```SQL will render as sql etc 52 | '.net-csharp' = 'C#' 53 | '.net-fsharp' = 'F#' 54 | '.net-powershell' = 'PowerShell' 55 | 'not found' = '' 56 | 'powershell' = 'PowerShell' 57 | 'python3' = 'Python' 58 | 'Python [Root]' = 'Python' 59 | 'sql' = 'SQL' 60 | } 61 | } 62 | process { 63 | $targetName = "$($NotebookName).ipynb" 64 | foreach ($file in Get-ChildItem $Path $targetName -Recurse:$Recurse) { 65 | $r = Get-Content $file.fullname | ConvertFrom-Json 66 | 67 | $kernelspecName = $r.metadata.kernelspec.name 68 | if (!$kernelspecName) { $kernelspecName = "not found" } 69 | 70 | $counts = $r.cells | Group-Object cell_type -AsHashTable 71 | 72 | [PSCustomObject][Ordered]@{ 73 | NoteBookName = $file.Name 74 | KernelName = $kernelspecName 75 | CodeBlocks = $counts.code.Count 76 | MarkdownBlocks = $counts.markdown.Count 77 | FullName = $file.FullName 78 | FormatStyle = $linguistNames[$kernelspecName] 79 | LanguageName = $r.metadata.language_info.name 80 | Lexer = $r.metadata.language_info.pygments_lexer 81 | HasParameterizedCell = $r.cells.metadata.tags -contains "parameters" 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /GetNotebookContent.ps1: -------------------------------------------------------------------------------- 1 | function Get-NotebookContent { 2 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "", Justification="Test doesn't understand -begin script blocks. ")] 3 | <# 4 | .SYNOPSIS 5 | Get-NotebookContents reads the contents of a Jupyter Notebooks 6 | 7 | .Example 8 | Get-NotebookContent .\samplenotebook\Chapter01code.ipynb 9 | 10 | NoteBookName Type Source 11 | ------------ ---- ------ 12 | Chapter01code.ipynb markdown ## Code for chapter 1 PowerShell in Action third edition 13 | Chapter01code.ipynb markdown ## Introduction 14 | Chapter01code.ipynb code 'Hello world.' 15 | Chapter01code.ipynb code Get-ChildItem -Path $env:windir\*.log | Select-String -List error | Format-Table Path,L... 16 | Chapter01code.ipynb code ([xml] [System.Net.WebClient]::new().DownloadString('http://blogs.msdn.com/powershell/r... 17 | Chapter01code.ipynb markdown ## 1.2 PowerShell example code 18 | Chapter01code.ipynb code Get-ChildItem -Path C:\somefile.txt 19 | 20 | .Example 21 | Get-Notebook .\samplenotebook\*sharp*|Get-NotebookContent 22 | 23 | NoteBookName Type Source 24 | ------------ ---- ------ 25 | csharp.ipynb code {Console.Write("hello world")} 26 | fsharp.ipynb code {printfn "hello world"} 27 | 28 | #> 29 | [cmdletbinding(DefaultParameterSetName="MarkdownAndCode")] 30 | param( 31 | [Parameter(ValueFromPipelineByPropertyName,Position=0)] 32 | [alias('FullName','NoteBookFullName')] 33 | $Path, 34 | [parameter(ParameterSetName='JustCode')] 35 | [alias('NoMarkdown')] 36 | [Switch]$JustCode, 37 | [parameter(ParameterSetName='JustMarkdown')] 38 | [alias('NoCode')] 39 | [Switch]$JustMarkdown, 40 | [Switch]$PassThru, 41 | [Switch]$IncludeOutput 42 | ) 43 | 44 | process { 45 | #allow Path to contain more than one item, if any are wild cards call the function recursively. 46 | foreach ($p in $Path) { 47 | if ([System.Uri]::IsWellFormedUriString($p, [System.UriKind]::Absolute)) { 48 | $r = Invoke-RestMethod $p 49 | } 50 | elseif (Test-Path $p -ErrorAction SilentlyContinue) { 51 | if ((Resolve-Path $p).count -gt 1) { 52 | [void]$PSBoundParameters.Remove('Path') 53 | Get-ChildItem $p | Get-NotebookContent @PSBoundParameters 54 | continue 55 | } 56 | else { 57 | $r = Get-Content $p | ConvertFrom-Json 58 | } 59 | } 60 | if ($PassThru) { return $r} 61 | elseif ($JustCode) { $cellType = 'code' } 62 | elseif ($JustMarkdown) { $cellType = 'markdown' } 63 | else { $cellType = '.' } 64 | 65 | $cellnumber = 0 66 | foreach ($cell in $r.cells) { 67 | $cellnumber += 1 68 | if ($null -ne $cellType -and $cell.'cell_type' -notmatch $cellType) { 69 | continue 70 | } 71 | $IsParameterCell = $false 72 | if ($cell.metadata.tags) { 73 | if ($null -ne ($cell.metadata.tags -eq 'parameters')) { 74 | $IsParameterCell = $true 75 | } 76 | } 77 | 78 | if ($cell.metadata.dotnet_interactive) { 79 | $Language = switch ($cell.metadata.dotnet_interactive.language) { 80 | 'sql' { 'SQL' } 81 | 'pwsh' { 'PowerShell' } 82 | 'fsharp' { 'F#' } 83 | 'csharp' { 'C#' } 84 | 'html' { 'html'} 85 | Default { 'C#' } 86 | } 87 | $Language += ' (.NET Interactive)' 88 | } 89 | elseif ($cell.'cell_type' -match 'code') { 90 | $Language = $r.metadata.kernelspec.language 91 | if ($r.metadata.kernelspec.display_name -match '^\.NET\s*\(') {$Language += ' (.NET Interactive)'} 92 | } 93 | elseif ($cell.'cell_type' -match 'code') { 94 | $Language = 'markdown' 95 | } 96 | $cellObj = [Ordered]@{ 97 | Cell = $cellNumber 98 | NoteBookName = Split-Path -Leaf $p 99 | Type = $cell.'cell_type' 100 | IsParameterCell = $IsParameterCell 101 | Language = $Language 102 | Source = -join $cell.source 103 | } 104 | 105 | if ($IncludeOutput) { 106 | # There may be one or many outputs. For each output 107 | # either a single string if has a .text field containing a string or array of strings 108 | # or a hash table if it has a .data field containing .mime-type = "Content" 109 | $cellObj['Output'] = foreach ($o in $cell.outputs) { 110 | if ($o.text -join "") {$o.text -join " `r`n" } 111 | elseif ($o.data) { 112 | $o.data.psobject.properties | foreach-object -Begin {$hash=@{}} -Process {$hash[$_.name] =$_.value} -end {$hash} 113 | } 114 | } 115 | #merge the text generated by .NET Interactive in VS code - every output will be a hash table with a text/plain entry. Others do the same with Text/HTML 116 | if ( $cellObj.Output.where({$_ -is [hashtable]}) -and -not #if some are hashtables and none are not hashtables 117 | $cellObj.Output.where({$_ -isnot [hashtable]}) ){ 118 | if ($cellObj.Output.where({ $_.containskey('text/html')}) -and -not #if some have text/html and none don't. 119 | $cellObj.Output.where({-not $_.containskey('text/html')}) ) { 120 | $cellObj['HtmlOutput'] = $cellObj.output.'text/html' -join "
`n" #Add an HTML Output field 121 | } 122 | if ($cellObj.Output.where({ $_.containskey('text/plain')}) -and -not #if some have text/plain and none don't. 123 | $cellObj.Output.where({-not $_.containskey('text/plain')}) ) { 124 | $cellObj.Output = -join $cellobj.output.'text/plain' #Merge plain text into the output field 125 | } 126 | } 127 | } 128 | [PSCustomObject]$cellObj 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /GetNotebookDisplayData.ps1: -------------------------------------------------------------------------------- 1 | function Get-NotebookDisplayData { 2 | <# 3 | .Synopsis 4 | #> 5 | param($NoteBookFullName) 6 | 7 | $result = Get-NotebookContent -Path $NoteBookFullName -PassThru 8 | 9 | foreach ($cell in $result.cells) { 10 | if ($cell.outputs.'output_type' -eq 'display_data') { 11 | [PSCustomObject][Ordered]@{ 12 | Source = -join $cell.source 13 | Display = -join $cell.outputs.data.'text/html' 14 | } 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /GetParameterInsertionIndex.ps1: -------------------------------------------------------------------------------- 1 | function Get-ParameterInsertionIndex { 2 | param( 3 | [Parameter(Mandatory)] 4 | $InputNotebook 5 | ) 6 | 7 | $cell = Find-ParameterizedCell $InputNotebook | Select-Object -First 1 8 | if ([string]::IsNullOrEmpty($cell)) { 9 | return 0 10 | } 11 | $cell + 1 12 | } -------------------------------------------------------------------------------- /InstallModule.ps1: -------------------------------------------------------------------------------- 1 | 2 | if (-not $fullPath) { 3 | $fullpath = $env:PSModulePath -split ":(?!\\)|;|," | 4 | Where-Object { $_ -notlike ([System.Environment]::GetFolderPath("UserProfile") + "*") -and $_ -notlike "$pshome*" } | 5 | Select-Object -First 1 6 | $fullPath = Join-Path $fullPath -ChildPath 'PowerShellNotebook' 7 | } 8 | 9 | Push-location $PSScriptRoot 10 | Robocopy . $fullPath /mir /XD .vscode .git CI __tests__ data mdHelp /XF appveyor.yml azure-pipelines.yml .gitattributes .gitignore filelist.txt install.ps1 InstallModule.ps1 11 | Pop-Location -------------------------------------------------------------------------------- /InvokeExecuteNotebook.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-ExecuteNotebook { 2 | <# 3 | .Synopsis 4 | Adds a new cell tagged with injected-parameters with input parameters in order to overwrite the values in parameters. 5 | If no cell is tagged with parameters the injected cell will be inserted at the top of the notebook. 6 | 7 | .Description 8 | This opens up new opportunities for how notebooks can be used. For example: 9 | 10 | Perhaps you have a financial report that you wish to run with different values on the first or last day of a month or at the beginning or end of the year, using parameters makes this task easier. 11 | Do you want to run a notebook and depending on its results, choose a particular notebook to run next? You can now programmatically execute a workflow without having to copy and paste from notebook to notebook manually. 12 | 13 | .Example 14 | Invoke-ExecuteNotebook -InputNotebook .\basic.ipynb -Parameters @{arr = 1, 2, 3} 15 | 16 | #> 17 | param( 18 | $InputNotebook, 19 | $OutputNotebook, 20 | [hashtable]$Parameters, 21 | # When cells are run, it returns objects not strings 22 | [Switch]$ReturnAsObjects, 23 | [Switch]$Force, 24 | [Switch]$DoNotLaunchBrowser, 25 | [Switch]$DotNetInteractive 26 | ) 27 | 28 | if (!$InputNotebook) { return } 29 | 30 | if ([System.Uri]::IsWellFormedUriString($InputNotebook, [System.UriKind]::Absolute)) { 31 | try { 32 | $data = Invoke-RestMethod $InputNotebook 33 | } 34 | catch { 35 | throw "$($InputNotebook) is not a valid Jupyter Notebook" 36 | } 37 | } 38 | else { 39 | $json = Get-Content $InputNotebook 40 | $data = $json | ConvertFrom-Json 41 | } 42 | 43 | [System.Collections.ArrayList]$cells = $data.cells 44 | 45 | $PSNotebookRunspace = New-PSNotebookRunspace -ReturnAsObjects:$ReturnAsObjects 46 | 47 | if ($Parameters) { 48 | $cvt = "@'`r`n" + ($Parameters | ConvertTo-Json) + "`r`n'@" 49 | 50 | $source = @' 51 | # injected parameters 52 | $payload = {0} | ConvertFrom-Json 53 | 54 | $names = $payload.psobject.Properties.name 55 | $names | foreach-object {{ Set-Variable -Name $_ -Value $payload.$_ }} 56 | 57 | Remove-Variable payload -ErrorAction SilentlyContinue 58 | Remove-Variable names -ErrorAction SilentlyContinue 59 | '@ -f $cvt 60 | 61 | $newParams = New-CodeCell $source -DotNetInteractive:$DotNetInteractive | ConvertFrom-Json -Depth 3 62 | 63 | $index = Get-ParameterInsertionIndex -InputNotebook $InputNotebook 64 | $cells.Insert($index, $newParams) 65 | } 66 | 67 | $startedExecution = Get-Date 68 | $totalCells = $cells.count 69 | for ($idx = 0; $idx -lt $totalCells; $idx++) { 70 | $pct = 100 * ($idx / $totalCells) 71 | Write-Progress -Activity "[$($startedExecution)] Executing Notebook $($InputNotebook)" -Status "Running cell $($idx+1) of $($totalCells)" -PercentComplete $pct 72 | 73 | $cell = $cells[$idx] 74 | if ($cell.cell_type -ne 'code') { continue } 75 | 76 | # Clear Errors 77 | $PSNotebookRunspace.PowerShell.Streams.Error.Clear() 78 | 79 | # Clear Commands 80 | $PSNotebookRunspace.PowerShell.Commands.Clear() 81 | 82 | $result = $PSNotebookRunspace.Invoke($cell.source) 83 | if ($PSNotebookRunspace.PowerShell.Streams.Error.Count -gt 0) { 84 | $text = $PSNotebookRunspace.PowerShell.Streams.Error | Out-String 85 | } 86 | else { 87 | $text = $result 88 | } 89 | 90 | $cell.outputs = @() 91 | if ($text) { 92 | $cell.outputs += [ordered]@{ 93 | name = "stdout" 94 | text = $text 95 | output_type = "stream" 96 | } 97 | } 98 | } 99 | 100 | $data.cells = $cells 101 | 102 | if ($OutputNotebook) { 103 | $isUri = Test-Uri $OutputNotebook 104 | if ($isUri) { 105 | if ($OutputNotebook.startswith("gist://")) { 106 | 107 | $OutFile = $OutputNotebook.replace("gist://", "") 108 | $targetFileName = Split-Path $OutFile -Leaf 109 | 110 | Write-Progress -Activity "Creating Gist" -Status $targetFileName 111 | $contents = $data | ConvertTo-Json -Depth 5 112 | 113 | $Show = !$DoNotLaunchBrowser 114 | $result = New-GistNotebook -contents $contents -fileName $targetFileName -Show:$Show 115 | } 116 | elseif ($OutputNotebook.startswith("abs://")) { 117 | if (Test-AzureBlobStorageUrl $outputNotebook) { 118 | 119 | $fullName = [System.IO.Path]::GetRandomFileName() 120 | ConvertTo-Json -InputObject $data -Depth 5 | Set-Content $fullName -Encoding utf8 121 | 122 | try { 123 | $headers = @{'x-ms-blob-type' = 'BlockBlob' } 124 | Invoke-RestMethod -Uri ($OutputNotebook.Replace('abs', 'https')) -Method Put -Headers $headers -InFile $fullName 125 | } 126 | catch { 127 | throw $_.Exception.Message 128 | } 129 | finally { 130 | Remove-Item $fullName -ErrorAction SilentlyContinue 131 | } 132 | } 133 | } 134 | else { 135 | throw "Invalid OutputNotebook url '{0}'" -f $OutputNotebook 136 | } 137 | } 138 | else { 139 | if ((Test-Path $OutputNotebook) -and !$Force) { 140 | throw "$OutputNotebook already exists" 141 | } 142 | 143 | ConvertTo-Json -InputObject $data -Depth 5 | 144 | Set-Content $OutputNotebook -Encoding utf8 145 | } 146 | } 147 | else { 148 | $data.cells.outputs.text 149 | } 150 | } -------------------------------------------------------------------------------- /InvokePowerShellNotebook.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PowerShellNotebook { 2 | <# 3 | .SYNOPSIS 4 | Invoke-PowerShellNotebook executes all the PowerShell code blocks in a PowerShell Notebook. 5 | 6 | .Example 7 | Invoke-PowerShellNotebook .\SingleCodeBlock.ipynb 8 | 9 | Region Item TotalSold 10 | ------ ---- --------- 11 | South lime 20 12 | West melon 76 13 | 14 | #> 15 | param( 16 | [Parameter(ValueFromPipelineByPropertyName)] 17 | $NoteBookFullName, 18 | $OutFile, 19 | [Switch]$AsExcel, 20 | [Switch]$Show 21 | ) 22 | 23 | Process { 24 | 25 | if ($OutFile) { 26 | if (Test-Path $OutFile) { 27 | throw "$OutFile already exists" 28 | } 29 | 30 | $PSNotebookRunspace = New-PSNotebookRunspace 31 | 32 | $null = Copy-Item $NoteBookFullName $OutFile 33 | $notebookJson = Get-Content $OutFile | ConvertFrom-Json 34 | 35 | $codeCells = $notebookJson.cells | Where-Object { $_.'cell_type' -eq 'code' } 36 | 37 | foreach ($codeCell in $codeCells) { 38 | 39 | # Clear Errors 40 | $PSNotebookRunspace.PowerShell.Streams.Error.Clear() 41 | # Clear Commands 42 | $PSNotebookRunspace.PowerShell.Commands.Clear() 43 | 44 | $result = $PSNotebookRunspace.Invoke($codeCell.source) 45 | if ($PSNotebookRunspace.PowerShell.Streams.Error.Count -gt 0) { 46 | $text = $PSNotebookRunspace.PowerShell.Streams.Error | Out-String 47 | } 48 | else { 49 | $text = $result 50 | } 51 | 52 | $codeCell.outputs = @() 53 | $codeCell.outputs += [ordered]@{ 54 | name = "stdout" 55 | text = $text -join "`n" 56 | output_type = "stream" 57 | } 58 | } 59 | 60 | $PSNotebookRunspace.Close() 61 | 62 | $notebookJson | ConvertTo-Json -Depth 5 | Set-Content $OutFile -Encoding UTF8 63 | } 64 | else { 65 | 66 | $codeBlocks = @(Get-NotebookContent $NoteBookFullName -JustCode) 67 | $codeBlockCount = $codeBlocks.Count 68 | $SheetCount = 0 69 | 70 | for ($idx = 0; $idx -lt $codeBlockCount; $idx++) { 71 | 72 | $targetCode = $codeblocks[$idx].source 73 | 74 | Write-Progress -Activity "Executing PowerShell code block - [$(Get-Date)]" -Status (-join $targetCode) -PercentComplete (($idx + 1) / $codeBlockCount * 100) 75 | 76 | if ($AsExcel) { 77 | if (!(Get-Module -ListAvailable ImportExcel -ErrorAction SilentlyContinue)) { 78 | throw "This feature requires the ImportExcel PowerShell module. Use 'Install-Module -Name ImportExcel' to get it from the PS Gallery." 79 | } 80 | 81 | if ($idx -eq 0) { 82 | $notebookFileName = Split-Path $NoteBookFullName -Leaf 83 | $xlFileName = $notebookFileName -replace ".ipynb", ".xlsx" 84 | 85 | $xlfile = "{0}\{1}" -f $pwd.Path, $xlFileName 86 | Remove-Item $xlfile -ErrorAction SilentlyContinue 87 | } 88 | 89 | foreach ($dataSet in , @($targetCode | Invoke-Expression)) { 90 | if ($dataSet) { 91 | $SheetCount++ 92 | $uniqueName = "Sheet$($SheetCount)" 93 | Export-Excel -InputObject $dataSet -Path $xlfile -WorksheetName $uniqueName -AutoSize -TableName $uniqueName 94 | } 95 | } 96 | } 97 | else { 98 | , @($targetCode | Invoke-Expression) 99 | } 100 | } 101 | 102 | if ($AsExcel) { 103 | if ($Show) { 104 | Invoke-Item $xlfile 105 | } 106 | else { 107 | $xlfile 108 | } 109 | } 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Doug Finke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MarkdownExamples/demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | {"cell_type":"markdown","metadata":{},"source":["# Chapter 1"]},{"cell_type":"markdown","metadata":{},"source":[""]},{ 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": { 21 | "azdata_cell_guid": "02f9d1df-fe44-47e7-847e-e68b99ce282f" 22 | }, 23 | "source": [ 24 | "\"Hello World\"\r\n" 25 | ], 26 | "outputs": [ 27 | { 28 | "output_type": "stream", 29 | "name": "stdout", 30 | "text": "Hello World\n" 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /MarkdownExamples/demo.md: -------------------------------------------------------------------------------- 1 | 2 | ```ps 3 | "Hello World" 4 | ``` 5 | -------------------------------------------------------------------------------- /MarkdownExamples/multiplePSLines.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | {"cell_type":"markdown","metadata":{},"source":["# Chapter 1"]},{"cell_type":"markdown","metadata":{},"source":["## PowerShell Notebooks - Executable Documents\r\n### Loops\r\n\r\n"]},{ 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": { 21 | "azdata_cell_guid": "9b02f1b5-a7f4-46f5-ad8a-8ec54306154f" 22 | }, 23 | "source": [ 24 | "1..5 | % {\r\n $_ * 2\r\n}\r\n" 25 | ], 26 | "outputs": [ 27 | { 28 | "output_type": "stream", 29 | "text": "2\n4\n6\n8\n10\n", 30 | "name": "stdout" 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /MarkdownExamples/multiplePSLines.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## PowerShell Notebooks - Executable Documents 4 | ### Loops 5 | 6 | ```ps 7 | 1..5 | % { 8 | $_ * 2 9 | } 10 | ``` 11 | 12 | 13 | -------------------------------------------------------------------------------- /New-InteractiveNotebook.ps1: -------------------------------------------------------------------------------- 1 | function New-InteractiveNotebook { 2 | <# 3 | .SYNOPSIS 4 | Create a new interactive notebook as a `dib` or `ipynb`, launching vscode 5 | .EXAMPLE 6 | New-InteractiveNotebook # Creates a new ipnyb interactive notebook 7 | .EXAMPLE 8 | New-InteractiveNotebook -AsDib # Creates a new dib interactive notebook 9 | #> 10 | param( 11 | [Switch]$AsDib 12 | ) 13 | 14 | Start-Process ("vscode://ms-dotnettools.dotnet-interactive-vscode/newNotebook?as={0}" -f ($AsDib ? 'dib':'ipynb')) 15 | } 16 | -------------------------------------------------------------------------------- /NewCodeCell.ps1: -------------------------------------------------------------------------------- 1 | function New-CodeCell { 2 | param( 3 | [Parameter(Mandatory)] 4 | $Source, 5 | [Switch]$DotNetInteractive 6 | ) 7 | 8 | $DotNetInteractiveMetadata = '' 9 | if ($DotNetInteractive) { 10 | $DotNetInteractiveMetadata = @' 11 | "dotnet_interactive": { 12 | "language": "pwsh" 13 | }, 14 | '@ 15 | } 16 | 17 | $targetSource = @($source.split("`n")) | ConvertTo-Json 18 | 19 | $result = @" 20 | {{ 21 | "cell_type": "code", 22 | "execution_count": 0, 23 | "metadata": {{ 24 | {0} 25 | "tags": [ 26 | "injected-parameters" 27 | ] 28 | }}, 29 | "outputs": [], 30 | "source": {1} 31 | }} 32 | "@ -f $DotNetInteractiveMetadata, $targetSource 33 | 34 | $result 35 | } -------------------------------------------------------------------------------- /NewGistNotebook.ps1: -------------------------------------------------------------------------------- 1 | function New-GistNotebook { 2 | param( 3 | [Parameter(ParameterSetName='File',ValueFromPipelineByPropertyName=$true)] 4 | [Alias('FullName')] 5 | $Path, 6 | [Parameter(Mandatory,ParameterSetName='TwoStrings',Position=0)] 7 | $Contents, 8 | [Parameter(ParameterSetName='File',ValueFromPipelineByPropertyName=$true)] 9 | [Parameter(Mandatory,ParameterSetName='TwoStrings',Position=1)] 10 | [alias('Name')] 11 | $FileName, 12 | $GistDescription = "PowerShell Notebook", 13 | [switch]$Public, 14 | [switch]$Show 15 | ) 16 | begin { 17 | if (Test-Path env:github_token) {$token = $env:github_token} 18 | elseif ($PSVersionTable.Platform -like "win*") { 19 | try {$token = & (Join-Path $PSScriptRoot 'Get-CredentialFromWindowsCredentialManager.ps1') -TargetName git:https://github.com -PlainTextPasswordOnly } 20 | catch {throw "Could not read stored access token and env:github_token not set. You need to set it to a GitHub PAT"} 21 | } 22 | else { throw "env:github_token not set. You need to set it to a GitHub PAT"} 23 | $params = @{ 24 | Method = 'Post' 25 | Uri = 'https://api.github.com/gists' 26 | Headers = @{"Authorization" = "token $token" } 27 | } 28 | } 29 | process { 30 | if ($PSBoundParameters.ContainsKey('Path')) { 31 | $Contents = Get-content $Path -Encoding utf8 32 | if (-not $FileName) {$FileName = Split-Path -Leaf $Path} 33 | } 34 | $gist = @{ 35 | 'description' = $GistDescription 36 | 'public' = ($Public -as [bool]) 37 | 'files' = @{ 38 | "$($FileName)" = @{ 39 | 'content' = "$($Contents)" 40 | } 41 | } 42 | } 43 | $result = Invoke-RestMethod @params -Body ($gist | ConvertTo-Json -EscapeHandling EscapeNonAscii) 44 | if ($Show) {Start-Process $result.html_url} 45 | else {return $result.html_Url} 46 | } 47 | } -------------------------------------------------------------------------------- /Open-InteractiveNotebook.ps1: -------------------------------------------------------------------------------- 1 | function Open-InteractiveNotebook { 2 | <# 3 | .SYNOPSIS 4 | Open a local notebook or from remote source 5 | .EXAMPLE 6 | # opens a local .dib interactive notebook 7 | Open-InteractiveNotebook .\Untitled-1.dib 8 | .EXAMPLE 9 | # opens a remote .dib interactive notebook 10 | Open-InteractiveNotebook https://raw.githubusercontent.com/dotnet/interactive/main/NotebookTestScript.dib 11 | #> 12 | param( 13 | [Parameter(ValueFromPipeline)] 14 | $Target 15 | ) 16 | 17 | Process { 18 | $Target = [uri]::UnescapeDataString($Target) 19 | if (Test-Path $Target) { 20 | $Target = Resolve-Path $Target 21 | $targetType = 'path' 22 | } 23 | elseif ([System.Uri]::IsWellFormedUriString($Target, [System.UriKind]::Absolute) ) { 24 | $targetType = 'url' 25 | } 26 | 27 | $targetMoniker = 'vscode://ms-dotnettools.dotnet-interactive-vscode/openNotebook?{0}={1}' -f $targetType, $Target 28 | $null = Start-Process $targetMoniker 29 | } 30 | } -------------------------------------------------------------------------------- /PowerShellNotebook.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | 3 | # Script module or binary module file associated with this manifest. 4 | RootModule = 'PowerShellNotebook.psm1' 5 | 6 | # Version number of this module. 7 | ModuleVersion = '3.0.0' 8 | 9 | # ID used to uniquely identify this module 10 | GUID = '2ab0cb25-b339-4726-8a2a-349ca1114bd4' 11 | 12 | # Author of this module 13 | Author = 'Douglas Finke' 14 | 15 | # Company or vendor of this module 16 | CompanyName = 'Doug Finke' 17 | 18 | # Copyright statement for this module 19 | Copyright = 'c 2020 All rights reserved.' 20 | 21 | # Description of the functionality provided by this module 22 | Description = @' 23 | Lets you automate both PowerShell & SQL Notebooks with PowerShell at the command line, exports to Excel and more. 24 | For detailed instructions and examples, click through the "Project Site" link or go to https://github.com/dfinke/PowerShellNotebook/blob/master/README.md 25 | '@ 26 | 27 | # Minimum version of the Windows PowerShell engine required by this module 28 | PowerShellVersion = '6.2' 29 | 30 | # Name of the Windows PowerShell host required by this module 31 | # PowerShellHostName = '' 32 | 33 | # Minimum version of the Windows PowerShell host required by this module 34 | # PowerShellHostVersion = '' 35 | 36 | # Minimum version of Microsoft .NET Framework required by this module 37 | # DotNetFrameworkVersion = '' 38 | 39 | # Minimum version of the common language runtime (CLR) required by this module 40 | # CLRVersion = '' 41 | 42 | # Processor architecture (None, X86, Amd64) required by this module 43 | # ProcessorArchitecture = '' 44 | 45 | # Modules that must be imported into the global environment prior to importing this module 46 | # RequiredModules = @() 47 | 48 | # Assemblies that must be loaded prior to importing this module 49 | # RequiredAssemblies = @() 50 | 51 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 52 | # ScriptsToProcess = @() 53 | 54 | # Type files (.ps1xml) to be loaded when importing this module 55 | # TypesToProcess = @() 56 | 57 | # Format files (.ps1xml) to be loaded when importing this module 58 | # FormatsToProcess = @() 59 | 60 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 61 | # NestedModules = @() 62 | 63 | # Functions to export from this module 64 | FunctionsToExport = @( 65 | 'Add-NotebookCode', 66 | 'Add-NotebookMarkdown', 67 | 'Convert-MarkdownToNoteBook', 68 | 'ConvertFrom-DIB', 69 | 'ConvertFrom-IPYNB', 70 | 'ConvertFrom-NotebookToMarkdown', 71 | 'ConvertFrom-NotebookToHTML', 72 | 'ConvertTo-Dib', 73 | 'ConvertTo-PowerShellNoteBook', 74 | 'ConvertTo-SQLNoteBook', 75 | 'Export-NotebookToPowerShellScript', 76 | 'Export-NotebookToSqlScript', 77 | 'Export-AsPowerShellNotebook', 78 | 'Find-ParameterizedCell', 79 | 'Get-DIBBlock', 80 | 'Get-Notebook', 81 | 'Get-NotebookContent', 82 | 'Get-ParameterInsertionIndex', 83 | 'Get-ParsedSql', 84 | 'Get-ParsedSqlOffsets', 85 | 'Invoke-ExecuteNotebook', 86 | 'Invoke-PowerShellNotebook', 87 | 'loadScriptDomModules', 88 | 'New-CodeCell', 89 | 'New-GistNotebook', 90 | 'New-PSNotebook', 91 | 'New-PSNotebookRunspace', 92 | 'New-SQLNotebook', 93 | 'Set-NotebookToPS', 94 | 'Test-HasParameterizedCell', 95 | 'New-InteractiveNotebook', 96 | 'Open-InteractiveNotebook', 97 | 'Test-Uri' 98 | ) 99 | 100 | # Cmdlets to export from this module 101 | #CmdletsToExport = '*' 102 | 103 | # Variables to export from this module 104 | #VariablesToExport = '*' 105 | 106 | # Aliases to export from this module 107 | AliasesToExport = @('xnb', 'psnotebook', 'codecell', 'mdcell') 108 | 109 | # List of all modules packaged with this module 110 | # ModuleList = @() 111 | 112 | # List of all files packaged with this module 113 | # FileList = @() 114 | 115 | # Private data to pass to the module specified in RootModule/ModuleToProcess 116 | PrivateData = @{ 117 | # PSData is module packaging and gallery metadata embedded in PrivateData 118 | # It's for rebuilding PowerShellGet (and PoshCode) NuGet-style packages 119 | # We had to do this because it's the only place we're allowed to extend the manifest 120 | # https://connect.microsoft.com/PowerShell/feedback/details/421837 121 | PSData = @{ 122 | # The primary categorization of this module (from the TechNet Gallery tech tree). 123 | Category = "PowerShell Notebook" 124 | 125 | # Keyword tags to help users find this module via navigations and search. 126 | Tags = @("PowerShell", "Notebook", "Jupyter", "IPYNB", "SQL", "Azure", "AzureDataStudio") 127 | 128 | # The web address of an icon which can be used in galleries to represent this module 129 | #IconUri = "http://pesterbdd.com/images/Pester.png" 130 | 131 | # The web address of this module's project or support homepage. 132 | ProjectUri = "https://github.com/dfinke/PowerShellNotebook" 133 | 134 | # The web address of this module's license. Points to a page that's embeddable and linkable. 135 | LicenseUri = "https://github.com/dfinke/PowerShellNotebook/blob/master/LICENSE" 136 | 137 | # Release notes for this particular version of the module 138 | #ReleaseNotes = $True 139 | 140 | # If true, the LicenseUrl points to an end-user license (not just a source license) which requires the user agreement before use. 141 | # RequireLicenseAcceptance = "" 142 | 143 | # Indicates this is a pre-release/testing version of the module. 144 | IsPrerelease = 'False' 145 | } 146 | } 147 | 148 | # HelpInfo URI of this module 149 | # HelpInfoURI = '' 150 | 151 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 152 | # DefaultCommandPrefix = '' 153 | 154 | } -------------------------------------------------------------------------------- /PowerShellNotebook.psm1: -------------------------------------------------------------------------------- 1 | . $PSScriptRoot\ConvertFrom-IPYNB.ps1 2 | . $PSScriptRoot\ConvertFrom-DIB.ps1 3 | . $PSScriptRoot\ConvertFromNotebookToMarkdown.ps1 4 | . $PSScriptRoot\ConvertMarkdownToNoteBook.ps1 5 | . $PSScriptRoot\ConvertToDib.ps1 6 | . $PSScriptRoot\ConvertToPowerShellNoteBook.ps1 7 | . $PSScriptRoot\ConvertToSQLNoteBook.ps1 8 | . $PSScriptRoot\ExportAsPowerShellNotebook.ps1 9 | . $PSScriptRoot\ExportNotebookToPowerShellScript.ps1 10 | . $PSScriptRoot\ExportNotebookToSqlScript.ps1 11 | . $PSScriptRoot\FindParameterizedCell.ps1 12 | . $PSScriptRoot\Get-DIBBlock.ps1 13 | . $PSScriptRoot\GetNotebook.ps1 14 | . $PSScriptRoot\GetNotebookContent.ps1 15 | . $PSScriptRoot\GetNotebookDisplayData.ps1 16 | . $PSScriptRoot\GetParameterInsertionIndex.ps1 17 | . $PSScriptRoot\InvokePowerShellNotebook.ps1 18 | . $PSScriptRoot\InvokeExecuteNotebook.ps1 19 | . $PSScriptRoot\New-InteractiveNotebook.ps1 20 | . $PSScriptRoot\NewCodeCell.ps1 21 | . $PSScriptRoot\NewGistNotebook.ps1 22 | . $PSScriptRoot\Open-InteractiveNotebook.ps1 23 | . $PSScriptRoot\PowerShellNotebookDSL.ps1 24 | . $PSScriptRoot\SetNotebookToPs.ps1 25 | . $PSScriptRoot\TestAzureBlobStorageUrl.ps1 26 | . $PSScriptRoot\TestHasParameterizedCell.ps1 27 | . $PSScriptRoot\TestUri.ps1 28 | 29 | Set-Alias xnb Invoke-ExecuteNotebook 30 | -------------------------------------------------------------------------------- /PublishToGallery.ps1: -------------------------------------------------------------------------------- 1 | $p = @{ 2 | Name = "PowerShellNotebook" 3 | NuGetApiKey = $NuGetApiKey 4 | } 5 | 6 | Publish-Module @p -------------------------------------------------------------------------------- /SetNotebookToPs.ps1: -------------------------------------------------------------------------------- 1 | Function Set-NotebookToPS { 2 | <# 3 | .Synopsis 4 | Re-write metadata from a VSCode add-in - C# with PWSH magic commands to server Jupyter style 5 | #> 6 | param ( 7 | [Parameter(ValueFromPipeline = $true)] 8 | $Path, 9 | $OutputPath, 10 | [alias('PT')] 11 | [switch]$PassThru 12 | ) 13 | process { 14 | #Re-write metadata from a VSCode add-in - C# with PWSH magic commands to server Jupyter style 15 | $nb = Get-Content $Path | ConvertFrom-Json -Depth 10 16 | $nb.metadata.kernelspec.display_name = '.NET (PowerShell)' # ".NET (C#)", / ".NET (F#)" 17 | $nb.metadata.kernelspec.language = 'PowerShell' # "C#" / "F#" 18 | $nb.metadata.kernelspec.name = '.net-powershell' #".net-csharp" / ".net-fsharp" 19 | $nb.metadata.language_info.file_extension = '.ps1' # ".cs", / ".fs", 20 | $nb.metadata.language_info.mimetype = 'text/x-powershell' # "text/x-csharp", / "text/x-fsharp" 21 | $nb.metadata.language_info.name = 'PowerShell' # "csharp", / "csharp" <-bug?? 22 | $nb.metadata.language_info.pygments_lexer = 'powerShell' # "csharp", / "fsharp" 23 | $nb.metadata.language_info.version = '7.0' # "8.0" / "4.5" 24 | foreach ($cell in $nb.cells.where({$_.cell_Type -eq 'code' -and 25 | $_.source[0] -match '^#!pwsh'})) { 26 | $cell.source = $cell.source[1..$cell.source.count] 27 | } 28 | 29 | if (-not $OutputPath) {$OutputPath = $Path} 30 | ConvertTo-Json $nb -Depth 10 | 31 | Out-File -Encoding utf8 -Path $OutputPath 32 | 33 | if ($PassThru) {Get-Item $OutputPath} 34 | } 35 | } -------------------------------------------------------------------------------- /TestAzureBlobStorageUrl.ps1: -------------------------------------------------------------------------------- 1 | function Test-AzureBlobStorageUrl { 2 | param( 3 | $Url 4 | ) 5 | 6 | $pattern = "abs://(.*)\.blob\.core\.windows\.net\/(.*)\/(.*)\?(.*)$" 7 | 8 | [regex]::Match($Url, $pattern).Success 9 | } -------------------------------------------------------------------------------- /TestHasParameterizedCell.ps1: -------------------------------------------------------------------------------- 1 | function Test-HasParameterizedCell { 2 | param( 3 | [Parameter(ValueFromPipelineByPropertyName)] 4 | [Alias("FullName")] 5 | $InputNotebook 6 | ) 7 | 8 | Process { 9 | 10 | $content = Get-NotebookContent $InputNotebook -PassThru 11 | $HasParameterizedCell = $false 12 | foreach ($tag in $content.cells.metadata.tags) { 13 | if ($tag -eq 'parameters') { 14 | $HasParameterizedCell = $true 15 | break 16 | } 17 | } 18 | 19 | [PSCustomObject][Ordered]@{ 20 | HasParameterizedCell = $HasParameterizedCell 21 | Path = $InputNotebook 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /TestUri.ps1: -------------------------------------------------------------------------------- 1 | function Test-Uri { 2 | param( 3 | $FullName 4 | ) 5 | 6 | [System.Uri]::IsWellFormedUriString($FullName, [System.UriKind]::Absolute) 7 | } -------------------------------------------------------------------------------- /Watch-Directory.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | File change watcher and handler. 4 | Author: Roman Kuzmin 5 | 6 | .Description 7 | The script watches for changed, created, deleted, and renamed files in the 8 | given directories. On changes it invokes the specified command with change 9 | info. It is a dictionary where keys are changed file paths, values are last 10 | change types. 11 | 12 | If the command is omitted then the script outputs change info as text. 13 | 14 | The script works until it is forcedly stopped (Ctrl-C). 15 | 16 | .Parameter Path 17 | Specifies the watched directory paths. 18 | .Parameter Command 19 | Specifies the command to process changes. 20 | It may be a script block or a command name. 21 | .Parameter Filter 22 | Simple and effective file system filter. Default *.* 23 | .Parameter Include 24 | Inclusion regular expression pattern applied after Filter. 25 | .Parameter Exclude 26 | Exclusion regular expression pattern applied after Filter. 27 | .Parameter Recurse 28 | Tells to watch files in subdirectories as well. 29 | .Parameter TestSeconds 30 | Time to sleep between checks for change events. 31 | .Parameter WaitSeconds 32 | Time to wait after the last change before processing. 33 | 34 | .Link 35 | https://github.com/nightroman/PowerShelf 36 | #> 37 | 38 | param( 39 | [Parameter(Position = 1, Mandatory = 1)] 40 | [string[]]$Path, 41 | [Parameter(Position = 2)] 42 | $Command, 43 | [string]$Filter, 44 | [string]$Include, 45 | [string]$Exclude, 46 | [switch]$Recurse, 47 | [int]$TestSeconds = 5, 48 | [int]$WaitSeconds = 5 49 | ) 50 | 51 | trap { $PSCmdlet.ThrowTerminatingError($_) } 52 | 53 | $Path = foreach ($_ in $Path) { 54 | $_ = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($_) 55 | if (!([System.IO.Directory]::Exists($_))) { 56 | throw "Missing directory: $_" 57 | } 58 | $_ 59 | } 60 | 61 | Add-Type @' 62 | using System; 63 | using System.Collections.Generic; 64 | using System.Collections.Specialized; 65 | using System.IO; 66 | using System.Text.RegularExpressions; 67 | 68 | public class FileSystemWatcherHelper : IDisposable 69 | { 70 | public string[] Path; 71 | public string Filter; 72 | public bool Recurse; 73 | 74 | public DateTime LastTime { get { return _lastTime; } } 75 | public bool HasChanges { get { return _changes.Count > 0; } } 76 | 77 | OrderedDictionary _changes = new OrderedDictionary(); 78 | readonly List _watchers = new List(); 79 | readonly object _lock = new object(); 80 | DateTime _lastTime; 81 | Regex _include; 82 | Regex _exclude; 83 | 84 | public void Include(string pattern) 85 | { 86 | if (!string.IsNullOrEmpty(pattern)) 87 | _include = new Regex(pattern, RegexOptions.IgnoreCase); 88 | } 89 | public void Exclude(string pattern) 90 | { 91 | if (!string.IsNullOrEmpty(pattern)) 92 | _exclude = new Regex(pattern, RegexOptions.IgnoreCase); 93 | } 94 | public void Start() 95 | { 96 | foreach (string p in Path) 97 | { 98 | FileSystemWatcher watcher = new FileSystemWatcher(p); 99 | _watchers.Add(watcher); 100 | 101 | watcher.IncludeSubdirectories = Recurse; 102 | watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite; 103 | if (!string.IsNullOrEmpty(Filter)) 104 | watcher.Filter = Filter; 105 | 106 | watcher.Created += OnChanged; 107 | watcher.Changed += OnChanged; 108 | watcher.Deleted += OnChanged; 109 | watcher.Renamed += OnChanged; 110 | watcher.EnableRaisingEvents = true; 111 | } 112 | } 113 | public object GetChanges() 114 | { 115 | lock (_lock) 116 | { 117 | object r = _changes; 118 | _changes = new OrderedDictionary(); 119 | return r; 120 | } 121 | } 122 | public void Dispose() 123 | { 124 | foreach (FileSystemWatcher watcher in _watchers) 125 | watcher.Dispose(); 126 | _watchers.Clear(); 127 | _changes.Clear(); 128 | } 129 | void OnChanged(object sender, FileSystemEventArgs e) 130 | { 131 | if (_include != null && !_include.IsMatch(e.Name)) 132 | return; 133 | 134 | if (_exclude != null && _exclude.IsMatch(e.Name)) 135 | return; 136 | 137 | lock (_lock) 138 | { 139 | _changes[e.FullPath] = e.ChangeType; 140 | _lastTime = DateTime.Now; 141 | } 142 | } 143 | } 144 | '@ 145 | 146 | $watcher = New-Object FileSystemWatcherHelper 147 | $watcher.Path = $Path 148 | $watcher.Filter = $Filter 149 | $watcher.Recurse = $Recurse 150 | try { $watcher.Include($Include) } catch { throw "Parameter Include: $_" } 151 | try { $watcher.Exclude($Exclude) } catch { throw "Parameter Exclude: $_" } 152 | try { 153 | $watcher.Start() 154 | for () { 155 | Start-Sleep -Seconds $TestSeconds 156 | 157 | if (!$watcher.HasChanges) { continue } 158 | if (([datetime]::Now - $watcher.LastTime).TotalSeconds -lt $WaitSeconds) { continue } 159 | 160 | $changes = $watcher.GetChanges() 161 | if ($Command) { 162 | try { 163 | & $Command $changes 164 | } 165 | catch { 166 | "$($_.ToString())`r`n$($_.InvocationInfo.PositionMessage)" 167 | } 168 | } 169 | else { 170 | foreach ($kv in $changes.GetEnumerator()) { 171 | "$($kv.Value) $($kv.Key)" 172 | } 173 | } 174 | } 175 | } 176 | finally { 177 | $watcher.Dispose() 178 | } -------------------------------------------------------------------------------- /__tests__/ChartNotebooks/charts.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 6, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/html": [ 11 | "
\r\n", 12 | "\r\n" 36 | ] 37 | }, 38 | "metadata": {}, 39 | "output_type": "display_data" 40 | } 41 | ], 42 | "source": [ 43 | "$chart = [Graph.Bar]::new()\n", 44 | "\n", 45 | "$chart.x = 10,7,12\n", 46 | "$chart.y = 'lions', 'tigers', 'bears'\n", 47 | "$chart.orientation='h'\n", 48 | "\n", 49 | "New-PlotlyChart -Trace $chart | Out-Display\n" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [] 58 | } 59 | ], 60 | "metadata": { 61 | "kernelspec": { 62 | "display_name": ".NET (PowerShell)", 63 | "language": "PowerShell", 64 | "name": ".net-powershell" 65 | }, 66 | "language_info": { 67 | "file_extension": ".ps1", 68 | "mimetype": "text/x-powershell", 69 | "name": "PowerShell", 70 | "pygments_lexer": "powershell", 71 | "version": "7.0" 72 | } 73 | }, 74 | "nbformat": 4, 75 | "nbformat_minor": 4 76 | } 77 | -------------------------------------------------------------------------------- /__tests__/ConvertFromNotebookToMarkdown.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test ConvertFrom-NoteBookToMarkdown" -Tag ConvertFromNoteBookToMarkdown { 4 | It "Should convert to markdown" { 5 | $targetFile = "$PSScriptRoot\GoodNotebooks\SimpleNotebookToTestConvertToMarkdown.ipynb" 6 | 7 | $actual = ConvertFrom-NoteBookToMarkdown -NotebookName $targetFile -AsText 8 | 9 | $actual.Count | Should -Be 3 10 | 11 | $actual[0].trim() | Should -Beexactly '# Test for converting a PS Notebook to Markdown' 12 | $actual[2].trim() | Should -Beexactly '## End of PS Notebook' 13 | $actual[1].trim() | Should -match '^```PowerShell' 14 | $actual[1].Trim( )| Should -match '```$' 15 | } 16 | 17 | It "Should convert to markdown in a file" { 18 | $targetFile = "$PSScriptRoot\GoodNotebooks\SimpleNotebookToTestConvertToMarkdown.ipynb" 19 | $mdFile = "$PSScriptRoot\GoodNotebooks\SimpleNotebookToTestConvertToMarkdown.md" 20 | $expected = Split-Path $mdFile -Leaf 21 | 22 | $actual = ConvertFrom-NoteBookToMarkdown -NotebookName $targetFile 23 | 24 | $actual | Should -Match "$expected`$" 25 | 26 | Remove-Item $expected -ErrorAction SilentlyContinue 27 | } 28 | } -------------------------------------------------------------------------------- /__tests__/ConvertMarkdownToNotebook.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test Convert-MarkdownToPowerShellNoteBook" -Tag ConvertMarkdownToPowerShellNoteBook { 4 | BeforeAll { 5 | $script:expectedPSNBFilename = "$PSScriptRoot\samplemarkdown\demo.ipynb" 6 | } 7 | 8 | BeforeEach { 9 | Remove-Item $script:expectedPSNBFilename -ErrorAction SilentlyContinue 10 | } 11 | 12 | AfterAll { 13 | Remove-Item $script:expectedPSNBFilename -ErrorAction SilentlyContinue 14 | } 15 | 16 | It "Should create a PSNotebookRunspace " { 17 | $actual = New-PSNotebookRunspace 18 | 19 | $actual | Should -Not -Be $null 20 | $actual.GetType().Name | Should -Be 'PSNotebookRunspace' 21 | } 22 | 23 | It "Should return this after Invoke" { 24 | $obj = New-PSNotebookRunspace 25 | 26 | $actual = $obj.Invoke("1+1") 27 | 28 | $actual | Should -Be 2 29 | } 30 | 31 | It "Should create a notebook file" { 32 | $sourceMD = "$PSScriptRoot\samplemarkdown\demo.md" 33 | Convert-MarkdownToNoteBook -filename $sourceMD 34 | (Test-Path $script:expectedPSNBFilename) | Should -Be $true 35 | } 36 | 37 | It "Check the PS NB content" { 38 | $sourceMD = "$PSScriptRoot\samplemarkdown\demo.md" 39 | Convert-MarkdownToNoteBook -filename $sourceMD 40 | (Test-Path $script:expectedPSNBFilename) | Should -Be $true 41 | 42 | $psnb = Get-Content $script:expectedPSNBFilename | ConvertFrom-Json 43 | 44 | $psnb.cells.count | Should -Be 4 45 | 46 | $psnb.cells[0].cell_type | Should -Be markdown 47 | $psnb.cells[0].source | Should -Beexactly '# Chapter 1' 48 | 49 | $psnb.cells[1].cell_type | Should -Be markdown 50 | $psnb.cells[1].source.trim() | Should -Beexactly 'This is `addition`' 51 | 52 | $psnb.cells[2].cell_type | Should -Be code 53 | $psnb.cells[2].source.trim() | Should -Beexactly "5 + 7" 54 | $psnb.cells[2].outputs.text.trim() | Should -Beexactly "12" 55 | 56 | $psnb.cells[3].cell_type | Should -Be markdown 57 | } 58 | 59 | It "Check the PowerShell fence block NB content" { 60 | $sourceMD = "$PSScriptRoot\samplemarkdown\demoPowerShellFenceBlock.md" 61 | $nbFile = "$PSScriptRoot\samplemarkdown\demoPowerShellFenceBlock.ipynb" 62 | 63 | Convert-MarkdownToNoteBook -filename $sourceMD 64 | 65 | (Test-Path $nbFile) | Should -Be $true 66 | 67 | $psnb = Get-Content $nbFile | ConvertFrom-Json 68 | 69 | $psnb.cells.count | Should -Be 4 70 | 71 | $psnb.cells[0].cell_type | Should -Be markdown 72 | $psnb.cells[0].source | Should -Beexactly '# Chapter 1' 73 | 74 | $psnb.cells[1].cell_type | Should -Be markdown 75 | $psnb.cells[1].source.trim() | Should -Beexactly 'This is `addition`' 76 | 77 | $psnb.cells[2].cell_type | Should -Be code 78 | $psnb.cells[2].source.trim() | Should -Beexactly "40 + 2" 79 | $psnb.cells[2].outputs.text.trim() | Should -Beexactly "42" 80 | 81 | $psnb.cells[3].cell_type | Should -Be markdown 82 | Remove-Item $nbFile -ErrorAction SilentlyContinue 83 | } 84 | 85 | It "Should exclude results from PowerShell Notebook" { 86 | $sourceMD = "$PSScriptRoot\samplemarkdown\excludeResults.md" 87 | Convert-MarkdownToNoteBook -filename $sourceMD 88 | $expectedOutFileName = "$PSScriptRoot\samplemarkdown\excludeResults.ipynb" 89 | 90 | (Test-Path $expectedOutFileName) | Should -Be $true 91 | 92 | $psnb = Get-Content $expectedOutFileName | ConvertFrom-Json 93 | 94 | $codeBlocks = $psnb.cells | Where-Object { $_.cell_type -eq 'code' } 95 | $codeBlocks.count | Should -Be 3 96 | 97 | $codeBlocks[0].outputs.text | Should -BenullorEmpty 0 98 | $codeBlocks[1].outputs.text | Should -BenullorEmpty 0 99 | 100 | $codeBlocks[2].outputs.text | Should -match "4[\r\n]+" 101 | 102 | Remove-Item $expectedOutFileName -Force -ErrorAction SilentlyContinue 103 | } 104 | 105 | It "Should convert more than one chapter" { 106 | $sourceMD = "$PSScriptRoot\MultipleChapters\MultipleChapters.md" 107 | 108 | Convert-MarkdownToNoteBook -filename $sourceMD 109 | 110 | $expectedOutFileName = "$PSScriptRoot\MultipleChapters\MultipleChapters.ipynb" 111 | 112 | (Test-Path $expectedOutFileName) | Should -Be $true 113 | 114 | $psnb = Get-Content $expectedOutFileName | ConvertFrom-Json 115 | 116 | $markdownBlocks = $psnb.cells | Where-Object { $_.cell_type -eq 'markdown' } 117 | $markdownBlocks.count | Should -Be 5 118 | 119 | $markdownBlocks[0].source | Should -Be "# Chapter 1" 120 | $markdownBlocks[2].source | Should -Be "# SUMMARY`r`n" 121 | $markdownBlocks[3].source | Should -Be "# Chapter 2" 122 | 123 | $result = $markdownBlocks[4].source -notmatch "^# SUMMARY" 124 | $result | Should -Be $true 125 | 126 | Remove-Item $expectedOutFileName -Force -ErrorAction SilentlyContinue 127 | } 128 | } -------------------------------------------------------------------------------- /__tests__/ConvertToPowerShellNotebook.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test ConvertTo-PowerShellNoteBook" -Tag "ConvertTo-PowerShellNoteBook" { 4 | It "Should convert the file to an ipynb" { 5 | $demoTextFile = "$PSScriptRoot\DemoFiles\demo.txt" 6 | $fullName = "TestDrive:\testConverted.ipynb" 7 | 8 | ConvertTo-PowerShellNoteBook -InputFileName $demoTextFile -OutputNotebookName $fullName 9 | { Test-Path $fullName } | Should -Be $true 10 | 11 | $actual = Get-NotebookContent -Path $fullName 12 | $actual.Count | Should -Be 8 13 | 14 | $actual = Get-NotebookContent -Path $fullName -JustCode 15 | 16 | $actual.Count | Should -Be 4 17 | $actual[0].Source | Should -BeExactly 'ps | select -first 10' 18 | $actual[1].Source | Should -BeExactly 'gsv | select -first 10' 19 | $actual[2].Source | Should -BeExactly 'function SayHello($p) {"Hello $p"}' 20 | $actual[3].Source | Should -BeExactly 'SayHello World' 21 | 22 | $actual = Get-NotebookContent -Path $fullName -JustMarkdown 23 | 24 | $actual.Count | Should -Be 4 25 | $actual[0].Source | Should -BeExactly '# Get first 10 process' 26 | $actual[1].Source | Should -BeExactly '# Get first 10 services' 27 | $actual[2].Source | Should -BeExactly '# Create a function' 28 | $actual[3].Source | Should -BeExactly '# Use the function' 29 | } 30 | 31 | It "Should convert the file with a single comment and single line of code to an ipynb" { 32 | $demoTextFile = "$PSScriptRoot\DemoFiles\demo_SingleCommentSingleLineCodeBlock.ps1" 33 | $fullName = "TestDrive:\testConverted.ipynb" 34 | 35 | ConvertTo-PowerShellNoteBook -InputFileName $demoTextFile -OutputNotebookName $fullName 36 | { Test-Pat $fullName } | Should -Be $true 37 | 38 | $actual = Get-NotebookContent -Path $fullName 39 | $actual.Count | Should -Be 2 40 | 41 | $actual = Get-NotebookContent -Path $fullName -JustCode 42 | 43 | @($actual).Count | Should -Be 1 44 | $actual[0].Source | Should -BeExactly 'ps | select -first 10' 45 | 46 | $actual = Get-NotebookContent -Path $fullName -JustMarkdown 47 | 48 | @($actual).Count | Should -Be 1 49 | $actual[0].Source | Should -BeExactly '# Get first 10 process' 50 | } 51 | 52 | It "Should convert the file to an ipynb" { 53 | $demoTextFile = "$PSScriptRoot\DemoFiles\GetParsedSqlOffsets.ps1" 54 | $fullName = "TestDrive:\GetParsedSqlOffsets.ipynb" 55 | 56 | ConvertTo-PowerShellNoteBook -InputFileName $demoTextFile -OutputNotebookName $fullName 57 | { Test-Path $fullName } | Should -Be $true 58 | 59 | $actual = Get-NotebookContent -Path $fullName 60 | $actual.Count | Should -Be 13 61 | 62 | $actual = Get-NotebookContent -Path $fullName -JustCode 63 | 64 | $actual.Count | Should -Be 7 65 | $actual[0].Source | Should -BeExactly 'function Get-ParsedSqlOffsets{ 66 | [CmdletBinding()] 67 | param( 68 | $ScriptPath 69 | )' 70 | 71 | $actual = Get-NotebookContent -Path $fullName -JustMarkdown 72 | 73 | $actual.Count | Should -Be 6 74 | $actual[2].Source | Should -BeExactly '<#################################################################################################>' 75 | } 76 | 77 | It "Test reading a ps1 from a URL" { 78 | $url = "https://raw.githubusercontent.com/dfinke/PowerShellNotebook/master/__tests__/DemoFiles/demo_SingleCommentSingleLineCodeBlock.ps1" 79 | $outputNotebook = "TestDrive:\testConverted.ipynb" 80 | 81 | ConvertTo-PowerShellNoteBook -InputFileName $url -OutputNotebookName $outputNotebook 82 | 83 | Test-Path $outputNotebook | Should -BeTrue 84 | 85 | $actual = Get-NotebookContent -Path $outputNotebook 86 | 87 | <# 88 | Type : code 89 | Source : # Get first 10 process 90 | ps | select -first 10 91 | #> 92 | 93 | $actual.Count | Should -Be 1 94 | $actual.Type | Should -BeExactly 'code' 95 | $actual.Source | Should -Not -BeNullOrEmpty 96 | $actual.Source.Length | Should -Be 45 97 | } 98 | 99 | It "Test reading from multiple inputs" -Skip { 100 | $( 101 | 'https://raw.githubusercontent.com/dfinke/PowerShellNotebook/master/__tests__/DemoFiles/demo_SingleCommentSingleLineCodeBlock.ps1' 102 | Get-ChildItem "$PSScriptRoot\MultiplePSFiles" *.ps1 103 | ) | ConvertTo-PowerShellNoteBook 104 | 105 | $r = Get-ChildItem . -Recurse *.ipynb | Out-String 106 | 107 | $r | Out-Host 108 | (Get-ChildItem $PSScriptRoot *.ipynb).count | Should -Be 4 109 | # Get-ChildItem $PSScriptRoot *.ipynb | Remove-Item 110 | } 111 | } -------------------------------------------------------------------------------- /__tests__/ConvertToSQLNotebook.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psm1 -Force 2 | if (-not (Get-Module -ListAvailable -Name sqlserver)) { return } 3 | Describe "Test ConvertTo-SQLNoteBook" { 4 | It "Should convert the file to an ipynb with a single code cell" { 5 | $demoTextFile = "$PSScriptRoot/DemoFiles/demo.sql" 6 | $fullName = "TestDrive:\sqlTestConverted.ipynb" 7 | 8 | try{ 9 | ConvertTo-SQLNoteBook -InputFileName $demoTextFile -OutputNotebookName $fullName 10 | { Test-Path $fullName } | Should -Be $true 11 | 12 | $actual = Get-NotebookContent -Path $fullName 13 | 14 | @($actual).Count | Should -Be 1 15 | 16 | $actual = Get-NotebookContent -Path $fullName -JustMarkdown 17 | 18 | $actual.Count | Should -Be 0 19 | 20 | $actual = Get-NotebookContent -Path $fullName -JustCode 21 | 22 | @($actual).Count | Should -Be 1 23 | 24 | write-verbose "tests $($actual[0].Source)" -Verbose 25 | $actual[0].Source | Should -BeLike '*table3*' 26 | $actual[0].Source | Should -BeExactly 'select DateDiff(MI,StartDate,EndDate) AS Timetaken,* FROM table1 27 | SELECT * FROM table2 WHERE id = 1 28 | /* Test1 */ 29 | /* Multiline test 30 | 1 31 | 2 32 | */ 33 | /* Test2 */ 34 | SELECT * FROM table3 where ID = 7 35 | /* Test3 */ 36 | SELECT * FROM table4 where ID = 8' 37 | } 38 | catch [System.Management.Automation.RuntimeException]{ 39 | Write-Verbose "Runtime exception encountered" -Verbose 40 | Write-Verbose $_ -Verbose 41 | throw 42 | } 43 | } 44 | 45 | It "Should convert the file to an ipynb with 3 code and 6 text cells" { 46 | $demoTextFile = "$PSScriptRoot/DemoFiles/demo_w3GOs.sql" 47 | $fullName = "TestDrive:\sqlTestConverted_demo_w3GOs.ipynb" 48 | 49 | try{ 50 | ConvertTo-SQLNoteBook -InputFileName $demoTextFile -OutputNotebookName $fullName 51 | { Test-Path $fullName } | Should -Be $true 52 | 53 | $actual = Get-NotebookContent -Path $fullName 54 | $actual.Count | Should -Be 9 55 | 56 | $actual = Get-NotebookContent -Path $fullName -JustCode 57 | 58 | $actual.Count | Should -Be 3 59 | write-verbose "tests $($actual[0].Source)" -Verbose 60 | $actual[0].Source | Should -BeLike '*table1*' 61 | $actual[1].Source | Should -BeLike '*table3*' 62 | 63 | $actual = Get-NotebookContent -Path $fullName -JustMarkdown 64 | 65 | $actual.Count | Should -Be 6 66 | $actual[0].Source.Trim() | Should -BeExactly 'GO' 67 | $actual[1].Source.Trim() | Should -BeExactly 'Test1' 68 | $actual[2].Source.Trim() | Should -BeLike '*Multiline test*' 69 | $actual[3].Source.Trim() | Should -BeExactly 'Test2' 70 | $actual[4].Source.Trim() | Should -BeExactly 'GO' 71 | $actual[5].Source.Trim() | Should -BeExactly 'Test3' 72 | } 73 | catch [System.Management.Automation.RuntimeException]{ 74 | Write-Verbose "Runtime exception encountered" -Verbose 75 | Write-Verbose $_ -Verbose 76 | throw 77 | } 78 | } 79 | 80 | It "Should convert the file to an ipynb with 3 code and 6 text cells" { 81 | $demoTextFile = "$PSScriptRoot/DemoFiles/AdventureWorksMultiStatementSBatch_NoGO2.sql" 82 | $fullName = "TestDrive:\AdventureWorksMultiStatementSBatch_NoGO2.ipynb" 83 | 84 | try{ 85 | ConvertTo-SQLNoteBook -InputFileName $demoTextFile -OutputNotebookName $fullName 86 | { Test-Path $fullName } | Should -Be $true 87 | 88 | $actual = Get-NotebookContent -Path $fullName 89 | $actual.Count | Should -Be 7 90 | 91 | $actual = Get-NotebookContent -Path $fullName -JustCode 92 | 93 | $actual.Count | Should -Be 3 94 | write-verbose "Code cells: $($actual.Count)" -Verbose 95 | $actual[0].Source | Should -BeLike '*Person.PersonPhone*' 96 | $actual[1].Source | Should -BeLike '*GETUTCDATE*' 97 | 98 | $actual = Get-NotebookContent -Path $fullName -JustMarkdown 99 | 100 | $actual.Count | Should -Be 4 101 | $actual[0].Source.Trim() | Should -BeLike 'Look up user and phone number by last name*' 102 | $actual[1].Source.Trim() | Should -BeLike '*-- Wait for 4 seconds total after go 2' 103 | $actual[2].Source.Trim() | Should -BeLike '*note the Go 2 has been*' 104 | $actual[3].Source.Trim() | Should -BeLike 'Commenting out until the field is added*' 105 | } 106 | catch [System.Management.Automation.RuntimeException]{ 107 | Write-Verbose "Runtime exception encountered" -Verbose 108 | Write-Verbose $_ -Verbose 109 | throw 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /__tests__/DemoFiles/AdventureWorksMultiStatementSBatch_NoGO2.sql: -------------------------------------------------------------------------------- 1 | /* Look up user and phone number by last name 2 | We match on first found in case of multiple 3 | */ 4 | declare @phonenumber nvarchar(50) 5 | declare @lastname nvarchar(50) 6 | declare @busentityid INT 7 | 8 | set @lastname = 'Tamburello' 9 | 10 | select 11 | top 1 12 | @busentityid = BusinessEntityID -- Will be used to lookup phone number 13 | from Person.Person 14 | where LastName = @lastname 15 | 16 | /* Only look for phone if we found the person 17 | -- Should usually be null or 1, values over 1 not really possible 18 | */ 19 | if @busentityid is not null 20 | begin 21 | 22 | select 23 | p.FirstName, 24 | p.MiddleName, 25 | p.LastName, 26 | p.Suffix, 27 | pp.PhoneNumber 28 | from 29 | Person.Person p 30 | left join Person.PersonPhone pp 31 | on p.BusinessEntityID = pp.BusinessEntityID 32 | where 33 | p.BusinessEntityID = @busentityid 34 | 35 | /* Save the phonenumber from the selected user */ 36 | select 37 | @phonenumber = PhoneNumber 38 | from 39 | Person.Person p 40 | left join Person.PersonPhone pp 41 | on p.BusinessEntityID = pp.BusinessEntityID 42 | where 43 | p.BusinessEntityID = @busentityid 44 | 45 | end 46 | 47 | go 48 | 49 | -- Wait for 4 seconds total after go 2 50 | RAISERROR('waiting', 1,1) with nowait 51 | select GETUTCDATE() as 'Waiting' 52 | waitfor delay '00:00:02' 53 | 54 | -- note the Go 2 has been *removed* 55 | go 56 | 57 | -- This select statement returns the full customer address list 58 | select 59 | p.FirstName, 60 | p.MiddleName, 61 | p.LastName, 62 | p.Suffix, 63 | a.AddressLine1, 64 | a.AddressLine2, -- TODO: Should we concat line 1 and 2 65 | a.City, 66 | /* 67 | Debug stuff 68 | p.BusinessEntityID, 69 | bea.BusinessEntityID, 70 | bea.AddressID, 71 | a.AddressiD, 72 | sp.StateProvinceID, 73 | sp.StateProvinceID 74 | */ 75 | sp.StateProvinceCode, /* TODO: Need to add country here too, join below */ 76 | a.PostalCode--, 77 | --sp.CountryRegionCode 78 | from 79 | Person.Person p 80 | left join Person.BusinessEntityAddress bea -- Used for joining, not selected from 81 | on p.BusinessEntityID = bea.BusinessEntityID 82 | left join Person.Address a 83 | on bea.AddressID = a.AddressID 84 | left join Person.StateProvince sp 85 | on a.StateProvinceID = sp.StateProvinceID 86 | /* Commenting out until the field is added 87 | left join Person.CountryRegion cr 88 | on sp.CountryRegionCode = cr.CountryRegionCode 89 | */ 90 | 91 | -------------------------------------------------------------------------------- /__tests__/DemoFiles/GetParsedSqlOffsets.ps1: -------------------------------------------------------------------------------- 1 | function Get-ParsedSqlOffsets{ 2 | [CmdletBinding()] 3 | param( 4 | $ScriptPath 5 | ) 6 | <################################################################################################# 7 | Pre-reqs: Install the SqlServer module. 8 | #################################################################################################> 9 | Import-Module SqlServer 10 | $ScriptDom = Join-Path -Path (Get-Module -Name SqlServer).ModuleBase -ChildPath 'Microsoft.SqlServer.TransactSql.ScriptDom.dll' 11 | if((Test-Path $ScriptDom) -eq $true ) {Add-Type -LiteralPath $ScriptDom} 12 | <################################################################################################# 13 | Qucik Helper-function to turn the file into a script fragment, using scriptdom. 14 | #################################################################################################> 15 | function Get-ParsedSql($ScriptPath){ 16 | [Microsoft.SqlServer.TransactSql.ScriptDom.TSql150Parser] $parser = new-object Microsoft.SqlServer.TransactSql.ScriptDom.TSql150Parser($false) 17 | $Reader = New-Object -TypeName System.IO.StreamReader -ArgumentList $ScriptToParse 18 | $Errors = $null 19 | $ScriptFrag = $parser.Parse($Reader, [ref]$Errors) 20 | return $ScriptFrag 21 | } 22 | <#################################################################################################> 23 | function Get-ScriptFragment($ScriptPath){ 24 | [Microsoft.SqlServer.TransactSql.ScriptDom.TSql150Parser] $parser = new-object Microsoft.SqlServer.TransactSql.ScriptDom.TSql150Parser($false) 25 | $Reader = New-Object -TypeName System.IO.StreamReader -ArgumentList $ScriptPath 26 | $Errors = $null 27 | $ScriptFrag = $parser.Parse($Reader, [ref]$Errors) 28 | # Look for MultilineComment within Statements 29 | ($ScriptFrag.ScriptTokenStream).where({$_.TokenType -eq 'MultilineComment'}) 30 | } 31 | <################################################################################################# 32 | Checking for Batch length 33 | #################################################################################################> 34 | $s = Get-Content -Raw ( Resolve-Path $ScriptToParse ) 35 | $ParsedSql = Get-ParsedSql $ScriptToParse 36 | $SqlBatches = @() 37 | $SqlBatch = @() 38 | $id=1 39 | foreach($Batch in $ParsedSql.Batches) { 40 | $SqlBatch=[pscustomobject][Ordered]@{ 41 | StartOffset = $Batch.StartOffset; 42 | StopOffset = $Batch.StartOffset+$Batch.FragmentLength; 43 | Length = $Batch.FragmentLength; 44 | StartColumn = $Batch.StartColumn; 45 | BatchId = $id; 46 | BlockType = 'Code'; 47 | Text = $s.Substring($Batch.StartOffset, $Batch.FragmentLength) 48 | } 49 | $SqlBatches+=$SqlBatch 50 | $id++ 51 | } 52 | 53 | $ScriptFrags = Get-ScriptFragment -ScriptPath $ScriptToParse 54 | $Comments = @() 55 | $Comment = @() 56 | foreach($Frag in $ScriptFrags ) { 57 | $Comment=[pscustomobject][Ordered]@{ 58 | StartOffset = $Frag.Offset; 59 | StopOffset = $Frag.Offset+$Frag.Text.Length; 60 | Length = $Frag.Text.Length; 61 | StartColumn = $Frag.Column; 62 | CommentLocation = $null; 63 | BlockType = 'Comment'; 64 | Text = $Frag.Text 65 | } 66 | 67 | foreach($SqlBatch in $SqlBatches){ 68 | 69 | if($Comment.StartOffset -ge $SqlBatch.StartOffset -and $Comment.StartOffset -le $SqlBatch.StopOffset) 70 | {$Comment.CommentLocation = "Within SQL Batch $($SqlBatch.BatchId)"} 71 | else {if($Comment.CommentLocation -notlike '*Within*'){$Comment.CommentLocation = "Outside"}} 72 | } 73 | $Comments+=$Comment 74 | } 75 | 76 | <################################################################################################# 77 | This is the basic product. 78 | #################################################################################################> 79 | $NotebookBlocks = $SqlBatches + ($Comments | WHERE { $_.CommentLocation -eq 'Outside' }) 80 | $NotebookBlocks | SORT StartOffset 81 | } -------------------------------------------------------------------------------- /__tests__/DemoFiles/ParsingExamples.ps1: -------------------------------------------------------------------------------- 1 | # # This is file is an example of what _should_ happen when converting a .PS1 file to a .IPYNB file. 2 | ps | select -first 10 3 | 4 | # Get first 10 services 5 | gsv | select -first 10 6 | 7 | # Create a function 8 | function SayHello($p) {"Hello $p"} 9 | 10 | # Use the function 11 | SayHello World -------------------------------------------------------------------------------- /__tests__/DemoFiles/demo.sql: -------------------------------------------------------------------------------- 1 | select DateDiff(MI,StartDate,EndDate) AS Timetaken,* FROM table1 2 | SELECT * FROM table2 WHERE id = 1 3 | /* Test1 */ 4 | /* Multiline test 5 | 1 6 | 2 7 | */ 8 | /* Test2 */ 9 | SELECT * FROM table3 where ID = 7 10 | /* Test3 */ 11 | SELECT * FROM table4 where ID = 8 -------------------------------------------------------------------------------- /__tests__/DemoFiles/demo.txt: -------------------------------------------------------------------------------- 1 | # Get first 10 process 2 | ps | select -first 10 3 | 4 | # Get first 10 services 5 | gsv | select -first 10 6 | 7 | # Create a function 8 | function SayHello($p) {"Hello $p"} 9 | 10 | # Use the function 11 | SayHello World -------------------------------------------------------------------------------- /__tests__/DemoFiles/demo_SingleCommentSingleLineCodeBlock.ps1: -------------------------------------------------------------------------------- 1 | # Get first 10 process 2 | ps | select -first 10 -------------------------------------------------------------------------------- /__tests__/DemoFiles/demo_w3GOs.sql: -------------------------------------------------------------------------------- 1 | select DateDiff(MI,StartDate,EndDate) AS Timetaken,* FROM table1 2 | SELECT * FROM table2 WHERE id = 1 3 | GO 4 | /* Test1 */ 5 | /* Multiline test 6 | 1 7 | 2 8 | */ 9 | /* Test2 */ 10 | SELECT * FROM table3 where ID = 7 11 | GO 12 | /* Test3 */ 13 | SELECT * FROM table4 where ID = 8 -------------------------------------------------------------------------------- /__tests__/DotNetInteractiveNotebooks/AllDotNetInteractive.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "Console.WriteLine(\"Hello from C#\");" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": { 16 | "dotnet_interactive": { 17 | "language": "fsharp" 18 | } 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "\"Hello from F#\" |> Console.WriteLine" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "metadata": { 29 | "dotnet_interactive": { 30 | "language": "pwsh" 31 | } 32 | }, 33 | "outputs": [], 34 | "source": [ 35 | "\"Hello from PowerShell\"" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "metadata": { 42 | "dotnet_interactive": { 43 | "language": "sql" 44 | } 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "SELECT * FROM table" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": { 55 | "dotnet_interactive": { 56 | "language": "csharp" 57 | } 58 | }, 59 | "outputs": [], 60 | "source": [ 61 | "Console.WriteLine(\"Hello again from C#\");" 62 | ] 63 | } 64 | ], 65 | "metadata": { 66 | "kernelspec": { 67 | "display_name": ".NET (C#)", 68 | "language": "C#", 69 | "name": ".net-csharp" 70 | }, 71 | "language_info": { 72 | "file_extension": ".cs", 73 | "mimetype": "text/x-csharp", 74 | "name": "C#", 75 | "pygments_lexer": "csharp", 76 | "version": "8.0" 77 | }, 78 | "orig_nbformat": 2 79 | }, 80 | "nbformat": 4, 81 | "nbformat_minor": 2 82 | } -------------------------------------------------------------------------------- /__tests__/DotNetInteractiveNotebooks/ExportNotebookToPowerShellScript.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test Export-NotebookToPowerShellScript" -Tag 'Export-NotebookToPowerShellScript' { 4 | It "Should create ps1 file with correct contents" { 5 | $outPath = "TestDrive:\" 6 | 7 | Export-NotebookToPowerShellScript -FullName "$PSScriptRoot\TestWithInvokePS.ipynb" -outPath $outPath 8 | $ps1File = "$outPath\TestWithInvokePS.ps1" 9 | 10 | Test-Path $ps1File | Should -Be $true 11 | 12 | $contents = Get-Content $ps1File 13 | 14 | $contents[0] | Should -BeExactly '<#' 15 | $contents[1] | Should -match '^\s*Created from:.*TestWithInvokePS.ipynb$' 16 | $contents[2] | Should -benullorempty 17 | $contents[3] | Should -match '^\s*Created by:' 18 | $contents[4] | Should -match '^\s*Created on:' 19 | $contents[5] | Should -BeExactly '#>' 20 | $contents[7] | Should -BeExactly '$PSVersionTable' 21 | $contents[8] | Should -match '<#\s+#>' 22 | $contents[9] | Should -BeExactly '1..10 | % {' 23 | $contents[10] | Should -BeExactly ' $_ * 2' 24 | $contents[11] | Should -BeExactly '}' 25 | } 26 | 27 | It "Should export the ipynb to ps1" { 28 | $ipynbFileName = "$PSScriptRoot\..\MultiLineSourceNotebooks\MultiLineSourceAsArray.ipynb" 29 | Export-NotebookToPowerShellScript -FullName $ipynbFileName 30 | $ps1File = "./MultiLineSourceAsArray.ps1" 31 | 32 | Test-Path $ps1File | Should -Be $true 33 | 34 | $actual = Get-Content $ps1File 35 | 36 | $actual.Count | Should -Be 10 37 | 38 | $actual[7] | Should -Be 'foreach ($item in 1..10) {' 39 | $actual[8] | Should -Be ' $item' 40 | $actual[9] | Should -Be '}' 41 | 42 | Remove-Item $ps1File -ErrorAction SilentlyContinue 43 | } 44 | 45 | It "Should export the ipynb from a URL to ps1" { 46 | $url = "https://raw.githubusercontent.com/dfinke/PowerShellNotebook/AddJupyterNotebookMetaInfo/samplenotebook/powershell.ipynb" 47 | 48 | Export-NotebookToPowerShellScript -FullName $url 49 | 50 | $ps1File = "./powershell.ps1" 51 | Test-Path $ps1File | Should -Be $true 52 | 53 | $contents = Get-Content $ps1File 54 | 55 | $contents[-1] | Should -BeExactly 'Write-Host "hello world"' 56 | 57 | Remove-Item $ps1File -ErrorAction SilentlyContinue 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /__tests__/DotNetInteractiveNotebooks/PSInteractive.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "dotnet_interactive": { 8 | "language": "pwsh" 9 | } 10 | }, 11 | "source": [ 12 | "$max = !$max ? 10 : $max" 13 | ], 14 | "outputs": [] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 1, 19 | "metadata": { 20 | "dotnet_interactive": { 21 | "language": "pwsh" 22 | } 23 | }, 24 | "source": [ 25 | "1..$max | ? {$_ % 2 -eq 1}" 26 | ], 27 | "outputs": [] 28 | } 29 | ], 30 | "metadata": { 31 | "kernelspec": { 32 | "display_name": ".NET (C#)", 33 | "language": "C#", 34 | "name": ".net-csharp" 35 | }, 36 | "language_info": { 37 | "file_extension": ".cs", 38 | "mimetype": "text/x-csharp", 39 | "name": "C#", 40 | "pygments_lexer": "csharp", 41 | "version": "8.0" 42 | } 43 | }, 44 | "nbformat": 4, 45 | "nbformat_minor": 4 46 | } -------------------------------------------------------------------------------- /__tests__/DotNetInteractiveNotebooks/TestWithInvokePS.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "\n", 13 | "Name Value\n", 14 | "---- -----\n", 15 | "PSVersion 7.0.0\n", 16 | "PSEdition Core\n", 17 | "GitCommitId 7.0.0\n", 18 | "OS Microsoft Windows 10.0.19041\n", 19 | "Platform Win32NT\n", 20 | "PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}\n", 21 | "PSRemotingProtocolVersion 2.3\n", 22 | "SerializationVersion 1.1.0.1\n", 23 | "WSManStackVersion 3.0\n", 24 | "\n" 25 | ] 26 | } 27 | ], 28 | "source": [ 29 | "$PSVersionTable" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 3, 35 | "metadata": {}, 36 | "outputs": [ 37 | { 38 | "name": "stdout", 39 | "output_type": "stream", 40 | "text": [ 41 | "2\n", 42 | "4\n", 43 | "6\n", 44 | "8\n", 45 | "10\n", 46 | "12\n", 47 | "14\n", 48 | "16\n", 49 | "18\n", 50 | "20\n" 51 | ] 52 | } 53 | ], 54 | "source": [ 55 | "1..10 | % {\n", 56 | " $_ * 2\n", 57 | "}" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [] 66 | } 67 | ], 68 | "metadata": { 69 | "kernelspec": { 70 | "display_name": ".NET (PowerShell)", 71 | "language": "PowerShell", 72 | "name": ".net-powershell" 73 | }, 74 | "language_info": { 75 | "file_extension": ".ps1", 76 | "mimetype": "text/x-powershell", 77 | "name": "PowerShell", 78 | "pygments_lexer": "powershell", 79 | "version": "7.0" 80 | } 81 | }, 82 | "nbformat": 4, 83 | "nbformat_minor": 4 84 | } 85 | -------------------------------------------------------------------------------- /__tests__/ExportAsPowerShellNotebook.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test Export-AsPowerShellNotebook" -Tag Export-AsPowerShellNotebook { 4 | It "Test throw if OutputNotebook not specified" { 5 | $s = "get-process *micro* | select -first 5" 6 | 7 | { $s | Export-AsPowerShellNotebook -OutputNotebook $outputNotebook } | Should -Throw '$OutputNotebook not specified' 8 | } 9 | 10 | It "Test create notebook from strings of PS code" { 11 | $outputNotebook = "TestDrive:\test.ipynb" 12 | 13 | $( 14 | "# get-process" 15 | "get-process *micro* | select -first 5" 16 | "# get-service" 17 | "get-service | select -first 5" 18 | ) | Export-AsPowerShellNotebook -OutputNotebook $outputNotebook 19 | 20 | Test-Path $outputNotebook | Should -BeTrue 21 | 22 | $actual = Get-NotebookContent -Path $outputNotebook 23 | 24 | $actual.Count | Should -Be 4 25 | 26 | $actual[0].Type | Should -BeExactly 'markdown' 27 | $actual[0].Source | Should -BeExactly '# get-process' 28 | 29 | $actual[1].Type | Should -BeExactly 'code' 30 | $actual[1].Source | Should -BeExactly 'get-process *micro* | select -first 5' 31 | 32 | $actual[2].Type | Should -BeExactly 'markdown' 33 | $actual[2].Source | Should -BeExactly '# get-service' 34 | $actual[3].Type | Should -BeExactly 'code' 35 | $actual[3].Source | Should -BeExactly 'get-service | select -first 5' 36 | } 37 | 38 | It "Test for no text piped to function" -Skip { 39 | $outputNotebook = "TestDrive:\test.ipynb" 40 | 41 | $( 42 | '' 43 | ) | Export-AsPowerShellNotebook -OutputNotebook $outputNotebook 44 | 45 | Test-Path $outputNotebook | Should -BeFalse 46 | 47 | # $actual = Get-NotebookContent -Path $outputNotebook 48 | 49 | # $actual.Count | Should -Be 4 50 | 51 | # $actual[0].Type | Should -BeExactly 'markdown' 52 | # $actual[0].Source | Should -BeExactly '# get-process' 53 | 54 | # $actual[1].Type | Should -BeExactly 'code' 55 | # $actual[1].Source | Should -BeExactly 'get-process *micro* | select -first 5' 56 | 57 | # $actual[2].Type | Should -BeExactly 'markdown' 58 | # $actual[2].Source | Should -BeExactly '# get-service' 59 | # $actual[3].Type | Should -BeExactly 'code' 60 | # $actual[3].Source | Should -BeExactly 'get-service | select -first 5' 61 | } 62 | } -------------------------------------------------------------------------------- /__tests__/GetNotebook.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test PS Notebooks" { 4 | 5 | It "Should have Get-Notebook" { 6 | $actual = Get-Command Get-Notebook -ErrorAction SilentlyContinue 7 | $actual | Should -Not -Be $Null 8 | } 9 | 10 | It "Should find no notebooks" -Skip { 11 | $actual = Get-Notebook 12 | $actual.Count | Should -Be 0 13 | } 14 | 15 | It "Should find no notebooks in specified directory" { 16 | $actual = Get-Notebook $PSScriptRoot\NoNotebooks 17 | $actual.Count | Should -Be 0 18 | } 19 | 20 | It "Should find one notebook in specified directory" { 21 | $actual = @(Get-Notebook "$PSScriptRoot\OneNotebook") 22 | $actual.Count | Should -Be 1 23 | } 24 | 25 | It "Should find notebooks in specified directory" { 26 | $actual = @(Get-Notebook "$PSScriptRoot\GoodNotebooks") 27 | $actual.Count | Should -Be 7 28 | } 29 | 30 | It "Should find a notebook by name in specified directory" { 31 | $actual = @(Get-Notebook "$PSScriptRoot\GoodNotebooks" testpsnb1*) 32 | $actual.Count | Should -Be 1 33 | } 34 | 35 | It "Should find a notebook by name in specified directory" { 36 | $actual = @(Get-Notebook "$PSScriptRoot\GoodNotebooks" testpsnb1*) 37 | $actual.Count | Should -Be 1 38 | } 39 | 40 | It "Should find notebook testpsnb1.ipynb and get metadata content" { 41 | $actual = @(Get-Notebook "$PSScriptRoot\GoodNotebooks" testpsnb1*) 42 | 43 | $actual.Count | Should -Be 1 44 | 45 | <# 46 | NoteBookName : testPSNb1.ipynb 47 | KernelName : powershell 48 | CodeBlocks : 2 49 | MarkdownBlocks : 1 50 | NoteBookFullName : C:\Users\Douglas\Documents\GitHub\MyPrivateGit\PowerShellNotebook\__tests__\GoodNotebooks\testPSNb1.ipynb 51 | #> 52 | 53 | $actual.NoteBookName | Should -Be "testPSNb1.ipynb" 54 | $actual.KernelName | Should -Be "powershell" 55 | $actual.CodeBlocks | Should -Be 2 56 | $actual.MarkdownBlocks | Should -Be 1 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /__tests__/GetNotebookContent.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test PS Notebook Content" -Tag "Get-NotebookContent" { 4 | 5 | It "Should have Get-NotebookContent" { 6 | $actual = Get-Command Get-NotebookContent -ErrorAction SilentlyContinue 7 | $actual | Should -Not -Be $Null 8 | } 9 | 10 | It "testPSNb1.ipynb should have this content" { 11 | <# 12 | NoteBookName Type Source 13 | ------------ ---- ------ 14 | testPSNb1.ipynb code 8+12 15 | testPSNb1.ipynb code 8+3 16 | testPSNb1.ipynb markdown ## Math... 17 | #> 18 | 19 | $actual = Get-NotebookContent -Path "$PSScriptRoot\GoodNotebooks\testPSNb1.ipynb" 20 | 21 | $actual.Count | Should -Be 3 22 | 23 | $actual[0].NoteBookName | Should -Be "testPSNb1.ipynb" 24 | $actual[0].Type | Should -Be "code" 25 | $actual[0].IsParameterCell | Should -Be $false 26 | $actual[0].Source | Should -Be "8+12" 27 | 28 | $actual[1].NoteBookName | Should -Be "testPSNb1.ipynb" 29 | $actual[1].Type | Should -Be "code" 30 | $actual[2].IsParameterCell | Should -Be $false 31 | $actual[1].Source | Should -Be "8+3" 32 | 33 | $actual[2].NoteBookName | Should -Be "testPSNb1.ipynb" 34 | $actual[2].Type | Should -Be "markdown" 35 | $actual[2].IsParameterCell | Should -Be $false 36 | 37 | $actual[2].Source.IndexOf('## Math') -ge 0 | Should -Be $true 38 | $actual[2].Source.IndexOf('- show addition') -ge 0 | Should -Be $true 39 | $actual[2].Source.IndexOf('- show other') -ge 0 | Should -Be $true 40 | } 41 | 42 | It "testPSNb1.ipynb should have only this code" { 43 | $actual = Get-NotebookContent -Path "$PSScriptRoot\GoodNotebooks\testPSNb1.ipynb" -JustCode 44 | 45 | $actual.Count | Should -Be 2 46 | $actual[0].NoteBookName | Should -Be "testPSNb1.ipynb" 47 | $actual[0].Type | Should -Be "code" 48 | $actual[0].Source | Should -Be "8+12" 49 | 50 | $actual[1].NoteBookName | Should -Be "testPSNb1.ipynb" 51 | $actual[1].Type | Should -Be "code" 52 | $actual[1].Source | Should -Be "8+3" 53 | } 54 | 55 | It "testPSNb1.ipynb should have only this markdown" { 56 | $actual = @(Get-NotebookContent -Path "$PSScriptRoot\GoodNotebooks\testPSNb1.ipynb" -JustMarkdown) 57 | 58 | $actual[0].NoteBookName | Should -Be "testPSNb1.ipynb" 59 | $actual[0].Type | Should -Be "markdown" 60 | 61 | $actual[0].Source.IndexOf('## Math') -ge 0 | Should -Be $true 62 | $actual[0].Source.IndexOf('- show addition') -ge 0 | Should -Be $true 63 | $actual[0].Source.IndexOf('- show other') -ge 0 | Should -Be $true 64 | } 65 | 66 | It "Should read ipynb from url" { 67 | $actual = Get-NotebookContent -Path "https://raw.githubusercontent.com/dfinke/PowerShellNotebook/AddJupyterNotebookMetaInfo/samplenotebook/powershell.ipynb" 68 | 69 | $actual | Should -Not -Be $null 70 | } 71 | 72 | It "Tests -Passthru Switch" { 73 | $fileName = "$PSScriptRoot\ChartNotebooks\charts.ipynb" 74 | $actual = Get-NotebookContent -Path $fileName -PassThru 75 | 76 | $actual.cells.Count | Should -Be 2 77 | $actual.cells[0].outputs.output_type | Should -BeExactly 'display_data' 78 | $actual.cells[0].outputs.data.'text/html'.Count | Should -Be 25 79 | } 80 | 81 | It "Tests IsParameterCell property" { 82 | $fileName = "$PSScriptRoot\NotebooksForUseWithInvokeOutfile\parameters.ipynb" 83 | $actual = Get-NotebookContent -NoteBookFullName $fileName 84 | 85 | $actual[0].Type | Should -BeExactly 'code' 86 | $actual[0].IsParameterCell | Should -BeTrue 87 | 88 | $actual[1].Type | Should -BeExactly 'code' 89 | $actual[1].IsParameterCell | Should -BeFalse 90 | 91 | $actual[2].Type | Should -BeExactly 'code' 92 | $actual[2].IsParameterCell | Should -BeFalse 93 | 94 | $actual[3].Type | Should -BeExactly 'code' 95 | $actual[3].IsParameterCell | Should -BeFalse 96 | } 97 | 98 | It "Tests .NET Interactive cells" { 99 | $fileName = "$PSScriptRoot\DotNetInteractiveNotebooks\AllDotNetInteractive.ipynb" 100 | 101 | $actual = Get-NotebookContent -NoteBookFullName $fileName 102 | 103 | $actual.Count | Should -Be 5 104 | 105 | $actual[0].Language | Should -BeExactly 'C# (.NET Interactive)' 106 | $actual[1].Language | Should -BeExactly 'F# (.NET Interactive)' 107 | $actual[2].Language | Should -BeExactly 'PowerShell (.NET Interactive)' 108 | $actual[3].Language | Should -BeExactly 'SQL (.NET Interactive)' 109 | $actual[4].Language | Should -BeExactly 'C# (.NET Interactive)' 110 | } 111 | 112 | It "Test all cell numbers" { 113 | $actual = Get-NotebookContent -NoteBookFullName "$PSScriptRoot\GoodNotebooks\UsedForCellNumbers.ipynb" 114 | 115 | $actual.Count | Should -Be 4 116 | 117 | $actual[0].Cell | Should -Be 1 118 | $actual[1].Cell | Should -Be 2 119 | $actual[2].Cell | Should -Be 3 120 | $actual[3].Cell | Should -Be 4 121 | } 122 | 123 | It "Test markdown cell numbers" { 124 | $actual = Get-NotebookContent -NoteBookFullName "$PSScriptRoot\GoodNotebooks\UsedForCellNumbers.ipynb" -JustMarkdown 125 | 126 | $actual.Count | Should -Be 2 127 | 128 | $actual[0].Cell | Should -Be 1 129 | $actual[1].Cell | Should -Be 3 130 | } 131 | 132 | It "Test code cell numbers" { 133 | $actual = Get-NotebookContent -NoteBookFullName "$PSScriptRoot\GoodNotebooks\UsedForCellNumbers.ipynb" -JustCode 134 | 135 | $actual.Count | Should -Be 2 136 | 137 | $actual[0].Cell | Should -Be 2 138 | $actual[1].Cell | Should -Be 4 139 | } 140 | 141 | } -------------------------------------------------------------------------------- /__tests__/GetParsedSqlOffsets.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psm1 -Force 2 | if (-not (Get-Module -ListAvailable -Name sqlserver)) { return } 3 | Describe "Test Get-ParsedSqlOffsets" { 4 | It "Should make sure demo.sql is in the \DemoFiles folder" { 5 | $demoTextFile = "$PSScriptRoot\DemoFiles\demo.sql" 6 | $fullName = "TestDrive:\demosql.csv" 7 | 8 | Write-Verbose "$(Get-ChildItem "$PSScriptRoot\DemoFiles\")" -Verbose 9 | 10 | try{ 11 | { Test-Path $demoTextFile } | Should -Be $true 12 | 13 | $demoTextFile = "$PSScriptRoot\DemoFiles\demo_w3GOs.sql" 14 | { Test-Path $demoTextFile } | Should -Be $true 15 | 16 | $demoTextFile = "$PSScriptRoot\DemoFiles\AdventureWorksMultiStatementSBatch_NoGO2.sql" 17 | { Test-Path $demoTextFile } | Should -Be $true 18 | } 19 | catch [System.Management.Automation.RuntimeException]{ 20 | Write-Verbose "Runtime exception encountered" -Verbose 21 | Write-Verbose $_ -Verbose 22 | throw 23 | } 24 | } 25 | 26 | It "Should retrieve the Batch, Comment, and Gap offsets with a single code cell" { 27 | $demoTextFile = "$PSScriptRoot/DemoFiles/demo.sql" 28 | $fullName = "TestDrive:\demosql.csv" 29 | 30 | try{ 31 | { Test-Path $demoTextFile } | Should -Be $true 32 | $Offsets = Get-ParsedSqlOffsets -ScriptPath $demoTextFile 33 | $Offsets | ConvertTo-Csv -NoTypeInformation > $fullName 34 | { Test-Path $fullName } | Should -Be $true 35 | 36 | @($Offsets).Count | Should -Be 1 37 | 38 | $Offsets = Get-ParsedSqlOffsets -ScriptPath $demoTextFile | Where-Object {$_.BlockType -ne 'Code'} 39 | 40 | $Offsets.Count | Should -Be 0 41 | 42 | $Offsets = Get-ParsedSqlOffsets -ScriptPath $demoTextFile | Where-Object {$_.BlockType -eq 'Code'} 43 | 44 | @($Offsets).Count | Should -Be 1 45 | 46 | $Offsets[0].Text | Should -BeExactly 'select DateDiff(MI,StartDate,EndDate) AS Timetaken,* FROM table1 47 | SELECT * FROM table2 WHERE id = 1 48 | /* Test1 */ 49 | /* Multiline test 50 | 1 51 | 2 52 | */ 53 | /* Test2 */ 54 | SELECT * FROM table3 where ID = 7 55 | /* Test3 */ 56 | SELECT * FROM table4 where ID = 8' 57 | } 58 | catch [System.Management.Automation.RuntimeException]{ 59 | Write-Verbose "Runtime exception encountered" -Verbose 60 | Write-Verbose $_ -Verbose 61 | throw 62 | } 63 | } 64 | 65 | It "Should retrieve the Batch, Comment, and Gap offsets with 3 code and 6 text cells" { 66 | $demoTextFile = "$PSScriptRoot/DemoFiles/demo_w3GOs.sql" 67 | $fullName = "TestDrive:\sqlTestConverted_demo_w3GOs.csv" 68 | 69 | try{ 70 | $Offsets = Get-ParsedSqlOffsets -ScriptPath $demoTextFile 71 | $Offsets | ConvertTo-Csv -NoTypeInformation > $fullName 72 | { Test-Path $fullName } | Should -Be $true 73 | 74 | @($Offsets).Count | Should -Be 9 75 | 76 | $Offsets = (Get-ParsedSqlOffsets -ScriptPath $demoTextFile).where({$_.BlockType -eq 'Comment'}) 77 | 78 | $Offsets.Count | Should -Be 4 79 | 80 | $Offsets = (Get-ParsedSqlOffsets -ScriptPath $demoTextFile).where({$_.BlockType -eq 'Gap'}) 81 | 82 | $Offsets.Count | Should -Be 2 83 | 84 | $Offsets = (Get-ParsedSqlOffsets -ScriptPath $demoTextFile).where({$_.BlockType -eq 'Code'}) 85 | 86 | $Offsets.Count | Should -Be 3 87 | } 88 | catch [System.Management.Automation.RuntimeException]{ 89 | Write-Verbose "Runtime exception encountered" -Verbose 90 | Write-Verbose $_ -Verbose 91 | throw 92 | } 93 | } 94 | 95 | It "Should retrieve the Batch, Comment, and Gap offsets with 3 code and 5 text cells" { 96 | $demoTextFile = "$PSScriptRoot/DemoFiles/AdventureWorksMultiStatementSBatch_NoGO2.sql" 97 | $fullName = "TestDrive:\AdventureWorksMultiStatementSBatch_NoGO2.csv" 98 | 99 | try{ 100 | $Offsets = Get-ParsedSqlOffsets -ScriptPath $demoTextFile 101 | $Offsets | ConvertTo-Csv -NoTypeInformation > $fullName 102 | { Test-Path $fullName } | Should -Be $true 103 | 104 | @($Offsets).Count | Should -Be 8 105 | 106 | $Offsets = (Get-ParsedSqlOffsets -ScriptPath $demoTextFile).where({$_.BlockType -eq 'Comment'}) 107 | 108 | $Offsets.Count | Should -Be 2 109 | 110 | $Offsets = (Get-ParsedSqlOffsets -ScriptPath $demoTextFile).where({$_.BlockType -eq 'Gap'}) 111 | 112 | $Offsets.Count | Should -Be 3 113 | 114 | $Offsets = (Get-ParsedSqlOffsets -ScriptPath $demoTextFile).where({$_.BlockType -eq 'Code'}) 115 | 116 | $Offsets.Count | Should -Be 3 117 | } 118 | catch [System.Management.Automation.RuntimeException]{ 119 | Write-Verbose "Runtime exception encountered" -Verbose 120 | Write-Verbose $_ -Verbose 121 | throw 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /__tests__/GoodConvertedNotebooks/AdventureWorksMultiStatementSBatch_NoGO2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "sql", 5 | "display_name": "SQL" 6 | }, 7 | "language_info": { 8 | "name": "sql", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".sql" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | {"cell_type":"markdown","metadata":{},"source":[" Look up user and phone number by last name \n We match on first found in case of multiple \n "]},{ 18 | "cell_type": "code", 19 | "source": [ 20 | "declare @phonenumber nvarchar(50)\r\ndeclare @lastname nvarchar(50)\r\ndeclare @busentityid INT\r\n\r\nset @lastname = 'Tamburello'\r\n\r\nselect \r\ntop 1\r\n@busentityid = BusinessEntityID -- Will be used to lookup phone number\r\nfrom Person.Person\r\nwhere LastName = @lastname\r\n\r\n/* Only look for phone if we found the person\r\n-- Should usually be null or 1, values over 1 not really possible\r\n*/\r\nif @busentityid is not null\r\nbegin\r\n\r\n select \r\n p.FirstName,\r\n p.MiddleName,\r\n p.LastName,\r\n p.Suffix,\r\n pp.PhoneNumber\r\n from \r\n Person.Person p\r\n left join Person.PersonPhone pp\r\n on p.BusinessEntityID = pp.BusinessEntityID\r\n where \r\n p.BusinessEntityID = @busentityid\r\n\r\n/* Save the phonenumber from the selected user */\r\n select \r\n @phonenumber = PhoneNumber \r\n from \r\n Person.Person p\r\n left join Person.PersonPhone pp\r\n on p.BusinessEntityID = pp.BusinessEntityID\r\n where\r\n p.BusinessEntityID = @busentityid\r\n\r\nend" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "729e42ae-3c57-48a9-890b-768c6982b64c" 24 | }, 25 | "outputs": [ 26 | { 27 | "output_type": "stream", 28 | "name": "stdout", 29 | "text": "" 30 | } 31 | ] 32 | },{"cell_type":"markdown","metadata":{},"source":["go \r \n \r \n -- Wait for 4 seconds total after go 2"]},{ 33 | "cell_type": "code", 34 | "source": [ 35 | "RAISERROR('waiting', 1,1) with nowait\r\nselect GETUTCDATE() as 'Waiting'\r\nwaitfor delay '00:00:02'" 36 | ], 37 | "metadata": { 38 | "azdata_cell_guid": "65bde348-4a0b-4c91-8ced-b91c84c42ec9" 39 | }, 40 | "outputs": [ 41 | { 42 | "output_type": "stream", 43 | "name": "stdout", 44 | "text": "" 45 | } 46 | ] 47 | },{"cell_type":"markdown","metadata":{},"source":["-- note the Go 2 has been *removed*\r \n go\r \n \r \n -- This select statement returns the full customer address list"]},{ 48 | "cell_type": "code", 49 | "source": [ 50 | "select \r\n p.FirstName,\r\n p.MiddleName,\r\n p.LastName,\r\n p.Suffix,\r\n a.AddressLine1,\r\n a.AddressLine2, -- TODO: Should we concat line 1 and 2\r\n a.City,\r\n /*\r\n Debug stuff\r\n p.BusinessEntityID,\r\n bea.BusinessEntityID,\r\n bea.AddressID,\r\n a.AddressiD,\r\n sp.StateProvinceID,\r\n sp.StateProvinceID\r\n */\r\n sp.StateProvinceCode, /* TODO: Need to add country here too, join below */\r\n a.PostalCode--,\r\n --sp.CountryRegionCode \r\n from \r\n Person.Person p\r\n left join Person.BusinessEntityAddress bea -- Used for joining, not selected from\r\n on p.BusinessEntityID = bea.BusinessEntityID \r\n left join Person.Address a \r\n on bea.AddressID = a.AddressID\r\n left join Person.StateProvince sp \r\n on a.StateProvinceID = sp.StateProvinceID" 51 | ], 52 | "metadata": { 53 | "azdata_cell_guid": "aba3de77-b194-45f4-ba4c-7af9cac92a93" 54 | }, 55 | "outputs": [ 56 | { 57 | "output_type": "stream", 58 | "name": "stdout", 59 | "text": "" 60 | } 61 | ] 62 | },{"cell_type":"markdown","metadata":{},"source":[" Commenting out until the field is added \n left join Person.CountryRegion cr \n on sp.CountryRegionCode = cr.CountryRegionCode \n "]} 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /__tests__/GoodConvertedNotebooks/ParsingExamples.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | {"cell_type":"markdown","metadata":{},"source":["# # This is file is an example of what _should_ happen when converting a .PS1 file to a .IPYNB file."]},{ 18 | "cell_type": "code", 19 | "source": [ 20 | "ps | select -first 10" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "60e9849a-c22f-4efc-9b52-e31a6365d550" 24 | }, 25 | "outputs": [ 26 | { 27 | "name": "stdout", 28 | "output_type": "stream", 29 | "text": "" 30 | } 31 | ] 32 | },{"cell_type":"markdown","metadata":{},"source":["# Get first 10 services"]},{ 33 | "cell_type": "code", 34 | "source": [ 35 | "gsv | select -first 10" 36 | ], 37 | "metadata": { 38 | "azdata_cell_guid": "b32ad383-0f0d-4141-93df-153bc4da9a8d" 39 | }, 40 | "outputs": [ 41 | { 42 | "name": "stdout", 43 | "output_type": "stream", 44 | "text": "" 45 | } 46 | ] 47 | },{"cell_type":"markdown","metadata":{},"source":["# Create a function"]},{ 48 | "cell_type": "code", 49 | "source": [ 50 | "function SayHello($p) {\"Hello $p\"}" 51 | ], 52 | "metadata": { 53 | "azdata_cell_guid": "46ded967-d569-48e7-a2c2-a41f8b32c0d2" 54 | }, 55 | "outputs": [ 56 | { 57 | "name": "stdout", 58 | "output_type": "stream", 59 | "text": "" 60 | } 61 | ] 62 | },{"cell_type":"markdown","metadata":{},"source":["# Use the function"]},{ 63 | "cell_type": "code", 64 | "source": [ 65 | "SayHello World" 66 | ], 67 | "metadata": { 68 | "azdata_cell_guid": "3093f59a-75be-464e-9789-7d1dd7cdc276" 69 | }, 70 | "outputs": [ 71 | { 72 | "name": "stdout", 73 | "output_type": "stream", 74 | "text": "" 75 | } 76 | ] 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /__tests__/GoodConvertedNotebooks/demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | {"cell_type":"markdown","metadata":{},"source":["# Get first 10 process"]},{ 18 | "cell_type": "code", 19 | "source": [ 20 | "ps | select -first 10" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "094bb95b-99c7-4236-aae1-394f5f7ccd55" 24 | }, 25 | "outputs": [ 26 | { 27 | "name": "stdout", 28 | "output_type": "stream", 29 | "text": "" 30 | } 31 | ] 32 | },{"cell_type":"markdown","metadata":{},"source":["# Get first 10 services"]},{ 33 | "cell_type": "code", 34 | "source": [ 35 | "gsv | select -first 10" 36 | ], 37 | "metadata": { 38 | "azdata_cell_guid": "9e31a902-e17a-4f15-b324-afc980cb82ab" 39 | }, 40 | "outputs": [ 41 | { 42 | "name": "stdout", 43 | "output_type": "stream", 44 | "text": "" 45 | } 46 | ] 47 | },{"cell_type":"markdown","metadata":{},"source":["# Create a function"]},{ 48 | "cell_type": "code", 49 | "source": [ 50 | "function SayHello($p) {\"Hello $p\"}" 51 | ], 52 | "metadata": { 53 | "azdata_cell_guid": "b3725f67-2847-44b3-a4b8-abd5b0d5b78e" 54 | }, 55 | "outputs": [ 56 | { 57 | "name": "stdout", 58 | "output_type": "stream", 59 | "text": "" 60 | } 61 | ] 62 | },{"cell_type":"markdown","metadata":{},"source":["# Use the function"]},{ 63 | "cell_type": "code", 64 | "source": [ 65 | "SayHello World" 66 | ], 67 | "metadata": { 68 | "azdata_cell_guid": "87b41c5a-f000-401a-b806-c156e7c6c059" 69 | }, 70 | "outputs": [ 71 | { 72 | "name": "stdout", 73 | "output_type": "stream", 74 | "text": "" 75 | } 76 | ] 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /__tests__/GoodConvertedNotebooks/sqlTestConverted.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "sql", 5 | "display_name": "SQL" 6 | }, 7 | "language_info": { 8 | "name": "sql", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".sql" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "code", 19 | "source": [ 20 | "select DateDiff(MI,StartDate,EndDate) AS Timetaken,* FROM table1\r\nSELECT * FROM table2 WHERE id = 1" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "0f2f0f31-fa05-41c7-bb20-1f83f234ee3e" 24 | }, 25 | "outputs": [ 26 | { 27 | "output_type": "stream", 28 | "name": "stdout", 29 | "text": "" 30 | } 31 | ] 32 | },{"cell_type":"markdown","metadata":{},"source":["GO"]},{"cell_type":"markdown","metadata":{},"source":[" Test1 "]},{"cell_type":"markdown","metadata":{},"source":[" Multiline test \n 1 \n 2 \n "]},{"cell_type":"markdown","metadata":{},"source":[" Test2 "]},{ 33 | "cell_type": "code", 34 | "source": [ 35 | "SELECT * FROM table3 where ID = 7" 36 | ], 37 | "metadata": { 38 | "azdata_cell_guid": "0dd78d7e-d3f7-42f9-b4c4-d4eabd617f36" 39 | }, 40 | "outputs": [ 41 | { 42 | "output_type": "stream", 43 | "name": "stdout", 44 | "text": "" 45 | } 46 | ] 47 | },{"cell_type":"markdown","metadata":{},"source":["GO"]},{"cell_type":"markdown","metadata":{},"source":[" Test3 "]},{ 48 | "cell_type": "code", 49 | "source": [ 50 | "SELECT * FROM table4 where ID = 8" 51 | ], 52 | "metadata": { 53 | "azdata_cell_guid": "6f46d7c8-7c8f-4945-8995-95a90f1ba3ab" 54 | }, 55 | "outputs": [ 56 | { 57 | "output_type": "stream", 58 | "name": "stdout", 59 | "text": "" 60 | } 61 | ] 62 | } 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /__tests__/GoodNotebooks/SimpleNotebookToTestConvertToMarkdown.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "markdown", 19 | "source": [ 20 | "# Test for converting a PS Notebook to Markdown" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "a2220b8c-8013-4306-bb38-fca3f5b897c7" 24 | } 25 | }, 26 | { 27 | "cell_type": "code", 28 | "source": [ 29 | "1..5 | % {\r\n", 30 | " $_ * 2\r\n", 31 | "}" 32 | ], 33 | "metadata": { 34 | "azdata_cell_guid": "486f57d2-18e0-42be-ac61-46bdd65ba4aa" 35 | }, 36 | "outputs": [], 37 | "execution_count": 0 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "source": [ 42 | "## End of PS Notebook" 43 | ], 44 | "metadata": { 45 | "azdata_cell_guid": "50b56e5f-5a71-4a1f-9c0a-cbf290c1cc1f" 46 | } 47 | } 48 | ] 49 | } -------------------------------------------------------------------------------- /__tests__/GoodNotebooks/SingleCodeBlock.ipynb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/__tests__/GoodNotebooks/SingleCodeBlock.ipynb -------------------------------------------------------------------------------- /__tests__/GoodNotebooks/UsedForCellNumbers.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "---\r\n", 8 | "some markdown" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": null, 14 | "metadata": { 15 | "dotnet_interactive": { 16 | "language": "pwsh" 17 | } 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "'hello world'" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "---\r\n", 29 | "some more markdown" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": { 36 | "dotnet_interactive": { 37 | "language": "pwsh" 38 | } 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "'hello world, again'" 43 | ] 44 | } 45 | ], 46 | "metadata": { 47 | "kernelspec": { 48 | "display_name": ".NET (C#)", 49 | "language": "C#", 50 | "name": ".net-csharp" 51 | }, 52 | "language_info": { 53 | "name": "C#", 54 | "version": "" 55 | }, 56 | "orig_nbformat": 2 57 | }, 58 | "nbformat": 4, 59 | "nbformat_minor": 2 60 | } -------------------------------------------------------------------------------- /__tests__/GoodNotebooks/demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "markdown", 19 | "source": [ 20 | "Get first 10 process" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "e04ff489-861c-46da-b13d-ebc686bd7d90" 24 | } 25 | }, 26 | { 27 | "cell_type": "code", 28 | "source": [ 29 | "ps | select -first 10" 30 | ], 31 | "metadata": { 32 | "azdata_cell_guid": "bd9e51f5-e9c7-4784-bab4-f873692deb97" 33 | }, 34 | "outputs": [], 35 | "execution_count": null 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "source": [ 40 | "Get first 10 services" 41 | ], 42 | "metadata": { 43 | "azdata_cell_guid": "05f8b72a-674c-4f94-a233-14f4ff078d3c" 44 | } 45 | }, 46 | { 47 | "cell_type": "code", 48 | "source": [ 49 | "gsv | select -first 10" 50 | ], 51 | "metadata": { 52 | "azdata_cell_guid": "93670f13-708f-42ce-b804-d0a25cdd3eb7" 53 | }, 54 | "outputs": [], 55 | "execution_count": null 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "source": [ 60 | "Create a function" 61 | ], 62 | "metadata": { 63 | "azdata_cell_guid": "94f6b887-f5f3-4275-bee4-fbfde27efa5d" 64 | } 65 | }, 66 | { 67 | "cell_type": "code", 68 | "source": [ 69 | "function SayHello($p) {\"Hello $p\"}" 70 | ], 71 | "metadata": { 72 | "azdata_cell_guid": "c63c950f-a629-4bd3-bc9b-3be9b79c43fa" 73 | }, 74 | "outputs": [], 75 | "execution_count": null 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "source": [ 80 | "Use the function" 81 | ], 82 | "metadata": { 83 | "azdata_cell_guid": "bbd33eb6-5e62-439d-a7c5-7f141d00335a" 84 | } 85 | }, 86 | { 87 | "cell_type": "code", 88 | "source": [ 89 | "SayHello World" 90 | ], 91 | "metadata": { 92 | "azdata_cell_guid": "92eb1f8a-01d8-4b30-af9f-56c25b728eb5" 93 | }, 94 | "outputs": [], 95 | "execution_count": null 96 | } 97 | ] 98 | } -------------------------------------------------------------------------------- /__tests__/GoodNotebooks/testPSExcel.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "code", 19 | "source": [ 20 | "#get-service | select -first 10 Status, Name, DisplayName" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "d89e0ca2-444f-474f-92ab-68e2dde95358" 24 | }, 25 | "outputs": [], 26 | "execution_count": 0 27 | }, 28 | { 29 | "cell_type": "code", 30 | "source": [ 31 | "get-process | select company, name, handles -first 10" 32 | ], 33 | "metadata": { 34 | "azdata_cell_guid": "57e06f10-6c61-4263-8060-5beaeb7aaedf" 35 | }, 36 | "outputs": [], 37 | "execution_count": 0 38 | }, 39 | { 40 | "cell_type": "code", 41 | "source": [ 42 | "1..10 | foreach {[PSCustomObject]@{ID=$_}}" 43 | ], 44 | "metadata": { 45 | "azdata_cell_guid": "1371ba2d-b882-4aa9-97dd-bd8883b70549" 46 | }, 47 | "outputs": [], 48 | "execution_count": 0 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /__tests__/GoodNotebooks/testPSNb1.ipynb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/__tests__/GoodNotebooks/testPSNb1.ipynb -------------------------------------------------------------------------------- /__tests__/GoodNotebooks/testPSNb2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "code", 19 | "source": "8+12" 20 | }, 21 | { 22 | "cell_type": "code", 23 | "source": "8+3" 24 | }, 25 | { 26 | "cell_type": "code", 27 | "source": "$a=3*7\r\n$a" 28 | }, 29 | {"cell_type":"markdown","source":"## Math\r\n\r\n- show addition\r\n- show other\r\n"} 30 | ] 31 | } -------------------------------------------------------------------------------- /__tests__/MultiLineSourceNotebooks/MultiLineSourceAsArray.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "markdown", 19 | "source": [ 20 | "# Chapter 1" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "b6b34a8a-c526-4d12-9c2c-5e700360ddd9" 24 | } 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "source": [ 29 | "stuff\r\n", 30 | "\r\n", 31 | "" 32 | ], 33 | "metadata": { 34 | "azdata_cell_guid": "e67964ea-dd40-4aaa-a056-abf961fd3074" 35 | } 36 | }, 37 | { 38 | "cell_type": "code", 39 | "source": [ 40 | "foreach ($item in 1..10) {\r\n", 41 | " $item\r\n", 42 | "}" 43 | ], 44 | "metadata": { 45 | "azdata_cell_guid": "f0731c45-5d3d-4af0-928e-40a7a5ca013a" 46 | }, 47 | "outputs": [], 48 | "execution_count": 0 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /__tests__/MultiLineSourceNotebooks/MultiLineSourceAsString.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | {"cell_type":"markdown","source":"# Chapter 1"},{"cell_type":"markdown","source":"stuff\r\n\r\n"},{ 18 | "cell_type": "code", 19 | "source": "foreach ($item in 1..10) {\r\n $item\r\n}\r\n", 20 | "metadata": { 21 | "azdata_cell_guid": "3db72df2-75e0-46d9-83d2-48cc19f0db18" 22 | }, 23 | "outputs": [ 24 | { 25 | "output_type": "stream", 26 | "text": "1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n", 27 | "name": "stdout" 28 | } 29 | ] 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /__tests__/MultipleChapters/MultipleChapters.md: -------------------------------------------------------------------------------- 1 | 2 | ```ps 3 | "hello" 4 | ``` 5 | # Summary 6 | 7 | 8 | # What you'll learn 9 | -------------------------------------------------------------------------------- /__tests__/MultiplePSFiles/a1.ps1: -------------------------------------------------------------------------------- 1 | "file a1.ps1" -------------------------------------------------------------------------------- /__tests__/MultiplePSFiles/a2.ps1: -------------------------------------------------------------------------------- 1 | "file a2.ps1" -------------------------------------------------------------------------------- /__tests__/MultiplePSFiles/a3.ps1: -------------------------------------------------------------------------------- 1 | "file a3.ps1" -------------------------------------------------------------------------------- /__tests__/NewCodeCell.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test New-CodeCell" -Tag 'New-CodeCell' { 4 | BeforeAll { 5 | $vars = @' 6 | $a=1 7 | $b=3 8 | '@ 9 | 10 | } 11 | 12 | It "Should have New-CodeCell" { 13 | $actual = Get-Command New-CodeCell -ErrorAction SilentlyContinue 14 | $actual | Should -Not -Be $Null 15 | } 16 | 17 | It "Test source" { 18 | $json = New-CodeCell -Source $vars 19 | 20 | $actual = $json | ConvertFrom-Json 21 | 22 | $actual.cell_type | Should -BeExactly "code" 23 | $actual.execution_count | Should -Be 0 24 | $actual.outputs | Should -Be $null 25 | $actual.metadata.tags | Should -Be "injected-parameters" 26 | $actual.source.count | Should -Be 2 27 | 28 | $actual.source[0].Trim() | Should -BeExactly '$a=1' 29 | $actual.source[1] | Should -BeExactly '$b=3' 30 | } 31 | 32 | It "Tests .net interactive metadata" { 33 | <# 34 | { 35 | "cell_type": "code", 36 | "execution_count": 0, 37 | "metadata": { 38 | "dotnet_interactive": { 39 | "language": "pwsh" 40 | }, 41 | "tags": [ 42 | "injected-parameters" 43 | ] 44 | }, 45 | "outputs": [], 46 | "source": [ 47 | "$a=1", 48 | "$b=3" 49 | ] 50 | } 51 | 52 | #> 53 | 54 | $json = New-CodeCell -Source $vars -DotNetInteractive 55 | $actual = $json | ConvertFrom-Json 56 | 57 | $actual.cell_type | Should -BeExactly "code" 58 | $actual.execution_count | Should -Be 0 59 | $actual.outputs | Should -Be $null 60 | 61 | $actual.metadata.psobject.properties.name.count | Should -Be 2 62 | $actual.metadata.psobject.properties.name[0] | Should -BeExactly 'dotnet_interactive' 63 | $actual.metadata.dotnet_interactive.language | Should -BeExactly 'pwsh' 64 | 65 | $actual.metadata.tags | Should -Be "injected-parameters" 66 | $actual.source.count | Should -Be 2 67 | 68 | $actual.source[0].Trim() | Should -BeExactly '$a=1' 69 | $actual.source[1] | Should -BeExactly '$b=3' 70 | } 71 | } -------------------------------------------------------------------------------- /__tests__/NewGistNotebook.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test New-GistNotebook" -Tag 'New-GistNotebook' { 4 | It "Should have New-GistNotebook" { 5 | $actual = Get-Command New-GistNotebook -ErrorAction SilentlyContinue 6 | $actual | Should -Not -Be $Null 7 | } 8 | } -------------------------------------------------------------------------------- /__tests__/NoNotebooks/NotANotebok.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/__tests__/NoNotebooks/NotANotebok.txt -------------------------------------------------------------------------------- /__tests__/NoteBookName: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/__tests__/NoteBookName -------------------------------------------------------------------------------- /__tests__/NotebooksForUseWithInvokeOutfile/CellHasAnError.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "code", 19 | "source": [ 20 | "1/0" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "fff22dd1-8c9f-4a72-a760-9986d9647c07" 24 | }, 25 | "outputs": [], 26 | "execution_count": null 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /__tests__/NotebooksForUseWithInvokeOutfile/ComboGoodAndErrorCells.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "code", 19 | "source": [ 20 | "$a = 2\r\n", 21 | "\r\n", 22 | "10*2" 23 | ], 24 | "metadata": { 25 | "azdata_cell_guid": "e33f9475-7ed4-48ea-b4b0-7bd344e3ea53" 26 | }, 27 | "outputs": [], 28 | "execution_count": null 29 | }, 30 | { 31 | "cell_type": "code", 32 | "source": [ 33 | "1/0" 34 | ], 35 | "metadata": { 36 | "azdata_cell_guid": "fff22dd1-8c9f-4a72-a760-9986d9647c07" 37 | }, 38 | "outputs": [], 39 | "execution_count": null 40 | }, 41 | { 42 | "cell_type": "code", 43 | "source": [ 44 | "40+$a" 45 | ], 46 | "metadata": { 47 | "azdata_cell_guid": "ae1b4f15-d066-4c5d-9df6-b925c68059c5" 48 | }, 49 | "outputs": [], 50 | "execution_count": null 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /__tests__/NotebooksForUseWithInvokeOutfile/Hello-PowerShell.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": { 7 | "tags": [ 8 | "parameters" 9 | ] 10 | }, 11 | "outputs": [], 12 | "source": [ 13 | "$msgs = \"Hello\", \"World!\"" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 4, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "name": "stdout", 23 | "output_type": "stream", 24 | "text": [ 25 | "Hello\n", 26 | "World!\n" 27 | ] 28 | } 29 | ], 30 | "source": [ 31 | "foreach($msg in $msgs) {\n", 32 | " $msg\n", 33 | "}" 34 | ] 35 | } 36 | ], 37 | "metadata": { 38 | "celltoolbar": "Tags", 39 | "kernelspec": { 40 | "display_name": ".NET (PowerShell)", 41 | "language": "PowerShell", 42 | "name": ".net-powershell" 43 | }, 44 | "language_info": { 45 | "file_extension": ".ps1", 46 | "mimetype": "text/x-powershell", 47 | "name": "PowerShell", 48 | "pygments_lexer": "powershell", 49 | "version": "7.0" 50 | } 51 | }, 52 | "nbformat": 4, 53 | "nbformat_minor": 4 54 | } 55 | -------------------------------------------------------------------------------- /__tests__/NotebooksForUseWithInvokeOutfile/NotebookMoreThanOneParameterCell.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 15, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "$msg = 'Hello World'" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 16, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "Hello World\r\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "$msg" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 17, 32 | "metadata": { 33 | "tags": [ 34 | "parameters" 35 | ] 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "$msg = 'Hello again!'" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 18, 45 | "metadata": {}, 46 | "outputs": [ 47 | { 48 | "name": "stdout", 49 | "output_type": "stream", 50 | "text": [ 51 | "Hello again!\r\n" 52 | ] 53 | } 54 | ], 55 | "source": [ 56 | "$msg" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 19, 62 | "metadata": { 63 | "tags": [ 64 | "parameters" 65 | ] 66 | }, 67 | "outputs": [], 68 | "source": [ 69 | "$msg = 'Goodbye'" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 20, 75 | "metadata": {}, 76 | "outputs": [ 77 | { 78 | "name": "stdout", 79 | "output_type": "stream", 80 | "text": [ 81 | "Goodbye\r\n" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "$msg" 87 | ] 88 | } 89 | ], 90 | "metadata": { 91 | "celltoolbar": "Tags", 92 | "kernelspec": { 93 | "display_name": ".NET (PowerShell)", 94 | "language": "PowerShell", 95 | "name": ".net-powershell" 96 | }, 97 | "language_info": { 98 | "file_extension": ".ps1", 99 | "mimetype": "text/x-powershell", 100 | "name": "PowerShell", 101 | "pygments_lexer": "powershell", 102 | "version": "7.0" 103 | } 104 | }, 105 | "nbformat": 4, 106 | "nbformat_minor": 4 107 | } 108 | -------------------------------------------------------------------------------- /__tests__/NotebooksForUseWithInvokeOutfile/NotebookNoParameterCells.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "if(-not $msg) { $msg = 'Hello World'}" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 4, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "Hello World\r\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "$msg" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 7, 32 | "metadata": {}, 33 | "outputs": [ 34 | { 35 | "name": "stdout", 36 | "output_type": "stream", 37 | "text": [ 38 | "The length of 'Hello World' is 11\r\n" 39 | ] 40 | } 41 | ], 42 | "source": [ 43 | "\"The length of '{0}' is {1}\" -f $msg, $msg.Length" 44 | ] 45 | } 46 | ], 47 | "metadata": { 48 | "kernelspec": { 49 | "display_name": ".NET (PowerShell)", 50 | "language": "PowerShell", 51 | "name": ".net-powershell" 52 | }, 53 | "language_info": { 54 | "file_extension": ".ps1", 55 | "mimetype": "text/x-powershell", 56 | "name": "PowerShell", 57 | "pygments_lexer": "powershell", 58 | "version": "7.0" 59 | } 60 | }, 61 | "nbformat": 4, 62 | "nbformat_minor": 4 63 | } 64 | -------------------------------------------------------------------------------- /__tests__/NotebooksForUseWithInvokeOutfile/VariablesAcrossCells.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "code", 19 | "source": [ 20 | "$a = 3" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "fff22dd1-8c9f-4a72-a760-9986d9647c07" 24 | }, 25 | "outputs": [], 26 | "execution_count": null 27 | }, 28 | { 29 | "cell_type": "code", 30 | "source": [ 31 | "$a*2" 32 | ], 33 | "metadata": { 34 | "azdata_cell_guid": "3b46bf84-4e55-4182-b1e3-1d20c8bc24c2" 35 | }, 36 | "outputs": [], 37 | "execution_count": null 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /__tests__/NotebooksForUseWithInvokeOutfile/parameters.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 23, 6 | "metadata": { 7 | "execution": { 8 | "iopub.execute_input": "2020-09-24T18:17:36.150Z", 9 | "iopub.status.busy": "2020-09-24T18:17:36.145Z", 10 | "iopub.status.idle": "2020-09-24T18:17:36.157Z" 11 | }, 12 | "tags": [ 13 | "parameters" 14 | ] 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "$alpha=1.2\n", 19 | "$ratio=3.7\n", 20 | "$a = 1" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 25, 26 | "metadata": {}, 27 | "outputs": [ 28 | { 29 | "name": "stdout", 30 | "output_type": "stream", 31 | "text": [ 32 | "alpha = 1.2, ratio = 3.7, and alpha * ratio = 4.44\r\n" 33 | ] 34 | } 35 | ], 36 | "source": [ 37 | "\"alpha = {0}, ratio = {1}, and alpha * ratio = {2}\" -f $alpha, $ratio, ($alpha * $ratio)" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 27, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "$twice = $a * 2" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 28, 52 | "metadata": {}, 53 | "outputs": [ 54 | { 55 | "name": "stdout", 56 | "output_type": "stream", 57 | "text": [ 58 | "a = 1 and twice = 2\r\n" 59 | ] 60 | } 61 | ], 62 | "source": [ 63 | "\"a = \" + $a + \" and twice = \" + $twice" 64 | ] 65 | } 66 | ], 67 | "metadata": { 68 | "celltoolbar": "Tags", 69 | "kernelspec": { 70 | "display_name": ".NET (PowerShell)", 71 | "language": "PowerShell", 72 | "name": ".net-powershell" 73 | }, 74 | "language_info": { 75 | "file_extension": ".ps1", 76 | "mimetype": "text/x-powershell", 77 | "name": "PowerShell", 78 | "pygments_lexer": "powershell", 79 | "version": "7.0" 80 | }, 81 | "nteract": { 82 | "version": "0.25.0" 83 | } 84 | }, 85 | "nbformat": 4, 86 | "nbformat_minor": 4 87 | } 88 | -------------------------------------------------------------------------------- /__tests__/NotebooksForUseWithInvokeOutfile/testFile1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "markdown", 19 | "source": [ 20 | "# Test Notebook" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "d0597b5b-aad0-45a8-adec-bbe20eabe4fc" 24 | } 25 | }, 26 | { 27 | "cell_type": "code", 28 | "source": [ 29 | "\"Hello World\"" 30 | ], 31 | "metadata": { 32 | "azdata_cell_guid": "04ee7955-4a8c-4961-ac9e-8ae575515363" 33 | }, 34 | "outputs": [], 35 | "execution_count": null 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /__tests__/OneNotebook/testPSNb.ipynb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/__tests__/OneNotebook/testPSNb.ipynb -------------------------------------------------------------------------------- /__tests__/PSNBDSL.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test Invoke PS Notebook" -Tag 'InvokePSNotebook' { 4 | 5 | It "Should have New-PSNotebook" { 6 | $actual = Get-Command New-PSNotebook -ErrorAction SilentlyContinue 7 | $actual | Should -Not -Be $Null 8 | } 9 | 10 | It "Should have Add-NotebookCode" { 11 | $actual = Get-Command Add-NotebookCode -ErrorAction SilentlyContinue 12 | $actual | Should -Not -Be $Null 13 | } 14 | 15 | It "Should have Add-NotebookMarkdown" { 16 | $actual = Get-Command Add-NotebookMarkdown -ErrorAction SilentlyContinue 17 | $actual | Should -Not -Be $Null 18 | } 19 | 20 | It "Should generate correct PowerShell notebook format" { 21 | $actualJson = New-PSNotebook -AsText { 22 | Add-NotebookCode "8+12" 23 | Add-NotebookCode "8+3" 24 | Add-NotebookMarkdown @' 25 | ## Math 26 | 27 | - show addition 28 | - show other 29 | 30 | '@ 31 | } 32 | 33 | $actual = $actualJson | ConvertFrom-Json 34 | $actual.cells.count | Should -Be 3 35 | 36 | $actual.cells[0].source | Should -BeExactly "8+12" 37 | $actual.cells[1].source | Should -BeExactly "8+3" 38 | $actual.cells[2].source | Should -BeExactly "## Math 39 | 40 | - show addition 41 | - show other 42 | " 43 | } 44 | 45 | It "Save file" { 46 | $fullName = "TestDrive:\test.ipynb" 47 | 48 | New-PSNotebook -NoteBookName $fullName { 49 | Add-NotebookCode "8+12" 50 | } 51 | 52 | $r = Test-Path $fullName 53 | $r | Should -Be $true 54 | } 55 | 56 | It "Should have correct top level metadata" { 57 | $actual = ConvertFrom-Json (New-PSNotebook -AsText { }) 58 | <# 59 | { 60 | "metadata": { 61 | "kernelspec": { 62 | "name": "powershell", 63 | "display_name": "PowerShell" 64 | }, 65 | "language_info": { 66 | "name": "powershell", 67 | "codemirror_mode": "shell", 68 | "mimetype": "text/x-sh", 69 | "file_extension": ".ps1" 70 | } 71 | }, 72 | "nbformat_minor": 2, 73 | "nbformat": 4, 74 | "cells": [ 75 | 76 | ] 77 | } 78 | #> 79 | 80 | $actual.metadata | Should -Not -Be $null 81 | $actual.metadata.kernelspec.name | Should -BeExactly 'powershell' 82 | $actual.metadata.kernelspec.display_name | Should -BeExactly 'PowerShell' 83 | 84 | $actual.metadata.language_info.name | Should -BeExactly 'powershell' 85 | $actual.metadata.language_info.codemirror_mode | Should -BeExactly 'shell' 86 | $actual.metadata.language_info.mimetype | Should -BeExactly 'text/x-sh' 87 | $actual.metadata.language_info.file_extension | Should -BeExactly '.ps1' 88 | 89 | $actual.nbformat_minor | Should -Be 2 90 | $actual.nbformat | Should -Be 4 91 | $actual.cells.Count | Should -Be 0 92 | } 93 | 94 | It "Should have correct markdown metadata" { 95 | <# 96 | { 97 | "metadata": { 98 | "kernelspec": { 99 | "name": "powershell", 100 | "display_name": "PowerShell" 101 | }, 102 | "language_info": { 103 | "name": "powershell", 104 | "codemirror_mode": "shell", 105 | "mimetype": "text/x-sh", 106 | "file_extension": ".ps1" 107 | } 108 | }, 109 | "nbformat_minor": 2, 110 | "nbformat": 4, 111 | "cells": [ 112 | {"cell_type":"markdown","metadata":{},"source":["# Hello World"]} 113 | ] 114 | } 115 | #> 116 | $nb = New-PSNotebook -AsText { 117 | Add-NotebookMarkdown -markdown "# Hello World" 118 | } 119 | 120 | $actual = ConvertFrom-Json $nb 121 | 122 | $actual.cells.Count | Should -Be 1 123 | $actual.cells[0].cell_type | Should -BeExactly 'markdown' 124 | $actual.cells[0].metadata.GetType().name | Should -BeExactly "PSCustomObject" 125 | $actual.cells[0].source | Should -BeExactly '# Hello World' 126 | } 127 | 128 | It "Tests Invoke returning a string" { 129 | $s = "'Hello World'" 130 | 131 | $PSNotebookRunspace = New-PSNotebookRunspace 132 | $actual = $PSNotebookRunspace.Invoke($s) 133 | 134 | $actual.Trim() | Should -BeExactly "Hello World" 135 | } 136 | 137 | It "Tests Invoke returning an object" { 138 | $s = "[PSCustomObject]@{msg='Hello World'}" 139 | 140 | $PSNotebookRunspace = New-PSNotebookRunspace -ReturnAsObjects 141 | $actual = $PSNotebookRunspace.Invoke($s) 142 | 143 | $actual.msg | Should -BeExactly "Hello World" 144 | } 145 | 146 | It "Tests code cell with null language specified" { 147 | $actualJson = New-PSNotebook -AsText { 148 | Add-NotebookCode '1..2' 149 | } | ConvertFrom-Json 150 | 151 | $actualJson.cells.Count | Should -Be 1 152 | $actualJson.cells[0].metadata.'dotnet_interactive' | Should -BeNullOrEmpty 153 | $actualJson.cells[0].metadata.'dotnet_interactive'.language | Should -BeNullOrEmpty 154 | } 155 | 156 | It "Tests code cell with PowerShell language specified" { 157 | $actualJson = New-PSNotebook -AsText { 158 | Add-NotebookCode '1..2' -language PowerShell 159 | } | ConvertFrom-Json 160 | 161 | $actualJson.cells.Count | Should -Be 1 162 | $actualJson.cells[0].metadata.'dotnet_interactive' | Should -Not -BeNullOrEmpty 163 | $actualJson.cells[0].metadata.'dotnet_interactive'.language | Should -BeExactly 'pwsh' 164 | } 165 | 166 | It "Tests code cell with C# language specified" { 167 | $actualJson = New-PSNotebook -AsText { 168 | Add-NotebookCode '1..2' -language C# 169 | } | ConvertFrom-Json 170 | 171 | $actualJson.cells.Count | Should -Be 1 172 | $actualJson.cells[0].metadata.'dotnet_interactive' | Should -Not -BeNullOrEmpty 173 | $actualJson.cells[0].metadata.'dotnet_interactive'.language | Should -BeExactly 'csharp' 174 | } 175 | 176 | It "Tests code cell with F# language specified" { 177 | $actualJson = New-PSNotebook -AsText { 178 | Add-NotebookCode '1..2' -language F# 179 | } | ConvertFrom-Json 180 | 181 | $actualJson.cells.Count | Should -Be 1 182 | $actualJson.cells[0].metadata.'dotnet_interactive' | Should -Not -BeNullOrEmpty 183 | $actualJson.cells[0].metadata.'dotnet_interactive'.language | Should -BeExactly 'fsharp' 184 | } 185 | 186 | It "Tests code cell with SQL language specified" { 187 | $actualJson = New-PSNotebook -AsText { 188 | Add-NotebookCode '1..2' -language SQL 189 | } | ConvertFrom-Json 190 | 191 | $actualJson.cells.Count | Should -Be 1 192 | $actualJson.cells[0].metadata.'dotnet_interactive' | Should -Not -BeNullOrEmpty 193 | $actualJson.cells[0].metadata.'dotnet_interactive'.language | Should -BeExactly 'sql' 194 | } 195 | } -------------------------------------------------------------------------------- /__tests__/PSNotebookRunspace.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "PSNotebookRunspace tests" { 4 | BeforeEach { 5 | $psrs = New-PSNotebookRunspace 6 | } 7 | 8 | It "Should -Not -Be null" { 9 | $psrs | Should -Not -Be $null 10 | } 11 | 12 | It "Should -Be 42" { 13 | $code = '10+32' 14 | $actual = $psrs.Invoke($code) 15 | $expected = 42 16 | 17 | $actual | Should -Be $expected 18 | } 19 | 20 | It "Should also be 42" { 21 | $null = $psrs.Invoke('$x = 10') 22 | $null = $psrs.Invoke('$y = 32') 23 | $null = $psrs.Invoke('$total = $x + $y') 24 | $actual = $psrs.Invoke('$total') 25 | $expected = 42 26 | 27 | $actual | Should -Be $expected 28 | } 29 | 30 | It "Should create the correct book and results" { 31 | $json = New-PSNotebook -AsText -IncludeCodeResults { 32 | Add-NotebookCode -code '10+2' 33 | } 34 | 35 | $obj = $json | ConvertFrom-Json 36 | $obj.Cells.outputs.text.Trim() -eq 12 37 | } 38 | } -------------------------------------------------------------------------------- /__tests__/SQLNotebooks/ExportNotebookToSqlScript.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test Export-NotebookToSqlScript" { 4 | It "Should create SQL file with correct contents" { 5 | $outPath = "TestDrive:\" 6 | 7 | Export-NotebookToSqlScript -FullName "$PSScriptRoot/Simple_SELECTs.ipynb" -outPath $outPath 8 | $SQLFile = "$outPath/Simple_SELECTs.sql" 9 | 10 | Test-Path $SQLFile | Should -Be $true 11 | 12 | $contents = Get-Content $SQLFile 13 | 14 | $contents[0] | Should -BeExactly '/*' 15 | $contents[1].StartsWith(' Created from:') | Should -Be $true 16 | $contents[3].StartsWith(' Created by:') | Should -Be $true 17 | $contents[4].StartsWith(' Created on:') | Should -Be $true 18 | $contents[5] | Should -BeExactly '*/' 19 | 20 | $contents[7] | Should -BeExactly '/* First, find out how many databases are on this instance. */' 21 | $contents[8] | Should -BeExactly '' 22 | $contents[9] | Should -BeExactly 'SELECT *' 23 | $contents[10] | Should -BeExactly ' FROM sys.databases' 24 | $contents[19] | Should -BeExactly 'SELECT SYSDATETIME()' 25 | 26 | Remove-Item $SQLFile -ErrorAction SilentlyContinue 27 | } 28 | 29 | It "Should export the BPCheck.ipynb from a URL to SQL" { 30 | $url = "https://raw.githubusercontent.com/microsoft/tigertoolbox/master/BPCheck/BPCheck.ipynb" 31 | $SQLFile = (Split-Path -Path $url -Leaf) -replace ".ipynb", ".sql" 32 | 33 | try{ 34 | 35 | Export-NotebookToSqlScript -FullName $url -IncludeTextCells $true 36 | 37 | Test-Path $SQLFile | Should -Be $true 38 | 39 | $contents = Get-Content $SQLFile 40 | 41 | $contents[7] | Should -BeExactly '/* BP Check READ ME - http://aka.ms/BPCheck;' 42 | 43 | 44 | Export-NotebookToSqlScript -FullName $url -IncludeTextCells $false 45 | 46 | Test-Path $SQLFile | Should -Be $true 47 | 48 | $contents = Get-Content $SQLFile 49 | 50 | $contents[7] | Should -BeExactly 'SET NOCOUNT ON;' 51 | } 52 | catch [System.Management.Automation.RuntimeException]{ 53 | Write-Verbose "Runtime exception encountered" -Verbose 54 | Write-Verbose $_ -Verbose 55 | throw 56 | } 57 | 58 | Remove-Item $SQLFile -ErrorAction SilentlyContinue 59 | } 60 | 61 | It "Should export the Simple_SELECTs.ipynb from a URL to SQL" { 62 | $url = "https://raw.githubusercontent.com/dfinke/PowerShellNotebook/master/__tests__/SQLNotebooks/Simple_SELECTs.ipynb" 63 | $SQLFile = (Split-Path -Path $url -Leaf) -replace ".ipynb", ".sql" 64 | 65 | try{ 66 | 67 | Export-NotebookToSqlScript -FullName $url 68 | 69 | Test-Path $SQLFile | Should -Be $true 70 | 71 | $contents = Get-Content $SQLFile 72 | 73 | $contents[7] | Should -BeExactly '/* First, find out how many databases are on this instance. */' 74 | 75 | 76 | Export-NotebookToSqlScript -FullName $url -IncludeTextCells $false 77 | 78 | Test-Path $SQLFile | Should -Be $true 79 | 80 | $contents = Get-Content $SQLFile 81 | 82 | $contents[7] | Should -BeExactly 'SELECT *' 83 | } 84 | catch [System.Management.Automation.RuntimeException]{ 85 | Write-Verbose "Runtime exception encountered" -Verbose 86 | Write-Verbose $_ -Verbose 87 | throw 88 | } 89 | 90 | Remove-Item $SQLFile -ErrorAction SilentlyContinue 91 | } 92 | } -------------------------------------------------------------------------------- /__tests__/SQLNotebooks/Simple_SELECTs.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "SQL", 5 | "display_name": "SQL", 6 | "language": "sql" 7 | }, 8 | "language_info": { 9 | "name": "sql", 10 | "version": "" 11 | } 12 | }, 13 | "nbformat_minor": 2, 14 | "nbformat": 4, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "source": [ 19 | " First, find out how many databases are on this instance. " 20 | ], 21 | "metadata": { 22 | "azdata_cell_guid": "45baa1a5-3365-41e2-ab87-70936c3f22b1" 23 | } 24 | }, 25 | { 26 | "cell_type": "code", 27 | "source": [ 28 | "SELECT *\r\n", 29 | " FROM sys.databases" 30 | ], 31 | "metadata": { 32 | "azdata_cell_guid": "f23c0758-f8e2-492d-8634-616840f74952" 33 | }, 34 | "outputs": [ 35 | { 36 | "output_type": "stream", 37 | "name": "stdout", 38 | "text": "" 39 | } 40 | ], 41 | "execution_count": null 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "source": [ 46 | " Next check out all the processes currently running, using sys.processes. " 47 | ], 48 | "metadata": { 49 | "azdata_cell_guid": "639560d4-3522-4f6a-88d3-9b32838f0234" 50 | } 51 | }, 52 | { 53 | "cell_type": "code", 54 | "source": [ 55 | "SELECT *\r\n", 56 | " FROM sys.processes" 57 | ], 58 | "metadata": { 59 | "azdata_cell_guid": "67004b57-2172-4439-84e9-1ba5791965b6" 60 | }, 61 | "outputs": [ 62 | { 63 | "output_type": "stream", 64 | "name": "stdout", 65 | "text": "" 66 | } 67 | ], 68 | "execution_count": null 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "source": [ 73 | "Check the current time." 74 | ], 75 | "metadata": { 76 | "azdata_cell_guid": "ad6460f1-3214-4ed4-8ba2-4235a1d23ba2" 77 | } 78 | }, 79 | { 80 | "cell_type": "code", 81 | "source": [ 82 | "SELECT SYSDATETIME()" 83 | ], 84 | "metadata": { 85 | "azdata_cell_guid": "4959e824-8ec9-4d6a-9107-a377bd4ab58d" 86 | }, 87 | "outputs": [ 88 | { 89 | "output_type": "stream", 90 | "name": "stdout", 91 | "text": "" 92 | } 93 | ], 94 | "execution_count": null 95 | } 96 | ] 97 | } -------------------------------------------------------------------------------- /__tests__/SQLNotebooks/sys_databases.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "sql", 5 | "display_name": "SQL" 6 | }, 7 | "language_info": { 8 | "name": "sql", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".sql" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "code", 19 | "source": [ 20 | "SELECT SYSDATETIME()\r\n" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "413a2c59-ca9a-440b-87ac-502f0506b51e" 24 | }, 25 | "outputs": [ 26 | { 27 | "output_type": "stream", 28 | "text": "", 29 | "name": "stdout" 30 | } 31 | ] 32 | },{"cell_type":"markdown","metadata":{},"source":["\r \n Created from: .\\sys_databases.ipynb\r \n \r \n Created by: Export-NotebookToSqlScript\r \n Created on: 07/27/2020 18:20:25 \r \n "]},{"cell_type":"markdown","metadata":{},"source":[" First, find out how many databases are on this instance. "]},{ 33 | "cell_type": "code", 34 | "source": [ 35 | "\r\n\r\nSELECT *\r\n FROM sys.databases\r\n\r\n" 36 | ], 37 | "metadata": { 38 | "azdata_cell_guid": "f23c0758-f8e2-492d-8634-616840f74952" 39 | }, 40 | "outputs": [ 41 | { 42 | "output_type": "stream", 43 | "text": "", 44 | "name": "stdout" 45 | } 46 | ] 47 | },{"cell_type":"markdown","metadata":{},"source":[" Next check out all the processes currently running, using sys.processes. "]},{ 48 | "cell_type": "code", 49 | "source": [ 50 | "\r\n\r\nSELECT *\r\n FROM sys.processes\r\n\r\n" 51 | ], 52 | "metadata": { 53 | "azdata_cell_guid": "67004b57-2172-4439-84e9-1ba5791965b6" 54 | }, 55 | "outputs": [ 56 | { 57 | "output_type": "stream", 58 | "text": "", 59 | "name": "stdout" 60 | } 61 | ] 62 | },{"cell_type":"markdown","metadata":{},"source":[" Disk space subsection "]},{ 63 | "cell_type": "code", 64 | "source": [ 65 | "\r\n\r\nSET NOCOUNT ON;\r\nSET ANSI_WARNINGS ON;\r\nSET QUOTED_IDENTIFIER ON;\r\n\r\nDECLARE @sqlmajorver int\r\nDECLARE @ErrorMessage NVARCHAR(4000)\r\n\r\nSELECT @sqlmajorver = CONVERT(int, (@@microsoftversion / 0x1000000) & 0xff);\r\n\r\nIF @sqlmajorver > 9\r\nBEGIN\r\n\tSELECT DISTINCT 'Information' AS [Category], 'Disk_Space' AS [Information], vs.logical_volume_name,\r\n\t\tvs.volume_mount_point, vs.file_system_type, CONVERT(int,vs.total_bytes/1048576.0) AS TotalSpace_MB,\r\n\t\tCONVERT(int,vs.available_bytes/1048576.0) AS FreeSpace_MB, vs.is_compressed\r\n\tFROM sys.master_files mf\r\n\tCROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.[file_id]) vs\r\n\tORDER BY FreeSpace_MB ASC\r\nEND;" 66 | ], 67 | "metadata": { 68 | "azdata_cell_guid": "4959e824-8ec9-4d6a-9107-a377bd4ab58d" 69 | }, 70 | "outputs": [ 71 | { 72 | "output_type": "stream", 73 | "text": "", 74 | "name": "stdout" 75 | } 76 | ] 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /__tests__/TestHasParameterizedCell.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\..\PowerShellNotebook.psd1 -Force 2 | 3 | Describe "Test Test-HasParameterizedCell" -Tag 'Test-HasParameterizedCell' { 4 | 5 | It "Should have Test-HasParameterizedCell" { 6 | $actual = Get-Command Test-HasParameterizedCell -ErrorAction SilentlyContinue 7 | $actual | Should -Not -Be $Null 8 | } 9 | 10 | It "Tests notebook has a parmeterized cell" { 11 | 12 | $actual = Test-HasParameterizedCell "$PSScriptRoot\NotebooksForUseWithInvokeOutfile\parameters.ipynb" 13 | 14 | split-path -Leaf $actual.Path | Should -BeExactly 'parameters.ipynb' 15 | $actual.HasParameterizedCell | Should -BeTrue 16 | } 17 | 18 | It "Tests notebook has a parmeterized cell via pipeline" { 19 | $actual = Get-ChildItem "$PSScriptRoot\NotebooksForUseWithInvokeOutfile" *.ipynb | Test-HasParameterizedCell 20 | 21 | $HasParameterizedCell = $actual | Where-Object { $_.HasParameterizedCell -eq $true } 22 | $DoesNotHaveParameterizedCell = $actual | Where-Object { $_.HasParameterizedCell -eq $false } 23 | 24 | # $HasParameterizedCell | Out-Host 25 | # $DoesNotHaveParameterizedCell | Out-Host 26 | 27 | $HasParameterizedCell.Count | Should -Be 3 28 | 29 | (Split-Path -Leaf $HasParameterizedCell[0].Path) | Should -BeExactly 'Hello-PowerShell.ipynb' 30 | (Split-Path -Leaf $HasParameterizedCell[1].Path) | Should -BeExactly 'NotebookMoreThanOneParameterCell.ipynb' 31 | (Split-Path -Leaf $HasParameterizedCell[2].Path) | Should -BeExactly 'parameters.ipynb' 32 | 33 | (Split-Path -Leaf $DoesNotHaveParameterizedCell[0].Path) | Should -BeExactly 'CellHasAnError.ipynb' 34 | (Split-Path -Leaf $DoesNotHaveParameterizedCell[1].Path) | Should -BeExactly 'ComboGoodAndErrorCells.ipynb' 35 | (Split-Path -Leaf $DoesNotHaveParameterizedCell[2].Path) | Should -BeExactly 'NotebookNoParameterCells.ipynb' 36 | (Split-Path -Leaf $DoesNotHaveParameterizedCell[3].Path) | Should -BeExactly 'testFile1.ipynb' 37 | (Split-Path -Leaf $DoesNotHaveParameterizedCell[4].Path) | Should -BeExactly 'VariablesAcrossCells.ipynb' 38 | } 39 | } -------------------------------------------------------------------------------- /__tests__/samplemarkdown/demo.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is `addition` 4 | 5 | ```ps 6 | 5 + 7 7 | ``` 8 | 9 | -------------------------------------------------------------------------------- /__tests__/samplemarkdown/demoPowerShellFenceBlock.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is `addition` 4 | 5 | ```powershell 6 | 40 + 2 7 | ``` 8 | 9 | -------------------------------------------------------------------------------- /__tests__/samplemarkdown/excludeResults.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```ps 4 | # Exclude Results 5 | 1+1 6 | ``` 7 | 8 | ```ps 9 | #exclude results 10 | 3+3 11 | ``` 12 | 13 | ```ps 14 | #exclude result 15 | 2+2 16 | ``` 17 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | branches: 8 | include: 9 | - '*' 10 | # - master 11 | # - releases/* 12 | paths: 13 | exclude: 14 | - README.md 15 | - CHANGELOG.md 16 | 17 | jobs: 18 | - job: Windows 19 | pool: 20 | vmImage: 'windows-latest' 21 | 22 | steps: 23 | - powershell: './CI/CI.ps1 -Test' 24 | displayName: 'Install and Test' 25 | 26 | # - task: PublishTestResults@2 27 | # inputs: 28 | # testResultsFormat: 'NUnit' 29 | # testResultsFiles: '**/TestResults*.xml' 30 | # failTaskOnFailedTests: true 31 | 32 | # - powershell: './CI/CI.ps1 -Artifact' 33 | # displayName: 'Prepare Artifact' 34 | # - task: PublishPipelineArtifact@1 35 | # inputs: 36 | # targetPath: '$(Build.ArtifactStagingDirectory)' 37 | # artifact: 'Modules' 38 | # - powershell: './CI/CI.ps1 -Analyzer' 39 | # displayName: 'Invoke ScriptAnalyzer' 40 | # - task: PublishPipelineArtifact@1 41 | # inputs: 42 | # targetPath: '$(Build.SourcesDirectory)' 43 | # artifact: 'Source' 44 | 45 | - job: WindowsPSCore 46 | pool: 47 | vmImage: 'windows-latest' 48 | 49 | steps: 50 | - pwsh: './CI/CI.ps1 -Test' 51 | displayName: 'Install and Test' 52 | 53 | # - task: PublishTestResults@2 54 | # inputs: 55 | # testResultsFormat: 'NUnit' 56 | # testResultsFiles: '**/TestResults*.xml' 57 | # failTaskOnFailedTests: true 58 | 59 | - job: Ubuntu 60 | pool: 61 | vmImage: 'ubuntu-latest' 62 | 63 | steps: 64 | - powershell: './CI/CI.ps1 -Test' 65 | displayName: 'Install and Test' 66 | 67 | # - task: PublishTestResults@2 68 | # inputs: 69 | # testResultsFormat: 'NUnit' 70 | # testResultsFiles: '**/TestResults*.xml' 71 | # failTaskOnFailedTests: true 72 | 73 | - job: macOS 74 | pool: 75 | vmImage: 'macOS-latest' 76 | 77 | steps: 78 | - powershell: './CI/CI.ps1 -Test' 79 | displayName: 'Install and Test' 80 | 81 | # - task: PublishTestResults@2 82 | # inputs: 83 | # testResultsFormat: 'NUnit' 84 | # testResultsFiles: '**/TestResults*.xml' 85 | # failTaskOnFailedTests: true 86 | -------------------------------------------------------------------------------- /input.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": ".net-powershell", 5 | "display_name": ".NET (PowerShell)" 6 | }, 7 | "language_info": { 8 | "name": "PowerShell", 9 | "version": "7.0", 10 | "mimetype": "text/x-powershell", 11 | "file_extension": ".ps1", 12 | "pygments_lexer": "powershell" 13 | } 14 | }, 15 | "nbformat_minor": 2, 16 | "nbformat": 4, 17 | "cells": [ 18 | { 19 | "cell_type": "code", 20 | "source": [ 21 | "$alpha = 1.2\r\n", 22 | "$ratio = 3.7\r\n", 23 | "\r\n", 24 | "$a = 5" 25 | ], 26 | "metadata": { 27 | "azdata_cell_guid": "fdf55bdd-2d89-41cc-a4fc-2fafab9e98eb", 28 | "tags": [ 29 | "parameters" 30 | ] 31 | }, 32 | "outputs": [], 33 | "execution_count": null 34 | }, 35 | { 36 | "cell_type": "code", 37 | "source": [ 38 | "\"alpha = {0}, ratio = {1}, and alpha * ratio = {2}\" -f $alpha, $ratio, ($alpha * $ratio)" 39 | ], 40 | "metadata": { 41 | "azdata_cell_guid": "b20b9960-d852-4330-87e1-87f000f0b53e" 42 | }, 43 | "outputs": [], 44 | "execution_count": null 45 | }, 46 | { 47 | "cell_type": "code", 48 | "source": [ 49 | "$twice = $a * 2" 50 | ], 51 | "metadata": { 52 | "azdata_cell_guid": "03dcc6b2-8e91-4531-a896-8fe88e84f353" 53 | }, 54 | "outputs": [], 55 | "execution_count": null 56 | }, 57 | { 58 | "cell_type": "code", 59 | "source": [ 60 | "\"a = \" + $a + \" and twice = \" + $twice" 61 | ], 62 | "metadata": { 63 | "azdata_cell_guid": "ee972003-ac98-40f1-bb47-212d902d0000" 64 | }, 65 | "outputs": [], 66 | "execution_count": null 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /media/ADSPowerShellNoteBook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/ADSPowerShellNoteBook.png -------------------------------------------------------------------------------- /media/ConvertMarkdownToNotebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/ConvertMarkdownToNotebook.png -------------------------------------------------------------------------------- /media/ConvertedFromDemoText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/ConvertedFromDemoText.png -------------------------------------------------------------------------------- /media/CreateNotebookUsingTheDSL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/CreateNotebookUsingTheDSL.png -------------------------------------------------------------------------------- /media/CvtFromMarkdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/CvtFromMarkdown.png -------------------------------------------------------------------------------- /media/EnableLanguageForDSL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/EnableLanguageForDSL.png -------------------------------------------------------------------------------- /media/InvokePowerShellNotebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/InvokePowerShellNotebook.png -------------------------------------------------------------------------------- /media/InvokePowerShellNotebookAsExcel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/InvokePowerShellNotebookAsExcel.png -------------------------------------------------------------------------------- /media/IsParameterCell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/IsParameterCell.png -------------------------------------------------------------------------------- /media/ParametersTag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/media/ParametersTag.gif -------------------------------------------------------------------------------- /samplenotebook/Chapter01code.ipynb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/samplenotebook/Chapter01code.ipynb -------------------------------------------------------------------------------- /samplenotebook/MDandCodeSrcAsArray.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "powershell", 5 | "display_name": "PowerShell" 6 | }, 7 | "language_info": { 8 | "name": "powershell", 9 | "codemirror_mode": "shell", 10 | "mimetype": "text/x-sh", 11 | "file_extension": ".ps1" 12 | } 13 | }, 14 | "nbformat_minor": 2, 15 | "nbformat": 4, 16 | "cells": [ 17 | { 18 | "cell_type": "markdown", 19 | "source": [ 20 | "# Chapter 1" 21 | ], 22 | "metadata": { 23 | "azdata_cell_guid": "0055dec0-e844-4de2-bdc2-7424bce57554" 24 | } 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "source": [ 29 | "This is multiple lines of markdown\r\n", 30 | "- Item Y\r\n", 31 | "- Item Y\r\n", 32 | "- Item Y\r\n", 33 | "\r\n", 34 | "and a numbered list\r\n", 35 | "1. Item\r\n", 36 | "1. Item\r\n", 37 | "1. Item" 38 | ], 39 | "metadata": { 40 | "azdata_cell_guid": "7c42eca7-d22e-4bee-8cc1-43d77a50b51d" 41 | } 42 | }, 43 | { 44 | "cell_type": "code", 45 | "source": [ 46 | "1..10 | % {\n", 47 | " \"Hello World\"\n", 48 | "}" 49 | ], 50 | "metadata": { 51 | "azdata_cell_guid": "b7f2d298-ea50-4fbe-9fe3-0f751c6038c6" 52 | }, 53 | "outputs": [ 54 | { 55 | "output_type": "stream", 56 | "name": "stdout", 57 | "text": "Hello World\n" 58 | } 59 | ], 60 | "execution_count": 1 61 | } 62 | ] 63 | } -------------------------------------------------------------------------------- /samplenotebook/SimpleNotebookToTestConvertToMarkdown.ipynb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/samplenotebook/SimpleNotebookToTestConvertToMarkdown.ipynb -------------------------------------------------------------------------------- /samplenotebook/SingleCodeBlock.ipynb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfinke/PowerShellNotebook/6edff625ea977e4036559b471fd76c5414a3e1af/samplenotebook/SingleCodeBlock.ipynb -------------------------------------------------------------------------------- /samplenotebook/csharp.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "hello world" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "Console.Write(\"hello world\")" 18 | ] 19 | } 20 | ], 21 | "metadata": { 22 | "kernelspec": { 23 | "display_name": ".NET (C#)", 24 | "language": "C#", 25 | "name": ".net-csharp" 26 | }, 27 | "language_info": { 28 | "file_extension": ".cs", 29 | "mimetype": "text/x-csharp", 30 | "name": "C#", 31 | "pygments_lexer": "csharp", 32 | "version": "8.0" 33 | } 34 | }, 35 | "nbformat": 4, 36 | "nbformat_minor": 4 37 | } 38 | -------------------------------------------------------------------------------- /samplenotebook/fsharp.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "hello world\n" 13 | ] 14 | }, 15 | { 16 | "data": { 17 | "text/html": [ 18 | "" 19 | ] 20 | }, 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "output_type": "execute_result" 24 | } 25 | ], 26 | "source": [ 27 | "printfn \"hello world\"" 28 | ] 29 | } 30 | ], 31 | "metadata": { 32 | "kernelspec": { 33 | "display_name": ".NET (F#)", 34 | "language": "F#", 35 | "name": ".net-fsharp" 36 | }, 37 | "language_info": { 38 | "file_extension": ".fs", 39 | "mimetype": "text/x-fsharp", 40 | "name": "C#", 41 | "pygments_lexer": "fsharp", 42 | "version": "4.5" 43 | } 44 | }, 45 | "nbformat": 4, 46 | "nbformat_minor": 4 47 | } 48 | -------------------------------------------------------------------------------- /samplenotebook/powershell.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "Write-Host \"hello world\"" 10 | ] 11 | } 12 | ], 13 | "metadata": { 14 | "kernelspec": { 15 | "display_name": ".NET (PowerShell)", 16 | "language": "PowerShell", 17 | "name": ".net-powershell" 18 | }, 19 | "language_info": { 20 | "file_extension": ".ps1", 21 | "mimetype": "text/x-powershell", 22 | "name": "PowerShell", 23 | "pygments_lexer": "powershell", 24 | "version": "7.0" 25 | } 26 | }, 27 | "nbformat": 4, 28 | "nbformat_minor": 4 29 | } 30 | -------------------------------------------------------------------------------- /samplenotebook/python.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "hello world\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "print(\"hello world\")" 18 | ] 19 | } 20 | ], 21 | "metadata": { 22 | "kernelspec": { 23 | "display_name": "Python 3", 24 | "language": "python", 25 | "name": "python3" 26 | }, 27 | "language_info": { 28 | "codemirror_mode": { 29 | "name": "ipython", 30 | "version": 3 31 | }, 32 | "file_extension": ".py", 33 | "mimetype": "text/x-python", 34 | "name": "python", 35 | "nbconvert_exporter": "python", 36 | "pygments_lexer": "ipython3", 37 | "version": "3.7.3" 38 | } 39 | }, 40 | "nbformat": 4, 41 | "nbformat_minor": 4 42 | } 43 | --------------------------------------------------------------------------------