qa-environment-aspire (#2)
Some checks failed
CI / build-and-push (push) Failing after 18s
CI / deploy-qa (push) Has been skipped

Reviewed-on: #2
Co-authored-by: James Wampler <james@wamp.dev>
Co-committed-by: James Wampler <james@wamp.dev>
This commit was merged in pull request #2.
This commit is contained in:
2026-07-02 12:42:46 -07:00
committed by wamplerj
parent 6887d09f9c
commit e10cba77ed
31 changed files with 1578 additions and 106 deletions

18
scripts/ci/build.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Compiles the API (and its dependents) and builds the admin SPA.
# Acts as the compile gate before tests/image builds run. TreatWarningsAsErrors
# is enabled across the solution, so this also fails on any compiler warning.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")" && source ./lib.sh
cd "$CI_ROOT"
log "Restoring and publishing MicCheck.Api (Release)"
dotnet publish src/api/MicCheck.Api/MicCheck.Api.csproj -c Release
log "Installing admin dependencies (npm ci)"
npm --prefix src/admin ci
log "Building admin SPA (vite build)"
npm --prefix src/admin run build
log "build.sh complete"

37
scripts/ci/deploy-qa.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Deploys the freshly-pushed :qa images to the dedicated, network-isolated QA
# stack and recreates the miccheck database in an empty state. Safe/idempotent
# to re-run: `down -v` removes the Postgres data volume, and the API's own
# startup logic (EF Core migrations + idempotent dev seeding) rebuilds it.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")" && source ./lib.sh
cd "$CI_ROOT"
image_names
registry_login
export API_IMAGE ADMIN_IMAGE
export JWT_SECRET_KEY="${JWT_SECRET_KEY:?JWT_SECRET_KEY env var is required}"
export QA_ADMIN_PORT="${QA_ADMIN_PORT:-3001}"
COMPOSE="docker compose -p miccheck-qa -f deploy/qa/docker-compose.qa.yml"
log "Pulling latest :qa images"
$COMPOSE pull
log "Tearing down existing QA stack and wiping the database volume"
$COMPOSE down -v
log "Starting QA stack"
$COMPOSE up -d
log "Waiting for API health check via admin proxy on port $QA_ADMIN_PORT"
attempts=30
until curl -fsS "http://localhost:${QA_ADMIN_PORT}/api/v1/health" >/dev/null 2>&1; do
attempts=$((attempts - 1))
if [[ "$attempts" -le 0 ]]; then
fail "QA stack did not become healthy in time"
fi
sleep 2
done
log "QA environment deployed and healthy at http://localhost:${QA_ADMIN_PORT}"

23
scripts/ci/docker-build.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Builds self-contained CI images for the API and admin apps and tags them
# with both the current git sha and "qa" (the tag the QA compose stack pulls).
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")" && source ./lib.sh
cd "$CI_ROOT"
image_names
log "Building $API_IMAGE:$GIT_SHA / :qa"
docker build \
-f src/api/MicCheck.Api/Dockerfile.ci \
-t "$API_IMAGE:$GIT_SHA" \
-t "$API_IMAGE:qa" \
.
log "Building $ADMIN_IMAGE:$GIT_SHA / :qa"
docker build \
-f src/admin/Dockerfile.ci \
-t "$ADMIN_IMAGE:$GIT_SHA" \
-t "$ADMIN_IMAGE:qa" \
src/admin
log "docker-build.sh complete"

17
scripts/ci/docker-push.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Pushes the images built by docker-build.sh (git-sha and qa tags) to the
# Gitea container registry.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")" && source ./lib.sh
cd "$CI_ROOT"
image_names
registry_login
for tag in "$GIT_SHA" qa; do
log "Pushing $API_IMAGE:$tag"
docker push "$API_IMAGE:$tag"
log "Pushing $ADMIN_IMAGE:$tag"
docker push "$ADMIN_IMAGE:$tag"
done
log "docker-push.sh complete"

46
scripts/ci/lib.sh Executable file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# Shared helpers for scripts/ci/*.sh.
# Every script in this directory is meant to run identically in CI and on a
# developer's machine - no Gitea/GitHub-specific built-in actions, just bash.
set -euo pipefail
log() {
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"
}
fail() {
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] ERROR: $*" >&2
exit 1
}
# Repo root, regardless of caller's cwd.
CI_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Registry configuration. All values come from the environment (CI secrets or
# a developer's shell) - nothing is hardcoded, per project convention.
REGISTRY="${REGISTRY:-}"
REGISTRY_OWNER="${REGISTRY_OWNER:-}"
REGISTRY_USER="${REGISTRY_USER:-}"
REGISTRY_TOKEN="${REGISTRY_TOKEN:-}"
GIT_SHA="$(git -C "$CI_ROOT" rev-parse --short HEAD)"
require_registry_vars() {
[[ -n "$REGISTRY" ]] || fail "REGISTRY env var is required (e.g. gitea.example.com)"
[[ -n "$REGISTRY_OWNER" ]] || fail "REGISTRY_OWNER env var is required (e.g. your gitea org/user)"
}
# Populates API_IMAGE / ADMIN_IMAGE, e.g. gitea.example.com/james/miccheck-api
image_names() {
require_registry_vars
API_IMAGE="$REGISTRY/$REGISTRY_OWNER/miccheck-api"
ADMIN_IMAGE="$REGISTRY/$REGISTRY_OWNER/miccheck-admin"
}
registry_login() {
require_registry_vars
[[ -n "$REGISTRY_USER" ]] || fail "REGISTRY_USER env var is required to push images"
[[ -n "$REGISTRY_TOKEN" ]] || fail "REGISTRY_TOKEN env var is required to push images"
log "Logging in to $REGISTRY as $REGISTRY_USER"
echo "$REGISTRY_TOKEN" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin
}

23
scripts/ci/prepush.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Husky pre-push hook body. Compiles everything and runs the fast test suites
# (no Docker, no registry) so broken code/tests never leave the workstation.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")" && source ./lib.sh
cd "$CI_ROOT"
log "Building full solution (Debug)"
dotnet build MicCheck.slnx -c Debug
log "Running MicCheck.Api.Tests.Unit"
dotnet test tests/api/MicCheck.Api.Tests.Unit/MicCheck.Api.Tests.Unit.csproj -c Debug
log "Installing admin dependencies (npm ci)"
npm --prefix src/admin ci
log "Running admin Jest tests"
npm --prefix src/admin test
log "Building admin SPA (vite build)"
npm --prefix src/admin run build
log "prepush.sh complete - ok to push"

15
scripts/ci/test.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Runs the fast test suites: .NET unit tests (EF InMemory, no DB/Docker needed)
# and the admin Jest suite. Integration tests are intentionally excluded here -
# they require a live API + Postgres (see readme in the Integration test project).
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")" && source ./lib.sh
cd "$CI_ROOT"
log "Running MicCheck.Api.Tests.Unit"
dotnet test tests/api/MicCheck.Api.Tests.Unit/MicCheck.Api.Tests.Unit.csproj -c Release --logger trx
log "Running admin Jest tests"
npm --prefix src/admin test
log "test.sh complete"