Winget-cli: Idea: Use PowerShell script as a dynamic manifest

Created on 22 May 2020  路  2Comments  路  Source: microsoft/winget-cli

Description of the new feature/enhancement

TL;DR

Use PowerShell script as a dynamic manifest.
A program then parses the script and generate a manifest.
winget reads the manifest and install the package.

Example

Here is a minimal example script. With a helper script/program, it generates real 7zip manifest.
Almost everything is same as the manifest, except that Version will always be the latest, and $Url now dynamic changes as $Version change.

$License = 'MIT',  'Copyright (C) 1999-2020 Igor Pavlov. - GNU LGPL'
$LicenseUrl = 'https://github.com/microsoft/msix-packaging/blob/master/LICENSE', 'https://7-zip.org/license.txt'
$Tags = '7zip','compression','file compression' ,'utility', 'tool', 'zip'
# $Publisher, $Author, $Homepage, $Description, $InstallerType skipped
function Version {
    # do something and return the latest version of 7zip
}
# $Version = (Version) # this line can be omitted since parser knows to add it
$Installers = (@{
    Arch = 'x64'
    Url = "https://www.7-zip.org/a/7z$((Version).Replace('.',''))-x64.msi" # <- notice here!
    Sha256 = 'A7803233EEDB6A4B59B3024CCF9292A6FFFB94507DC998AA67C5B745D197A5DC'
})

More details

Manifest is a great idea, I like it! But I don't think they are 'Packages'...
actually what they do is telling winget how to get a package and install it silently.
A package should be msi/msix files, especially msix since it can handle dependence.

Definition of terms

  • Script: a shell script that provides instructions on how to build a manifest file
  • Manifest: a file describes the information about the Package
  • Package: a msix/msi/exe/... file that can be used to install program offline.
  • Parser: External script or program, or winget itself, parse the Script and generate the Manifest.
  • winget: the program that reads the Manifest, download the Package and install it

Workflow example

How winget now works:
    Manifest -> Package -> Program
How Arch AUR Works:
    Script -[use Script as Manifest]-> Package -> Program
What the issue wants:
    Script -[parse]-> Manifest -> Package -> Program

How Script converted to Manifest

The Parser reads the script first, for each field if it is a function,
run it and use the return value as new field value.
Then use these field variables to generate the yaml Manifest file.

Further Step

  • Now there is a offiicial Manifest repository (official repo) hosted by Microsoft.
    We can have another Script repository (user repo) hosted by community.
    User can submit Scripts to user repository, or submit Manifest to official repository.

  • There maybe also some helpers that automatically submit manifest generate by script.

  • There may can be a way that software developer can submit their package into official repo,
    so user can download both manifest and package from Microsoft.
    And such a package can be also available in Microsoft Store.

Some questions and problems

  • Should the Script use a dynamic Version function or static Version variable?

    • If it's in a user repo, it's better to use the static one that user can update their package by consulting the user repo.

    • However, if we considering generate latest Manifest from the Script, the dynamic one may be a better choice.

  • Should we consider using Script as Manifest?
  • Should the Script be a .ps1 script that can run directly, or something like PKGBUILD?
  • One more step: can we automatically generate MSIX package from a Manifest, for a portable/standalone program?
  • Is Git suitable for a package repo?
    We should have another real manifest manage system, instead of using a git repo.
    As there are more and more pull requests coming, it can be really difficult to manage.
    It's also confusing that I fork and clone the whole repo, commit a file,
    only for submitting a new manifest.
  • Is this too complex? Actually we can just write our own script to generate Manifest, and submit a PR when new version is out automatically. Maybe this is a better choice.

Related issue: #223

Issue-Feature

Most helpful comment

If using PowerShell to generate the manifests is a popular idea, it would not be much work to take a DSL style approach to it. Having syntax like this is very doable with PowerShell.

function Get-Version {
    # do something and return the latest version of 7zip
}

New-WinGetManifest -Path "./7zip$(Get-Version).yml" @{
    # $Publisher, $Author, $Homepage, $Description, $InstallerType skipped
    License = 'MIT',  'Copyright (C) 1999-2020 Igor Pavlov. - GNU LGPL'
    LicenseUrl = 'https://github.com/microsoft/msix-packaging/blob/master/LICENSE', 'https://7-zip.org/license.txt'
    Tags = '7zip','compression','file compression' ,'utility', 'tool', 'zip'
    Installers = (@{
        Arch = 'x64'
        Url = "https://www.7-zip.org/a/7z$((Get-Version).Replace('.',''))-x64.msi" # <- notice here!
        Sha256 = 'A7803233EEDB6A4B59B3024CCF9292A6FFFB94507DC998AA67C5B745D197A5DC'
        Version = Get-Version
    })
}

Its basically a function that takes a path and a hashtable. Because its PowerShell, you can call whatever code you need to in there. Either calling a function or just adding inline code. I'm ignoring the magic mapping for version from your example, but this would be trivial to document and more intuitive for a seasoned PowerSheller to look at an example and just go.

Get-Help New-WinGetManifest  -Example

All 2 comments

If using PowerShell to generate the manifests is a popular idea, it would not be much work to take a DSL style approach to it. Having syntax like this is very doable with PowerShell.

function Get-Version {
    # do something and return the latest version of 7zip
}

New-WinGetManifest -Path "./7zip$(Get-Version).yml" @{
    # $Publisher, $Author, $Homepage, $Description, $InstallerType skipped
    License = 'MIT',  'Copyright (C) 1999-2020 Igor Pavlov. - GNU LGPL'
    LicenseUrl = 'https://github.com/microsoft/msix-packaging/blob/master/LICENSE', 'https://7-zip.org/license.txt'
    Tags = '7zip','compression','file compression' ,'utility', 'tool', 'zip'
    Installers = (@{
        Arch = 'x64'
        Url = "https://www.7-zip.org/a/7z$((Get-Version).Replace('.',''))-x64.msi" # <- notice here!
        Sha256 = 'A7803233EEDB6A4B59B3024CCF9292A6FFFB94507DC998AA67C5B745D197A5DC'
        Version = Get-Version
    })
}

Its basically a function that takes a path and a hashtable. Because its PowerShell, you can call whatever code you need to in there. Either calling a function or just adding inline code. I'm ignoring the magic mapping for version from your example, but this would be trivial to document and more intuitive for a seasoned PowerSheller to look at an example and just go.

Get-Help New-WinGetManifest  -Example

Actually we can just write our own script to generate Manifest, and submit a PR when new version is out automatically. Maybe this is a better choice.

This is how I handle it in Chocolatey today. The CI pipeline (which runs on a schedule) runs a script that fetches the latest version (usually using invoke-webrequest). If the remote version is newer, the url, version and filehash are updated in the manifest and pushed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mdales picture mdales  路  3Comments

auchenberg picture auchenberg  路  3Comments

brunovieira97 picture brunovieira97  路  3Comments

chrpai picture chrpai  路  3Comments

aetos382 picture aetos382  路  5Comments