Skip to content

Server telemetry (#850) #274

Server telemetry (#850)

Server telemetry (#850) #274

Workflow file for this run

# Build Snapshot — Builds snapshot Docker images on every push to develop.
#
# Builds both nginx (React frontend) and tomcat (Grails backend) images and pushes them
# to ECR with snapshot tags. Also triggered when hoist-core or hoist-react publish new
# snapshots (via repository_dispatch). For numbered releases, see Build Release.
name: Build Snapshot
on:
push:
branches: [ "develop" ]
repository_dispatch:
types: [ hoist-core-snapshot, hoist-react-snapshot ]
workflow_dispatch:
# Debounce builds by branch. Newer runs from a branch will cancel the current run and start over.
concurrency:
group: build-snapshot
cancel-in-progress: true
env:
TOMCAT_IMAGE: xh/toolbox-tomcat
NGINX_IMAGE: xh/toolbox-nginx
jobs:
prepare:
runs-on: ubuntu-latest
permissions: {}
outputs:
build-tag: ${{ steps.tag.outputs.build-tag }}
steps:
- name: Generate build tag
id: tag
run: echo "build-tag=${GITHUB_SHA::7}_${GITHUB_REF_NAME}_$(date -u +'%Y-%m-%dT%H:%MZ')" >> "$GITHUB_OUTPUT"
build-tomcat:
needs: prepare
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- name: Setup JDK 25
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
with:
build-scan-publish: false
build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service'
build-scan-terms-of-use-agree: 'yes'
- name: Build WAR
run: ./gradlew -PxhAppBuild="${{ needs.prepare.outputs.build-tag }}" war
- name: Copy WAR into Docker context
run: cp build/libs/*.war docker/tomcat/app.war
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push tomcat image
env:
ECR_ROOT: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
run: |
docker build -t "$ECR_ROOT/${{ env.TOMCAT_IMAGE }}:snapshot" docker/tomcat
docker push "$ECR_ROOT/${{ env.TOMCAT_IMAGE }}:snapshot"
build-nginx:
needs: prepare
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'client-app/.nvmrc'
# Cache the yarn download directory directly (rather than via setup-node's
# `cache: yarn`, which keys strictly on the lockfile hash with no fallback).
# The `restore-keys` prefix lets a lockfile-changed run restore the most
# recent prior cache, so `yarn install` only downloads packages that
# actually moved — important given the FontAwesome Pro bandwidth cap.
# Note: snapshot runs `yarn upgrade`, which can fetch packages beyond the
# committed lockfile. On primary-key hits those deltas aren't persisted,
# so a newly-published FontAwesome version gets re-downloaded on each
# snapshot until the lockfile catches up. The "Detect FontAwesome
# lockfile drift" step below surfaces exactly this case as a warning so
# it gets resolved by committing an updated lockfile.
- name: Resolve Yarn cache directory
id: yarn-cache-dir
run: echo "dir=$(cd client-app && yarn cache dir)" >> "$GITHUB_OUTPUT"
- name: Restore Yarn cache
uses: actions/cache@v5
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
key: yarn-${{ runner.os }}-${{ hashFiles('client-app/yarn.lock') }}
restore-keys: |
yarn-${{ runner.os }}-
- name: Configure Font Awesome registry auth
env:
FONTAWESOME_PACKAGE_TOKEN: ${{ secrets.FONTAWESOME_PACKAGE_TOKEN }}
run: echo "//npm.fontawesome.com/:_authToken=$FONTAWESOME_PACKAGE_TOKEN" >> client-app/.npmrc
- name: Install and upgrade dependencies, lint, and build client app
run: cd client-app && yarn install && yarn upgrade && yarn lint && yarn build --env appBuild="${{ needs.prepare.outputs.build-tag }}"
# `yarn upgrade` rewrites yarn.lock when any semver-compatible transitive
# has moved. That's expected and usually too noisy to act on — but a
# FontAwesome bump matters, because every run until we commit the updated
# lockfile will re-download the new FA tarballs against our bandwidth cap.
# This step filters lockfile drift specifically to @fortawesome changes
# and surfaces a workflow warning + summary pointing at the standard
# remediation: run `yarn upgrade` locally, verify, and commit.
- name: Detect FontAwesome lockfile drift
working-directory: client-app
run: |
if git diff yarn.lock | grep -E '^[+-][^+-]' | grep -iq fortawesome; then
echo "::warning title=FontAwesome lockfile drift::yarn upgrade picked up a newer FontAwesome version than the committed yarn.lock. Run yarn upgrade locally, verify, and commit the updated lockfile to stop re-downloading these packages on every snapshot build."
{
echo "### FontAwesome lockfile drift detected"
echo ""
echo "\`yarn upgrade\` moved one or more \`@fortawesome/*\` packages beyond the committed \`client-app/yarn.lock\`. Until the new lockfile is committed, each snapshot build will re-download these tarballs against the FontAwesome Pro bandwidth cap."
echo ""
echo "**Action:** run \`yarn upgrade\` locally, verify the app still builds and behaves correctly, and commit the updated \`yarn.lock\`."
echo ""
echo "<details><summary>FontAwesome diff</summary>"
echo ""
echo '```diff'
git --no-pager diff yarn.lock | grep -iE '^[+-].*fortawesome' || true
echo '```'
echo ""
echo "</details>"
} >> "$GITHUB_STEP_SUMMARY"
fi
- name: Copy client build into Docker context
run: cp -r client-app/build/ docker/nginx/build/
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push nginx image
env:
ECR_ROOT: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
run: |
docker build -t "$ECR_ROOT/${{ env.NGINX_IMAGE }}:snapshot" docker/nginx
docker push "$ECR_ROOT/${{ env.NGINX_IMAGE }}:snapshot"