diff --git a/.docker/compose/docker-compose.playwright-ci.yml b/.docker/compose/docker-compose.playwright-ci.yml index 0e4c6b64..79006f41 100644 --- a/.docker/compose/docker-compose.playwright-ci.yml +++ b/.docker/compose/docker-compose.playwright-ci.yml @@ -27,11 +27,9 @@ services: # Charon Application - Core E2E Testing Service # ============================================================================= charon-app: - # CI default (digest-pinned via workflow output): - # CHARON_E2E_IMAGE_DIGEST=ghcr.io/wikid82/charon:nightly@sha256: - # Local override (tag-based): - # CHARON_E2E_IMAGE=charon:e2e-test - image: ${CHARON_E2E_IMAGE_DIGEST:-${CHARON_E2E_IMAGE:-charon:e2e-test}} + # CI provides CHARON_E2E_IMAGE_TAG=charon:e2e-test (locally built image) + # Local development uses the default fallback value + image: ${CHARON_E2E_IMAGE_TAG:-charon:e2e-test} container_name: charon-playwright restart: "no" # CI generates CHARON_ENCRYPTION_KEY dynamically in GitHub Actions workflow diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 02c16518..cfc9925e 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -155,7 +155,7 @@ jobs: # Enable security-focused endpoints and test gating CHARON_EMERGENCY_SERVER_ENABLED: "true" CHARON_SECURITY_TESTS_ENABLED: "true" - CHARON_E2E_IMAGE_DIGEST: ${{ needs.build.outputs.image_digest }} + CHARON_E2E_IMAGE_TAG: charon:e2e-test strategy: fail-fast: false matrix: diff --git a/docs/plans/current_spec.md b/docs/plans/current_spec.md index cba2b391..dbcaadc3 100644 --- a/docs/plans/current_spec.md +++ b/docs/plans/current_spec.md @@ -1,20 +1,21 @@ -# CI Workflow Failures - Fix Plan +# Docker Compose CI Failure Remediation Plan -**Version:** 1.0 -**Status:** Ready for Implementation -**Priority:** HIGH -**Created:** 2026-01-30 -**Scope:** Three CI failures in GitHub Actions workflows +**Status**: Active +**Created**: 2026-01-30 +**Priority**: CRITICAL (Blocking CI) --- ## Executive Summary -Three CI workflows are failing in production. This plan documents the root causes, affected files, and specific fixes required for each issue: +The E2E test workflow (`e2e-tests.yml`) is failing when attempting to start containers via `docker-compose.playwright-ci.yml`. The root cause is an incorrect Docker image reference format in the compose file that attempts to use a bare SHA256 digest instead of a fully-qualified image reference with registry and repository. -1. **Nightly Build Failure**: GoReleaser macOS cross-compile failing with incorrect Zig target -2. **Playwright E2E Failure**: Emergency server unreachable on port 2020 due to missing env var -3. **Trivy Scan Failure**: Invalid Docker image reference when PR number is missing +**Error Message**: +``` +charon-app Error pull access denied for sha256, repository does not exist or may require 'docker login': denied: requested access to the resource is denied +``` + +**Root Cause**: The compose file's `image:` directive evaluates to a bare SHA256 digest (e.g., `sha256:057a9998...`) instead of a properly formatted image reference like `ghcr.io/wikid82/charon@sha256:057a9998...`. --- diff --git a/docs/plans/docker_compose_ci_fix.md b/docs/plans/docker_compose_ci_fix.md new file mode 100644 index 00000000..41511109 --- /dev/null +++ b/docs/plans/docker_compose_ci_fix.md @@ -0,0 +1,546 @@ +# Docker Compose CI Failure Remediation Plan + +**Status**: Active +**Created**: 2026-01-30 +**Priority**: CRITICAL (Blocking CI) + +--- + +## Executive Summary + +The E2E test workflow (`e2e-tests.yml`) is failing when attempting to start containers via `docker-compose.playwright-ci.yml`. The root cause is an incorrect Docker image reference format in the compose file that attempts to use a bare SHA256 digest instead of a fully-qualified image reference with registry and repository. + +**Error Message**: +``` +charon-app Error pull access denied for sha256, repository does not exist or may require 'docker login': denied: requested access to the resource is denied +``` + +**Root Cause**: The compose file's `image:` directive evaluates to a bare SHA256 digest (e.g., `sha256:057a9998...`) instead of a properly formatted image reference like `ghcr.io/wikid82/charon@sha256:057a9998...`. + +--- + +## Root Cause Analysis + +### Current Implementation (Broken) + +**File**: `.docker/compose/docker-compose.playwright-ci.yml` +**Lines**: 29-37 + +```yaml +charon-app: + # CI default (digest-pinned via workflow output): + # CHARON_E2E_IMAGE_DIGEST=ghcr.io/wikid82/charon:nightly@sha256: + # Local override (tag-based): + # CHARON_E2E_IMAGE=charon:e2e-test + image: ${CHARON_E2E_IMAGE_DIGEST:-${CHARON_E2E_IMAGE:-charon:e2e-test}} +``` + +### Workflow Environment Variable + +**File**: `.github/workflows/e2e-tests.yml` +**Line**: 158 + +```yaml +env: + CHARON_E2E_IMAGE_DIGEST: ${{ needs.build.outputs.image_digest }} +``` + +**Problem**: The `needs.build.outputs.image_digest` from the `build` job in `e2e-tests.yml` returns **only the SHA256 digest** (e.g., `sha256:057a9998fa7a5b224a06ec8989c892d2ac8f9323530470965baaf5fcaab7557c`), not a fully-qualified image reference. + +### Why Docker Fails + +Docker Compose interprets the `image:` field as: +- `sha256:057a9998...` ← **Bare digest, no registry/repository** + +Docker then tries to: +1. Parse this as a repository name +2. Look for a repository literally named "sha256" +3. Fail with "pull access denied" because no such repository exists + +### Correct Reference Format + +Docker requires one of these formats: +1. **Tag-based**: `charon:e2e-test` (local image) +2. **Digest-pinned**: `ghcr.io/wikid82/charon@sha256:057a9998...` (registry + repo + digest) + +--- + +## Technical Investigation + +### How the Image is Built and Loaded + +**Workflow Flow** (`e2e-tests.yml`): + +1. **Build Job** (lines 90-148): + - Builds Docker image with tag `charon:e2e-test` + - Saves image to `charon-e2e-image.tar` artifact + - Outputs image digest from build step + +2. **E2E Test Job** (lines 173-177): + - Downloads `charon-e2e-image.tar` artifact + - Loads image with: `docker load -i charon-e2e-image.tar` + - **Loaded image has tag**: `charon:e2e-test` (from build step) + +3. **Start Container** (line 219): + - Runs: `docker compose -f .docker/compose/docker-compose.playwright-ci.yml up -d` + - Compose file tries to use `$CHARON_E2E_IMAGE_DIGEST` (bare SHA256) + - **Docker cannot find image** because the digest doesn't match loaded tag + +### Mismatch Between Build and Reference + +| Step | Image Reference | Status | +|------|----------------|--------| +| Build | `charon:e2e-test` | ✅ Image tagged | +| Save/Load | `charon:e2e-test` | ✅ Tag preserved in tar | +| Compose | `sha256:057a9998...` | ❌ Wrong reference type | + +**The loaded image is available as `charon:e2e-test`, but the compose file is looking for `sha256:...`** + +--- + +## Comparison with Working Workflow + +### `playwright.yml` (Working) vs `e2e-tests.yml` (Broken) + +**playwright.yml** (lines 207-209): +```yaml +- name: Load Docker image + run: | + docker load < charon-pr-image.tar + docker images | grep charon +``` + +**Container Start** (lines 213-277): +```yaml +- name: Start Charon container + run: | + # Explicitly constructs image reference from variables + IMAGE_NAME=$(echo "${{ github.repository_owner }}/charon" | tr '[:upper:]' '[:lower:]') + IMAGE_REF="ghcr.io/${IMAGE_NAME}:pr-${{ steps.pr-info.outputs.pr_number }}" + + docker run -d \ + --name charon-test \ + -e CHARON_ENV="${CHARON_ENV}" \ + # ... (uses constructed IMAGE_REF) +``` + +**Key Difference**: `playwright.yml` uses `docker run` directly with explicit image reference construction, not Docker Compose with environment variable substitution. + +--- + +## Solution Architecture + +### Option 1: Use Local Tag Reference (Recommended) + +**Rationale**: The loaded image is already tagged as `charon:e2e-test`. We should use this tag directly instead of trying to use a digest. + +**Change**: Set `CHARON_E2E_IMAGE_DIGEST` to the **tag** instead of the digest, or use a different variable name. + +### Option 2: Re-tag Image with Digest + +**Rationale**: Re-tag the loaded image to match the digest-based reference expected by the compose file. + +**Change**: After loading, re-tag the image with the full digest reference. + +### Option 3: Simplify Compose File + +**Rationale**: Remove the digest-based environment variable and always use the local tag for CI. + +**Change**: Hard-code `charon:e2e-test` or use a simpler env var pattern. + +--- + +## Recommended Solution: Option 1 (Modified Approach) + +### Strategy + +**Use the pre-built tag for CI, not the digest.** The digest output from the build is metadata but not needed for referencing a locally loaded image. + +### Implementation + +#### Change 1: Remove Digest from Workflow Environment + +**File**: `.github/workflows/e2e-tests.yml` +**Lines**: 155-158 + +**Current**: +```yaml +env: + # Required for security teardown (emergency reset fallback when ACL blocks API) + CHARON_EMERGENCY_TOKEN: ${{ secrets.CHARON_EMERGENCY_TOKEN }} + # Enable security-focused endpoints and test gating + CHARON_EMERGENCY_SERVER_ENABLED: "true" + CHARON_SECURITY_TESTS_ENABLED: "true" + CHARON_E2E_IMAGE_DIGEST: ${{ needs.build.outputs.image_digest }} +``` + +**Corrected**: +```yaml +env: + # Required for security teardown (emergency reset fallback when ACL blocks API) + CHARON_EMERGENCY_TOKEN: ${{ secrets.CHARON_EMERGENCY_TOKEN }} + # Enable security-focused endpoints and test gating + CHARON_EMERGENCY_SERVER_ENABLED: "true" + CHARON_SECURITY_TESTS_ENABLED: "true" + # Use local tag for pre-built image (loaded from artifact) + CHARON_E2E_IMAGE: charon:e2e-test +``` + +**Rationale**: +- The `docker load` command restores the image with its original tag `charon:e2e-test` +- We should use this tag, not the digest +- The digest is only useful for verifying image integrity, not for referencing locally loaded images + +#### Change 2: Update Compose File Comment Documentation + +**File**: `.docker/compose/docker-compose.playwright-ci.yml` +**Lines**: 31-37 + +**Current**: +```yaml + charon-app: + # CI default (digest-pinned via workflow output): + # CHARON_E2E_IMAGE_DIGEST=ghcr.io/wikid82/charon:nightly@sha256: + # Local override (tag-based): + # CHARON_E2E_IMAGE=charon:e2e-test + image: ${CHARON_E2E_IMAGE_DIGEST:-${CHARON_E2E_IMAGE:-charon:e2e-test}} +``` + +**Corrected**: +```yaml + charon-app: + # CI default: Uses pre-built image loaded from artifact + # Set via workflow: CHARON_E2E_IMAGE=charon:e2e-test + # Local development: Uses locally built image + # Override with: CHARON_E2E_IMAGE=charon:local-dev + image: ${CHARON_E2E_IMAGE:-charon:e2e-test} +``` + +**Rationale**: +- Simplify the environment variable fallback chain +- Remove confusing `CHARON_E2E_IMAGE_DIGEST` variable that was set incorrectly +- Document the actual behavior: CI loads pre-built image with known tag +- Make local development override clearer + +--- + +## Alternative Solution: Option 2 (If Digest-Pinning Required) + +If there's a requirement to use digest-based references for security/reproducibility, we must re-tag the loaded image. + +### Implementation + +#### Change 1: Re-tag After Load + +**File**: `.github/workflows/e2e-tests.yml` +**After Line**: 177 (in "Load Docker image" step) + +**Add**: +```yaml + - name: Load and re-tag Docker image + run: | + # Load the pre-built image + docker load -i charon-e2e-image.tar + docker images | grep charon + + # Re-tag for digest-based reference if needed + IMAGE_DIGEST="${{ needs.build.outputs.image_digest }}" + if [[ -n "$IMAGE_DIGEST" ]]; then + # Extract just the digest hash (sha256:...) + DIGEST_HASH=$(echo "$IMAGE_DIGEST" | grep -oP 'sha256:[a-f0-9]{64}') + + # Construct full reference + FULL_REF="ghcr.io/wikid82/charon@${DIGEST_HASH}" + + echo "Re-tagging charon:e2e-test as $FULL_REF" + docker tag charon:e2e-test "$FULL_REF" + + # Export for compose file + echo "CHARON_E2E_IMAGE_DIGEST=$FULL_REF" >> $GITHUB_ENV + else + # Fallback to tag-based reference + echo "CHARON_E2E_IMAGE=charon:e2e-test" >> $GITHUB_ENV + fi +``` + +#### Change 2: Update Compose File + +**File**: `.docker/compose/docker-compose.playwright-ci.yml` +**Lines**: 31-37 + +Keep the current implementation but fix the comment: + +```yaml + charon-app: + # CI: Digest-pinned reference (re-tagged from loaded artifact) + # CHARON_E2E_IMAGE_DIGEST=ghcr.io/wikid82/charon@sha256: + # Local: Tag-based reference for development + # CHARON_E2E_IMAGE=charon:e2e-test + image: ${CHARON_E2E_IMAGE_DIGEST:-${CHARON_E2E_IMAGE:-charon:e2e-test}} +``` + +**Rationale**: +- Preserves digest-based pinning for supply chain security +- Re-tagging creates a local image reference that Docker can resolve +- Falls back gracefully to tag-based reference for local development + +--- + +## Recommended Approach: Option 1 (Simplicity) + +**Why Option 1**: +1. **Simpler**: No re-tagging logic needed +2. **Faster**: Fewer Docker operations +3. **Sufficient**: The image is already built and loaded; tag reference is adequate +4. **Consistent**: Matches how `playwright.yml` handles loaded images +5. **Local-first**: The image is local after `docker load`, not in a registry + +**When to use Option 2**: +- If there's a compliance requirement to use digest references +- If SBOM/attestation workflows need digest traceability +- If multi-registry scenarios require content-addressable references + +--- + +## Implementation Steps + +### Phase 1: Apply Recommended Fix (Option 1) + +1. **Update workflow environment variables** + - File: `.github/workflows/e2e-tests.yml` + - Line: 158 + - Change: Replace `CHARON_E2E_IMAGE_DIGEST` with `CHARON_E2E_IMAGE: charon:e2e-test` + +2. **Update compose file documentation** + - File: `.docker/compose/docker-compose.playwright-ci.yml` + - Lines: 31-37 + - Change: Simplify variable fallback and update comments + +3. **Verify changes** + - Run: `docker compose -f .docker/compose/docker-compose.playwright-ci.yml config` + - Ensure: `image: charon:e2e-test` in output + - Validate: No environment variable warnings + +### Phase 2: Test in CI + +1. **Create test PR** + - Branch: `fix/docker-compose-image-reference` + - Include: Both file changes from Phase 1 + +2. **Monitor workflow execution** + - Watch: `e2e-tests.yml` workflow + - Check: "Start test environment" step succeeds + - Verify: Container starts and health check passes + +3. **Validate container** + - Check: `docker ps` shows `charon-playwright` running + - Test: Health endpoint responds at `http://localhost:8080/api/v1/health` + - Confirm: Playwright tests execute successfully + +### Phase 3: Documentation Update + +1. **Update workflow documentation** + - File: `.github/workflows/e2e-tests.yml` + - Section: Top-level comments (lines 1-29) + - Add: Note about using local tag vs. digest + +2. **Update compose file documentation** + - File: `.docker/compose/docker-compose.playwright-ci.yml` + - Section: Usage section (lines 11-16) + - Clarify: Environment variable expectations + +--- + +## Verification Checklist + +### Pre-Deployment Validation + +- [ ] **Syntax Check**: Run `docker compose config` with test environment variables +- [ ] **Variable Resolution**: Confirm `image:` field resolves to `charon:e2e-test` +- [ ] **Local Test**: Load image locally and run compose up +- [ ] **Workflow Dry-run**: Test changes in a draft PR before merging + +### CI Validation Points + +- [ ] **Build Job**: Completes successfully, uploads image artifact +- [ ] **Download**: Image artifact downloads correctly +- [ ] **Load**: `docker load` succeeds, image appears in `docker images` +- [ ] **Compose Up**: Container starts without pull errors +- [ ] **Health Check**: Container becomes healthy within timeout +- [ ] **Test Execution**: Playwright tests run and report results + +### Post-Deployment Monitoring + +- [ ] **Success Rate**: Monitor e2e-tests.yml success rate for 10 runs +- [ ] **Startup Time**: Verify container startup time remains under 30s +- [ ] **Resource Usage**: Check for memory/CPU regressions +- [ ] **Flake Rate**: Ensure no new test flakiness introduced + +--- + +## Risk Assessment + +### Low Risk Changes +✅ Workflow environment variable change (isolated to CI) +✅ Compose file comment updates (documentation only) + +### Medium Risk Changes +⚠️ Compose file `image:` field modification +- **Mitigation**: Test locally before pushing +- **Rollback**: Revert single line in compose file + +### No Risk +✅ Read-only investigation and analysis +✅ Documentation improvements + +--- + +## Rollback Plan + +### If Option 1 Fails + +**Symptoms**: +- Container still fails to start +- Error: "No such image: charon:e2e-test" + +**Rollback**: +```bash +git revert # Revert the workflow change +``` + +**Alternative Fix**: Switch to Option 2 (re-tagging approach) + +### If Option 2 Fails + +**Symptoms**: +- Re-tag logic fails +- Digest extraction errors + +**Rollback**: +1. Remove re-tagging step +2. Fall back to simple tag reference: `CHARON_E2E_IMAGE=charon:e2e-test` + +--- + +## Success Metrics + +### Immediate Success Indicators +- ✅ `docker compose up` starts container without errors +- ✅ Container health check passes within 30 seconds +- ✅ Playwright tests execute (pass or fail is separate concern) + +### Long-term Success Indicators +- ✅ E2E workflow success rate returns to baseline (>95%) +- ✅ No image reference errors in CI logs for 2 weeks +- ✅ Local development workflow unaffected + +--- + +## Related Issues and Context + +### Why Was Digest Being Used? + +**Comment from compose file** (line 33): +```yaml +# CHARON_E2E_IMAGE_DIGEST=ghcr.io/wikid82/charon:nightly@sha256: +``` + +**Hypothesis**: The original intent was to support digest-pinned references for security/reproducibility, but the implementation was incomplete: +1. The workflow sets only the digest hash, not the full reference +2. The compose file expects the full reference format +3. No re-tagging step bridges the gap + +### Why Does playwright.yml Work? + +**Key difference** (lines 213-277): +- Uses `docker run` directly with explicit image reference +- Constructs full `ghcr.io/...` reference from variables +- Does not rely on environment variable substitution in compose file + +**Lesson**: Direct Docker commands give more control than Compose environment variable interpolation. + +--- + +## Dependencies + +### Required Secrets +- ✅ `CHARON_EMERGENCY_TOKEN` (already configured) +- ✅ `CHARON_CI_ENCRYPTION_KEY` (generated in workflow) + +### Required Tools +- ✅ Docker Compose (available in GitHub Actions) +- ✅ Docker CLI (available in GitHub Actions) + +### No External Dependencies +- ✅ No registry authentication needed (local image) +- ✅ No network calls required (image pre-loaded) + +--- + +## Timeline + +| Phase | Duration | Blocking | +|-------|----------|----------| +| **Analysis & Planning** | Complete | ✅ | +| **Implementation** | 30 minutes | ⏳ | +| **Testing (PR)** | 10-15 minutes (CI runtime) | ⏳ | +| **Verification** | 2 hours (10 workflow runs) | ⏳ | +| **Documentation** | 15 minutes | ⏳ | + +**Estimated Total**: 3-4 hours from start to complete verification + +--- + +## Next Actions + +1. **Immediate**: Implement Option 1 changes (2 file modifications) +2. **Test**: Create PR and monitor e2e-tests.yml workflow +3. **Verify**: Check container startup and health check success +4. **Document**: Update this plan with results +5. **Close**: Mark as complete once verified in main branch + +--- + +## Appendix: Full File Changes + +### File 1: `.github/workflows/e2e-tests.yml` + +**Line 158**: Change environment variable + +```diff + e2e-tests: + name: E2E Tests (Shard ${{ matrix.shard }}/${{ matrix.total-shards }}) + runs-on: ubuntu-latest + needs: build + timeout-minutes: 30 + env: + # Required for security teardown (emergency reset fallback when ACL blocks API) + CHARON_EMERGENCY_TOKEN: ${{ secrets.CHARON_EMERGENCY_TOKEN }} + # Enable security-focused endpoints and test gating + CHARON_EMERGENCY_SERVER_ENABLED: "true" + CHARON_SECURITY_TESTS_ENABLED: "true" +- CHARON_E2E_IMAGE_DIGEST: ${{ needs.build.outputs.image_digest }} ++ # Use local tag for pre-built image (loaded from artifact) ++ CHARON_E2E_IMAGE: charon:e2e-test +``` + +### File 2: `.docker/compose/docker-compose.playwright-ci.yml` + +**Lines 31-37**: Simplify image reference + +```diff + charon-app: +- # CI default (digest-pinned via workflow output): +- # CHARON_E2E_IMAGE_DIGEST=ghcr.io/wikid82/charon:nightly@sha256: +- # Local override (tag-based): ++ # CI default: Uses pre-built image loaded from artifact ++ # Set via workflow: CHARON_E2E_IMAGE=charon:e2e-test ++ # Local development: Uses locally built image ++ # Override with: CHARON_E2E_IMAGE=charon:local-dev +- image: ${CHARON_E2E_IMAGE_DIGEST:-${CHARON_E2E_IMAGE:-charon:e2e-test}} ++ image: ${CHARON_E2E_IMAGE:-charon:e2e-test} +``` + +--- + +**End of Remediation Plan** diff --git a/docs/plans/docker_compose_ci_fix_summary.md b/docs/plans/docker_compose_ci_fix_summary.md new file mode 100644 index 00000000..95ff6dd9 --- /dev/null +++ b/docs/plans/docker_compose_ci_fix_summary.md @@ -0,0 +1,83 @@ +# Docker Compose CI Fix - Quick Reference + +**Document**: [Full Remediation Plan](docker_compose_ci_fix.md) +**Status**: Ready for Implementation +**Priority**: CRITICAL + +--- + +## Problem + +E2E tests failing with: +``` +charon-app Error pull access denied for sha256, repository does not exist +``` + +## Root Cause + +The workflow passes **bare SHA256 digest** to Docker Compose: +```yaml +CHARON_E2E_IMAGE_DIGEST: sha256:057a9998... +``` + +Docker tries to pull from a repository named "sha256" (doesn't exist). + +## Solution + +Use the **local tag** that already exists after `docker load`: + +### Change 1: Workflow + +**File**: `.github/workflows/e2e-tests.yml` (line 158) + +```diff +- CHARON_E2E_IMAGE_DIGEST: ${{ needs.build.outputs.image_digest }} ++ # Use local tag for pre-built image (loaded from artifact) ++ CHARON_E2E_IMAGE: charon:e2e-test +``` + +### Change 2: Compose File + +**File**: `.docker/compose/docker-compose.playwright-ci.yml` (lines 31-37) + +```diff +- # CI default (digest-pinned via workflow output): +- # CHARON_E2E_IMAGE_DIGEST=ghcr.io/wikid82/charon:nightly@sha256: +- # Local override (tag-based): ++ # CI default: Uses pre-built image loaded from artifact ++ # Set via workflow: CHARON_E2E_IMAGE=charon:e2e-test ++ # Local development: Uses locally built image ++ # Override with: CHARON_E2E_IMAGE=charon:local-dev +- image: ${CHARON_E2E_IMAGE_DIGEST:-${CHARON_E2E_IMAGE:-charon:e2e-test}} ++ image: ${CHARON_E2E_IMAGE:-charon:e2e-test} +``` + +## Why This Works + +| Step | Current (Broken) | Fixed | +|------|-----------------|-------| +| Build | Tags as `charon:e2e-test` | Same | +| Load | Image available as `charon:e2e-test` | Same | +| Compose | Tries to use `sha256:...` ❌ | Uses `charon:e2e-test` ✅ | + +## Verification + +```bash +# After changes, run locally: +export CHARON_E2E_IMAGE=charon:e2e-test +docker compose -f .docker/compose/docker-compose.playwright-ci.yml config | grep "image:" + +# Should output: +# image: charon:e2e-test +``` + +## Testing + +1. Create PR with both changes +2. Monitor `e2e-tests.yml` workflow +3. Verify "Start test environment" step succeeds +4. Confirm health check passes + +--- + +**See [docker_compose_ci_fix.md](docker_compose_ci_fix.md) for full analysis and implementation details.** diff --git a/docs/reports/qa_report.md b/docs/reports/qa_report.md index 7629e6d9..99919239 100644 --- a/docs/reports/qa_report.md +++ b/docs/reports/qa_report.md @@ -1,3 +1,144 @@ +# QA Report: Docker Compose CI Fix Verification + +**Date**: 2026-01-30 +**Verification**: Docker Compose E2E Image Tag Fix + +--- + +## Summary + +**RESULT: ✅ PASS** + +The Docker Compose CI fix has been correctly implemented. The environment variable change from `CHARON_E2E_IMAGE_DIGEST` to `CHARON_E2E_IMAGE_TAG` is properly configured in both the workflow and compose files. + +--- + +## Verification Results + +### 1. Workflow File Analysis (`.github/workflows/e2e-tests.yml`) + +**Status**: ✅ PASS + +| Check | Result | Details | +|-------|--------|---------| +| `CHARON_E2E_IMAGE_TAG` defined | ✅ | Set to `charon:e2e-test` at line 159 in `e2e-tests` job env block | +| No `CHARON_E2E_IMAGE_DIGEST` references | ✅ | Searched entire file (533 lines) - no occurrences found | +| Image build tag matches | ✅ | Build job uses `tags: charon:e2e-test` at line 122 | +| Image save/load flow | ✅ | Saves as `charon-e2e-image.tar`, loads in test shards | + +**Relevant Code (lines 157-160)**: +```yaml +env: + CHARON_EMERGENCY_TOKEN: ${{ secrets.CHARON_EMERGENCY_TOKEN }} + CHARON_EMERGENCY_SERVER_ENABLED: "true" + CHARON_SECURITY_TESTS_ENABLED: "true" + CHARON_E2E_IMAGE_TAG: charon:e2e-test +``` + +### 2. Compose File Analysis (`.docker/compose/docker-compose.playwright-ci.yml`) + +**Status**: ✅ PASS + +| Check | Result | Details | +|-------|--------|---------| +| Variable substitution syntax | ✅ | Uses `${CHARON_E2E_IMAGE_TAG:-charon:e2e-test}` | +| Fallback default value | ✅ | Falls back to `charon:e2e-test` when env var not set | +| Service definition correct | ✅ | `charon-app` service uses the image reference at line 30 | + +**Relevant Code (lines 28-31)**: +```yaml +charon-app: + # CI provides CHARON_E2E_IMAGE_TAG=charon:e2e-test (locally built image) + # Local development uses the default fallback value + image: ${CHARON_E2E_IMAGE_TAG:-charon:e2e-test} +``` + +### 3. Variable Substitution Verification + +**Status**: ✅ PASS (Verified via code analysis) + +| Scenario | Expected Image | Analysis | +|----------|----------------|----------| +| CI with `CHARON_E2E_IMAGE_TAG=charon:e2e-test` | `charon:e2e-test` | ✅ Env var value used | +| Local without env var | `charon:e2e-test` | ✅ Default fallback used | +| Custom tag override | User-specified value | ✅ Bash variable substitution syntax correct | + +### 4. YAML Syntax Validation + +**Status**: ✅ PASS (Verified via structure analysis) + +| File | Status | Details | +|------|--------|---------| +| `e2e-tests.yml` | ✅ Valid | 533 lines, proper YAML structure | +| `docker-compose.playwright-ci.yml` | ✅ Valid | 159 lines, proper compose v3 structure | + +### 5. Consistency Checks + +**Status**: ✅ PASS + +| Check | Result | +|-------|--------| +| Build tag matches runtime tag | ✅ Both use `charon:e2e-test` | +| Environment variable naming consistent | ✅ `CHARON_E2E_IMAGE_TAG` used everywhere | +| No digest-based references remain | ✅ No `@sha256:` references for the app image | +| Compose file references in workflow | ✅ All 4 references use correct path `.docker/compose/docker-compose.playwright-ci.yml` | + +--- + +## Architecture Summary + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ E2E Test Workflow │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ [Build Job] │ +│ ├── Build image with tag: charon:e2e-test │ +│ ├── Save to: charon-e2e-image.tar │ +│ └── Upload artifact │ +│ │ +│ [E2E Tests Job] (4 shards) │ +│ ├── Download artifact │ +│ ├── docker load -i charon-e2e-image.tar │ +│ ├── env: CHARON_E2E_IMAGE_TAG=charon:e2e-test │ +│ └── docker compose up (uses ${CHARON_E2E_IMAGE_TAG}) │ +│ │ +│ [docker-compose.playwright-ci.yml] │ +│ └── image: ${CHARON_E2E_IMAGE_TAG:-charon:e2e-test} │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Issues Found + +**None** - The implementation is correct and ready for CI testing. + +--- + +## Recommendations + +1. **Merge and Test**: The fix is ready for CI validation +2. **Monitor First Run**: Watch the first CI run to confirm the compose file resolves the image correctly +3. **Log Verification**: Check `docker images | grep charon` output in CI logs shows `charon:e2e-test` + +--- + +## Conclusion + +The Docker Compose CI fix has been **correctly implemented**: + +- ✅ Environment variable renamed from `CHARON_E2E_IMAGE_DIGEST` to `CHARON_E2E_IMAGE_TAG` +- ✅ Compose file uses proper variable substitution with fallback +- ✅ Build and runtime tags are consistent (`charon:e2e-test`) +- ✅ No legacy digest references remain +- ✅ YAML syntax is valid + +**Ready for CI testing.** + +--- + # QA Validation Report: CI Workflow Fixes **Report Date:** 2026-01-30