Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.git
.github
.idea
.vscode
*.md
docs
tests
.env
.env.*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dockerignore missing vendor breaks prod image build

Medium Severity

The new .dockerignore omits vendor/. The new prod Dockerfile target runs composer install --no-dev then COPY . .. On any machine where composer install was run on the host (e.g. via make install), COPY . . overwrites the container's --no-dev vendor directory with the host's full vendor tree including dev dependencies like PHPUnit. The documented prod build command produces an image with dev dependencies baked in.

Additional Locations (1)
Fix in Cursor Fix in Web

31 changes: 29 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,49 @@ DB_DATABASE=events_db
DB_USERNAME=events_user
DB_PASSWORD=events_pass

# Docker image build (optional) — default uses AWS Public ECR mirror of official php (same as Hub, different CDN).
# If pulls still fail, try one of:
# PHP_BASE_IMAGE=mirror.gcr.io/library/php:8.2-fpm-alpine
# PHP_BASE_IMAGE=php:8.2-fpm-alpine
# PHP_BASE_IMAGE=public.ecr.aws/docker/library/php:8.2-fpm-alpine

# Redis
REDIS_HOST=redis
REDIS_PORT=6379

# RabbitMQ
# RabbitMQ (async plan sync — see README)
RABBITMQ_HOST=rabbitmq
RABBITMQ_PORT=5672
RABBITMQ_USER=events_user
RABBITMQ_PASS=events_pass
# Default vhost "/" — set explicitly if your broker uses another vhost
# RABBITMQ_VHOST=/
# Optional overrides (defaults match AmqpPlanSyncJobPublisher constants):
# RABBITMQ_EXCHANGE=ticketbridge
# RABBITMQ_DLX_EXCHANGE=ticketbridge.dlx
# RABBITMQ_SYNC_QUEUE=ticketbridge.sync.plans
# RABBITMQ_SYNC_ROUTING_KEY=sync.plans
# RABBITMQ_SYNC_DLQ=ticketbridge.sync.plans.dlq
# RABBITMQ_SYNC_DLQ_ROUTING_KEY=sync.plans.dlq
# RABBITMQ_CONN_TIMEOUT=5
# RABBITMQ_IO_TIMEOUT=10
# RABBITMQ_CONSUMER_IO_TIMEOUT=130
# RABBITMQ_HEARTBEAT=30

# Provider feed: local XML from tests/Fixtures (default) OR HTTP if you clear this path
PROVIDER_FIXTURE_PATH=tests/Fixtures/provider_responses/response_1.xml
# Used only when PROVIDER_FIXTURE_PATH is empty:
PROVIDER_URL=https://your-provider.example/api/events

# HTTP client (sync only — /search does not call the provider)
EVENT_PROVIDER_TIMEOUT=10
EVENT_PROVIDER_CONNECT_TIMEOUT=5
EVENT_PROVIDER_RETRY_ATTEMPTS=3
EVENT_PROVIDER_RETRY_BASE_MS=500

# Circuit breaker (sync only; state stored in Redis)
EVENT_PROVIDER_CB_FAILURES=5
EVENT_PROVIDER_CB_OPEN_SECONDS=60

# Cache Configuration
# /search response cache (TTL per key; invalidation bumps epoch — see README)
CACHE_SEARCH_TTL=60
75 changes: 75 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: CI

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

jobs:
unit:
name: PHPUnit (unit)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: shivammathur/setup-php@v2
with:
php-version: "8.2"
extensions: dom, mbstring, zip
coverage: none

- name: Install Composer dependencies
run: composer install --no-interaction --prefer-dist

- name: Run unit tests
run: ./vendor/bin/phpunit tests/Unit --testdox

integration-database:
name: PHPUnit (integration / DB)
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_USER: events_user
POSTGRES_PASSWORD: events_pass
POSTGRES_DB: events_db
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U events_user -d events_db"
--health-interval 5s
--health-timeout 5s
--health-retries 10

steps:
- uses: actions/checkout@v4

- uses: shivammathur/setup-php@v2
with:
php-version: "8.2"
extensions: dom, mbstring, pdo_pgsql, zip
coverage: none

- name: Install PostgreSQL client
run: sudo apt-get update && sudo apt-get install -y postgresql-client

- name: Apply schema
env:
PGPASSWORD: events_pass
run: |
psql -h 127.0.0.1 -p 5432 -U events_user -d events_db -f database/schema.sql
psql -h 127.0.0.1 -p 5432 -U events_user -d events_db -f database/migrations/002_zones_available_seats.sql

- name: Install Composer dependencies
run: composer install --no-interaction --prefer-dist

