Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
599 changes: 599 additions & 0 deletions packaging/build-feeds.ps1

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions packaging/chocolatey/package.nuspec.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Rendered by packaging/build-feeds.ps1 - do not edit the generated copy. -->
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>{{CHOCO_ID}}</id>
<version>{{VERSION}}</version>
<packageSourceUrl>{{PACKAGE_SOURCE_URL}}</packageSourceUrl>
<owners>{{PUBLISHER}}</owners>
<title>{{TITLE}}</title>
<authors>{{PUBLISHER}}</authors>
<projectUrl>{{PROJECT_URL}}</projectUrl>
<iconUrl>{{ICON_URL}}</iconUrl>
<copyright>{{COPYRIGHT}}</copyright>
<licenseUrl>{{LICENSE_URL}}</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<projectSourceUrl>{{REPO_URL}}</projectSourceUrl>
<docsUrl>{{PROJECT_URL}}</docsUrl>
<bugTrackerUrl>{{ISSUES_URL}}</bugTrackerUrl>
<tags>{{CHOCO_TAGS}}</tags>
<summary>{{SHORT_DESCRIPTION}}</summary>
<description><![CDATA[{{DESCRIPTION}}

{{CHOCO_HOW_TO_START}}]]></description>
<releaseNotes>{{RELEASE_NOTES_URL}}</releaseNotes>
</metadata>
<files>
<file src="tools\**" target="tools" />
</files>
</package>
23 changes: 23 additions & 0 deletions packaging/chocolatey/tools/chocolateyinstall.cli.ps1.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Rendered by packaging/build-feeds.ps1 - do not edit the generated copy.
#
# The package DOWNLOADS the release archive instead of carrying it: the archive
# is the same file, checksum and all, that the release page publishes, and a
# package with no binaries inside owes the moderators no VERIFICATION.txt.
#
# Windows PowerShell 5.1 is what Chocolatey runs package scripts with, so this
# file is ASCII and uses no PowerShell 7 syntax.
$ErrorActionPreference = 'Stop'
$toolsDir = Split-Path -Parent $MyInvocation.MyCommand.Definition

# Exactly as the archive holds it: one folder with bws, its licence and its
# notices. Chocolatey makes one shim for the one program in it, so bws answers
# by name in a new terminal.
Install-ChocolateyZipPackage `
-PackageName $env:ChocolateyPackageName `
-Url64bit '{{CLI_ZIP_URL}}' `
-Checksum64 '{{CLI_ZIP_SHA256}}' `
-ChecksumType64 'sha256' `
-UnzipLocation $toolsDir

Write-Host ''
Write-Host 'Type bws --help to see the commands.'
33 changes: 33 additions & 0 deletions packaging/chocolatey/tools/chocolateyinstall.window.ps1.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Rendered by packaging/build-feeds.ps1 - do not edit the generated copy.
#
# The package runs the Windows installer of the release - the same signed file,
# checksum and all, that the release page publishes and that WinGet installs
# first. Everything this package puts on the machine comes from that installer:
# the Start menu entry for every account, its folder on the machine PATH with
# both programs in it, and the entry in Installed apps. So the two feeds install
# one thing in one shape, the one packaging/test-installer.ps1 installs on a
# clean runner, rather than one a script of ours makes and has to take away
# again. The package carries no binaries, so it owes the moderators no
# VERIFICATION.txt, and makes no shims.
#
# Windows PowerShell 5.1 is what Chocolatey runs package scripts with, so this
# file is ASCII and uses no PowerShell 7 syntax.
$ErrorActionPreference = 'Stop'

$packageArgs = @{
packageName = $env:ChocolateyPackageName
fileType = 'msi'
url64bit = '{{MSI_URL}}'
checksum64 = '{{MSI_SHA256}}'
checksumType64 = 'sha256'
softwareName = '{{TITLE}}'
silentArgs = "/qn /norestart /l*v `"$env:TEMP\$env:ChocolateyPackageName.$env:ChocolateyPackageVersion.install.log`""
# An upgrade answers 0 even with a bws still running: the installer closes
# nothing, moves the file in use aside and puts the new one in place
# (measured 2026-10-14 on Windows Server 2025, packaging/test-installer.ps1).
# The copy left running can stop with an error, which is why the description
# asks to close the window and every bws first. 3010 and 1641 are the
# successes that ask for a restart or start one.
validExitCodes = @(0, 3010, 1641)
}
Install-ChocolateyPackage @packageArgs
45 changes: 45 additions & 0 deletions packaging/chocolatey/tools/chocolateyuninstall.window.ps1.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Rendered by packaging/build-feeds.ps1 - do not edit the generated copy.
#
# The installer is found by its UpgradeCode, the product's identity for good,
# and not by its name in Installed apps: a name can match another program, and
# the UpgradeCode finds the copy whichever build of it is installed - this
# package's, or one installed from the release page by hand. Windows Installer
# then takes away everything it put down, the Start menu entry and the PATH
# entry included, and leaves the window's own file in the account's
# AppData\Roaming\BetterWindowsServices alone.
#
# Windows PowerShell 5.1 is what Chocolatey runs package scripts with, so this
# file is ASCII and uses no PowerShell 7 syntax.
$ErrorActionPreference = 'Stop'

