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
115 changes: 115 additions & 0 deletions .github/scripts/ci_matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"""Build the CI test matrix from the files a change touches.
Reads changed file paths from stdin, one per line, or tests every package
when called with --all. Prints GitHub Actions outputs to stdout.
"""

import fnmatch
import json
import sys

PYTHON_VERSIONS = ["3.11", "3.12", "3.13", "3.14"]
LOWEST_PYTHON_VERSION = PYTHON_VERSIONS[0]
PACKAGES = [
"functions",
"realtime",
"storage",
"auth",
"postgrest",
"supabase",
"utils",
]
PACKAGES_WITHOUT_COVERAGE = {"utils"}
IMPORT_NAMES = {
"functions": "supabase_functions",
"realtime": "realtime",
"storage": "storage3",
"auth": "supabase_auth",
"postgrest": "postgrest",
"supabase": "supabase",
"utils": "supabase_utils",
}
DEPENDENTS = {
"utils": set(PACKAGES),
"functions": {"supabase"},
"realtime": {"supabase"},
"storage": {"supabase"},
"auth": {"supabase"},
"postgrest": {"supabase"},
"supabase": set(),
}
IGNORED_PATTERNS = [
"*.md",
".devcontainer/*",
".github/*",
"LICENSE",
"sdk-compliance.yaml",
"release-please-config.json",
".release-please-manifest.json",
]
ALWAYS_TEST_ALL_PATTERNS = [".github/workflows/ci.yml", ".github/scripts/ci_matrix.py"]


def affected_packages(paths: list[str]) -> set[str]:
affected: set[str] = set()
for path in paths:
if any(fnmatch.fnmatch(path, pattern) for pattern in ALWAYS_TEST_ALL_PATTERNS):
return set(PACKAGES)
if any(fnmatch.fnmatch(path, pattern) for pattern in IGNORED_PATTERNS):
continue
parts = path.split("/")
if len(parts) > 2 and parts[0] == "src" and parts[1] in DEPENDENTS:
affected.add(parts[1])
affected |= DEPENDENTS[parts[1]]
else:
return set(PACKAGES)
return affected


def flag_name(entry: dict[str, str]) -> str:
return f"{entry['package']}-py{entry['python-version']}-{entry['resolution']}"


def matrix_entry(version: str, package: str, resolution: str) -> dict[str, str]:
return {
"python-version": version,
"package": package,
"import-name": IMPORT_NAMES[package],
"resolution": resolution,
}


def matrix_entries(packages: set[str]) -> list[dict[str, str]]:
ordered = [package for package in PACKAGES if package in packages]
entries = [
matrix_entry(version, package, "highest")
for version in PYTHON_VERSIONS
for package in ordered
]
entries += [
matrix_entry(LOWEST_PYTHON_VERSION, package, "lowest-direct")
for package in ordered
]
return entries


def main() -> None:
if "--all" in sys.argv[1:]:
packages = set(PACKAGES)
else:
packages = affected_packages(
[line.strip() for line in sys.stdin if line.strip()]
)
entries = matrix_entries(packages)
coverage_flags = [
flag_name(entry)
for entry in matrix_entries(set(PACKAGES))
if entry["package"] not in PACKAGES_WITHOUT_COVERAGE
]
print(f"matrix={json.dumps({'include': entries})}")
print(f"has-tests={'true' if entries else 'false'}")
print(f"coverage-flags={','.join(coverage_flags)}")


if __name__ == "__main__":
main()
130 changes: 97 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,72 +19,118 @@ concurrency:
cancel-in-progress: true

jobs:
changes:
name: Select packages to test
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
has-tests: ${{ steps.matrix.outputs.has-tests }}
coverage-flags: ${{ steps.matrix.outputs.coverage-flags }}
steps:
- name: Clone Repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 1 }}

# A pull request checks out a merge commit, so its first parent is the
# base branch and the diff is exactly what the pull request changes.
- name: Build test matrix
id: matrix
run: |
if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
git diff --name-only --no-renames HEAD^1 HEAD | python3 .github/scripts/ci_matrix.py >> "$GITHUB_OUTPUT"
else
python3 .github/scripts/ci_matrix.py --all >> "$GITHUB_OUTPUT"
fi
cat "$GITHUB_OUTPUT"
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Clone Repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
python-version: "3.11"

