Skip to content

Commit

Permalink
Add initial PSGalleryScript implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
RamblingCookieMonster committed Jul 23, 2016
1 parent 37c0e0f commit 3d31dc5
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 3 deletions.
6 changes: 5 additions & 1 deletion PSDeploy/PSDeploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

AppVeyorModule:
Script: AppVeyorModule.ps1
Description: EXPERIMENTAL... Deploys a module to AppVeyor, simplifying creation and distribution of development builds for PowerShell Modules.
Description: Deploys a module to AppVeyor, simplifying creation and distribution of development builds for PowerShell Modules.

Artifactory:
Script: Artifactory.ps1
Expand Down Expand Up @@ -47,6 +47,10 @@ PSGalleryModule:
Script: PSGalleryModule.ps1
Description: Deploys a PowerShell module to a repository like the PowerShell Gallery.

PSGalleryScript:
Script: PSGalleryScript.ps1
Description: EXPERIMENTAL... Deploys a PowerShell script to a repository like the PowerShell Gallery. WARNING- this modifies the scripts you want to deploy

Task:
Script: Task.ps1
Description: Support deployments by handling simple tasks.
Expand Down
4 changes: 2 additions & 2 deletions PSDeploy/PSDeployScripts/AppVeyorModule.ps1
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<#
.SYNOPSIS
EXPERIMENTAL: Deploys a module as an AppVeyor artifact
Deploys a module as an AppVeyor artifact
.DESCRIPTION
EXPERIMENTAL: Deploys a module as an AppVeyor artifact
Deploys a module as an AppVeyor artifact
Deployment source should be either:
The path to the module folder, or;
Expand Down
202 changes: 202 additions & 0 deletions PSDeploy/PSDeployScripts/PSGalleryScript.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
<#
.SYNOPSIS
Deploys a script to a PowerShell repository like the PowerShell Gallery.
.DESCRIPTION
Deploys a script to a PowerShell repository like the PowerShell Gallery.
This only supports publishing items that do not already have a PSScriptInfo header.
We might support this down the line when Update-ScriptFileInfo is fixed.
Notes on how we define the PSScriptInfo header based on your WithOptions parameters and other info:
* If you specify a WithOptions parameter, that takes precedence over an existing publication
* If you do not specify a WithOptions parameter and have previously published this,
we query and re-use data from the existing published script
* In a few special cases (required fields), we will generate initial data if you
do not include it in WithOptions or have an existing published script:
GUID - We create a new GUID
VERSION - we set to 1.0.0
AUTHOR - We set to Unknown
DESCRIPTION - We set to the file name (e.g. if I publish Open-IseFunction.ps1, DESCRIPTION=Open-ISEFunction)
.PARAMETER Deployment
Deployment to run
Source is the path of the module to deploy.
Target is a valid PSRepository name. Defaults to PSGallery
.PARAMETER ApiKey
API Key used to authenticate to PowerShell repository.
.PARAMETER VERSION
VERSION for script info
We set to 1.0.0 if you don't include it here or in a previously published version
Note that you need to bump this for a successful publish, you can't overwrite an existing version
.PARAMETER GUID
GUID for script info
We create a new one if you don't include it here or in a previously published version
.PARAMETER AUTHOR
AUTHOR for script info
We set to unknown if you don't include it here or in a previously published version
.PARAMETER DESCRIPTION
DESCRIPTION for script info
We set to the basename of your script if you don't include it here or in a previously published version
.PARAMETER COMPANYNAME
COMPANYNAME for script info
.PARAMETER COPYRIGHT
COPYRIGHT for script info
.PARAMETER TAGS
TAGS for script info
.PARAMETER LICENSEURI
LICENSEURI for script info
.PARAMETER PROJECTURI
PROJECTURI for script info
.PARAMETER ICONURI
ICONURI for script info
.PARAMETER EXTERNALMODULEDEPENDENCIES
EXTERNALMODULEDEPENDENCIES for script info
.PARAMETER REQUIREDSCRIPTS
REQUIREDSCRIPTS for script info
.PARAMETER EXTERNALSCRIPTDEPENDENCIES
EXTERNALSCRIPTDEPENDENCIES for script info
.PARAMETER RELEASENOTES
RELEASENOTES for script info
#>
[cmdletbinding()]
param(
[ValidateScript({ $_.PSObject.TypeNames[0] -eq 'PSDeploy.Deployment' })]
[psobject[]]$Deployment,

[Parameter(Mandatory)]
[string]$ApiKey
)

