diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml deleted file mode 100644 index f951855..0000000 --- a/.github/workflows/preview.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Preview - -on: - workflow_dispatch: - inputs: - name: - description: Preview Worker name prefix - required: false - default: preview - type: string - -permissions: - contents: read - -concurrency: - group: preview-${{ inputs.name || 'preview' }} - cancel-in-progress: true - -jobs: - upload-preview: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v5 - with: - enable-cache: false - - uses: actions/setup-python@v5 - with: - python-version: '3.13' - - uses: actions/setup-node@v4 - with: - node-version: '22' - - name: Install locked dependencies - run: | - uv sync --locked --all-groups - npm ci --ignore-scripts - - name: Build generated assets - run: make build - - name: Verify Cloudflare auth - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - run: npm exec -- wrangler whoami - - name: Sync Python Workers vendor - run: uv run pywrangler sync - # Workflow inputs are passed through env vars rather than interpolated - # into the script, so a crafted input cannot inject shell commands into - # steps that hold the Cloudflare API token. - - name: Upload Cloudflare Preview - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - PREVIEW_NAME: ${{ inputs.name || 'preview' }} - PREVIEW_MESSAGE: ${{ github.sha }} - run: | - set -x - uv run pywrangler preview \ - --name "$PREVIEW_NAME" \ - --message "$PREVIEW_MESSAGE" \ - --json - - name: Smoke test deployed Preview - env: - PREVIEW_NAME: ${{ inputs.name || 'preview' }} - PBE_SMOKE_BYPASS_SECRET: ${{ secrets.PBE_SMOKE_BYPASS_SECRET }} - run: scripts/smoke_deployment.py "https://${PREVIEW_NAME}-pythonbyexample.adewale-883.workers.dev" - - name: Dump wrangler logs on failure - if: failure() - run: | - find ~ /tmp /root -name "*.log" -path "*wrangler*" 2>/dev/null | while read f; do - echo "=== $f ===" - tail -300 "$f" || true - done diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 76ed308..bf06099 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -97,7 +97,7 @@ The single source of truth for the registries is `docs/quality-registries.toml`. ## Secrets and deploy configuration -Deployment uses repository secrets: `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` (Preview workflow), plus optional `PBE_SMOKE_BYPASS_SECRET` so deploy smoke tests can run edited-code POSTs past the Turnstile challenge. Runtime Worker secrets (`TURNSTILE_SECRET_KEY`, `TURNSTILE_CLEARANCE_SECRET`, `PBE_SMOKE_BYPASS_SECRET`) are managed with `wrangler secret put`; see `docs/turnstile-runner-protection-spec.md`. +Production deployment is manual through an authenticated Wrangler session with `make deploy`; public `workers.dev` and version preview URLs are disabled. Runtime Worker secrets (`TURNSTILE_SECRET_KEY`, `TURNSTILE_CLEARANCE_SECRET`, `PBE_SMOKE_BYPASS_SECRET`) are managed with `wrangler secret put`; see `docs/turnstile-runner-protection-spec.md`. Generated output is prevented from drifting before merge: install the local hooks with `scripts/install-git-hooks.sh`, and keep `main` protected so pull requests require the `verify` status check to pass against the current base. CI enforces the same `make check-generated` contract for contributors without local hooks. diff --git a/README.md b/README.md index 12dc590..2dd69bd 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ Use the active Cloudflare-supported Python version. ## Cloudflare notes -- `wrangler.jsonc` enables `python_workers` and `python_dedicated_snapshot`. +- `wrangler.jsonc` enables `python_workers` and `python_dedicated_snapshot` while disabling both public `workers.dev` and version preview URLs. - The parent Worker uses a `LOADER` Worker Loader binding. - Dynamic Worker IDs include Python version, example slug, and submitted code hash. - Dynamic Workers run with `globalOutbound: null` and tight CPU/subrequest limits. diff --git a/docs/turnstile-runner-protection-spec.md b/docs/turnstile-runner-protection-spec.md index 7b40feb..581f7cf 100644 --- a/docs/turnstile-runner-protection-spec.md +++ b/docs/turnstile-runner-protection-spec.md @@ -404,8 +404,8 @@ this rule, record its zone, rule ID, plan, expression, characteristics, threshold/period, mitigation/action, deployment date, and a controlled 429 Security Events result in the release record. `wrangler.jsonc` deploys the Worker only; it cannot declare or verify zone WAF rate-limiting rules. The -production configuration disables `workers.dev` so the custom-domain WAF rule -is not bypassed by a public fallback hostname. +production configuration disables `workers.dev` and version preview URLs so the +custom-domain WAF rule is not bypassed by a public fallback hostname. Cloudflare Dashboard: diff --git a/tests/test_app.py b/tests/test_app.py index b046487..488262f 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -432,9 +432,10 @@ def test_generated_drift_is_blocked_before_commit_and_merge(self): self.assertIn("make verify", verify_workflow) self.assertIn("uv sync --locked --all-groups", verify_workflow) self.assertIn("npm ci --ignore-scripts", verify_workflow) - preview_workflow = (ROOT / ".github" / "workflows" / "preview.yml").read_text() - self.assertIn("uv sync --locked --all-groups", preview_workflow) - self.assertIn("npm exec -- wrangler whoami", preview_workflow) + self.assertFalse((ROOT / ".github" / "workflows" / "preview.yml").exists()) + wrangler_config = (ROOT / "wrangler.jsonc").read_text() + self.assertIn('"workers_dev": false', wrangler_config) + self.assertIn('"preview_urls": false', wrangler_config) package = json.loads((ROOT / "package.json").read_text()) lock = json.loads((ROOT / "package-lock.json").read_text()) self.assertEqual(package["devDependencies"]["wrangler"], "4.110.0") diff --git a/wrangler.jsonc b/wrangler.jsonc index f68ca7d..d4f0273 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -8,7 +8,7 @@ "python_dedicated_snapshot" ], "workers_dev": false, - "preview_urls": true, + "preview_urls": false, "routes": [ { "pattern": "www.pythonbyexample.dev",