Files
healthchat-web/.github/workflows/build-healthchat.yaml
T

201 lines
8.2 KiB
YAML

name: Release HealthChat
# Tag-driven release pipeline for HealthChat builds of element-web.
# Mirrors ../element-ios/.github/workflows/release-healthchat.yml.
# Runbook: .claude/skills/releasing-healthchat/SKILL.md
on:
push:
tags:
- "healthchat-v*"
workflow_dispatch:
inputs:
version:
description: "Version (e.g. 1.4.0). Required when running manually."
required: true
type: string
workspace_ref:
description: "Override healthchat-workspace ref (defaults to .healthchat-workspace-version)."
required: false
type: string
release_notes_override:
description: "Optional release notes; falls back to HEALTHCHAT_CHANGES.md section for the given version."
required: false
type: string
permissions:
contents: write # create GitHub Release
packages: write # push Docker image
id-token: write # attestations
attestations: write
concurrency:
group: release-healthchat-${{ github.event.inputs.version || github.ref_name }}
cancel-in-progress: false
jobs:
release:
name: Build & publish HealthChat image
runs-on: ubuntu-latest
env:
DOCKER_REGISTRY: "ghcr.io"
DOCKER_IMAGE: ${{ format('{0}/healthchat-web', github.repository) }}
DOCKER_PUSH: true
DOCKER_SIGNING: false
FORCE_COLOR: 1
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve version
id: version
run: |
set -euo pipefail
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
VERSION="${{ github.event.inputs.version }}"
else
VERSION="${GITHUB_REF_NAME#healthchat-v}"
fi
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Invalid version '$VERSION' — expected e.g. 1.4.0 or 1.4.0-rc1"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
# `:latest` is what the ansible inventory deploys from, so only a clean
# X.Y.Z tag push may move it — never an -rc/-hotfix/-dryrun tag, and never
# a manual re-run. Otherwise a release candidate or a pipeline test would
# become production on the next deploy of any kind.
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ && "$GITHUB_EVENT_NAME" == "push" ]]; then
IS_STABLE=true
else
IS_STABLE=false
fi
echo "is_stable=$IS_STABLE" >> "$GITHUB_OUTPUT"
echo "Resolved version: $VERSION (moves :latest: $IS_STABLE)"
- name: Resolve workspace ref
id: workspace
env:
OVERRIDE: ${{ github.event.inputs.workspace_ref }}
run: |
set -euo pipefail
if [[ -n "$OVERRIDE" ]]; then
REF="$OVERRIDE"
echo "Using workspace ref from workflow input: $REF"
elif [[ -f .healthchat-workspace-version ]]; then
REF=$(head -n1 .healthchat-workspace-version | tr -d '[:space:]')
if [[ -z "$REF" ]]; then
echo "::error::.healthchat-workspace-version is empty"
exit 1
fi
echo "Using workspace ref from .healthchat-workspace-version: $REF"
else
echo "::error::.healthchat-workspace-version missing and no workspace_ref override provided"
exit 1
fi
echo "ref=$REF" >> "$GITHUB_OUTPUT"
- name: Extract release notes
id: notes
env:
VERSION: ${{ steps.version.outputs.version }}
OVERRIDE: ${{ github.event.inputs.release_notes_override }}
run: |
set -euo pipefail
if [[ -n "$OVERRIDE" ]]; then
NOTES="$OVERRIDE"
echo "Using release notes from workflow input"
else
NOTES=$(awk -v ver="$VERSION" '
/^## Changes in / {
if (found) { exit }
if ($0 ~ "## Changes in " ver "( |$|\\()") { found = 1; next }
}
found { print }
' HEALTHCHAT_CHANGES.md | sed '/^[[:space:]]*$/{N;/^\n$/D;}')
if [[ -z "$NOTES" ]]; then
NOTES="HealthChat ${VERSION} — automated build from ${GITHUB_REF_NAME}."
echo "::warning::No HEALTHCHAT_CHANGES.md section found for $VERSION — using fallback"
fi
fi
{
echo 'notes<<NOTES_EOF'
echo "$NOTES"
echo 'NOTES_EOF'
} >> "$GITHUB_OUTPUT"
- name: Checkout healthchat-workspace
uses: actions/checkout@v4
with:
repository: Novaloop-AG/healthchat-workspace
path: healthchat-workspace
token: ${{ secrets.HEALTHCHAT_WORKSPACE_TOKEN }}
ref: ${{ steps.workspace.outputs.ref }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Build element-plugin
run: |
cd healthchat-workspace/element-plugin
npm ci
npm run build
cd module
npm ci
npm run build
- name: Stage plugin artifacts for Docker
run: |
mkdir -p plugin-dist/elements
cp healthchat-workspace/element-plugin/module/dist/healthchat-module.js plugin-dist/healthchat-module.js
cp healthchat-workspace/element-plugin/dist/elements/browser/main.js plugin-dist/elements/healthchat-elements.js
- name: Log in to the Github Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: metadata
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}
tags: |
type=raw,value=${{ steps.version.outputs.version }}
type=raw,value=latest,enable=${{ steps.version.outputs.is_stable == 'true' }}
type=raw,value=${{ github.sha }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker images
id: push
uses: docker/build-push-action@v6
with:
context: .
file: ./apps/web/Dockerfile
push: ${{ env.DOCKER_PUSH }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
- name: Generate artifact attestation
if: ${{ env.DOCKER_SIGNING == 'true' }}
uses: actions/attest-build-provenance@v1
with:
subject-name: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: ${{ env.DOCKER_PUSH }}
- name: Create GitHub Release
if: github.event_name == 'push'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: HealthChat ${{ steps.version.outputs.version }}
body: ${{ steps.notes.outputs.notes }}