From c47cc808f207d48b81adf6c95137dcc4a71b16bc Mon Sep 17 00:00:00 2001 From: Sebastian N Date: Thu, 27 Aug 2020 16:38:43 +0200 Subject: [PATCH] Feature/revise manifest creation script (#3241) * Make script PSv5 compatible again, apply some PS best practices * this commit removes PowerShell Core only features that were introduced with #2378 to ensure compatibility with PSv5 * adds comment based help * use consistently cased Cmdlet and variable names * improves input data related error handling * adds PS code regions for automatic code folding * automates the creation of the manifest file at the correct destination path * corrects the use of Write-Host and Write-Output Fixes #3061 * Adhere to YAML formatting conventions * Use cross-platform new line handling Co-authored-by: Sebastian Neumann --- Tools/YamlCreate.ps1 | 371 +++++++++++++++++++++---------------------- 1 file changed, 185 insertions(+), 186 deletions(-) diff --git a/Tools/YamlCreate.ps1 b/Tools/YamlCreate.ps1 index d2843e4d2308c..cde9794c9f140 100644 --- a/Tools/YamlCreate.ps1 +++ b/Tools/YamlCreate.ps1 @@ -1,262 +1,261 @@ -[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 - -# The intent of this file is to help you generate a YAML file for publishing -# to the Windows Package Manager repository. +#Requires -Version 5 + +<# +.SYNOPSIS + Winget Manifest creation helper script +.DESCRIPTION + The intent of this file is to help you generate a manifest for publishing + to the Windows Package Manager repository. + + It'll attempt to download an installer from the user-provided URL to calculate + a checksum. That checksum and the rest of the input data will be compiled in a + .YAML file. +.EXAMPLE + PS C:\Projects\winget-pkgs> Get-Help .\Tools\YamlCreate.ps1 -Full + Show this script's help +.EXAMPLE + PS C:\Projects\winget-pkgs> .\Tools\YamlCreate.ps1 + Run the script to create a manifest file +.NOTES + Please file an issue if you run into errors with this script: + https://github.com/microsoft/winget-pkgs/issues/ +.LINK + https://github.com/microsoft/winget-pkgs/blob/master/Tools/YamlCreate.ps1 +#> -# define variables -$OFS = "`r`n" #linebreak -$CurrentDirectory = Get-Location +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +$NewLine = [System.Environment]::NewLine -# Prompt for URL -While ($url.Length -eq 0) { - $url = Read-Host -Prompt 'Enter the URL to the installer' +# https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions?view=powershell-7#filters +filter TrimString { + $_.Trim() } -$OFS -write-host "Downloading URL. This will take awhile... " -ForeGroundColor Blue +########################################## +#region checksum +while ([string]::IsNullOrWhiteSpace($URL)) { + $URL = Read-Host -Prompt 'Enter the URL to the installer' | TrimString +} +Write-Host $NewLine +Write-Host "Downloading URL. This will take awhile..." -ForegroundColor Blue $WebClient = New-Object System.Net.WebClient -# This downloads the installer try { - $stream = $WebClient.OpenRead($URL) + $Stream = $WebClient.OpenRead($URL) + $Hash = (Get-FileHash -InputStream $Stream -Algorithm SHA256).Hash } catch { - write-host "Error downloading file. Please run the script again." -ForeGroundColor red + Write-Host "Error downloading file. Please run the script again." -ForegroundColor Red exit 1 } +finally { + $Stream.Close() +} +Write-Host "Url: $URL" +Write-Host "Sha256: $Hash" -# This command will get the sha256 hash -$Hash = get-filehash -InputStream $stream -$stream.Close() - - -$string = "Url: " + $URL ; -Write-Output $string -$string = "Sha256: " + $Hash.Hash -$string -$OFS -write-host "File downloaded. Please Fill out required fields. " +Write-Host $NewLine +Write-Host "File downloaded. Please Fill out required fields." +#endregion ########################################## -# Read in metadata + ########################################## +#region Read in metadata -While ($id.Length -lt 4 -or $id.length -ge 255) { - write-host 'Enter the package Id, in the following format ' - $id = Read-Host -Prompt 'For example: Microsoft.Excel' +while ($ID.Length -lt 4 -or $ID.Length -ge 255) { + Write-Host 'Enter the package Id, in the following format ' + $ID = Read-Host -Prompt 'For example: Microsoft.Excel' | TrimString } $host.UI.RawUI.ForegroundColor = "White" -While ($publisher.Length -eq 0 -or $publisher.length -ge 128) { - $publisher = Read-Host -Prompt 'Enter the publisher' +while ([string]::IsNullOrWhiteSpace($Publisher) -or $Publisher.Length -ge 128) { + $Publisher = Read-Host -Prompt 'Enter the publisher' | TrimString } -While ($AppName.Length -eq 0 -or $AppName.length -ge 128) { - $AppName = Read-Host -Prompt 'Enter the application name' +while ([string]::IsNullOrWhiteSpace($AppName) -or $AppName.Length -ge 128) { + $AppName = Read-Host -Prompt 'Enter the application name' | TrimString } -While ($version.Length -eq 0) { - $version = Read-Host -Prompt 'Enter the version. For example: 1.0, 1.0.0.0' - $filename = $version + ".yaml" +while ([string]::IsNullOrWhiteSpace($version)) { + $version = Read-Host -Prompt 'Enter the version. For example: 1.0.0, 1.0.0.0, 1.0' | TrimString + $ManifestName = $version + ".yaml" } -While ($License.Length -eq 0 -or $License.length -ge 40) { - $License = Read-Host -Prompt 'Enter the License, For example: MIT, or Copyright (c) Microsoft Corporation' +while ([string]::IsNullOrWhiteSpace($License) -or $License.Length -ge 40) { + $License = Read-Host -Prompt 'Enter the License, For example: MIT, or Copyright (c) Microsoft Corporation' | TrimString } -While ($InstallerType -notin ("exe", "msi", "msix", "inno", "nullsoft", "appx", "wix", "zip")) { - $InstallerType = Read-Host -Prompt 'Enter the InstallerType. For example: exe, msi, msix, inno, nullsoft' +while ($InstallerType -notin @("exe", "msi", "msix", "inno", "nullsoft", "appx", "wix", "zip")) { + $InstallerType = Read-Host -Prompt 'Enter the InstallerType. For example: exe, msi, msix, inno, nullsoft' } -While ($architecture -notin ("x86", "x64", "arm", "arm64", "neutral")) { +while ($architecture -notin @("x86", "x64", "arm", "arm64", "neutral")) { $architecture = Read-Host -Prompt 'Enter the architecture (x86, x64, arm, arm64, Neutral)' } do { - $LicenseUrl = Read-Host -Prompt '[OPTIONAL] Enter the license URL' -} while ($LicenseUrl.Length -ge 1 -AND ($LicenseUrl.Length -lt 10 -or $LicenseUrl.Length -gt 2000)) + $LicenseUrl = Read-Host -Prompt '[OPTIONAL] Enter the license URL' | TrimString +} while (-not [string]::IsNullOrWhiteSpace($LicenseUrl) -and ($LicenseUrl.Length -lt 10 -or $LicenseUrl.Length -gt 2000)) do { - $AppMoniker = Read-Host -Prompt '[OPTIONAL] Enter the AppMoniker (friendly name). For example: vscode' + $AppMoniker = Read-Host -Prompt '[OPTIONAL] Enter the AppMoniker (friendly name/alias). For example: vscode' | TrimString } while ($AppMoniker.Length -gt 40) do { - $Tags = Read-Host -Prompt '[OPTIONAL] Enter any tags that would be useful to discover this tool. For example: zip, c++' -} while ($Tags.length -gt 40) + $Tags = Read-Host -Prompt '[OPTIONAL] Enter any tags that would be useful to discover this tool. For example: zip, c++' | TrimString +} while ($Tags.Length -gt 40) do { - $Homepage = Read-Host -Prompt '[OPTIONAL] Enter the Url to the homepage of the application' -} while ($Homepage.length -ge 1 -AND ($Homepage.Length -lt 10 -or $Homepage.Length -gt 2000)) + $Homepage = Read-Host -Prompt '[OPTIONAL] Enter the Url to the homepage of the application' | TrimString +} while (-not [string]::IsNullOrWhiteSpace($LicenseUrl) -and ($Homepage.Length -lt 10 -or $Homepage.Length -gt 2000)) do { - $Description = Read-Host -Prompt '[OPTIONAL] Enter a description of the application' -} while ($Description.length -gt 500) + $Description = Read-Host -Prompt '[OPTIONAL] Enter a description of the application' | TrimString +} while ($Description.Length -gt 500) # Only prompt for silent switches if $InstallerType is "exe" -if ($InstallerType.ToLower() -eq "exe") { - $Silent = Read-Host -Prompt '[OPTIONAL] Enter the silent install switch' - $SilentWithProgress = Read-Host -Prompt '[OPTIONAL] Enter the silent (with progress) install switch' +if ($InstallerType -ieq "exe") { + $Silent = Read-Host -Prompt '[OPTIONAL] Enter the silent install switch'| TrimString + $SilentWithProgress = Read-Host -Prompt '[OPTIONAL] Enter the silent (with progress) install switch'| TrimString } - -########################################## -# Write metadata +#endregion ########################################## -$OFS -$string = "Id: " + $id -write-output $string | out-file $filename -write-host "Id: " -ForeGroundColor Blue -NoNewLine -write-host $id -ForeGroundColor White - -$string = "Version: " + $Version -write-output $string | out-file $filename -append -write-host "Version: " -ForeGroundColor Blue -NoNewLine -write-host $Version -ForeGroundColor White - - -$string = "Name: " + $AppName -write-output $string | out-file $filename -append -write-host "Name: " -ForeGroundColor Blue -NoNewLine -write-host $AppName -ForeGroundColor White - -$string = "Publisher: " + $Publisher -write-output $string | out-file $filename -append -write-host "Publisher: " -ForeGroundColor Blue -NoNewLine -write-host $Publisher -ForeGroundColor White - -$string = "License: " + $License -write-output $string | out-file $filename -append -write-host "License: " -ForeGroundColor Blue -NoNewLine -write-host $License -ForeGroundColor White - -if (!($LicenseUrl.length -eq 0)) { - - $string = "LicenseUrl: " + $LicenseUrl - write-output $string | out-file $filename -append - write-host "LicenseUrl: " -ForeGroundColor Blue -NoNewLine - write-host $LicenseUrl -ForeGroundColor White - +########################################## +#region Write metadata + +# YAML files should always start with the document start separator "---" +# https://yaml.org/spec/1.2/spec.html#id2760395 +$string = "---$NewLine" +Write-Output $string | Out-File $ManifestName + +Write-Host $NewLine +$string = "Id: $ID" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "Id: " -ForegroundColor Blue -NoNewline +Write-Host $ID -ForegroundColor White + +$string = "Version: $Version" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "Version: " -ForegroundColor Blue -NoNewline +Write-Host $Version -ForegroundColor White + +$string = "Name: $AppName" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "Name: " -ForegroundColor Blue -NoNewline +Write-Host $AppName -ForegroundColor White + +$string = "Publisher: $Publisher" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "Publisher: " -ForegroundColor Blue -NoNewline +Write-Host $Publisher -ForegroundColor White + +$string = "License: $License" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "License: " -ForegroundColor Blue -NoNewline +Write-Host $License -ForegroundColor White + +if (-not [string]::IsNullOrWhiteSpace($LicenseUrl)) { + $string = "LicenseUrl: $LicenseUrl" + Write-Output $string | Out-File $ManifestName -Append + Write-Host "LicenseUrl: " -ForegroundColor Blue -NoNewline + Write-Host $LicenseUrl -ForegroundColor White } -if (!($AppMoniker.length -eq 0)) { - - $string = "AppMoniker: " + $AppMoniker - write-output $string | out-file $filename -append - write-host "AppMoniker: " -ForeGroundColor Blue -NoNewLine - write-host $AppMoniker -ForeGroundColor White - +if (-not [string]::IsNullOrWhiteSpace($AppMoniker)) { + $string = "AppMoniker: $AppMoniker" + Write-Output $string | Out-File $ManifestName -Append + Write-Host "AppMoniker: " -ForegroundColor Blue -NoNewline + Write-Host $AppMoniker -ForegroundColor White } -if (!($Commands.length -eq 0)) { - - $string = "Commands: " + $Commands - write-output $string | out-file $filename -append - write-host "Commands: " -ForeGroundColor Blue -NoNewLine - write-host $Commands -ForeGroundColor White - +if (-not [string]::IsNullOrWhiteSpace($Commands)) { + $string = "Commands: $Commands" + Write-Output $string | Out-File $ManifestName -Append + Write-Host "Commands: " -ForegroundColor Blue -NoNewline + Write-Host $Commands -ForegroundColor White } -if (!($Tags.length -eq 0)) { - - $string = "Tags: " + $Tags - write-output $string | out-file $filename -append - write-host "Tags: " -ForeGroundColor Blue -NoNewLine - write-host $Tags -ForeGroundColor White - +if (-not [string]::IsNullOrWhiteSpace($Tags)) { + $string = "Tags: $Tags" + Write-Output $string | Out-File $ManifestName -Append + Write-Host "Tags: " -ForegroundColor Blue -NoNewline + Write-Host $Tags -ForegroundColor White } -if (!($Description.length -eq 0)) { - - $string = "Description: " + $Description - write-output $string | out-file $filename -append - write-host "Description: " -ForeGroundColor Blue -NoNewLine - write-host $Description -ForeGroundColor White - +if (-not [string]::IsNullOrWhiteSpace($Description)) { + $string = "Description: $Description" + Write-Output $string | Out-File $ManifestName -Append + Write-Host "Description: " -ForegroundColor Blue -NoNewline + Write-Host $Description -ForegroundColor White } -if (!($Homepage.Length -eq 0)) { - - $string = "Homepage: " + $Homepage - write-output $string | out-file $filename -append - write-host "Homepage: " -ForeGroundColor Blue -NoNewLine - write-host $Homepage -ForeGroundColor White - +if (-not [string]::IsNullOrWhiteSpace($Homepage)) { + $string = "Homepage: $Homepage" + Write-Output $string | Out-File $ManifestName -Append + Write-Host "Homepage: " -ForegroundColor Blue -NoNewline + Write-Host $Homepage -ForegroundColor White } +Write-Output "Installers:" | Out-File $ManifestName -Append -write-output "Installers:" | out-file $filename -append - - -$string = " - Arch: " + $architecture -write-output $string | out-file $filename -append -write-host "Arch: " -ForeGroundColor Blue -NoNewLine -write-host $architecture -ForeGroundColor White +$string = " - Arch: $architecture" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "Arch: " -ForegroundColor Blue -NoNewline +Write-Host $architecture -ForegroundColor White -$string = " Url: " + $Url -write-output $string | out-file $filename -append -write-host "Url: " -ForeGroundColor Blue -NoNewLine -write-host $Url -ForeGroundColor White +$string = " Url: $URL" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "Url: " -ForegroundColor Blue -NoNewline +Write-Host $URL -ForegroundColor White -$string = " Sha256: " + $Hash.Hash -write-output $string | out-file $filename -append -write-host "Sha256: " -ForeGroundColor Blue -NoNewLine -write-host $Hash.Hash -ForeGroundColor White +$string = " Sha256: $Hash" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "Sha256: " -ForegroundColor Blue -NoNewline +Write-Host $Hash -ForegroundColor White -$string = " InstallerType: " + $InstallerType -write-output $string | out-file $filename -append -write-host "InstallerType: " -ForeGroundColor Blue -NoNewLine -write-host $InstallerType -ForeGroundColor White - -if (!($Silent.Length) -eq 0 -Or !($SilentWithProgress.Length -eq 0)) { +$string = " InstallerType: $InstallerType" +Write-Output $string | Out-File $ManifestName -Append +Write-Host "InstallerType: " -ForegroundColor Blue -NoNewline +Write-Host $InstallerType -ForegroundColor White +if ((-not [string]::IsNullOrWhiteSpace($Silent)) -or + (-not [string]::IsNullOrWhiteSpace($SilentWithProgress))) { $string = " Switches:" - write-output $string | out-file $filename -append - write-host "Switches: " -ForeGroundColor Blue -NoNewLine - + Write-Output $string | Out-File $ManifestName -Append + Write-Host "Switches: " -ForegroundColor Blue -NoNewline } -if (!($Silent.Length -eq 0)) { - - $string = " Silent: " + $Silent - write-output $string | out-file $filename -append - write-host "Silent: " -ForeGroundColor Blue -NoNewLine - write-host $Silent -ForeGroundColor White - +if (-not [string]::IsNullOrWhiteSpace($Silent)) { + $string = " Silent: $Silent" + Write-Output $string | Out-File $ManifestName -Append + Write-Host "Silent: " -ForegroundColor Blue -NoNewline + Write-Host $Silent -ForegroundColor White } -if (!($SilentWithProgress.Length -eq 0)) { - - $string = " SilentWithProgress: " + $SilentWithProgress - write-output $string | out-file $filename -append - write-host "SilentWithProgress: " -ForeGroundColor Blue -NoNewLine - write-host $SilentWithProgress -ForeGroundColor White - +if (-not [string]::IsNullOrWhiteSpace($SilentWithProgress)) { + $string = " SilentWithProgress: $SilentWithProgress" + Write-Output $string | Out-File $ManifestName -Append + Write-Host "SilentWithProgress: " -ForegroundColor Blue -NoNewline + Write-Host $SilentWithProgress -ForegroundColor White } -if ($CurrentDirectory -match "winget-pkgs\\manifests\\(?.+?)\\(?.+?)$") { - $FileLocation = Join-Path "\" "manifests" $Matches.publisher $Matches.appname - $AbsoluteFileLocation = $CurrentDirectory -} elseif ($CurrentDirectory -match "winget-pkgs\\manifests\\(?.+?)$") { - $appNameDirectory = $id.Split('.')[1] - $FileLocation = Join-Path "\" "manifests" $Matches.publisher $appNameDirectory - $AbsoluteFileLocation = Join-Path $CurrentDirectory $appNameDirectory -} else { - $RepositoryRoot = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Source) - $FileLocation = Join-Path "\" "manifests" $id.Split('.')[0..1] - $AbsoluteFileLocation = Join-Path $RepositoryRoot $FileLocation -} -if (-not (Test-Path $AbsoluteFileLocation)) { - New-Item $AbsoluteFileLocation -ItemType Directory | Out-Null -} -$FileOldEnconding = Get-Content -Raw $filename -Remove-Item -Path $filename -$filename = Join-Path $AbsoluteFileLocation $filename +$ManifestsFolder = (Resolve-Path "$PSScriptRoot\..\manifests").Path +$PublisherFolder = Join-Path $ManifestsFolder $Publisher +$AppFolder = Join-Path $PublisherFolder $AppName +New-Item -ItemType "Directory" -Force -Path $AppFolder | Out-Null + +$FileOldEncoding = Get-Content -Raw $ManifestName +Remove-Item -Path $ManifestName +$ManifestPath = Join-Path $AppFolder $ManifestName $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False -[System.IO.File]::WriteAllLines($filename, $FileOldEnconding, $Utf8NoBomEncoding) +[System.IO.File]::WriteAllLines($ManifestPath, $FileOldEncoding, $Utf8NoBomEncoding) -$string = "Yaml file created: " + $filename -write-output $string +Write-Host $NewLine +Write-Host "Yaml file created: $ManifestPath" -write-host "Now place this file in the following location: $FileLocation " +#endregion +##########################################