Skip to content

Commit

Permalink
Add information on AppsAndFeaturesEntries and Version Sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
Trenly committed Mar 15, 2024
1 parent d6cf4d7 commit 63b4132
Showing 1 changed file with 74 additions and 0 deletions.
74 changes: 74 additions & 0 deletions doc/Authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,77 @@ It is important to test your manifest before submission to ensure it meets the r

After enabling the setting for local manifests (`winget settings --enable LocalManifestFiles`), manifests can be tested locally with `winget install --manifest <path>`.
If your system supports Windows Sandbox, you can also use the [SandboxTest.ps1 Script](https://github.com/microsoft/winget-pkgs/blob/master/doc/tools/SandboxTest.md) to test the manifest in the Windows Sandbox. This is the preferred method, as it ensures the package doesn't require any dependencies to install.

## Advanced Authoring

### AppsAndFeaturesEntries

Most installers write accurate version data to the Windows Registry, but not all. To help with version matching and correllation between the installed application and the manifest in repo, additional `AppsAndFeaturesEntries` metadata can be used. These include the `PackageFamilyName`, `ProductCode`, `UpgradeCode`, and `InstallerType`. Additional information on how `AppsAndFeaturesEntries` affect version matching, package correllation, and sort order can be found below.

#### What is Version Matching & Package Correllation?

Version Matching and Package Correlation is the process by which WinGet attempts to correlate the metadata for an application installed on your machine and match it to a specific package (Package Correlation) and a specific version of that package (Version Matching) which is available in any source. The goal is to accurately determine the currently installed application and its version so that upgrades can be correctly suggested when available (and not suggested when not available). To do this, WinGet relies on data in Windows Registry and the information available from your configured sources.

#### When is AppsAndFeaturesEntries needed?

There are a few typical use cases when `AppsAndFeaturesEntries` should be specified in a manifest.

1. The installer does not write a `DisplayVersion` to registry and either of the following are true:
a. The `DisplayName` contains the version.

b. The `ProductCode` contains version.

In either of these cases, the respective field is required in every manifest to accurately match the installed package version to an available version from the source.

2. The `PackageVersion` differed from the installer's `DisplayVersion` at any point in the manifest history.

In this case, `DisplayVersion` is required in every manifest to prevent version range mapping errors. If the field is left out, users will be caught in an upgrade loop where the `Available` version shown when running `winget upgrade` is lower than the `PackageVersion` of the latest manifest.

3. The `DisplayVersion` the installer writes to the registry is inherently un-ordered or cannot be sorted properly by WinGet

There are many ways that publishers choose to version their software. This leads to some cases where the way WinGet sorts versions will not work properly. Some examples include packages that only use commit hashses for their releases, packages which prefix the version number with a string, or packages using date versioning of DD-MM-YYYY.

Check failure on line 116 in doc/Authoring.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`hashses` is not a recognized word. (unrecognized-spelling)

When this happens, `PackageVersion` should be set to something which is sortable by WinGet and `DisplayVersion` should be set to the value the installer writes to the registry. For more information, see the section on [Version Sorting in WinGet](/doc/Authoring.md#version-sorting-in-winget)

3. The `InstallerType` of the installer which writes the registry keys does not match the `InstallerType` of the manifest

In some cases an EXE installer may call an embedded MSI which writes data to the registry in a different format. While the `InstallerType` may be correctly identified in the manifest, the WinGet CLI will detect the registry entries as being from an MSI and return an error that the installation technology does not match when running `winget upgrade`. This requires the `InstallerType` to be specified in `AppsAndFeaturesEntries`

For more information on how to specify `AppsAndFeaturesEntries` and what the available metadata fields are, please see the [Manifest Specification](/doc/manifest).

## Version Sorting in WinGet

Inherently, all versions are strings. Whether a publisher uses a date code, a commit hash, or some other crazy format they are all saved as string values in the Windows Registry. In fact, a sematic version is just a string with a certain format. To convert these strings into versions and sort them, WinGet goes through the following process.

1. Split the string at each `.`, discarding the `.`
2. Create a new `Part` from each of the split sections
* A `Part` consists of two components - an `integer`, and a `string`
* To create a `Part`, numeric characters are parsed from the start of the section and used to create the `integer`. Once a non-numeric character is encountered, the remainder of the section is considered the `string`
* Example: If the section is `2024Mar15`, then `2024 → integer` and `Mar15 → string`
3. Compare the two parts created from the first section of the `Version`.
* If the two parts are not equal, whichever `Part` is larger corresponds to the larger version
* See below for an explanation on how parts are compared
4. If the two parts are equal, repeat step 3 with each consecutive pair of parts
* If both versions have no more parts, they are equal
* If one version has more parts and the other does not, pad the shorter version with an additional `0` as needed

When comparing one `Part` to another, WinGet goes through the following process.

1. Compare the two `integer` values
* If the two `integer` are not equal, whichever `Part` has the larger `integer` is larger
2. If the two `integer` are equal, check if there is a value in `string`
* If both values of `string` are empty, the parts are equal
* If one `Part` has a value in `string` and the other does not, the `Part` which ***does not*** have a value in `string` is considered to be greater
* Example: When comparing `34` and `34-beta`, the `integer` is equal for both (`34`). However, the `string` for the former is empty and the `string` for the latter is `-beta`, so `34` is the larger `Part`. This leads to `1.2.34` being considered a higher `Version` than `1.2.34-beta`
4. If both parts have a value in `string`, perform a case-insensitive comparison of the two
* If the values of `string` are not equal, the lexographical comparison determines which `Part` is larger

Check failure on line 151 in doc/Authoring.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`lexographical` is not a recognized word. (unrecognized-spelling)

#### Examples of Version Comparisons

| Version A | Version B | Comparison Result | Explanation |
| --- | --- | --- | ---|
| 1.2.0 | 1.2 | Equal | `Version B` will be padded with zeros to match the same number of `Parts` as `Version A` |
| 1.2 | 1.2-rc | `Version A` | The `-rc` causes `Version B` to have a `string` in the seond `Part` where `Version A` does not |

Check failure on line 158 in doc/Authoring.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`seond` is not a recognized word. (unrecognized-spelling)
| 1.2.3 | 1.2.4-rc | `Version B` | The `integer` on the third `Part` is larger for `Version B` |
| v1.2 | 1.1 | `Version B` | The leading `v` causes the `integer` for `Version A` to be `0`, which is less than `1` |
| 1.2.3a | 1.2.3b | `Version B` | `b` is lexographically greater than `a` |

Check failure on line 161 in doc/Authoring.md

View workflow job for this annotation

GitHub Actions / Check Spelling

`lexographically` is not a recognized word. (unrecognized-spelling)

1 comment on commit 63b4132

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log for details.

Unrecognized words (7)

aslo
hashses
imporant
lexographical
lexographically
Repology
seond

Previously acknowledged words that are now absent admins craigloewen Daa Esco fabricbot Filezilla jedieaston Joakim Kad Karan Levvie mdanish Megamix msft quhxl redistribution russellbanks Tbot timezone Trenly :arrow_right:
To accept ✔️ these unrecognized words as correct and remove the previously acknowledged and now absent words, run the following commands

... in a clone of the [email protected]:denelon/winget-pkgs.git repository
on the Documentation branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.21/apply.pl' |
perl - 'https://github.com/denelon/winget-pkgs/actions/runs/8302394267/attempts/1'
If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.