fix: accept string request field in stripeEventSchema #794
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main, dev, v2] | |
| workflow_dispatch: | |
| permissions: | |
| packages: write | |
| contents: write | |
| env: | |
| STRIPE_NPM_REGISTRY: https://npm.pkg.github.com | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # Changes — skip Docker/publish jobs when only docs/ changed | |
| # --------------------------------------------------------------------------- | |
| changes: | |
| name: Detect code changes | |
| runs-on: ubuntu-24.04-arm | |
| outputs: | |
| code: ${{ steps.filter.outputs.code }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: dorny/paths-filter@v4 | |
| id: filter | |
| with: | |
| filters: | | |
| code: | |
| - '**' | |
| - '!docs/**' | |
| # --------------------------------------------------------------------------- | |
| # Test — lint, build, unit & integration tests | |
| # --------------------------------------------------------------------------- | |
| test: | |
| name: Lint, build & test | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Start services | |
| run: docker compose up -d --wait | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Set up .env file | |
| run: | | |
| touch packages/source-stripe/.env | |
| echo DATABASE_URL='postgres://postgres:postgres@localhost:55432/postgres?sslmode=disable&search_path=stripe' >> packages/source-stripe/.env | |
| echo NODE_ENV=dev >> packages/source-stripe/.env | |
| echo STRIPE_SECRET_KEY=sk_test_ >> packages/source-stripe/.env | |
| echo STRIPE_WEBHOOK_SECRET=whsec_ >> packages/source-stripe/.env | |
| echo SCHEMA=stripe >> packages/source-stripe/.env | |
| echo PORT=8080 >> packages/source-stripe/.env | |
| echo API_KEY=api_key_test >> packages/source-stripe/.env | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Formatting checks | |
| run: pnpm format:check | |
| continue-on-error: true | |
| - name: Lint | |
| run: pnpm lint | |
| - name: Build | |
| run: pnpm build | |
| - name: Check deprecated paths are up to date | |
| run: | | |
| cd packages/openapi | |
| bundled=$(ls oas/*.json | grep -v manifest.json | grep -v index.html) | |
| [ "$(echo "$bundled" | wc -l)" -eq 1 ] || { echo "Expected 1 bundled spec"; exit 1; } | |
| version=$(basename "$bundled" .json) | |
| grep -q "DEPRECATED_PATHS_BUNDLED_VERSION = '${version}'" src/deprecatedPaths.ts \ | |
| || { echo "deprecatedPaths.ts is stale (bundled spec is ${version}). Fix: node packages/openapi/scripts/generate-versions.mjs"; exit 1; } | |
| - name: Check generated OpenAPI files | |
| run: ./scripts/generate-openapi.sh --check | |
| env: | |
| STRIPE_API_KEY: 'sk_test_placeholder' | |
| - name: Ensure all shell tests are wired up | |
| run: | | |
| missing=() | |
| for f in e2e/*.test.sh; do | |
| name=$(basename "$f") | |
| if ! grep -rq "$name" .github/workflows/; then | |
| missing+=("$f") | |
| fi | |
| done | |
| if [ ${#missing[@]} -gt 0 ]; then | |
| printf 'Not referenced in any workflow: %s\n' "${missing[@]}" | |
| exit 1 | |
| fi | |
| - name: Initialize DB schema | |
| run: | | |
| docker run --rm \ | |
| --network="host" \ | |
| -e PGPASSWORD=postgres \ | |
| postgres:18 \ | |
| psql -h localhost -p 55432 -U postgres -d postgres -c 'create schema if not exists "stripe";' | |
| - name: stripe-mock smoke test | |
| run: bash e2e/stripe-mock.test.sh | |
| - name: Tests | |
| run: pnpm --filter '!@stripe/sync-e2e' test | |
| env: | |
| DATABASE_URL: 'postgres://postgres:postgres@localhost:55432/postgres?sslmode=disable&search_path=stripe' | |
| POSTGRES_URL: 'postgres://postgres:postgres@localhost:55432/postgres' | |
| STRIPE_MOCK_URL: 'http://localhost:12111' | |
| STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} | |
| GOOGLE_CLIENT_ID: ${{ vars.GOOGLE_CLIENT_ID }} | |
| GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} | |
| GOOGLE_REFRESH_TOKEN: ${{ secrets.GOOGLE_REFRESH_TOKEN }} | |
| GOOGLE_SPREADSHEET_ID: ${{ vars.GOOGLE_SPREADSHEET_ID }} | |
| - name: Disconnect & time-limit tests (Node) | |
| run: | | |
| pnpm --filter @stripe/sync-e2e exec vitest run test-disconnect.test.ts | |
| env: | |
| DISCONNECT_TEST_NODE: '1' | |
| SKIP_SETUP: '1' | |
| - name: Install Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Disconnect & time-limit tests (Bun) | |
| run: | | |
| pnpm --filter @stripe/sync-e2e exec vitest run test-disconnect.test.ts | |
| env: | |
| DISCONNECT_TEST_BUN: '1' | |
| SKIP_SETUP: '1' | |
| - name: Connector loading test | |
| run: bash e2e/connector-loading.test.sh | |
| - name: ESBUILD binary path test | |
| run: bash e2e/esbuild-binary-path.test.sh | |
| - name: Skipped test warnings | |
| if: always() | |
| run: '[ -f /tmp/vitest-skip-warnings.txt ] && cat /tmp/vitest-skip-warnings.txt || true' | |
| # --------------------------------------------------------------------------- | |
| # mitmweb — proxy intercept smoke test (curl, Node, Bun → httpbin.org) | |
| # --------------------------------------------------------------------------- | |
| mitmweb: | |
| name: mitmweb proxy intercept test | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| - name: Install Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Install mitmproxy | |
| run: pip3 install --quiet mitmproxy | |
| - name: Run mitmweb intercept test | |
| run: bash scripts/mitmweb-env.test.sh | |
| - name: Stop mitmweb | |
| if: always() | |
| run: pkill -f mitmweb || true | |
| # --------------------------------------------------------------------------- | |
| # Publish npm — publish @stripe/* packages to GitHub Packages | |
| # --------------------------------------------------------------------------- | |
| publish_npm: | |
| name: Publish npm packages | |
| needs: [changes] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Install dependencies & build | |
| run: pnpm install --frozen-lockfile && pnpm build | |
| - name: Publish to GitHub Packages | |
| run: | | |
| echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc | |
| ./scripts/publish-packages.sh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # --------------------------------------------------------------------------- | |
| # Publish npmjs.org — auto-publish when version changes | |
| # --------------------------------------------------------------------------- | |
| publish_npmjs: | |
| name: Publish to npmjs.org | |
| needs: [test, publish_npm, changes] | |
| if: >- | |
| github.event_name == 'push' | |
| && needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Check if version changed | |
| id: version-check | |
| run: | | |
| LOCAL_VERSION=$(jq -r .version apps/engine/package.json) | |
| NPM_VERSION=$(npm view @stripe/sync-engine version \ | |
| --registry https://registry.npmjs.org 2>/dev/null || echo "0.0.0") | |
| echo "local=$LOCAL_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "npm=$NPM_VERSION" >> "$GITHUB_OUTPUT" | |
| if [ "$LOCAL_VERSION" = "$NPM_VERSION" ]; then | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| echo "Version unchanged: $LOCAL_VERSION" | |
| elif [ "$(printf '%s\n%s\n' "$LOCAL_VERSION" "$NPM_VERSION" | sort -V | tail -n1)" = "$LOCAL_VERSION" ]; then | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| echo "Version increased: npm=$NPM_VERSION -> local=$LOCAL_VERSION" | |
| else | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Skipping npmjs publish because local version $LOCAL_VERSION is older than npmjs version $NPM_VERSION" | |
| fi | |
| - name: Install pnpm | |
| if: steps.version-check.outputs.changed == 'true' | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| if: steps.version-check.outputs.changed == 'true' | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Install dependencies & build | |
| if: steps.version-check.outputs.changed == 'true' | |
| run: pnpm install --frozen-lockfile && pnpm build | |
| - name: Publish to npmjs.org | |
| if: steps.version-check.outputs.changed == 'true' | |
| run: bash scripts/promote-to-npmjs.sh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| GITHUB_REPO_OWNER: ${{ github.repository_owner }} | |
| GITHUB_REPO_NAME: ${{ github.event.repository.name }} | |
| RELEASE_VERSION: ${{ steps.version-check.outputs.local }} | |
| - name: Create GitHub Release | |
| if: steps.version-check.outputs.changed == 'true' | |
| run: | | |
| VERSION="${{ steps.version-check.outputs.local }}" | |
| # Extract changelog section for this version | |
| BODY=$(sed -n "/^## v${VERSION}/,/^## v/{ /^## v${VERSION}/d; /^## v/d; p; }" CHANGELOG.md 2>/dev/null | head -100 || true) | |
| gh release create "v${VERSION}" \ | |
| --title "v${VERSION}" \ | |
| --notes "${BODY:-Release v${VERSION}}" \ | |
| --target "${{ github.sha }}" || echo "::warning::GitHub Release creation skipped (tag may not exist or release already exists)" | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # --------------------------------------------------------------------------- | |
| # Build arm64 — native arm build with BuildKit registry cache | |
| # --------------------------------------------------------------------------- | |
| build: | |
| name: Build Docker (arm64) | |
| needs: [changes] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Install dependencies & build | |
| run: pnpm install --frozen-lockfile && pnpm build | |
| - name: Login to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build and push Docker images (arm64) | |
| run: | | |
| BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) | |
| COMMIT_URL="${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}" | |
| docker buildx build \ | |
| --platform linux/arm64 \ | |
| --build-arg GIT_COMMIT=${{ github.sha }} \ | |
| --build-arg BUILD_DATE="${BUILD_DATE}" \ | |
| --build-arg COMMIT_URL="${COMMIT_URL}" \ | |
| --cache-from type=registry,ref=ghcr.io/${{ github.repository }}:cache-arm64 \ | |
| --cache-to type=registry,ref=ghcr.io/${{ github.repository }}:cache-arm64,mode=max \ | |
| --target engine \ | |
| --tag ghcr.io/${{ github.repository }}:${{ github.sha }}-arm64 \ | |
| --push \ | |
| . | |
| docker buildx build \ | |
| --platform linux/arm64 \ | |
| --build-arg GIT_COMMIT=${{ github.sha }} \ | |
| --build-arg BUILD_DATE="${BUILD_DATE}" \ | |
| --build-arg COMMIT_URL="${COMMIT_URL}" \ | |
| --cache-from type=registry,ref=ghcr.io/${{ github.repository }}-service:cache-arm64 \ | |
| --cache-to type=registry,ref=ghcr.io/${{ github.repository }}-service:cache-arm64,mode=max \ | |
| --target service \ | |
| --tag ghcr.io/${{ github.repository }}-service:${{ github.sha }}-arm64 \ | |
| --push \ | |
| . | |
| # --------------------------------------------------------------------------- | |
| # Build amd64 — native x86 build in parallel with arm64 [push only] | |
| # --------------------------------------------------------------------------- | |
| build_amd64: | |
| name: Build Docker (amd64) | |
| needs: [changes] | |
| if: ${{ github.event_name == 'push' && needs.changes.outputs.code == 'true' }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Install dependencies & build | |
| run: pnpm install --frozen-lockfile && pnpm build | |
| - name: Login to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build and push Docker images (amd64) | |
| run: | | |
| BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) | |
| COMMIT_URL="${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}" | |
| docker buildx build \ | |
| --platform linux/amd64 \ | |
| --build-arg GIT_COMMIT=${{ github.sha }} \ | |
| --build-arg BUILD_DATE="${BUILD_DATE}" \ | |
| --build-arg COMMIT_URL="${COMMIT_URL}" \ | |
| --cache-from type=registry,ref=ghcr.io/${{ github.repository }}:cache-amd64 \ | |
| --cache-to type=registry,ref=ghcr.io/${{ github.repository }}:cache-amd64,mode=max \ | |
| --target engine \ | |
| --tag ghcr.io/${{ github.repository }}:${{ github.sha }}-amd64 \ | |
| --push \ | |
| . | |
| docker buildx build \ | |
| --platform linux/amd64 \ | |
| --build-arg GIT_COMMIT=${{ github.sha }} \ | |
| --build-arg BUILD_DATE="${BUILD_DATE}" \ | |
| --build-arg COMMIT_URL="${COMMIT_URL}" \ | |
| --cache-from type=registry,ref=ghcr.io/${{ github.repository }}-service:cache-amd64 \ | |
| --cache-to type=registry,ref=ghcr.io/${{ github.repository }}-service:cache-amd64,mode=max \ | |
| --target service \ | |
| --tag ghcr.io/${{ github.repository }}-service:${{ github.sha }}-amd64 \ | |
| --push \ | |
| . | |
| # --------------------------------------------------------------------------- | |
| # Merge — combine arm64 + amd64 into a single multi-arch manifest [push only] | |
| # --------------------------------------------------------------------------- | |
| build_manifest: | |
| name: Merge multi-arch manifest | |
| needs: [build, build_amd64] | |
| if: ${{ github.event_name == 'push' }} | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - name: Login to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create and push multi-arch manifests | |
| run: | | |
| TAG="${GITHUB_REF_NAME//\//-}" | |
| docker buildx imagetools create \ | |
| --tag ghcr.io/${{ github.repository }}:${{ github.sha }} \ | |
| --tag ghcr.io/${{ github.repository }}:${TAG} \ | |
| ghcr.io/${{ github.repository }}:${{ github.sha }}-amd64 \ | |
| ghcr.io/${{ github.repository }}:${{ github.sha }}-arm64 | |
| docker buildx imagetools create \ | |
| --tag ghcr.io/${{ github.repository }}-service:${{ github.sha }} \ | |
| --tag ghcr.io/${{ github.repository }}-service:${TAG} \ | |
| ghcr.io/${{ github.repository }}-service:${{ github.sha }}-amd64 \ | |
| ghcr.io/${{ github.repository }}-service:${{ github.sha }}-arm64 | |
| - name: Docker smoke test | |
| run: | | |
| docker run -d --name smoke -p 3000:3000 ghcr.io/${{ github.repository }}:${{ github.sha }} | |
| trap 'docker rm -f smoke 2>/dev/null || true' EXIT | |
| for i in $(seq 1 30); do | |
| body=$(curl -sf http://localhost:3000/health 2>/dev/null || true) | |
| if echo "$body" | grep -q '"ok":true'; then | |
| echo "health check passed: $body" | |
| exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "health check timed out" | |
| docker logs smoke | |
| exit 1 | |
| # --------------------------------------------------------------------------- | |
| # E2E Docker — Docker image smoke + engine tests (runs on every push/PR) | |
| # --------------------------------------------------------------------------- | |
| e2e_docker: | |
| name: E2E Docker | |
| needs: build | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Install dependencies & build | |
| run: pnpm install --frozen-lockfile && pnpm build | |
| - name: Start Postgres with SSL | |
| run: | | |
| docker run -d --name ci-postgres \ | |
| -e POSTGRES_PASSWORD=postgres \ | |
| -p 55432:5432 \ | |
| postgres:18 \ | |
| -c ssl=on \ | |
| -c ssl_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem \ | |
| -c ssl_key_file=/etc/ssl/private/ssl-cert-snakeoil.key | |
| for i in $(seq 1 30); do | |
| docker exec ci-postgres pg_isready -U postgres && break || sleep 2 | |
| done | |
| - name: Login to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Docker tests | |
| run: | | |
| IMAGE="ghcr.io/${{ github.repository }}:${{ github.sha }}-arm64" | |
| docker pull "$IMAGE" | |
| if [ -n "${STRIPE_API_KEY:-}" ]; then | |
| bash e2e/docker.test.sh "$IMAGE" | |
| else | |
| echo "::warning::Docker e2e skipped — STRIPE_API_KEY not available. Running smoke only." | |
| docker run --rm "$IMAGE" --version | |
| fi | |
| env: | |
| STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} | |
| POSTGRES_URL: 'postgres://postgres:postgres@localhost:55432/postgres' | |
| GOOGLE_CLIENT_ID: ${{ vars.GOOGLE_CLIENT_ID }} | |
| GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} | |
| GOOGLE_REFRESH_TOKEN: ${{ secrets.GOOGLE_REFRESH_TOKEN }} | |
| GOOGLE_SPREADSHEET_ID: ${{ vars.GOOGLE_SPREADSHEET_ID }} | |
| - name: Disconnect & time-limit tests (Docker) | |
| run: | | |
| pnpm --filter @stripe/sync-e2e exec vitest run test-disconnect.test.ts | |
| env: | |
| DISCONNECT_TEST_DOCKER: '1' | |
| DISCONNECT_TEST_DOCKER_HOST_NETWORK: '1' | |
| ENGINE_IMAGE: 'ghcr.io/${{ github.repository }}:${{ github.sha }}-arm64' | |
| - name: Smokescreen proxy e2e | |
| run: | | |
| if [ -z "${STRIPE_API_KEY:-}" ]; then | |
| echo "::warning::smokescreen.test.sh skipped — STRIPE_API_KEY not available" | |
| exit 0 | |
| fi | |
| bash e2e/smokescreen.test.sh | |
| env: | |
| STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} | |
| ENGINE_IMAGE: 'ghcr.io/${{ github.repository }}:${{ github.sha }}-arm64' | |
| - name: Publish test | |
| run: | | |
| if [ -z "${STRIPE_NPM_REGISTRY:-}" ]; then | |
| echo "::warning::publish.test.sh skipped — STRIPE_NPM_REGISTRY not set" | |
| exit 0 | |
| fi | |
| bash e2e/publish.test.sh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # --------------------------------------------------------------------------- | |
| # E2E Stripe — Stripe API + Temporal integration tests (runs on every push/PR) | |
| # --------------------------------------------------------------------------- | |
| e2e_stripe: | |
| name: E2E Stripe | |
| runs-on: ubuntu-24.04-arm | |
| services: | |
| temporal-db: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: temporal | |
| POSTGRES_PASSWORD: temporal | |
| options: >- | |
| --health-cmd "pg_isready -U temporal" | |
| --health-interval 5s | |
| --health-timeout 3s | |
| --health-retries 5 | |
| temporal: | |
| image: temporalio/auto-setup:latest | |
| env: | |
| DB: postgres12 | |
| DB_PORT: 5432 | |
| POSTGRES_USER: temporal | |
| POSTGRES_PWD: temporal | |
| POSTGRES_SEEDS: temporal-db | |
| ports: | |
| - 7233:7233 | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Start Postgres with SSL | |
| run: | | |
| docker run -d --name ci-postgres \ | |
| -e POSTGRES_PASSWORD=postgres \ | |
| -p 55432:5432 \ | |
| postgres:18 \ | |
| -c ssl=on \ | |
| -c ssl_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem \ | |
| -c ssl_key_file=/etc/ssl/private/ssl-cert-snakeoil.key | |
| for i in $(seq 1 30); do | |
| docker exec ci-postgres pg_isready -U postgres && break || sleep 2 | |
| done | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Install dependencies & build | |
| run: pnpm install --frozen-lockfile && pnpm build | |
| - name: E2E vitest tests | |
| run: | | |
| if [ -z "${STRIPE_API_KEY:-}" ]; then | |
| echo "::warning::E2E tests skipped — STRIPE_API_KEY not available (fork PR?)" | |
| exit 0 | |
| fi | |
| pnpm --filter @stripe/sync-e2e exec vitest run \ | |
| --exclude 'service-docker.test.ts' \ | |
| --exclude 'test-server-all-api.test.ts' \ | |
| --exclude 'test-server-sync.test.ts' \ | |
| --exclude 'test-sync-e2e.test.ts' \ | |
| --exclude 'test-sync-engine.test.ts' \ | |
| --exclude 'test-e2e-network.test.ts' \ | |
| --exclude 'test-disconnect.test.ts' # ↑ run in dedicated Docker / main jobs | |
| env: | |
| STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} | |
| POSTGRES_URL: 'postgres://postgres:postgres@localhost:55432/postgres' | |
| TEMPORAL_ADDRESS: 'localhost:7233' | |
| - name: Skipped test warnings | |
| if: always() | |
| run: '[ -f /tmp/vitest-skip-warnings.txt ] && cat /tmp/vitest-skip-warnings.txt || true' | |
| # --------------------------------------------------------------------------- | |
| # E2E Test Server — test-server suites that need Docker Compose infrastructure | |
| # --------------------------------------------------------------------------- | |
| e2e_test_server: | |
| name: E2E Test Server | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Install dependencies & build | |
| run: pnpm install --frozen-lockfile && pnpm build | |
| - name: Start Docker Compose stack | |
| run: | | |
| docker compose -f compose.yml -f compose.dev.yml -f e2e/compose.e2e.yml \ | |
| up --build -d --wait temporal engine service worker | |
| - name: Test server suites (parallel) | |
| run: | | |
| pnpm --filter @stripe/sync-e2e exec vitest run \ | |
| test-server-all-api.test.ts \ | |
| test-server-sync.test.ts \ | |
| test-sync-e2e.test.ts \ | |
| test-sync-engine.test.ts | |
| env: | |
| SKIP_SETUP: '1' | |
| GH_TOKEN: ${{ github.token }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| - name: Network interruption tests (pauses containers) | |
| run: | | |
| pnpm --filter @stripe/sync-e2e exec vitest run \ | |
| test-e2e-network.test.ts | |
| env: | |
| SKIP_SETUP: '1' | |
| - name: Skipped test warnings | |
| if: always() | |
| run: '[ -f /tmp/vitest-skip-warnings.txt ] && cat /tmp/vitest-skip-warnings.txt || true' | |
| # --------------------------------------------------------------------------- | |
| # E2E Service — service + worker Docker containers end-to-end (every push/PR) | |
| # --------------------------------------------------------------------------- | |
| e2e_service: | |
| name: E2E Service Docker | |
| needs: build | |
| runs-on: ubuntu-24.04-arm | |
| services: | |
| temporal-db: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: temporal | |
| POSTGRES_PASSWORD: temporal | |
| options: >- | |
| --health-cmd "pg_isready -U temporal" | |
| --health-interval 5s | |
| --health-timeout 3s | |
| --health-retries 5 | |
| temporal: | |
| image: temporalio/auto-setup:latest | |
| env: | |
| DB: postgres12 | |
| DB_PORT: 5432 | |
| POSTGRES_USER: temporal | |
| POSTGRES_PWD: temporal | |
| POSTGRES_SEEDS: temporal-db | |
| ports: | |
| - 7233:7233 | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Start Postgres with SSL | |
| run: | | |
| docker run -d --name ci-postgres \ | |
| -e POSTGRES_PASSWORD=postgres \ | |
| -p 55432:5432 \ | |
| postgres:18 \ | |
| -c ssl=on \ | |
| -c ssl_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem \ | |
| -c ssl_key_file=/etc/ssl/private/ssl-cert-snakeoil.key | |
| for i in $(seq 1 30); do | |
| docker exec ci-postgres pg_isready -U postgres && break || sleep 2 | |
| done | |
| - name: Start stripe-mock | |
| run: | | |
| docker run -d --name ci-stripe-mock -p 12111:12111 stripe/stripe-mock:latest | |
| for i in $(seq 1 20); do | |
| if nc -z localhost 12111 2>/dev/null; then | |
| echo "stripe-mock ready after ${i}s" | |
| break | |
| fi | |
| sleep 0.5 | |
| done | |
| nc -z localhost 12111 || (echo "stripe-mock failed to start"; exit 1) | |
| - name: Login to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v5 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ./.nvmrc | |
| cache: pnpm | |
| - name: Pull images and install dependencies in parallel | |
| run: | | |
| ENGINE_IMAGE="ghcr.io/${{ github.repository }}:${{ github.sha }}-arm64" | |
| SERVICE_IMAGE="ghcr.io/${{ github.repository }}-service:${{ github.sha }}-arm64" | |
| docker pull "$ENGINE_IMAGE" & | |
| docker pull "$SERVICE_IMAGE" & | |
| pnpm install --frozen-lockfile && pnpm build | |
| wait | |
| env: | |
| ENGINE_IMAGE: ${{ github.repository }} | |
| SERVICE_IMAGE: ${{ github.repository }}-service | |
| - name: Wait for Temporal to be ready | |
| run: | | |
| echo "Waiting for Temporal gRPC on localhost:7233..." | |
| for i in $(seq 1 90); do | |
| if nc -z 127.0.0.1 7233 2>/dev/null; then | |
| echo "Temporal port open after ${i}s" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| nc -z 127.0.0.1 7233 || (echo "Temporal never became ready"; exit 1) | |
| # Sleep to allow schema migration + namespace creation to complete after port opens | |
| sleep 20 | |
| - name: Start service containers | |
| run: | | |
| ENGINE_IMAGE="ghcr.io/${{ github.repository }}:${{ github.sha }}-arm64" | |
| SERVICE_IMAGE="ghcr.io/${{ github.repository }}-service:${{ github.sha }}-arm64" | |
| docker run -d --name ci-engine --network=host \ | |
| -e PORT=3000 \ | |
| "$ENGINE_IMAGE" | |
| docker volume create ci-pipeline-data | |
| docker run -d --name ci-service --network=host \ | |
| -v ci-pipeline-data:/data \ | |
| "$SERVICE_IMAGE" \ | |
| serve --temporal-address localhost:7233 --port 4020 --data-dir /data | |
| docker run -d --name ci-worker --network=host \ | |
| -v ci-pipeline-data:/data \ | |
| "$SERVICE_IMAGE" \ | |
| worker --temporal-address localhost:7233 --engine-url http://localhost:3000 --data-dir /data | |
| # Wait for service API health | |
| for i in $(seq 1 60); do | |
| if curl -sf http://localhost:4020/health > /dev/null 2>&1; then | |
| echo "Service healthy after ${i}s" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| curl -sf http://localhost:4020/health || (echo "Service failed to start"; docker logs ci-service; exit 1) | |
| # Verify worker is still running (crashes immediately on misconfiguration) | |
| sleep 3 | |
| if ! docker inspect ci-worker --format='{{.State.Running}}' | grep -q true; then | |
| echo "Worker container exited unexpectedly:" | |
| docker logs ci-worker 2>&1 | tail -30 | |
| exit 1 | |
| fi | |
| echo "Worker is running" | |
| - name: Service Docker e2e | |
| run: | | |
| if [ -z "${STRIPE_API_KEY:-}" ]; then | |
| echo "::warning::service-docker.test.ts skipped — STRIPE_API_KEY not available" | |
| exit 0 | |
| fi | |
| pnpm --filter @stripe/sync-e2e exec vitest run service-docker.test.ts | |
| env: | |
| STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} | |
| POSTGRES_URL: 'postgres://postgres:postgres@localhost:55432/postgres' | |
| POSTGRES_CONTAINER_URL: 'postgresql://postgres:postgres@localhost:55432/postgres' | |
| STRIPE_MOCK_URL: 'http://localhost:12111' | |
| SERVICE_DOCKER_E2E: '1' | |
| SKIP_SETUP: '1' | |
| - name: Dump container logs | |
| if: always() | |
| run: | | |
| for name in ci-engine ci-service ci-worker; do | |
| echo "=== $name ===" | |
| docker logs "$name" 2>&1 | tail -100 || true | |
| done | |
| - name: Stop service containers | |
| if: always() | |
| run: docker rm -f ci-engine ci-service ci-worker ci-stripe-mock 2>/dev/null || true | |
| - name: Skipped test warnings | |
| if: always() | |
| run: '[ -f /tmp/vitest-skip-warnings.txt ] && cat /tmp/vitest-skip-warnings.txt || true' | |
| # --------------------------------------------------------------------------- | |
| # Docker Hub — promote the built GHCR image to Docker Hub tags | |
| # --------------------------------------------------------------------------- | |
| publish_dockerhub: | |
| name: Publish Docker Hub | |
| needs: [build_manifest] | |
| if: ${{ github.event_name == 'push' }} | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| sparse-checkout: scripts/promote-to-dockerhub.sh | |
| - name: Login to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN_SYNC_ENGINE }} | |
| - name: Sanitize ref name for Docker tag | |
| id: tag | |
| run: echo "name=${GITHUB_REF_NAME//\//-}" >> "$GITHUB_OUTPUT" | |
| - name: Promote built image to Docker Hub | |
| run: bash scripts/promote-to-dockerhub.sh | |
| env: | |
| GHCR_IMAGE: ghcr.io/${{ github.repository }}:${{ github.sha }} | |
| DOCKERHUB_TAGS: ${{ steps.tag.outputs.name }}${{ github.ref_name == 'main' && ' latest' || '' }} |