# We make the assumption that WithOptions may be updated by the user or build process. It takes precedence.
function Pick-Precedence {
[cmdletbinding()]
param(
$Name,
$PSGalleryOutput = $Existing
)

$WithOptionsValue = $null
$WithOptionsValue = $Deploy.DeploymentOptions.$Name
$ExistingValue = $null
$ExistingValue = $PSGalleryOutput.$Name
if($WithOptionsValue)
{
$WithOptionsValue
}
else
{
$ExistingValue
}
}

foreach($deploy in $Deployment) {
if(-not $deploy.Targets)
{
#Default to the PSGallery
$deploy.Targets = @('PSGallery')
}
foreach($target in $deploy.Targets) {
Write-Verbose -Message "Starting deployment [$($deploy.DeploymentName)] to PowerShell repository [$Target]"

# Validate that $target has been setup as a valid PowerShell repository
$validRepo = Get-PSRepository -Name $target -Verbose:$false -ErrorAction SilentlyContinue
if (-not $validRepo) {
throw "[$target] has not been setup as a valid PowerShell repository."
}

# Check gallery for existing. We don't want a new GUID every time...
$Name = ( Get-Item $Deploy.source -ErrorAction Stop ).BaseName
$Existing = $null
$Existing = @( Find-Script -Name $Name -Repository $Target )
if ($Existing.Count -gt 1)
{
Write-Error "We found more than one script matching $Name. Did you include a wildcard?"
continue
}

# guid is in the additionalmetadata hash
$Existing[0] | Add-Member -MemberType NoteProperty -Name GUID -Value $Existing.AdditionalMetadata['GUID']
# Extract deployment options / header values. Not all of these are props.
$AllNodes = echo VERSION GUID AUTHOR COMPANYNAME COPYRIGHT,
TAGS LICENSEURI PROJECTURI ICONURI,
EXTERNALMODULEDEPENDENCIES, REQUIREDSCRIPTS,
EXTERNALSCRIPTDEPENDENCIES, RELEASENOTES, DESCRIPTION

foreach($item in $AllNodes)
{
$value = $null
$value = Pick-Precedence -Name $Item -PSGalleryOutput $Existing
Set-Variable -Name $Item -Value $value
}

# Items that might be blank on new scripts, that we need filled out
if(-not $GUID)
{
$GUID = [GUID]::NewGuid().Guid
}
if(-not $VERSION)
{
$VERSION = '1.0.0'
}
if(-not $AUTHOR)
{
$AUTHOR = 'Unknown'
}
if(-NOT $DESCRIPTION)
{
$DESCRIPTION = $Name
}

# Build up the header
$Header = "<#PSScriptInfo`r`n"
$Nodes = echo VERSION GUID AUTHOR DESCRIPTION COMPANYNAME COPYRIGHT TAGS LICENSEURI PROJECTURI,
ICONURI EXTERNALMODULEDEPENDENCIES REQUIREDSCRIPTS EXTERNALSCRIPTDEPENDENCIES RELEASENOTES

foreach($item in $Nodes)
{
$Value = $null
If($Value = Get-Variable -Name $item -ValueOnly -ErrorAction SilentlyContinue)
{
$header += ".$item`r`n $Value`r`n"
}
}

$header += "#>`r`n"

# Write the header, publish
$SourceContent = Get-Content $Deploy.Source -Raw
Set-Content $Deploy.Source -Value "$Header$SourceContent" -Force

# Start building params
$Params = @{
Path = $Deploy.Source
Repository = $Target
NugetApiKey = $Deploy.DeploymentOptions.ApiKey
Verbose = $VerbosePreference
}

Publish-Script @params
}
}
51 changes: 51 additions & 0 deletions Tests/Types/PSGalleryScript.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Remove-Module PSDeploy -ErrorAction SilentlyContinue
Import-Module $PSScriptRoot\..\..\PSDeploy\PSDeploy.psd1
Set-BuildEnvironment -Path $PSScriptRoot\..\..


