From 962244c84212feb4b8aa9e4cc88a7dc9abd42189 Mon Sep 17 00:00:00 2001 From: Rasmus Jelsgaard Date: Mon, 27 May 2019 12:47:36 +0200 Subject: [PATCH] Add scripts to handle PR flow Logic to point to the last commit of branch being reviewed in PR Set correct requirements psake and pester Use deploy instead of stages Only deploy on master when tagged Small improvement to debug logging Change the name of module so we can test it sneakily --- .travis.yml | 36 ++++++ Build.ps1 | 116 +----------------- GitVersion.yml | 4 + Install-Prerequisites.ps1 | 6 + Public/Invoke-Jenkins.ps1 | 7 ++ ReleaseNotes.md | 8 +- Tests/Invoke-Jenkins.Tests.ps1 | 29 ++--- ...nkins.psd1 => ThreeShape.Jenkins.Beta.psd1 | 27 ++-- ...nkins.psm1 => ThreeShape.Jenkins.Beta.psm1 | 0 scripts/setup_repo.sh | 14 +++ 10 files changed, 102 insertions(+), 145 deletions(-) create mode 100644 .travis.yml create mode 100644 GitVersion.yml create mode 100644 Install-Prerequisites.ps1 rename ThreeShape.Jenkins.psd1 => ThreeShape.Jenkins.Beta.psd1 (83%) rename ThreeShape.Jenkins.psm1 => ThreeShape.Jenkins.Beta.psm1 (100%) create mode 100644 scripts/setup_repo.sh diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d62c8e5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,36 @@ +sudo: required + +language: csharp +dotnet: 2.1 +mono: none + +services: + - docker + +dist: trusty +addons: + apt: + sources: + - sourceline: "deb [arch=amd64] https://packages.microsoft.com/ubuntu/14.04/prod trusty main" + key_url: "https://packages.microsoft.com/keys/microsoft.asc" + packages: + - powershell + +before_install: + - chmod ugo+x ./scripts/setup_repo.sh && ./scripts/setup_repo.sh + - sudo apt-get install -y powershell + +install: + - pwsh -f "./Install-Prerequisites.ps1" + - export GitVersion_Version=$(docker run --rm -v "$(pwd):/repo" gittools/gitversion:5.0.0-linux-debian-9-netcoreapp2.2 /repo -output json -showvariable majorminorpatch) + - "echo \"GitVersion says: ${GitVersion_Version} \"" + +script: + - pwsh -c "Invoke-Psake -buildFile Build.ps1 -taskList build,test" + +deploy: + provider: script + script: pwsh -c "Invoke-Psake -buildFile Build.ps1 -taskList build,test,publish" + on: + tags: true + branch: master diff --git a/Build.ps1 b/Build.ps1 index 5dcf47e..9eaca12 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -1,43 +1,3 @@ -# This is a PSake script that supports the following tasks: -# clean, build, test and publish. The default task is build. -# -# The publish task uses the Publish-Module command to publish -# to either the PowerShell Gallery (the default) or you can change -# the $Repository property to the name of an alternate repository. -# -# The test task invokes Pester to run any Pester tests in your -# workspace folder. Name your test scripts .Tests.ps1 -# and Pester will find and run the tests contained in the files. -# -# You can run this build script directly using the invoke-psake -# command which will execute the build task. This task "builds" -# a temporary folder from which the module can be published. -# -# PS C:\> invoke-psake build.ps1 -# -# You can run your Pester tests (if any) by running the following command. -# -# PS C:\> invoke-psake build.ps1 -taskList test -# -# You can execute the publish task with the following command. Note that -# the publish task will run the test task first. The Pester tests must pass -# before the publish task will run. The first time you run the publish -# command, you will be prompted to enter your PowerShell Gallery NuGetApiKey. -# After entering the key, it is encrypted and stored so you will not have to -# enter it again. -# -# PS C:\> invoke-psake build.ps1 -taskList publish -# -# You can verify the stored and encrypted NuGetApiKey by running the following -# command. This will display your NuGetApiKey in plain text! -# -# PS C:\> invoke-psake build.ps1 -taskList showKey -# -# You can store a new NuGetApiKey with this command. You can leave off -# the -properties parameter and you'll be prompted for the key. -# -# PS C:\> invoke-psake build.ps1 -taskList storeKey -properties @{NuGetApiKey='test123'} -# ############################################################################### # Customize these properties for your module. @@ -63,15 +23,6 @@ Properties { (Split-Path $PSCommandPath -Leaf) ) - # Name of the repository you wish to publish to. Default repo is the PSGallery. - $PublishRepository = $null - - # Your NuGet API key for the PSGallery. Leave it as $null and the first time - # you publish you will be prompted to enter your API key. The build will - # store the key encrypted in a file, so that on subsequent publishes you - # will no longer be prompted for the API key. - $NuGetApiKey = $null - $EncryptedApiKeyPath = "$env:LOCALAPPDATA\vscode-powershell\NuGetApiKey.clixml" } ############################################################################### @@ -79,15 +30,11 @@ Properties { ############################################################################### Task PrePublish { $functionDeclarations = @( Get-ChildItem -Path $PublishDir\Public\*.ps1 -ErrorAction SilentlyContinue ) - $functionNames = @() - foreach ($function in $functionDeclarations) { - $functionNames += $function.BaseName - } - $functionsToExport = $functionNames -join "," + [string[]]$functionNames = @($functionDeclarations.BaseName) Update-ModuleManifest -Path $PublishDir\${ModuleName}.psd1 ` - -ModuleVersion "0.0.14" ` - -FunctionsToExport $functionsToExport + -ModuleVersion "$env:GitVersion_Version" ` + -FunctionsToExport $functionNames } Task PostPublish { @@ -102,8 +49,8 @@ Task default -depends Build Task Publish -depends Test, PrePublish, PublishImpl, PostPublish { } -Task PublishImpl -depends Test -requiredVariables PublishDir, EncryptedApiKeyPath { - $NuGetApiKey = Get-NuGetApiKey $NuGetApiKey $EncryptedApiKeyPath +Task PublishImpl -depends Test -requiredVariables PublishDir { + $NuGetApiKey = $env:PSGalleryAPIToken $publishParams = @{ Path = $PublishDir @@ -119,7 +66,7 @@ Task PublishImpl -depends Test -requiredVariables PublishDir, EncryptedApiKeyPat Task Test -depends Build { Import-Module Pester - Invoke-Pester $PSScriptRoot + Invoke-Pester $PSScriptRoot/Tests } Task Build -depends Clean -requiredVariables PublishDir, Exclude, ModuleName { @@ -148,58 +95,7 @@ Task Init -requiredVariables PublishDir { } } -Task StoreKey -requiredVariables EncryptedApiKeyPath { - if (Test-Path $EncryptedApiKeyPath) { - Remove-Item $EncryptedApiKeyPath - } - - $null = Get-NuGetApiKey $NuGetApiKey $EncryptedApiKeyPath - "The NuGetApiKey has been stored in $EncryptedApiKeyPath" -} - -Task ShowKey -requiredVariables EncryptedApiKeyPath { - $NuGetApiKey = Get-NuGetApiKey $NuGetApiKey $EncryptedApiKeyPath - "The stored NuGetApiKey is: $NuGetApiKey" -} - Task ? -description 'Lists the available tasks' { "Available tasks:" $psake.context.Peek().tasks.Keys | Sort } - -############################################################################### -# Helper functions -############################################################################### -function Get-NuGetApiKey($NuGetApiKey, $EncryptedApiKeyPath) { - $storedKey = $null - if (!$NuGetApiKey) { - if (Test-Path $EncryptedApiKeyPath) { - $storedKey = Import-Clixml $EncryptedApiKeyPath | ConvertTo-SecureString - $cred = New-Object -TypeName PSCredential -ArgumentList 'kh',$storedKey - $NuGetApiKey = $cred.GetNetworkCredential().Password - Write-Verbose "Retrieved encrypted NuGetApiKey from $EncryptedApiKeyPath" - } - else { - $apiKeySS = Read-Host -Prompt "Enter your NuGet API Key" -AsSecureString - $cred = New-Object -TypeName PSCredential -ArgumentList 'dw',$apiKeySS - $NuGetApiKey = $cred.GetNetworkCredential().Password - } - } - - if (!$storedKey) { - # Store encrypted NuGet API key to use for future invocations - if (!$apiKeySS) { - $apiKeySS = ConvertTo-SecureString -String $NuGetApiKey -AsPlainText -Force - } - - $parentDir = Split-Path $EncryptedApiKeyPath -Parent - if (!(Test-Path -Path $parentDir)) { - $null = New-Item -Path $parentDir -ItemType Directory - } - - $apiKeySS | ConvertFrom-SecureString | Export-Clixml $EncryptedApiKeyPath - Write-Verbose "Stored encrypted NuGetApiKey to $EncryptedApiKeyPath" - } - - $NuGetApiKey -} diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..05e9ac4 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,4 @@ +mode: ContinuousDelivery +branches: {} +ignore: + sha: [] diff --git a/Install-Prerequisites.ps1 b/Install-Prerequisites.ps1 new file mode 100644 index 0000000..0915bd8 --- /dev/null +++ b/Install-Prerequisites.ps1 @@ -0,0 +1,6 @@ +# The psake module is needed to run tests and publish the module to powershell gallery. +Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted +Install-Module -Name psake -RequiredVersion 4.8.0 -Repository "PSGallery" +Install-Module -Name pester -RequiredVersion 4.8.0 -Repository "PSGallery" + + diff --git a/Public/Invoke-Jenkins.ps1 b/Public/Invoke-Jenkins.ps1 index 8299130..a115e37 100644 --- a/Public/Invoke-Jenkins.ps1 +++ b/Public/Invoke-Jenkins.ps1 @@ -22,6 +22,13 @@ function Invoke-Jenkins { $attempts = 0 + Write-Debug "---- Invoke-Jenkins ----" + Write-Debug "Resource: $Resource" + Write-Debug "Request: $request" + Write-Debug "Method: $Method" + Write-Debug "Body: $Body" + Write-Debug "Query: $Query" + while ($attempts -lt $MaximumAttempts) { try { diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 9e1b778..38b500f 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,4 +1,6 @@ -Release Notes for v0.0.14 -==================================== +# Release Notes + +## v0.1.0 +Initial version that supports making basic requests to Jenkins via the Remote API. +Includes convenience functions to make and delete jobs. -Really nothing special to note yet. diff --git a/Tests/Invoke-Jenkins.Tests.ps1 b/Tests/Invoke-Jenkins.Tests.ps1 index f6924f5..fdaf36f 100644 --- a/Tests/Invoke-Jenkins.Tests.ps1 +++ b/Tests/Invoke-Jenkins.Tests.ps1 @@ -1,4 +1,4 @@ -Import-Module "$PSScriptRoot\..\ThreeShape.Jenkins.psm1" -Force +Import-Module "$PSScriptRoot\..\Threeshape.Jenkins.Beta.psm1" -Force . $PSScriptRoot\..\Private\Get-JenkinsRequestHeaders.ps1 . $PSScriptRoot\..\Private\ConvertTo-BasicAuth.ps1 . $PSScriptRoot\..\Private\Get-CrumbHeader.ps1 @@ -36,6 +36,7 @@ function Get-MockHttpResponseException { Describe "Initialize-Jenkins method" { BeforeAll { + $script:moduleName = (Get-Item $PSScriptRoot\..\*.psd1)[0].BaseName $jenkinsUrl = "http://localhost" $apiUser = "Bobby Bobbelhead" $apiPassword = "Fallout4ever" | ConvertTo-SecureString -AsPlainText -Force @@ -44,8 +45,8 @@ Describe "Initialize-Jenkins method" { -ApiUsername $apiUser ` -ApiPassword $apiPassword - Mock -ModuleName ThreeShape.Jenkins -CommandName Get-CrumbHeader { return "this=crumb" } - Mock -ModuleName ThreeShape.Jenkins -CommandName Invoke-RestMethod { return "dfgdfg:sdfsdsf" } + Mock -ModuleName $script:moduleName -CommandName Get-CrumbHeader { return "this=crumb" } + Mock -ModuleName $script:moduleName -CommandName Invoke-RestMethod { return "dfgdfg:sdfsdsf" } } Context 'When invalid MaximumAttempts args are passed to Invoke-Jenkins' { @@ -63,7 +64,7 @@ Describe "Initialize-Jenkins method" { Context 'When a request to a jenkins backend is succesful' { It 'Then Invoke-WebRequest is called within the Invoke-Jenkins method once and only once' { - Mock -ModuleName ThreeShape.Jenkins Invoke-WebRequest { "return 200 OK"} + Mock -ModuleName $script:moduleName Invoke-WebRequest { "return 200 OK"} $result = Invoke-JenkinsRequest -Resource "nowhere" ` -Username "rasmus" ` @@ -71,13 +72,13 @@ Describe "Initialize-Jenkins method" { $result | Should -Be "return 200 OK" - Assert-MockCalled -ModuleName ThreeShape.Jenkins -Scope "It" Invoke-WebRequest -Exactly -Times 1 + Assert-MockCalled -ModuleName $script:moduleName -Scope "It" Invoke-WebRequest -Exactly -Times 1 } } Context 'When a request to jenkins fails' { It 'then Invoke-WebRequest call is retried three times by default' { - Mock -ModuleName ThreeShape.Jenkins Invoke-WebRequest { throw [System.Net.Http.HttpRequestException]::new("Simulate an exception") } + Mock -ModuleName $script:moduleName Invoke-WebRequest { throw [System.Net.Http.HttpRequestException]::new("Simulate an exception") } $result = Invoke-JenkinsRequest -Resource "nowhere" ` -Username "rasmus" ` @@ -85,12 +86,12 @@ Describe "Initialize-Jenkins method" { $result | Should -BeExactly $null - Assert-MockCalled -ModuleName ThreeShape.Jenkins -Scope "It" Invoke-WebRequest -Exactly -Times 3 + Assert-MockCalled -ModuleName $script:moduleName -Scope "It" Invoke-WebRequest -Exactly -Times 3 } It 'Then Invoke-WebRequest is retried up to the number of times specified' { - Mock -ModuleName ThreeShape.Jenkins Invoke-WebRequest { throw [System.Net.Http.HttpRequestException]::new("Simulate an exception") } + Mock -ModuleName $script:moduleName Invoke-WebRequest { throw [System.Net.Http.HttpRequestException]::new("Simulate an exception") } $result = Invoke-JenkinsRequest -Resource "nowhere" ` -Username "rasmus" ` @@ -99,13 +100,13 @@ Describe "Initialize-Jenkins method" { $result | Should -BeExactly $null - Assert-MockCalled -ModuleName ThreeShape.Jenkins -Scope "It" Invoke-WebRequest -Exactly -Times 5 + Assert-MockCalled -ModuleName $script:moduleName -Scope "It" Invoke-WebRequest -Exactly -Times 5 } It 'Any failure causes an exception to be re-thrown' { $exceptionBlock = Get-MockHttpResponseException -HttpStatusCode "[System.Net.HttpStatusCode]::Forbidden" - Mock -ModuleName ThreeShape.Jenkins Invoke-WebRequest $exceptionBlock + Mock -ModuleName $script:moduleName Invoke-WebRequest $exceptionBlock $request = { Invoke-JenkinsRequest -Resource "nowhere" ` @@ -117,7 +118,7 @@ Describe "Initialize-Jenkins method" { } It 'Any unexpected exception is rethrown for the caller to handle' { - Mock -ModuleName ThreeShape.Jenkins Invoke-WebRequest { throw [System.AccessViolationException]::new() } + Mock -ModuleName $script:moduleName Invoke-WebRequest { throw [System.AccessViolationException]::new() } $request = { Invoke-JenkinsRequest -Resource "nowhere" ` @@ -132,19 +133,19 @@ Describe "Initialize-Jenkins method" { Context 'When a request returns a redirect' { It 'The redirect is not treated as an exception when TreatRedirectsAsSucces is true as is the case by default)' { $exceptionBlock = Get-MockHttpResponseException -HttpStatusCode "[System.Net.HttpStatusCode]::Redirect" - Mock -ModuleName ThreeShape.Jenkins Invoke-WebRequest $exceptionBlock + Mock -ModuleName $script:moduleName Invoke-WebRequest $exceptionBlock $result = Invoke-JenkinsRequest -Resource "nowhere" ` -Username "rasmus" ` -Password $script:apiPassword - Assert-MockCalled -ModuleName ThreeShape.Jenkins -Scope "It" Invoke-WebRequest -Exactly -Times 1 + Assert-MockCalled -ModuleName $script:moduleName -Scope "It" Invoke-WebRequest -Exactly -Times 1 $result | Should -Not -BeNullOrEmpty } It 'The redirect causes an exception if the caller sets TreatRedirectsAsSucces = false' { $exceptionBlock = Get-MockHttpResponseException -HttpStatusCode "[System.Net.HttpStatusCode]::Redirect" - Mock -ModuleName ThreeShape.Jenkins Invoke-WebRequest $exceptionBlock + Mock -ModuleName $script:moduleName Invoke-WebRequest $exceptionBlock $request = { Invoke-JenkinsRequest -Resource "nowhere" ` diff --git a/ThreeShape.Jenkins.psd1 b/ThreeShape.Jenkins.Beta.psd1 similarity index 83% rename from ThreeShape.Jenkins.psd1 rename to ThreeShape.Jenkins.Beta.psd1 index b231370..fa1196c 100644 --- a/ThreeShape.Jenkins.psd1 +++ b/ThreeShape.Jenkins.Beta.psd1 @@ -1,18 +1,18 @@ # # Module manifest for module 'ThreeShape.Jenkins' # -# Generated by: BAT +# Generated by: 3Shape A/S # -# Generated on: 03/05/2019 +# Generated on: 03/06/2019 # @{ # Script module or binary module file associated with this manifest. - RootModule = 'ThreeShape.Jenkins.psm1' + RootModule = 'ThreeShape.Jenkins.Beta.psm1' # Version number of this module. - ModuleVersion = '0.0.14' + ModuleVersion = '0.0.17' # Supported PSEditions CompatiblePSEditions = 'Core' @@ -21,16 +21,16 @@ GUID = '006752EA-7AEB-40BE-9B64-A7396F8EA242' # Author of this module - Author = 'Team BAT' + Author = '3Shape A/S' # Company or vendor of this module CompanyName = '3Shape' # Copyright statement for this module - Copyright = '(c) 2019 3Shape. All rights reserved.' + Copyright = '(c) 2019 3Shape, licensed under the Apache 2.0 license.' # Description of the functionality provided by this module - Description = 'PowerShell module to work with Jenkins' + Description = 'PowerShell module to interact with the Jenkins Remote Access API. The module aims to wrap the API endpoints and expose them in a way that should look and feel familiar to Powershell developers.' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '6.0' @@ -80,25 +80,16 @@ # Aliases to export from this module AliasesToExport = '*' - # DSC resources to export from this module - # DscResourcesToExport = @() - - # List of all modules packaged with this module - # ModuleList = @() - - # List of all files packaged with this module - # FileList = @() - # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. - # Tags = @() + Tags = @('powershell', 'core', 'jenkins', 'hudson', 'api', 'PSEdition_Core', 'Windows', 'Linux') # A URL to the license for this module. - # LicenseUri = '' + LicenseUri = 'https://www.apache.org/licenses/LICENSE-2.0.txt' # A URL to the main website for this project. # ProjectUri = '' diff --git a/ThreeShape.Jenkins.psm1 b/ThreeShape.Jenkins.Beta.psm1 similarity index 100% rename from ThreeShape.Jenkins.psm1 rename to ThreeShape.Jenkins.Beta.psm1 diff --git a/scripts/setup_repo.sh b/scripts/setup_repo.sh new file mode 100644 index 0000000..5bdceba --- /dev/null +++ b/scripts/setup_repo.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# The git clone command in this script is needed because travis always performs git clone using a branch. +# This makes it impossible to get all the branch and tag information needed without fully cloning. +set -ev + +if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then + COMMIT_SHA=${TRAVIS_COMMIT} +else + COMMIT_SHA=${TRAVIS_PULL_REQUEST_SHA} +fi + +git clone https://github.com/$TRAVIS_REPO_SLUG.git $TRAVIS_REPO_SLUG +cd $TRAVIS_REPO_SLUG +git checkout -qf $COMMIT_SHA