- name: Run database integration tests
env:
DB_HOST: 127.0.0.1
DB_PORT: "5432"
DB_DATABASE: events_db
DB_USERNAME: events_user
DB_PASSWORD: events_pass
run: ./vendor/bin/phpunit tests/Integration/Database --testdox
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 TicketBridge contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
69 changes: 57 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,26 +1,66 @@
.PHONY: install test run sync migrate cache-clear stop clean help
.PHONY: install test test-all test-integration ready wait-ready run sync migrate cache-clear stop clean help consume-sync enqueue-plan-sync pull-base

help:
@echo "make install - Setup"
@echo "make run - Start"
@echo "make test - Unit tests"
@echo "make test-integration - DB integration tests (Docker app)"
@echo "make stop - Stop"
@echo "make pull-base - docker pull PHP base (default: AWS Public ECR mirror of official php)"
@echo "make ready - install + run full test suite (best first run after clone)"
@echo "make install - composer + compose up + wait for Postgres + migrate + sync"
@echo "make test-all - all PHPUnit tests inside app container (needs stack up)"
@echo "make test - unit tests only (inside app container)"
@echo "make test-integration - DB integration tests (inside app container)"
@echo "make run - Start Docker stack"
@echo "make migrate - Apply schema + migrations (Postgres in compose)"
@echo "make sync - Run provider sync inside app container (inline)"
@echo "make enqueue-plan-sync - Publish one async plan-sync job (RabbitMQ)"
@echo "make consume-sync - Run plan-sync consumer in foreground (worker)"
@echo "make stop - Stop"

# Wait until Postgres accepts connections (avoids flaky migrate/sync after cold start).
wait-ready:
@echo "Waiting for PostgreSQL…"
@i=1; while [ $$i -le 60 ]; do \
docker-compose -f docker/docker-compose.yml exec -T postgres pg_isready -U events_user -d events_db >/dev/null 2>&1 && exit 0; \
i=$$((i+1)); sleep 1; \
done; \
echo "PostgreSQL did not become ready in time."; exit 1

# Same image as Dockerfile ARG (AWS Public ECR mirror of official php — often works when Docker Hub CDN times out).
PHP_BASE_IMAGE ?= public.ecr.aws/docker/library/php:8.2-fpm-alpine

# Pull base image before compose build — avoids BuildKit "DeadlineExceeded" on flaky Docker Hub CDN.
pull-base:
@echo "Pulling $(PHP_BASE_IMAGE) (up to 6 attempts)…"
@attempt=1; \
while [ $$attempt -le 6 ]; do \
docker pull $(PHP_BASE_IMAGE) && exit 0; \
echo "Pull failed (attempt $$attempt/6). Next retry in 25s…"; \
echo "Tip: try PHP_BASE_IMAGE=mirror.gcr.io/library/php:8.2-fpm-alpine make pull-base"; \
attempt=$$((attempt+1)); \
sleep 25; \
done; \
echo "Could not pull base image. Set PHP_BASE_IMAGE in .env (see .env.example) or fix network/VPN/DNS."; \
exit 1

install:
@if [ ! -f .env ]; then cp .env.example .env; fi
composer install
docker-compose -f docker/docker-compose.yml up -d
@sleep 15
@$(MAKE) pull-base
BUILDKIT_PULL_POLICY=if-not-present docker-compose -f docker/docker-compose.yml up -d
@$(MAKE) wait-ready
@$(MAKE) migrate
@$(MAKE) sync
@echo "✅ Setup complete! API available at http://localhost:8000"
@echo "✅ Setup complete! API at http://localhost:8000"

ready: install test-all
@echo "✅ Ready: stack is up, migrations applied, sync done, all tests passed."

test:
./vendor/bin/phpunit tests/Unit --testdox
docker-compose -f docker/docker-compose.yml exec app ./vendor/bin/phpunit tests/Unit --testdox

test-all:
docker-compose -f docker/docker-compose.yml exec app ./vendor/bin/phpunit tests --testdox

run:
docker-compose -f docker/docker-compose.yml up -d
BUILDKIT_PULL_POLICY=if-not-present docker-compose -f docker/docker-compose.yml up -d
@echo "API: http://localhost:8000"

sync:
Expand All @@ -31,8 +71,13 @@ migrate:
@docker-compose -f docker/docker-compose.yml exec -T postgres psql -U events_user -d events_db < database/migrations/002_zones_available_seats.sql 2>/dev/null || true

test-integration:
docker-compose -f docker/docker-compose.yml exec app ./vendor/bin/phpunit tests/Integration/Database --testdox
docker-compose -f docker/docker-compose.yml exec app ./vendor/bin/phpunit tests/Integration --testdox

enqueue-plan-sync:
docker-compose -f docker/docker-compose.yml exec app php scripts/enqueue-plan-sync.php

consume-sync:
docker-compose -f docker/docker-compose.yml exec app php scripts/rabbitmq-consume-plan-sync.php

stop:
docker-compose -f docker/docker-compose.yml down
Expand Down
Loading
Loading