14 KiB
Branding, Builds, and Releases
This document describes how branded builds work in this repository, how to add or edit a brand, how to build the default and branded variants, and how to publish releases.
Overview
The project supports one default product and any number of branded variants from the same codebase.
- The default product is defined by built-in defaults in
build/scripts/brand_config.py. - The default product identifier is
webdrop_bridge. - Additional brands are defined by JSON manifests in
build/brands/. - Runtime behavior can also be branded through application config values such as
brand_id,config_dir_name,app_name, and update settings. - Windows and macOS installers are built as separate artifacts per brand.
- Releases are shared by version. A single Forgejo release can contain installers for the default product and multiple brands.
Branding Model
There are two layers to branding:
-
Packaging identity Controls installer name, executable/app bundle name, product display name, bundle identifier, MSI upgrade code, installer artwork, and related metadata.
-
Runtime configuration Controls app name shown in the UI, config directory name, update feed settings, URL mappings, allowed roots, and similar application behavior.
Packaging identity lives in build/brands/<brand>.json.
Runtime configuration lives in app config files loaded by the application. See config.example.json for the current branded example.
Important Files
build/scripts/brand_config.py: central helper for brand metadata, artifact naming, and release manifest generationbuild/brands/agravity.json: example branded manifestbuild/scripts/build_windows.py: Windows build entrypointbuild/scripts/build_macos.sh: macOS build entrypointbuild/scripts/create_release.ps1: Windows release uploaderbuild/scripts/create_release.sh: macOS release uploaderconfig.example.json: example runtime branding config
Create a New Brand
To create a new brand, add a new manifest file under build/brands/.
Example:
- Copy
build/brands/template.jsonctobuild/brands/<new-brand>.json. - Update the values for the new brand.
- Add any brand-specific assets if you do not want to reuse the default icons/license assets.
Minimal example:
{
"brand_id": "customerx",
"display_name": "Customer X Bridge",
"asset_prefix": "CustomerXBridge",
"exe_name": "CustomerXBridge",
"manufacturer": "Customer X",
"install_dir_name": "Customer X Bridge",
"shortcut_description": "Customer X drag-and-drop bridge",
"bundle_identifier": "com.customerx.bridge",
"config_dir_name": "customerx_bridge",
"msi_upgrade_code": "PUT-A-NEW-GUID-HERE",
"update_channel": "stable",
"icon_ico": "resources/icons/app.ico",
"icon_icns": "resources/icons/app.icns",
"dialog_bmp": "resources/icons/background.bmp",
"banner_bmp": "resources/icons/banner.bmp",
"license_rtf": "resources/license.rtf"
}
Required Fields
brand_id: internal identifier used for build output folders and release manifest entriesdisplay_name: user-facing product nameasset_prefix: base name for installer artifacts and app bundle nameexe_name: executable name for Windows and app bundle name base for macOSmanufacturer: MSI manufacturer stringinstall_dir_name: installation directory name shown to the OSshortcut_description: Windows shortcut descriptionbundle_identifier: macOS bundle identifierconfig_dir_name: local app config/log/cache directory namemsi_upgrade_code: stable GUID for Windows upgradesupdate_channel: currently typicallystable
Generate a new msi_upgrade_code for a new brand once and keep it stable afterwards.
Examples:
New-Guid
uuidgen
Asset Fields
These can point at brand-specific files or default shared files:
icon_icoicon_icnsdialog_bmpbanner_bmplicense_rtf
Optional toolbar icon overrides:
toolbar_icon_hometoolbar_icon_reloadtoolbar_icon_opentoolbar_icon_openwith
If a referenced asset path does not exist, the helper falls back to the default asset defined in build/scripts/brand_config.py.
For toolbar icons, the runtime looks for the configured paths in packaged and development layouts. If an icon is missing:
- Home falls back to a standard Qt home icon
- Reload/Open/OpenWith keep their existing icon behavior
Identity Rules
Treat these values as long-lived product identity once a brand has shipped:
brand_idasset_prefixexe_namebundle_identifierconfig_dir_namemsi_upgrade_code
Changing them later can break one or more of the following:
- Windows upgrade behavior
- macOS app identity
- auto-update asset selection
- local config/log/cache continuity
- installer and artifact naming consistency
If the product is already in use, only change these values deliberately and with migration planning.
Edit an Existing Brand
To edit a shipped or in-progress brand:
- Update the brand manifest in
build/brands/<brand>.json. - If needed, update brand-specific assets referenced by that manifest.
- If runtime behavior should also change, update the relevant application config values.
- Rebuild the affected platform artifacts.
- Validate the result with a dry-run release before publishing.
Safe edits after release usually include:
display_nameshortcut_description- artwork paths
- license text
- update channel, if release policy changes
High-risk edits after release are the identity fields listed above.
Runtime Branding Configuration
Packaging branding alone is not enough if the app should also present a different name, use different local storage, or point to different update settings.
Relevant runtime config keys include:
brand_idconfig_dir_nameapp_nameupdate_base_urlupdate_repoupdate_channelupdate_manifest_name
Toolbar icon env overrides (useful for packaged branding):
TOOLBAR_ICON_HOMETOOLBAR_ICON_RELOADTOOLBAR_ICON_OPENTOOLBAR_ICON_OPENWITH
The current example in config.example.json shows the Agravity runtime setup.
When adding a new brand, make sure the runtime config matches the packaging manifest at least for:
brand_idconfig_dir_nameapp_name
Build the Default Product
Windows
Build the default executable only:
python .\build\scripts\build_windows.py
Build the default Windows MSI:
python .\build\scripts\build_windows.py --msi
Build with a specific .env file:
python .\build\scripts\build_windows.py --msi --env-file .\.env
macOS
Build the default macOS app and DMG:
bash build/scripts/build_macos.sh
Build with a specific .env file:
bash build/scripts/build_macos.sh --env-file .env
Build a Brand
Windows
Build a branded executable only:
python .\build\scripts\build_windows.py --brand agravity
Build a branded MSI:
python .\build\scripts\build_windows.py --brand agravity --msi
macOS
Build a branded macOS app and DMG:
bash build/scripts/build_macos.sh --brand agravity
Build Output Locations
Windows artifacts are written to:
build/dist/windows/webdrop_bridge/for the default productbuild/dist/windows/<brand_id>/for branded products
macOS artifacts are written to:
build/dist/macos/webdrop_bridge/for the default productbuild/dist/macos/<brand_id>/for branded products
Typical artifact names:
- Windows MSI:
<asset_prefix>-<version>-win-x64.msi - Windows checksum:
<asset_prefix>-<version>-win-x64.msi.sha256 - macOS DMG:
<asset_prefix>-<version>-macos-universal.dmg - macOS checksum:
<asset_prefix>-<version>-macos-universal.dmg.sha256
Create a Release
Releases are shared by version. The release scripts scan local build outputs on the current machine and upload every artifact they find for that platform.
This means:
- a Windows machine can upload all locally built MSIs for the current version
- a macOS machine can later upload all locally built DMGs for the same version
- both runs contribute to the same Forgejo release tag
release-manifest.jsonis merged so later runs do not wipe earlier platform entries
Windows Release
Dry run first:
.\build\scripts\create_release.ps1 -DryRun
Publish all locally built Windows variants for the current version:
.\build\scripts\create_release.ps1
Publish only selected brands:
.\build\scripts\create_release.ps1 -Brands agravity
Publish only the default product:
.\build\scripts\create_release.ps1 -Brands webdrop_bridge
Publish a specific version:
.\build\scripts\create_release.ps1 -Version 0.8.4
macOS Release
Dry run first:
bash build/scripts/create_release.sh --dry-run
Publish all locally built macOS variants for the current version:
bash build/scripts/create_release.sh
Publish only selected brands:
bash build/scripts/create_release.sh --brand agravity
Publish only the default product:
bash build/scripts/create_release.sh --brand webdrop_bridge
Publish a specific version:
bash build/scripts/create_release.sh --version 0.8.4
Credentials
Both release scripts use Forgejo credentials from environment variables when available:
FORGEJO_USERFORGEJO_PASS
If they are not set and you are not in dry-run mode, the script prompts for them.
Both scripts also support clearing credentials from the current shell session:
- Windows:
-ClearCredentials - macOS:
--clear-credentials
Dry Run Behavior
Dry-run mode is the preferred validation step before publishing.
Dry-run mode:
- discovers the local artifacts exactly like a real release run
- prints the release tag and target release URL
- prints the brands that were discovered locally
- prints the artifact paths that would be uploaded
- writes a local manifest preview to
build/dist/release-manifest.json - does not prompt for credentials
- does not perform network requests
- does not delete or upload assets
Release Manifest
The release scripts generate and upload release-manifest.json.
This file is used by the updater to select the correct installer and checksum for a given brand and platform.
Current platform keys are:
windows-x64macos-universal
The manifest is built from local artifacts and merged with any existing manifest already attached to the release.
First Manual Download (Before Auto-Update)
After creating a release, a user can manually download the first installer directly from Forgejo. Once installed, auto-update handles later versions.
Base repository URL:
https://git.him-tools.de/HIM-public/webdrop-bridge
Release page pattern:
https://git.him-tools.de/HIM-public/webdrop-bridge/releases/tag/v<version>
Direct asset download pattern:
https://git.him-tools.de/HIM-public/webdrop-bridge/releases/download/v<version>/<asset-file-name>
Example asset names:
WebDropBridge-0.8.4-win-x64.msiWebDropBridge-0.8.4-macos-universal.dmgAgravityBridge-0.8.4-win-x64.msiAgravityBridge-0.8.4-macos-universal.dmg
wget Examples
# Default Windows installer
wget "https://git.him-tools.de/HIM-public/webdrop-bridge/releases/download/v0.8.4/WebDropBridge-0.8.4-win-x64.msi"
# Agravity macOS installer
wget "https://git.him-tools.de/HIM-public/webdrop-bridge/releases/download/v0.8.4/AgravityBridge-0.8.4-macos-universal.dmg"
curl Examples
# Default macOS installer
curl -L -o WebDropBridge-0.8.4-macos-universal.dmg \
"https://git.him-tools.de/HIM-public/webdrop-bridge/releases/download/v0.8.4/WebDropBridge-0.8.4-macos-universal.dmg"
# Agravity Windows installer
curl -L -o AgravityBridge-0.8.4-win-x64.msi \
"https://git.him-tools.de/HIM-public/webdrop-bridge/releases/download/v0.8.4/AgravityBridge-0.8.4-win-x64.msi"
PowerShell Example
Invoke-WebRequest `
-Uri "https://git.him-tools.de/HIM-public/webdrop-bridge/releases/download/v0.8.4/WebDropBridge-0.8.4-win-x64.msi" `
-OutFile "WebDropBridge-0.8.4-win-x64.msi"
You can inspect release-manifest.json on the release to see the exact file names for each brand and platform.
Recommended Workflow for a New Brand
- Create
build/brands/<brand>.json. - Add or update brand-specific assets if needed.
- Prepare matching runtime config values.
- Build the brand on Windows and/or macOS.
- Run the release script in dry-run mode.
- Verify artifact names, discovered brands, and manifest contents.
- Run the actual release script.
- Validate update behavior against the shared release.
Troubleshooting Notes
Brand not discovered by release script
Check that:
- the build completed successfully
- the artifact is under the expected platform folder
- the artifact name matches the
asset_prefixand current version - the version used by the release script matches the built artifact version
Windows upgrade behavior is wrong
Check that the brand has its own stable msi_upgrade_code. Reusing or changing it incorrectly will break expected MSI upgrade semantics.
App uses the wrong local config folder
Check that runtime config uses the intended config_dir_name, and that it matches the packaging brand you expect.
Auto-update downloads the wrong installer
Check that:
- the release contains the correct installer files
release-manifest.jsonincludes the correct brand and platform entry- runtime update settings point to the expected repo/channel/manifest
Current Example Brand
The first branded variant currently in the repository is:
build/brands/agravity.json
Use it as the template for future branded variants.