mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-02 20:38:52 +00:00
167 lines
8.7 KiB
YAML
167 lines
8.7 KiB
YAML
name: Deploy HealthChat Desktop
|
|
|
|
# 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 stages the
|
|
# update feed + public installers at packages.healthchat.ch/desktop-releases/<version>/, but
|
|
# never touches the live feed. Nothing reaches installed apps until this workflow is dispatched
|
|
# by hand.
|
|
#
|
|
# 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.
|
|
#
|
|
# The versioned archive directories persist on the server, so any staged version can be
|
|
# re-published without rebuilding or re-signing — that is the rollback path.
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: "Version to publish (e.g. 1.5.0). Must have a staged feed at packages.healthchat.ch/desktop-releases/<version>/ (produced by Build HealthChat Desktop)."
|
|
required: true
|
|
type: string
|
|
dry_run:
|
|
description: "Dry run — list what would be uploaded without writing to the server."
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions: {} # the feed comes from the packages.healthchat.ch archive, not from GitHub
|
|
|
|
concurrency:
|
|
# Never let two runs mirror onto the same FTP tree at once.
|
|
group: deploy-healthchat-desktop
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
deploy:
|
|
name: Publish update feed to packages.healthchat.ch
|
|
runs-on: ubuntu-latest
|
|
# Fetches ~2 GiB from the archive and uploads it to the live feed.
|
|
timeout-minutes: 60
|
|
steps:
|
|
- name: Resolve version
|
|
id: version
|
|
env:
|
|
INPUT_VERSION: ${{ inputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION="$INPUT_VERSION"
|
|
# Same regex as the release workflows, so -rc1/-hotfix1 builds can be published
|
|
# deliberately (e.g. to a test machine) but never by accident.
|
|
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
|
|
echo "::error::Invalid version '$VERSION' — expected e.g. 1.5.0 or 1.5.0-rc1"
|
|
exit 1
|
|
fi
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "tag=healthchat-v$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "Publishing HealthChat Desktop $VERSION (tag healthchat-v$VERSION)"
|
|
|
|
- name: Install lftp
|
|
run: sudo apt-get update && sudo apt-get install -y lftp
|
|
|
|
- name: Fetch staged feed from the archive
|
|
env:
|
|
FTP_SERVER: ${{ secrets.FTP_SERVER }}
|
|
FTP_USERNAME: ${{ secrets.FTP_USERNAME }}
|
|
# Passed to lftp via LFTP_PASSWORD (--env-password) so it never appears on the
|
|
# command line / process list.
|
|
LFTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
: "${FTP_SERVER:?FTP_SERVER secret not set}"
|
|
: "${FTP_USERNAME:?FTP_USERNAME secret not set}"
|
|
: "${LFTP_PASSWORD:?FTP_PASSWORD secret not set}"
|
|
# The archive is public — probe it over HTTPS first for a clear error before
|
|
# opening the FTP session.
|
|
if ! curl -fsSI "https://packages.healthchat.ch/desktop-releases/$VERSION/update/macos/releases.json" >/dev/null 2>&1; then
|
|
echo "::error::No staged feed at https://packages.healthchat.ch/desktop-releases/$VERSION/ — run 'Build HealthChat Desktop' (workflow_dispatch, version=$VERSION) to build and stage this version, then re-run the deploy."
|
|
exit 1
|
|
fi
|
|
lftp -u "$FTP_USERNAME" --env-password "$FTP_SERVER" <<LFTP
|
|
set cmd:fail-exit true
|
|
set ftp:ssl-force true
|
|
set ftp:ssl-protect-data true
|
|
set ssl:verify-certificate true
|
|
mirror --parallel=4 --verbose /public_html/packages/desktop-releases/$VERSION/ feed/
|
|
bye
|
|
LFTP
|
|
test -d feed || { echo "::error::Archive fetch produced no feed/ directory"; exit 1; }
|
|
echo "--- feed to publish ---"
|
|
find feed -type f | sort
|
|
|
|
- name: Sanity-check the feed before publishing
|
|
run: |
|
|
set -euo pipefail
|
|
# A partial feed is worse than none: Squirrel clients poll these paths, and a
|
|
# missing manifest breaks auto-update for everyone already installed.
|
|
fail=0
|
|
for f in feed/update/macos/releases.json \
|
|
feed/update/win32/x64/RELEASES \
|
|
feed/update/win32/arm64/RELEASES; do
|
|
if [[ ! -s "$f" ]]; then echo "::error::missing or empty: $f"; fail=1; fi
|
|
done
|
|
# The macOS updater fetches the zip named in releases.json; make sure it is present.
|
|
if [[ -s feed/update/macos/releases.json ]]; then
|
|
if ! ls feed/update/macos/*-mac.zip >/dev/null 2>&1; then
|
|
echo "::error::releases.json present but no *-mac.zip payload alongside it"; fail=1
|
|
fi
|
|
fi
|
|
exit $fail
|
|
|
|
- name: Publish to packages.healthchat.ch over FTPS
|
|
env:
|
|
FTP_SERVER: ${{ secrets.FTP_SERVER }}
|
|
FTP_USERNAME: ${{ secrets.FTP_USERNAME }}
|
|
# Passed to lftp via LFTP_PASSWORD (--env-password) so it never appears on the
|
|
# command line / process list.
|
|
LFTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
|
|
DRY_RUN: ${{ inputs.dry_run }}
|
|
run: |
|
|
set -euo pipefail
|
|
: "${FTP_SERVER:?FTP_SERVER secret not set}"
|
|
: "${FTP_USERNAME:?FTP_USERNAME secret not set}"
|
|
: "${LFTP_PASSWORD:?FTP_PASSWORD secret not set}"
|
|
MIRROR_OPTS="--parallel=4 --verbose"
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
echo "::notice::DRY RUN — no files will be written to the server."
|
|
MIRROR_OPTS="$MIRROR_OPTS --dry-run"
|
|
fi
|
|
# mirror -R (reverse) uploads the local tree; default is no-delete, so prior
|
|
# *.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.
|
|
lftp -u "$FTP_USERNAME" --env-password "$FTP_SERVER" <<LFTP
|
|
set cmd:fail-exit true
|
|
set ftp:ssl-force true
|
|
set ftp:ssl-protect-data true
|
|
set ssl:verify-certificate true
|
|
mkdir -pf /public_html/packages/desktop
|
|
mirror -R $MIRROR_OPTS feed/ /public_html/packages/desktop/
|
|
bye
|
|
LFTP
|
|
|
|
- name: Verify published feed
|
|
if: ${{ !inputs.dry_run }}
|
|
run: |
|
|
set -euo pipefail
|
|
base="https://packages.healthchat.ch/desktop"
|
|
fail=0
|
|
for url in "$base/update/macos/releases.json" \
|
|
"$base/update/win32/x64/RELEASES" \
|
|
"$base/update/win32/arm64/RELEASES" \
|
|
"$base/install/macos/HealthChat.dmg" \
|
|
"$base/install/win32/x64/HealthChat-Setup.exe"; do
|
|
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
|
|
done
|
|
if [[ $fail -ne 0 ]]; then
|
|
echo "::error::One or more feed URLs are not reachable — auto-update may be broken."
|
|
exit 1
|
|
fi
|
|
echo "::notice::Update feed for ${{ steps.version.outputs.version }} is live. Installed apps will pick it up on their next update check."
|