- name: Check lint
run: uv run --frozen ruff check --output-format=github

- name: Check formatting
run: uv run --frozen ruff format --check

test:
name: py${{ matrix.python-version }} | ${{ matrix.package }} @ ${{ matrix.os }}${{ matrix.resolution == 'lowest-direct' && ' | lowest' || '' }}
name: py${{ matrix.python-version }} | ${{ matrix.package }}${{ matrix.resolution == 'lowest-direct' && ' | lowest' || '' }}
needs: changes
if: ${{ needs.changes.outputs.has-tests == 'true' }}
strategy:
# Don't let one broken package cancel the other packages' jobs.
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.11", "3.12", "3.13", "3.14"]
package: ["functions", "realtime", "storage", "auth", "postgrest", "supabase", "utils"]
resolution: ["highest"]
include:
- { os: ubuntu-latest, python-version: "3.11", package: functions, resolution: lowest-direct }
- { os: ubuntu-latest, python-version: "3.11", package: realtime, resolution: lowest-direct }
- { os: ubuntu-latest, python-version: "3.11", package: storage, resolution: lowest-direct }
- { os: ubuntu-latest, python-version: "3.11", package: auth, resolution: lowest-direct }
- { os: ubuntu-latest, python-version: "3.11", package: postgrest, resolution: lowest-direct }
- { os: ubuntu-latest, python-version: "3.11", package: supabase, resolution: lowest-direct }
- { os: ubuntu-latest, python-version: "3.11", package: utils, resolution: lowest-direct }
runs-on: ${{ matrix.os }}
matrix: ${{ fromJSON(needs.changes.outputs.matrix) }}
runs-on: ubuntu-latest
timeout-minutes: 20
env:
UV_RESOLUTION: ${{ matrix.resolution }}
USES_SUPABASE_CLI: ${{ contains(fromJSON('["realtime", "storage"]'), matrix.package) }}
USES_INFRA: ${{ contains(fromJSON('["realtime", "storage", "auth", "postgrest"]'), matrix.package) }}
steps:
- name: Clone Repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Install supabase cli latest
- name: Install supabase cli
if: ${{ env.USES_SUPABASE_CLI == 'true' }}
uses: supabase/setup-cli@d239d1052f6f024a8d746d7e5aeddead63b9cb53 # v3.1.0
with:
version: "latest"
version: latest

# The image versions come from the cli, so a new cli release must not
# reuse images cached by an older one.
- name: Print supabase cli version
if: ${{ env.USES_SUPABASE_CLI == 'true' }}
id: supabase-cli
run: echo "version=$(supabase --version | head -n 1)" >> "$GITHUB_OUTPUT"

- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
version: "0.8.2"
python-version: ${{ matrix.python-version }}

# The supabase cli publishes host ports inside the ephemeral range, and
# parallel image pulls can take one as an outgoing source port, which
# fails the container start with "address already in use".
- name: Reserve infra ports
if: ${{ env.USES_SUPABASE_CLI == 'true' }}
run: sudo sysctl -w net.ipv4.ip_local_reserved_ports=54320-54329,55320-55329

- name: Print supabase cli version
id: supabase-cli
run: echo "version=$(supabase --version | head -n 1)" >> "$GITHUB_OUTPUT"

# Pulling images from the registries on every job gets throttled
# (429 / "Data limit exceeded"), so reuse the images between runs.
- name: Cache docker images
if: ${{ contains(fromJSON('["realtime", "storage", "auth", "postgrest"]'), matrix.package) }}
if: ${{ env.USES_INFRA == 'true' }}
id: docker-images
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: /tmp/docker-images
key: docker-images-${{ matrix.package }}-${{ steps.supabase-cli.outputs.version }}-${{ hashFiles(format('src/{0}/infra/**', matrix.package)) }}
key: docker-images-${{ matrix.package }}-${{ steps.supabase-cli.outputs.version || 'compose' }}-${{ hashFiles(format('src/{0}/infra/**', matrix.package)) }}