$upgradeCode = '{{MSI_UPGRADE_CODE}}'
$installer = New-Object -ComObject WindowsInstaller.Installer
$related = $null
try {
$related = $installer.RelatedProducts($upgradeCode)
# Copied into strings, so nothing below holds a COM object.
$products = @(foreach ($code in $related) { [string]$code })
}
finally {
# Released on every path, the way build-feeds.ps1 releases what it opens.
foreach ($held in @($related, $installer)) {
if ($null -ne $held) { [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($held) }
}
}

if ($products.Count -eq 0) {
# Removed already, in Settings or with msiexec. Nothing is left to take
# away, and refusing here would leave Chocolatey listing a package that is
# not on the machine.
Write-Warning '{{TITLE}} was not installed any more, so there was nothing to uninstall.'
return
}

foreach ($productCode in $products) {
# 1605 and 1614: the product went away between the lookup and the uninstall.
Uninstall-ChocolateyPackage `
-PackageName $env:ChocolateyPackageName `
-FileType 'msi' `
-SilentArgs "$productCode /qn /norestart" `
-ValidExitCodes @(0, 3010, 1605, 1614, 1641)
}
27 changes: 27 additions & 0 deletions packaging/winget/installer.cli.yaml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Rendered by packaging/build-feeds.ps1 - do not edit the generated copy.
# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.{{WINGET_SCHEMA}}.schema.json

PackageIdentifier: {{WINGET_ID}}
PackageVersion: {{VERSION}}
ReleaseDate: {{RELEASE_DATE}}
# Windows 10 1809 and Windows Server 2019, the oldest Windows the README promises.
MinimumOSVersion: 10.0.17763.0
InstallerType: zip
NestedInstallerType: portable
NestedInstallerFiles:
- RelativeFilePath: {{CLI_ZIP_EXE}}
# WinGet then makes no link in its own links folder and puts the folder bws is
# in on PATH instead (PortableInstaller.cpp in winget-cli, read by the sibling
# project 2026-11-02). That folder holds bws, its licence and its notices and
# nothing else. Without the field WinGet would make a link named bws.exe, and
# the WinGet package of Bitwarden Secrets Manager CLI makes one of that name
# too (PortableCommandAlias: bws, read 2026-11-03).
ArchiveBinariesDependOnPath: true
Commands:
- bws
Installers:
- Architecture: x64
InstallerUrl: {{CLI_ZIP_URL}}
InstallerSha256: {{CLI_ZIP_SHA256}}
ManifestType: installer
ManifestVersion: {{WINGET_SCHEMA}}
69 changes: 69 additions & 0 deletions packaging/winget/installer.window.yaml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Rendered by packaging/build-feeds.ps1 - do not edit the generated copy.
# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.{{WINGET_SCHEMA}}.schema.json

PackageIdentifier: {{WINGET_ID}}
PackageVersion: {{VERSION}}
ReleaseDate: {{RELEASE_DATE}}
# Windows 10 1809 and Windows Server 2019, the oldest Windows the README promises.
MinimumOSVersion: 10.0.17763.0
# Two installers, and their ORDER is load-bearing.
#
# Only the Windows installer puts the window in the Start menu. A portable
# package cannot have a shortcut at all: the manifest schema has no field for
# one, WinGet's portable installer makes none, and microsoft/winget-cli#2299
# asking for it has been open since 2022. The installer comes FIRST because
# that is what decides a fresh install: recent clients prefer it over portable
# when the user has set no preference (microsoft/winget-cli#6123), and older
# ones keep the first installer that applies when nothing else tells two apart.
#
# The archive comes SECOND, with no scope, for --scope user: a person without
# administrator rights, for whom the installer is out of reach because it is
# for the whole machine. The description says that such an install gets no
# Start menu entry. Keep it once it is published: WinGet upgrades only within
# the kind that is installed, and portable is compatible with nothing but
# portable, so a manifest without it would leave every such install where it
# is for good.
Installers:
- Architecture: x64
InstallerType: wix
Scope: machine
# Windows Installer asks for elevation itself.
ElevationRequirement: elevatesSelf
InstallerUrl: {{MSI_URL}}
InstallerSha256: {{MSI_SHA256}}
# The ProductCode of THIS release's installer, read out of it - every build
# gets a new one. WinGet finds an installed program for a manifest by the
# installer's ProductCode and never by AppsAndFeaturesEntries
# (SearchSourceUsingManifest in winget-cli, read 2026-11-03). Without it an
# upgrade or an uninstall from a manifest found nothing: "No app found
# matching input criteria" in 5 of 5 cases on Windows Server 2025 that day.
ProductCode: '{{MSI_PRODUCT_CODE}}'
# How WinGet recognises the installed program from one release to the next,
# an install made from the release page by hand included: the UpgradeCode
# never changes, while every build gets a new ProductCode.
AppsAndFeaturesEntries:
- UpgradeCode: '{{MSI_UPGRADE_CODE}}'
# The installer puts its folder on the machine PATH, and both programs are in it.
Commands:
- BetterWindowsServices
- bws
- Architecture: x64
InstallerType: zip
NestedInstallerType: portable
NestedInstallerFiles:
- RelativeFilePath: {{APP_ZIP_EXE}}
# WinGet then makes no link in its own links folder and puts the folder the
# window is in on PATH instead (PortableInstaller.cpp in winget-cli, read by
# the sibling project 2026-11-02). That folder holds the window, its licence
# and its notices and nothing else, so nothing on PATH can be loaded by
# another program. And the window always runs from its own file: it restarts
# itself as administrator from the path it was started by
# (Environment.ProcessPath in src/Bws.Gui/Elevation.cs), which through a link
# would be the link.
ArchiveBinariesDependOnPath: true
InstallerUrl: {{APP_ZIP_URL}}
InstallerSha256: {{APP_ZIP_SHA256}}
Commands:
- BetterWindowsServices
ManifestType: installer
ManifestVersion: {{WINGET_SCHEMA}}
26 changes: 26 additions & 0 deletions packaging/winget/locale.en-US.yaml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Rendered by packaging/build-feeds.ps1 - do not edit the generated copy.
# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.{{WINGET_SCHEMA}}.schema.json

PackageIdentifier: {{WINGET_ID}}
PackageVersion: {{VERSION}}
PackageLocale: en-US
Publisher: {{PUBLISHER}}
PublisherUrl: {{REPO_URL}}
PublisherSupportUrl: {{ISSUES_URL}}
Author: {{PUBLISHER}}
PackageName: '{{TITLE}}'
PackageUrl: {{PROJECT_URL}}
License: {{LICENSE}}
LicenseUrl: {{LICENSE_URL}}
Copyright: '{{COPYRIGHT}}'
ShortDescription: '{{SHORT_DESCRIPTION}}'
Description: |-
{{DESCRIPTION}}

{{WINGET_HOW_TO_START}}
Moniker: {{MONIKER}}
Tags:
{{WINGET_TAGS}}
ReleaseNotesUrl: {{RELEASE_NOTES_URL}}
ManifestType: defaultLocale
ManifestVersion: {{WINGET_SCHEMA}}
8 changes: 8 additions & 0 deletions packaging/winget/version.yaml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Rendered by packaging/build-feeds.ps1 - do not edit the generated copy.
# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.{{WINGET_SCHEMA}}.schema.json

PackageIdentifier: {{WINGET_ID}}
PackageVersion: {{VERSION}}
DefaultLocale: en-US
ManifestType: version
ManifestVersion: {{WINGET_SCHEMA}}
Loading
Loading