@@ -431,8 +431,113 @@ jobs:
431431 candidate-application-image : ghcr.io/ls1intum/hephaestus/application-server@${{ needs.tag-images.outputs.application-server-digest }}
432432 postgres-image : ghcr.io/ls1intum/hephaestus/postgres@${{ needs.tag-images.outputs.postgres-digest }}
433433 candidate-source-sha : ${{ needs.release.outputs.sha }}
434+
435+ supported-host-smoke :
436+ needs : [release, tag-images]
437+ if : needs.release.outputs.released == 'true'
438+ name : Host smoke (${{ matrix.architecture }})
439+ strategy :
440+ fail-fast : false
441+ matrix :
442+ include :
443+ - architecture : amd64
444+ runner : ubuntu-24.04
445+ - architecture : arm64
446+ runner : ubuntu-24.04-arm
447+ runs-on : ${{ matrix.runner }}
448+ timeout-minutes : 20
449+ permissions :
450+ # The release is still a draft here, and draft releases are visible only to tokens with push
451+ # access, so `gh release download` in prepare-release-lock.ts needs contents: write.
452+ contents : write
453+ packages : read
454+ steps :
455+ - name : Check out the released tree
456+ uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
457+ with :
458+ ref : ${{ needs.release.outputs.sha }}
459+ persist-credentials : false
460+
461+ - uses : ./.github/actions/setup-bun
462+
463+ - name : Install cosign
464+ uses : sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
465+
466+ - name : Login to GitHub Container Registry
467+ uses : docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
468+ with :
469+ registry : ghcr.io
470+ username : ${{ github.actor }}
471+ password : ${{ secrets.GITHUB_TOKEN }}
472+
473+ - name : Smoke-test the blessed install
474+ env :
475+ ARCHITECTURE : ${{ matrix.architecture }}
476+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
477+ RELEASE_SHA : ${{ needs.release.outputs.sha }}
478+ TAG_NAME : ${{ needs.release.outputs.tag_name }}
479+ run : |
480+ set -euo pipefail
481+ cd docker/self-host
482+ cleanup() {
483+ status=$?
484+ if [ "$status" -ne 0 ]; then
485+ docker compose --env-file .env --env-file release-lock.env logs --no-color || true
486+ fi
487+ docker compose --env-file .env --env-file release-lock.env down -v --remove-orphans || true
488+ exit "$status"
489+ }
490+ trap cleanup EXIT
491+
492+ actual_arch=$(docker version --format '{{.Server.Arch}}')
493+ [ "$actual_arch" = "$ARCHITECTURE" ] || { echo "::error::Expected native $ARCHITECTURE, got $actual_arch"; exit 1; }
494+ . /etc/os-release
495+ [ "$ID" = ubuntu ] && [ "$VERSION_ID" = 24.04 ] || { echo "::error::Ubuntu 24.04 is required"; exit 1; }
496+ docker_major=$(docker version --format '{{.Server.Version}}' | cut -d. -f1)
497+ [ "$docker_major" -ge 28 ] || { echo "::error::Docker Engine 28 or newer is required"; exit 1; }
498+ compose_version=$(docker compose version --short)
499+ [ "$(printf '%s\n' 2.24.4 "$compose_version" | sort -V | head -n1)" = 2.24.4 ] || {
500+ echo "::error::Docker Compose 2.24.4 or newer is required"; exit 1; }
501+
502+ ./setup.sh
503+ sed -i \
504+ -e 's/^APP_HOSTNAME=$/APP_HOSTNAME=hephaestus-smoke.invalid/' \
505+ -e 's/^ACME_EMAIL=$/ACME_EMAIL=release-smoke@example.invalid/' \
506+ -e 's/^GH_OAUTH_CLIENT_ID=$/GH_OAUTH_CLIENT_ID=release-smoke/' \
507+ -e 's/^GH_OAUTH_CLIENT_SECRET=$/GH_OAUTH_CLIENT_SECRET=release-smoke-secret/' \
508+ -e 's/^HEPHAESTUS_AUTH_BOOTSTRAP_ADMINS=$/HEPHAESTUS_AUTH_BOOTSTRAP_ADMINS=github:1/' \
509+ .env
510+ node ../../scripts/prepare-release-lock.ts "$TAG_NAME"
511+ docker compose --env-file .env --env-file release-lock.env config --quiet
512+ docker compose --env-file .env --env-file release-lock.env up -d --wait --wait-timeout 600
513+ curl --fail --insecure --silent --show-error --max-time 10 --noproxy '*' \
514+ --resolve hephaestus-smoke.invalid:443:127.0.0.1 \
515+ https://hephaestus-smoke.invalid/ >/dev/null
516+
517+ jq -n \
518+ --arg schemaVersion "1" \
519+ --arg release "$TAG_NAME" \
520+ --arg commit "$RELEASE_SHA" \
521+ --arg testedAt "$(date -u +%FT%TZ)" \
522+ --arg os "$(. /etc/os-release; printf '%s %s' "$NAME" "$VERSION_ID")" \
523+ --arg architecture "$actual_arch" \
524+ --arg dockerEngine "$(docker version --format '{{.Server.Version}}')" \
525+ --arg dockerCompose "$compose_version" \
526+ --argjson services "$(docker compose --env-file .env --env-file release-lock.env ps --all --format json | jq -s 'map({service: .Service, state: .State, health: .Health, exitCode: .ExitCode}) | sort_by(.service)')" \
527+ '{schemaVersion: ($schemaVersion | tonumber), release: $release, commit: $commit,
528+ testedAt: $testedAt, host: {os: $os, architecture: $architecture,
529+ dockerEngine: $dockerEngine, dockerCompose: $dockerCompose}, services: $services}' \
530+ > "../../host-smoke-$ARCHITECTURE.json"
531+
532+ - name : Upload smoke-test record
533+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
534+ with :
535+ name : host-smoke-${{ matrix.architecture }}
536+ path : host-smoke-${{ matrix.architecture }}.json
537+ if-no-files-found : error
538+
434539 publish-release :
435- needs : [release, tag-images, upgrade-test]
540+ needs : [release, tag-images, upgrade-test, supported-host-smoke ]
436541 if : needs.release.outputs.released == 'true'
437542 timeout-minutes : 20
438543 runs-on : ubuntu-latest
@@ -460,6 +565,34 @@ jobs:
460565 username : ${{ github.actor }}
461566 password : ${{ secrets.GITHUB_TOKEN }}
462567
568+ - name : Download supported-host smoke records
569+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
570+ with :
571+ pattern : host-smoke-*
572+ path : host-smoke
573+ merge-multiple : true
574+
575+ - name : Verify supported-host smoke records
576+ env :
577+ RELEASE_SHA : ${{ needs.release.outputs.sha }}
578+ TAG_NAME : ${{ needs.release.outputs.tag_name }}
579+ run : |
580+ set -euo pipefail
581+ jq -s -e --arg release "$TAG_NAME" --arg commit "$RELEASE_SHA" '
582+ length == 2 and
583+ ([.[].host.architecture] | sort) == ["amd64", "arm64"] and
584+ all(.[];
585+ .schemaVersion == 1 and .release == $release and .commit == $commit and
586+ (.testedAt | fromdateiso8601) and
587+ .host.os == "Ubuntu 24.04" and
588+ (.host.dockerEngine | type) == "string" and
589+ (.host.dockerCompose | type) == "string" and
590+ (.services | length) > 0 and
591+ ([.services[].service] | length) == ([.services[].service] | unique | length) and
592+ all(.services[];
593+ if .state == "exited" then .exitCode == 0
594+ else .state == "running" and .health == "healthy" end))' host-smoke/*.json >/dev/null
595+
463596 - name : Verify durable assets from a clean environment
464597 env :
465598 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
@@ -563,6 +696,7 @@ jobs:
563696 TAG_NAME : ${{ needs.release.outputs.tag_name }}
564697 run : |
565698 set -euo pipefail
699+ gh release upload "$TAG_NAME" host-smoke/*.json --repo "${{ github.repository }}"
566700 gh release edit "$TAG_NAME" --repo "${{ github.repository }}" --draft=false
567701 [ "$(gh release view "$TAG_NAME" --repo "${{ github.repository }}" --json isImmutable --jq .isImmutable)" = true ] || {
568702 echo "::error::Repository immutable releases must be enabled before publishing"; exit 1; }
0 commit comments