- name: Load cached docker images
if: ${{ steps.docker-images.outputs.cache-hit == 'true' }}
run: zstd -d --stdout /tmp/docker-images/images.tar.zst | docker image load

- name: Install dependencies
run: uv sync --package ${{ matrix.import-name }}

# Type checking runs while the infra starts, since pytest depends on
# start-infra but not on pyrefly. The environment is already synced, so
# the parallel uv runs only read it.
- name: Run Tests
run: make ${{ matrix.package }}.tests
env:
UV_RESOLUTION: ${{ matrix.resolution }}
run: make -j2 --output-sync=target ${{ matrix.package }}.tests

# On a cache miss cache-hit is an empty string, not 'false'.
- name: Save docker images to cache
if: ${{ steps.docker-images.outputs.cache-hit == 'false' }}
if: ${{ env.USES_INFRA == 'true' && steps.docker-images.outputs.cache-hit != 'true' }}
run: |
mkdir -p /tmp/docker-images
docker ps --all --format '{{.Image}}' | sort -u \
Expand All @@ -97,25 +143,44 @@ jobs:
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
flag-name: run-${{ join(matrix.*, '-') }}
flag-name: ${{ matrix.package }}-py${{ matrix.python-version }}-${{ matrix.resolution }}
parallel: true
fail-on-error: false

finish_tests:
needs: test
needs: [changes, test]
name: Upload tests coveralls results
if: ${{ always() }}
if: ${{ always() && needs.changes.outputs.has-tests == 'true' }}
runs-on: ubuntu-latest
steps:
# Packages a pull request does not touch are not tested, so carry their
# coverage forward from the base build.
- name: Coveralls Finished
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
carryforward: ${{ needs.changes.outputs.coverage-flags }}
fail-on-error: false

ci-passed:
name: CI passed
needs: [changes, lint, test]
if: ${{ always() }}
runs-on: ubuntu-latest
steps:
- name: Check job results
env:
CHANGES: ${{ needs.changes.result }}
LINT: ${{ needs.lint.result }}
TEST: ${{ needs.test.result }}
run: |
echo "changes=$CHANGES lint=$LINT test=$TEST"
[ "$CHANGES" = success ] && [ "$LINT" = success ] \
&& { [ "$TEST" = success ] || [ "$TEST" = skipped ]; }
release-please:
needs: test
needs: [lint, test]
if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' && github.repository_owner == 'supabase' }}
runs-on: ubuntu-latest
name: "Run release-please"
Expand Down Expand Up @@ -160,7 +225,6 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
version: "0.8.2"
python-version: "3.11"

- name: Build all packages and publish
Expand Down
10 changes: 5 additions & 5 deletions src/realtime/infra/supabase/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ enabled = true
# max_header_length = 4096

[studio]
enabled = true
enabled = false
# Port to use for Supabase Studio.
port = 54323
# External URL of the API server that frontend connects to.
Expand All @@ -60,15 +60,15 @@ openai_api_key = "env(OPENAI_API_KEY)"
# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they
# are monitored, and you can view the emails that would have been sent from the web interface.
[local_smtp]
enabled = true
enabled = false
# Port to use for the email testing server web interface.
port = 54324
# Uncomment to expose additional ports for testing user applications that send emails.
# smtp_port = 54325
# pop3_port = 54326

[storage]
enabled = true
enabled = false
# The maximum file size allowed (e.g. "5MB", "500KB").
file_size_limit = "50MiB"

Expand Down Expand Up @@ -180,14 +180,14 @@ url = ""
skip_nonce_check = false

[edge_runtime]
enabled = true
enabled = false
# Configure one of the supported request policies: `oneshot`, `per_worker`.
# Use `oneshot` for hot reload, or `per_worker` for load testing.
policy = "oneshot"
inspector_port = 8083

[analytics]
enabled = true
enabled = false
port = 54327
# Configure one of the supported backends: `postgres`, `bigquery`.
backend = "postgres"
Expand Down
2 changes: 1 addition & 1 deletion src/storage/infra/supabase/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ file_size_limit = "50MiB"
enabled = true

[auth]
enabled = true
enabled = false
# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used
# in emails.
site_url = "http://127.0.0.1:3000"
Expand Down
Loading