# 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:

1. Packaging identity
   Controls installer name, executable/app bundle name, product display name, bundle identifier, MSI upgrade code, installer artwork, and related metadata.

2. 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 generation
- `build/brands/agravity.json`: example branded manifest
- `build/scripts/build_windows.py`: Windows build entrypoint
- `build/scripts/build_macos.sh`: macOS build entrypoint
- `build/scripts/create_release.ps1`: Windows release uploader
- `build/scripts/create_release.sh`: macOS release uploader
- `config.example.json`: example runtime branding config

## Create a New Brand

To create a new brand, add a new manifest file under `build/brands/`.

Example:

1. Copy `build/brands/template.jsonc` to `build/brands/<new-brand>.json`.
2. Update the values for the new brand.
3. Add any brand-specific assets if you do not want to reuse the default icons/license assets.

Minimal example:

```json
{
  "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 entries
- `display_name`: user-facing product name
- `asset_prefix`: base name for installer artifacts and app bundle name
- `exe_name`: executable name for Windows and app bundle name base for macOS
- `manufacturer`: MSI manufacturer string
- `install_dir_name`: installation directory name shown to the OS
- `shortcut_description`: Windows shortcut description
- `bundle_identifier`: macOS bundle identifier
- `config_dir_name`: local app config/log/cache directory name
- `msi_upgrade_code`: stable GUID for Windows upgrades
- `update_channel`: currently typically `stable`

Generate a new `msi_upgrade_code` for a new brand once and keep it stable afterwards.

Examples:

```powershell
New-Guid
```

```bash
uuidgen
```

### Asset Fields

These can point at brand-specific files or default shared files:

- `icon_ico`
- `icon_icns`
- `dialog_bmp`
- `banner_bmp`
- `license_rtf`

Optional toolbar icon overrides:

- `toolbar_icon_home`
- `toolbar_icon_reload`
- `toolbar_icon_open`
- `toolbar_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_id`
- `asset_prefix`
- `exe_name`
- `bundle_identifier`
- `config_dir_name`
- `msi_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:

1. Update the brand manifest in `build/brands/<brand>.json`.
2. If needed, update brand-specific assets referenced by that manifest.
3. If runtime behavior should also change, update the relevant application config values.
4. Rebuild the affected platform artifacts.
5. Validate the result with a dry-run release before publishing.

Safe edits after release usually include:

- `display_name`
- `shortcut_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_id`
- `config_dir_name`
- `app_name`
- `update_base_url`
- `update_repo`
- `update_channel`
- `update_manifest_name`

Toolbar icon env overrides (useful for packaged branding):

- `TOOLBAR_ICON_HOME`
- `TOOLBAR_ICON_RELOAD`
- `TOOLBAR_ICON_OPEN`
- `TOOLBAR_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_id`
- `config_dir_name`
- `app_name`

## Build the Default Product

### Windows

Build the default executable only:

```powershell
python .\build\scripts\build_windows.py
```

Build the default Windows MSI:

```powershell
python .\build\scripts\build_windows.py --msi
```

Build with a specific `.env` file:

```powershell
python .\build\scripts\build_windows.py --msi --env-file .\.env
```

### macOS

Build the default macOS app and DMG:

```bash
bash build/scripts/build_macos.sh
```

Build with a specific `.env` file:

```bash
bash build/scripts/build_macos.sh --env-file .env
```

## Build a Brand

### Windows

Build a branded executable only:

```powershell
python .\build\scripts\build_windows.py --brand agravity
```

Build a branded MSI:

```powershell
python .\build\scripts\build_windows.py --brand agravity --msi
```

### macOS

Build a branded macOS app and DMG:

```bash
bash build/scripts/build_macos.sh --brand agravity
```

## Build Output Locations

Windows artifacts are written to:

- `build/dist/windows/webdrop_bridge/` for the default product
- `build/dist/windows/<brand_id>/` for branded products

macOS artifacts are written to:

- `build/dist/macos/webdrop_bridge/` for the default product
- `build/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.json` is merged so later runs do not wipe earlier platform entries

### Windows Release

Dry run first:

```powershell
.\build\scripts\create_release.ps1 -DryRun
```

Publish all locally built Windows variants for the current version:

```powershell
.\build\scripts\create_release.ps1
```

Publish only selected brands:

```powershell
.\build\scripts\create_release.ps1 -Brands agravity
```

Publish only the default product:

```powershell
.\build\scripts\create_release.ps1 -Brands webdrop_bridge
```

Publish a specific version:

```powershell
.\build\scripts\create_release.ps1 -Version 0.8.4
```

### macOS Release

Dry run first:

```bash
bash build/scripts/create_release.sh --dry-run
```

Publish all locally built macOS variants for the current version:

```bash
bash build/scripts/create_release.sh
```

Publish only selected brands:

```bash
bash build/scripts/create_release.sh --brand agravity
```

Publish only the default product:

```bash
bash build/scripts/create_release.sh --brand webdrop_bridge
```

Publish a specific version:

```bash
bash build/scripts/create_release.sh --version 0.8.4
```

### Credentials

Both release scripts use Forgejo credentials from environment variables when available:

- `FORGEJO_USER`
- `FORGEJO_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-x64`
- `macos-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.msi`
- `WebDropBridge-0.8.4-macos-universal.dmg`
- `AgravityBridge-0.8.4-win-x64.msi`
- `AgravityBridge-0.8.4-macos-universal.dmg`

### wget Examples

```bash
# 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

```bash
# 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

```powershell
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

1. Create `build/brands/<brand>.json`.
2. Add or update brand-specific assets if needed.
3. Prepare matching runtime config values.
4. Build the brand on Windows and/or macOS.
5. Run the release script in dry-run mode.
6. Verify artifact names, discovered brands, and manifest contents.
7. Run the actual release script.
8. 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_prefix` and 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.json` includes 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.