InModuleScope 'PSDeploy' {
$PSVersion = $PSVersionTable.PSVersion.Major
$ProjectRoot = $ENV:BHProjectPath

$Verbose = @{}
if($ENV:BHBranchName -notlike "master" -or $env:BHCommitMessage -match "!verbose")
{
$Verbose.add("Verbose",$True)
}

Describe "PSGalleryScript PS$PSVersion" {

Context 'Publishes Script' {
Mock Publish-Script { Return $true }
Mock Get-PSRepository { Return $true }
Mock Find-Script { }
Mock Set-Content { }

$Results = Invoke-PSDeploy @Verbose -Path "$ProjectRoot\Tests\artifacts\DeploymentsPSGalleryScript.psdeploy.ps1" -Force

It 'Should execute Set-Content' {
Assert-MockCalled Set-Content -Times 1 -Exactly
}

It 'Should execute Publish-Module' {
Assert-MockCalled Publish-Script -Times 1 -Exactly
}

It 'Should Return Mocked output' {
$Results | Should be $True
}
}

Context 'Repository does not Exist' {
Mock Publish-Script {}
Mock Get-PSRepository { Return $false }
Mock Find-Script { }
Mock Set-Content { }

It 'Throws because Repository could not be found' {
$Results = { Invoke-PSDeploy @Verbose -Path "$ProjectRoot\Tests\artifacts\DeploymentsPSGalleryScript.psdeploy.ps1" -Force }
$Results | Should Throw
}
}
}
}
10 changes: 10 additions & 0 deletions Tests/artifacts/DeploymentsPSGalleryScript.psdeploy.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Deploy TestScript {
By PSGalleryScript {
FromSource '\Scripts\Test-Script.ps1'
To 'PSGallery'
Tagged Testing
WithOptions @{
ApiKey = '0c3e374b-49a3-4b05-a597-fd45773a4fb6'
}
}
}
40 changes: 40 additions & 0 deletions Tests/artifacts/Scripts/Open-ISEFunction.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
function Open-ISEFunction {
<#
.SYNOPSIS
Open a function in ISE
.DESCRIPTION
Open a function in ISE. Any function that can be obtained by (get-command <command>).definition. Pretty much anything that isn't compiled in a DLL or obfuscated in some other manner.
.FUNCTIONALITY
General Command
#>
[cmdletbinding()]
param(

#In the validation block, check if input is a function and get the definition
[ValidateScript({ Get-Command -commandtype function -name $_ })]
[string[]]$function
)

foreach($fn in $function){

#Get the definition
$definition = (Get-Command -commandtype function -name $fn).definition

#If the definition exists, add a new tab with the contents.
if($definition){

#Definition won't include function keyword. Add it.
$definition = "function $fn { $definition }"

#Add the file and definition content
$tab = $psise.CurrentPowerShellTab.files.Add()
$tab.editor.text = $definition

#set the caret to column 1 line 1
$tab.editor.SetCaretPosition(1,1)

#Sleep a few milliseconds. Not sure why but omitting this has caused issues for me.
start-sleep -Milliseconds 200
}
}
}
41 changes: 41 additions & 0 deletions docs/Example-PSGalleryScript-Deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
This is a quick example showing a PSGalleryScript deployment:

## Example

Here's the deployment config, My.PSDeploy.ps1:

```PowerShell
Deploy Script {
By PSGalleryScript {
FromSource 'Relative\Path\To\This-Script.ps1'
To PSGallery
WithOptions @{
ApiKey = $ENV:NugetApiKey
Version = $ENV:Version
Author = "Warren F."
Description = "A Description for the gallery"
}
}
}
```

In this example, we're deploying the script 'This-Script.ps1' using an API key stored in $ENV:NugetApiKey.

The API key might be stored in a [secure variable](https://www.appveyor.com/docs/build-configuration#secure-variables) of some sort for your build system.

## Notes

WARNING: This deployment type will modify the content of your FromSource files, by appending a header with script info required by the PowerShell Gallery.

Notes on how we define the PSScriptInfo header based on your WithOptions parameters and other info:

* If you specify a WithOptions parameter, that takes precedence over an existing publication
* If you do not specify a WithOptions parameter and have previously published this, we query and re-use data from the existing published script
* In a few special cases (required fields), we will generate initial data if you do not include it in WithOptions or have an existing published script:
* GUID - We create a new GUID
* VERSION - we set to 1.0.0
* AUTHOR - We set to Unknown
* DESCRIPTION - We set to the file name (e.g. if I publish Open-IseFunction.ps1, DESCRIPTION=Open-ISEFunction)

No output is produced from this deployment type.

0 comments on commit 3d31dc5

Please sign in to comment.