diff --git a/.claude/skills/releasing-healthchat/SKILL.md b/.claude/skills/releasing-healthchat/SKILL.md index d95a6b7dc8..8dd5b25def 100644 --- a/.claude/skills/releasing-healthchat/SKILL.md +++ b/.claude/skills/releasing-healthchat/SKILL.md @@ -29,8 +29,10 @@ Releases are driven by git tags of the form `healthchat-v` pushed to **does not publish them**. See "Deploying the desktop release" below. - `.github/workflows/deploy-healthchat-desktop.yaml` (desktop deploy) — **manual dispatch only.** Downloads that tarball and mirrors it to - `https://healthchat.ch/packages/desktop/` over FTPS (the private GitHub Release can - serve neither end users nor the auto-updater). This is what makes installed apps + `https://packages.healthchat.ch/desktop/` over FTPS (the private GitHub Release can + 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 the two caveats (first install is manual; Windows auto-update needs the Setup.exe path, not the .msi). @@ -185,7 +187,7 @@ run or `:latest` had not moved yet. ## Deploying the desktop release The desktop build attaches `healthchat-desktop-feed-.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 installers. @@ -321,7 +323,7 @@ warning. Prefer fixing the changelog over relying on the fallback. "* Setup*.exe") **plus** `healthchat-desktop-feed-.tar.gz`. Without that tarball the desktop deploy has nothing to publish. - [ ] 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 ` 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. - [ ] 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: - `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 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. | | `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 → Environments). Without them the desktop builds still run but produce diff --git a/.github/workflows/build-healthchat-desktop.yaml b/.github/workflows/build-healthchat-desktop.yaml index 66a8a30120..478faaae57 100644 --- a/.github/workflows/build-healthchat-desktop.yaml +++ b/.github/workflows/build-healthchat-desktop.yaml @@ -11,7 +11,7 @@ name: Build HealthChat Desktop # # Notes: # - 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 # 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 @@ -195,10 +195,10 @@ jobs: targets: "dmg zip" # Generates dist/releases.json (the Squirrel.Mac feed manifest). The reusable appends # `/update/macos/` to this, so the in-manifest URL becomes - # https://healthchat.ch/packages/desktop/update/macos/, matching where the + # https://packages.healthchat.ch/desktop/update/macos/, matching where the # `release` job publishes the feed. Keep in sync with update_base_url in - # element.io/healthchat/config.json (.../packages/desktop/update/). - base-url: https://healthchat.ch/packages/desktop + # element.io/healthchat/config.json (.../desktop/update/). + base-url: https://packages.healthchat.ch/desktop windows: name: Windows ${{ matrix.arch }} @@ -273,7 +273,7 @@ jobs: gh release upload "$TAG" release-files/* --clobber # 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 # (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 diff --git a/.github/workflows/deploy-healthchat-desktop.yaml b/.github/workflows/deploy-healthchat-desktop.yaml index 2d71b607f1..0e1dc7e85b 100644 --- a/.github/workflows/deploy-healthchat-desktop.yaml +++ b/.github/workflows/deploy-healthchat-desktop.yaml @@ -1,13 +1,13 @@ 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 # (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 # 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 # deployment, not a publication step — the same reason the ansible playbook's deploy is a manual # dispatch rather than a release trigger. @@ -38,7 +38,7 @@ concurrency: jobs: deploy: - name: Publish update feed to healthchat.ch + name: Publish update feed to packages.healthchat.ch runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -126,11 +126,21 @@ jobs: # *.nupkg are preserved (Squirrel.Windows delta history) and only the staged files # are written/overwritten. If the host's FTPS cert is untrusted, set # 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" </dev/null || echo "000") if [[ "$code" == "200" ]]; then echo "ok $code $url"; else echo "FAIL $code $url"; fail=1; fi done diff --git a/apps/desktop/element.io/healthchat/README b/apps/desktop/element.io/healthchat/README index 8a2b58f227..df335ee4ee 100644 --- a/apps/desktop/element.io/healthchat/README +++ b/apps/desktop/element.io/healthchat/README @@ -98,14 +98,14 @@ runs can reach the signing credentials. Auto-update + public installer hosting -------------------------------------- -The apps self-update over a feed hosted on healthchat.ch (the GitHub Release is private to this -internal fork, so it can serve neither end users nor the Squirrel auto-updater). Both the update -feed and the public installers live under https://healthchat.ch/packages/desktop/ (FTP web root -/public_html/healthchat.ch maps to https://healthchat.ch/; no packages. subdomain, so it's a path): +The apps self-update over a feed hosted on packages.healthchat.ch (the GitHub Release is private +to this internal fork, so it can serve neither end users nor the Squirrel auto-updater). Both the +update feed and the public installers live under https://packages.healthchat.ch/desktop/ (the +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_base_url": "https://healthchat.ch/packages/desktop/update/" - and the macos job passes base-url: https://healthchat.ch/packages/desktop so the + "update_base_url": "https://packages.healthchat.ch/desktop/update/" + and the macos job passes base-url: https://packages.healthchat.ch/desktop so the generated releases.json points the *-mac.zip at this origin. update/macos/releases.json + HealthChat--mac.zip (Squirrel.Mac) update/win32//RELEASES + HealthChat--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-.dmg + HealthChat.dmg (stable "latest") install/win32//HealthChat-Setup-.exe + HealthChat-Setup.exe (stable) + *.msi -The `release` job in build-healthchat-desktop.yaml stages these trees and uploads them via lftp/FTPS -(mirror -R, no-delete so prior nupkgs survive for delta history). Add these repo secrets: +The `release` job in build-healthchat-desktop.yaml stages these trees; the manually dispatched +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, flip ssl:verify-certificate to no in the workflow) FTP_USERNAME FTP_PASSWORD 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 self-update to the first build that carries it; users must download once from the install/ URL. Every release after that updates automatically. diff --git a/apps/desktop/element.io/healthchat/config.json b/apps/desktop/element.io/healthchat/config.json index 0fb25e2d2b..b3fe963dd8 100644 --- a/apps/desktop/element.io/healthchat/config.json +++ b/apps/desktop/element.io/healthchat/config.json @@ -88,7 +88,7 @@ ] }, "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"], "ch.healthchat": { "api_base_url": "https://api.healthchat.ch", "elements_base_url": "/webapp/modules/elements" } }