├── .gitattributes ├── Build.ps1 ├── Deploy.PSDeploy.ps1 ├── GitVersion.yml ├── License ├── Media ├── Out-GridView.png └── PSDockerHub.png ├── PSDockerHub ├── PSDockerHub.psd1 ├── PSDockerHub.psm1 ├── Private │ ├── Add-TypeName.ps1 │ ├── Invoke-DockerHubWebRequest.ps1 │ └── Resolve-DockerHubRepoName.ps1 ├── Public │ ├── Find-DockerImage.ps1 │ ├── Get-DockerImageBuildDetail.ps1 │ ├── Get-DockerImageBuildHistory.ps1 │ ├── Get-DockerImageDetail.ps1 │ ├── Get-DockerImageDockerfile.ps1 │ └── Get-DockerImageTag.ps1 ├── TypeData │ ├── PSDockerHub.Format.ps1xml │ └── PSDockerHub.Types.ps1xml └── en-US │ └── about_PSDockerHub.help.txt ├── PSake.ps1 ├── README.md ├── Tests ├── Find-DockerImage.Tests.ps1 ├── Invoke-DockerHubWebRequest.Mock.ps1 ├── PSScriptAnalyzer.Tests.ps1 └── RunAllTests.cmd └── appveyor.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | * -text -------------------------------------------------------------------------------- /Build.ps1: -------------------------------------------------------------------------------- 1 | # Stop on all errors 2 | $ErrorActionPreference = 'Stop' 3 | 4 | # Grab nuget bits, install modules, set build variables, start build. 5 | Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 6 | 7 | Install-Module PSDeploy, BuildHelpers, Psake, Pester, PSScriptAnalyzer -Force -SkipPublisherCheck 8 | Import-Module Psake, BuildHelpers 9 | 10 | Set-BuildEnvironment 11 | 12 | Invoke-psake .\psake.ps1 13 | exit ( [int]( -not $psake.build_success ) ) 14 | -------------------------------------------------------------------------------- /Deploy.PSDeploy.ps1: -------------------------------------------------------------------------------- 1 | # Generic module deployment. 2 | # This stuff should be moved to psake for a cleaner deployment view 3 | 4 | # ASSUMPTIONS: 5 | 6 | # folder structure of: 7 | # - RepoFolder 8 | # - This PSDeploy file 9 | # - ModuleName 10 | # - ModuleName.psd1 11 | 12 | # Nuget key in $ENV:NugetApiKey 13 | 14 | # Set-BuildEnvironment from BuildHelpers module has populated ENV:BHProjectName 15 | 16 | # Publish to gallery with a few restrictions 17 | if( 18 | $env:BHProjectName -and $env:BHProjectName.Count -eq 1 -and 19 | $env:BHBuildSystem -ne 'Unknown' -and 20 | $env:BHBranchName -eq "master" -and 21 | $env:BHCommitMessage -match '!deploy' 22 | ) 23 | { 24 | Deploy Module { 25 | By PSGalleryModule { 26 | FromSource $ENV:BHProjectName 27 | To PSGallery 28 | WithOptions @{ 29 | ApiKey = $ENV:NugetApiKey 30 | } 31 | } 32 | } 33 | } 34 | else 35 | { 36 | "Skipping deployment: To deploy, ensure that...`n" + 37 | "`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" + 38 | "`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" + 39 | "`t* Your commit message includes !deploy (Current: $ENV:BHCommitMessage)" | 40 | Write-Host 41 | } 42 | 43 | # Publish to AppVeyor if we're in AppVeyor 44 | if( 45 | $env:BHProjectName -and $ENV:BHProjectName.Count -eq 1 -and 46 | $env:BHBuildSystem -eq 'AppVeyor' 47 | ) 48 | { 49 | Deploy DeveloperBuild { 50 | By AppVeyorModule { 51 | FromSource $ENV:BHProjectName 52 | To AppVeyor 53 | WithOptions @{ 54 | Version = $env:GitVersion_NuGetVersion 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: Mainline 2 | next-version: 1.1.0 -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | Microsoft Public License (Ms-PL) 2 | 3 | This license governs use of the accompanying software. If you use the 4 | software, you accept this license. If you do not accept the license, do not 5 | use the software. 6 | 7 | 1. Definitions 8 | 9 | The terms "reproduce," "reproduction," "derivative works," and 10 | "distribution" have the same meaning here as under U.S. copyright law. 11 | 12 | A "contribution" is the original software, or any additions or changes to 13 | the software. 14 | 15 | A "contributor" is any person that distributes its contribution under this 16 | license. 17 | 18 | "Licensed patents" are a contributor's patent claims that read directly on 19 | its contribution. 20 | 21 | 2. Grant of Rights 22 | 23 | (A) Copyright Grant- Subject to the terms of this license, including the 24 | license conditions and limitations in section 3, each contributor grants 25 | you a non-exclusive, worldwide, royalty-free copyright license to reproduce 26 | its contribution, prepare derivative works of its contribution, and 27 | distribute its contribution or any derivative works that you create. 28 | 29 | (B) Patent Grant- Subject to the terms of this license, including the 30 | license conditions and limitations in section 3, each contributor grants 31 | you a non-exclusive, worldwide, royalty-free license under its licensed 32 | patents to make, have made, use, sell, offer for sale, import, and/or 33 | otherwise dispose of its contribution in the software or derivative works 34 | of the contribution in the software. 35 | 36 | 3. Conditions and Limitations 37 | 38 | (A) No Trademark License- This license does not grant you rights to use any 39 | contributors' name, logo, or trademarks. 40 | 41 | (B) If you bring a patent claim against any contributor over patents that 42 | you claim are infringed by the software, your patent license from such 43 | contributor to the software ends automatically. 44 | 45 | (C) If you distribute any portion of the software, you must retain all 46 | copyright, patent, trademark, and attribution notices that are present in 47 | the software. 48 | 49 | (D) If you distribute any portion of the software in source code form, you 50 | may do so only under this license by including a complete copy of this 51 | license with your distribution. If you distribute any portion of the 52 | software in compiled or object code form, you may only do so under a 53 | license that complies with this license. 54 | 55 | (E) The software is licensed "as-is." You bear the risk of using it. The 56 | contributors give no express warranties, guarantees or conditions. You may 57 | have additional consumer rights under your local laws which this license 58 | cannot change. To the extent permitted under your local laws, the 59 | contributors exclude the implied warranties of merchantability, fitness for 60 | a particular purpose and non-infringement. 61 | -------------------------------------------------------------------------------- /Media/Out-GridView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beatcracker/PSDockerHub/36e48232fdf78ee232daba16db392a0002add72c/Media/Out-GridView.png -------------------------------------------------------------------------------- /Media/PSDockerHub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beatcracker/PSDockerHub/36e48232fdf78ee232daba16db392a0002add72c/Media/PSDockerHub.png -------------------------------------------------------------------------------- /PSDockerHub/PSDockerHub.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PSDockerHub' 3 | # 4 | # Generated by: beatcracker 5 | # 6 | # Generated on: 13.07.2016 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PSDockerHub.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '1.0.0' 16 | 17 | # ID used to uniquely identify this module 18 | GUID = '4f082512-cfb9-4a4b-a420-ae6c6bf40721' 19 | 20 | # Author of this module 21 | Author = 'beatcracker' 22 | 23 | # Company or vendor of this module 24 | CompanyName = 'NA' 25 | 26 | # Copyright statement for this module 27 | Copyright = '(c) 2019 beatcracker. All rights reserved.' 28 | 29 | # Description of the functionality provided by this module 30 | Description = 'PSDockerHub is a PowerShell module written to access the official Docker Hub/Registry. Its main goal is to to make sure that you have never had to use the public part of Docker Hub site in the browser.' 31 | 32 | # Minimum version of the Windows PowerShell engine required by this module 33 | PowerShellVersion = '3.0' 34 | 35 | # Name of the Windows PowerShell host required by this module 36 | # PowerShellHostName = '' 37 | 38 | # Minimum version of the Windows PowerShell host required by this module 39 | # PowerShellHostVersion = '' 40 | 41 | # Minimum version of Microsoft .NET Framework required by this module 42 | # DotNetFrameworkVersion = '' 43 | 44 | # Minimum version of the common language runtime (CLR) required by this module 45 | # CLRVersion = '' 46 | 47 | # Processor architecture (None, X86, Amd64) required by this module 48 | # ProcessorArchitecture = '' 49 | 50 | # Modules that must be imported into the global environment prior to importing this module 51 | # RequiredModules = @() 52 | 53 | # Assemblies that must be loaded prior to importing this module 54 | # RequiredAssemblies = @() 55 | 56 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 57 | # ScriptsToProcess = @() 58 | 59 | # Type files (.ps1xml) to be loaded when importing this module 60 | TypesToProcess = @('.\TypeData\PSDockerHub.Types.ps1xml') 61 | 62 | # Format files (.ps1xml) to be loaded when importing this module 63 | FormatsToProcess = @('.\TypeData\PSDockerHub.Format.ps1xml') 64 | 65 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 66 | # NestedModules = @() 67 | 68 | # Functions to export from this module 69 | FunctionsToExport = @( 70 | 'Find-DockerImage' 71 | 'Get-DockerImageBuildDetail' 72 | 'Get-DockerImageBuildHistory' 73 | 'Get-DockerImageDetail' 74 | 'Get-DockerImageDockerfile' 75 | 'Get-DockerImageTag' 76 | ) 77 | 78 | # Cmdlets to export from this module 79 | CmdletsToExport = @() 80 | 81 | # Variables to export from this module 82 | VariablesToExport = @() 83 | 84 | # Aliases to export from this module 85 | AliasesToExport = @() 86 | 87 | # DSC resources to export from this module 88 | # DscResourcesToExport = @() 89 | 90 | # List of all modules packaged with this module 91 | # ModuleList = @() 92 | 93 | # List of all files packaged with this module 94 | # FileList = @() 95 | 96 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 97 | PrivateData = @{ 98 | 99 | PSData = @{ 100 | 101 | # Tags applied to this module. These help with module discovery in online galleries. 102 | Tags = @('Docker', 'Hub', 'Container', 'API', 'Find', 'Search') 103 | 104 | # A URL to the license for this module. 105 | LicenseUri = 'https://github.com/beatcracker/PSDockerHub/blob/master/License' 106 | 107 | # A URL to the main website for this project. 108 | ProjectUri = 'https://github.com/beatcracker/PSDockerHub' 109 | 110 | # A URL to an icon representing this module. 111 | IconUri = 'https://raw.githubusercontent.com/beatcracker/PSDockerHub/master/Media/PSDockerHub.png' 112 | 113 | # ReleaseNotes of this module 114 | ReleaseNotes = "Quick fix to make module work, no new features added." 115 | 116 | } # End of PSData hashtable 117 | 118 | } # End of PrivateData hashtable 119 | 120 | # HelpInfo URI of this module 121 | # HelpInfoURI = '' 122 | 123 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 124 | # DefaultCommandPrefix = '' 125 | 126 | } 127 | -------------------------------------------------------------------------------- /PSDockerHub/PSDockerHub.psm1: -------------------------------------------------------------------------------- 1 | $script:FunctionsToExport = @() 2 | $Folders = 'Public', 'Private' 3 | 4 | foreach ($Scope in $Folders) { 5 | Get-ChildItem -LiteralPath ( 6 | Join-Path -Path $PSScriptRoot -ChildPath $Scope 7 | ) -File -Filter '*.ps1' | ForEach-Object { 8 | $File = $_ 9 | try { 10 | Write-Verbose "Dotsourcing file: $File" 11 | . $File.FullName 12 | 13 | switch ($Scope) { 14 | 'Public' { 15 | $script:FunctionsToExport += $File.BaseName 16 | } 17 | } 18 | } catch { 19 | throw "Can't import functions from file: $File" 20 | } 21 | } 22 | } 23 | 24 | Export-ModuleMember -Function $script:FunctionsToExport -------------------------------------------------------------------------------- /PSDockerHub/Private/Add-TypeName.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Adds TypeName to piped object 4 | 5 | .Parameter TypeName 6 | String array of typenames 7 | #> 8 | filter Add-TypeName { 9 | Param([string[]]$TypeName) 10 | foreach ($name in $TypeName) { 11 | $_.PSObject.TypeNames.Insert(0, $name) 12 | } 13 | $_ 14 | } -------------------------------------------------------------------------------- /PSDockerHub/Private/Invoke-DockerHubWebRequest.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Helper function to query Docker Hub API. 4 | 5 | .Description 6 | Helper function to query Docker Hub API. 7 | Supports singular and paginated requests. 8 | 9 | .Parameter Request 10 | Request part of the URL, e.g.: 'search/repositories/?query=alpine' 11 | 12 | .Parameter Paginated 13 | Make paginated request. Automatically requests URL for a next page (Docker Hub API returns it as a part of responce). 14 | 15 | .Parameter UsePageSize 16 | Hint function, that endpoint supports 'page_size' parameter and it should be used. 17 | 18 | .Parameter PageSize 19 | Sets page size to use, if endpoint supports 'page_size' parameter 20 | 21 | .Parameter MaxResults 22 | Maximum number of results to return, if request is paginated. 23 | #> 24 | function Invoke-DockerHubWebRequest 25 | { 26 | [CmdletBinding(DefaultParameterSetName = 'Single')] 27 | Param 28 | ( 29 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Single')] 30 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Paginated')] 31 | [ValidateNotNullOrEmpty()] 32 | [string[]]$Request, 33 | 34 | [Parameter(Mandatory = $true, ParameterSetName = 'Paginated')] 35 | [switch]$Paginated, 36 | 37 | [Parameter(ParameterSetName = 'Paginated')] 38 | [Parameter(ParameterSetName = 'PageSize')] 39 | [switch]$UsePageSize, 40 | 41 | [Parameter(ParameterSetName = 'Paginated')] 42 | [Parameter(ParameterSetName = 'PageSize')] 43 | [int]$PageSize = 100, 44 | 45 | [Parameter(ParameterSetName = 'Paginated')] 46 | [int]$MaxResults = [int]::MaxValue 47 | ) 48 | 49 | Begin 50 | { 51 | $DockerHub = 'https://hub.docker.com' 52 | $ApiVersion = 'v2' 53 | $PageSizeParam = 'page_size={0}' 54 | } 55 | 56 | Process 57 | { 58 | foreach ($item in $Request) { 59 | [uri]$Uri = ($DockerHub, $ApiVersion ,$item) -join '/' 60 | 61 | if ($Paginated) { 62 | if ($UsePageSize) { 63 | if ($Uri.Query) { 64 | $Joiner = '&' 65 | } else { 66 | $Joiner = '?' 67 | } 68 | 69 | if($MaxResults -lt $PageSize) { 70 | $PageSize = $MaxResults 71 | } 72 | 73 | [uri]$Uri = ($Uri, ($PageSizeParam -f $PageSize)) -join $Joiner 74 | } 75 | 76 | Write-Verbose "Making paginated request to DockerHub API: $Uri" 77 | $ResultsCount = 0 78 | 79 | while ($Uri) { 80 | try { 81 | $Response = (Invoke-WebRequest -Uri $Uri -UseBasicParsing -ErrorAction Stop).Content 82 | } catch { 83 | throw $_ 84 | } 85 | 86 | if ($Response) { 87 | try { 88 | $ret = $Response | ConvertFrom-Json -ErrorAction Stop 89 | } catch{ 90 | throw $_ 91 | } 92 | 93 | $ResultsCount += $ret.results.Count 94 | 95 | 'Page size: {0}. Total items available: {1}. Total items received : {2}. Items received in this batch: {3}. ' -f ( 96 | ($false, $PageSize)[[bool]$UsePageSize], 97 | $ret.count, 98 | $ResultsCount, 99 | $ret.results.Count 100 | ) | Write-Verbose 101 | 102 | 'Next result uri: {0}' -f $ret.next | Write-Verbose 103 | 104 | if ($ResultsCount -gt $MaxResults) { 105 | $Uri = $null 106 | $SkipCount = $ResultsCount - $MaxResults 107 | Write-Verbose "Results limit reached, skipping last: $SkipCount" 108 | $ret.results | Select-Object -SkipLast $SkipCount 109 | } else { 110 | $NextUri = $null 111 | 112 | if (-not [System.String]::IsNullOrEmpty($ret.next)) { 113 | if([uri]::TryCreate($ret.next, [System.UriKind]::Absolute, [ref]$NextUri)) { 114 | if ($NextUri.Host -ne $Uri.Host) { 115 | $UriBuilder = New-Object -TypeName System.UriBuilder -ArgumentList $NextUri 116 | $UriBuilder.Host = $Uri.Host 117 | $NextUri = $UriBuilder.ToString() 118 | Write-Verbose "Fixing url for next result: $NextUri -> $Uri" 119 | } 120 | } 121 | } 122 | 123 | $Uri = $NextUri 124 | $ret.results 125 | } 126 | } 127 | } 128 | } else { 129 | Write-Verbose "Making singular request to DockerHub API: $Uri" 130 | try { 131 | $Response = (Invoke-WebRequest -Uri $Uri -UseBasicParsing -ErrorAction Stop).Content 132 | } catch { 133 | throw $_ 134 | } 135 | 136 | if ($Response) { 137 | $Response | ConvertFrom-Json -ErrorAction Stop 138 | } 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /PSDockerHub/Private/Resolve-DockerHubRepoName.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Adds 'library' path component to the piped name, if it has only one path component. 4 | Used to resolve names for official repositories like 'alpine' and 'mariadb'. 5 | 6 | .Link 7 | https://docs.docker.com/registry/spec/api/ 8 | #> 9 | filter Resolve-DockerHubRepoName { 10 | if ($_ -match '/') { 11 | $_ 12 | } else { 13 | "library/$_" 14 | } 15 | } -------------------------------------------------------------------------------- /PSDockerHub/Public/Find-DockerImage.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Search for docker images on Docker Hub 4 | 5 | .Description 6 | Search for docker images on Docker Hub via Docker Hub API. 7 | You can filter search by Name/Description, Stars, Downloads, Official images and Automated builds. 8 | 9 | .Parameter SearchTerm 10 | Search term. Docker Hub will search it in the Name and Description. 11 | If search term is not specified, function will return all available images using wildcard query ('*'). 12 | 13 | .Parameter SortBy 14 | Sort by Downloads or Stars. Sorting is performed on Docker Hub side. 15 | 16 | .Parameter Automated 17 | Search only for automated builds 18 | Note, that this parameter is not available, when SearchTerm is omitted (wildcard query) 19 | 20 | .Parameter Official 21 | Search only for official images 22 | Note, that this parameter is not available, when SearchTerm is omitted (wildcard query) 23 | 24 | .Parameter MaxResults 25 | Maximum number of results to return. Default is 25. 26 | 27 | .Example 28 | Find-DockerImage -SearchTerm mariadb -Official 29 | 30 | Search for official MariaDB docker images 31 | 32 | .Example 33 | Find-DockerImage -SearchTerm ruby -SortBy Stars -Automated -MaxResults 5 34 | 35 | Search for automated builds of 'ruby', sort by stars, return 5 top results 36 | 37 | .Example 38 | Find-DockerImage -SearchTerm mariadb -SortBy Downloads | Where-Object {$_.Name -like '*alpine*'} 39 | 40 | Search for MariaDB docker images, sort by downloads. Then find images built on Alpine Linux using PowerShell filtering. 41 | 42 | .Example 43 | Find-DockerImage -SortBy Downloads 44 | 45 | Get most downloaded docker images 46 | 47 | 48 | .Example 49 | Find-DockerImage -SortBy Stars 50 | 51 | Get most starred docker images 52 | #> 53 | function Find-DockerImage 54 | { 55 | [CmdletBinding(DefaultParameterSetName = '__AllParameterSets')] 56 | Param 57 | ( 58 | [Parameter(Position = 0, ValueFromPipeline = $true)] 59 | [string[]]$SearchTerm, 60 | 61 | [Parameter(Position = 1)] 62 | [ValidateSet('Downloads', 'Stars')] 63 | [string]$SortBy, 64 | 65 | [Parameter(Position = 2, ParameterSetName = 'SearchTerm_Automated')] 66 | [switch]$Automated, 67 | 68 | [Parameter(Position = 2, ParameterSetName = 'SearchTerm_Official')] 69 | [switch]$Official, 70 | 71 | [Parameter(Position = 3)] 72 | [int]$MaxResults = 25 73 | ) 74 | 75 | Begin 76 | { 77 | $RequestTpl = 'search/repositories/?query={0}' 78 | $QueryParamMap = @{ 79 | Automated = 'is_automated=1' 80 | Official = 'is_official=1' 81 | Downloads = 'ordering=-pull_count' 82 | Stars = 'ordering=-star_count' 83 | } 84 | } 85 | 86 | Process 87 | { 88 | $SearchTerm = $SearchTerm -replace '^$', '*' 89 | 90 | $QueryParams = @() 91 | 92 | if ($Automated) { 93 | $QueryParams += $QueryParamMap.Automated 94 | } 95 | 96 | if ($Official) { 97 | $QueryParams += $QueryParamMap.Official 98 | } 99 | 100 | if ($SortBy) { 101 | $QueryParams += $QueryParamMap.$SortBy 102 | } 103 | 104 | $Request = $SearchTerm | ForEach-Object { 105 | ( 106 | @($RequestTpl -f [System.Net.WebUtility]::UrlEncode($_)) + $QueryParams 107 | ) -join '&' 108 | } 109 | 110 | Invoke-DockerHubWebRequest -Request $Request -Paginated -UsePageSize -MaxResults $MaxResults | 111 | Select-Object -Property ( 112 | @{n = 'Name' ; e = {$_.repo_name}}, 113 | @{n = 'Description' ; e = {$_.short_description}}, 114 | @{n = 'Stars' ; e = {$_.star_count}}, 115 | @{n = 'Downloads' ; e = {$_.pull_count}}, 116 | @{n = 'Official' ; e = {$_.is_official}}, 117 | @{n = 'Automated' ; e = {$_.is_automated}} 118 | ) | Add-TypeName -TypeName $PSCmdlet.MyInvocation.MyCommand.Name 119 | } 120 | } -------------------------------------------------------------------------------- /PSDockerHub/Public/Get-DockerImageBuildDetail.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Get Docker image build details 4 | 5 | .Description 6 | Get Docker image build details 7 | Build history is available only for some automated builds. 8 | 9 | .Parameter Name 10 | Docker repository (image) name 11 | 12 | .Parameter MaxResults 13 | Maximum number of results to return. Default is 100. 14 | 15 | .Example 16 | Get-DockerImageBuildDetail -Name 'jwilder/nginx-proxy' 17 | 18 | Get build details for 'jwilder/nginx-proxy' image 19 | #> 20 | function Get-DockerImageBuildDetail 21 | { 22 | [CmdletBinding()] 23 | Param 24 | ( 25 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 26 | [ValidateNotNullOrEmpty()] 27 | [string[]]$Name 28 | ) 29 | 30 | Begin 31 | { 32 | $RequestTpl = 'repositories/{0}/autobuild/' 33 | } 34 | 35 | Process 36 | { 37 | foreach ($item in $Name) { 38 | $Request = $RequestTpl -f ($item | Resolve-DockerHubRepoName) 39 | (Invoke-DockerHubWebRequest -Request $Request) | Select-Object -Property ( 40 | @{n = 'Name' ; e = {$_.build_name}}, 41 | @{n = 'Provider' ; e = {$_.provider}}, 42 | @{n = 'Type' ; e = {$_.repo_type}}, 43 | @{n = 'Url' ; e = {$_.repo_web_url}}, 44 | @{n = 'Repo' ; e = {$_.source_url}}, 45 | @{n = 'Tags' ; e = { 46 | $_.build_tags | Select-Object -Property ( 47 | @{n = 'Name' ; e = {$_.name}}, 48 | @{n = 'Source' ; e = {$_.source_name}}, 49 | @{n = 'Type' ; e = {$_.source_type}}, 50 | @{n = 'Dockerfile' ; e = {$_.dockerfile_location}}, 51 | @{n = 'Id' ; e = {$_.id}} 52 | ) | Add-TypeName -TypeName ($PSCmdlet.MyInvocation.MyCommand.Name + '-Tags') 53 | }} 54 | ) | Add-TypeName -TypeName $PSCmdlet.MyInvocation.MyCommand.Name 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /PSDockerHub/Public/Get-DockerImageBuildHistory.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Get Docker image build history 4 | 5 | .Description 6 | Get Docker image build history 7 | Build history is available only for some automated builds. 8 | 9 | .Parameter Name 10 | Docker repository (image) name 11 | 12 | .Parameter MaxResults 13 | Maximum number of results to return. Default is 100. 14 | 15 | .Example 16 | Get-DockerImageBuildHistory -Name 'jwilder/nginx-proxy' 17 | 18 | Get build history for 'jwilder/nginx-proxy' image 19 | #> 20 | function Get-DockerImageBuildHistory 21 | { 22 | [CmdletBinding()] 23 | Param 24 | ( 25 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 26 | [ValidateNotNullOrEmpty()] 27 | [string[]]$Name, 28 | 29 | [int]$MaxResults = 100 30 | ) 31 | 32 | Begin 33 | { 34 | $RequestTpl = 'repositories/{0}/buildhistory/' 35 | 36 | # https://github.com/badges/shields/issues/241 37 | $StatusMap = @{ 38 | 0 = 'Queued' 39 | -1 = 'Error' 40 | 3 = 'Building' 41 | 10 = 'Success' 42 | } 43 | } 44 | 45 | Process 46 | { 47 | $Request = $Name | ForEach-Object { 48 | $RequestTpl -f ($_ | Resolve-DockerHubRepoName) 49 | } 50 | 51 | Invoke-DockerHubWebRequest -Request $Request -Paginated -UsePageSize -MaxResults $MaxResults | 52 | Select-Object -Property ( 53 | @{n = 'Tag' ; e = {$_.dockertag_name}}, 54 | @{n = 'Status' ; e = {$StatusMap.($_.status)}}, 55 | @{n = 'Id' ; e = {$_.id}}, 56 | @{n = 'BuildCode' ; e = {$_.build_code}}, 57 | @{n = 'Cause' ; e = {$_.cause}}, 58 | @{n = 'Created' ; e = {[Nullable[DateTime]]$_.created_date}}, 59 | @{n = 'Updated' ; e = {[Nullable[DateTime]]$_.last_updated}} 60 | ) | Add-TypeName -TypeName $PSCmdlet.MyInvocation.MyCommand.Name 61 | } 62 | } -------------------------------------------------------------------------------- /PSDockerHub/Public/Get-DockerImageDetail.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Get detailed information for a Docker image. 4 | 5 | .Description 6 | Get detailed information for a Docker image, including full description in markdown. 7 | 8 | .Parameter Name 9 | Docker repository (image) name 10 | 11 | .Example 12 | Get-DockerImageDetail -Name 'mariadb' 13 | 14 | .Example 15 | 'alpine' | Get-DockerImageDetail 16 | #> 17 | function Get-DockerImageDetail 18 | { 19 | [CmdletBinding()] 20 | Param 21 | ( 22 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 23 | [ValidateNotNullOrEmpty()] 24 | [string[]]$Name 25 | ) 26 | 27 | Begin 28 | { 29 | $RequestTpl = 'repositories/{0}/' 30 | } 31 | 32 | Process 33 | { 34 | foreach ($item in $Name) { 35 | $Request = $RequestTpl -f ($item | Resolve-DockerHubRepoName) 36 | (Invoke-DockerHubWebRequest -Request $Request) | Select-Object -Property ( 37 | @{n = 'Name' ; e = {$_.name}}, 38 | @{n = 'Owner' ; e = {$_.user}}, 39 | @{n = 'Description' ; e = {$_.description}}, 40 | @{n = 'Active' ; e = {[bool]$_.status}}, 41 | @{n = 'Updated' ; e = {[Nullable[DateTime]]$_.last_updated}}, 42 | @{n = 'Private' ; e = {$_.is_private}}, 43 | @{n = 'Stars' ; e = {$_.star_count}}, 44 | @{n = 'Downloads' ; e = {$_.pull_count}}, 45 | @{n = 'Official' ; e = {if ($_.namespace -eq 'library') {$true} else {$false}}}, 46 | @{n = 'Automated' ; e = {$_.is_automated}}, 47 | @{n = 'FullDescription' ; e = {$_.full_description}} 48 | ) 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /PSDockerHub/Public/Get-DockerImageDockerfile.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Get Docker image Dockerfile. 4 | 5 | .Description 6 | Get Docker image Dockerfile. 7 | Build history is available only for some automated builds. 8 | 9 | .Parameter Name 10 | Docker repository (image) name 11 | 12 | .Example 13 | Get-DockerImageDockerfile -Name 'jwilder/nginx-proxy' 14 | 15 | Get Dockerfile for 'jwilder/nginx-proxy' image 16 | 17 | #> 18 | function Get-DockerImageDockerfile 19 | { 20 | [CmdletBinding()] 21 | Param 22 | ( 23 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 24 | [ValidateNotNullOrEmpty()] 25 | [string[]]$Name 26 | ) 27 | 28 | Begin 29 | { 30 | $RequestTpl = 'repositories/{0}/dockerfile/' 31 | } 32 | 33 | Process 34 | { 35 | foreach ($item in $Name) { 36 | $Request = $RequestTpl -f ($item | Resolve-DockerHubRepoName) 37 | (Invoke-DockerHubWebRequest -Request $Request).contents 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /PSDockerHub/Public/Get-DockerImageTag.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Get Docker image tags for image 4 | 5 | .Description 6 | Get Docker image tags for image via Docker Hub API 7 | 8 | .Parameter Name 9 | Docker repository (image) name 10 | 11 | .Parameter MaxResults 12 | Maximum number of results to return. Default is 100. 13 | 14 | .Example 15 | Get-DockerImageTag -Name 'mariadb' 16 | 17 | Get tags for 'mariadb' image 18 | 19 | .Example 20 | Find-DockerImage 'alpine' -Official | Get-DockerImageTag 21 | 22 | Search for official Alpine Linux docker image, then get its tags. 23 | 24 | #> 25 | function Get-DockerImageTag 26 | { 27 | [CmdletBinding()] 28 | Param 29 | ( 30 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 31 | [ValidateNotNullOrEmpty()] 32 | [string[]]$Name, 33 | 34 | [int]$MaxResults = 100 35 | ) 36 | 37 | Begin 38 | { 39 | $RequestTpl = 'repositories/{0}/tags/' 40 | } 41 | 42 | Process 43 | { 44 | $Request = $Name | ForEach-Object { 45 | $RequestTpl -f ($_ | Resolve-DockerHubRepoName) 46 | } 47 | 48 | Invoke-DockerHubWebRequest -Request $Request -Paginated -UsePageSize -MaxResults $MaxResults | 49 | Select-Object -Property ( 50 | @{n = 'Name' ; e = {$_.name}}, 51 | @{n = 'Size' ; e = {$_.full_size}}, 52 | @{n = 'Updated' ; e = {[Nullable[DateTime]]$_.last_updated}}, 53 | @{n = 'Id' ; e = {$_.id}} 54 | ) | Add-TypeName -TypeName $PSCmdlet.MyInvocation.MyCommand.Name 55 | } 56 | } -------------------------------------------------------------------------------- /PSDockerHub/TypeData/PSDockerHub.Format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Get-DockerImageBuildDetail-GroupingFormat 6 | 7 | 8 | 9 | 10 | Name : 11 | 12 | Name 13 | 14 | 15 | Provider : 16 | 17 | Provider 18 | 19 | 20 | Type : 21 | 22 | Type 23 | 24 | 25 | Repo : 26 | 27 | Repo 28 | 29 | 30 | Url : 31 | 32 | Url 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | FindDockerImageView 45 | 46 | Find-DockerImage 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Left 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Name 76 | 77 | 78 | Description 79 | 80 | 81 | Stars 82 | Right 83 | 84 | 85 | Downloads 86 | Right 87 | 88 | 89 | Official 90 | Right 91 | 92 | 93 | Automated 94 | Right 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | GetDockerImageTagView 103 | 104 | Get-DockerImageTag 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Right 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | Name 127 | 128 | 129 | '{0} MB' -f [math]::Round($_.Size / 1MB) 130 | 131 | 132 | Updated 133 | 134 | 135 | Id 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | GetDockerImageTagView 144 | 145 | Get-DockerImageTag 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | Right 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | Name 168 | 169 | 170 | '{0} MB' -f [math]::Round($_.Size / 1MB) 171 | 172 | 173 | Updated 174 | 175 | 176 | Id 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | GetDockerImageBuildDetailView 185 | 186 | Get-DockerImageBuildDetail-Tags 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | Name 211 | 212 | 213 | Source 214 | 215 | 216 | Type 217 | 218 | 219 | Dockerfile 220 | 221 | 222 | Id 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | GetDockerImageBuildDetailView 231 | 232 | Get-DockerImageBuildDetail 233 | 234 | 235 | Name 236 | Get-DockerImageBuildDetail-GroupingFormat 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | ($_.Tags | Out-String).Trim() 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /PSDockerHub/TypeData/PSDockerHub.Types.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Find-DockerImage 4 | 5 | 6 | PSStandardMembers 7 | 8 | 9 | DefaultDisplayPropertySet 10 | 11 | Name 12 | Description 13 | Stars 14 | Downloads 15 | Official 16 | Automated 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Get-DockerImageTag 25 | 26 | 27 | PSStandardMembers 28 | 29 | 30 | DefaultDisplayPropertySet 31 | 32 | Name 33 | Size 34 | Updated 35 | Id 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Get-DockerImageBuildDetail 44 | 45 | 46 | PSStandardMembers 47 | 48 | 49 | DefaultDisplayPropertySet 50 | 51 | Name 52 | Provider 53 | Type 54 | Url 55 | Repo 56 | Tags 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /PSDockerHub/en-US/about_PSDockerHub.help.txt: -------------------------------------------------------------------------------- 1 | PSTOPIC 2 | about_PSDockerHub 3 | 4 | SHORT DESCRIPTION 5 | PSDockerHub is a PowerShell module written to access the official Docker Hub/Registry 6 | 7 | LONG DESCRIPTION 8 | PSDockerHub is a PowerShell module written to access the official Docker Hub/Registry. 9 | Its main goal is to to make sure that you have never had to use the public part of Docker Hub site in the browser. 10 | 11 | Most of the APIs used were sniffed using the Chrome DevTools, 12 | because there is significant fragmentation of APIs between the various Docker offerings. 13 | 14 | You can find API documentation here: 15 | 16 | * https://docs.docker.com/registry/spec/api/ 17 | 18 | Search is not documented and the best I could find is this: 19 | 20 | * http://stackoverflow.com/questions/35444178/public-docker-v2-api-endpoints 21 | * https://github.com/docker/hub-feedback/issues/451 22 | 23 | Please note, that at the moment I have no plans to introduce support of authorization and private repositories. 24 | 25 | Suggestions, pull requests, and other contributions would be more than welcome! -------------------------------------------------------------------------------- /PSake.ps1: -------------------------------------------------------------------------------- 1 | # PSake makes variables declared here available in other scriptblocks 2 | # Init some things 3 | Properties { 4 | # Find the build folder based on build system 5 | $ProjectRoot = $ENV:BHProjectPath 6 | if(-not $ProjectRoot) 7 | { 8 | $ProjectRoot = $PSScriptRoot 9 | } 10 | 11 | $Timestamp = Get-date -uformat "%Y%m%d-%H%M%S" 12 | $PSVersion = $PSVersionTable.PSVersion.Major 13 | $TestFile = "TestResults_PS$PSVersion`_$TimeStamp.xml" 14 | $lines = '----------------------------------------------------------------------' 15 | 16 | $Verbose = @{} 17 | if($ENV:BHCommitMessage -match "!verbose") 18 | { 19 | $Verbose = @{Verbose = $True} 20 | } 21 | } 22 | 23 | Task Default -Depends Deploy 24 | 25 | Task Init { 26 | $lines 27 | Set-Location $ProjectRoot 28 | "Build System Details:" 29 | Get-Item ENV:BH* 30 | "`n" 31 | } 32 | 33 | Task Test -Depends Init { 34 | $lines 35 | "`n`tSTATUS: Testing with PowerShell $PSVersion" 36 | 37 | # Gather test results. Store them in a variable and file 38 | $TestResults = Invoke-Pester -Path $ProjectRoot\Tests -PassThru -OutputFormat NUnitXml -OutputFile "$ProjectRoot\$TestFile" 39 | 40 | # In Appveyor? Upload our tests! #Abstract this into a function? 41 | If($ENV:BHBuildSystem -eq 'AppVeyor') 42 | { 43 | (New-Object 'System.Net.WebClient').UploadFile( 44 | "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", 45 | "$ProjectRoot\$TestFile" ) 46 | } 47 | 48 | Remove-Item "$ProjectRoot\$TestFile" -Force -ErrorAction SilentlyContinue 49 | 50 | # Failed tests? 51 | # Need to tell psake or it will proceed to the deployment. Danger! 52 | if($TestResults.FailedCount -gt 0) 53 | { 54 | Write-Error "Failed '$($TestResults.FailedCount)' tests, build failed" 55 | } 56 | "`n" 57 | } 58 | 59 | Task Build -Depends Test { 60 | $lines 61 | 62 | # Load the module, read the exported functions, update the psd1 FunctionsToExport 63 | Set-ModuleFunctions 64 | 65 | # Set the module version 66 | Update-Metadata -Path $env:BHPSModuleManifest -PropertyName ModuleVersion -Value $env:GitVersion_NuGetVersion 67 | } 68 | 69 | Task Deploy -Depends Build { 70 | $lines 71 | 72 | $Params = @{ 73 | Path = $ProjectRoot 74 | Force = $true 75 | Recurse = $false # We keep psdeploy artifacts, avoid deploying those : ) 76 | } 77 | Invoke-PSDeploy @Verbose @Params 78 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | |AppVeyor|PowerShell Gallery| 2 | |:-:|:-:| 3 | |[![Build status](https://ci.appveyor.com/api/projects/status/ga9uuklbvdexw8re?svg=true)](https://ci.appveyor.com/project/beatcracker/psdockerhub)|[![PSDockerHub](https://raw.githubusercontent.com/beatcracker/PSDockerHub/master/Media/PSDockerHub.png)](https://www.powershellgallery.com/packages/PSDockerHub)| 4 | 5 | # `PSDockerHub` 6 | 7 | PSDockerHub is a PowerShell module written to access the official [Docker Hub/Registry](https://hub.docker.com). Its main goal is to to make sure that you have never had to use the public part of Docker Hub site in the browser. 8 | 9 | Most of the APIs used were sniffed using the Chrome DevTools, because there is [significant fragmentation](https://github.com/ngageoint/seed/blob/7ae36873dab9ea46369826a8debb666ef454aa26/detail.adoc#discovery) of APIs between the various Docker offerings. 10 | 11 | You can find API documentation here: 12 | 13 | * [Docker Registry HTTP API V2](https://docs.docker.com/registry/spec/api/) 14 | 15 | Search API is not documented and, the best I could find is this: 16 | 17 | * [Public Docker v2 API Endpoints](http://stackoverflow.com/questions/35444178/public-docker-v2-api-endpoints) 18 | * [docker/hub-feedback: Search uses OR instead of AND to combine terms](https://github.com/docker/hub-feedback/issues/451) 19 | 20 | Please note, that at the moment I have no plans to introduce support of authorization and private repositories. 21 | 22 | Suggestions, pull requests and other contributions would be more than welcome! 23 | 24 | # Requirements 25 | 26 | * PowerShell 3.0 or higher 27 | 28 | # Instructions 29 | 30 | ## Installation 31 | 32 | ### From [PowerShell Gallery](https://www.powershellgallery.com/) 33 | 34 | If you have PowerShell 5, or the PowerShellGet module ([MSI Installer for PowerShell 3 and 4](http://go.microsoft.com/fwlink/?LinkID=746217&clcid=0x409)): 35 | 36 | ```posh 37 | Install-Module PSDockerHub 38 | ``` 39 | 40 | ### From GitHub repo 41 | 42 | 1. Download the repository 43 | 2. Unblock the zip 44 | 3. Extract the `PSDockerHub` folder to a module path (e.g. `$env:USERPROFILE\Documents\WindowsPowerShell\Modules\`) 45 | 46 | ## Usage 47 | 48 | ```posh 49 | # Import the module 50 | Import-Module PSDockerHub 51 | 52 | #Alternatively 53 | Import-Module \\Path\To\PSDockerHub 54 | 55 | # Get commands in the module 56 | Get-Command -Module PSDockerHub 57 | 58 | # Get help 59 | Get-Help Find-DockerImage -Full 60 | Get-Help about_PSDockerHub 61 | ``` 62 | 63 | # Functions 64 | 65 | * [Find-DockerImage](#find-dockerimage) 66 | * [Get-DockerImageDetail](#get-dockerimagedetail) 67 | * [Get-DockerImageTag](#get-dockerimagetag) 68 | * [Get-DockerImageBuildDetail](#get-dockerimagebuilddetail) 69 | * [Get-DockerImageBuildHistory](#get-dockerimagebuildhistory) 70 | * [Get-DockerImageDockerfile](#get-dockerimagedockerfile) 71 | 72 | ## Find-DockerImage 73 | 74 | Search for docker images on Docker Hub via Docker Hub API. You can filter search by `Name/Description`, `Stars`, `Downloads`, `Official` images and `Automated` builds. 75 | 76 | ### Example 77 | 78 | #### Search for [MariaDB](https://mariadb.org) docker images, sort by downloads. Then find images built on [Alpine Linux](https://www.alpinelinux.org) using PowerShell filtering 79 | 80 | ```posh 81 | 'mariadb' | Find-DockerImage -SortBy Downloads -MaxResults 100 | ? Name -Like '*alpine*' 82 | ``` 83 | 84 | ```no-highlight 85 | Name Description Stars Downloads Official Automated 86 | ---- ----------- ----- --------- -------- --------- 87 | wodby/mariadb-alpine mariadb-alpine 1 6533 False True 88 | k0st/alpine-mariadb MariaDB/MySQL on Alpine (size: ~154 MB) 3 2939 False True 89 | dydx/alpine-mariadb 1 671 False True 90 | timhaak/docker-maria docker mariadb using alpine 2 357 False True 91 | db-alpine 92 | ``` 93 | 94 | Or you can pipe output to `Out-GridView` and apply filters there: 95 | 96 | ```powershell 97 | 'mariadb' | Find-DockerImage -SortBy Downloads -MaxResults 100 | Out-GridView 98 | ``` 99 | 100 | ![Out-GridView](https://raw.githubusercontent.com/beatcracker/PSDockerHub/master/Media/Out-GridView.png) 101 | 102 | #### Get most downloaded docker images: 103 | 104 | ```posh 105 | Find-DockerImage -SortBy Downloads 106 | ``` 107 | 108 | #### Get most starred docker images: 109 | 110 | ```posh 111 | Find-DockerImage -SortBy Stars 112 | ``` 113 | 114 | ## Get-DockerImageDetail 115 | 116 | Get detailed information for a Docker image, including full description in markdown. 117 | 118 | ### Example 119 | 120 | ```posh 121 | 'zzrot/whale-awkward' | Get-DockerImageDetail 122 | ``` 123 | 124 | ```no-highlight 125 | Name : whale-awkward 126 | Owner : zzrot 127 | Description : Whale, this is awkward 128 | Active : True 129 | Updated : 07.04.2016 10:15:54 130 | Private : False 131 | Stars : 2 132 | Downloads : 216653529 133 | Official : False 134 | Automated : False 135 | FullDescription : # Whale Awkward 136 | [![Docker Pulls](https://img.shields.io/docker/pulls/zzrot/whale-awkward.svg)](https://hub.d 137 | ocker.com/r/zzrot/whale-awkward/) 138 | 139 | 140 | Welcome to Whale Awkward! This is a project created by the team at [ZZROT](https://zzrot.com 141 | ). We decided it would be fun to build a simple image with a message, and then see how high 142 | we could get it on [Docker-Hub](https://hub.docker.com/). 143 | 144 | We are currently [ranked 8th](https://hub.docker.com/search/?isAutomated=0&isOfficial=0&page 145 | =1&pullCount=1&q=%22%22&starCount=0) amongst all time pulls! [Check it out](https://hub.dock 146 | er.com/r/zzrot/whale-awkward/) for yourself. 147 | 148 | Whale Awkward can be found on [Github](https://github.com/ZZROTDesign/whale-awkward) 149 | 150 | Whale Awkward was developed by: 151 | - [Sean Kilgarriff](https://seankilgarriff.com) 152 | - [Killian Brackey](https://killianbrackey.com) 153 | 154 | Through [ZZROT](https://zzrot.com) - [Github](https://github.com/ZZROTDesign) 155 | ``` 156 | 157 | ## Get-DockerImageTag 158 | 159 | Get Docker image tags for image. 160 | 161 | ### Example 162 | 163 | ```posh 164 | 'alpine' | Get-DockerImageTag 165 | ``` 166 | 167 | ```no-highlight 168 | Name Size Updated Id 169 | ---- ---- ------- -- 170 | edge 2 MB 23.06.2016 22:56:45 170603 171 | latest 2 MB 23.06.2016 22:56:28 170608 172 | 3.4 2 MB 23.06.2016 22:56:22 3272293 173 | 3.3 2 MB 23.06.2016 22:56:10 1622498 174 | 3.2 2 MB 23.06.2016 22:55:56 170604 175 | 3.1 2 MB 23.06.2016 22:55:39 170605 176 | 2.7 2 MB 02.02.2016 22:50:30 170606 177 | 2.6 2 MB 02.02.2016 22:50:22 170607 178 | ``` 179 | 180 | ## Get-DockerImageBuildDetail 181 | 182 | Get Docker image build details. Build details are available only for some [automated builds](https://docs.docker.com/docker-hub/builds/). 183 | 184 | ### Example 185 | 186 | ```posh 187 | 'jwilder/nginx-proxy' | Get-DockerImageBuildDetail 188 | ``` 189 | 190 | ```no-highlight 191 | Name : jwilder/nginx-proxy 192 | Provider : github 193 | Type : git 194 | Repo : git://github.com/jwilder/nginx-proxy.git 195 | Url : https://github.com/jwilder/nginx-proxy 196 | 197 | Tags : 198 | ---------- 199 | Name Source Type Dockerfile Id 200 | ---- ------ ---- ---------- -- 201 | 0.3.0 0.3.0 Tag / 284672 202 | 0.4.0 0.4.0 Branch / 317837 203 | 0.2.0 0.2.0 Tag / 143192 204 | 0.1.0 0.1.0 Tag / 119443 205 | latest master Branch / 13991 206 | ``` 207 | 208 | ## Get-DockerImageBuildHistory 209 | 210 | Get Docker image build history. Build history is available only for some [automated builds](https://docs.docker.com/docker-hub/builds/). 211 | 212 | ### Example 213 | 214 | ```posh 215 | 'jwilder/nginx-proxy' | Get-DockerImageBuildHistory 216 | ``` 217 | 218 | ```no-highlight 219 | Tag : latest 220 | Status : Success 221 | Id : 5581215 222 | BuildCode : ba9fsgpkffixp8udcxdjp2j 223 | Cause : VCS_CHANGE 224 | Created : 13.06.2016 17:18:12 225 | Updated : 13.06.2016 17:24:31 226 | 227 | Tag : latest 228 | Status : Success 229 | Id : 5575244 230 | BuildCode : bxvykrwfncpdhzsoypmajme 231 | Cause : VCS_CHANGE 232 | Created : 13.06.2016 9:21:40 233 | Updated : 13.06.2016 9:23:47 234 | 235 | Tag : 0.4.0 236 | Status : Success 237 | Id : 5574427 238 | BuildCode : bcppfp2jtnt4s7ke2dhztrb 239 | Cause : TRIGGERED_VIA_API 240 | Created : 13.06.2016 6:45:57 241 | Updated : 13.06.2016 6:47:43 242 | ``` 243 | 244 | ## Get-DockerImageDockerfile 245 | 246 | Get Docker image [Dockerfile](https://docs.docker.com/engine/reference/builder/). Dockerfiles are available only for some [automated builds](https://docs.docker.com/docker-hub/builds/). 247 | 248 | ### Example 249 | 250 | ```posh 251 | 'jwilder/nginx-proxy' | Get-DockerImageDockerfile 252 | ``` 253 | 254 | ```no-highlight 255 | FROM nginx:1.9.15 256 | MAINTAINER Jason Wilder mail@jasonwilder.com 257 | 258 | # Install wget and install/updates certificates 259 | RUN apt-get update \ 260 | && apt-get install -y -q --no-install-recommends \ 261 | ca-certificates \ 262 | wget \ 263 | && apt-get clean \ 264 | && rm -r /var/lib/apt/lists/* 265 | 266 | # Configure Nginx and apply fix for very long server names 267 | RUN echo "daemon off;" >> /etc/nginx/nginx.conf \ 268 | && sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf 269 | 270 | # Install Forego 271 | ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego 272 | RUN chmod u+x /usr/local/bin/forego 273 | 274 | ENV DOCKER_GEN_VERSION 0.7.3 275 | 276 | RUN wget https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \ 277 | && tar -C /usr/local/bin -xvzf docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \ 278 | && rm /docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz 279 | 280 | COPY . /app/ 281 | WORKDIR /app/ 282 | 283 | ENV DOCKER_HOST unix:///tmp/docker.sock 284 | 285 | VOLUME ["/etc/nginx/certs"] 286 | 287 | ENTRYPOINT ["/app/docker-entrypoint.sh"] 288 | CMD ["forego", "start", "-r"] 289 | ``` 290 | -------------------------------------------------------------------------------- /Tests/Find-DockerImage.Tests.ps1: -------------------------------------------------------------------------------- 1 | if (-not $ENV:BHProjectPath) { 2 | Set-BuildEnvironment -Path $PSScriptRoot\.. 3 | } 4 | 5 | $FunctionName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name).Replace('.Tests', '') 6 | 7 | Import-Module -Name (Join-Path $ENV:BHProjectPath $ENV:BHProjectName) -ErrorAction Stop 8 | 9 | Describe "$FunctionName" { 10 | InModuleScope $ENV:BHProjectName { 11 | 12 | . (Join-Path $ENV:BHProjectPath '.\Tests\Invoke-DockerHubWebRequest.Mock.ps1') 13 | 14 | 15 | # Wildcard 16 | 17 | It 'Use wildcard "*" if invoked w/o "SearchTerm" parameter' { 18 | $Result = Find-DockerImage 19 | 20 | $Result | Should -Not -BeNullOrEmpty 21 | } 22 | 23 | 24 | # Wildcard + SortBy 25 | 26 | It 'Use wildcard "*" if invoked w/o "SearchTerm" parameter and with "-SortBy Downloads"' { 27 | $Result = Find-DockerImage -SortBy Downloads 28 | 29 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 30 | } 31 | 32 | It 'Use wildcard "*" if invoked w/o "SearchTerm" parameter and with "-SortBy Stars"' { 33 | $Result = Find-DockerImage -SortBy Stars 34 | 35 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 36 | } 37 | 38 | 39 | # Wildcard + SortBy + Official/Automated 40 | 41 | It 'Use wildcard "*" if invoked w/o "SearchTerm" parameter and with "-SortBy Downloads -Automated"' { 42 | $Result = Find-DockerImage -SortBy Downloads -Automated 43 | 44 | $Result.Automated | Should -Not -Contain $false 45 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 46 | } 47 | 48 | It 'Use wildcard "*" if invoked w/o "SearchTerm" parameter and with "-SortBy Stars -Automated"' { 49 | $Result = Find-DockerImage -SortBy Stars -Automated 50 | 51 | $Result.Automated | Should -Not -Contain $false 52 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 53 | } 54 | 55 | It 'Use wildcard "*" if invoked w/o "SearchTerm" parameter and with "-SortBy Downloads -Official"' { 56 | $Result = Find-DockerImage -SortBy Downloads -Official 57 | 58 | $Result.Official | Should -Not -Contain $false 59 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 60 | } 61 | 62 | It 'Use wildcard "*" if invoked w/o "SearchTerm" parameter and with "-SortBy Stars -Official"' { 63 | $Result = Find-DockerImage -SortBy Stars -Official 64 | 65 | $Result.Official | Should -Not -Contain $false 66 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 67 | } 68 | 69 | 70 | # Pipeline: SearchTerm 71 | 72 | It 'Accept "SearchTerm" via Pipeline parameter' { 73 | $Result = 'automated' | Find-DockerImage 74 | 75 | $Result.Name -like '*automated*' | Should -HaveCount $Result.Count 76 | } 77 | 78 | 79 | # Pipeline: SearchTerm . Named: SortBy + Official/Automated 80 | 81 | It 'Accept "SearchTerm" via Pipeline parameter. Accept "-SortBy Downloads -Official" via Named parameters' { 82 | $Result = 'official' | Find-DockerImage -SortBy Downloads -Official 83 | 84 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 85 | $Result.Official | Should -Not -Contain $false 86 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 87 | } 88 | 89 | It 'Accept "SearchTerm" via Pipeline parameter. Accept "-SortBy Stars -Official" via Named parameters' { 90 | $Result = 'official' | Find-DockerImage -SortBy Stars -Official 91 | 92 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 93 | $Result.Official | Should -Not -Contain $false 94 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 95 | } 96 | 97 | It 'Accept "SearchTerm" via Pipeline parameter. Accept "-SortBy Downloads -Automated" via Named parameters' { 98 | $Result = 'automated' | Find-DockerImage -SortBy Downloads -Automated 99 | 100 | $Result.Name -like '*automated*' | Should -HaveCount $Result.Count 101 | $Result.Automated | Should -Not -Contain $false 102 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 103 | } 104 | 105 | It 'Accept "SearchTerm" via Pipeline parameter. Accept "-SortBy Stars -Automated" via Named parameters' { 106 | $Result = 'official' | Find-DockerImage -SortBy Stars -Official 107 | 108 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 109 | $Result.Official | Should -Not -Contain $false 110 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 111 | } 112 | 113 | 114 | # Named: SearchTerm 115 | 116 | It 'Accept "SearchTerm" via Pipeline parameter' { 117 | $Result = 'automated' | Find-DockerImage 118 | 119 | $Result.Name -like '*automated*' | Should -HaveCount $Result.Count 120 | } 121 | 122 | 123 | # Named: SearchTerm + SortBy 124 | 125 | It 'Accept "SearchTerm", "-SortBy Downloads" via Named parameters' { 126 | $Result = 'official' | Find-DockerImage -SortBy Downloads 127 | 128 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 129 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 130 | } 131 | 132 | It 'Accept "SearchTerm", "-SortBy Stars" via Named parameters' { 133 | $Result = 'official' | Find-DockerImage -SortBy Downloads 134 | 135 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 136 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 137 | } 138 | 139 | 140 | # Named: SearchTerm + SortBy + Official/Automated 141 | 142 | It 'Accept "SearchTerm", "-SortBy Downloads -Official" via Named parameters' { 143 | $Result = 'official' | Find-DockerImage -SortBy Downloads -Official 144 | 145 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 146 | $Result.Official | Should -Not -Contain $false 147 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 148 | } 149 | 150 | It 'Accept "SearchTerm", "-SortBy Stars -Official" via Named parameters' { 151 | $Result = 'official' | Find-DockerImage -SortBy Stars -Official 152 | 153 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 154 | $Result.Official | Should -Not -Contain $false 155 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 156 | } 157 | 158 | It 'Accept "SearchTerm", "-SortBy Downloads -Automated" via Named parameters' { 159 | $Result = 'automated' | Find-DockerImage -SortBy Downloads -Automated 160 | 161 | $Result.Name -like '*automated*' | Should -HaveCount $Result.Count 162 | $Result.Automated | Should -Not -Contain $false 163 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 164 | } 165 | 166 | It 'Accept "SearchTerm", "-SortBy Stars -Automated" via Named parameters' { 167 | $Result = 'official' | Find-DockerImage -SortBy Stars -Automated 168 | 169 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 170 | $Result.Automated | Should -Not -Contain $false 171 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 172 | } 173 | 174 | 175 | 176 | 177 | 178 | # Positional: SearchTerm 179 | 180 | It 'Accept "SearchTerm" via Positional parameter' { 181 | $Result = Find-DockerImage 'automated' 182 | 183 | $Result.Name -like '*automated*' | Should -HaveCount $Result.Count 184 | } 185 | 186 | 187 | # Positional: SearchTerm + SortBy 188 | 189 | It 'Accept "SearchTerm", "-SortBy Downloads" via Positional parameters' { 190 | $Result = Find-DockerImage 'official' 'Downloads' 191 | 192 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 193 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 194 | } 195 | 196 | It 'Accept "SearchTerm", "-SortBy Stars" via Positional parameters' { 197 | $Result = Find-DockerImage 'official' 'Stars' 198 | 199 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 200 | $Result[0].Stars | Should -BeGreaterThan $Result[-1].Stars 201 | } 202 | 203 | 204 | # Positional: SearchTerm + SortBy 205 | 206 | It 'Accept "SearchTerm", "-SortBy Downloads -Official" via Positional parameters' { 207 | $Result = Find-DockerImage -SortBy Downloads -Official 208 | 209 | $Result.Name -like '*official*' | Should -HaveCount $Result.Count 210 | $Result.Official | Should -Not -Contain $false 211 | $Result[0].Downloads | Should -BeGreaterThan $Result[-1].Downloads 212 | } 213 | 214 | } 215 | } -------------------------------------------------------------------------------- /Tests/Invoke-DockerHubWebRequest.Mock.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-DockerHubWebRequest { 2 | [CmdletBinding()] 3 | Param ( 4 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)] 5 | [ValidateNotNullOrEmpty()] 6 | $Request, 7 | 8 | [Parameter(ValueFromRemainingArguments = $true)] 9 | $Dummy 10 | ) 11 | 12 | Process { 13 | $Responce = ' 14 | { 15 | "count":9, 16 | "next":"", 17 | "previous":"", 18 | "results":[ 19 | { 20 | "repo_name":"official-1", 21 | "short_description":"Official image #1", 22 | "star_count":1, 23 | "pull_count":1, 24 | "repo_owner":"", 25 | "is_automated":false, 26 | "is_official":true 27 | }, 28 | { 29 | "repo_name":"official-2", 30 | "short_description":"Official image #2", 31 | "star_count":2, 32 | "pull_count":2, 33 | "repo_owner":"", 34 | "is_automated":false, 35 | "is_official":true 36 | }, 37 | { 38 | "repo_name":"official-3", 39 | "short_description":"Official image #3", 40 | "star_count":3, 41 | "pull_count":3, 42 | "repo_owner":"", 43 | "is_automated":true, 44 | "is_official":false 45 | }, 46 | { 47 | "repo_name":"unofficial/not-automated", 48 | "short_description":"Unofficial & not automated image", 49 | "star_count":4, 50 | "pull_count":4, 51 | "repo_owner":"", 52 | "is_automated":false, 53 | "is_official":false 54 | }, 55 | { 56 | "repo_name":"unofficial/automated", 57 | "short_description":"Unofficial & automated image", 58 | "star_count":5, 59 | "pull_count":5, 60 | "repo_owner":"", 61 | "is_automated":true, 62 | "is_official":false 63 | }, 64 | { 65 | "repo_name":"unofficial/not-automated-stars", 66 | "short_description":"Unofficial & not automated [High stars count]", 67 | "star_count":666, 68 | "pull_count":6, 69 | "repo_owner":"", 70 | "is_automated":false, 71 | "is_official":false 72 | }, 73 | { 74 | "repo_name":"unofficial/not-automated-downloads]", 75 | "short_description":"Unofficial & not automated [High downloads count]", 76 | "star_count":7, 77 | "pull_count":777, 78 | "repo_owner":"", 79 | "is_automated":false, 80 | "is_official":false 81 | }, 82 | { 83 | "repo_name":"unofficial/automated-stars", 84 | "short_description":"Unofficial & automated [High stars count]", 85 | "star_count":888, 86 | "pull_count":8, 87 | "repo_owner":"", 88 | "is_automated":true, 89 | "is_official":false 90 | }, 91 | { 92 | "repo_name":"unofficial/automated-downloads]", 93 | "short_description":"Unofficial & automated [High downloads count]", 94 | "star_count":9, 95 | "pull_count":999, 96 | "repo_owner":"", 97 | "is_automated":true, 98 | "is_official":false 99 | } 100 | ] 101 | } 102 | ' | ConvertFrom-Json 103 | 104 | switch -Wildcard ($Request) { 105 | 106 | '*is_automated=1*' { 107 | Write-Verbose 'Mocking: is_automated' 108 | $Responce.results = $Responce.results | Where-Object {$_.is_automated} 109 | } 110 | 111 | '*is_official=1*' { 112 | Write-Verbose 'Mocking: is_official' 113 | $Responce.results = $Responce.results | Where-Object {$_.is_official} 114 | } 115 | 116 | '*ordering=-pull_count' { 117 | Write-Verbose 'Mocking: pull_count' 118 | $Responce.results = $Responce.results | Sort-Object -Property 'pull_count' -Descending 119 | } 120 | 121 | '*ordering=-star_count*' { 122 | Write-Verbose 'Mocking: star_count' 123 | $Responce.results = $Responce.results | Sort-Object -Property 'star_count' -Descending 124 | } 125 | 126 | '*query=*' { 127 | if ($Request -match 'query=([^\*]\w+)') { 128 | Write-Verbose 'Mocking: query' 129 | $Responce.results = $Responce.results | Where-Object {$_.repo_name -like ('*{0}*' -f $Matches[1])} 130 | } 131 | } 132 | } 133 | 134 | $Responce.results 135 | } 136 | } -------------------------------------------------------------------------------- /Tests/PSScriptAnalyzer.Tests.ps1: -------------------------------------------------------------------------------- 1 | if (-not $ENV:BHProjectPath) { 2 | Set-BuildEnvironment -Path $PSScriptRoot\.. 3 | } 4 | 5 | $FunctionName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name).Replace('.Tests', '') 6 | 7 | Describe "$FunctionName" { 8 | It 'Should not have any PSScriptAnalyzer warnings' { 9 | $ProjectRoot = $ENV:BHProjectPath 10 | $ModuleName = $ENV:BHProjectName 11 | 12 | $ScriptWarnings = @( 13 | Invoke-ScriptAnalyzer -Path "$ProjectRoot\$ModuleName" -Recurse -ExcludeRule 'PSUseToExportFieldsInManifest' 14 | ) 15 | 16 | if ($ScriptWarnings) { 17 | $ScriptWarnings | Format-Table -AutoSize | Out-String | Write-Warning 18 | } 19 | 20 | $ScriptWarnings.Length | Should be 0 21 | } 22 | } -------------------------------------------------------------------------------- /Tests/RunAllTests.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell.exe -NoProfile -ExecutionPolicy RemoteSigned -Command "[Console]::SetBufferSize(1000, 3000) ; Import-Module -Name PSScriptAnalyzer, Pester, BuildHelpers ; Invoke-Pester -Path '%~dp0'" 3 | pause -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # See http://www.appveyor.com/docs/appveyor-yml for many more options 2 | 3 | image: Visual Studio 2017 4 | 5 | #Publish to PowerShell Gallery with this key 6 | environment: 7 | NuGetApiKey: 8 | secure: NbIQ9vEo/6ZsKp4lA2FcYQYQ6BeNAkAtYbDLxQ8QJi2hIbaRVDf3DOrw5M9IMcyQ 9 | 10 | # Skip on updates to the readme. 11 | # We can force this by adding [skip ci] or [ci skip] anywhere in commit message 12 | skip_commits: 13 | message: /^(?=.*update)(?=.*readme).*$/ 14 | 15 | build: off 16 | 17 | # Get latest GitVersion 18 | install: 19 | - choco install gitversion.portable --version 4.0.0 -y 20 | 21 | # Generate version 22 | before_test: 23 | - ps: '& (Join-Path $env:ChocolateyInstall "bin\GitVersion.exe") /l console /output buildserver' 24 | 25 | #Kick off the CI/CD pipeline 26 | test_script: 27 | - ps: . .\Build.ps1 --------------------------------------------------------------------------------