├── .gitignore ├── AU ├── AU.psm1 ├── Plugins │ ├── Gist.ps1 │ ├── Git.ps1 │ ├── GitLab.ps1 │ ├── GitReleases.ps1 │ ├── Gitter.ps1 │ ├── History.ps1 │ ├── Mail.ps1 │ ├── PullRequest.ps1 │ ├── Report.ps1 │ ├── Report │ │ ├── markdown.ps1 │ │ ├── markdown_funcs.ps1 │ │ ├── r_er.png │ │ ├── r_ok.png │ │ └── text.ps1 │ ├── RunInfo.ps1 │ └── Snippet.ps1 ├── Private │ ├── AUPackage.ps1 │ ├── AUVersion.ps1 │ ├── check_url.ps1 │ ├── is_url.ps1 │ ├── is_version.ps1 │ └── request.ps1 └── Public │ ├── Get-AUPackages.ps1 │ ├── Get-RemoteChecksum.ps1 │ ├── Get-RemoteFiles.ps1 │ ├── Get-Version.ps1 │ ├── Push-Package.ps1 │ ├── Set-DescriptionFromReadme.ps1 │ ├── Test-Package.ps1 │ ├── Update-AUPackages.ps1 │ └── Update-Package.ps1 ├── CHANGELOG.md ├── DEVEL.md ├── Plugins.md ├── README.md ├── appveyor.yml ├── build.ps1 ├── chocolatey ├── au.nuspec ├── build-package.ps1 └── tools │ ├── chocolateyInstall.ps1 │ └── chocolateyUninstall.ps1 ├── install.ps1 ├── license.txt ├── publish.ps1 ├── scripts ├── Create-ModuleManifest.ps1 ├── Github-CreateRelease.ps1 └── Install-AU.ps1 ├── setup.ps1 ├── test.ps1 ├── tests ├── AUPackage.Tests.ps1 ├── General.Tests.ps1 ├── Get-Version.Tests.ps1 ├── Update-AUPackages.Streams.Tests.ps1 ├── Update-AUPackages.Tests.ps1 ├── Update-Package.Streams.Tests.ps1 ├── Update-Package.Tests.ps1 ├── test_package │ ├── test_package.nuspec │ ├── tools │ │ └── chocolateyInstall.ps1 │ └── update.ps1 └── test_package_with_streams │ ├── test_package_with_streams.json │ ├── test_package_with_streams.nuspec │ ├── tools │ └── chocolateyInstall.ps1 │ └── update.ps1 └── vars_default.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /chocolatey/tools/AU 3 | /chocolatey/tools/install.ps1 4 | /chocolatey/*.nupkg 5 | /vars.ps1 6 | /tests/.vscode/launch.json 7 | /.vscode/launch.json 8 | -------------------------------------------------------------------------------- /AU/AU.psm1: -------------------------------------------------------------------------------- 1 | #requires -version 3 2 | 3 | $paths = "Private", "Public" 4 | foreach ($path in $paths) { 5 | Get-ChildItem ([System.IO.Path]::Combine($PSScriptRoot, $path, '*.ps1')) | ForEach-Object { . $_.FullName } 6 | } 7 | -------------------------------------------------------------------------------- /AU/Plugins/Gist.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 10-Nov-2016. 3 | <# 4 | .SYNOPSIS 5 | Upload files to Github gist platform. 6 | 7 | .DESCRIPTION 8 | Plugin uploads one or more local files to the gist with the given id 9 | #> 10 | param( 11 | $Info, 12 | 13 | # Gist id, leave empty to create a new gist 14 | [string] $Id, 15 | 16 | # Github ApiKey, create in Github profile -> Settings -> Personal access tokens -> Generate new token 17 | # Make sure token has 'gist' scope set. 18 | [string] $ApiKey, 19 | 20 | # File paths to attach to gist 21 | [string[]] $Path, 22 | 23 | # Gist description 24 | [string] $Description = "Update-AUPackages Report #powershell #chocolatey", 25 | 26 | # GitHub API base url, overridable for GitHub Enterprise installations 27 | [string] $GitHubAPI = "https://api.github.com", 28 | 29 | # If the Gist should be created as public or not, ignored when Id is provided 30 | [bool] $PublicGist = $true 31 | ) 32 | 33 | # Create gist 34 | $gist = @{ 35 | description = $Description 36 | public = $PublicGist 37 | files = @{} 38 | } 39 | 40 | Get-ChildItem $Path | ForEach-Object { 41 | $name = Split-Path $_ -Leaf 42 | $content = Get-Content $_ -Raw 43 | $gist.files[$name] = @{content = "$content"} 44 | } 45 | 46 | # request 47 | 48 | #https://github.com/majkinetor/au/issues/142 49 | if ($PSVersionTable.PSVersion.major -ge 6) { 50 | $AvailableTls = [enum]::GetValues('Net.SecurityProtocolType') | Where-Object { $_ -ge 'Tls' } # PowerShell 6+ does not support SSL3, so use TLS minimum 51 | $AvailableTls.ForEach({[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor $_}) 52 | } else { 53 | [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor [System.Net.SecurityProtocolType]::Tls -bor [System.Net.SecurityProtocolType]::Ssl3 54 | } 55 | 56 | $params = @{ 57 | ContentType = 'application/json' 58 | Method = if ($Id) { "PATCH" } else { "POST" } 59 | Uri = if ($Id) { "$GitHubAPI/gists/$Id" } else { "$GitHubAPI/gists" } 60 | Body = $gist | ConvertTo-Json 61 | UseBasicparsing = $true 62 | Headers = @{ 'Accept' = 'application/vnd.github.v3+json' } 63 | } 64 | 65 | if ($ApiKey) { 66 | $params.Headers['Authorization'] = ('Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($ApiKey))) 67 | } 68 | 69 | $Response = Invoke-WebRequest @params 70 | if ($Response.StatusCode -in @(200, 201, 304)) { 71 | $JsonResponse = $Response.Content | ConvertFrom-Json 72 | $GistURL = $JsonResponse.html_url 73 | $Revision = $JsonResponse.history[0].version 74 | Write-Output "$GistURL/$Revision" 75 | } 76 | -------------------------------------------------------------------------------- /AU/Plugins/Git.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 09-Nov-2016. 3 | 4 | # https://www.appveyor.com/docs/how-to/git-push/ 5 | 6 | param( 7 | $Info, 8 | 9 | # Git username 10 | [string] $User, 11 | 12 | # Git password. You can use Github Token here if you omit username. 13 | [string] $Password, 14 | 15 | # Force git commit when package is updated but not pushed. 16 | [switch] $Force, 17 | 18 | # Add the package to the repository if it was created during the update process. 19 | [switch] $AddNew, 20 | 21 | # Commit strategy: 22 | # single - 1 commit with all packages 23 | # atomic - 1 commit per package 24 | # atomictag - 1 commit and tag per package 25 | [ValidateSet('single', 'atomic', 'atomictag')] 26 | [string]$commitStrategy = 'single', 27 | 28 | # Branch name 29 | [string]$Branch = 'master' 30 | ) 31 | 32 | [array]$packages = if ($Force) { $Info.result.updated } else { $Info.result.pushed } 33 | if ($packages.Length -eq 0) { Write-Host "No package updated, skipping"; return } 34 | 35 | $root = Split-Path $packages[0].Path 36 | Push-Location $root 37 | $origin = git config --get remote.origin.url 38 | $origin -match '(?<=:/+)[^/]+' | Out-Null 39 | $machine = $Matches[0] 40 | 41 | if ($User -and $Password) { 42 | Write-Host "Setting credentials for: $machine" 43 | 44 | if ( "machine $server" -notmatch (Get-Content ~/_netrc)) { 45 | Write-Host "Credentials already found for machine: $machine" 46 | } 47 | "machine $machine", "login $User", "password $Password" | Out-File -Append ~/_netrc -Encoding ascii 48 | } elseif ($Password) { 49 | Write-Host "Setting oauth token for: $machine" 50 | git config --global credential.helper store 51 | Add-Content "$env:USERPROFILE\.git-credentials" "https://${Password}:x-oauth-basic@$machine`n" 52 | } 53 | 54 | Write-Host "Checking out & resetting $Branch branch" 55 | git checkout -q -B $Branch 56 | 57 | Write-Host "Executing git pull" 58 | git pull -q origin $Branch 59 | 60 | $gitAddArgs = @() 61 | if (-not $AddNew) 62 | { 63 | $gitAddArgs += @('--update') 64 | } 65 | 66 | if ($commitStrategy -like 'atomic*') { 67 | $packages | ForEach-Object { 68 | Write-Host "Adding update package to git repository: $($_.Name)" 69 | git add @gitAddArgs $_.Path 70 | git status 71 | 72 | Write-Host "Commiting $($_.Name)" 73 | $message = "AU: $($_.Name) upgraded from $($_.NuspecVersion) to $($_.RemoteVersion)" 74 | $gist_url = $Info.plugin_results.Gist -split '\n' | Select-Object -Last 1 75 | $snippet_url = $Info.plugin_results.Snippet -split '\n' | Select-Object -Last 1 76 | git commit -m "$message`n[skip ci] $gist_url $snippet_url" --allow-empty 77 | 78 | if ($commitStrategy -eq 'atomictag') { 79 | $tagcmd = "git tag -a $($_.Name)-$($_.RemoteVersion) -m '$($_.Name)-$($_.RemoteVersion)'" 80 | Invoke-Expression $tagcmd 81 | } 82 | } 83 | } 84 | else { 85 | Write-Host "Adding updated packages to git repository: $( $packages | % Name)" 86 | $packages | ForEach-Object { git add @gitAddArgs $_.Path } 87 | git status 88 | 89 | Write-Host "Commiting" 90 | $message = "AU: $($packages.Length) updated - $($packages | % Name)" 91 | $gist_url = $Info.plugin_results.Gist -split '\n' | Select-Object -Last 1 92 | $snippet_url = $Info.plugin_results.Snippet -split '\n' | Select-Object -Last 1 93 | git commit -m "$message`n[skip ci] $gist_url $snippet_url" --allow-empty 94 | 95 | } 96 | Write-Host "Pushing changes" 97 | git push -q origin $Branch 98 | if ($commitStrategy -eq 'atomictag') { 99 | write-host 'Atomic Tag Push' 100 | git push -q --tags 101 | } 102 | Pop-Location 103 | -------------------------------------------------------------------------------- /AU/Plugins/GitLab.ps1: -------------------------------------------------------------------------------- 1 | # Author: Josh Ameli 2 | # Based off of the Git plugin by Miodrag Milic 3 | # Last Change: 20-Aug-2019. 4 | 5 | # https://www.appveyor.com/docs/how-to/git-push/ 6 | 7 | param( 8 | $Info, 9 | 10 | # GitLab username 11 | [string] $User, 12 | 13 | # GitLab API key. 14 | [string] $API_Key, 15 | 16 | # Repository HTTP(S) URL 17 | [string]$PushURL, 18 | 19 | # Force git commit when package is updated but not pushed. 20 | [switch] $Force, 21 | 22 | # Commit strategy: 23 | # single - 1 commit with all packages 24 | # atomic - 1 commit per package 25 | # atomictag - 1 commit and tag per package 26 | [ValidateSet('single', 'atomic', 'atomictag')] 27 | [string]$commitStrategy = 'single', 28 | 29 | # Branch name 30 | [string]$Branch = 'master' 31 | ) 32 | 33 | [array]$packages = if ($Force) { $Info.result.updated } else { $Info.result.pushed } 34 | if ($packages.Length -eq 0) { Write-Host "No package updated, skipping"; return } 35 | 36 | $root = Split-Path $packages[0].Path 37 | 38 | Push-Location $root 39 | $origin = git config --get remote.origin.url 40 | $origin -match '(?<=:/+)[^/]+' | Out-Null 41 | $machine = $Matches[0] 42 | 43 | ### Construct RepoURL to be set as new origin 44 | $RepoURL = ( 45 | $PushURL.split('://')[0] ` 46 | + "://" ` 47 | + $User ` 48 | + ":" ` 49 | + $API_Key ` 50 | + "@" ` 51 | + $PushURL.TrimStart( 52 | $( 53 | $PushURL.split('://')[0] ` 54 | + "://" 55 | ) 56 | ) 57 | ) 58 | 59 | ### Set new push URL 60 | git remote set-url origin $RepoURL 61 | 62 | ### Ensure local is up-to-date to avoid conflicts 63 | Write-Host "Executing git pull" 64 | git checkout -q $Branch 65 | git pull -q origin $Branch 66 | 67 | ### Commit 68 | if ($commitStrategy -like 'atomic*') { 69 | $packages | ForEach-Object { 70 | Write-Host "Adding update package to git repository: $($_.Name)" 71 | git add -u $_.Path 72 | git status 73 | 74 | Write-Host "Commiting $($_.Name)" 75 | $message = "AU: $($_.Name) upgraded from $($_.NuspecVersion) to $($_.RemoteVersion)" 76 | $gist_url = $Info.plugin_results.Gist -split '\n' | Select-Object -Last 1 77 | $snippet_url = $Info.plugin_results.Snippet -split '\n' | Select-Object -Last 1 78 | git commit -m "$message`n[skip ci] $gist_url $snippet_url" --allow-empty 79 | 80 | if ($commitStrategy -eq 'atomictag') { 81 | $tagcmd = "git tag -a $($_.Name)-$($_.RemoteVersion) -m '$($_.Name)-$($_.RemoteVersion)'" 82 | Invoke-Expression $tagcmd 83 | } 84 | } 85 | } 86 | else { 87 | Write-Host "Adding updated packages to git repository: $( $packages | % Name)" 88 | $packages | ForEach-Object { git add -u $_.Path } 89 | git status 90 | 91 | Write-Host "Commiting" 92 | $message = "AU: $($packages.Length) updated - $($packages | % Name)" 93 | $gist_url = $Info.plugin_results.Gist -split '\n' | Select-Object -Last 1 94 | $snippet_url = $Info.plugin_results.Snippet -split '\n' | Select-Object -Last 1 95 | git commit -m "$message`n[skip ci] $gist_url $snippet_url" --allow-empty 96 | 97 | } 98 | 99 | ### Push 100 | Write-Host "Pushing changes" 101 | git push -q 102 | if ($commitStrategy -eq 'atomictag') { 103 | write-host 'Atomic Tag Push' 104 | git push -q --tags 105 | } 106 | Pop-Location 107 | 108 | git remote set-url origin $origin 109 | -------------------------------------------------------------------------------- /AU/Plugins/GitReleases.ps1: -------------------------------------------------------------------------------- 1 | # Author: Kim Nordmo 2 | # Last Change: 29-Oct-2017. 3 | 4 | <# 5 | .SYNOPSIS 6 | Creates Github release for updated packages 7 | #> 8 | param( 9 | $Info, 10 | 11 | # Github API token to use when creating/checking releases and uploading artifacts 12 | [string]$ApiToken, 13 | 14 | # What kind of release should be created, either 1 release per date, or 1 release per package and version is supported. 15 | [ValidateSet('date', 'package')] 16 | [string]$releaseType, 17 | 18 | # The text that should be used in the header of the release. 19 | [string]$releaseHeader = $null, 20 | 21 | # The text that should be used in the description of the release. 22 | [string]$releaseDescription = $null, 23 | 24 | # The formatting to use when replacing in release header/description and on date based releases. 25 | [string]$dateFormat = '{0:yyyy-MM-dd}', 26 | 27 | # Force creating a release when a package have been updated and not just been pushed. 28 | [switch]$Force, 29 | 30 | # The name of the branch, to create the release at 31 | [string]$Branch = 'master' 32 | ) 33 | 34 | function GetOrCreateRelease() { 35 | param( 36 | [string]$tagName, 37 | [string]$releaseName, 38 | [string]$releaseDescription, 39 | [string]$repository, 40 | $headers) 41 | 42 | try { 43 | Write-Verbose "Checking for a release using the tag: $tagName..." 44 | $response = Invoke-RestMethod -UseBasicParsing -Uri "https://api.github.com/repos/$repository/releases/tags/$tagName" -Headers $headers | Where-Object tag_name -eq $tagName 45 | if ($response) { 46 | return $response 47 | } 48 | } 49 | catch { 50 | } 51 | 52 | $json = @{ 53 | "tag_name" = $tagName 54 | "target_commitish" = $Branch 55 | "name" = $releaseName 56 | "body" = $releaseDescription 57 | "draft" = $false 58 | "prerelease" = $false 59 | } | ConvertTo-Json -Compress 60 | 61 | Write-Host "Creating the new release $tagName..." 62 | return Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://api.github.com/repos/$repository/releases" -Body $json -Headers $headers 63 | } 64 | 65 | [array]$packages = if ($Force) { $Info.result.updated } else { $Info.result.pushed } 66 | 67 | if ($packages.Length -eq 0) { Write-Host "No package updated, skipping"; return } 68 | 69 | $packagesToRelease = New-Object 'System.Collections.Generic.List[hashtable]' 70 | 71 | $packages | ForEach-Object { 72 | if ($_.Streams) { 73 | $_.Streams.Values | Where-Object { $_.Updated } | ForEach-Object { 74 | $packagesToRelease.Add(@{ 75 | Name = $_.Name 76 | NuspecVersion = $_.NuspecVersion 77 | RemoteVersion = $_.RemoteVersion 78 | NuFile = Resolve-Path ("$($_.Path)/$($_.Name).$($_.RemoteVersion).nupkg") 79 | }) 80 | } 81 | } 82 | else { 83 | $packagesToRelease.Add(@{ 84 | Name = $_.Name 85 | NuspecVersion = $_.NuspecVersion 86 | RemoteVersion = $_.RemoteVersion 87 | NuFile = Resolve-Path ("$($_.Path)/$($_.Name).$($_.RemoteVersion).nupkg") 88 | }) 89 | } 90 | } 91 | 92 | $origin = git config --get remote.origin.url 93 | 94 | if (!($origin -match "github.com\/([^\/]+\/[^\/\.]+)")) { 95 | Write-Warning "Unable to parse the repository information, skipping..." 96 | return; 97 | } 98 | $repository = $Matches[1] 99 | 100 | $headers = @{ 101 | Authorization = "token $ApiToken" 102 | } 103 | 104 | if ($releaseType -eq 'date' -and !$releaseHeader) { 105 | $releaseHeader = 'Packages updated on ' 106 | } 107 | elseif (!$releaseHeader) { 108 | $releaseHeader = ' ' 109 | } 110 | 111 | if ($releaseType -eq 'date' -and !$releaseDescription) { 112 | $releaseDescription = 'We had packages that were updated on ' 113 | } 114 | elseif (!$releaseDescription) { 115 | $releaseDescription = ' was updated from version to ' 116 | } 117 | 118 | $date = Get-Date -UFormat $dateFormat 119 | 120 | if ($releaseType -eq 'date') { 121 | $release = GetOrCreateRelease ` 122 | -tagName $date ` 123 | -releaseName ($releaseHeader -replace '', $date) ` 124 | -releaseDescription ($releaseDescription -replace '', $date) ` 125 | -repository $repository ` 126 | -headers $headers 127 | 128 | if (!$release) { 129 | Write-Error "Unable to create a new release, please check your permissions..." 130 | return 131 | } 132 | } 133 | 134 | $uploadHeaders = $headers.Clone() 135 | $uploadHeaders['Content-Type'] = 'application/zip' 136 | 137 | $packagesToRelease | ForEach-Object { 138 | # Because we grab all streams previously, we need to ignore 139 | # cases when a stream haven't been updated (no nupkg file created) 140 | if (!$_.NuFile) { return } 141 | 142 | if ($releaseType -eq 'package') { 143 | $releaseName = $releaseHeader -replace '', $_.Name -replace '', $_.RemoteVersion -replace '', $_.NuspecVersion -replace '', $date 144 | $packageDesc = $releaseDescription -replace '', $_.Name -replace '', $_.RemoteVersion -replace '', $_.NuspecVersion -replace '', $date 145 | 146 | $release = GetOrCreateRelease ` 147 | -tagName "$($_.Name)-$($_.RemoteVersion)" ` 148 | -releaseName $releaseName ` 149 | -releaseDescription $packageDesc ` 150 | -repository $repository ` 151 | -headers $headers 152 | } 153 | 154 | $fileName = [System.IO.Path]::GetFileName($_.NuFile) 155 | 156 | $existing = $release.assets | Where-Object name -eq $fileName 157 | if ($existing) { 158 | Write-Verbose "Removing existing $fileName asset..." 159 | Invoke-RestMethod -UseBasicParsing -Uri $existing.url -method Delete -Headers $headers | Out-Null 160 | } 161 | 162 | $uploadUrl = $release.upload_url -replace '\{.*\}$', '' 163 | $rawContent = [System.IO.File]::ReadAllBytes($_.NuFile) 164 | Write-Host "Uploading $fileName asset..." 165 | Invoke-RestMethod -UseBasicParsing -Uri "${uploadUrl}?name=${fileName}&label=$($_.Name) v$($_.RemoteVersion)" -Body $rawContent -Headers $uploadHeaders -Method Post | Out-Null 166 | } 167 | -------------------------------------------------------------------------------- /AU/Plugins/Gitter.ps1: -------------------------------------------------------------------------------- 1 | # Author: Kim Nordmo 2 | # Last Change: 2018-06-13 3 | <# 4 | .SYNOPSIS 5 | Publishes the package update status to gitter. 6 | 7 | .PARAMETER WebHookUrl 8 | This is the cusotm webhook url created through gitter integrations. 9 | 10 | .PARAMETER MessageFormat 11 | The format of the message that is meant to be published on gitter. 12 | {0} = The total number of automated packages. 13 | {1} = The number of updated packages, 14 | {2} = The number of published packages. 15 | {3} = The number of failed packages. 16 | {4} = The url to the github gist. 17 | #> 18 | param( 19 | $Info, 20 | [string]$WebHookUrl, 21 | [string]$MessageFormat = "[Update Status:{0} packages.`n {1} updated, {2} Published, {3} Failed]({4})" 22 | ) 23 | 24 | if (!$WebHookUrl) { return } # If we don't have a webhookurl we can't push status messages, so ignore. 25 | 26 | $updatedPackages = @($Info.result.updated).Count 27 | $publishedPackages = @($Info.result.pushed).Count 28 | $failedPackages = $Info.error_count.total 29 | $gistUrl = $Info.plugin_results.Gist -split '\n' | Select-Object -Last 1 30 | $packageCount = $Info.result.all.Length 31 | 32 | $gitterMessage = ($MessageFormat -f $packageCount, $updatedPackages, $publishedPackages, $failedPackages, $gistUrl) 33 | 34 | $arguments = @{ 35 | Body = if ($failedPackages -gt 0) { "message=$gitterMessage&level=error" } else { "message=$gitterMessage" } 36 | UseBasicParsing = $true 37 | Uri = $WebHookUrl 38 | ContentType = 'application/x-www-form-urlencoded' 39 | Method = 'Post' 40 | } 41 | 42 | "Submitting message to gitter" 43 | Invoke-RestMethod @arguments 44 | "Message submitted to gitter" -------------------------------------------------------------------------------- /AU/Plugins/History.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 09-Dec-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Create update history as markdown report 7 | 8 | .DESCRIPTION 9 | Shows one date per line and all of the packages pushed to the Chocolatey community 10 | repository during that day. First letter of the package name links to report 11 | (produced by the Report plugin), the rest links to the actuall commit (produced by the Git plugin). 12 | #> 13 | param( 14 | $Info, 15 | 16 | #Number of dates to show in the report 17 | $Lines=30, 18 | 19 | #Github user repository, used to create commit links 20 | $Github_UserRepo = 'chocolatey/chocolatey-coreteampackages', 21 | 22 | #Package Source Repository Root URL. Use for non-github source repositories. (If specified used to create commit links instead of Github_UserRepo.) 23 | $PackageSourceRootUrl, 24 | 25 | #File path where to save the markdown report 26 | $Path = "Update-History.md" 27 | ) 28 | 29 | if (!$PackageSourceRootUrl) {$PackageSourceRootUrl = "https://github.com/$Github_UserRepo"} 30 | 31 | Write-Host "Saving history to $Path" 32 | 33 | $res=[System.Collections.Specialized.OrderedDictionary]@{} 34 | $log = git --no-pager log -q --grep '^AU: ' --date iso --no-merges --all | Out-String 35 | $all_commits = $log | Select-String 'commit(.|\n)+?(?=\ncommit )' -AllMatches 36 | foreach ($commit in $all_commits.Matches.Value) { 37 | $commit = $commit -split '\n' 38 | 39 | $id = $commit[0].Replace('commit','').Trim().Substring(0,7) 40 | $date = $commit[2].Replace('Date:','').Trim() 41 | $date = ([datetime]$date).Date.ToString("yyyy-MM-dd") 42 | $report = $commit[5].Replace('[skip ci]','').Trim() 43 | [array] $packages = ($commit[4] -replace '^\s+AU:.+?(-|:) |\[skip ci\]').Trim().ToLower() 44 | 45 | $packages_md = $packages -split ' ' | ForEach-Object { 46 | $first = $_.Substring(0,1).ToUpper(); $rest = $_.Substring(1) 47 | if ($report) { 48 | "[$first]($report)[$rest]($PackageSourceRootUrl/commit/$id)" 49 | } else { 50 | "[$_]($PackageSourceRootUrl/commit/$id)" 51 | } 52 | } 53 | 54 | if (!$res.Contains($date)) { $res.$date=@() } 55 | $res.$date += $packages_md 56 | } 57 | 58 | $res = $res.Keys | Select-Object -First $Lines | ForEach-Object { $r=[System.Collections.Specialized.OrderedDictionary]@{} } { $r[$_] = $res[$_] } {$r} 59 | 60 | $history = @" 61 | # Update History 62 | 63 | Showing maximum $Lines dates. 64 | Click on the first letter of the package name to see its report and on the remaining letters to see its git commit. 65 | 66 | --- 67 | 68 | "@ 69 | foreach ($kv in $res.GetEnumerator()) { $history += "`n{0} ({2}) {1}`n" -f "**$($kv.Key)**", "$($kv.Value -join ' – ')", $kv.Value.Length } 70 | $history | Out-File $Path 71 | -------------------------------------------------------------------------------- /AU/Plugins/Mail.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 12-Nov-2016. 3 | 4 | param( 5 | $Info, 6 | [string] $To, 7 | [string] $From, 8 | [string] $Server, 9 | [string] $UserName, 10 | [string] $Password, 11 | [int] $Port, 12 | [string[]] $Attachment, 13 | [switch] $EnableSsl, 14 | [string] $UserMessage, 15 | # Do not send only on errors 16 | [switch] $SendAlways 17 | ) 18 | 19 | if (($Info.error_count.total -eq 0) -and !$SendAlways) { 20 | Write-Host 'Mail not sent as there are no errors (override with SendAlways param)' 21 | return 22 | } 23 | 24 | $errors_word = if ($Info.error_count.total -eq 1) { 'error' } else { 'errors' } 25 | 26 | # Create mail message 27 | 28 | if (!$From) { $From = "Update-AUPackages@{0}.{1}" -f $Env:UserName, $Env:ComputerName } 29 | 30 | $msg = New-Object System.Net.Mail.MailMessage $from, $To 31 | $msg.IsBodyHTML = $true 32 | 33 | if ($Info.error_count.total -eq 0) { 34 | $msg.Subject = "AU: run was OK" 35 | $msg.Body = $Info.stats | Out-String 36 | } 37 | else { 38 | $context = "with errors " 39 | $msg.Subject = "AU: $($info.error_count.total) $errors_word during update" 40 | $msg.Body = @" 41 |
42 | $($Info.error_count.total) $errors_word during update.
43 | $UserMessage
44 | $($info.error_info | Out-String)
45 | 
46 | "@ 47 | } 48 | 49 | $Attachment | ForEach-Object { if ($_) { $msg.Attachments.Add($_)} } 50 | 51 | # Send mail message 52 | $smtp = new-object Net.Mail.SmtpClient($Server) 53 | if ($UserName) { $smtp.Credentials = new-object System.Net.NetworkCredential($UserName, $Password) } 54 | if ($Port) { $smtp.Port = $Port } 55 | $smtp.EnableSsl = $EnableSsl 56 | $smtp.Send($msg) 57 | 58 | Write-Host "Mail ${context}sent to $To" 59 | -------------------------------------------------------------------------------- /AU/Plugins/PullRequest.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Creates a GitHub pull request for the updated packages. 4 | 5 | .DESCRIPTION 6 | This plugin will open a GitHub pull request for the commits created by the Git plugin, which of course needs to run first. 7 | It has options to add assignees and reviewers to the created pull request and, supports on-prem GitHub Enterprise Server. 8 | #> 9 | param( 10 | $Info, 11 | 12 | # Git branch to create a GitHub pull request for 13 | [string]$Branch, 14 | 15 | # Target Git branch for the GitHub pull request 16 | [string]$BaseBranch = "master", 17 | 18 | # GitHub usernames to be added to the list of assignees 19 | [string[]]$Assignees = @(), 20 | 21 | # GitHub usernames to be added to the list of reviewers 22 | [string[]]$Reviewers = @(), 23 | 24 | # GitHub team slugs to be added to the list of reviewers 25 | [string[]]$TeamReviewers = @(), 26 | 27 | # GitHub API base URL, overridable for GitHub Enterprise installations 28 | [string]$GitHubAPI = "https://api.github.com", 29 | 30 | # Github ApiKey, create in GitHub profile -> Settings -> Personal access tokens -> Generate new token 31 | [string]$ApiKey 32 | ) 33 | 34 | if ($Info.result.updated.Length -eq 0) { Write-Host "No package updated, skipping"; return } 35 | 36 | $ErrorActionPreference = "Stop" 37 | 38 | $origin = git config --get remote.origin.url 39 | $originParts = $origin -split {$_ -eq "/" -or $_ -eq ":"} 40 | $owner = $originParts[-2] 41 | $repo = $originParts[-1] -replace "\.git$", "" 42 | 43 | #https://github.com/majkinetor/au/issues/142 44 | if ($PSVersionTable.PSVersion.major -ge 6) { 45 | $AvailableTls = [enum]::GetValues('Net.SecurityProtocolType') | Where-Object { $_ -ge 'Tls' } # PowerShell 6+ does not support SSL3, so use TLS minimum 46 | $AvailableTls.ForEach({[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor $_}) 47 | } else { 48 | [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor [System.Net.SecurityProtocolType]::Tls -bor [System.Net.SecurityProtocolType]::Ssl3 49 | } 50 | 51 | $data = @{ 52 | title = git log -1 --pretty="%s" 53 | head = $Branch 54 | base = $BaseBranch 55 | } 56 | $params = @{ 57 | ContentType = 'application/json' 58 | Method = "POST" 59 | Uri = "$GitHubAPI/repos/$owner/$repo/pulls" 60 | Body = $data | ConvertTo-Json 61 | UseBasicparsing = $true 62 | Headers = @{ 63 | 'Accept' = 'application/vnd.github.v3+json' 64 | 'Authorization' = ('Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($ApiKey))) 65 | } 66 | } 67 | $response = Invoke-WebRequest @params 68 | $jsonResponse = $response.Content | ConvertFrom-Json 69 | $prUrl = $jsonResponse.html_url 70 | $prNumber = $jsonResponse.number 71 | 72 | if ($Assignees) { 73 | $data = @{ 74 | assignees = $Assignees 75 | } 76 | $params['Uri'] = "$GitHubAPI/repos/$owner/$repo/issues/$prNumber/assignees" 77 | $params['Body'] = $data | ConvertTo-Json 78 | $response = Invoke-WebRequest @params 79 | } 80 | 81 | if ($Reviewers -or $TeamReviewers) { 82 | $data = @{ 83 | reviewers = $Reviewers 84 | team_reviewers = $TeamReviewers 85 | } 86 | $params['Uri'] = "$GitHubAPI/repos/$owner/$repo/pulls/$prNumber/requested_reviewers" 87 | $params['Body'] = $data | ConvertTo-Json 88 | $response = Invoke-WebRequest @params 89 | } 90 | 91 | Write-Host "Pull request sucessfully created: $prUrl" 92 | -------------------------------------------------------------------------------- /AU/Plugins/Report.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 10-Nov-2016. 3 | <# 4 | .SYNOPSIS 5 | Create different types of reports about the current run. 6 | 7 | .DESCRIPTION 8 | The plugin saves state of all packages in a file that can be used locally or 9 | uploaded via other plugins to remote (such as Gist or Mail). 10 | #> 11 | 12 | param( 13 | $Info, 14 | 15 | # Type of the report, currently 'markdown' or 'text' 16 | [string] $Type = 'markdown', 17 | 18 | # Path where to save the report 19 | [string] $Path = 'Update-AUPackages.md', 20 | 21 | # Report parameters 22 | [HashTable] $Params 23 | ) 24 | 25 | Write-Host "Saving $Type report: $Path" 26 | 27 | $Type = ([System.IO.Path]::Combine($PSScriptRoot, 'Report', "$Type.ps1")) 28 | if (!(Test-Path $Type )) { throw "Report type not found: '$Type" } 29 | 30 | $result = & $Type 31 | $result | Out-File $Path 32 | -------------------------------------------------------------------------------- /AU/Plugins/Report/markdown.ps1: -------------------------------------------------------------------------------- 1 | . (Join-Path $PSScriptRoot 'markdown_funcs.ps1') 2 | 3 | $Github_UserRepo = $Params.Github_UserRepo 4 | $UserMessage = $Params.UserMessage 5 | $NoAppVeyor = $Params.NoAppVeyor 6 | $IconSize = if ($Params.IconSize) { $Params.IconSize } else { 32 } 7 | $NoIcons = $Params.NoIcons 8 | $Title = if ($Params.Title) { $Params.Title } else { 'Update-AUPackages' } 9 | 10 | # Optional Parameters for non github.com source code repositories 11 | # If PackageSourceRoot is specified, links will be drived from it instead of $Github_UserRepo 12 | $PackageSourceRootUrl = if ($Params.PackageSourceRootUrl) { $Params.PackageSourceRootUrl } elseif ($Params.Github_UserRepo) { "https://github.com/$Github_UserRepo" } else { 'https://github.com/majkinetor/au-packages-template' } 13 | $PackageSourceBranch = if ($Params.PackageSourceBranch) { $Params.PackageSourceBranch } else { 'master' } 14 | 15 | #======================================================================================= 16 | 17 | $now = $Info.startTime.ToUniversalTime().ToString('yyyy-MM-dd HH:mm') 18 | $au_version = Get-Module au -ListAvailable | ForEach-Object Version | Select-Object -First 1 | ForEach-Object { "$_" } 19 | $package_no = $Info.result.all.Length 20 | 21 | $update_all_url = "$PackageSourceRootUrl/blob/$PackageSourceBranch/update_all.ps1" 22 | 23 | $icon_ok = 'https://cdn.jsdelivr.net/gh/majkinetor/au@master/AU/Plugins/Report/r_ok.png' 24 | $icon_er = 'https://cdn.jsdelivr.net/gh/majkinetor/au@master/AU/Plugins/Report/r_er.png' 25 | 26 | "# $Title" 27 | 28 | #=== Header =============================== 29 | if (!$NoAppVeyor -and $Github_UserRepo) { "[![](https://ci.appveyor.com/api/projects/status/github/${Github_UserRepo}?svg=true)](https://ci.appveyor.com/project/$Github_UserRepo/build/$Env:APPVEYOR_BUILD_NUMBER)" } 30 | 31 | @" 32 | [![$package_no](https://img.shields.io/badge/AU%20packages-$($package_no)-red.svg)](#ok) 33 | [![$au_version](https://img.shields.io/badge/AU-$($au_version)-blue.svg)](https://www.powershellgallery.com/packages/AU) 34 | [![](http://transparent-favicon.info/favicon.ico)](#)[![](http://transparent-favicon.info/favicon.ico)](#) 35 | **UTC**: $now [![](http://transparent-favicon.info/favicon.ico)](#) [$Github_UserRepo]($PackageSourceRootUrl) 36 | 37 | _This file is automatically generated by the [update_all.ps1]($update_all_url) script using the [AU module](https://github.com/majkinetor/au)._ 38 | "@ 39 | 40 | "`n$UserMessage`n" 41 | 42 | #=== Body =============================== 43 | 44 | $errors_word = if ($Info.error_count.total -eq 1) {'error'} else {'errors' } 45 | if ($Info.error_count.total) { 46 | " **LAST RUN HAD $($Info.error_count.total) [$($errors_word.ToUpper())](#errors) !!!**" } 47 | else { 48 | " **Last run was OK**" 49 | } 50 | 51 | "" 52 | md_fix_newline $Info.stats 53 | 54 | $columns = 'Icon', 'Name', 'Updated', 'Pushed', 'RemoteVersion', 'NuspecVersion' 55 | if ($NoIcons) { $columns = $columns[1.10] } 56 | if ($Info.pushed) { 57 | md_title Pushed 58 | md_table $Info.result.pushed -Columns $columns 59 | } 60 | 61 | if ($Info.error_count.total) { 62 | md_title Errors 63 | md_table $Info.result.errors -Columns ($columns + 'Error' | Where-Object { ('Updated', 'Pushed') -notcontains $_ } ) 64 | $Info.result.errors | ForEach-Object { 65 | md_title $_.Name -Level 3 66 | md_code "$($_.Error)" 67 | } 68 | } 69 | 70 | if ($Info.result.ignored) { 71 | md_title Ignored 72 | md_table $Info.result.ignored -Columns 'Icon', 'Name', 'NuspecVersion', 'IgnoreMessage' 73 | } 74 | 75 | if ($Info.result.ok) { 76 | md_title OK 77 | md_table $Info.result.ok -Columns $columns 78 | $Info.result.ok | ForEach-Object { 79 | md_title $_.Name -Level 3 80 | md_code $_.Result 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /AU/Plugins/Report/markdown_funcs.ps1: -------------------------------------------------------------------------------- 1 | function md_fix_newline($Text) { 2 | $Text -replace "\.`n", "\.`n " | Out-String 3 | } 4 | 5 | function md_title($Title, $Level=2 ) { 6 | "" 7 | "#"*$Level + ' ' + $Title | Out-String 8 | "" 9 | } 10 | 11 | function md_code($Text) { 12 | "`n" + '```' 13 | ($Text -join "`n").Trim() 14 | '```' + "`n" 15 | } 16 | 17 | function md_table($result, $Columns, $MaxErrorLength=150) { 18 | if (!$Columns) { $Columns = 'Name', 'Updated', 'Pushed', 'RemoteVersion', 'NuspecVersion', 'Error' } 19 | $res = '|' + ($Columns -join '|') + "|`r`n" 20 | $res += ((1..$Columns.Length | ForEach-Object { '|---' }) -join '') + "|`r`n" 21 | if ( !$au_GalleryPackageRootUrl ) { 22 | $au_GalleryPackageRootUrl = if ($env:au_GalleryPackageRootUrl) { $env:au_GalleryPackageRootUrl } else { 23 | if ($au_GalleryUrl) { "$au_GalleryUrl/packages" } else { 'https://chocolatey.org/packages' } 24 | } 25 | } 26 | 27 | $result | ForEach-Object { 28 | $o = $_ | Select-Object ` 29 | @{ N='Icon' 30 | E={'' -f $_.NuspecXml.package.metadata.iconUrl, $IconSize } 31 | }, 32 | @{ N='Name' 33 | E={"[{0}]($au_GalleryPackageRootUrl/{0}/{1})" -f $_.Name, $(if ($_.Updated) { $_.RemoteVersion } else {$_.NuspecVersion }) } 34 | }, 35 | @{ N='Updated' 36 | E={ 37 | $r = "[{0}](#{1})" -f $_.Updated, $_.Name.Replace('.','').ToLower() 38 | $r += if ($_.Updated) { ' 🔸' } 39 | $r += if ($_.Streams) { ' 🕄' } 40 | $r += if (Get-ChildItem $_.Path -Recurse -Include VERIFICATION.txt) { ' 📥' } 41 | $r 42 | } 43 | }, 44 | 'Pushed', 45 | @{ N='RemoteVersion' 46 | E={"[{0}]({1})" -f $_.RemoteVersion, $_.NuspecXml.package.metadata.projectUrl } 47 | }, 48 | @{ N='NuspecVersion' 49 | E={"[{0}]({1})" -f $_.NuspecVersion, $_.NuspecXml.package.metadata.packageSourceUrl } 50 | }, 51 | @{ N='Error' 52 | E={ 53 | $err = ("$($_.Error)" -replace "`r?`n", '; ').Trim() 54 | if ($err) { 55 | if ($err.Length -gt $MaxErrorLength) { $err = $err.Substring(0,$MaxErrorLength) + ' ...' } 56 | "[{0}](#{1})" -f $err, $_.Name.ToLower() 57 | } 58 | } 59 | }, 60 | 'Ignored', 61 | 'IgnoreMessage' 62 | 63 | $res += ((1..$Columns.Length | ForEach-Object { $col = $Columns[$_-1]; '|' + $o.$col }) -join '') + "|`r`n" 64 | } 65 | 66 | $res 67 | } 68 | -------------------------------------------------------------------------------- /AU/Plugins/Report/r_er.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/au/7f9c84e1e15995595dae68b4e8d8a71f50417752/AU/Plugins/Report/r_er.png -------------------------------------------------------------------------------- /AU/Plugins/Report/r_ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majkinetor/au/7f9c84e1e15995595dae68b4e8d8a71f50417752/AU/Plugins/Report/r_ok.png -------------------------------------------------------------------------------- /AU/Plugins/Report/text.ps1: -------------------------------------------------------------------------------- 1 | $UserMessage = $Params.UserMessage 2 | $Title = if ($Params.Title) { $Params.Title } else { 'Update-AUPackages' } 3 | 4 | #============================================================================== 5 | 6 | function title($txt) { "`r`n{0}`r`n{1}`r`n" -f $txt,('-'*$txt.Length) } 7 | function indent($txt, $level=4) { $txt -split "`n" | ForEach-Object { ' '*$level + $_ } } 8 | 9 | $now = $Info.startTime.ToUniversalTime().ToString('yyyy-MM-dd HH:mm') 10 | $au_version = Get-Module au -ListAvailable | ForEach-Object Version | Select-Object -First 1 | ForEach-Object { "$_" } 11 | $package_no = $Info.result.all.Length 12 | 13 | "{0,-15}{1}" -f 'Title:', $Title 14 | "{0,-15}{1}" -f 'Time:', $now 15 | "{0,-15}{1}" -f 'AU version:', $au_version 16 | "{0,-15}{1}" -f 'AU packages:', $package_no 17 | 18 | $errors_word = if ($Info.error_count.total -eq 1) {'error'} else {'errors' } 19 | if ($Info.error_count.total) { 20 | "LAST RUN HAD $($Info.error_count.total) $errors_word !!!" } 21 | else { 22 | "Last run was OK" 23 | } 24 | 25 | ""; $Info.stats 26 | 27 | ""; $UserMessage; "" 28 | 29 | if ($Info.pushed) { 30 | title Pushed 31 | $Info.result.pushed | Select-Object 'Name', 'Updated', 'Pushed', 'RemoteVersion', 'NuspecVersion' | Format-Table | Out-String | Set-Variable r 32 | indent $r 2 33 | 34 | $Info.result.pushed | ForEach-Object { $_.Name; indent $_.Result; "" } 35 | } 36 | 37 | if ($Info.error_count.total) { 38 | title Errors 39 | $Info.result.errors | Select-Object 'Name', 'NuspecVersion', 'Error' | Format-Table | Out-String | Set-Variable r 40 | indent $r 2 41 | 42 | $Info.result.errors | ForEach-Object { $_.Name; indent $_.Error; "" } 43 | } 44 | 45 | 46 | if ($Info.result.ignored) { 47 | title Ignored 48 | $Info.result.ignored | Format-Table | Select-Object 'Name', 'NuspecVersion', 'IgnoreMessage' | Format-Table | Out-String | Set-Variable r 49 | indent $r 2 50 | } 51 | 52 | $ok = $Info.result.ok | Where-Object { !$_.Pushed } 53 | if ($ok) { 54 | title OK 55 | $ok | Select-Object 'Name', 'Updated', 'RemoteVersion', 'NuspecVersion' | Format-Table | Out-String | Set-Variable r 56 | indent $r 2 57 | 58 | $ok | ForEach-Object { $_.Name; indent $_.Result; "" } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /AU/Plugins/RunInfo.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 21-Sep-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Save run info to the file and exclude sensitive information. 7 | 8 | .DESCRIPTION 9 | Run this plugin as the last one to save all other info produced during the run. 10 | To load it for inspection use `$info = Import-CliXml update_info.xml`. 11 | #> 12 | param( 13 | $Info, 14 | 15 | #Path to XML file to save 16 | [string] $Path = 'update_info.xml', 17 | 18 | #Match options with those words to erase 19 | [string[]] $Exclude = @('password', 'apikey') 20 | ) 21 | 22 | function deep_clone { 23 | param($DeepCopyObject) 24 | 25 | $memStream = new-object IO.MemoryStream 26 | $formatter = new-object Runtime.Serialization.Formatters.Binary.BinaryFormatter 27 | $formatter.Serialize($memStream,$DeepCopyObject) 28 | $memStream.Position=0 29 | $formatter.Deserialize($memStream) 30 | } 31 | 32 | # Runinfo must save its own run results directly in Info 33 | function result($msg) { $Info.plugin_results.RunInfo += $msg; Write-Host $msg } 34 | 35 | $Info.plugin_results.RunInfo = @() 36 | $format = '{0,-15}{1}' 37 | 38 | $orig_opts = $Info.Options 39 | $opts = deep_clone $orig_opts 40 | $excluded = '' 41 | foreach ($w in $Exclude) { 42 | foreach ($key in $Info.Options.Keys) { 43 | if ($Info.Options.$key -is [HashTable]) { 44 | foreach ($subkey in $Info.Options.$key.Keys) { 45 | if ($subkey -like "*$w*") { 46 | $excluded += "$key.$subkey " 47 | $opts.$key.$subkey = '*****' 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | if ($excluded) { result ($format -f 'Excluded:', $excluded) } 55 | result ($format -f 'File:', $Path) 56 | $Info.Options = $opts 57 | $Info | Export-CliXML $Path 58 | $Info.Options = $orig_opts 59 | -------------------------------------------------------------------------------- /AU/Plugins/Snippet.ps1: -------------------------------------------------------------------------------- 1 | # Author: dimqua 2 | # Last Change: 11-Oct-2018. 3 | <# 4 | .SYNOPSIS 5 | Upload update history report to Gitlab snippet. 6 | 7 | .DESCRIPTION 8 | Plugin uploads update history report (created by Report plugin) to the snippet with the given id and filename. You can use gitlab.com instance (default) or self-hosted one. 9 | #> 10 | param( 11 | $Info, 12 | 13 | # Snippet id 14 | [string] $Id, 15 | 16 | # Gitlab API Token, create in User Settings -> Access Tokens -> Create personal access token 17 | # Make sure token has 'api' scope. 18 | [string] $ApiToken, 19 | 20 | # File paths to attach to snippet 21 | [string[]] $Path, 22 | 23 | # Snippet file name 24 | [string] $FileName = 'Update-AUPackages.md', 25 | 26 | # GitLab instance's (sub)domain name 27 | [string] $Domain = 'gitlab.com' 28 | 29 | ) 30 | 31 | # Create snippet 32 | Get-ChildItem $Path | ForEach-Object { 33 | $file_name = Split-Path $_ -Leaf 34 | $content = Get-Content $_ -Raw 35 | $snippet = '{"content": "' + $content + '"}' 36 | } 37 | 38 | $params = @{ 39 | ContentType = 'application/json' 40 | Method = "PUT" 41 | Uri = "https://$Domain/api/v4/snippets/$Id" 42 | Body = ($snippet | ConvertTo-Json).replace('"{\"content\": \"','{"content": "').replace('\"}"','"') + ', "file_name": "' + $FileName + '"}' 43 | Headers = @{ 'PRIVATE-TOKEN'=$ApiToken } 44 | } 45 | 46 | # Request 47 | $res = Invoke-WebRequest @params 48 | "https://$Domain/snippets/$Id" 49 | -------------------------------------------------------------------------------- /AU/Private/AUPackage.ps1: -------------------------------------------------------------------------------- 1 | class AUPackage { 2 | [string] $Path 3 | [string] $Name 4 | [bool] $Updated 5 | [bool] $Pushed 6 | [string] $RemoteVersion 7 | [string] $NuspecVersion 8 | [string[]] $Result 9 | [string] $Error 10 | [string] $NuspecPath 11 | [xml] $NuspecXml 12 | [bool] $Ignored 13 | [string] $IgnoreMessage 14 | [string] $StreamsPath 15 | [System.Collections.Specialized.OrderedDictionary] $Streams 16 | 17 | AUPackage([string] $Path ){ 18 | if ([String]::IsNullOrWhiteSpace( $Path )) { throw 'Package path can not be empty' } 19 | 20 | $this.Path = $Path 21 | $this.Name = Split-Path -Leaf $Path 22 | 23 | $this.NuspecPath = '{0}{2}{1}.nuspec' -f $this.Path, $this.Name, [IO.Path]::DirectorySeparatorChar 24 | if (!(Get-Item $this.NuspecPath -ea ignore)) { throw 'No nuspec file found in the package directory' } 25 | 26 | $this.NuspecXml = [AUPackage]::LoadNuspecFile( $this.NuspecPath ) 27 | $this.NuspecVersion = $this.NuspecXml.package.metadata.version 28 | 29 | $this.StreamsPath = '{0}{2}{1}.json' -f $this.Path, $this.Name, [IO.Path]::DirectorySeparatorChar 30 | $this.Streams = [AUPackage]::LoadStreams( $this.StreamsPath ) 31 | } 32 | 33 | [hashtable] GetStreamDetails() { 34 | return @{ 35 | Path = $this.Path 36 | Name = $this.Name 37 | Updated = $this.Updated 38 | RemoteVersion = $this.RemoteVersion 39 | } 40 | } 41 | 42 | static [xml] LoadNuspecFile( $NuspecPath ) { 43 | $nu = New-Object xml 44 | $nu.PSBase.PreserveWhitespace = $true 45 | $nu.Load($NuspecPath) 46 | return $nu 47 | } 48 | 49 | SaveNuspec(){ 50 | $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False) 51 | [System.IO.File]::WriteAllText($this.NuspecPath, $this.NuspecXml.InnerXml, $Utf8NoBomEncoding) 52 | } 53 | 54 | static [System.Collections.Specialized.OrderedDictionary] LoadStreams( $streamsPath ) { 55 | if (!(Test-Path $streamsPath)) { return $null } 56 | $res = [System.Collections.Specialized.OrderedDictionary] @{} 57 | $versions = Get-Content $streamsPath | ConvertFrom-Json 58 | $versions.psobject.Properties | ForEach-Object { 59 | $stream = $_.Name 60 | $res.Add($stream, @{ NuspecVersion = $versions.$stream }) 61 | } 62 | return $res 63 | } 64 | 65 | UpdateStream( $stream, $version ){ 66 | $s = $stream.ToString() 67 | $v = $version.ToString() 68 | if (!$this.Streams) { $this.Streams = [System.Collections.Specialized.OrderedDictionary] @{} } 69 | if (!$this.Streams.Contains($s)) { $this.Streams.$s = @{} } 70 | if ($this.Streams.$s -ne 'ignore') { $this.Streams.$s.NuspecVersion = $v } 71 | $versions = [System.Collections.Specialized.OrderedDictionary] @{} 72 | $this.Streams.Keys | ForEach-Object { 73 | $versions.Add($_, $this.Streams.$_.NuspecVersion) 74 | } 75 | $versions | ConvertTo-Json | Set-Content $this.StreamsPath -Encoding UTF8 76 | } 77 | 78 | Backup() { 79 | $d = ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'au', $this.Name)) 80 | 81 | Remove-Item (Join-Path $d '*') -Recurse -ea 0 82 | Copy-Item . (Join-Path $d '_backup') -Recurse 83 | } 84 | 85 | [string] SaveAndRestore() { 86 | $d = ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'au', $this.Name)) 87 | 88 | Copy-Item . (Join-Path $d '_output') -Recurse 89 | Remove-Item (Join-Path '.' '*') -Recurse 90 | Copy-Item ([System.IO.Path]::Combine($d, '_backup', '*')) . -Recurse 91 | 92 | return (Join-Path $d '_output') 93 | } 94 | 95 | AUPackage( [hashtable] $obj ) { 96 | if (!$obj) { throw 'Obj can not be empty' } 97 | $obj.Keys | Where-Object { $_ -ne 'Streams' } | ForEach-Object { 98 | $this.$_ = $obj.$_ 99 | } 100 | if ($obj.Streams) { 101 | $this.Streams = [System.Collections.Specialized.OrderedDictionary] @{} 102 | $obj.Streams.psobject.Properties | ForEach-Object { 103 | $this.Streams.Add($_.Name, $_.Value) 104 | } 105 | } 106 | } 107 | 108 | [hashtable] Serialize() { 109 | $res = @{} 110 | $this | Get-Member -Type Properties | Where-Object { $_.Name -ne 'Streams' } | ForEach-Object { 111 | $property = $_.Name 112 | $res.Add($property, $this.$property) 113 | } 114 | if ($this.Streams) { 115 | $res.Add('Streams', [PSCustomObject] $this.Streams) 116 | } 117 | return $res 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /AU/Private/AUVersion.ps1: -------------------------------------------------------------------------------- 1 | class AUVersion : System.IComparable { 2 | [version] $Version 3 | [string] $Prerelease 4 | [string] $BuildMetadata 5 | 6 | AUVersion([version] $version, [string] $prerelease, [string] $buildMetadata) { 7 | if (!$version) { throw 'Version cannot be null.' } 8 | $this.Version = $version 9 | $this.Prerelease = $prerelease 10 | $this.BuildMetadata = $buildMetadata 11 | } 12 | 13 | AUVersion($input) { 14 | if (!$input) { throw 'Input cannot be null.' } 15 | $v = [AUVersion]::Parse($input -as [string]) 16 | $this.Version = $v.Version 17 | $this.Prerelease = $v.Prerelease 18 | $this.BuildMetadata = $v.BuildMetadata 19 | } 20 | 21 | static [AUVersion] Parse([string] $input) { return [AUVersion]::Parse($input, $true) } 22 | 23 | static [AUVersion] Parse([string] $input, [bool] $strict) { 24 | if (!$input) { throw 'Version cannot be null.' } 25 | $reference = [ref] $null 26 | if (![AUVersion]::TryParse($input, $reference, $strict)) { throw "Invalid version: $input." } 27 | return $reference.Value 28 | } 29 | 30 | static [bool] TryParse([string] $input, [ref] $result) { return [AUVersion]::TryParse($input, $result, $true) } 31 | 32 | static [bool] TryParse([string] $input, [ref] $result, [bool] $strict) { 33 | $result.Value = [AUVersion] $null 34 | if (!$input) { return $false } 35 | $pattern = [AUVersion]::GetPattern($strict) 36 | if ($input -notmatch $pattern) { return $false } 37 | $reference = [ref] $null 38 | if (![version]::TryParse($Matches['version'], $reference)) { return $false } 39 | $pr = $Matches['prerelease'] 40 | $bm = $Matches['buildMetadata'] 41 | if ($pr -and !$strict) { $pr = $pr.Replace(' ', '.') } 42 | if ($bm -and !$strict) { $bm = $bm.Replace(' ', '.') } 43 | # for now, chocolatey does only support SemVer v1 (no dot separated identifiers in pre-release): 44 | if ($pr -and $strict -and $pr -like '*.*') { return $false } 45 | if ($bm -and $strict -and $bm -like '*.*') { return $false } 46 | if ($pr) { $pr = $pr.Replace('.', '') } 47 | if ($bm) { $bm = $bm.Replace('.', '') } 48 | # 49 | $result.Value = [AUVersion]::new($reference.Value, $pr, $bm) 50 | return $true 51 | } 52 | 53 | hidden static [string] GetPattern([bool] $strict) { 54 | $versionPattern = '(?\d+(?:\.\d+){1,3})' 55 | if ($strict) { 56 | $identifierPattern = "[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*" 57 | return "^$versionPattern(?:-(?$identifierPattern))?(?:\+(?$identifierPattern))?`$" 58 | } else { 59 | $identifierPattern = "[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+| \d+)*" 60 | return "$versionPattern(?:[- ]*(?$identifierPattern))?(?:[+ *](?$identifierPattern))?" 61 | } 62 | } 63 | 64 | [AUVersion] WithVersion([version] $version) { return [AUVersion]::new($version, $this.Prerelease, $this.BuildMetadata) } 65 | 66 | [int] CompareTo($obj) { 67 | if ($obj -eq $null) { return 1 } 68 | if ($obj -isnot [AUVersion]) { throw "AUVersion expected: $($obj.GetType())" } 69 | $t = $this.GetParts() 70 | $o = $obj.GetParts() 71 | for ($i = 0; $i -lt $t.Length -and $i -lt $o.Length; $i++) { 72 | if ($t[$i].GetType() -ne $o[$i].GetType()) { 73 | $t[$i] = [string] $t[$i] 74 | $o[$i] = [string] $o[$i] 75 | } 76 | if ($t[$i] -gt $o[$i]) { return 1 } 77 | if ($t[$i] -lt $o[$i]) { return -1 } 78 | } 79 | if ($t.Length -eq 1 -and $o.Length -gt 1) { return 1 } 80 | if ($o.Length -eq 1 -and $t.Length -gt 1) { return -1 } 81 | if ($t.Length -gt $o.Length) { return 1 } 82 | if ($t.Length -lt $o.Length) { return -1 } 83 | return 0 84 | } 85 | 86 | [bool] Equals($obj) { return $this.CompareTo($obj) -eq 0 } 87 | 88 | [int] GetHashCode() { return $this.GetParts().GetHashCode() } 89 | 90 | [string] ToString() { 91 | $result = $this.Version.ToString() 92 | if ($this.Prerelease) { $result += "-$($this.Prerelease)" } 93 | if ($this.BuildMetadata) { $result += "+$($this.BuildMetadata)" } 94 | return $result 95 | } 96 | 97 | [string] ToString([int] $fieldCount) { 98 | if ($fieldCount -eq -1) { return $this.Version.ToString() } 99 | return $this.Version.ToString($fieldCount) 100 | } 101 | 102 | hidden [object[]] GetParts() { 103 | $result = @($this.Version) 104 | if ($this.Prerelease) { 105 | $this.Prerelease -split '\.' | ForEach-Object { 106 | # if identifier is exclusively numeric, cast it to an int 107 | if ($_ -match '^[0-9]+$') { 108 | $result += [int] $_ 109 | } else { 110 | $result += $_ 111 | } 112 | } 113 | } 114 | return $result 115 | } 116 | } 117 | 118 | function ConvertTo-AUVersion($Version) { 119 | return [AUVersion] $Version 120 | } 121 | -------------------------------------------------------------------------------- /AU/Private/check_url.ps1: -------------------------------------------------------------------------------- 1 | # Returns nothing if url is valid, error otherwise 2 | function check_url( [string] $Url, [int]$Timeout, $ExcludeType='text/html', $Options ) { 3 | if (!(is_url $Url)) { return "URL syntax is invalid" } 4 | 5 | try 6 | { 7 | $response = request $url $Timeout -Options $Options 8 | if ($response.ContentType -like "*${ExcludeType}*") { return "Bad content type '$ExcludeType'" } 9 | } 10 | catch { 11 | return "Can't validate URL`n$_" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /AU/Private/is_url.ps1: -------------------------------------------------------------------------------- 1 | # Returns [bool] 2 | function is_url([string] $Url ) { 3 | [Uri]::IsWellFormedUriString($URL, [UriKind]::Absolute) 4 | } 5 | -------------------------------------------------------------------------------- /AU/Private/is_version.ps1: -------------------------------------------------------------------------------- 1 | # Returns [bool] 2 | function is_version( [string] $Version ) { 3 | return [AUVersion]::TryParse($Version, [ref]($__)) 4 | } 5 | -------------------------------------------------------------------------------- /AU/Private/request.ps1: -------------------------------------------------------------------------------- 1 | function request( [string]$Url, [int]$Timeout, $Options ) { 2 | if ([string]::IsNullOrWhiteSpace($url)) {throw 'The URL is empty'} 3 | $request = [System.Net.WebRequest]::Create($Url) 4 | if ($Timeout) { $request.Timeout = $Timeout*1000 } 5 | 6 | if ($Options.Headers) { 7 | $Options.Headers.Keys | ForEach-Object { 8 | if ([System.Net.WebHeaderCollection]::IsRestricted($_)) { 9 | $key = $_.Replace('-','') 10 | $request.$key = $Options.Headers[$_] 11 | } 12 | else { 13 | $request.Headers.add($_, $Options.Headers[$_]) 14 | } 15 | } 16 | } 17 | 18 | $response = $request.GetResponse() 19 | $response.Close() 20 | $response 21 | } 22 | -------------------------------------------------------------------------------- /AU/Public/Get-AUPackages.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 12-Nov-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get AU packages 7 | 8 | .DESCRIPTION 9 | 10 | Returns list of directories that have update.ps1 script in them and package name 11 | doesn't start with the '_' char (unpublished packages, not considered by Update-AUPackages 12 | function). 13 | 14 | Function looks in the directory pointed to by the global variable $au_root or, if not set, 15 | the current directory. 16 | 17 | .EXAMPLE 18 | gau p* 19 | 20 | Get all automatic packages that start with 'p' in the current directory. 21 | 22 | .EXAMPLE 23 | $au_root = 'c:\packages'; lsau 'cpu-z*','p*','copyq' 24 | 25 | Get all automatic packages in the directory 'c:\packages' that start with 'cpu-z' or 'p' and package which name is 'copyq'. 26 | #> 27 | function Get-AUPackages( [string[]] $Name ) { 28 | $root = $global:au_root 29 | if (!$root) { $root = $pwd } 30 | 31 | Get-ChildItem ([System.IO.Path]::Combine($root, '*', 'update.ps1')) | ForEach-Object { 32 | $packageDir = Get-Item (Split-Path $_) 33 | 34 | if ($Name -and $Name.Length -gt 0) { 35 | $m = $Name | Where-Object { $packageDir.Name -like $_ } 36 | if (!$m) { return } 37 | } 38 | 39 | if ($packageDir.Name -like '_*') { return } 40 | $packageDir 41 | } 42 | } 43 | 44 | Set-Alias gau Get-AuPackages 45 | Set-Alias lsau Get-AuPackages 46 | -------------------------------------------------------------------------------- /AU/Public/Get-RemoteChecksum.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 26-Nov-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Download file from internet and calculate its checksum 7 | 8 | #> 9 | function Get-RemoteChecksum( [string] $Url, $Algorithm='sha256', $Headers ) { 10 | $fn = [System.IO.Path]::GetTempFileName() 11 | Invoke-WebRequest $Url -OutFile $fn -UseBasicParsing -Headers $Headers 12 | $res = Get-FileHash $fn -Algorithm $Algorithm | ForEach-Object Hash 13 | Remove-Item $fn -ea ignore 14 | return $res.ToLower() 15 | } 16 | 17 | -------------------------------------------------------------------------------- /AU/Public/Get-RemoteFiles.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 22-Feb-2017. 3 | 4 | <# 5 | .SYNOPSIS 6 | Get Latest URL32 and/or URL64 into tools directory. 7 | 8 | .DESCRIPTION 9 | This function will download the binaries pointed to by $Latest.URL32 and $Latest.URL64. 10 | The function is used to embed binaries into the Chocolatey package. 11 | 12 | The function will keep original remote file name but it will add suffix _x32 or _x64. 13 | This is intentional because you can use those to match particular installer via wildcards, 14 | e.g. `gi *_x32.exe`. 15 | 16 | #> 17 | function Get-RemoteFiles { 18 | param ( 19 | # Delete existing file having $Latest.FileType extension. 20 | # Otherwise, when state of the package remains after the update, older installers 21 | # will pile up and may get included in the updated package. 22 | [switch] $Purge, 23 | 24 | # Do not add arch suffix (_x32 or _64) at the end of the filename 25 | [switch] $NoSuffix, 26 | 27 | # Override remote file name, use this one as a base. Suffixes _x32/_x64 are added. 28 | # Use this parameter if remote URL doesn't contain file name but generated hash. 29 | [string] $FileNameBase, 30 | 31 | # By default last URL part is used as a file name. Use this paramter to skip parts 32 | # if file name is specified earlier in the path. 33 | [int] $FileNameSkip=0, 34 | 35 | # Sets the algorithm to use when calculating checksums 36 | # This defaults to sha256 37 | [ValidateSet('md5','sha1','sha256','sha384','sha512')] 38 | [string] $Algorithm = 'sha256' 39 | ) 40 | 41 | function name4url($url) { 42 | if ($FileNameBase) { return $FileNameBase } 43 | $res = $url -split '/' | Select-Object -Last 1 -Skip $FileNameSkip 44 | $res -replace '\.[^.]+$' 45 | } 46 | 47 | function ext() { 48 | if ($Latest.FileType) { return $Latest.FileType } 49 | $url = $Latest.Url32; if (!$url) { $url = $Latest.Url64 } 50 | if ($url -match '(?<=\.)[^.]+$') { return $Matches[0] } 51 | } 52 | 53 | New-Item -Type Directory tools -ea 0 | Out-Null 54 | $toolsPath = Resolve-Path tools 55 | 56 | $ext = ext 57 | if (!$ext) { throw 'Unknown file type' } 58 | 59 | if ($Purge) { 60 | Write-Host 'Purging' $ext 61 | $purgePath = "$toolsPath{0}*.$ext" -f [IO.Path]::DirectorySeparatorChar 62 | Remove-Item -Force $purgePath -ea ignore 63 | } 64 | 65 | function headers($client) { 66 | if ($Latest.Options.Headers) { 67 | $Latest.Options.Headers.GetEnumerator() | ForEach-Object { $client.Headers.Add($_.Key, $_.Value) | Out-Null } 68 | } 69 | } 70 | 71 | try { 72 | $client = New-Object System.Net.WebClient 73 | 74 | if ($Latest.Url32) { 75 | headers($client) 76 | $base_name = name4url $Latest.Url32 77 | $file_name = "{0}{2}.{1}" -f $base_name, $ext, $(if ($NoSuffix) { '' } else {'_x32'}) 78 | $file_path = Join-Path $toolsPath $file_name 79 | 80 | Write-Host "Downloading to $file_name -" $Latest.Url32 81 | $client.DownloadFile($Latest.URL32, $file_path) 82 | $global:Latest.Checksum32 = Get-FileHash $file_path -Algorithm $Algorithm | ForEach-Object Hash 83 | $global:Latest.ChecksumType32 = $Algorithm 84 | $global:Latest.FileName32 = $file_name 85 | } 86 | 87 | if ($Latest.Url64) { 88 | headers($client) 89 | $base_name = name4url $Latest.Url64 90 | $file_name = "{0}{2}.{1}" -f $base_name, $ext, $(if ($NoSuffix) { '' } else {'_x64'}) 91 | $file_path = Join-Path $toolsPath $file_name 92 | 93 | Write-Host "Downloading to $file_name -" $Latest.Url64 94 | $client.DownloadFile($Latest.URL64, $file_path) 95 | $global:Latest.Checksum64 = Get-FileHash $file_path -Algorithm $Algorithm | ForEach-Object Hash 96 | $global:Latest.ChecksumType64 = $Algorithm 97 | $global:Latest.FileName64 = $file_name 98 | } 99 | } catch{ throw $_ } finally { $client.Dispose() } 100 | } 101 | -------------------------------------------------------------------------------- /AU/Public/Get-Version.ps1: -------------------------------------------------------------------------------- 1 | # Author: Thomas Démoulins 2 | 3 | <# 4 | .SYNOPSIS 5 | Parses a semver-like object from a string in a flexible manner. 6 | 7 | .DESCRIPTION 8 | This function parses a string containing a semver-like version 9 | and returns an object that represents both the version (with up to 4 parts) 10 | and optionally a pre-release and a build metadata. 11 | 12 | The parsing is quite flexible: 13 | - the version can be in the middle of a url or sentence 14 | - first version found is returned 15 | - there can be no hyphen between the version and the pre-release 16 | - extra spaces are ignored 17 | - optional delimiters can be provided to help parsing the string 18 | 19 | .EXAMPLE 20 | Get-Version 'Last version: 1.2.3 beta 3.' 21 | 22 | Returns 1.2.3-beta3 23 | 24 | .EXAMPLE 25 | Get-Version 'https://github.com/atom/atom/releases/download/v1.24.0-beta2/AtomSetup.exe' 26 | 27 | Return 1.24.0-beta2 28 | 29 | .EXAMPLE 30 | Get-Version 'http://mirrors.kodi.tv/releases/windows/win32/kodi-17.6-Krypton-x86.exe' -Delimiter '-' 31 | 32 | Return 17.6 33 | #> 34 | function Get-Version { 35 | [CmdletBinding()] 36 | param( 37 | # Version string to parse. 38 | [Parameter(Mandatory=$true)] 39 | [string] $Version, 40 | # Optional delimiter(s) to help locate the version in the string: the version must start and end with one of these chars. 41 | [char[]] $Delimiter 42 | ) 43 | if ($Delimiter) { 44 | $delimiters = $Delimiter -join '' 45 | @('\', ']', '^', '-') | ForEach-Object { $delimiters = $delimiters.Replace($_, "\$_") } 46 | $regex = $Version | Select-String -Pattern "[$delimiters](\d+\.\d+[^$delimiters]*)[$delimiters]" -AllMatches 47 | foreach ($match in $regex.Matches) { 48 | $reference = [ref] $null 49 | if ([AUVersion]::TryParse($match.Groups[1], $reference, $false)) { 50 | return $reference.Value 51 | } 52 | } 53 | } 54 | return [AUVersion]::Parse($Version, $false) 55 | } 56 | -------------------------------------------------------------------------------- /AU/Public/Push-Package.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 22-Oct-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Push latest (or all) created package(s) to the Chocolatey community repository. 7 | 8 | .DESCRIPTION 9 | The function uses they API key from the file api_key in current or parent directory, environment variable 10 | or cached nuget API key. 11 | #> 12 | function Push-Package() { 13 | param( 14 | [switch] $All 15 | ) 16 | $api_key = if (Test-Path api_key) { Get-Content api_key } 17 | elseif (Test-Path (Join-Path '..' 'api_key')) { Get-Content (Join-Path '..' 'api_key') } 18 | elseif ($Env:api_key) { $Env:api_key } 19 | 20 | $push_url = if ($Env:au_PushUrl) { $Env:au_PushUrl } 21 | else { 'https://push.chocolatey.org' } 22 | 23 | $force_push = if ($Env:au_ForcePush) { '--force' } 24 | else { '' } 25 | 26 | $packages = Get-ChildItem *.nupkg | Sort-Object -Property CreationTime -Descending 27 | if (!$All) { $packages = $packages | Select-Object -First 1 } 28 | if (!$packages) { throw 'There is no nupkg file in the directory'} 29 | if ($api_key) { 30 | $packages | ForEach-Object { choco push $_.Name --api-key $api_key --source $push_url $force_push } 31 | } else { 32 | $packages | ForEach-Object { choco push $_.Name --source $push_url $force_push } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /AU/Public/Set-DescriptionFromReadme.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Updates nuspec file description from README.md 4 | 5 | .DESCRIPTION 6 | This script should be called in au_AfterUpdate to put the text in the README.md 7 | into description tag of the Nuspec file. The current description will be replaced. 8 | 9 | You need to call this function manually only if you want to pass it custom parameters. 10 | In that case use NoReadme parameter of the Update-Package. 11 | 12 | .EXAMPLE 13 | function global:au_AfterUpdate { Set-DescriptionFromReadme -Package $args[0] -SkipLast 2 -SkipFirst 2 } 14 | #> 15 | function Set-DescriptionFromReadme{ 16 | param( 17 | [AUPackage] $Package, 18 | # Number of start lines to skip from the README.md, by default 0. 19 | [int] $SkipFirst=0, 20 | # Number of end lines to skip from the README.md, by default 0. 21 | [int] $SkipLast=0, 22 | # Readme file path 23 | [string] $ReadmePath = 'README.md' 24 | ) 25 | 26 | "Setting package description from $ReadmePath" 27 | 28 | $description = Get-Content $ReadmePath -Encoding UTF8 29 | $endIdx = $description.Length - $SkipLast 30 | $description = $description | Select-Object -Index ($SkipFirst..$endIdx) | Out-String 31 | 32 | $cdata = $Package.NuspecXml.CreateCDataSection($description) 33 | $xml_Description = $Package.NuspecXml.GetElementsByTagName('description')[0] 34 | $xml_Description.RemoveAll() 35 | $xml_Description.AppendChild($cdata) | Out-Null 36 | 37 | $Package.SaveNuspec() 38 | } 39 | -------------------------------------------------------------------------------- /AU/Public/Test-Package.ps1: -------------------------------------------------------------------------------- 1 | # Author: Miodrag Milic 2 | # Last Change: 15-Nov-2016. 3 | 4 | <# 5 | .SYNOPSIS 6 | Test Chocolatey package 7 | 8 | .DESCRIPTION 9 | The function can test install, uninistall or both and provide package parameters during test. 10 | It will force install and then remove the Chocolatey package if called without arguments. 11 | 12 | It accepts either nupkg or nuspec path. If none specified, current directory will be searched 13 | for any of them. 14 | 15 | .EXAMPLE 16 | Test-Package -Install 17 | 18 | Test the install of the package from the current directory. 19 | 20 | .LINK 21 | https://github.com/chocolatey/choco/wiki/CreatePackages#testing-your-package 22 | #> 23 | function Test-Package { 24 | param( 25 | # If file, path to the .nupkg or .nuspec file for the package. 26 | # If directory, latest .nupkg or .nuspec file wil be looked in it. 27 | # If ommited current directory will be used. 28 | $Nu, 29 | 30 | # Test chocolateyInstall.ps1 only. 31 | [switch] $Install, 32 | 33 | # Test chocolateyUninstall.ps1 only. 34 | [switch] $Uninstall, 35 | 36 | # Package parameters 37 | [string] $Parameters, 38 | 39 | # Path to chocolatey-test-environment: https://github.com/majkinetor/chocolatey-test-environment 40 | [string] $Vagrant = $Env:au_Vagrant, 41 | 42 | # Open new shell window 43 | [switch] $VagrantOpen, 44 | 45 | # Do not remove existing packages from vagrant package directory 46 | [switch] $VagrantNoClear 47 | ) 48 | 49 | if (!$Install -and !$Uninstall) { $Install = $true } 50 | 51 | if (!$Nu) { $dir = Get-Item $pwd } 52 | else { 53 | if (!(Test-Path $Nu)) { throw "Path not found: $Nu" } 54 | $Nu = Get-Item $Nu 55 | $dir = if ($Nu.PSIsContainer) { $Nu; $Nu = $null } else { $Nu.Directory } 56 | } 57 | 58 | if (!$Nu) { 59 | $Nu = Get-Item $dir/*.nupkg | Sort-Object -Property CreationTime -Descending | Select-Object -First 1 60 | if (!$Nu) { $Nu = Get-Item $dir/*.nuspec } 61 | if (!$Nu) { throw "Can't find nupkg or nuspec file in the directory" } 62 | } 63 | 64 | if ($Nu.Extension -eq '.nuspec') { 65 | Write-Host "Nuspec file given, running choco pack" 66 | choco pack -r $Nu.FullName --OutputDirectory $Nu.DirectoryName | Write-Host 67 | if ($LASTEXITCODE -ne 0) { throw "choco pack failed with $LastExitCode"} 68 | $Nu = Get-Item ([System.IO.Path]::Combine($Nu.DirectoryName, '*.nupkg')) | Sort-Object -Property CreationTime -Descending | Select-Object -First 1 69 | } elseif ($Nu.Extension -ne '.nupkg') { throw "File is not nupkg or nuspec file" } 70 | 71 | #At this point Nu is nupkg file 72 | 73 | $package_name = $Nu.Name -replace '(\.\d+)+(-[^-]+)?\.nupkg$' 74 | $package_version = ($Nu.BaseName -replace $package_name).Substring(1) 75 | 76 | Write-Host "`nPackage info" 77 | Write-Host " Path:".PadRight(15) $Nu 78 | Write-Host " Name:".PadRight(15) $package_name 79 | Write-Host " Version:".PadRight(15) $package_version 80 | if ($Parameters) { Write-Host " Parameters:".PadRight(15) $Parameters } 81 | if ($Vagrant) { Write-Host " Vagrant: ".PadRight(15) $Vagrant } 82 | 83 | if ($Vagrant) { 84 | Write-Host "`nTesting package using vagrant" 85 | 86 | if (!$VagrantNoClear) { 87 | Write-Host 'Removing existing vagrant packages' 88 | Remove-Item ([System.IO.Path]::Combine($Vagrant, 'packages', '*.nupkg')) -ea ignore 89 | Remove-Item ([System.IO.Path]::Combine($Vagrant, 'packages', '*.xml')) -ea ignore 90 | } 91 | 92 | Copy-Item $Nu (Join-Path $Vagrant 'packages') 93 | $options_file = "$package_name.$package_version.xml" 94 | @{ Install = $Install; Uninstall = $Uninstall; Parameters = $Parameters } | Export-CliXML ([System.IO.Path]::Combine($Vagrant, 'packages', $options_file)) 95 | if ($VagrantOpen) { 96 | Start-Process powershell -Verb Open -ArgumentList "-NoProfile -NoExit -Command `$Env:http_proxy=`$Env:https_proxy=`$Env:ftp_proxy=`$Env:no_proxy=''; cd $Vagrant; vagrant up" 97 | } else { 98 | powershell -NoProfile -Command "`$Env:http_proxy=`$Env:https_proxy=`$Env:ftp_proxy=`$Env:no_proxy=''; cd $Vagrant; vagrant up" 99 | } 100 | return 101 | } 102 | 103 | if ($Install) { 104 | Write-Host "`nTesting package install" 105 | choco install -y -r $package_name --version $package_version --source "'$($Nu.DirectoryName);https://chocolatey.org/api/v2/'" --force --packageParameters "'$Parameters'" | Write-Host 106 | if ($LASTEXITCODE -ne 0) { throw "choco install failed with $LastExitCode"} 107 | } 108 | 109 | if ($Uninstall) { 110 | Write-Host "`nTesting package uninstall" 111 | choco uninstall -y -r $package_name | Write-Host 112 | if ($LASTEXITCODE -ne 0) { throw "choco uninstall failed with $LastExitCode"} 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /AU/Public/Update-AUPackages.ps1: -------------------------------------------------------------------------------- 1 | 2 | # Author: Miodrag Milic 3 | # Last Change: 08-May-2018 4 | 5 | <# 6 | .SYNOPSIS 7 | Update all automatic packages 8 | 9 | .DESCRIPTION 10 | Function Update-AUPackages will iterate over update.ps1 scripts and execute each. If it detects 11 | that a package is updated it will push it to the Chocolatey community repository. 12 | 13 | The function will look for AU packages in the directory pointed to by the global variable au_root 14 | or in the current directory if mentioned variable is not set. 15 | 16 | For the push to work, specify your API key in the file 'api_key' in the script's directory or use 17 | cached nuget API key or set environment variable '$Env:api_key'. 18 | 19 | The function accepts many options via ordered HashTable parameter Options. 20 | 21 | .EXAMPLE 22 | Update-AUPackages p* @{ Threads = 5; Timeout = 10 } 23 | 24 | Update all automatic packages in the current directory that start with letter 'p' using 5 threads 25 | and web timeout of 10 seconds. 26 | 27 | .EXAMPLE 28 | $au_root = 'c:\chocolatey'; updateall @{ Force = $true } 29 | 30 | Force update of all automatic ackages in the given directory. 31 | 32 | .LINK 33 | Update-Package 34 | 35 | .OUTPUTS 36 | AUPackage[] 37 | #> 38 | function Update-AUPackages { 39 | [CmdletBinding()] 40 | param( 41 | # Filter package names. Supports globs. 42 | [string[]] $Name, 43 | 44 | <# 45 | Hashtable with options: 46 | Threads - Number of background jobs to use, by default 10. 47 | Timeout - WebRequest timeout in seconds, by default 100. 48 | UpdateTimeout - Timeout for background job in seconds, by default 1200 (20 minutes). 49 | Force - Force package update even if no new version is found. 50 | Push - Set to true to push updated packages to Chocolatey community repository. 51 | PushAll - Set to true to push all updated packages and not only the most recent one per folder. 52 | WhatIf - Set to true to set WhatIf option for all packages. 53 | PluginPath - Additional path to look for user plugins. If not set only module integrated plugins will work 54 | NoCheckChocoVersion - Set to true to set NoCheckChocoVersion option for all packages. 55 | 56 | Plugin - Any HashTable key will be treated as plugin with the same name as the option name. 57 | A script with that name will be searched for in the AU module path and user specified path. 58 | If script is found, it will be called with splatted HashTable passed as plugin parameters. 59 | 60 | To list default AU plugins run: 61 | 62 | ls "$(Split-Path (gmo au -list).Path)\Plugins\*.ps1" 63 | IgnoreOn - Array of strings, error messages that packages will get ignored on 64 | RepeatOn - Array of strings, error messages that package updaters will run again on 65 | RepeatCount - Number of repeated runs to do when given error occurs, by default 1 66 | RepeatSleep - How long to sleep between repeast, by default 0 67 | 68 | BeforeEach - User ScriptBlock that will be called before each package and accepts 2 arguments: Name & Options. 69 | To pass additional arguments, specify them as Options key/values. 70 | AfterEach - Similar as above. 71 | Script - Script that will be called before and after everything. 72 | #> 73 | [System.Collections.Specialized.OrderedDictionary] $Options=@{}, 74 | 75 | #Do not run plugins, defaults to global variable `au_NoPlugins`. 76 | [switch] $NoPlugins = $global:au_NoPlugins 77 | ) 78 | 79 | $startTime = Get-Date 80 | 81 | if (!$Options.Threads) { $Options.Threads = 10 } 82 | if (!$Options.Timeout) { $Options.Timeout = 100 } 83 | if (!$Options.UpdateTimeout){ $Options.UpdateTimeout = 1200 } 84 | if (!$Options.Force) { $Options.Force = $false } 85 | if (!$Options.Push) { $Options.Push = $false } 86 | if (!$Options.PluginPath) { $Options.PluginPath = '' } 87 | if (!$Options.NoCheckChocoVersion){ $Options.NoCheckChocoVersion = $false } 88 | 89 | Remove-Job * -force #remove any previously run jobs 90 | 91 | $tmp_dir = ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "chocolatey", "au")) 92 | New-Item -Type Directory -ea 0 $tmp_dir | Out-Null 93 | Get-ChildItem $tmp_dir | Where-Object PSIsContainer -eq $false | Remove-Item #clear tmp dir files 94 | 95 | $aup = Get-AUPackages $Name 96 | Write-Host 'Updating' $aup.Length 'automatic packages at' $($startTime.ToString("s") -replace 'T',' ') $(if ($Options.Force) { "(forced)" } else {}) 97 | Write-Host 'Push is' $( if ($Options.Push) { 'enabled' } else { 'disabled' } ) 98 | Write-Host 'NoCheckChocoVersion is' $( if ($Options.NoCheckChocoVersion) { 'enabled' } else { 'disabled' } ) 99 | if ($Options.Force) { Write-Host 'FORCE IS ENABLED. All packages will be updated' } 100 | 101 | $script_err = 0 102 | if ($Options.Script) { try { & $Options.Script 'START' $aup | Write-Host } catch { Write-Error $_; $script_err += 1 } } 103 | 104 | $threads = New-Object object[] $Options.Threads 105 | $result = @() 106 | $j = $p = 0 107 | while( $p -ne $aup.length ) { 108 | 109 | # Check for completed jobs 110 | foreach ($job in (Get-Job | Where-Object state -ne 'Running')) { 111 | $p += 1 112 | 113 | if ( 'Stopped', 'Failed', 'Completed' -notcontains $job.State) { 114 | Write-Host "Invalid job state for $($job.Name): " $job.State 115 | } 116 | else { 117 | Write-Verbose ($job.State + ' ' + $job.Name) 118 | 119 | if ($job.ChildJobs[0].JobStateInfo.Reason.Message) { 120 | $pkg = [AUPackage]::new((Get-AuPackages $job.Name)) 121 | $pkg.Error = $job.ChildJobs[0].JobStateInfo.Reason.Message 122 | } else { 123 | $pkg = $null 124 | Receive-Job $job | Set-Variable pkg 125 | 126 | $ignored = $pkg -eq 'ignore' 127 | if ( !$pkg -or $ignored ) { 128 | $pkg = [AUPackage]::new( (Get-AuPackages $($job.Name)) ) 129 | 130 | if ($ignored) { 131 | $pkg.Result = @('ignored', '') + (Get-Content ([System.IO.Path]::Combine($tmp_dir, $pkg.Name)) -ea 0) 132 | $pkg.Ignored = $true 133 | $pkg.IgnoreMessage = $pkg.Result[-1] 134 | } elseif ($job.State -eq 'Stopped') { 135 | $pkg.Error = "Job terminated due to the $($Options.UpdateTimeout)s UpdateTimeout" 136 | } else { 137 | $pkg.Error = 'Job returned no object, Vector smash ?' 138 | } 139 | } else { 140 | $pkg = [AUPackage]::new($pkg) 141 | } 142 | } 143 | Remove-Job $job 144 | 145 | $jobseconds = ($job.PSEndTime.TimeOfDay - $job.PSBeginTime.TimeOfDay).TotalSeconds 146 | $message = "[$($p)/$($aup.length)] " + $pkg.Name + ' ' 147 | $message += if ($pkg.Updated) { 'is updated to ' + $pkg.RemoteVersion } else { 'has no updates' } 148 | if ($pkg.Updated -and $Options.Push) { 149 | $message += if (!$pkg.Pushed) { ' but push failed!' } else { ' and pushed'} 150 | } 151 | if ($pkg.Error) { 152 | $message = "[$($p)/$($aup.length)] $($pkg.Name) ERROR: " 153 | $message += $pkg.Error.ToString() -split "`n" | ForEach-Object { "`n" + ' '*5 + $_ } 154 | } 155 | $message+= " ({0:N2}s)" -f $jobseconds 156 | Write-Host ' ' $message 157 | 158 | $result += $pkg 159 | } 160 | } 161 | 162 | # Sleep a bit and check for running tasks update timeout 163 | $job_count = Get-Job | Measure-Object | ForEach-Object count 164 | if (($job_count -eq $Options.Threads) -or ($j -eq $aup.Length)) { 165 | Start-Sleep 1 166 | foreach ($job in $(Get-Job -State Running)) { 167 | $elapsed = ((get-date) - $job.PSBeginTime).TotalSeconds 168 | if ($elapsed -ge $Options.UpdateTimeout) { Stop-Job $job } 169 | } 170 | continue 171 | } 172 | 173 | # Start a new thread 174 | $package_path = $aup[$j++] 175 | $package_name = Split-Path $package_path -Leaf 176 | Write-Verbose "Starting $package_name" 177 | Start-Job -Name $package_name { #TODO: fix laxxed variables in job for BE and AE 178 | function repeat_ignore([ScriptBlock] $Action) { # requires $Options 179 | $run_no = 0 180 | $run_max = if ($Options.RepeatOn) { if (!$Options.RepeatCount) { 2 } else { $Options.RepeatCount+1 } } else {1} 181 | 182 | :main while ($run_no -lt $run_max) { 183 | $run_no++ 184 | try { 185 | $res = & $Action 6> $out 186 | break main 187 | } catch { 188 | if ($run_no -ne $run_max) { 189 | foreach ($msg in $Options.RepeatOn) { 190 | if ($_.Exception -notlike "*${msg}*") { continue } 191 | Write-Warning "Repeating $using:package_name ($run_no): $($_.Exception)" 192 | if ($Options.RepeatSleep) { Write-Warning "Sleeping $($Options.RepeatSleep) seconds before repeating"; Start-Sleep $Options.RepeatSleep } 193 | continue main 194 | } 195 | } 196 | foreach ($msg in $Options.IgnoreOn) { 197 | if ($_.Exception -notlike "*${msg}*") { continue } 198 | Write-Warning "Ignoring $using:package_name ($run_no): $($_.Exception)" 199 | "AU ignored on: $($_.Exception)" | Out-File -Append $out 200 | $res = 'ignore' 201 | break main 202 | } 203 | $type = if ($res) { $res.GetType() } 204 | if ( "$type" -eq 'AUPackage') { $res.Error = $_ } else { throw } 205 | } 206 | } 207 | $res 208 | } 209 | 210 | $Options = $using:Options 211 | 212 | Set-Location $using:package_path 213 | $out = (Join-Path $using:tmp_dir $using:package_name) 214 | 215 | $global:au_Timeout = $Options.Timeout 216 | $global:au_Force = $Options.Force 217 | $global:au_WhatIf = $Options.WhatIf 218 | $global:au_Result = 'pkg' 219 | $global:au_NoCheckChocoVersion = $Options.NoCheckChocoVersion 220 | 221 | if ($Options.BeforeEach) { 222 | $s = [Scriptblock]::Create( $Options.BeforeEach ) 223 | . $s $using:package_name $Options 224 | } 225 | 226 | $pkg = repeat_ignore { ./update.ps1 } 227 | if (!$pkg) { throw "'$using:package_name' update script returned nothing" } 228 | if (($pkg -eq 'ignore') -or ($pkg[-1] -eq 'ignore')) { return 'ignore' } 229 | 230 | $pkg = $pkg[-1] 231 | $type = $pkg.GetType() 232 | if ( "$type" -ne 'AUPackage') { throw "'$using:package_name' update script didn't return AUPackage but: $type" } 233 | 234 | if ($pkg.Updated -and $Options.Push) { 235 | $res = repeat_ignore { 236 | $r = Push-Package -All:$Options.PushAll 237 | if ($LastExitCode -eq 0) { return $r } else { throw $r } 238 | } 239 | if (($res -eq 'ignore') -or ($res[-1] -eq 'ignore')) { return 'ignore' } 240 | 241 | if ($res -is [System.Management.Automation.ErrorRecord]) { 242 | $pkg.Error = "Push ERROR`n" + $res 243 | } else { 244 | $pkg.Pushed = $true 245 | $pkg.Result += $res 246 | } 247 | } 248 | 249 | if ($Options.AfterEach) { 250 | $s = [Scriptblock]::Create( $Options.AfterEach ) 251 | . $s $using:package_name $Options 252 | } 253 | 254 | $pkg.Serialize() 255 | } | Out-Null 256 | } 257 | $result = $result | Sort-Object Name 258 | 259 | $info = get_info 260 | run_plugins 261 | 262 | if ($Options.Script) { try { & $Options.Script 'END' $info | Write-Host } catch { Write-Error $_; $script_err += 1 } } 263 | 264 | @('') + $info.stats + '' | Write-Host 265 | 266 | $result 267 | } 268 | 269 | function run_plugins() { 270 | if ($NoPlugins) { return } 271 | 272 | Remove-Item -Force -Recurse (Join-Path $tmp_dir 'plugins') -ea ig 273 | New-Item -Type Directory -Force (Join-Path $tmp_dir 'plugins') | Out-Null 274 | foreach ($key in $Options.Keys) { 275 | $params = $Options.$key 276 | if ($params -isnot [HashTable]) { continue } 277 | 278 | $plugin_path = "$PSScriptRoot/../Plugins/$key.ps1" 279 | if (!(Test-Path $plugin_path)) { 280 | if([string]::IsNullOrWhiteSpace($Options.PluginPath)) { continue } 281 | 282 | $plugin_path = $Options.PluginPath + "/$key.ps1" 283 | if(!(Test-Path $plugin_path)) { continue } 284 | } 285 | 286 | try { 287 | Write-Host "`nRunning $key" 288 | & $plugin_path $Info @params *>&1 | Tee-Object ([System.IO.Path]::Combine($tmp_dir, 'plugins', $key)) | Write-Host 289 | $info.plugin_results.$key += Get-Content ([System.IO.Path]::Combine($tmp_dir, 'plugins', $key)) -ea ig 290 | } catch { 291 | $err_lines = $_.ToString() -split "`n" 292 | Write-Host " ERROR: " $(foreach ($line in $err_lines) { "`n" + ' '*4 + $line }) 293 | $info.plugin_errors.$key = $_.ToString() 294 | } 295 | } 296 | } 297 | 298 | 299 | function get_info { 300 | $errors = $result | Where-Object { $_.Error } 301 | $info = [PSCustomObject]@{ 302 | result = [PSCustomObject]@{ 303 | all = $result 304 | ignored = $result | Where-Object Ignored 305 | errors = $errors 306 | ok = $result | Where-Object { !$_.Error } 307 | pushed = $result | Where-Object Pushed 308 | updated = $result | Where-Object Updated 309 | } 310 | 311 | error_count = [PSCustomObject]@{ 312 | update = $errors | Where-Object {!$_.Updated} | Measure-Object | ForEach-Object count 313 | push = $errors | Where-Object {$_.Updated -and !$_.Pushed} | Measure-Object | ForEach-Object count 314 | total = $errors | Measure-Object | ForEach-Object count 315 | } 316 | error_info = '' 317 | 318 | packages = $aup 319 | startTime = $startTime 320 | minutes = ((Get-Date) - $startTime).TotalMinutes.ToString('#.##') 321 | pushed = $result | Where-Object Pushed | Measure-Object | ForEach-Object count 322 | updated = $result | Where-Object Updated | Measure-Object | ForEach-Object count 323 | ignored = $result | Where-Object Ignored | Measure-Object | ForEach-Object count 324 | stats = '' 325 | options = $Options 326 | plugin_results = @{} 327 | plugin_errors = @{} 328 | } 329 | $info.PSObject.TypeNames.Insert(0, 'AUInfo') 330 | 331 | $info.stats = get-stats 332 | $info.error_info = $errors | ForEach-Object { 333 | "`nPackage: " + $_.Name + "`n" 334 | $_.Error 335 | } 336 | 337 | $info 338 | } 339 | 340 | function get-stats { 341 | "Finished {0} packages after {1} minutes. " -f $info.packages.length, $info.minutes 342 | "{0} updated, {1} pushed, {2} ignored " -f $info.updated, $info.pushed, $info.ignored 343 | "{0} errors - {1} update, {2} push. " -f $info.error_count.total, $info.error_count.update, $info.error_count.push 344 | } 345 | 346 | 347 | Set-Alias updateall Update-AuPackages 348 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # AU Project Changelog 2 | 3 | The AU project is now archived and there will be no new versions. 4 | 5 | ## 2022.10.24 6 | 7 | - Added `$au_GalleryPackageRootUrl` as a slightly more flexible alternative to `$au_GalleryUrl` ([#250](https://github.com/majkinetor/au/issues/250)) 8 | - `$au_GalleryUrl` global variable is still maintained for compatibility 9 | - Can specify as an environment variable `$env:au_GalleryPackageRootUrl` when working with Update-AUPackages (update_all.ps1) ([#254](https://github.com/majkinetor/au/issues/254)) 10 | - Can specify as a global variable in update.ps1 to override on per package basis. 11 | - Plugins: 12 | - `History`: 13 | - History plugin now ignores merges ([#258](https://github.com/majkinetor/au/issues/258)) 14 | - `Package_Source_Root_Url` param added to History plugin so links can be configured for non-Github users. (related to [#257](https://github.com/majkinetor/au/issues/257)) 15 | - `Report`: 16 | - `Package_Source_Root_Url` param added to Markdown report type so links can be configured for non-Github users. ([#257](https://github.com/majkinetor/au/issues/257)) 17 | - `Package_Source_Branch` parameter added to Markdown report type to configure the branch name for the package source if not using master. ([#244](https://github.com/majkinetor/au/issues/244)) 18 | - `PullRequest`: New plugin that creates a GitHub pull request for the updated packages. ([#269](https://github.com/majkinetor/au/pull/269)) 19 | 20 | ## 2021.7.18 21 | 22 | - Fixed bad packaging of previous version 23 | 24 | ## 2021.6.27 25 | 26 | - Linux support ([#234](https://github.com/majkinetor/au/pull/234)) 27 | - TLS settings updated ([#216](https://github.com/majkinetor/au/pull/233)) 28 | - Plugins: 29 | - `Git`: Added `AddNew` parameter to allow adding new packages, created by the AU script ([#239](https://github.com/majkinetor/au/issues/239)) 30 | - `Git`: Changed `Branch` parameter to create branch if it doesnt exist 31 | - `History`: Changed so it looks into all branches for AU commit messages 32 | 33 | ## 2020.11.21 34 | 35 | - `Get-RemoteFiles` now considers `$Latest.Options.Headers` 36 | - TLS settings updated ([#216](https://github.com/majkinetor/au/pull/216)) 37 | - Support ChocolateyPackageFolder environment variable ([#255](https://github.com/majkinetor/au/pull/255)) 38 | - Improved Powershell 7 compatibility ([#208](https://github.com/majkinetor/au/issues/208)) 39 | - Set the AU version to the last commit date ([#217](https://github.com/majkinetor/au/pull/217)) 40 | 41 | ### Plugins: 42 | 43 | - **GitLab**: new plugin ([#195](https://github.com/majkinetor/au/pull/195)) 44 | - **Gist**: option for secret gist and Enterprise API 45 | - **GitReleases**: Added a `Branch` parameter ([#227](https://github.com/majkinetor/au/issues/227)) 46 | 47 | ## 2019.5.22 48 | 49 | - `Get-RemoteChecksum`: New parameter `Headers` 50 | - Plugins: 51 | - New plugin: Gitter 52 | - New plugin: Snippet 53 | 54 | ### Bugfixes 55 | 56 | - Fixed header handling during request ([#164](https://github.com/majkinetor/au/issues/164)) 57 | - Push errors are not repeated or ignored on ([#175](https://github.com/majkinetor/au/issues/175)) 58 | - Few small changes and fixes 59 | 60 | ## 2018.5.18 61 | 62 | - `Update-Package`: 63 | - Now you can pass HTTP/HTTPS headers to `$Latest.Options.Headers` to avoid `Unauthorized` errors while checking URLs. 64 | - Package Gallery URL is no longer hard-coded but taken from the `$au_GalleryUrl` if it exists ([#95](https://github.com/majkinetor/au/issues/95)) 65 | - `Update-AUPackages`: Added `NoCheckChocoVersion` option. 66 | - Plugins: 67 | - `Git`: Added `Branch` parameter to specify a branch name 68 | - `Mail`: Added `From` parameter to be used with mail servers that do not allow non-existent email addresses. 69 | 70 | ### Bugfixes 71 | 72 | - `Gist` plugin: Security protocol is set according to updated [Github requirements](https://githubengineering.com/crypto-removal-notice). 73 | - `Get-RemoteFiles`: Fixed wrong checksum type being set on 64bit url 74 | 75 | ## 2018.1.11 76 | - `Update-AuPackage` 77 | - New feature [streams](https://github.com/majkinetor/au#streams) that extends `au_GetLatest` with option to return multiple HashTables (one for each stream). 78 | - New parameter `IncludeStream` to force update of specific stream. 79 | - `au_BeforeUpdate` and `au_AfterUpdate` now have parameter `Package` of type `[AUPackage]` which you can use among other things to modify the Nuspec data. 80 | - Added new function `Set-DescriptionFromReadme` that is called automatically when README.md is present in the package folder ([#85](https://github.com/majkinetor/au/issues/85)). See [documentation](README.md#automatic-package-description-from-readmemd). 81 | - Plugins: 82 | - New plugin: [GitReleases](https://github.com/majkinetor/au/blob/master/AU/Plugins/GitReleases.ps1) creates Github release on successifully pushed packages. 83 | - Git: new parameter `Strategy` with options on how to commit repository changes 84 | - Report: symbols in markdown report to mark embedded and stream packages 85 | 86 | ## 2017.8.30 87 | 88 | - `Update-AUPackages` 89 | - New options to handle update.ps1 errors: `IgnoreOn`, `RepeatOn`,`RepeatCount`,`RepeatSleep`. See [documentation](https://github.com/majkinetor/au#handling-update-errors). ([#76](https://github.com/majkinetor/au/issues/76)). 90 | - New option `WhatIf` option that will trigger WhatIf on all packages. 91 | - New AUPackage properties: `Ignored` (boolean) and `IgnoreMessage`. 92 | - Report plugin: `IgnoreMessage` is added in the ignore section. 93 | - `Update-AuPackage` 94 | - Added parameter `WhatIf` that will save and restore the package. See [documentation](https://github.com/majkinetor/au#whatif). ([#30](https://github.com/majkinetor/au/issues/30)) 95 | - `au_GetLatest` can now return `ignore` to make package ignored in the `updateall` context. 96 | 97 | ### Bugfixes 98 | 99 | - Git plugin: package that changed `$Latest.PackageName` was not pushed when updated ([#66](https://github.com/majkinetor/au/issues/66)). 100 | 101 | ## 2017.3.29 102 | 103 | - `Get-RemoteFiles` 104 | - `NoSuffix` switch to not add `_x32` and/or `_x64` suffix at the end of the file names. 105 | - Now also sets `ChecksumTypeXX` and `FileNameXX` and accepts `Algorithm` parameter. 106 | 107 | ### Bugfixes 108 | 109 | - Fix ps1 files encoded in UTF8 without BOM being treated as ANSI. 110 | - Fix chocolatey.org package check using wrong package name when overridden in update.ps1. 111 | 112 | ## 2017.1.14 113 | 114 | **NOTE**: License changed from MIT to GPL2. 115 | 116 | - New function `Get-RemoteFiles`. See [documentation](https://github.com/majkinetor/au#embedding-binaries). 117 | - `Update-Package` 118 | - Support newer TLS version support by setting the `SecurityProtocol` property of `ServicePointManager`. 119 | - Posh 5 dependency removed for chocolatey package because it is not practical. 120 | 121 | ### Bugfixes 122 | 123 | - Fix encoding of nuspec (UTF-8 NO BOM) and ps1 (UTF-8 BOM) files. 124 | 125 | ## 2016.12.17 126 | 127 | **NOTE**: Minimal PowerShell version required to run AU is now 5.0 instead of 4.0. This wont affect AppVeyor builds, but might affect local runs. Please update your local PowerShell version (`cinst powershell`) if you run it locally. 128 | 129 | - New function `Get-RemoteChecksum` that can be used instead of automatic checksum. 130 | - `Get-AuPackages` now accepts array of globs, for example `lsau 'cpu-z*','p*','copyq'`. 131 | - `Update-AUPackages` 132 | - New plugin `History` that creates markdown report of package updates grouped by dates. 133 | - Report plugin 134 | - Added link to `packageSourceUrl` and `projectUrl`. 135 | - New parameter `Title` to change report title. 136 | - New parameters for markdown report - `IconSize` and `NoIcons`. Icons are now shown by default. 137 | - Plugins documentation updated. 138 | - `Test-Package`: new parameter `VagrantNoClear` that will not delete existing packages from the vagrant package directory. 139 | - `update.ps1` script can now return keyword [ignore](https://github.com/majkinetor/au#ignoring-specific-errors) to cause `udpateall` to not report it as an error in output. 140 | - `Update-Package` 141 | - `au_GetLatest` can now force update by setting `$global:au_Force = $true`. 142 | - Refactoring code to use PowerShell 5 classes, functions now return `[AUPackage]` object. 143 | 144 | ### Bugfixes 145 | 146 | - `Git` plugin bugfixes. 147 | - Small fixes and tweaks all around. 148 | - Packages shouldn't drop from the results now no matter what happens with the `updateall` thread. 149 | - `$Latest.FileType` is not overwritten when its set in the `au_GetLatest`. 150 | 151 | ### CD 152 | 153 | Changes in [au-packages-template](https://github.com/majkinetor/au-packages-template): 154 | - Added new script `test_all.ps1` to force test all desired packages and randomly test package groups. See wiki page [setting up the force test](https://github.com/majkinetor/au/wiki/AppVeyor#setting-up-the-force-test-project-optional) for how to set this up on AppVeyor. 155 | 156 | ## 2016.11.5 157 | 158 | - `Update-Package` 159 | - It now automatically adds `$Latest.Filetype` based on the extension of the first URL it finds. 160 | 161 | ### CD 162 | 163 | - Added script `scripts\Install-AU.ps1` to install any AU version or branch using git tags. 164 | 165 | Changes in [au-packages-template](https://github.com/majkinetor/au-packages-template): 166 | - Added new AppVeyor commit command `[PUSH pkg1 ... pkgN]` to push any package to the community repository. 167 | 168 | ### Bugfixes 169 | 170 | - Fixed missing temporary directory for package download [ref](https://github.com/chocolatey/chocolatey-coreteampackages/pull/350). 171 | 172 | ## 2016.10.30 173 | 174 | - `Update-Package` 175 | - Show 'URL check' in output. 176 | - `$global:au_Version` can now be used when update is forced to explicitly provide version. 177 | - Invalid version in the nuspec file doesn't throw error any more but uses version 0.0 with warning. 178 | - `Update-AUPackages` 179 | - Added `BeforeEach` and `AfterEach` scripts to Options. 180 | - New Option `UpdateTimeout` to limit update total execution time ([#38](https://github.com/majkinetor/au/issues/38)). 181 | - `Git` plugin: only push files that are changed, not entire package content. 182 | - `Test-Package` 183 | - New string parameter `Vagrant` and global variable `$au_Vagrant` that contain path to the [chocolatey test environment](https://github.com/majkinetor/chocolatey-test-environment) so you can test the package using the Vagrant system. 184 | - PowerShell documentation improved. 185 | 186 | ### Bugfixes 187 | 188 | - Fixed frequent URL check timeout [#35](https://github.com/majkinetor/au/issues/35). 189 | - AU updated nuspec file to UTF-8 with BOM [#39](https://github.com/majkinetor/au/issues/39). 190 | - Checksum verification mode didn't work with updateall [#36](https://github.com/majkinetor/au/issues/36). 191 | - Small fixes. 192 | 193 | ### CD 194 | 195 | Changes in [au-packages-template](https://github.com/majkinetor/au-packages-template): 196 | - `update_all.ps1` now accepts `ForcedPackages` and `Root` parameters. 197 | - AppVeyor commit message parsing for AU options. 198 | 199 | 200 | ## 2016.10.9 201 | 202 | - `Update-Package` uses last returned value of `au_GetLatest` instead of everything ([#28](https://github.com/majkinetor/au/issues/28)). 203 | - `Test-Package` new option `Parameters` to support testing packages with custom parameters. 204 | 205 | ### Bugfixes 206 | 207 | - `Test-Package` - Uninstall test fixed. 208 | - `Git` plugin error - _A positional parameter cannot be found_ error fixed ([#31](https://github.com/majkinetor/au/issues/31)). 209 | - Small fixes. 210 | 211 | ## 2016.9.25 212 | 213 | **NOTE**: This update breaks compatibility with existing `update_all.ps1` scripts - parameter `Options` 214 | is now of the type ordered HashTable ( `[ordered]@{...}` ). This is the only required change for the script 215 | to continue working and behave the same as before, however, other things are required in order to fully use AU features: 216 | 217 | - Remove the user scripts `Save-XXX.ps1` as improved versions now come with AU (plugins). 218 | - Take a look at the [update_all.ps1](https://github.com/majkinetor/au-packages-template/blob/master/update_all.ps1) 219 | to see how plugins are used and setup. Migrate current custom options to the new style. 220 | See [plugins section](https://github.com/majkinetor/au#plugins) for details. 221 | 222 | Take a look at the [working example](https://github.com/majkinetor/au-packages/blob/master/update_all.ps1) and [plugin wiki page](https://github.com/majkinetor/au/wiki/Plugins). 223 | 224 | ### Changes 225 | 226 | - `Update-Package` 227 | - Support for Semantic Versioning [#21](https://github.com/majkinetor/au/issues/21). 228 | - `Test-Package` 229 | - Optional parameter Nu to test package from the .nupkg, .nuspec or directory. 230 | - Test chocolatey uninstaller. 231 | - Refactoring. 232 | - Installer improvements. 233 | - `Update-AUPackages` 234 | - Plugin system with the following default plugins included: 235 | - `RunInfo` - Save run info to the CliXml file and exclude sensitive information. 236 | - `Report` - Saves run info as a report file via included templates (currently markdown and text). 237 | - `Gist` - Save files as anonymous or user gists. 238 | - `Git` - Commits package changes to the git repository. 239 | - `Mail` - Send mail with attachments. 240 | - New parameter `NoPlugins` (by default `$Env:au_NoPlugins` to disable all plugins. 241 | - New option parameter `PluginPath` to specify additional path where plugins are located. 242 | - Output now shows if Push and Force options are used. 243 | - Created [au-packages-template](https://github.com/majkinetor/au-packages-template) to quick start AU. 244 | - Documentation is rewritten and [wiki](https://github.com/majkinetor/au/wiki) created. 245 | 246 | ### Bugfixes 247 | 248 | - Fixed bug due to the typo when pushing and sorting packages when executing `Update-AUPackages`. 249 | 250 | ### CD 251 | 252 | - New `./test.ps1` script that run some or all of the tests. 253 | - AU version environment variable added to `appveyor.yml`. 254 | 255 | 256 | ## 2016.9.21 257 | 258 | ### Bugfixes 259 | 260 | - Push was not working when package was updated. 261 | 262 | ## 2016.9.14.233253 263 | 264 | - `Update-Package` 265 | - New alias `lsau`. 266 | - Return an object of type `AUPackage` instead of text. 267 | - New parameters 268 | - `NoHostOutput` to not show any `Write-Host` output. 269 | - `Result` to hold the name of the global output variable. 270 | - Verbose parameter. 271 | - `NuspecVersion` added to the `$Latest` HashTable. 272 | - Pester tests. 273 | - run standalone, `update` in the package directory calls `./update.ps1`. 274 | - `README.md` made available via `man about_au`. 275 | - Consider global variable `$au_root` when looking for AU packages. 276 | - Optimization and refactoring. 277 | - Bugfixes 278 | - `Update-Packages` exception when `au_GetLatests` returned nothing. 279 | - `$Latest.Version` remains set to remote version when forcing update [#24](https://github.com/majkinetor/au/issues/24). 280 | - Chocolatey installation fixed [#15](https://github.com/majkinetor/au/issues/15) 281 | 282 | ### CD 283 | 284 | - Build module script. 285 | - Build chocolatey package script. 286 | - Publish to Github, Chocolatey and PSGallery. 287 | - `install.ps1` script to install/remove Powershell module in the current system. 288 | - AppVeyor automatic build. 289 | 290 | 291 | ## 2016.8.15 292 | 293 | - Use Chocoloatey fix notation with `Force` parameter. 294 | - Checksum verification using `ChecksumType32` and `ChecksumType64` when `Checksum32` or `Checksum64` are present in the `$Latest` HashTable. 295 | - `PackageName` added to the $Latest HashTable by the framework. 296 | - Use chocolatey cached nuget API key when all other methods fail. 297 | 298 | ### Bugfixes 299 | 300 | - Multiple updates happening at the same time lead to issues with the fix-choco method. 301 | - Copy fails if 'extensions' directory doesn't exist. 302 | 303 | 304 | ## 2016.8.13 305 | 306 | ### Bugfixes 307 | 308 | - Fixed `cpack` name collision with that of CMake. 309 | 310 | ## 2016.8.12 311 | 312 | - Support for Chocoloatey version 0.10.0. 313 | 314 | 315 | ## 2016.8.7 316 | 317 | - Automatic checksum. 318 | - Raise errors on search pattern not found. 319 | - Bugfixes and small improvements. 320 | 321 | ## 2016.6.6 322 | 323 | - First PowerShell Gallery version. 324 | 325 | ## 2016.2.19 326 | 327 | - First PoC version. 328 | -------------------------------------------------------------------------------- /DEVEL.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | The development requires Powershell 5+. 4 | 5 | The following scripts are used during development and deployment: 6 | 7 | - `setup.ps1` 8 | Install dependencies for everything. 9 | - `build.ps1` 10 | Build the module and packages. 11 | - `install.ps1` 12 | Install the module in the system. 13 | - `publish.ps1` 14 | Publish module to Powershell Gallery, Chocolatey and Github. 15 | 16 | 17 | ## Build and test 18 | 19 | The builded module will be available in the `_build\{version}` directory. Version is by default determined automatically based on the current time. 20 | 21 | ``` 22 | ./build.ps1 23 | ``` 24 | The following example commands can be run from the repository root: 25 | 26 | | Description | Command | 27 | | :--- | :--- | 28 | | Override default version | `./build -Version 0.0.1` | 29 | | Build and install in the system with short version | `./build.ps1 -Install -ShortVersion` | 30 | | Install latest build in the system | `./install.ps1` | 31 | | Install using given path in the system | `./install.ps1 -module_path AU` | 32 | | Uninstall from the system | `./install.ps1 -Remove` | 33 | | Run tests (use `Pester` & `Chocolatey` params to limit) | `./test.ps1` | 34 | | Clean temporary build files | `git clean -Xfd -e vars.ps1` | 35 | 36 | 37 | ## Publish 38 | 39 | The `publish.ps1` script publishes to Github, PSGallery and Chocolatey. There is a switch parameter for each publishing platform and there is also a parameter for creating a git tag. 40 | 41 | ```powershell 42 | $v = ./build.ps1 #create a new version 43 | ./publish.ps1 -Version $v -Tag -Github -PSGallery -Chocolatey #publish everywhere 44 | ``` 45 | 46 | Before publishing, edit the `NEXT` header in the `CHANGELOG.md` file to set the release notes. The publish script will take content of the second level header named after version as the release notes. The publishing will fail if release notes are not found. If that happens, don't forget to edit the file **and commit/push it to repository** in order for next tag to include it. 47 | 48 | Chocolatey package description is automatically taken from the README.md section "## Features". 49 | 50 | Publishing procedure depends on number of environment variables. Rename `vars_default.ps1` to `vars.ps1` and set variables there to get them included. 51 | -------------------------------------------------------------------------------- /Plugins.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | 4 | [Gist](#gist)   [Git](#git)   [GitLab](#gitlab)   [GitReleases](#gitreleases)   [Gitter](#gitter)   [History](#history)   [Mail](#mail)  [PullRequest](#pullrequest) [Report](#report)   [RunInfo](#runinfo)   [Snippet](#snippet) 5 | 6 | --- 7 | 8 | [AU plugins](https://github.com/majkinetor/au/blob/master/AU/Plugins) are [configured](https://github.com/majkinetor/au#plugins) using parameters passed in the HashTable contained in the Options under the key that is named by the plugin. So,`$Options.xyz=@{...}` is a plugin if `xyz.ps1` exists in a directory pointed to by the `PluginPath` updateall option. The AU will then run this script and pass it `$Options.xyz` HashTable as plugin specific options. AU comes with several integrated plugins that are described bellow. 9 | 10 | Default [update_all.ps1](https://github.com/majkinetor/au-packages-template/blob/master/update_all.ps1) uses environment variables to configure some options. If you use [AppVeyor](https://github.com/majkinetor/au/wiki/AppVeyor) set those variables in the [.appveyor.yml](https://github.com/majkinetor/au-packages-template/blob/master/.appveyor.yml) and to run it locally use [update_vars.ps1](https://github.com/majkinetor/au-packages-template/blob/master/update_vars_default.ps1). 11 | 12 | ## [Gist](https://github.com/majkinetor/au/blob/master/AU/Plugins/Gist.ps1) 13 | 14 | **Upload one or more files to gist**. 15 | 16 | To set up plugin to create gist under your user name you need to give it your gist id and authentication: 17 | 18 | * Log into https://gist.github.com with the user you want to use. 19 | * Create an empty gist (secret or not). Grab the id at the end of it - `https://gist.github.com/name/{id}`. Set it as `$Env:gist_id` environment variable. 20 | * Create [Github personal access token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/) and **make sure token has _gist_ scope selected**. Authenticating with username and password isn't supported for security reasons. Set it as `$Env:github_api_key` environment variable. 21 | 22 | 23 | ## [Git](https://github.com/majkinetor/au/blob/master/AU/Plugins/Git.ps1) 24 | 25 | **Persist modified files**. 26 | 27 | * To use it locally, just ensure `git push` doesn't require credentials and dont set any environment variables. 28 | * To use on build server such as [[AppVeyor]], specify `$Env:username` and `$Env:password`. If you host git repository on Github its preferable to use personal access token. You can use the same token as with gist as long as _**public repo**_ scope is activated. 29 | 30 | ## [GitLab](https://github.com/majkinetor/au/blob/master/AU/Plugins/GitLab.ps1) 31 | 32 | **Persist modified files**. 33 | 34 | * Same functionality as Git plugin, but tailored to HTTP(S) API-key pushes against a GitLab server. 35 | * To use on build server such as [[AppVeyor]], specify `$Env:gitlab_user`, `$Env:gitlab_apikey`, and `$Env:gitlab_pushurl`. 36 | * `pushurl` must be a full HTTP(S) URL to the repo; the same one you would use to clone it. Internal plugin logic will use this to reconstruct a compatible URL. 37 | 38 | 39 | ## [GitReleases](https://github.com/majkinetor/au/blob/master/AU/Plugins/GitReleases.ps1) 40 | 41 | **Creates Github release for updated packages**. 42 | 43 | * It is recommended to add the following line `skip_tags: true` in the `appveyor.yml` file to prevent tags from being built. While it may not be necessary, this is used to prevent packages from being submitted again when `[AU]` or `[PUSH]` is being used in the commit header message. 44 | 45 | ## [Gitter](https://github.com/majkinetor/au/blob/master/AU/Plugins/Gitter.ps1) 46 | 47 | **Setup project to submit gitter status** 48 | 49 | * First of all, navigate to the gitter channel you wish to have the status listed (you'll need to have permission to add integrations, and need to do it through the webpage). 50 | 1. Click on the icon for room settings, then select `Integrations`. 51 | 2. Select a `Custom` Integration 52 | 3. Copy the unique webhook url listed in the dialog. 53 | 4. Update your appveyor environment variable with your unique webhook, and set the name to `gitter_webhook`. 54 | 5. Navigate to the `update_all.ps1` file in your repository, and update the `$Options` hashtable with the following 55 | ```powershell 56 | Gitter = @{ 57 | WebHookUrl = $env:gitter_webhook 58 | } 59 | ``` 60 | 6. Enjoy your status updates, or frown on failures. 61 | 62 | ## [History](https://github.com/majkinetor/au/blob/master/AU/Plugins/History.ps1) 63 | 64 | **Create update history as markdown report using git log**. 65 | 66 | Shows one date per line and all of the packages pushed to the Chocolatey community repository during that day. First letter of the package name links to report (produced by Report plugin), the rest links to actuall commit (produced by the Git plugin). 67 | 68 | This plugin requires Git plugin and that clone is done with adequate depth. 69 | 70 | ## [Mail](https://github.com/majkinetor/au/blob/master/AU/Plugins/Mail.ps1) 71 | 72 | **Send mail notifications on errors or always**. 73 | 74 | * If you use Google mail for error notifications on a build server such as AppVeyor, Google may block authentication from unknown device. To receive those emails enable less secure apps - see [Allowing less secure apps to access your account](https://support.google.com/accounts/answer/6010255?hl=en). 75 | * If you do not want to use your private email for this, create a new Google account and redirect its messages to your private one. This wont affect you if you run the scripts from your own machine from which you usually access the email. 76 | 77 | ## [PullRequest](https://github.com/majkinetor/au/blob/master/AU/Plugins/PullRequest.ps1) 78 | 79 | **Create GitHub pull request for the updated packages**. 80 | 81 | The plugin will open a GitHub pull request for the commits created by the Git plugin, which of course needs to run first. 82 | It has options to add assignees and reviewers to the created pull request and, supports on-prem GitHub Enterprise Server. 83 | 84 | ## [Report](https://github.com/majkinetor/au/blob/master/AU/Plugins/Report.ps1) 85 | 86 | **Create different types of reports about the current run**. 87 | 88 | The plugin saves state of all packages in a file that can be used locally or uploaded via other plugins to remote (such as Gist or Mail). 89 | 90 | Report Types and associated Params: 91 | 92 | * **Markdown:** - Markdown report with links 93 | * *Github_UserRepo* - Use in generating the display name of the link to the package source. (Also used to generate the link address when PackageSourceRootUrl is not specified.) 94 | * *IconSize* - Size of package icons. Defaults to 32. 95 | * *NoAppVeyor* - Set to $true if not using AppVeyor. 96 | * *NoIcons* - Set to $true to remove package icons. 97 | * *PackageSourceBranch* - Used in generating link back to update_all script. Defaults to master. 98 | * *PackageSourceRootUrl* - Use in generating links to the package source. Link addresses default to `https://github.com/`. 99 | * *Title* - Title for the report. 100 | * *UserMessage* - Specify to add a message at the top of the report. 101 | * **Text:** - Simple plain text report. 102 | * *Title* - Title for the report. 103 | * *UserMessage* - Specify to add a message at the top of the report. 104 | 105 | ## [RunInfo](https://github.com/majkinetor/au/blob/master/AU/Plugins/RunInfo.ps1) 106 | 107 | **Save run info to the file and exclude sensitive information**. 108 | 109 | Run this plugin as the last one to save all other info produced during the run in such way that it can be recreated as object. 110 | To load it for inspection use `$info = Import-CliXml update_info.xml`. 111 | 112 | ## [Snippet](https://github.com/majkinetor/au/blob/master/AU/Plugins/Snippet.ps1) 113 | 114 | **Upload update history report to GitLab snippet**. 115 | 116 | To set up plugin to create snippet under your user name you need to give it your snippet id and authentication: 117 | 118 | * Log into https://gitlab.com/users/sign_in with the user you want to use. 119 | * [Create a snippet](https://gitlab.com/snippets/new) (private or not) with a title and some random content. Grab the id at the end of it - `https://gitlab.com/snippets/{id}`. Set it as `$Env:snippet_id` environment variable. 120 | * Create [GitLab personal access token](https://gitlab.com/profile/personal_access_tokens) and **make sure token has _api_ scope selected**. Authenticating with username and password isn't supported for security reasons. Set it as `$Env:gitlab_api_token` environment variable. 121 | 122 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | image: WMF 5 3 | 4 | environment: 5 | mail_user: majkinetor@gmail.com 6 | 7 | install: 8 | - ps: 'Get-CimInstance win32_operatingsystem -Property Caption, OSArchitecture, Version | fl Caption, OSArchitecture, Version' 9 | - ps: $PSVersionTable 10 | - ps: ./setup.ps1 11 | 12 | build_script: 13 | - ps: | 14 | $version = ./build.ps1 15 | Update-AppveyorBuild -Version $version 16 | 17 | test_script: 18 | - ps: | 19 | $res = ./test.ps1 20 | 21 | "Uploading test results" 22 | $url = "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)" 23 | (New-Object 'System.Net.WebClient').UploadFile($url, (Resolve-Path _build/*/TestResults.xml)) 24 | if ($res.FailedCount -gt 0) { throw "$($res.FailedCount) tests failed." } 25 | 26 | #on_finish: 27 | #- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 28 | 29 | artifacts: 30 | - path: _build\*\*.nupkg 31 | - path: _build\*\*.7z 32 | - path: _build\*\*.xml 33 | 34 | notifications: 35 | - provider: Email 36 | to: $(mail_user) 37 | on_build_success: false 38 | on_build_failure: true 39 | on_build_status_changed: true 40 | 41 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 3 2 | 3 | <# 4 | .SYNOPSIS 5 | AU build script 6 | #> 7 | param( 8 | # Version to set 9 | [string] $Version = [Version](Get-Date).ToUniversalTime().ToString("yyyy.M.d.HHmmss"), 10 | 11 | # Install module in the system after the build 12 | [switch] $Install, 13 | 14 | # Use short date string 15 | [switch] $ShortVersion, 16 | 17 | # Clean up 18 | [switch] $Clean, 19 | 20 | # Do not build chocolatey package 21 | [switch] $NoChocoPackage, 22 | 23 | # Date from last commit 24 | [switch] $LastCommitDate 25 | ) 26 | 27 | $b = { 28 | if ($Clean) { git clean -Xfd -e vars.ps1; return } 29 | if ($ShortVersion) { $Version = [string] $Version = [Version](Get-Date).ToUniversalTime().ToString("yyyy.M.d") } 30 | if ($LastCommitDate) { $Version = [string] $Version = [Version]$(((git log -1 --date=short) | Where-Object { $_ -match "date:"}) | Select-Object -First 1).split(' ')[-1].replace("-",".") } 31 | 32 | $module_path = "$PSScriptRoot/AU" 33 | $module_name = Split-Path -Leaf $module_path 34 | $build_dir = "$PSScriptRoot/_build/$version" 35 | $installer_path = "$PSScriptRoot/install.ps1" 36 | $remove_old = $true 37 | 38 | $ErrorActionPreference = 'Stop' 39 | 40 | Write-Host "`n==| Building $module_name $version`n" 41 | init 42 | 43 | $module_path = "$build_dir/$module_name" 44 | create_manifest 45 | create_help 46 | 47 | Copy-Item $installer_path $build_dir 48 | zip_module 49 | build_chocolatey_package 50 | 51 | if ($Install) { & $installer_path } 52 | 53 | $Version 54 | } 55 | 56 | function zip_module() { 57 | Write-Host "Creating 7z package" 58 | 59 | $zip_path = "$build_dir\${module_name}_$version.7z" 60 | $cmd = "$Env:ChocolateyInstall/tools/7z.exe a '$zip_path' '$module_path' '$installer_path'" 61 | $cmd | Invoke-Expression | Out-Null 62 | if (!(Test-Path $zip_path)) { throw "Failed to build 7z package" } 63 | } 64 | 65 | function init() { 66 | if ($remove_old) { 67 | Write-Host "Removing older builds" 68 | Remove-Item -Recurse (Split-Path $build_dir) -ea ignore 69 | } 70 | New-Item -Type Directory -Force $build_dir | Out-Null 71 | Copy-Item -Recurse $module_path $build_dir 72 | } 73 | 74 | function build_chocolatey_package { 75 | if ($NoChocoPackage) { Write-Host "Skipping chocolatey package build"; return } 76 | 77 | & $PSScriptRoot/chocolatey/build-package.ps1 78 | Move-Item "$PSScriptRoot/chocolatey/${module_name}.$version.nupkg" $build_dir 79 | } 80 | 81 | function create_help() { 82 | Write-Host 'Creating module help' 83 | 84 | $help_dir = "$module_path/en-US" 85 | New-Item -Type Directory -Force $help_dir | Out-Null 86 | Get-Content $PSScriptRoot/README.md | Select-Object -Skip 4 | Set-Content "$help_dir/about_${module_name}.help.txt" -Encoding ascii 87 | } 88 | 89 | function create_manifest() { 90 | Write-Host 'Creating module manifest' 91 | $params = @{ 92 | ModulePath = $module_path 93 | Version = $version 94 | } 95 | & $PSScriptRoot/scripts/Create-ModuleManifest.ps1 @params 96 | } 97 | 98 | & $b 99 | 100 | -------------------------------------------------------------------------------- /chocolatey/au.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | au 6 | Chocolatey Automatic Package Updater Module 7 | 8 | Miodrag Milić 9 | Miodrag Milić 10 | AU is Powershell module that helps you to automate Chocolatey package updates 11 | 12 | https://github.com/majkinetor/au 13 | admin powershell module package chocolatey 14 | Miodrag Milić 15 | https://www.gnu.org/licenses/gpl-2.0.txt 16 | false 17 | https://github.com/majkinetor/au/blob/master/CHANGELOG.md 18 | https://github.com/majkinetor/au/blob/master/README.md 19 | https://github.com/majkinetor/au/issues 20 | https://github.com/majkinetor/au/issues 21 | https://github.com/majkinetor/au 22 | https://github.com/majkinetor/au/tree/master/chocolatey 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /chocolatey/build-package.ps1: -------------------------------------------------------------------------------- 1 | # Build the chocolatey package based on the latest module build in ..\_build folder 2 | 3 | $build_path = Resolve-Path $PSScriptRoot\..\_build 4 | $version = Get-ChildItem $build_path | Sort-Object CreationDate -desc | Select-Object -First 1 -Expand Name 5 | $version = $version.ToString() 6 | if (![version]$version) { throw 'Latest module build can not be found' } 7 | 8 | $module_path = "$build_path\$version\AU" 9 | $nuspec_path = "$PSScriptRoot\au.nuspec" 10 | 11 | Write-Host "`n==| Building Chocolatey package for AU $version at: '$module_path'`n" 12 | 13 | Write-Host 'Setting description' 14 | $readme_path = Resolve-Path $PSScriptRoot\..\README.md 15 | $readme = Get-Content $readme_path -Raw 16 | $res = $readme -match '## Features(.|\n)+?(?=\n##)' 17 | if (!$res) { throw "Can't find markdown header 'Features' in the README.md" } 18 | 19 | $features = $Matches[0] 20 | 21 | Write-Host 'Updating nuspec file' 22 | $nuspec_build_path = $nuspec_path -replace '\.nuspec$', '_build.nuspec' 23 | [xml]$au = Get-Content $nuspec_path 24 | $description = $au.package.metadata.summary + ".`n`n" + $features 25 | $au.package.metadata.version = $version 26 | $au.package.metadata.description = $description 27 | $au.package.metadata.releaseNotes = 'https://github.com/majkinetor/au/releases/tag/' + $version 28 | $au.Save($nuspec_build_path) 29 | 30 | Write-Host 'Copying module' 31 | Copy-Item -Force -Recurse $module_path $PSScriptRoot\tools 32 | Copy-Item $PSScriptRoot\..\install.ps1 $PSScriptRoot\tools 33 | 34 | Remove-Item $PSScriptRoot\*.nupkg 35 | choco pack -r $nuspec_build_path --outputdirectory $PSScriptRoot | Write-Host 36 | Remove-Item $nuspec_build_path -ea ignore 37 | -------------------------------------------------------------------------------- /chocolatey/tools/chocolateyInstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | 3 | $toolsPath = Split-Path $MyInvocation.MyCommand.Definition 4 | & "$toolsPath/install.ps1" 5 | -------------------------------------------------------------------------------- /chocolatey/tools/chocolateyUninstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | 3 | $toolsPath = Split-Path $MyInvocation.MyCommand.Definition 4 | & "$toolsPath/install.ps1" -Remove 5 | -------------------------------------------------------------------------------- /install.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 2 2 | 3 | <# 4 | .SYNOPSIS 5 | AU install script 6 | 7 | .NOTES 8 | Always install AU versionless in Program Files to support older PowerShell versions ( v < 5 ) 9 | Multiple AU versions can be installed using Install-Module if needed (on Posh 5+). 10 | #> 11 | param( 12 | #If given it is path to the module to be installed. 13 | #If not given, use first build directory and if doesn't exist, try scripts folder. 14 | [string] $module_path, 15 | 16 | #Remove module from the system. 17 | [switch] $Remove 18 | ) 19 | 20 | $ErrorActionPreference = 'Stop' 21 | 22 | $module_name = 'AU' 23 | 24 | if ($PSVersionTable.PSEdition -ne "Core") { 25 | $module_dst = "$Env:ProgramFiles\WindowsPowerShell\Modules" 26 | } else { 27 | $module_dst = "$Env:ProgramFiles\PowerShell\Modules" 28 | } 29 | 30 | Remove-Item -Force -Recurse "$module_dst\$module_name" -ErrorAction ignore 31 | if ($Remove) { remove-module $module_name -ea ignore; Write-Host "Module $module_name removed"; return } 32 | 33 | Write-Host "`n==| Starting $module_name installation`n" 34 | 35 | if (!$module_path) { 36 | if (Test-Path $PSScriptRoot\_build\*) { 37 | $module_path = (Get-ChildItem $PSScriptRoot\_build\* -ea ignore | Sort-Object CreationDate -desc | Select-Object -First 1 -Expand FullName) + '/' + $module_name 38 | } else { 39 | $module_path = "$PSScriptRoot\$module_name" 40 | if (!(Test-Path $module_path)) { throw "module_path not specified and scripts directory doesn't contain the module" } 41 | } 42 | } 43 | $module_path = Resolve-Path $module_path 44 | 45 | if (!(Test-Path $module_path)) { throw "Module path invalid: '$module_path'" } 46 | 47 | Write-Host "Module path: '$module_path'" 48 | 49 | New-Item -ItemType Directory "$module_dst/$module_name" -ErrorAction Ignore | Out-Null 50 | 51 | Copy-Item -Recurse -Force $module_path $module_dst 52 | 53 | $res = Get-Module $module_name -ListAvailable | Where-Object { (Split-Path $_.ModuleBase) -eq $module_dst } 54 | if (!$res) { throw 'Module installation failed' } 55 | 56 | Write-Host "`n$($res.Name) version $($res.Version) installed successfully at '$module_dst\$module_name'" 57 | 58 | $functions = $res.ExportedFunctions.Keys 59 | 60 | import-module $module_dst\$module_name -force 61 | $aliases = get-alias | Where-Object { $_.Source -eq $module_name } 62 | 63 | if ($functions.Length) { 64 | $functions | ForEach-Object { 65 | [PSCustomObject]@{ Function = $_; Alias = $aliases | Where-Object Definition -eq $_ } 66 | } | ForEach-Object { Write-Host ("`n {0,-20} {1}`n -------- -----" -f 'Function', 'Alias') } { 67 | Write-Host (" {0,-20} {1}" -f $_.Function, "$($_.Alias)") 68 | } 69 | } 70 | 71 | remove-module $module_name 72 | Write-Host "`nTo learn more about ${module_name}: man about_${module_name}" 73 | Write-Host "See help for any function: man updateall`n" 74 | -------------------------------------------------------------------------------- /publish.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5 2 | 3 | param( 4 | [Parameter(Mandatory=$true)] 5 | [ValidateNotNullOrEmpty()] 6 | [string]$Version, 7 | [switch]$Tag, 8 | 9 | [switch]$PSGallery, 10 | [switch]$Github, 11 | [switch]$Chocolatey 12 | ) 13 | 14 | $ErrorActionPreference = 'STOP' 15 | 16 | $p = { 17 | $build_dir = "$PSScriptRoot/_build/$Version" 18 | $module_name = "AU" 19 | $module_path = "$build_dir/$module_name" 20 | $release_notes = get_release_notes 21 | 22 | 23 | if (!(Test-Path $build_dir)) { throw "Build for that version doesn't exist" } 24 | if (!(Get-Command git)) {throw "Git is not installed. Use Chocolatey to install it: cinst git" } 25 | 26 | if (Test-Path $PSScriptRoot/vars.ps1) { . $PSScriptRoot/vars.ps1 } 27 | 28 | git_tag 29 | 30 | Publish-Github 31 | Publish-PSGallery 32 | Publish-Chocolatey 33 | } 34 | 35 | function git_tag() { 36 | if (!$Tag) { Write-Host "Creating git tag disabled"; return } 37 | Write-Host "Creating git tag for version $version" 38 | 39 | Push-Location $PSScriptRoot 40 | git status 41 | git tag $version 42 | git push --tags 43 | Pop-Location 44 | } 45 | 46 | 47 | function get_release_notes() { 48 | $changelog_path = Resolve-Path $PSScriptRoot\CHANGELOG.md 49 | 50 | $clog = Get-Content $changelog_path -Raw 51 | $res = $clog -match "(?<=## $version)(.|\n)+?(?=\n## )" 52 | if (!$res) { throw "Version $version header can't be found in the CHANGELOG.md" } 53 | $Matches[0] 54 | } 55 | 56 | function Publish-Github() { 57 | 58 | #[System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor [System.Net.SecurityProtocolType]::Tls -bor [System.Net.SecurityProtocolType]::Ssl3 59 | 60 | if (!$Github) { Write-Host "Github publish disabled."; return } 61 | Write-Host 'Publishing to Github' 62 | 63 | 'Github_UserRepo', 'Github_ApiKey' | test-var 64 | $params = @{ 65 | Github_UserRepo = $Env:Github_UserRepo 66 | Github_ApiKey = $Env:Github_ApiKey 67 | TagName = $version 68 | ReleaseNotes = $release_notes 69 | Artifacts = "$build_dir/*.nupkg", "$build_dir/*.7z" 70 | } 71 | . $PSScriptRoot/scripts/Github-CreateRelease.ps1 @params 72 | } 73 | 74 | function Publish-PSGallery() { 75 | if (!$PSGallery) { Write-Host "Powershell Gallery publish disabled."; return } 76 | Write-Host 'Publishing to Powershell Gallery' 77 | 78 | 'NuGet_ApiKey' | test-var 79 | $params = @{ 80 | Path = $module_path 81 | NuGetApiKey = $Env:NuGet_ApiKey 82 | } 83 | Publish-Module @params -Verbose 84 | } 85 | 86 | function Publish-Chocolatey() { 87 | if (!$Chocolatey) { Write-Host "Chocolatey publish disabled."; return } 88 | Write-Verbose 'Publishing to Chocolatey' 89 | 90 | 'Chocolatey_ApiKey' | test-var 91 | choco push (Resolve-Path $build_dir/*.$version.nupkg) --api-key $Env:Chocolatey_ApiKey 92 | if ($LastExitCode) {throw "Chocolatey push failed with exit code: $LastExitCode"} 93 | } 94 | 95 | function test-var() { 96 | $input | ForEach-Object { if (!(Test-Path Env:$_)) {throw "Environment Variable $_ must be set"} } 97 | } 98 | 99 | & $p 100 | -------------------------------------------------------------------------------- /scripts/Create-ModuleManifest.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [Parameter(Mandatory = $true)] 4 | [String] $ModulePath, 5 | 6 | [Parameter(Mandatory = $true)] 7 | [Version] $Version 8 | ) 9 | 10 | $module_name = Split-Path -Leaf $ModulePath 11 | 12 | Write-Verbose "Getting public module functions" 13 | $functions = Get-ChildItem $ModulePath\Public\*.ps1 | ForEach-Object { $_.Name -replace '\.ps1$' } 14 | if ($functions.Count -eq 0) { throw 'No public functions to export' } 15 | 16 | Write-Verbose "Getting public module aliases" 17 | try { import-module $ModulePath -force } catch { throw $_ } 18 | $aliases = Get-Alias | Where-Object { $_.Source -eq $module_name -and ($functions -contains $_.Definition) } 19 | 20 | Write-Verbose "Generating module manifest" 21 | $params = @{ 22 | Guid = 'b2cb6770-ecc4-4a51-a57a-3a34654a0938' 23 | Author = 'Miodrag Milic' 24 | PowerShellVersion = '5.0' 25 | Description = 'Chocolatey Automatic Package Updater Module' 26 | HelpInfoURI = 'https://github.com/majkinetor/au/blob/master/README.md' 27 | Tags = 'chocolatey', 'update' 28 | LicenseUri = 'https://www.gnu.org/licenses/gpl-2.0.txt' 29 | ProjectUri = 'https://github.com/majkinetor/au' 30 | ReleaseNotes = 'https://github.com/majkinetor/au/blob/master/CHANGELOG.md' 31 | 32 | ModuleVersion = $Version 33 | FunctionsToExport = $functions 34 | AliasesToExport = $aliases #better then * as each alias is shown in PowerShell Galery 35 | Path = "$ModulePath\$module_name.psd1" 36 | RootModule = "$module_name.psm1" 37 | 38 | } 39 | New-ModuleManifest @params 40 | -------------------------------------------------------------------------------- /scripts/Github-CreateRelease.ps1: -------------------------------------------------------------------------------- 1 | # To create Github token go to Account settings, then goto 'Personal Access Tokens' and make sure token has scope repo/public_repo 2 | 3 | param( 4 | [Parameter(Mandatory=$true)] 5 | [string] $Github_UserRepo, 6 | 7 | [Parameter(Mandatory=$true)] #https://github.com/blog/1509-personal-api-tokens 8 | [string] $Github_ApiKey, 9 | 10 | [Parameter(Mandatory=$true)] 11 | [string] $TagName, 12 | 13 | [string] $ReleaseNotes, 14 | [string[]] $Artifacts 15 | ) 16 | 17 | $ErrorActionPreference = 'STOP' 18 | 19 | "`n==| Creating Github release`n" 20 | 21 | $auth_header = @{ Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($Github_ApiKey + ":x-oauth-basic")) } 22 | 23 | $release_data = @{ 24 | tag_name = $TagName 25 | target_commitish = 'master' #$commitId 26 | name = $TagName 27 | body = $ReleaseNotes 28 | draft = $false 29 | prerelease = $false 30 | } 31 | 32 | $params = @{ 33 | Uri = "https://api.github.com/repos/$Github_UserRepo/releases" 34 | Method = 'POST' 35 | Headers = $auth_header 36 | ContentType = 'application/json' 37 | Body = ConvertTo-Json $release_data 38 | } 39 | 40 | $res = Invoke-RestMethod @params 41 | $res 42 | 43 | if ($Artifacts.Count -eq 0) { return } 44 | 45 | "`n==| Uploading files`n" 46 | foreach ($artifact in $Artifacts) { 47 | if (!$artifact -or !(Test-Path $artifact)) { throw "Artifact not found: $artifact" } 48 | $name = Get-Item $artifact | ForEach-Object Name 49 | 50 | $params = @{ 51 | Uri = ($res.upload_url -replace '{.+}') + "?name=$name" 52 | Method = 'POST' 53 | Headers = $auth_header 54 | ContentType = 'application/zip' 55 | InFile = $artifact 56 | } 57 | Invoke-RestMethod @params 58 | "`n" + "="*80 + "`n" 59 | } 60 | -------------------------------------------------------------------------------- /scripts/Install-AU.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Intall AU from git repository using given version. Can also be used to install development branches. 3 | Git tags are treated as autoritative AU release source. 4 | 5 | This script is used for build server. 6 | #> 7 | 8 | [CmdletBinding()] 9 | param( 10 | # If parsable to [version], exact AU version will be installed. Example: '2016.10.30' 11 | # If not parsable to [version] it is assumed to be name of the AU git branch. Example: 'master' 12 | # If empty string or $null, latest release (git tag) will be installed. 13 | [string] $Version 14 | ) 15 | 16 | $ErrorActionPreference = 'STOP' 17 | $git_url = 'https://github.com/majkinetor/au.git' 18 | 19 | if (!(Get-Command git -ea 0)) { throw 'Git must be installed' } 20 | [version]$git_version = (git --version) -replace 'git|version|\.windows' 21 | if ($git_version -lt [version]2.5) { throw 'Git version must be higher then 2.5' } 22 | 23 | $is_latest = [string]::IsNullOrWhiteSpace($Version) 24 | $is_branch = !($is_latest -or [version]::TryParse($Version, [ref]($_))) 25 | 26 | Push-Location $PSScriptRoot\.. 27 | 28 | if ($is_latest) { $Version = (git tag | ForEach-Object { [version]$_ } | Sort-Object -desc | Select-Object -first 1).ToString() } 29 | if ($is_branch) { 30 | $branches = git branch -r -q | ForEach-Object { $_.Replace('origin/','').Trim() } 31 | if ($branches -notcontains $Version) { throw "AU branch '$Version' doesn't exist" } 32 | if ($Version -ne 'master') { git fetch -q origin "${Version}:${Version}" } 33 | } else { 34 | $tags = git tag 35 | if ($tags -notcontains $Version ) { throw "AU version '$Version' doesn't exist"} 36 | } 37 | 38 | git checkout -q $Version 39 | 40 | $params = @{ Install = $true; NoChocoPackage = $true} 41 | if (!$is_branch) { $params.Version = $Version } 42 | 43 | "Build parameters:" 44 | $params.GetEnumerator() | ForEach-Object { " {0,-20} {1}" -f $_.Key, $_.Value } 45 | ./build.ps1 @params 46 | 47 | Pop-Location 48 | -------------------------------------------------------------------------------- /setup.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5 2 | 3 | $s = { 4 | chocolatey 5 | psgallery 6 | git_4windows 7 | 8 | pester 9 | cinst papercut 10 | } 11 | 12 | function git_4windows() { 13 | if (!(Get-Command git -ea ignore)) { "Installing git"; cinst git } 14 | git --version 15 | } 16 | 17 | function pester() { 18 | "Installing pester" 19 | 20 | inmo pester -Force -MaximumVersion 4.10.1 #3.4.3 21 | $version = Get-Module pester -ListAvailable | ForEach-Object { $_.Version.ToString() } 22 | "Pester version: $version" 23 | } 24 | 25 | function chocolatey() { 26 | "Installing chocolatey" 27 | 28 | Invoke-WebRequest https://chocolatey.org/install.ps1 -UseBasicParsing | Invoke-Expression 29 | "Chocolatey version: $(choco -v)" 30 | } 31 | 32 | function psgallery() { 33 | "Installing PSGallery" 34 | 35 | Install-PackageProvider -Name NuGet -Force 36 | Set-PSRepository -Name PSGallery -InstallationPolicy Trusted 37 | } 38 | 39 | & $s 40 | -------------------------------------------------------------------------------- /test.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [switch]$Chocolatey, 3 | 4 | [switch]$Pester, 5 | [string]$Tag, 6 | [switch]$CodeCoverage 7 | ) 8 | 9 | if (!$Chocolatey -and !$Pester) { $Chocolatey = $Pester = $true } 10 | 11 | $build_dir = Get-Item $PSScriptRoot/_build/* 12 | 13 | if ($Chocolatey) { 14 | Write-Host "`n==| Running Chocolatey tests" 15 | 16 | . $PSScriptRoot/AU/Public/Test-Package.ps1 17 | Test-Package $build_dir 18 | } 19 | 20 | if ($Pester) { 21 | Write-Host "`n==| Running Pester tests" 22 | 23 | $testResultsFile = "$build_dir/TestResults.xml" 24 | if ($CodeCoverage) { 25 | $files = @(Get-ChildItem $PSScriptRoot/AU/* -Filter *.ps1 -Recurse | ForEach-Object FullName) 26 | Invoke-Pester -Tag $Tag -OutputFormat NUnitXml -OutputFile $testResultsFile -PassThru -CodeCoverage $files 27 | } else { 28 | Invoke-Pester -Tag $Tag -OutputFormat NUnitXml -OutputFile $testResultsFile -PassThru 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/AUPackage.Tests.ps1: -------------------------------------------------------------------------------- 1 | remove-module AU -ea ignore 2 | import-module $PSScriptRoot\..\AU 3 | 4 | Describe 'AUPackage' -Tag aupackage { 5 | InModuleScope AU { 6 | It 'throws an error when intanciating without a path' { 7 | { [AUPackage]::new('') } | Should Throw 'empty' 8 | } 9 | 10 | It 'throws an error when intanciating without a hashtable' { 11 | { [AUPackage]::new([hashtable] $null) } | Should Throw 'empty' 12 | } 13 | 14 | It 'can serialize and deserialize' { 15 | $expected = @{ 16 | Path = 'path' 17 | Name = 'name' 18 | Updated = $true 19 | Pushed = $true 20 | RemoteVersion = '1.2.3' 21 | NuspecVersion = '0.1.2' 22 | Result = 'result1,result2,result3' -split ',' 23 | Error = 'error' 24 | NuspecPath = 'nuspecPath' 25 | Ignored = $true 26 | IgnoreMessage = 'ignoreMessage' 27 | StreamsPath = 'streamsPath' 28 | Streams = [PSCustomObject] @{ 29 | '0.1' = @{ 30 | NuspecVersion = '0.1.2' 31 | Path = 'path' 32 | Name = 'name' 33 | Updated = $true 34 | RemoteVersion = '1.2.3' 35 | } 36 | '0.2' = @{ 37 | NuspecVersion = '0.2.2' 38 | Path = 'path' 39 | Name = 'name' 40 | Updated = $true 41 | RemoteVersion = '1.2.3' 42 | } 43 | } 44 | } 45 | 46 | $package = [AUPackage]::new($expected) 47 | $actual = $package.Serialize() 48 | 49 | $expected.Keys | Where-Object { $_ -ne 'Streams' } | ForEach-Object { 50 | $actual.$_ | Should Be $expected.$_ 51 | } 52 | $expected.Streams.psobject.Properties | ForEach-Object { 53 | $actual.Streams.$_ | Should Be $expected.Streams.$_ 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/General.Tests.ps1: -------------------------------------------------------------------------------- 1 | remove-module AU -ea ignore 2 | import-module $PSScriptRoot\..\AU 3 | 4 | Describe 'General' { 5 | $saved_pwd = $pwd 6 | 7 | BeforeEach { 8 | Set-Location TestDrive:\ 9 | Remove-Item -Recurse -Force TestDrive:\test_package -ea ignore 10 | Copy-Item -Recurse -Force $PSScriptRoot\test_package TestDrive:\test_package 11 | } 12 | 13 | It 'considers au_root global variable when looking for packages' { 14 | $path = 'TestDrive:\packages\test_package2' 15 | New-Item -Type Directory $path -Force 16 | Copy-Item -Recurse -Force $PSScriptRoot\test_package\* $path 17 | 18 | $global:au_root = Split-Path $path 19 | $res = lsau 20 | 21 | $res | Should Not BeNullOrEmpty 22 | $res[0].Name | Should Be 'test_package2' 23 | } 24 | 25 | Set-Location $saved_pwd 26 | } 27 | -------------------------------------------------------------------------------- /tests/Get-Version.Tests.ps1: -------------------------------------------------------------------------------- 1 | remove-module AU -ea ignore 2 | import-module $PSScriptRoot\..\AU 3 | 4 | Describe 'Get-Version' -Tag getversion { 5 | InModuleScope AU { 6 | It 'should convert a strict version' { 7 | $expectedVersionStart = '1.2' 8 | $expectedVersion = "$expectedVersionStart.3.4" 9 | # for now, chocolatey does only support SemVer v1 (no dot separated identifiers in pre-release): 10 | $expectedPrerelease = 'beta1' 11 | $expectedBuildMetadata = 'xyz001' 12 | # here is the SemVer v2 equivalent: 13 | #$expectedPrerelease = 'beta.1' 14 | #$expectedBuildMetadata = 'xyz.001' 15 | $expected = "$expectedVersion-$expectedPrerelease+$expectedBuildMetadata" 16 | $res = ConvertTo-AUVersion $expected 17 | 18 | $res | Should Not BeNullOrEmpty 19 | $res.Version | Should Be ([version] $expectedVersion) 20 | $res.Prerelease | Should BeExactly $expectedPrerelease 21 | $res.BuildMetadata | Should BeExactly $expectedBuildMetadata 22 | $res.ToString() | Should BeExactly $expected 23 | $res.ToString(2) | Should BeExactly $expectedVersionStart 24 | $res.ToString(-1) | Should BeExactly $expectedVersion 25 | } 26 | 27 | It 'should not convert a non-strict version' { 28 | { ConvertTo-AUVersion '1.2.3.4a' } | Should Throw 29 | { ConvertTo-AUVersion 'v1.2.3.4-beta.1+xyz.001' } | Should Throw 30 | } 31 | 32 | It 'should parse a non strict version' { 33 | $expectedVersion = "1.2.3.4" 34 | # for now, chocolatey does only support SemVer v1 (no dot separated identifiers in pre-release): 35 | $expectedPrerelease = 'beta1' 36 | $expectedBuildMetadata = 'xyz001' 37 | # here is the SemVer v2 equivalent: 38 | #$expectedPrerelease = 'beta.1' 39 | #$expectedBuildMetadata = 'xyz.001' 40 | $res = Get-Version "v$expectedVersion$expectedPrerelease+$expectedBuildMetadata" 41 | 42 | $res | Should Not BeNullOrEmpty 43 | $res.Version | Should Be ([version] $expectedVersion) 44 | $res.Prerelease | Should BeExactly $expectedPrerelease 45 | $res.BuildMetadata | Should BeExactly $expectedBuildMetadata 46 | } 47 | 48 | $testCases = @( 49 | @{A = '1.9.0' ; B = '1.9.0' ; ExpectedResult = 0} 50 | @{A = '1.9.0' ; B = '1.10.0' ; ExpectedResult = -1} 51 | @{A = '1.10.0' ; B = '1.11.0' ; ExpectedResult = -1} 52 | @{A = '1.0.0' ; B = '2.0.0' ; ExpectedResult = -1} 53 | @{A = '2.0.0' ; B = '2.1.0' ; ExpectedResult = -1} 54 | @{A = '2.1.0' ; B = '2.1.1' ; ExpectedResult = -1} 55 | @{A = '1.0.0-alpha' ; B = '1.0.0-alpha' ; ExpectedResult = 0} 56 | @{A = '1.0.0-alpha' ; B = '1.0.0' ; ExpectedResult = -1} 57 | # for now, chocolatey does only support SemVer v1 (no dot separated identifiers in pre-release): 58 | @{A = '1.0.0-alpha1' ; B = '1.0.0-alpha1' ; ExpectedResult = 0} 59 | @{A = '1.0.0-alpha' ; B = '1.0.0-alpha1' ; ExpectedResult = -1} 60 | @{A = '1.0.0-alpha1' ; B = '1.0.0-alphabeta' ; ExpectedResult = -1} 61 | @{A = '1.0.0-alphabeta' ; B = '1.0.0-beta' ; ExpectedResult = -1} 62 | @{A = '1.0.0-beta' ; B = '1.0.0-beta2' ; ExpectedResult = -1} 63 | @{A = '1.0.0-beta2' ; B = '1.0.0-rc1' ; ExpectedResult = -1} 64 | @{A = '1.0.0-rc1' ; B = '1.0.0' ; ExpectedResult = -1} 65 | # here is the SemVer v2 equivalent: 66 | #@{A = '1.0.0-alpha.1' ; B = '1.0.0-alpha.1' ; ExpectedResult = 0} 67 | #@{A = '1.0.0-alpha.1' ; B = '1.0.0-alpha.01' ; ExpectedResult = 0} 68 | #@{A = '1.0.0-alpha' ; B = '1.0.0-alpha.1' ; ExpectedResult = -1} 69 | #@{A = '1.0.0-alpha.1' ; B = '1.0.0-alpha.beta'; ExpectedResult = -1} 70 | #@{A = '1.0.0-alpha.beta'; B = '1.0.0-beta' ; ExpectedResult = -1} 71 | #@{A = '1.0.0-beta' ; B = '1.0.0-beta.2' ; ExpectedResult = -1} 72 | #@{A = '1.0.0-beta.2' ; B = '1.0.0-beta.11' ; ExpectedResult = -1} 73 | #@{A = '1.0.0-beta.11' ; B = '1.0.0-rc.1' ; ExpectedResult = -1} 74 | #@{A = '1.0.0-rc.1' ; B = '1.0.0' ; ExpectedResult = -1} 75 | @{A = '1.0.0' ; B = '1.0.0+1' ; ExpectedResult = 0} 76 | @{A = '1.0.0+1' ; B = '1.0.0+2' ; ExpectedResult = 0} 77 | @{A = '1.0.0-alpha' ; B = '1.0.0-alpha+1' ; ExpectedResult = 0} 78 | @{A = '1.0.0-alpha+1' ; B = '1.0.0-alpha+2' ; ExpectedResult = 0} 79 | ) 80 | 81 | It 'should compare 2 versions successfully' -TestCases $testCases { param([string] $A, [string] $B, [int] $ExpectedResult) 82 | $VersionA = ConvertTo-AUVersion $A 83 | $VersionB = ConvertTo-AUVersion $B 84 | if ($ExpectedResult -gt 0 ) { 85 | $VersionA | Should BeGreaterThan $VersionB 86 | } elseif ($ExpectedResult -lt 0 ) { 87 | $VersionA | Should BeLessThan $VersionB 88 | } else { 89 | $VersionA | Should Be $VersionB 90 | } 91 | } 92 | 93 | $testCases = @( 94 | @{Value = '1.2'} 95 | @{Value = '1.2-beta+003'} 96 | @{Value = [AUVersion] '1.2'} 97 | @{Value = [AUVersion] '1.2-beta+003'} 98 | @{Value = [version] '1.2'} 99 | @{Value = [regex]::Match('1.2', '^(.+)$').Groups[1]} 100 | @{Value = [regex]::Match('1.2-beta+003', '^(.+)$').Groups[1]} 101 | ) 102 | 103 | It 'converts from any type of values' -TestCases $testCases { param($Value) 104 | $version = [AUVersion] $Value 105 | $version | Should Not BeNullOrEmpty 106 | } 107 | 108 | $testCases = @( 109 | @{Value = '1.2-beta.3'} 110 | @{Value = '1.2+xyz.4'} 111 | @{Value = '1.2-beta.3+xyz.4'} 112 | ) 113 | 114 | It 'does not convert semver v2' -TestCases $testCases { param($Value, $ExpectedResult) 115 | { [AUVersion] $Value } | Should Throw 'Invalid version' 116 | } 117 | 118 | $testCases = @( 119 | @{ExpectedResult = '5.4.9' ; Delimiter = '-' ; Value = 'http://dl.airserver.com/pc32/AirServer-5.4.9-x86.msi'} 120 | @{ExpectedResult = '1.24.0-beta2' ; Value = 'https://github.com/atom/atom/releases/download/v1.24.0-beta2/AtomSetup.exe'} 121 | @{ExpectedResult = '2.4.0.24-beta' ; Value = 'https://github.com/gurnec/HashCheck/releases/download/v2.4.0.24-beta/HashCheckSetup-v2.4.0.24-beta.exe'} 122 | @{ExpectedResult = '2.0.9' ; Value = 'http://www.ltr-data.se/files/imdiskinst_2.0.9.exe'} 123 | @{ExpectedResult = '17.6' ; Delimiter = '-' ; Value = 'http://mirrors.kodi.tv/releases/windows/win32/kodi-17.6-Krypton-x86.exe'} 124 | @{ExpectedResult = '0.70.2' ; Value = 'https://github.com/Nevcairiel/LAVFilters/releases/download/0.70.2/LAVFilters-0.70.2-Installer.exe'} 125 | @{ExpectedResult = '2.2.0-1' ; Value = 'https://files.kde.org/marble/downloads/windows/Marble-setup_2.2.0-1_x64.exe'} 126 | @{ExpectedResult = '2.3.2' ; Value = 'https://github.com/sabnzbd/sabnzbd/releases/download/2.3.2/SABnzbd-2.3.2-win-setup.exe'} 127 | @{ExpectedResult = '1.9' ; Delimiter = '-' ; Value = 'http://download.serviio.org/releases/serviio-1.9-win-setup.exe'} 128 | @{ExpectedResult = '0.17.0' ; Value = 'https://github.com/Stellarium/stellarium/releases/download/v0.17.0/stellarium-0.17.0-win32.exe'} 129 | @{ExpectedResult = '5.24.3.1' ; Value = 'http://strawberryperl.com/download/5.24.3.1/strawberry-perl-5.24.3.1-32bit.msi'} 130 | @{ExpectedResult = '3.5.4' ; Value = 'https://github.com/SubtitleEdit/subtitleedit/releases/download/3.5.4/SubtitleEdit-3.5.4-Setup.zip'} 131 | # for now, chocolatey does only support SemVer v1 (no dot separated identifiers in pre-release): 132 | @{ExpectedResult = '1.2.3-beta4' ; Value = 'v 1.2.3 beta 4'} 133 | @{ExpectedResult = '1.2.3-beta3' ; Value = 'Last version: 1.2.3 beta 3.'} 134 | # here is the SemVer v2 equivalent: 135 | #@{ExpectedResult = '1.2.3-beta.4' ; Value = 'v 1.2.3 beta 4'} 136 | #@{ExpectedResult = '1.2.3-beta.3' ; Value = 'Last version: 1.2.3 beta 3.'} 137 | ) 138 | 139 | It 'should parse any non strict version' -TestCases $testCases { param($Value, $Delimiter, $ExpectedResult) 140 | $version = Get-Version $Value -Delimiter $Delimiter 141 | $version | Should Be ([AUVersion] $ExpectedResult) 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /tests/Update-AUPackages.Streams.Tests.ps1: -------------------------------------------------------------------------------- 1 | remove-module AU -ea ignore 2 | import-module $PSScriptRoot\..\AU 3 | 4 | Describe 'Update-AUPackages using streams' -Tag updateallstreams { 5 | $saved_pwd = $pwd 6 | 7 | function global:nuspec_file() { [xml](Get-Content $PSScriptRoot/test_package_with_streams/test_package_with_streams.nuspec) } 8 | $pkg_no = 2 9 | $streams_no = $pkg_no * 3 10 | 11 | BeforeEach { 12 | $global:au_Root = "TestDrive:\packages" 13 | $global:au_NoPlugins = $true 14 | 15 | Remove-Item -Recurse $global:au_root -ea ignore 16 | foreach ( $i in 1..$pkg_no ) { 17 | $name = "test_package_with_streams_$i" 18 | $path = "$au_root\$name" 19 | 20 | Copy-Item -Recurse -Force $PSScriptRoot\test_package_with_streams $path 21 | $nu = nuspec_file 22 | $nu.package.metadata.id = $name 23 | Remove-Item "$path\*.nuspec" 24 | $nu.OuterXml | Set-Content "$path\$name.nuspec" 25 | Move-Item "$path\test_package_with_streams.json" "$path\$name.json" 26 | 27 | $module_path = Resolve-Path $PSScriptRoot\..\AU 28 | "import-module '$module_path' -Force", (Get-Content $path\update.ps1 -ea ignore) | Set-Content $path\update.ps1 29 | } 30 | 31 | $Options = [ordered]@{} 32 | } 33 | 34 | Context 'Plugins' { 35 | It 'should ignore the package that returns "ignore"' { 36 | Get-Content $global:au_Root\test_package_with_streams_1\update.ps1 | Set-Variable content 37 | $content -replace 'update', "Write-Host 'test ignore'; 'ignore'" | Set-Variable content 38 | $content | Set-Content $global:au_Root\test_package_with_streams_1\update.ps1 39 | 40 | $res = updateall -Options $Options -NoPlugins:$false 6>$null 41 | 42 | $res[0].Ignored | Should Be $true 43 | $res[0].IgnoreMessage | Should Be 'test ignore' 44 | } 45 | 46 | It 'should execute text Report plugin' { 47 | Get-Content $global:au_Root\test_package_with_streams_1\update.ps1 | Set-Variable content 48 | $content -replace '@\{.+1\.3.+\}', "@{ Version = '1.3.2' }" | Set-Variable content 49 | $content -replace '@\{.+1\.2.+\}', "@{ Version = '1.2.4' }" | Set-Variable content 50 | $content | Set-Content $global:au_Root\test_package_with_streams_1\update.ps1 51 | 52 | $Options.Report = @{ 53 | Type = 'text' 54 | Path = "$global:au_Root\report.txt" 55 | } 56 | 57 | $res = updateall -NoPlugins:$false -Options $Options 6> $null 58 | 59 | $pattern = "\bFinished $pkg_no packages\b[\S\s]*" 60 | $pattern += '\b1 updated\b[\S\s]*' 61 | $pattern += '\b0 errors\b[\S\s]*' 62 | $pattern += '\btest_package_with_streams_1 +True +1\.3\.2 +1\.3\.1\b[\S\s]*' 63 | $pattern += "\btest_package_with_streams_2 +False +1\.4-beta1 +1\.4-beta1\b[\S\s]*" 64 | $pattern += '\btest_package_with_streams_1\b[\S\s]*' 65 | $pattern += '\bStream: 1\.2\b[\S\s]*' 66 | $pattern += '\bnuspec version: 1\.2\.3\b[\S\s]*' 67 | $pattern += '\bremote version: 1\.2\.4\b[\S\s]*' 68 | $pattern += '\bNew version is available\b[\S\s]*' 69 | $pattern += '\bStream: 1\.3\b[\S\s]*' 70 | $pattern += '\bnuspec version: 1\.3\.1\b[\S\s]*' 71 | $pattern += '\bremote version: 1\.3\.2\b[\S\s]*' 72 | $pattern += '\bNew version is available\b[\S\s]*' 73 | $pattern += '\bStream: 1\.4\b[\S\s]*' 74 | $pattern += '\bnuspec version: 1\.4-beta1\b[\S\s]*' 75 | $pattern += '\bremote version: 1\.4-beta1\b[\S\s]*' 76 | $pattern += '\bNo new version found\b[\S\s]*' 77 | $pattern += '\bPackage updated\b[\S\s]*' 78 | $pattern += '\btest_package_with_streams_2\b[\S\s]*' 79 | $pattern += '\bStream: 1\.2\b[\S\s]*' 80 | $pattern += '\bnuspec version: 1\.2\.3\b[\S\s]*' 81 | $pattern += '\bremote version: 1\.2\.3\b[\S\s]*' 82 | $pattern += '\bNo new version found\b[\S\s]*' 83 | $pattern += '\bStream: 1\.3\b[\S\s]*' 84 | $pattern += '\bnuspec version: 1\.3\.1\b[\S\s]*' 85 | $pattern += '\bremote version: 1\.3\.1\b[\S\s]*' 86 | $pattern += '\bNo new version found\b[\S\s]*' 87 | $pattern += '\bStream: 1\.4\b[\S\s]*' 88 | $pattern += '\bnuspec version: 1\.4-beta1\b[\S\s]*' 89 | $pattern += '\bremote version: 1\.4-beta1\b[\S\s]*' 90 | $pattern += '\bNo new version found\b[\S\s]*' 91 | $Options.Report.Path | Should Exist 92 | $Options.Report.Path | Should FileContentMatchMultiline $pattern 93 | } 94 | 95 | It 'should execute markdown Report plugin' { 96 | Get-Content $global:au_Root\test_package_with_streams_1\update.ps1 | Set-Variable content 97 | $content -replace '@\{.+1\.3.+\}', "@{ Version = '1.3.2' }" | Set-Variable content 98 | $content -replace '@\{.+1\.2.+\}', "@{ Version = '1.2.4' }" | Set-Variable content 99 | $content | Set-Content $global:au_Root\test_package_with_streams_1\update.ps1 100 | 101 | $Options.Report = @{ 102 | Type = 'markdown' 103 | Path = "$global:au_Root\report.md" 104 | Params = @{ Github_UserRepo = 'majkinetor/chocolatey' } 105 | } 106 | 107 | $res = updateall -NoPlugins:$false -Options $Options 6> $null 108 | 109 | $pattern = "\bFinished $pkg_no packages\b[\S\s]*" 110 | $pattern += '\b1 updated\b[\S\s]*' 111 | $pattern += '\b0 errors\b[\S\s]*' 112 | $pattern += '\btest_package_with_streams_1\b.*\bTrue\b.*\bFalse\b.*\b1\.3\.2\b.*\b1\.3\.1\b[\S\s]*' 113 | $pattern += "\btest_package_with_streams_2\b.*\bFalse\b.*\bFalse\b.*\b1\.4-beta1\b.*\b1\.4-beta1\b[\S\s]*" 114 | $pattern += '\btest_package_with_streams_1\b[\S\s]*' 115 | $pattern += '\bStream: 1\.2\b[\S\s]*' 116 | $pattern += '\bnuspec version: 1\.2\.3\b[\S\s]*' 117 | $pattern += '\bremote version: 1\.2\.4\b[\S\s]*' 118 | $pattern += '\bNew version is available\b[\S\s]*' 119 | $pattern += '\bStream: 1\.3\b[\S\s]*' 120 | $pattern += '\bnuspec version: 1\.3\.1\b[\S\s]*' 121 | $pattern += '\bremote version: 1\.3\.2\b[\S\s]*' 122 | $pattern += '\bNew version is available\b[\S\s]*' 123 | $pattern += '\bStream: 1\.4\b[\S\s]*' 124 | $pattern += '\bnuspec version: 1\.4-beta1\b[\S\s]*' 125 | $pattern += '\bremote version: 1\.4-beta1\b[\S\s]*' 126 | $pattern += '\bNo new version found\b[\S\s]*' 127 | $pattern += '\bPackage updated\b[\S\s]*' 128 | $pattern += '\btest_package_with_streams_2\b[\S\s]*' 129 | $pattern += '\bStream: 1\.2\b[\S\s]*' 130 | $pattern += '\bnuspec version: 1\.2\.3\b[\S\s]*' 131 | $pattern += '\bremote version: 1\.2\.3\b[\S\s]*' 132 | $pattern += '\bNo new version found\b[\S\s]*' 133 | $pattern += '\bStream: 1\.3\b[\S\s]*' 134 | $pattern += '\bnuspec version: 1\.3\.1\b[\S\s]*' 135 | $pattern += '\bremote version: 1\.3\.1\b[\S\s]*' 136 | $pattern += '\bNo new version found\b[\S\s]*' 137 | $pattern += '\bStream: 1\.4\b[\S\s]*' 138 | $pattern += '\bnuspec version: 1\.4-beta1\b[\S\s]*' 139 | $pattern += '\bremote version: 1\.4-beta1\b[\S\s]*' 140 | $pattern += '\bNo new version found\b[\S\s]*' 141 | $Options.Report.Path | Should Exist 142 | $Options.Report.Path | Should FileContentMatchMultiline $pattern 143 | } 144 | 145 | It 'should execute GitReleases plugin when there are updates' { 146 | Get-Content $global:au_Root\test_package_with_streams_1\update.ps1 | Set-Variable content 147 | $content -replace '@\{.+1\.3.+\}', "@{ Version = '1.3.2' }" | Set-Variable content 148 | $content -replace '@\{.+1\.2.+\}', "@{ Version = '1.2.4' }" | Set-Variable content 149 | $content | Set-Content $global:au_Root\test_package_with_streams_1\update.ps1 150 | 151 | $Options.GitReleases = @{ 152 | ApiToken = 'apiToken' 153 | ReleaseType = 'package' 154 | Force = $true 155 | } 156 | 157 | Mock Invoke-RestMethod { 158 | return @{ 159 | tag_name = 'test_package_with_streams_1-1.2.4' 160 | assets = @( 161 | @{ 162 | url = 'https://api.github.com/test_package_with_streams_1.1.2.4.nupkg' 163 | name = 'test_package_with_streams_1.1.2.4.nupkg' 164 | } 165 | ) 166 | } 167 | } -ModuleName AU 168 | 169 | updateall -NoPlugins:$false -Options $Options 6> $null 170 | 171 | Assert-MockCalled Invoke-RestMethod -Exactly 6 -ModuleName AU 172 | } 173 | } 174 | 175 | It 'should update package with checksum verification mode' { 176 | 177 | $choco_path = Get-Command choco.exe | ForEach-Object Source 178 | $choco_hash = Get-FileHash $choco_path -Algorithm SHA256 | ForEach-Object Hash 179 | Get-Content $global:au_Root\test_package_with_streams_1\update.ps1 | Set-Variable content 180 | $content -replace '@\{.+1\.3.+\}', "@{ Version = '1.3.2'; ChecksumType32 = 'sha256'; Checksum32 = '$choco_hash'}" | Set-Variable content 181 | $content -replace 'update', "update -ChecksumFor 32" | Set-Variable content 182 | $content | Set-Content $global:au_Root\test_package_with_streams_1\update.ps1 183 | 184 | $res = updateall -Options $Options 6> $null 185 | $res.Count | Should Be $pkg_no 186 | $res[0].Updated | Should Be $true 187 | } 188 | 189 | It 'should limit update time' { 190 | Get-Content $global:au_Root\test_package_with_streams_1\update.ps1 | Set-Variable content 191 | $content -replace 'update', "sleep 10; update" | Set-Variable content 192 | $content | Set-Content $global:au_Root\test_package_with_streams_1\update.ps1 193 | $Options.UpdateTimeout = 5 194 | 195 | $res = updateall -Options $Options 3>$null 6> $null 196 | $res[0].Error -eq "Job terminated due to the 5s UpdateTimeout" | Should Be $true 197 | } 198 | 199 | It 'should update all packages when forced' { 200 | $Options.Force = $true 201 | 202 | $res = updateall -Options $Options 6> $null 203 | 204 | lsau | Measure-Object | ForEach-Object Count | Should Be $pkg_no 205 | $res.Count | Should Be $pkg_no 206 | ($res.Result -match 'update is forced').Count | Should Be $pkg_no 207 | ($res | Where-Object Updated).Count | Should Be $pkg_no 208 | } 209 | 210 | It 'should update no packages when none is newer' { 211 | $res = updateall 6> $null 212 | 213 | lsau | Measure-Object | ForEach-Object Count | Should Be $pkg_no 214 | $res.Count | Should Be $pkg_no 215 | ($res.Result -match 'No new version found').Count | Should Be $streams_no 216 | ($res | Where-Object Updated).Count | Should Be 0 217 | } 218 | 219 | $saved_pwd = $pwd 220 | } 221 | 222 | -------------------------------------------------------------------------------- /tests/Update-AUPackages.Tests.ps1: -------------------------------------------------------------------------------- 1 | remove-module AU -ea ignore 2 | import-module $PSScriptRoot\..\AU 3 | 4 | Describe 'Update-AUPackages' -Tag updateall { 5 | $saved_pwd = $pwd 6 | 7 | function global:nuspec_file() { [xml](Get-Content $PSScriptRoot/test_package/test_package.nuspec) } 8 | $pkg_no = 3 9 | 10 | BeforeEach { 11 | $global:au_Root = "TestDrive:\packages" 12 | $global:au_NoPlugins = $true 13 | 14 | Remove-Item -Recurse $global:au_root -ea ignore 15 | foreach ( $i in 1..$pkg_no ) { 16 | $name = "test_package_$i" 17 | $path = "$au_root\$name" 18 | 19 | Copy-Item -Recurse -Force $PSScriptRoot\test_package $path 20 | $nu = nuspec_file 21 | $nu.package.metadata.id = $name 22 | Remove-Item "$au_root\$name\*.nuspec" 23 | $nu.OuterXml | Set-Content "$path\$name.nuspec" 24 | 25 | $module_path = Resolve-Path $PSScriptRoot\..\AU 26 | "import-module '$module_path' -Force", (Get-Content $path\update.ps1 -ea ignore) | Set-Content $path\update.ps1 27 | } 28 | 29 | $Options = [ordered]@{} 30 | } 31 | 32 | Context 'Plugins' { 33 | # Commented tests are invoked manually 34 | 35 | #It 'should execute Gist plugin' { 36 | #$Options.Report = @{ 37 | #Type = 'text' 38 | #Path = "$au_root\report.txt" 39 | #} 40 | #$Options.RunInfo = @{ 41 | #Path = "$au_root\runinfo.xml" 42 | #} 43 | #$Options.Gist = @{ 44 | #Path = "$au_root\*.*" 45 | #} 46 | 47 | #$res = updateall -NoPlugins:$false -Options $Options 48 | #} 49 | 50 | #It 'should execute Mail plugin' { 51 | #$Options.Report = @{ 52 | #Type = 'text' 53 | #Path = "$global:au_Root\report.txt" 54 | #} 55 | 56 | #$Options.Mail = @{ 57 | #To = 'test@localhost' 58 | #Server = 'localhost' 59 | #UserName = 'test_user' 60 | #Password = 'test_pass' 61 | #Port = 25 62 | #EnableSsl = $true 63 | #Attachment = ("$global:au_Root\report.txt" -replace 'TestDrive:', $TestDrive) 64 | #SendAlways = $true 65 | #} 66 | 67 | #if (!(ps papercut -ea ignore)) { 68 | #if (gcm papercut.exe -ea ignore) { start papercut.exe; sleep 5 } 69 | #else { Write-Warning 'Papercut is not installed - skipping test'; return } 70 | #} 71 | #rm $Env:APPDATA\Papercut\* -ea ignore 72 | #$res = updateall -NoPlugins:$false -Options $Options 6> $null 73 | 74 | #sleep 5 75 | #(ls $Env:APPDATA\Papercut\*).Count | Should Be 1 76 | #} 77 | 78 | 79 | It 'should ignore the package that returns "ignore"' { 80 | Get-Content $global:au_Root\test_package_1\update.ps1 | Set-Variable content 81 | $content -replace 'update', "Write-Host 'test ignore'; 'ignore'" | Set-Variable content 82 | $content | Set-Content $global:au_Root\test_package_1\update.ps1 83 | 84 | $res = updateall -Options $Options -NoPlugins:$false 6>$null 85 | 86 | $res[0].Ignored | Should Be $true 87 | $res[0].IgnoreMessage | Should Be 'test ignore' 88 | } 89 | 90 | It 'should repeat and ignore on specific error' { 91 | Get-Content $global:au_Root\test_package_1\update.ps1 | Set-Variable content 92 | $content -replace 'update', "1|Out-File -Append $TestDrive\tmp_test; throw 'test ignore'; update" | Set-Variable content 93 | $content | Set-Content $global:au_Root\test_package_1\update.ps1 94 | 95 | $Options.RepeatOn = @('test ignore') 96 | $Options.RepeatCount = 2 97 | $Options.IgnoreOn = @('test ignore') 98 | 99 | $res = updateall -Options $Options -NoPlugins:$false 6>$null 100 | 101 | $res[0].Ignored | Should Be $true 102 | $res[0].IgnoreMessage | Should BeLike 'AU ignored on*test ignore' 103 | 104 | (Get-Content $TestDrive\tmp_test).Count | Should be 3 105 | } 106 | 107 | It 'should execute text Report plugin' { 108 | Get-Content $global:au_Root\test_package_1\update.ps1 | Set-Variable content 109 | $content -replace '@\{.+\}', "@{ Version = '1.3' }" | Set-Variable content 110 | $content | Set-Content $global:au_Root\test_package_1\update.ps1 111 | 112 | $Options.Report = @{ 113 | Type = 'text' 114 | Path = "$global:au_Root\report.txt" 115 | } 116 | 117 | $res = updateall -NoPlugins:$false -Options $Options 6> $null 118 | 119 | $pattern = "\bFinished $pkg_no packages\b[\S\s]*" 120 | $pattern += '\b1 updated\b[\S\s]*' 121 | $pattern += '\b0 errors\b[\S\s]*' 122 | $pattern += '\btest_package_1 +True +1\.3 +1\.2\.3\b[\S\s]*' 123 | foreach ( $i in 2..$pkg_no ) { 124 | $pattern += "\btest_package_$i +False +1\.2\.3 +1\.2\.3\b[\S\s]*" 125 | } 126 | $pattern += '\btest_package_1\b[\S\s]*' 127 | $pattern += '\bnuspec version: 1\.2\.3\b[\S\s]*' 128 | $pattern += '\bremote version: 1\.3\b[\S\s]*' 129 | $pattern += '\bNew version is available\b[\S\s]*' 130 | $pattern += '\bPackage updated\b[\S\s]*' 131 | foreach ( $i in 2..$pkg_no ) { 132 | $pattern += "\btest_package_$i\b[\S\s]*" 133 | $pattern += '\bnuspec version: 1\.2\.3\b[\S\s]*' 134 | $pattern += '\bremote version: 1\.2\.3\b[\S\s]*' 135 | $pattern += '\bNo new version found\b[\S\s]*' 136 | } 137 | $Options.Report.Path | Should Exist 138 | $Options.Report.Path | Should FileContentMatchMultiline $pattern 139 | } 140 | 141 | It 'should execute markdown Report plugin' { 142 | Get-Content $global:au_Root\test_package_1\update.ps1 | Set-Variable content 143 | $content -replace '@\{.+\}', "@{ Version = '1.3' }" | Set-Variable content 144 | $content | Set-Content $global:au_Root\test_package_1\update.ps1 145 | 146 | $Options.Report = @{ 147 | Type = 'markdown' 148 | Path = "$global:au_Root\report.md" 149 | Params = @{ Github_UserRepo = 'majkinetor/chocolatey' } 150 | } 151 | 152 | $res = updateall -NoPlugins:$false -Options $Options 6> $null 153 | 154 | $pattern = "\bFinished $pkg_no packages\b[\S\s]*" 155 | $pattern += '\b1 updated\b[\S\s]*' 156 | $pattern += '\b0 errors\b[\S\s]*' 157 | $pattern += '\btest_package_1\b.*\bTrue\b.*\bFalse\b.*\b1\.3\b.*\b1\.2\.3\b[\S\s]*' 158 | foreach ( $i in 2..$pkg_no ) { 159 | $pattern += "\btest_package_$i\b.*\bFalse\b.*\bFalse\b.*\b1\.2\.3\b.*\b1\.2\.3\b[\S\s]*" 160 | } 161 | $pattern += '\btest_package_1\b[\S\s]*' 162 | $pattern += '\bnuspec version: 1\.2\.3\b[\S\s]*' 163 | $pattern += '\bremote version: 1\.3\b[\S\s]*' 164 | $pattern += '\bNew version is available\b[\S\s]*' 165 | $pattern += '\bPackage updated\b[\S\s]*' 166 | foreach ( $i in 2..$pkg_no ) { 167 | $pattern += "\btest_package_$i\b[\S\s]*" 168 | $pattern += '\bnuspec version: 1\.2\.3\b[\S\s]*' 169 | $pattern += '\bremote version: 1\.2\.3\b[\S\s]*' 170 | $pattern += '\bNo new version found\b[\S\s]*' 171 | } 172 | $Options.Report.Path | Should Exist 173 | $Options.Report.Path | Should FileContentMatchMultiline $pattern 174 | } 175 | 176 | It 'should execute RunInfo plugin' { 177 | $Options.RunInfo = @{ 178 | Path = "$global:au_Root\update_info.xml" 179 | Exclude = 'password' 180 | } 181 | $Options.Test = @{ 182 | MyPassword = 'password' 183 | Parameter2 = 'p2'` 184 | } 185 | 186 | $res = updateall -NoPlugins:$false -Options $Options 6> $null 187 | 188 | Test-Path $Options.RunInfo.Path | Should Be $true 189 | $info = Import-Clixml $Options.RunInfo.Path 190 | $info.plugin_results.RunInfo -match 'Test.MyPassword' | Should Be $true 191 | $info.Options.Test.MyPassword | Should Be '*****' 192 | } 193 | 194 | It 'should not execute GitReleases plugin when there are no updates' { 195 | $Options.GitReleases = @{ 196 | ApiToken = 'apiToken' 197 | ReleaseType = 'package' 198 | Force = $true 199 | } 200 | 201 | Mock -ModuleName AU Invoke-RestMethod {} 202 | 203 | updateall -NoPlugins:$false -Options $Options 6> $null 204 | 205 | Assert-MockCalled -ModuleName AU Invoke-RestMethod -Exactly 0 -Scope It 206 | } 207 | 208 | It 'should execute GitReleases plugin per package when there are updates' { 209 | Get-Content $global:au_Root\test_package_1\update.ps1 | Set-Variable content 210 | $content -replace '@\{.+\}', "@{ Version = '1.3' }" | Set-Variable content 211 | $content | Set-Content $global:au_Root\test_package_1\update.ps1 212 | 213 | $Options.GitReleases = @{ 214 | ApiToken = 'apiToken' 215 | ReleaseType = 'package' 216 | Force = $true 217 | } 218 | 219 | Mock -ModuleName AU Invoke-RestMethod { 220 | return @{ 221 | tag_name = 'test_package_1-1.3' 222 | assets = @( 223 | @{ 224 | url = 'https://api.github.com/test_package_1.1.3.nupkg' 225 | name = 'test_package_1.1.3.nupkg' 226 | } 227 | ) 228 | } 229 | } 230 | 231 | updateall -NoPlugins:$false -Options $Options 6> $null 232 | 233 | Assert-MockCalled -ModuleName AU Invoke-RestMethod -Exactly 3 -Scope It 234 | } 235 | 236 | It 'should execute GitReleases plugin per date when there are updates' { 237 | Get-Content $global:au_Root\test_package_1\update.ps1 | Set-Variable content 238 | $content -replace '@\{.+\}', "@{ Version = '1.3' }" | Set-Variable content 239 | $content | Set-Content $global:au_Root\test_package_1\update.ps1 240 | 241 | $Options.GitReleases = @{ 242 | ApiToken = 'apiToken' 243 | ReleaseType = 'date' 244 | Force = $true 245 | } 246 | 247 | Mock -ModuleName AU Get-Date { return '2017-11-05' } -ParameterFilter { $UFormat -eq '{0:yyyy-MM-dd}' } 248 | Mock -ModuleName AU Invoke-RestMethod { return @{ tag_name = '2017-11-05' } } 249 | 250 | updateall -NoPlugins:$false -Options $Options 6> $null 251 | 252 | Assert-MockCalled -ModuleName AU Get-Date -Exactly 1 -Scope It 253 | Assert-MockCalled -ModuleName AU Invoke-RestMethod -Exactly 2 -Scope It 254 | } 255 | } 256 | 257 | It 'should update package with checksum verification mode' { 258 | 259 | $choco_path = Get-Command choco.exe | ForEach-Object Source 260 | $choco_hash = Get-FileHash $choco_path -Algorithm SHA256 | ForEach-Object Hash 261 | Get-Content $global:au_Root\test_package_1\update.ps1 | Set-Variable content 262 | $content -replace '@\{.+\}', "@{ Version = '1.3'; ChecksumType32 = 'sha256'; Checksum32 = '$choco_hash'}" | Set-Variable content 263 | $content -replace 'update', "update -ChecksumFor 32" | Set-Variable content 264 | $content | Set-Content $global:au_Root\test_package_1\update.ps1 265 | 266 | $res = updateall -Options $Options 6> $null 267 | $res.Count | Should Be $pkg_no 268 | $res[0].Updated | Should Be $true 269 | } 270 | 271 | It 'should limit update time' { 272 | Get-Content $global:au_Root\test_package_1\update.ps1 | Set-Variable content 273 | $content -replace 'update', "sleep 10; update" | Set-Variable content 274 | $content | Set-Content $global:au_Root\test_package_1\update.ps1 275 | $Options.UpdateTimeout = 5 276 | 277 | $res = updateall -Options $Options 3>$null 6> $null 278 | $res[0].Error -eq "Job terminated due to the 5s UpdateTimeout" | Should Be $true 279 | } 280 | 281 | It 'should update all packages when forced' { 282 | $Options.Force = $true 283 | 284 | $res = updateall -Options $Options 6> $null 285 | 286 | lsau | Measure-Object | ForEach-Object Count | Should Be $pkg_no 287 | $res.Count | Should Be $pkg_no 288 | ($res.Result -match 'update is forced').Count | Should Be $pkg_no 289 | ($res | Where-Object Updated).Count | Should Be $pkg_no 290 | } 291 | 292 | It 'should update no packages when none is newer' { 293 | $res = updateall 6> $null 294 | 295 | lsau | Measure-Object | ForEach-Object Count | Should Be $pkg_no 296 | $res.Count | Should Be $pkg_no 297 | ($res.Result -match 'No new version found').Count | Should Be $pkg_no 298 | ($res | Where-Object Updated).Count | Should Be 0 299 | } 300 | 301 | $saved_pwd = $pwd 302 | } 303 | 304 | -------------------------------------------------------------------------------- /tests/Update-Package.Streams.Tests.ps1: -------------------------------------------------------------------------------- 1 | remove-module AU -ea ignore 2 | import-module $PSScriptRoot\..\AU -force 3 | 4 | Describe 'Update-Package using streams' -Tag updatestreams { 5 | $saved_pwd = $pwd 6 | 7 | function global:get_latest([string] $Version, [string] $URL32, [string] $Checksum32) { 8 | $streams = @{ 9 | '1.4' = @{ Version = '1.4-beta1'; URL32 = 'test.1.4-beta1' } 10 | '1.3' = @{ Version = '1.3.1'; URL32 = 'test.1.3.1' } 11 | '1.2' = @{ Version = '1.2.4'; URL32 = 'test.1.2.4' } 12 | } 13 | if ($Version) { 14 | $stream = (ConvertTo-AUVersion $Version).ToString(2) 15 | if (!$URL32) { 16 | $URL32 = if ($streams.$stream) { $streams.$stream.URL32 } else { "test.$Version" } 17 | } 18 | $streams.Remove($stream) 19 | $s = @{ Version = $Version; URL32 = $URL32 } 20 | if ($Checksum32) { $s += @{ Checksum32 = $Checksum32 } } 21 | $streams.Add($stream, $s) 22 | } 23 | $command = "function global:au_GetLatest { @{ Fake = 1; Streams = [ordered] @{`n" 24 | foreach ($item in ($streams.Keys| Sort-Object { ConvertTo-AUVersion $_ } -Descending)) { 25 | $command += "'$item' = @{Version = '$($streams.$item.Version)'; URL32 = '$($streams.$item.URL32)'" 26 | if ($streams.$item.Checksum32) { $command += "; Checksum32 = '$($streams.$item.Checksum32)'" } 27 | $command += "}`n" 28 | } 29 | $command += "} } }" 30 | $command | Invoke-Expression 31 | } 32 | 33 | function global:seach_replace() { 34 | "function global:au_SearchReplace { @{} }" | Invoke-Expression 35 | } 36 | 37 | function global:nuspec_file() { [xml](Get-Content TestDrive:\test_package_with_streams\test_package_with_streams.nuspec) } 38 | 39 | function global:json_file() { (Get-Content TestDrive:\test_package_with_streams\test_package_with_streams.json) | ConvertFrom-Json } 40 | 41 | BeforeEach { 42 | Set-Location $TestDrive 43 | Remove-Item -Recurse -Force TestDrive:\test_package_with_streams -ea ignore 44 | Copy-Item -Recurse -Force $PSScriptRoot\test_package_with_streams TestDrive:\test_package_with_streams 45 | Set-Location $TestDrive\test_package_with_streams 46 | 47 | $global:au_Timeout = 100 48 | $global:au_Force = $false 49 | $global:au_IncludeStream = '' 50 | $global:au_NoHostOutput = $true 51 | $global:au_NoCheckUrl = $true 52 | $global:au_NoCheckChocoVersion = $true 53 | $global:au_ChecksumFor = 'none' 54 | $global:au_WhatIf = $false 55 | $global:au_NoReadme = $false 56 | 57 | Remove-Variable -Scope global Latest -ea ignore 58 | 'BeforeUpdate', 'AfterUpdate' | ForEach-Object { Remove-Item "Function:/au_$_" -ea ignore } 59 | get_latest 60 | seach_replace 61 | } 62 | 63 | InModuleScope AU { 64 | 65 | Context 'Updating' { 66 | 67 | It 'can set description from README.md' { 68 | $readme = 'dummy readme & test' 69 | '','', $readme | Out-File $TestDrive\test_package_with_streams\README.md 70 | $res = update 71 | 72 | $res.Result -match 'Setting package description from README.md' | Should Be $true 73 | (nuspec_file).package.metadata.description.InnerText.Trim() | Should Be $readme 74 | } 75 | 76 | It 'can set stream specific descriptions from README.md' { 77 | get_latest -Version 1.4.0 78 | 79 | $readme = 'dummy readme & test: ' 80 | function au_BeforeUpdate { param([AUPackage] $package) 81 | '','', ($readme + $package.RemoteVersion) | Out-File $TestDrive\test_package_with_streams\README.md 82 | } 83 | function au_AfterUpdate { param([AUPackage] $package) 84 | $package.NuspecXml.package.metadata.description.InnerText.Trim() | Should Be ($readme + $package.RemoteVersion) 85 | } 86 | 87 | $res = update 88 | $res.Result -match 'Setting package description from README.md' | Should Not BeNullOrEmpty 89 | } 90 | 91 | It 'does not set description from README.md with NoReadme parameter' { 92 | $readme = 'dummy readme & test' 93 | '','', $readme | Out-File $TestDrive\test_package_with_streams\README.md 94 | $res = update -NoReadme 95 | 96 | $res.Result -match 'Setting package description from README.md' | Should BeNullOrEmpty 97 | (nuspec_file).package.metadata.description | Should Be 'This is a test package with streams for Pester' 98 | } 99 | 100 | It 'can backup and restore using WhatIf' { 101 | get_latest -Version 1.2.3 102 | $global:au_Force = $true 103 | $global:au_Version = '1.0' 104 | $global:au_WhatIf = $true 105 | $res = update -ChecksumFor 32 6> $null 106 | 107 | $res.Updated | Should Be $true 108 | $res.RemoteVersion | Should Be '1.0' 109 | (nuspec_file).package.metadata.version | Should Be 1.2.3 110 | (json_file).'1.2' | Should Be 1.2.3 111 | (json_file).'1.3' | Should Be 1.3.1 112 | (json_file).'1.4' | Should Be 1.4-beta1 113 | } 114 | 115 | It 'can let user override the version of the latest stream' { 116 | get_latest -Version 1.2.3 117 | $global:au_Force = $true 118 | $global:au_Version = '1.0' 119 | 120 | $res = update -ChecksumFor 32 6> $null 121 | 122 | $res.Updated | Should Be $true 123 | $res.RemoteVersion | Should Be '1.0' 124 | (json_file).'1.2' | Should Be 1.2.3 125 | (json_file).'1.3' | Should Be 1.3.1 126 | (json_file).'1.4' | Should Be 1.0 127 | } 128 | 129 | It 'can let user override the version of a specific stream' { 130 | get_latest -Version 1.2.3 131 | $global:au_Force = $true 132 | $global:au_IncludeStream = '1.2' 133 | $global:au_Version = '1.0' 134 | 135 | $res = update -ChecksumFor 32 6> $null 136 | 137 | $res.Updated | Should Be $true 138 | $res.RemoteVersion | Should Be '1.0' 139 | (json_file).'1.2' | Should Be 1.0 140 | (json_file).'1.3' | Should Be 1.3.1 141 | (json_file).'1.4' | Should Be 1.4-beta1 142 | } 143 | 144 | It 'automatically verifies the checksum' { 145 | $choco_path = Get-Command choco.exe | ForEach-Object Source 146 | $choco_hash = Get-FileHash $choco_path -Algorithm SHA256 | ForEach-Object Hash 147 | 148 | get_latest -Version 1.2.4 -URL32 $choco_path -Checksum32 $choco_hash 149 | 150 | $res = update -ChecksumFor 32 6> $null 151 | $res.Result -match 'hash checked for 32 bit version' | Should Be $true 152 | } 153 | 154 | It 'automatically calculates the checksum' { 155 | update -ChecksumFor 32 -IncludeStream 1.2 6> $null 156 | 157 | $global:Latest.Checksum32 | Should Not BeNullOrEmpty 158 | $global:Latest.ChecksumType32 | Should Be 'sha256' 159 | $global:Latest.Checksum64 | Should BeNullOrEmpty 160 | $global:Latest.ChecksumType64 | Should BeNullOrEmpty 161 | } 162 | 163 | It 'updates package when remote version is higher' { 164 | $res = update 165 | 166 | $res.Updated | Should Be $true 167 | $res.NuspecVersion | Should Be 1.2.3 168 | $res.RemoteVersion | Should Be 1.2.4 169 | $res.Streams.'1.2'.NuspecVersion | Should Be 1.2.3 170 | $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.4 171 | $res.Streams.'1.3'.NuspecVersion | Should Be 1.3.1 172 | $res.Streams.'1.3'.RemoteVersion | Should Be 1.3.1 173 | $res.Streams.'1.4'.NuspecVersion | Should Be 1.4-beta1 174 | $res.Streams.'1.4'.RemoteVersion | Should Be 1.4-beta1 175 | $res.Result[-1] | Should Be 'Package updated' 176 | (nuspec_file).package.metadata.version | Should Be 1.2.4 177 | (json_file).'1.2' | Should Be 1.2.4 178 | (json_file).'1.3' | Should Be 1.3.1 179 | (json_file).'1.4' | Should Be 1.4-beta1 180 | } 181 | 182 | It 'updates package when multiple remote versions are higher' { 183 | get_latest -Version 1.3.2 184 | 185 | $res = update 186 | 187 | $res.Updated | Should Be $true 188 | $res.NuspecVersion | Should Be 1.3.1 189 | $res.RemoteVersion | Should Be 1.3.2 190 | $res.Streams.'1.2'.NuspecVersion | Should Be 1.2.3 191 | $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.4 192 | $res.Streams.'1.3'.NuspecVersion | Should Be 1.3.1 193 | $res.Streams.'1.3'.RemoteVersion | Should Be 1.3.2 194 | $res.Streams.'1.4'.NuspecVersion | Should Be 1.4-beta1 195 | $res.Streams.'1.4'.RemoteVersion | Should Be 1.4-beta1 196 | $res.Result[-1] | Should Be 'Package updated' 197 | (json_file).'1.2' | Should Be 1.2.4 198 | (json_file).'1.3' | Should Be 1.3.2 199 | (json_file).'1.4' | Should Be 1.4-beta1 200 | } 201 | 202 | It "does not update the package when remote version is not higher" { 203 | get_latest -Version 1.2.3 204 | 205 | $res = update 206 | 207 | $res.Updated | Should Be $false 208 | $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.3 209 | $res.Streams.'1.3'.RemoteVersion | Should Be 1.3.1 210 | $res.Streams.'1.4'.RemoteVersion | Should Be 1.4-beta1 211 | (nuspec_file).package.metadata.version | Should Be 1.2.3 212 | (json_file).'1.2' | Should Be 1.2.3 213 | (json_file).'1.3' | Should Be 1.3.1 214 | (json_file).'1.4' | Should Be 1.4-beta1 215 | } 216 | 217 | It "throws an error when forcing update whithout specifying a stream" { 218 | get_latest -Version 1.2.3 219 | { update -Force -IncludeStream 1.2,1.4 } | Should Throw 'A single stream must be included when forcing package update' 220 | } 221 | 222 | It "updates the package when forced using choco fix notation" { 223 | get_latest -Version 1.2.3 224 | 225 | $res = update -Force -IncludeStream 1.2 226 | 227 | $d = (get-date).ToString('yyyyMMdd') 228 | $res.Updated | Should Be $true 229 | $res.Result[-1] | Should Be 'Package updated' 230 | $res.Result -match 'No new version found, but update is forced' | Should Not BeNullOrEmpty 231 | (nuspec_file).package.metadata.version | Should Be "1.2.3.$d" 232 | (json_file).'1.2' | Should Be "1.2.3.$d" 233 | (json_file).'1.3' | Should Be 1.3.1 234 | (json_file).'1.4' | Should Be 1.4-beta1 235 | } 236 | 237 | It "does not use choco fix notation if the package remote version is higher" { 238 | $res = update -Force -IncludeStream 1.2 239 | 240 | $res.Updated | Should Be $true 241 | $res.Streams.'1.2'.RemoteVersion | Should Be 1.2.4 242 | (nuspec_file).package.metadata.version | Should Be 1.2.4 243 | (json_file).'1.2' | Should Be 1.2.4 244 | (json_file).'1.3' | Should Be 1.3.1 245 | (json_file).'1.4' | Should Be 1.4-beta1 246 | } 247 | 248 | It "searches and replaces given file lines when updating" { 249 | function global:au_SearchReplace { 250 | @{ 251 | 'test_package_with_streams.nuspec' = @{ 252 | '()(.*)()' = "`$1test_package_with_streams.$($Latest.Version)`$3" 253 | } 254 | } 255 | } 256 | 257 | update 258 | 259 | $nu = (nuspec_file).package.metadata 260 | $nu.releaseNotes | Should Be 'test_package_with_streams.1.2.4' 261 | $nu.id | Should Be 'test_package_with_streams' 262 | $nu.version | Should Be 1.2.4 263 | } 264 | } 265 | 266 | Context 'Json file' { 267 | 268 | It 'loads a json file from the package directory' { 269 | { update } | Should Not Throw 270 | } 271 | 272 | It "uses version 0.0 if it can't find the json file in the current directory" { 273 | Remove-Item *.json 274 | update *> $null 275 | $global:Latest.NuspecVersion | Should Be '0.0' 276 | } 277 | 278 | It "uses version 0.0 on invalid json version" { 279 | $streams = json_file 280 | $streams.'1.2' = '{{PackageVersion}}' 281 | $streams | ConvertTo-Json | Set-Content "$TestDrive\test_package_with_streams\test_package_with_streams.json" -Encoding UTF8 282 | 283 | update -IncludeStream 1.2 *> $null 284 | 285 | $global:Latest.NuspecVersion | Should Be '0.0' 286 | } 287 | 288 | It "uses version 0.0 when a new stream is available" { 289 | get_latest -Version 1.5.0 290 | update *> $null 291 | $global:Latest.NuspecVersion | Should Be '0.0' 292 | } 293 | 294 | It "does not update the package when stream is ignored in json file" { 295 | $streams = json_file 296 | $streams.'1.2' = 'ignore' 297 | $streams | ConvertTo-Json | Set-Content "$TestDrive\test_package_with_streams\test_package_with_streams.json" -Encoding UTF8 298 | 299 | $res = update 300 | 301 | $res.Updated | Should Be $false 302 | } 303 | } 304 | 305 | Context 'au_GetLatest' { 306 | 307 | It "throws if au_GetLatest doesn't return OrderedDictionary or HashTable for streams" { 308 | $return_value = @(1) 309 | function global:au_GetLatest { @{ Streams = $return_value } } 310 | { update } | Should Throw "doesn't return an OrderedDictionary or HashTable" 311 | $return_value = @() 312 | { update } | Should Throw "returned nothing" 313 | } 314 | 315 | It 'supports returning "ignore"' { 316 | function global:au_GetLatest { 'ignore' } 317 | $res = update 318 | $res | Should BeExactly 'ignore' 319 | } 320 | 321 | It 'supports returning "ignore" on specific streams' { 322 | function global:au_GetLatest { @{ Streams = [ordered] @{ 323 | '1.4' = @{ Version = '1.4.0' } 324 | '1.3' = 'ignore' 325 | '1.2' = @{ Version = '1.2.4' } 326 | } } } 327 | $res = update 328 | $res.Streams.Count | Should Be 2 329 | $res.Streams.Keys | Should Contain '1.4' 330 | $res.Streams.Keys | Should Not Contain '1.3' 331 | $res.Streams.Keys | Should Contain '1.2' 332 | } 333 | 334 | It "supports properties defined outside streams" { 335 | get_latest -Version 1.4.0 336 | function au_BeforeUpdate { $global:Latest.Fake | Should Be 1 } 337 | update 338 | } 339 | 340 | It 'supports alphabetical streams' { 341 | $return_value = @{ 342 | dev = @{ Version = '1.4.0' } 343 | beta = @{ Version = '1.3.1' } 344 | stable = @{ Version = '1.2.4' } 345 | } 346 | function global:au_GetLatest { @{ Streams = $return_value } } 347 | 348 | $res = update 349 | 350 | $res.Updated | Should Be $true 351 | $res.Result[-1] | Should Be 'Package updated' 352 | (json_file).stable | Should Be 1.2.4 353 | (json_file).beta | Should Be 1.3.1 354 | (json_file).dev | Should Be 1.4.0 355 | } 356 | } 357 | 358 | Context 'Before and after update' { 359 | It 'calls au_BeforeUpdate if package is updated' { 360 | function au_BeforeUpdate { $global:Latest.test = 1 } 361 | update -IncludeStream 1.2 362 | $global:Latest.test | Should Be 1 363 | } 364 | 365 | It 'calls au_AfterUpdate if package is updated' { 366 | function au_AfterUpdate { $global:Latest.test = 1 } 367 | update -IncludeStream 1.2 368 | $global:Latest.test | Should Be 1 369 | } 370 | 371 | It 'doesnt call au_BeforeUpdate if package is not updated' { 372 | get_latest -Version 1.2.3 373 | function au_BeforeUpdate { $global:Latest.test = 1 } 374 | update -IncludeStream 1.2 375 | $global:Latest.test | Should BeNullOrEmpty 376 | } 377 | 378 | It 'does not change type of $Latest.Version when calling au_BeforeUpdate and au_AfterUpdate' { 379 | $return_value = @{ 380 | '1.4' = @{ Version = ConvertTo-AUVersion '1.4-beta1' } 381 | '1.2' = @{ Version = '1.2.4' } 382 | '1.3' = @{ Version = [version] '1.3.1' } 383 | } 384 | function global:au_GetLatest { @{ Streams = $return_value } } 385 | function checkLatest { 386 | $return_latest = $return_value[$global:Latest.Stream] 387 | $return_latest.Keys | ForEach-Object { 388 | $global:Latest[$_] | Should BeOfType $return_latest[$_].GetType() 389 | $global:Latest[$_] | Should BeExactly $return_latest[$_] 390 | } 391 | } 392 | function au_BeforeUpdate { checkLatest } 393 | function au_BeforeUpdate { checkLatest } 394 | update 395 | } 396 | } 397 | } 398 | 399 | Set-Location $saved_pwd 400 | } 401 | -------------------------------------------------------------------------------- /tests/Update-Package.Tests.ps1: -------------------------------------------------------------------------------- 1 | remove-module AU -ea ignore 2 | import-module $PSScriptRoot\..\AU -force 3 | 4 | Describe 'Update-Package' -Tag update { 5 | $saved_pwd = $pwd 6 | 7 | function global:get_latest($Version='1.3', $URL='test') { 8 | "function global:au_GetLatest { @{Version = '$Version'; URL = '$URL'} }" | Invoke-Expression 9 | } 10 | 11 | function global:seach_replace() { 12 | "function global:au_SearchReplace { @{} }" | Invoke-Expression 13 | } 14 | 15 | function global:nuspec_file() { [xml](Get-Content TestDrive:\test_package\test_package.nuspec) } 16 | 17 | BeforeEach { 18 | Set-Location $TestDrive 19 | Remove-Item -Recurse -Force TestDrive:\test_package -ea ignore 20 | Copy-Item -Recurse -Force $PSScriptRoot\test_package TestDrive:\test_package 21 | Set-Location $TestDrive\test_package 22 | 23 | $global:au_Timeout = 100 24 | $global:au_Force = $false 25 | $global:au_NoHostOutput = $true 26 | $global:au_NoCheckUrl = $true 27 | $global:au_NoCheckChocoVersion = $true 28 | $global:au_ChecksumFor = 'none' 29 | $global:au_WhatIf = $false 30 | $global:au_NoReadme = $false 31 | 32 | Remove-Variable -Scope global Latest -ea ignore 33 | 'BeforeUpdate', 'AfterUpdate' | ForEach-Object { Remove-Item "Function:/au_$_" -ea ignore } 34 | get_latest 35 | seach_replace 36 | } 37 | 38 | InModuleScope AU { 39 | 40 | Context 'Updating' { 41 | 42 | It 'can set description from README.md' { 43 | $readme = 'dummy readme & test' 44 | '','', $readme | Out-File $TestDrive\test_package\README.md 45 | $res = update 46 | 47 | $res.Result -match 'Setting package description from README.md' | Should Be $true 48 | (nuspec_file).package.metadata.description.InnerText.Trim() | Should Be $readme 49 | } 50 | 51 | It 'does not set description from README.md with NoReadme parameter' { 52 | $readme = 'dummy readme & test' 53 | '','', $readme | Out-File $TestDrive\test_package\README.md 54 | $res = update -NoReadme 55 | 56 | $res.Result -match 'Setting package description from README.md' | Should BeNullOrEmpty 57 | (nuspec_file).package.metadata.description | Should Be 'This is a test package for Pester' 58 | } 59 | 60 | It 'can backup and restore using WhatIf' { 61 | get_latest -Version 1.2.3 62 | $global:au_Force = $true; $global:au_Version = '1.0' 63 | $global:au_WhatIf = $true 64 | $res = update -ChecksumFor 32 6> $null 65 | 66 | $res.Updated | Should Be $true 67 | $res.RemoteVersion | Should Be '1.0' 68 | (nuspec_file).package.metadata.version | Should Be 1.2.3 69 | } 70 | 71 | It 'can let user override the version' { 72 | get_latest -Version 1.2.3 73 | $global:au_Force = $true; $global:au_Version = '1.0' 74 | 75 | $res = update -ChecksumFor 32 6> $null 76 | 77 | $res.Updated | Should Be $true 78 | $res.RemoteVersion | Should Be '1.0' 79 | } 80 | 81 | It 'automatically verifies the checksum' { 82 | $choco_path = Get-Command choco.exe | ForEach-Object Source 83 | $choco_hash = Get-FileHash $choco_path -Algorithm SHA256 | ForEach-Object Hash 84 | 85 | function global:au_GetLatest { 86 | @{ PackageName = 'test'; Version = '1.3'; URL32=$choco_path; Checksum32 = $choco_hash } 87 | } 88 | 89 | $res = update -ChecksumFor 32 6> $null 90 | $res.Result -match 'hash checked for 32 bit version' | Should Be $true 91 | } 92 | 93 | It 'automatically calculates the checksum' { 94 | update -ChecksumFor 32 6> $null 95 | 96 | $global:Latest.Checksum32 | Should Not BeNullOrEmpty 97 | $global:Latest.ChecksumType32 | Should Be 'sha256' 98 | $global:Latest.Checksum64 | Should BeNullOrEmpty 99 | $global:Latest.ChecksumType64 | Should BeNullOrEmpty 100 | } 101 | 102 | It 'updates package when remote version is higher' { 103 | $res = update 104 | 105 | $res.Updated | Should Be $true 106 | $res.RemoteVersion | Should Be 1.3 107 | $res.Result[-1] | Should Be 'Package updated' 108 | (nuspec_file).package.metadata.version | Should Be 1.3 109 | } 110 | 111 | It "does not update the package when remote version is not higher" { 112 | get_latest -Version 1.2.3 113 | 114 | $res = update 115 | 116 | $res.Updated | Should Be $false 117 | $res.RemoteVersion | Should Be 1.2.3 118 | $res.Result[-1] | Should Be 'No new version found' 119 | (nuspec_file).package.metadata.version | Should Be 1.2.3 120 | } 121 | 122 | It "updates the package when forced using choco fix notation" { 123 | get_latest -Version 1.2.3 124 | 125 | $res = update -Force:$true 126 | 127 | $d = (get-date).ToString('yyyyMMdd') 128 | $res.Updated | Should Be $true 129 | $res.Result[-1] | Should Be 'Package updated' 130 | $res.Result -match 'No new version found, but update is forced' | Should Not BeNullOrEmpty 131 | (nuspec_file).package.metadata.version | Should Be "1.2.3.$d" 132 | } 133 | 134 | It "does not use choco fix notation if the package remote version is higher" { 135 | $res = update -Force:$true 136 | 137 | $res.Updated | Should Be $true 138 | $res.RemoteVersion | Should Be 1.3 139 | (nuspec_file).package.metadata.version | Should Be 1.3 140 | } 141 | 142 | It "searches and replaces given file lines when updating" { 143 | 144 | function global:au_SearchReplace { 145 | @{ 146 | 'test_package.nuspec' = @{ '()(.*)()' = '$1test$3' } 147 | } 148 | } 149 | 150 | function global:au_GetLatest { 151 | @{ PackageName = 'test'; Version = '1.3' } 152 | } 153 | 154 | update 155 | 156 | $nu = (nuspec_file).package.metadata 157 | $nu.releaseNotes | Should Be 'test' 158 | $nu.id | Should Be 'test' 159 | $nu.version | Should Be 1.3 160 | } 161 | } 162 | 163 | Context 'Checks' { 164 | It 'verifies semantic version' { 165 | get_latest -Version 1.0.1-alpha 166 | $res = update 167 | $res.Updated | Should Be $false 168 | 169 | get_latest 1.2.3-alpha 170 | $res = update 171 | $res.Updated | Should Be $false 172 | 173 | get_latest -Version 1.3-alpha 174 | $res = update 175 | $res.Updated | Should Be $true 176 | 177 | get_latest -Version 1.3-alpha.1 178 | { update } | Should Throw "Invalid version" 179 | 180 | get_latest -Version 1.3a 181 | { update } | Should Throw "Invalid version" 182 | } 183 | 184 | It 'throws if latest URL is non existent' { 185 | { update -NoCheckUrl:$false } | Should Throw "URL syntax is invalid" 186 | } 187 | 188 | It 'throws if latest URL ContentType is text/html' { 189 | Mock request { @{ ContentType = 'text/html' } } 190 | Mock is_url { $true } 191 | { update -NoCheckUrl:$false } | Should Throw "Bad content type" 192 | } 193 | 194 | It 'quits if updated package version already exist in Chocolatey community feed' { 195 | $res = update -NoCheckChocoVersion:$false 196 | $res.Result[-1] | Should Match "New version is available but it already exists in the Chocolatey community feed" 197 | } 198 | 199 | It 'throws if search string is not found in given file' { 200 | function global:au_SearchReplace { 201 | @{ 202 | 'test_package.nuspec' = @{ 'not existing' = '' } 203 | } 204 | } 205 | 206 | { update } | Should Throw "Search pattern not found: 'not existing'" 207 | } 208 | } 209 | 210 | Context 'Global variables' { 211 | Mock Write-Verbose 212 | 213 | 214 | It 'sets Force parameter from global variable au_Force if it is not bound' { 215 | $global:au_Force = $true 216 | $filter_msg = "Parameter Force set from global variable au_Force: $au_Force" 217 | update -Verbose 218 | Assert-MockCalled Write-Verbose -ParameterFilter { $Message -eq $filter_msg } 219 | 220 | } 221 | 222 | It "doesn't set Force parameter from global variable au_Force if it is bound" { 223 | $global:au_Force = $true 224 | $filter_msg = "Parameter Force set from global variable au_Force: $au_Force" 225 | update -Verbose -Force:$false 226 | Assert-MockCalled Write-Verbose -ParameterFilter { $Message -ne $filter_msg } 227 | } 228 | 229 | It 'sets Timeout parameter from global variable au_Timeout if it is not bound' { 230 | $global:au_Timeout = 50 231 | $filter_msg = "Parameter Timeout set from global variable au_Timeout: $au_Timeout" 232 | update -Verbose 233 | Assert-MockCalled Write-Verbose -ParameterFilter { $Message -eq $filter_msg } 234 | } 235 | 236 | } 237 | 238 | Context 'Nuspec file' { 239 | 240 | It 'loads a nuspec file from the package directory' { 241 | { update } | Should Not Throw 'No nuspec file' 242 | $global:Latest.NuspecVersion | Should Be 1.2.3 243 | } 244 | 245 | It "throws if it can't find the nuspec file in the current directory" { 246 | Set-Location TestDrive:\ 247 | { update } | Should Throw 'No nuspec file' 248 | } 249 | 250 | It "uses version 0.0 on invalid nuspec version" { 251 | $nu = nuspec_file 252 | $nu.package.metadata.version = '{{PackageVersion}}' 253 | $nu.Save("$TestDrive\test_package\test_package.nuspec") 254 | 255 | update *> $null 256 | 257 | $global:Latest.NuspecVersion | Should Be '0.0' 258 | } 259 | } 260 | 261 | Context 'au_GetLatest' { 262 | 263 | It 'throws if au_GetLatest is not defined' { 264 | Remove-Item Function:/au_GetLatest 265 | { update } | Should Throw "'au_GetLatest' is not recognized" 266 | } 267 | 268 | It "throws if au_GetLatest doesn't return HashTable" { 269 | $return_value = @(1) 270 | function global:au_GetLatest { $return_value } 271 | { update } | Should Throw "doesn't return a HashTable" 272 | $return_value = @() 273 | { update } | Should Throw "returned nothing" 274 | } 275 | 276 | It "rethrows if au_GetLatest throws" { 277 | function global:au_GetLatest { throw 'test' } 278 | { update } | Should Throw "test" 279 | } 280 | 281 | It 'checks values in $Latest when entering au_GetLatest' { 282 | function global:au_GetLatest { 283 | $Latest.Count | Should Be 1 284 | $Latest.PackageName | Should Be 'test_package' 285 | @{ Version = '1.2' } 286 | } 287 | update 288 | } 289 | 290 | It 'supports returning "ignore"' { 291 | function global:au_GetLatest { 'ignore' } 292 | $res = update 293 | $res | Should BeExactly 'ignore' 294 | } 295 | 296 | It 'supports returning custom values' { 297 | function global:au_GetLatest { @{ Version = '1.2'; NewValue = 1 } } 298 | update 299 | $global:Latest.NewValue | Should Be 1 300 | } 301 | 302 | It 'supports adding values to $global:Latest' { 303 | function global:au_GetLatest { $global:Latest += @{ NewValue = 1 }; @{ Version = '1.2' } } 304 | update 305 | $global:Latest.NewValue | Should Be 1 306 | } 307 | 308 | It 'supports adding values to $Latest' { 309 | function global:au_GetLatest { $Latest.NewValue = 1; @{ Version = '1.2' } } 310 | update 311 | $global:Latest.NewValue | Should Be 1 312 | } 313 | 314 | $testCases = @( 315 | @{ Version = '1.2'; Type = [string] } 316 | @{ Version = [AUVersion] '1.2'; Type = [AUVersion] } 317 | @{ Version = [version] '1.2'; Type = [version] } 318 | @{ Version = [regex]::Match('1.2', '^(.+)$').Groups[1]; Type = [string] } 319 | ) 320 | 321 | It 'supports various Version types' -TestCases $testCases { param($Version) 322 | function global:au_GetLatest { @{ Version = $Version } } 323 | { update } | Should Not Throw 324 | } 325 | 326 | It 'supports various Version types when forcing update' -TestCases $testCases { param($Version, $Type) 327 | function global:au_GetLatest { @{ Version = $Version } } 328 | function global:au_BeforeUpdate { $Latest.Version | Should BeOfType $Type } 329 | { update -Force } | Should Not Throw 330 | } 331 | } 332 | 333 | Context 'Before and after update' { 334 | It 'calls au_BeforeUpdate if package is updated' { 335 | function au_BeforeUpdate { $global:Latest.test = 1 } 336 | update 337 | $global:Latest.test | Should Be 1 338 | } 339 | 340 | It 'calls au_AfterUpdate if package is updated' { 341 | function au_AfterUpdate { $global:Latest.test = 1 } 342 | update 343 | $global:Latest.test | Should Be 1 344 | } 345 | 346 | It 'doesnt call au_BeforeUpdate if package is not updated' { 347 | get_latest -Version 1.2.3 348 | function au_BeforeUpdate { $global:Latest.test = 1 } 349 | update 350 | $global:Latest.test | Should BeNullOrEmpty 351 | } 352 | } 353 | } 354 | Set-Location $saved_pwd 355 | } 356 | 357 | -------------------------------------------------------------------------------- /tests/test_package/test_package.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test_package 5 | 1.2.3 6 | Test Package 7 | Miodrag Milić 8 | Miodrag Milić 9 | http://www.gnu.org/copyleft/gpl.html 10 | https://github.com/majkinetor/au 11 | false 12 | This is a test package for Pester 13 | This is a test package for Pester 14 | test pester 15 | 16 | 17 | 18 | None 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/test_package/tools/chocolateyInstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | 3 | $packageName = 'test_package' 4 | $url32 = Get-Command choco.exe | ForEach-Object Source 5 | $checksum32 = '' 6 | 7 | $params = @{ 8 | packageName = $packageName 9 | fileFullPath = "$PSScriptRoot\choco.exe" 10 | Url = "file:///$url32" 11 | } 12 | Get-ChocolateyWebFile @params 13 | -------------------------------------------------------------------------------- /tests/test_package/update.ps1: -------------------------------------------------------------------------------- 1 | function global:au_SearchReplace() { 2 | @{} 3 | } 4 | 5 | function global:au_GetLatest() { 6 | @{ Version = '1.2.3' } 7 | } 8 | 9 | update 10 | -------------------------------------------------------------------------------- /tests/test_package_with_streams/test_package_with_streams.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.2": "1.2.3", 3 | "1.3": "1.3.1", 4 | "1.4": "1.4-beta1", 5 | "stable": "1.2.3", 6 | "beta": "1.3.1", 7 | "dev": "1.4-beta1" 8 | } 9 | -------------------------------------------------------------------------------- /tests/test_package_with_streams/test_package_with_streams.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test_package_with_streams 5 | 1.2.3 6 | Test Package with Streams 7 | Miodrag Milić 8 | Miodrag Milić 9 | http://www.gnu.org/copyleft/gpl.html 10 | https://github.com/majkinetor/au 11 | false 12 | This is a test package with streams for Pester 13 | This is a test package with streams for Pester 14 | test streams pester 15 | 16 | 17 | 18 | None 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/test_package_with_streams/tools/chocolateyInstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | 3 | $packageName = 'test_package_with_streams' 4 | $url32 = Get-Command choco.exe | ForEach-Object Source 5 | $checksum32 = '' 6 | 7 | $params = @{ 8 | packageName = $packageName 9 | fileFullPath = "$PSScriptRoot\choco.exe" 10 | Url = "file:///$url32" 11 | } 12 | Get-ChocolateyWebFile @params 13 | -------------------------------------------------------------------------------- /tests/test_package_with_streams/update.ps1: -------------------------------------------------------------------------------- 1 | function global:au_SearchReplace() { 2 | @{} 3 | } 4 | 5 | function global:au_GetLatest() { 6 | @{ Streams = [ordered] @{ 7 | '1.4' = @{ Version = '1.4-beta1' } 8 | '1.3' = @{ Version = '1.3.1' } 9 | '1.2' = @{ Version = '1.2.3' } 10 | } } 11 | } 12 | 13 | update 14 | -------------------------------------------------------------------------------- /vars_default.ps1: -------------------------------------------------------------------------------- 1 | # Edit variable values and then save this file as 'vars.ps1' to get it included into the publish procedure. 2 | 3 | $Env:Github_UserRepo = '' # Publish to Github; commit git changes 4 | $Env:Github_ApiKey = '' # Publish to Github token 5 | $Env:NuGet_ApiKey = '' # Publish to PSGallery token 6 | $Env:Chocolatey_ApiKey = '' # Publish to Chocolatey token 7 | 8 | $Env:gitlab_user = '' # GitLab username to use for the push 9 | $Env:gitlab_api_key = '' # GitLab API key associated with gitlab_user 10 | $Env:gitlab_push_url = '' # GitLab URL to push to. Must be HTTP or HTTPS. e.g. https://jekotia:MyPassword@git.example.org/jekotia/au.git 11 | $Env:gitlab_commit_strategy = '' # Same values as the Git plugin; single, atomic, or atomictag 12 | --------------------------------------------------------------------------------