22 Commits

Author SHA1 Message Date
david f34dc23661 fix: add 20s delay before CI health check polling
Pull Request / unit-tests (pull_request) Successful in 2m3s
Pull Request / analyze-bump-type (pull_request) Successful in 11s
Pull Request / build-and-deploy-ci (pull_request) Failing after 14m13s
2026-05-18 17:18:20 -07:00
david f16ad054dd fix: extend CI health check timeout from 15s to 90s (6 retries × 15s)
Pull Request / unit-tests (pull_request) Successful in 2m4s
Pull Request / analyze-bump-type (pull_request) Successful in 13s
Pull Request / build-and-deploy-ci (pull_request) Failing after 5m45s
2026-05-18 17:03:47 -07:00
david 98b18de00a revert: remove registry cache backend, keep native Docker layer cache
--cache-to with docker driver doesn't support registry backend.
Host daemon already persists build layers via shared socket.
No changes needed - cache works automatically.
2026-05-17 05:05:53 -07:00
david 3e50916132 revert: remove actions/cache - runner v0.3.1 doesn't set ACTIONS_CACHE_URL in Docker mode
Keeping Docker layer caching (--cache-from/--cache-to) as the
effective optimization. actions/cache needs runner update.
2026-05-17 04:59:37 -07:00
david bd646ff4a4 fix: use actions/cache@v3 for Gitea v1 cache API
actions/cache@v4 uses v2 API which Gitea's cache server doesn't
support, causing hangs. @v3 uses v1 protocol compatible with
act_runner.
2026-05-17 04:50:59 -07:00
david 61be5efadc feat: add actions/cache and Docker layer caching
- actions/cache@v4 for node_modules across unit-tests and build jobs
- Manual sha256sum key since Gitea lacks hashFiles() support
- Docker BuildKit registry-based cache layers (--cache-from/--cache-to)
- Both runners configured with their own cache server ports
2026-05-17 04:38:50 -07:00
david 954abb0939 fix: restore npm ci and prisma generate in build-and-deploy-ci
Acceptance tests import prisma via @/ path alias and @cucumber/cucumber,
so they need node_modules and Prisma client. Cannot remove these steps
without refactoring tests to not import local code.
2026-05-17 04:08:31 -07:00
david 5f9705a740 fix: remove redundant npm ci and prisma generate from build-and-deploy-ci
build-and-deploy-ci only needs Docker, compose, and playwright
from ci-base. npm ci and prisma generate run inside Docker build.
Saves ~60s per PR run.
2026-05-17 03:57:25 -07:00
david a921fe5682 fix: replace HTTP healthcheck with container running check
HTTP healthcheck unreliable - nginx proxy routing delay and
Docker HEALTHCHECK interval make it flaky. Container running
is sufficient verification since site confirmed working every
time.
2026-05-17 03:38:37 -07:00
david 0cc4764aa5 fix: healthcheck with verbose error output
Remove 2>/dev/null to surface the actual error when docker exec
or wget fails inside the CI container.
2026-05-17 03:38:25 -07:00
david 9e3d2a85fd fix: use docker exec for healthcheck instead of external URL
External URL goes through nginx proxy which has slow routing
updates. Internal docker exec is instant and tests the actual
app health.
2026-05-17 03:28:12 -07:00
david 301ad2132f fix: increase healthcheck timeout and force-cleanup PR images
CI site took longer than 15 seconds to become ready.
Cleanup step needed --force flag when image is in use.
2026-05-17 03:19:15 -07:00
david 4a09bd044a fix: swap bun install for npm ci to fix integrity check failures
Known Bun bug (oven-sh/bun#1590, #26879, #18864) causes
IntegrityCheckFailed during tarball extraction in Docker/CI.
This is a 3+ year old issue with no fix in sight.

Changes:
- Generate package-lock.json via npm install --package-lock-only
- Dockerfile: add nodejs npm to all stages, replace bun install
  with npm ci --legacy-peer-deps (peer dep conflict exists for
  eslint@8 with eslint-config-next@16)
- Keep bun as runtime (bun test, bun run build, bun run start)
- pr.yml: npm ci, npx prisma generate, npx playwright
- release.yml: node scripts, npm test
- build-ci-images.yml: add package-lock.json trigger path
2026-05-17 03:05:57 -07:00
david baeab0fbe5 fix: add --retry 3 to bun install in Dockerfile
Pull Request / unit-tests (pull_request) Successful in 1m24s
Pull Request / analyze-bump-type (pull_request) Successful in 28s
Pull Request / build-and-deploy-ci (pull_request) Failing after 53s
Docker build steps (builder, test-runner, runner stages) also
run bun install and suffer the same transient tarball extraction
failures as the workflow steps.
2026-05-17 02:52:27 -07:00
david 90ecbb6fba fix: add retry 3 to bun install steps
Pull Request / unit-tests (pull_request) Successful in 1m29s
Pull Request / analyze-bump-type (pull_request) Successful in 19s
Pull Request / build-and-deploy-ci (pull_request) Failing after 4m20s
Transient tarball extraction failures from npm registry hit bun
install intermittently. --retry 3 makes bun retry failed
downloads/extractions automatically.
2026-05-17 02:44:32 -07:00
david b3ba4b5a8c fix: skip docker compose pull and only comment on PR events
Pull Request / unit-tests (pull_request) Failing after 1m12s
Pull Request / build-and-deploy-ci (pull_request) Has been skipped
Pull Request / analyze-bump-type (pull_request) Has been skipped
- Remove explicit docker compose pull in build-and-deploy-ci;
  image is already built locally on the same host, compose
  default pull_policy:missing uses local image
- Gate Comment bump type on PR step with
  if: github.event_name == 'pull_request' so it doesn't
  fail on workflow_dispatch triggers
2026-05-17 02:35:20 -07:00
david a5a5f41c16 fix: use correct host path for /apps mount in CI workflow
Pull Request / unit-tests (pull_request) Successful in 1m17s
Pull Request / analyze-bump-type (pull_request) Successful in 22s
Pull Request / build-and-deploy-ci (pull_request) Failing after 3m56s
The /apps:/apps mount was incorrect - the runner container has /var/lib/casaos/apps
mounted at /apps, but the job container needs the actual host path. Updated to use
/var/lib/casaos/apps:/apps so the job container can access CI compose files.
2026-05-16 21:40:11 -07:00
david a3e46ec83e fix: remove duplicate docker socket mount from PR workflow
Pull Request / unit-tests (pull_request) Successful in 54s
Pull Request / analyze-bump-type (pull_request) Successful in 25s
Pull Request / build-and-deploy-ci (pull_request) Failing after 1m10s
The runner already passes through its own docker socket mount to job
containers. Specifying it again in the workflow causes a duplicate
mount point error.
2026-05-16 20:38:49 -07:00
david 66fc2386c2 fix: set DATABASE_URL at job level for unit tests in PR workflow
Pull Request / unit-tests (pull_request) Successful in 54s
Pull Request / build-and-deploy-ci (pull_request) Failing after 0s
Pull Request / analyze-bump-type (pull_request) Successful in 10s
prisma.ts throws at import time if DATABASE_URL is not set. The env var
was only set on the 'Generate Prisma client' step, not 'Run unit tests'.
2026-05-16 20:35:52 -07:00
david 83cccf4987 fix: add missing build context and --push flag to CI base image workflow
The docker build command was missing the build context path (.) and
the --push flag to actually push to the registry after building.
2026-05-16 20:16:38 -07:00
david b162750a67 fix: remove permissions module mock from tournament-update tests to prevent test pollution
Pull Request / unit-tests (pull_request) Failing after 55s
Pull Request / build-and-deploy-ci (pull_request) Has been skipped
Pull Request / analyze-bump-type (pull_request) Has been skipped
tournament-update.test.ts was mocking the entire @/lib/permissions module,
which replaced canManageTournament globally and caused permissions.test.ts
to fail when run in the same process.

Instead of mocking permissions, mock its dependencies (auth-simple and prisma)
so canManageTournament runs through naturally.
2026-05-16 20:03:10 -07:00
david 671ee78a47 fix: mount /apps and docker socket in PR workflow build-and-deploy-ci job
Pull Request / unit-tests (pull_request) Failing after 55s
Pull Request / build-and-deploy-ci (pull_request) Has been skipped
Pull Request / analyze-bump-type (pull_request) Has been skipped
The build-and-deploy-ci job failed because /apps/euchre_camp_ci/docker-compose.yml
was not accessible inside the job container. Add volume mounts for /apps and the
docker socket so the job can read the CI compose file and run docker compose commands.

Requires runner config.yaml with valid_volumes for /apps and /var/run/docker.sock.
2026-05-16 19:57:48 -07:00
8 changed files with 10542 additions and 47 deletions
+3 -1
View File
@@ -8,6 +8,7 @@ on:
- "Dockerfile.ci-base"
- "package.json"
- "bun.lock"
- "package-lock.json"
- ".gitea/workflows/build-ci-images.yml"
schedule:
# Weekly rebuild to get latest Playwright/Bun versions
@@ -52,12 +53,13 @@ jobs:
- name: Build and push CI base image
run: |
# Build with multiple tags
docker build \
--file Dockerfile.ci-base \
--tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/ci-base:latest \
--tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/ci-base:playwright-${{ steps.meta.outputs.playwright_version }} \
--tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/ci-base:${{ github.sha }} \
--push \
.
- name: Clean up
if: always()
+2 -2
View File
@@ -40,12 +40,12 @@ jobs:
# Wait for production site to be healthy
echo "Waiting for production site to be healthy..."
for i in {1..30}; do
for i in {1..6}; do
if curl -sf https://euchre.notsosm.art/api/health > /dev/null 2>&1; then
echo "✅ Production successfully deployed with version ${VERSION}"
exit 0
fi
sleep 0.5
sleep 15
done
echo "❌ Production deployment failed"
docker compose logs app
+21 -15
View File
@@ -15,21 +15,21 @@ jobs:
container:
image: docker.notsosm.art/euchre-camp/ci-base:latest
options: --user root
env:
DATABASE_URL: postgresql://user:pass@localhost:5432/dummy
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: bun install
run: npm ci --legacy-peer-deps
- name: Generate Prisma client
run: bun x prisma generate
env:
DATABASE_URL: postgresql://user:pass@localhost:5432/dummy
run: npx prisma generate
- name: Run unit tests
run: bun test src/__tests__/unit/ src/__tests__/*.test.tsx src/__tests__/auth-simple.test.ts
run: npm test
build-and-deploy-ci:
runs-on: ubuntu-latest
@@ -37,16 +37,20 @@ jobs:
container:
image: docker.notsosm.art/euchre-camp/ci-base:latest
options: --user root
volumes:
- /var/lib/casaos/apps:/apps
steps:
- name: Checkout code
uses: actions/checkout@v4
# Required for acceptance tests - they import prisma via @/ path alias
# and @cucumber/cucumber for cucumber-e2e tests
- name: Install dependencies
run: bun install
run: npm ci --legacy-peer-deps
- name: Generate Prisma client
run: bun x prisma generate
run: npx prisma generate
env:
DATABASE_URL: postgresql://user:pass@localhost:5432/dummy
@@ -73,33 +77,34 @@ jobs:
# Update the image tag in the compose file
sed -i "s|image: docker.notsosm.art/euchre-camp:[a-zA-Z0-9.-]*|image: docker.notsosm.art/euchre-camp:${IMAGE_TAG}|" ${COMPOSE_FILE}
# Pull the new image and restart the CI stack
# Image was built locally in the previous step; compose uses it without pulling
cd /apps/euchre_camp_ci
docker compose pull app
docker compose up -d app
- name: Wait for CI site to be healthy
- name: Wait for CI site to be ready
run: |
for i in {1..30}; do
echo "Waiting for Next.js to start..."
sleep 20
for i in {1..6}; do
if curl -sf https://euchre-ci.notsosm.art/api/health > /dev/null 2>&1; then
echo "CI site is healthy"
exit 0
fi
sleep 0.5
sleep 15
done
echo "CI site failed to become healthy after 15 seconds"
echo "CI site failed to become healthy after 90 seconds"
docker compose -f /apps/euchre_camp_ci/docker-compose.yml logs app
exit 1
- name: Run acceptance tests
run: DATABASE_URL="${{ secrets.CI_DATABASE_URL }}" bun test:acceptance
run: DATABASE_URL="${{ secrets.CI_DATABASE_URL }}" npx playwright test e2e/
env:
CI: true
- name: Cleanup PR images
if: always()
run: |
docker rmi ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ steps.info.outputs.pr_number }}-${{ steps.info.outputs.short_sha }} || true
docker rmi --force ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ steps.info.outputs.pr_number }}-${{ steps.info.outputs.short_sha }} || true
analyze-bump-type:
runs-on: ubuntu-latest
@@ -143,6 +148,7 @@ jobs:
echo "reason=$REASON" >> $GITHUB_OUTPUT
- name: Comment bump type on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
+6 -5
View File
@@ -73,10 +73,10 @@ jobs:
echo "Bumping version: $BUMP"
# Run the bump script
bun run scripts/bump-version.js "$BUMP" --yes
node scripts/bump-version.js "$BUMP" --yes
# Get new version
NEW_VERSION=$(bun -e "console.log(require('./package.json').version)")
NEW_VERSION=$(node -e "console.log(require('./package.json').version)")
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version: $NEW_VERSION"
@@ -121,8 +121,9 @@ jobs:
run: |
docker run --rm \
-e DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" \
-e NODE_ENV=test \
${{ env.IMAGE_NAME }}-test:${{ steps.version.outputs.new_version }} \
bun test 'src/__tests__/unit/**' 'src/__tests__/*.test.tsx' 'src/__tests__/auth-simple.test.ts'
npm test
- name: Build production image
if: steps.commit.outputs.committed == 'true'
@@ -176,12 +177,12 @@ jobs:
# Wait for container to be healthy
echo "Waiting for dev site to be healthy..."
for i in {1..30}; do
for i in {1..6}; do
if curl -sf https://euchre-dev.notsosm.art/api/health > /dev/null 2>&1; then
echo "✅ Dev environment successfully deployed with version ${{ steps.version.outputs.new_version }}"
exit 0
fi
sleep 0.5
sleep 15
done
echo "❌ Dev environment deployment failed"
docker compose logs app
+11 -11
View File
@@ -4,7 +4,7 @@
FROM oven/bun:alpine AS builder
# Install dependencies (needed for native modules)
RUN apk add --no-cache python3 make g++
RUN apk add --no-cache python3 make g++ nodejs npm
# Set working directory
WORKDIR /app
@@ -13,14 +13,14 @@ WORKDIR /app
COPY package*.json ./
# Install dependencies (including dev dependencies for building)
RUN bun install
RUN npm ci --legacy-peer-deps
# Copy source code
COPY . .
# Generate Prisma client (with dummy PostgreSQL DATABASE_URL for build-time generation)
# Note: A dummy URL is used since the real database is not available during build
RUN DATABASE_PROVIDER=postgresql DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" bun x prisma generate
RUN DATABASE_PROVIDER=postgresql DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" npx prisma generate
# Build the application (with dummy DATABASE_URL for static page generation and git commit)
ARG GIT_COMMIT=unknown
@@ -30,7 +30,7 @@ RUN DATABASE_PROVIDER=postgresql DATABASE_URL="postgresql://user:pass@localhost:
FROM oven/bun:alpine AS test-runner
# Install dependencies
RUN apk add --no-cache python3 make g++ git
RUN apk add --no-cache python3 make g++ git nodejs npm
# Set working directory
WORKDIR /app
@@ -39,19 +39,19 @@ WORKDIR /app
COPY package*.json ./
# Install ALL dependencies (including dev dependencies for testing)
RUN bun install
RUN npm ci --legacy-peer-deps
# Copy source code
COPY . .
# Generate Prisma client
RUN DATABASE_PROVIDER=postgresql DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" bun x prisma generate
RUN DATABASE_PROVIDER=postgresql DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" npx prisma generate
# Stage 3: Production runner
FROM oven/bun:alpine AS runner
# Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init
# Install dumb-init and npm for production install
RUN apk add --no-cache dumb-init nodejs npm
# Create non-root user
RUN addgroup --system --gid 1001 euchre && \
@@ -64,15 +64,15 @@ WORKDIR /app
COPY --from=builder --chown=euchre:euchre /app/.next ./.next
COPY --from=builder --chown=euchre:euchre /app/public ./public
COPY --from=builder --chown=euchre:euchre /app/package.json ./package.json
COPY --from=builder --chown=euchre:euchre /app/bun.lock ./bun.lock
COPY --from=builder --chown=euchre:euchre /app/package-lock.json ./package-lock.json
COPY --from=builder --chown=euchre:euchre /app/prisma ./prisma
# Install only production dependencies
RUN bun install --production
RUN npm ci --legacy-peer-deps --omit=dev
# Generate Prisma client
# Note: We need to set DATABASE_URL even for generation because prisma.config.ts requires it
RUN DATABASE_PROVIDER=postgresql DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" bun x prisma generate
RUN DATABASE_PROVIDER=postgresql DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" npx prisma generate
# Switch to non-root user
USER euchre
+2 -2
View File
@@ -259,12 +259,12 @@ deploy-prod version:
docker compose pull app && \
docker compose up -d app && \
echo "Waiting for production site to be healthy..." && \
for i in {1..30}; do \
for i in {1..6}; do \
if curl -sf https://euchre.notsosm.art/api/health > /dev/null 2>&1; then \
echo "✅ Production successfully deployed with version {{version}}"; \
exit 0; \
fi; \
sleep 0.5; \
sleep 15; \
done && \
echo "❌ Production deployment failed - health check timed out"; \
docker compose logs app; \
+10473
View File
File diff suppressed because it is too large Load Diff
+24 -11
View File
@@ -8,12 +8,32 @@ import { describe, it, expect, mock, beforeEach,} from 'bun:test';
// Create mock functions at module level
const eventFindUniqueMock = mock(async () => ({}));
const eventUpdateMock = mock(async () => ({}));
const canManageTournamentMock = mock(async () => ({ allowed: true }));
const canDeleteTournamentMock = mock(async () => ({ allowed: true }));
const userFindUniqueMock = mock(async () => ({
id: 'admin-1',
email: 'admin@example.com',
role: 'club_admin',
emailVerified: false,
name: null,
image: null,
playerId: null,
createdAt: new Date(),
updatedAt: new Date(),
}));
// Mock prisma first
// Mock auth-simple to return a valid session
mock.module('@/lib/auth-simple', () => ({
getSession: mock(async () => ({
user: { id: 'admin-1', email: 'admin@example.com' },
session: { token: 'test', expiresAt: new Date() }
})),
}));
// Mock prisma with user and event
mock.module('@/lib/prisma', () => ({
prisma: {
user: {
findUnique: userFindUniqueMock,
},
event: {
findUnique: eventFindUniqueMock,
update: eventUpdateMock,
@@ -21,12 +41,6 @@ mock.module('@/lib/prisma', () => ({
},
}));
// Mock the permissions module
mock.module('@/lib/permissions', () => ({
canManageTournament: canManageTournamentMock,
canDeleteTournament: canDeleteTournamentMock,
}));
// Import the route handler after mocking
import { PUT } from '@/app/api/tournaments/[id]/route';
import { prisma } from '@/lib/prisma';
@@ -36,8 +50,7 @@ describe('Tournament Update API', () => {
// Clear all mock history before each test
eventFindUniqueMock.mockClear();
eventUpdateMock.mockClear();
canManageTournamentMock.mockClear();
canDeleteTournamentMock.mockClear();
userFindUniqueMock.mockClear();
});
it('should update allowTies field when provided', async () => {