name: Verify and publish container on: push: branches: - main jobs: build-and-push: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 with: # The changed-file lint step compares the pushed commit with its parent. fetch-depth: 2 - name: Log into Local Registry run: | echo "${{ secrets.FORGEJO_PAT }}" | docker login git.elijahkuntz.com -u "${{ gitea.actor }}" --password-stdin - name: Install locked dependencies run: npm ci - name: Run tests run: npm test - name: Validate Prisma schema and migration drift run: | npx prisma validate DATABASE_URL=file:./ci-migration.test.db npx prisma migrate deploy npx prisma migrate diff --from-migrations prisma/migrations --to-schema prisma/schema.prisma --exit-code - name: Build application run: npm run build - name: Lint changed source files run: | if BASE=$(git rev-parse HEAD^ 2>/dev/null); then git diff --name-only -z "$BASE" HEAD -- '*.ts' '*.tsx' '*.js' '*.mjs' | xargs -0 -r npx eslint else git ls-files -z -- '*.ts' '*.tsx' '*.js' '*.mjs' | xargs -0 -r npx eslint fi - name: Build and smoke production image run: | IMAGE_PATH=$(echo "git.elijahkuntz.com/${{ gitea.actor }}/${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]') IMAGE_SHA="$IMAGE_PATH:${{ github.sha }}" CI_SECRET=$(openssl rand -hex 32) SMOKE_CONTAINER="study-smoke-$(openssl rand -hex 8)" cleanup_smoke() { docker rm -f "$SMOKE_CONTAINER" > /dev/null 2>&1 || true } trap cleanup_smoke EXIT docker build -t "$IMAGE_SHA" . docker run -d --name "$SMOKE_CONTAINER" -e SESSION_SECRET="$CI_SECRET" -e ALLOW_INITIAL_SETUP=true "$IMAGE_SHA" for attempt in $(seq 1 30); do if [ "$(docker inspect --format='{{.State.Health.Status}}' "$SMOKE_CONTAINER")" = "healthy" ]; then break; fi sleep 2 done test "$(docker inspect --format='{{.State.Health.Status}}' "$SMOKE_CONTAINER")" = "healthy" docker exec "$SMOKE_CONTAINER" node -e "fetch('http://127.0.0.1:3726/login').then(r => { if (!r.ok) process.exit(1) }).catch(() => process.exit(1))" docker exec "$SMOKE_CONTAINER" node -e "fetch('http://127.0.0.1:3726/api/auth/setup-status').then(async r => { const body = await r.json(); if (!r.ok || body.setupRequired !== true) process.exit(1) }).catch(() => process.exit(1))" cleanup_smoke trap - EXIT docker tag "$IMAGE_SHA" "$IMAGE_PATH:latest" docker push "$IMAGE_SHA" docker push "$IMAGE_PATH:latest"