chore: move desktop packages to packages.healthchat.ch

This commit is contained in:
2026-07-29 22:28:43 +02:00
parent 675e812f00
commit ab295d810c
5 changed files with 51 additions and 26 deletions
+10 -7
View File
@@ -29,8 +29,10 @@ Releases are driven by git tags of the form `healthchat-v<semver>` pushed to
**does not publish them**. See "Deploying the desktop release" below. **does not publish them**. See "Deploying the desktop release" below.
- `.github/workflows/deploy-healthchat-desktop.yaml` (desktop deploy) — **manual - `.github/workflows/deploy-healthchat-desktop.yaml` (desktop deploy) — **manual
dispatch only.** Downloads that tarball and mirrors it to dispatch only.** Downloads that tarball and mirrors it to
`https://healthchat.ch/packages/desktop/` over FTPS (the private GitHub Release can `https://packages.healthchat.ch/desktop/` over FTPS (the private GitHub Release can
serve neither end users nor the auto-updater). This is what makes installed apps serve neither end users nor the auto-updater), plus the legacy
`https://healthchat.ch/packages/desktop/` path while clients built <= 1.5.0 are still
in the field. This is what makes installed apps
self-update; see `apps/desktop/element.io/healthchat/README` for the feed layout and self-update; see `apps/desktop/element.io/healthchat/README` for the feed layout and
the two caveats (first install is manual; Windows auto-update needs the Setup.exe path, the two caveats (first install is manual; Windows auto-update needs the Setup.exe path,
not the .msi). not the .msi).
@@ -185,7 +187,7 @@ run or `:latest` had not moved yet.
## Deploying the desktop release ## Deploying the desktop release
The desktop build attaches `healthchat-desktop-feed-<version>.tar.gz` to the Release but The desktop build attaches `healthchat-desktop-feed-<version>.tar.gz` to the Release but
leaves `https://healthchat.ch/packages/desktop/` untouched. Until you run this, installed leaves `https://packages.healthchat.ch/desktop/` untouched. Until you run this, installed
apps keep whatever version they have and the public download links still serve the old apps keep whatever version they have and the public download links still serve the old
installers. installers.
@@ -321,7 +323,7 @@ warning. Prefer fixing the changelog over relying on the fallback.
"* Setup*.exe") **plus** `healthchat-desktop-feed-<version>.tar.gz`. Without that "* Setup*.exe") **plus** `healthchat-desktop-feed-<version>.tar.gz`. Without that
tarball the desktop deploy has nothing to publish. tarball the desktop deploy has nothing to publish.
- [ ] Confirm the live feed is still on the **previous** version — releasing must not have - [ ] Confirm the live feed is still on the **previous** version — releasing must not have
moved it. `curl -fsS https://healthchat.ch/packages/desktop/update/macos/releases.json` moved it. `curl -fsS https://packages.healthchat.ch/desktop/update/macos/releases.json`
- [ ] Smoke-check signing before shipping to users: macOS `spctl -a -vvv -t install <app>` - [ ] Smoke-check signing before shipping to users: macOS `spctl -a -vvv -t install <app>`
is accepted; Windows installer shows a valid publisher (no SmartScreen block). is accepted; Windows installer shows a valid publisher (no SmartScreen block).
@@ -332,9 +334,10 @@ release"):
- [ ] Web deployed and `curl -fsS https://app.healthchat.ch/version` reports the new version. - [ ] Web deployed and `curl -fsS https://app.healthchat.ch/version` reports the new version.
- [ ] Desktop feed deployed: the "Deploy HealthChat Desktop" run is green. Its own verify - [ ] Desktop feed deployed: the "Deploy HealthChat Desktop" run is green. Its own verify
step already asserts the five feed URLs return 200, so a green run is the check. step already asserts the feed URLs (canonical + legacy) return 200, so a green run is
the check.
- [ ] `releases.json` now advertises the new version: - [ ] `releases.json` now advertises the new version:
`curl -fsS https://healthchat.ch/packages/desktop/update/macos/releases.json | jq -r '.releases[0].updateTo.version'` `curl -fsS https://packages.healthchat.ch/desktop/update/macos/releases.json | jq -r '.releases[0].updateTo.version'`
- [ ] Spot-check auto-update on a real installed client (or at least confirm the app's - [ ] Spot-check auto-update on a real installed client (or at least confirm the app's
update check reaches the feed) before announcing. update check reaches the feed) before announcing.
@@ -346,7 +349,7 @@ Configured in repo Settings → Secrets and variables → Actions:
|---|---| |---|---|
| `HEALTHCHAT_WORKSPACE_TOKEN` | PAT with read access to `Novaloop-AG/healthchat-workspace`. Needed by `actions/checkout` for the private plugin repo. | | `HEALTHCHAT_WORKSPACE_TOKEN` | PAT with read access to `Novaloop-AG/healthchat-workspace`. Needed by `actions/checkout` for the private plugin repo. |
| `GITHUB_TOKEN` | Auto-provided. Used for ghcr.io login and GitHub Release creation. No manual setup required. | | `GITHUB_TOKEN` | Auto-provided. Used for ghcr.io login and GitHub Release creation. No manual setup required. |
| `FTP_SERVER` / `FTP_USERNAME` / `FTP_PASSWORD` | Credentials for the healthchat.ch FTP(S) account. Used **only** by `deploy-healthchat-desktop.yaml` to publish the update feed + public installers under `/public_html/healthchat.ch/packages/desktop/`. `FTP_SERVER` is the hostname only (explicit FTPS / AUTH TLS). The release workflows never touch FTP, so a missing/expired credential breaks the deploy but never the release. | | `FTP_SERVER` / `FTP_USERNAME` / `FTP_PASSWORD` | Credentials for the healthchat.ch FTP(S) account. Used **only** by `deploy-healthchat-desktop.yaml` to publish the update feed + public installers under `/public_html/packages/desktop/` (docroot of packages.healthchat.ch) plus, during the URL migration, the legacy `/public_html/healthchat.ch/packages/desktop/`. `FTP_SERVER` is the hostname only (explicit FTPS / AUTH TLS). The release workflows never touch FTP, so a missing/expired credential breaks the deploy but never the release. |
Desktop signing additionally requires two GitHub **environments** (Settings → Desktop signing additionally requires two GitHub **environments** (Settings →
Environments). Without them the desktop builds still run but produce Environments). Without them the desktop builds still run but produce
@@ -11,7 +11,7 @@ name: Build HealthChat Desktop
# #
# Notes: # Notes:
# - This workflow RELEASES but does not DEPLOY. It attaches the installers and a staged update-feed # - This workflow RELEASES but does not DEPLOY. It attaches the installers and a staged update-feed
# tarball to the GitHub Release; the live auto-update feed at healthchat.ch/packages/desktop/ is # tarball to the GitHub Release; the live auto-update feed at packages.healthchat.ch/desktop/ is
# only touched by the manually dispatched deploy-healthchat-desktop.yaml. So cutting a tag never # only touched by the manually dispatched deploy-healthchat-desktop.yaml. So cutting a tag never
# pushes an update to already-installed apps until a human decides to ship it. # pushes an update to already-installed apps until a human decides to ship it.
# - The reusable build workflows are pointed at THIS fork (repository/ref inputs) so the Electron # - The reusable build workflows are pointed at THIS fork (repository/ref inputs) so the Electron
@@ -195,10 +195,10 @@ jobs:
targets: "dmg zip" targets: "dmg zip"
# Generates dist/releases.json (the Squirrel.Mac feed manifest). The reusable appends # Generates dist/releases.json (the Squirrel.Mac feed manifest). The reusable appends
# `/update/macos/<zip>` to this, so the in-manifest URL becomes # `/update/macos/<zip>` to this, so the in-manifest URL becomes
# https://healthchat.ch/packages/desktop/update/macos/<zip>, matching where the # https://packages.healthchat.ch/desktop/update/macos/<zip>, matching where the
# `release` job publishes the feed. Keep in sync with update_base_url in # `release` job publishes the feed. Keep in sync with update_base_url in
# element.io/healthchat/config.json (.../packages/desktop/update/). # element.io/healthchat/config.json (.../desktop/update/).
base-url: https://healthchat.ch/packages/desktop base-url: https://packages.healthchat.ch/desktop
windows: windows:
name: Windows ${{ matrix.arch }} name: Windows ${{ matrix.arch }}
@@ -273,7 +273,7 @@ jobs:
gh release upload "$TAG" release-files/* --clobber gh release upload "$TAG" release-files/* --clobber
# The GitHub Release above is private (internal fork), so it can't serve end users or the # The GitHub Release above is private (internal fork), so it can't serve end users or the
# Squirrel auto-updater. Those are served from healthchat.ch/packages/desktop/, which is # Squirrel auto-updater. Those are served from packages.healthchat.ch/desktop/, which is
# published by the SEPARATE, MANUALLY DISPATCHED "Deploy HealthChat Desktop" workflow # published by the SEPARATE, MANUALLY DISPATCHED "Deploy HealthChat Desktop" workflow
# (deploy-healthchat-desktop.yaml) — releasing never touches the live update feed. # (deploy-healthchat-desktop.yaml) — releasing never touches the live update feed.
# This job only *stages* that feed and attaches it to the Release as a tarball, so the # This job only *stages* that feed and attaches it to the Release as a tarball, so the
@@ -1,13 +1,13 @@
name: Deploy HealthChat Desktop name: Deploy HealthChat Desktop
# Publishes an already-released desktop build to healthchat.ch/packages/desktop/ over FTPS. # Publishes an already-released desktop build to packages.healthchat.ch/desktop/ over FTPS.
# #
# This is the DEPLOY half of the desktop pipeline, deliberately split from the release half # This is the DEPLOY half of the desktop pipeline, deliberately split from the release half
# (build-healthchat-desktop.yaml). Cutting a `healthchat-v*` tag builds, signs and attaches the # (build-healthchat-desktop.yaml). Cutting a `healthchat-v*` tag builds, signs and attaches the
# installers plus a staged update-feed tarball to the GitHub Release, but never touches the live # installers plus a staged update-feed tarball to the GitHub Release, but never touches the live
# feed. Nothing reaches installed apps until this workflow is dispatched by hand. # feed. Nothing reaches installed apps until this workflow is dispatched by hand.
# #
# Why that split matters: the feed under .../packages/desktop/update/ is what the Squirrel # Why that split matters: the feed under .../desktop/update/ is what the Squirrel
# auto-updater polls. Writing to it pushes an update to every installed client, so it is a # auto-updater polls. Writing to it pushes an update to every installed client, so it is a
# deployment, not a publication step — the same reason the ansible playbook's deploy is a manual # deployment, not a publication step — the same reason the ansible playbook's deploy is a manual
# dispatch rather than a release trigger. # dispatch rather than a release trigger.
@@ -38,7 +38,7 @@ concurrency:
jobs: jobs:
deploy: deploy:
name: Publish update feed to healthchat.ch name: Publish update feed to packages.healthchat.ch
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
@@ -126,11 +126,21 @@ jobs:
# *.nupkg are preserved (Squirrel.Windows delta history) and only the staged files # *.nupkg are preserved (Squirrel.Windows delta history) and only the staged files
# are written/overwritten. If the host's FTPS cert is untrusted, set # are written/overwritten. If the host's FTPS cert is untrusted, set
# ssl:verify-certificate to no. # ssl:verify-certificate to no.
#
# Dual-publish during the URL migration: /public_html/packages is the docroot of
# packages.healthchat.ch (the canonical feed URL since builds > 1.5.0), while
# /public_html/healthchat.ch/packages/desktop serves the legacy
# healthchat.ch/packages/desktop URL that clients built <= 1.5.0 still poll —
# they only learn the new URL by installing an update delivered over the old
# feed. Drop the legacy mirror (and its verify checks below) once the installed
# base is on a build carrying the new update_base_url.
lftp -u "$FTP_USERNAME" --env-password "$FTP_SERVER" <<LFTP lftp -u "$FTP_USERNAME" --env-password "$FTP_SERVER" <<LFTP
set cmd:fail-exit true set cmd:fail-exit true
set ftp:ssl-force true set ftp:ssl-force true
set ftp:ssl-protect-data true set ftp:ssl-protect-data true
set ssl:verify-certificate true set ssl:verify-certificate true
mkdir -pf /public_html/packages/desktop
mirror -R $MIRROR_OPTS feed/ /public_html/packages/desktop/
mkdir -pf /public_html/healthchat.ch/packages/desktop mkdir -pf /public_html/healthchat.ch/packages/desktop
mirror -R $MIRROR_OPTS feed/ /public_html/healthchat.ch/packages/desktop/ mirror -R $MIRROR_OPTS feed/ /public_html/healthchat.ch/packages/desktop/
bye bye
@@ -140,13 +150,19 @@ jobs:
if: ${{ !inputs.dry_run }} if: ${{ !inputs.dry_run }}
run: | run: |
set -euo pipefail set -euo pipefail
base="https://healthchat.ch/packages/desktop" base="https://packages.healthchat.ch/desktop"
# The legacy healthchat.ch URLs keep clients built <= 1.5.0 updating; drop them
# together with the legacy mirror above once the installed base has migrated.
legacy="https://healthchat.ch/packages/desktop"
fail=0 fail=0
for url in "$base/update/macos/releases.json" \ for url in "$base/update/macos/releases.json" \
"$base/update/win32/x64/RELEASES" \ "$base/update/win32/x64/RELEASES" \
"$base/update/win32/arm64/RELEASES" \ "$base/update/win32/arm64/RELEASES" \
"$base/install/macos/HealthChat.dmg" \ "$base/install/macos/HealthChat.dmg" \
"$base/install/win32/x64/HealthChat-Setup.exe"; do "$base/install/win32/x64/HealthChat-Setup.exe" \
"$legacy/update/macos/releases.json" \
"$legacy/update/win32/x64/RELEASES" \
"$legacy/update/win32/arm64/RELEASES"; do
code=$(curl -fsS -o /dev/null -w '%{http_code}' -I "$url" 2>/dev/null || echo "000") code=$(curl -fsS -o /dev/null -w '%{http_code}' -I "$url" 2>/dev/null || echo "000")
if [[ "$code" == "200" ]]; then echo "ok $code $url"; else echo "FAIL $code $url"; fail=1; fi if [[ "$code" == "200" ]]; then echo "ok $code $url"; else echo "FAIL $code $url"; fail=1; fi
done done
+14 -8
View File
@@ -98,14 +98,14 @@ runs can reach the signing credentials.
Auto-update + public installer hosting Auto-update + public installer hosting
-------------------------------------- --------------------------------------
The apps self-update over a feed hosted on healthchat.ch (the GitHub Release is private to this The apps self-update over a feed hosted on packages.healthchat.ch (the GitHub Release is private
internal fork, so it can serve neither end users nor the Squirrel auto-updater). Both the update to this internal fork, so it can serve neither end users nor the Squirrel auto-updater). Both the
feed and the public installers live under https://healthchat.ch/packages/desktop/ (FTP web root update feed and the public installers live under https://packages.healthchat.ch/desktop/ (the
/public_html/healthchat.ch maps to https://healthchat.ch/; no packages. subdomain, so it's a path): subdomain's docroot on the healthchat.ch FTP account is /public_html/packages):
update/ the Squirrel feed the installed apps poll hourly. config.json sets update/ the Squirrel feed the installed apps poll hourly. config.json sets
"update_base_url": "https://healthchat.ch/packages/desktop/update/" "update_base_url": "https://packages.healthchat.ch/desktop/update/"
and the macos job passes base-url: https://healthchat.ch/packages/desktop so the and the macos job passes base-url: https://packages.healthchat.ch/desktop so the
generated releases.json points the *-mac.zip at this origin. generated releases.json points the *-mac.zip at this origin.
update/macos/releases.json + HealthChat-<ver>-mac.zip (Squirrel.Mac) update/macos/releases.json + HealthChat-<ver>-mac.zip (Squirrel.Mac)
update/win32/<arch>/RELEASES + HealthChat-<ver>-full.nupkg (Squirrel.Windows; x64 + arm64) update/win32/<arch>/RELEASES + HealthChat-<ver>-full.nupkg (Squirrel.Windows; x64 + arm64)
@@ -113,14 +113,20 @@ feed and the public installers live under https://healthchat.ch/packages/desktop
install/macos/HealthChat-<ver>.dmg + HealthChat.dmg (stable "latest") install/macos/HealthChat-<ver>.dmg + HealthChat.dmg (stable "latest")
install/win32/<arch>/HealthChat-Setup-<ver>.exe + HealthChat-Setup.exe (stable) + *.msi install/win32/<arch>/HealthChat-Setup-<ver>.exe + HealthChat-Setup.exe (stable) + *.msi
The `release` job in build-healthchat-desktop.yaml stages these trees and uploads them via lftp/FTPS The `release` job in build-healthchat-desktop.yaml stages these trees; the manually dispatched
(mirror -R, no-delete so prior nupkgs survive for delta history). Add these repo secrets: deploy-healthchat-desktop.yaml uploads them via lftp/FTPS (mirror -R, no-delete so prior nupkgs
survive for delta history). Add these repo secrets:
FTP_SERVER hostname only (explicit FTPS / AUTH TLS on port 21; if the host cert is untrusted, FTP_SERVER hostname only (explicit FTPS / AUTH TLS on port 21; if the host cert is untrusted,
flip ssl:verify-certificate to no in the workflow) flip ssl:verify-certificate to no in the workflow)
FTP_USERNAME FTP_USERNAME
FTP_PASSWORD FTP_PASSWORD
Caveats: Caveats:
- URL migration: builds <= 1.5.0 poll the legacy feed at
https://healthchat.ch/packages/desktop/ (docroot /public_html/healthchat.ch/packages/) —
they only learn the new URL by installing an update delivered over that old feed. The
deploy workflow therefore dual-publishes to both directories; drop the legacy mirror and
its verify checks once the installed base is on a build carrying the new update_base_url.
- First install is manual. Apps already installed without update_base_url (<= 1.1.2) cannot - First install is manual. Apps already installed without update_base_url (<= 1.1.2) cannot
self-update to the first build that carries it; users must download once from the install/ URL. self-update to the first build that carries it; users must download once from the install/ URL.
Every release after that updates automatically. Every release after that updates automatically.
@@ -88,7 +88,7 @@
] ]
}, },
"show_labs_settings": false, "show_labs_settings": false,
"update_base_url": "https://healthchat.ch/packages/desktop/update/", "update_base_url": "https://packages.healthchat.ch/desktop/update/",
"modules": ["/modules/healthchat-module.js"], "modules": ["/modules/healthchat-module.js"],
"ch.healthchat": { "api_base_url": "https://api.healthchat.ch", "elements_base_url": "/webapp/modules/elements" } "ch.healthchat": { "api_base_url": "https://api.healthchat.ch", "elements_base_url": "/webapp/modules/elements" }
} }