Fix Python nested model serialization #684
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: Validation | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| on: [pull_request] | |
| jobs: | |
| generate-and-build: | |
| name: ${{ matrix.sdk }} (${{ matrix.platform }}) | |
| runs-on: ubuntu-latest | |
| env: | |
| # Bun 1.3.12 has a sig_size calculation bug in macho.zig that truncates the | |
| # LC_CODE_SIGNATURE blob on cross-compiled Darwin binaries (oven-sh/bun#29120). | |
| # Unpin once a Bun release includes the upstream fix (oven-sh/bun#29122). | |
| CLI_BUN_VERSION: '1.3.11' | |
| FLUTTER_VERSION: '3.35.7' | |
| OSV_SCANNER_VERSION: 'v2.3.1' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Client SDKs | |
| - sdk: web | |
| platform: client | |
| - sdk: flutter | |
| platform: client | |
| - sdk: apple | |
| platform: client | |
| - sdk: android | |
| platform: client | |
| - sdk: react-native | |
| platform: client | |
| # Server SDKs | |
| - sdk: node | |
| platform: server | |
| - sdk: php | |
| platform: server | |
| - sdk: python | |
| platform: server | |
| - sdk: ruby | |
| platform: server | |
| - sdk: dart | |
| platform: server | |
| - sdk: go | |
| platform: server | |
| - sdk: swift | |
| platform: server | |
| - sdk: dotnet | |
| platform: server | |
| - sdk: kotlin | |
| platform: server | |
| - sdk: rust | |
| platform: server | |
| # Tooling targets | |
| - sdk: zed-extension | |
| platform: console | |
| # Console SDKs | |
| - sdk: cli | |
| platform: console | |
| - sdk: go-cli | |
| platform: console | |
| - sdk: web | |
| platform: console | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| submodules: recursive | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 | |
| with: | |
| php-version: '8.5' | |
| extensions: curl | |
| - name: Install Composer Dependencies | |
| run: composer install | |
| - name: Generate SDK for ${{ matrix.sdk }} | |
| run: php example.php ${{ matrix.sdk }} ${{ matrix.platform }} | |
| - name: Verify generated SDK is idempotent | |
| if: matrix.sdk == 'zed-extension' || matrix.sdk == 'go-cli' | |
| run: | | |
| checksums() { | |
| find examples/${{ matrix.sdk }} -type f -print0 \ | |
| | sort -z \ | |
| | xargs -0 sha256sum | |
| } | |
| checksums > /tmp/${{ matrix.sdk }}-checksums | |
| php example.php ${{ matrix.sdk }} ${{ matrix.platform }} | |
| checksums | diff -u /tmp/${{ matrix.sdk }}-checksums - | |
| # Language-specific setup | |
| - name: Setup Node.js | |
| if: matrix.sdk == 'web' || matrix.sdk == 'node' || matrix.sdk == 'react-native' || matrix.sdk == 'cli' | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '22' | |
| - name: Setup Bun | |
| if: matrix.sdk == 'cli' | |
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: ${{ env.CLI_BUN_VERSION }} | |
| - name: Cache Flutter SDK | |
| if: matrix.sdk == 'flutter' | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ${{ runner.tool_cache }}/flutter | |
| key: ${{ runner.os }}-flutter-${{ env.FLUTTER_VERSION }} | |
| - name: Setup Flutter | |
| if: matrix.sdk == 'flutter' | |
| run: | | |
| if [ ! -d "$RUNNER_TOOL_CACHE/flutter/bin" ]; then | |
| git clone --depth 1 --branch "$FLUTTER_VERSION" https://github.com/flutter/flutter.git "$RUNNER_TOOL_CACHE/flutter" | |
| fi | |
| echo "$RUNNER_TOOL_CACHE/flutter/bin" >> "$GITHUB_PATH" | |
| "$RUNNER_TOOL_CACHE/flutter/bin/flutter" --version | |
| - name: Setup Swift | |
| if: matrix.sdk == 'apple' || matrix.sdk == 'swift' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y wget | |
| wget https://download.swift.org/swift-6.1.3-release/ubuntu2204/swift-6.1.3-RELEASE/swift-6.1.3-RELEASE-ubuntu22.04.tar.gz | |
| tar xzf swift-6.1.3-RELEASE-ubuntu22.04.tar.gz | |
| sudo mv swift-6.1.3-RELEASE-ubuntu22.04 /usr/share/swift | |
| echo "/usr/share/swift/usr/bin" >> $GITHUB_PATH | |
| - name: Setup Java | |
| if: matrix.sdk == 'android' || matrix.sdk == 'kotlin' | |
| uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '17' | |
| - name: Setup Python | |
| if: matrix.sdk == 'python' | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.11' | |
| - name: Setup Ruby | |
| if: matrix.sdk == 'ruby' | |
| uses: ruby/setup-ruby@a30dfa457ad68707b8b910ac3a244714b61c0626 # v1.320.0 | |
| with: | |
| ruby-version: '3.1' | |
| - name: Setup Dart | |
| if: matrix.sdk == 'dart' | |
| uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2 | |
| with: | |
| sdk: 'stable' | |
| - name: Setup Go | |
| if: matrix.sdk == 'go' || matrix.sdk == 'go-cli' || matrix.sdk == 'dart' || matrix.sdk == 'flutter' | |
| uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| go-version: '1.26.5' | |
| - name: Install OSV Scanner | |
| if: matrix.sdk == 'dart' || matrix.sdk == 'flutter' | |
| run: go install github.com/google/osv-scanner/v2/cmd/osv-scanner@${{ env.OSV_SCANNER_VERSION }} | |
| - name: Setup .NET | |
| if: matrix.sdk == 'dotnet' | |
| uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 | |
| with: | |
| dotnet-version: '8.0.x' | |
| - name: Setup Rust | |
| if: matrix.sdk == 'rust' | |
| uses: dtolnay/rust-toolchain@bd41891a8e7f4b8649f6d684415e1a6155fe4e22 # 1.83.0 | |
| - name: Setup Rust for Zed extension | |
| if: matrix.sdk == 'zed-extension' | |
| run: rustup toolchain install 1.86.0 --profile minimal --no-self-update --component rustfmt --target wasm32-wasip1 | |
| - name: Setup Rust for cargo-audit | |
| if: matrix.sdk == 'rust' | |
| run: rustup toolchain install 1.85.0 --profile minimal --no-self-update | |
| - name: Cache cargo-audit | |
| if: matrix.sdk == 'rust' | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: | | |
| ~/.cargo/bin/cargo-audit | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| key: ${{ runner.os }}-cargo-audit-0.22.1 | |
| - name: Build SDK | |
| working-directory: examples/${{ matrix.sdk }} | |
| run: | | |
| case "${{ matrix.sdk }}" in | |
| web) | |
| npm ci | |
| npm audit | |
| npm run build | |
| ;; | |
| cli) | |
| bun install | |
| bun run linux-x64 | |
| bun run linux-arm64 | |
| bun run mac-x64 | |
| bun run mac-arm64 | |
| bun run windows-x64 | |
| bun run windows-arm64 | |
| npm ci | |
| npm audit | |
| npm run build | |
| node dist/cli.cjs --help | |
| ;; | |
| react-native) | |
| npm ci --omit=peer | |
| npm audit --omit=peer | |
| npm run build | |
| ;; | |
| flutter) | |
| flutter pub get | |
| osv-scanner scan source --lockfile=pubspec.lock | |
| dart analyze --no-fatal-warnings | |
| flutter test | |
| ;; | |
| apple|swift) | |
| swift build | |
| ;; | |
| android) | |
| chmod +x ./gradlew || true | |
| ./gradlew build -x lint | |
| ;; | |
| kotlin) | |
| chmod +x ./gradlew || true | |
| ./gradlew build | |
| ;; | |
| php) | |
| composer install | |
| composer audit | |
| composer test | |
| ;; | |
| python) | |
| python -m pip install pip-audit | |
| pip install -e .[test] | |
| pip-audit --strict . | |
| python -m compileall appwrite/ | |
| python -m unittest | |
| ;; | |
| ruby) | |
| bundle install | |
| gem install bundler-audit | |
| bundle-audit update | |
| bundle-audit check --no-update | |
| ;; | |
| node) | |
| npm ci | |
| npm audit | |
| npm run build | |
| npm run test | |
| ;; | |
| dart) | |
| dart pub get | |
| osv-scanner scan source --lockfile=pubspec.lock | |
| dart analyze --no-fatal-warnings | |
| ;; | |
| go) | |
| go mod tidy || true | |
| go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 | |
| govulncheck ./... | |
| go build ./... | |
| go test ./... | |
| ;; | |
| go-cli) | |
| go mod tidy | |
| unformatted=$(gofmt -l .) | |
| if [ -n "$unformatted" ]; then | |
| echo "Not gofmt-clean:" | |
| echo "$unformatted" | |
| exit 1 | |
| fi | |
| go build ./... | |
| go vet ./... | |
| go test -race ./... | |
| go build -o appwrite . | |
| ;; | |
| dotnet) | |
| dotnet restore | |
| dotnet build --no-restore | |
| dotnet test --no-build | |
| ;; | |
| rust) | |
| cargo generate-lockfile | |
| if ! command -v cargo-audit >/dev/null; then | |
| cargo +1.85.0 install cargo-audit --version 0.22.1 --locked | |
| fi | |
| cargo audit | |
| cargo +1.83.0 build --release | |
| cargo +1.83.0 test --lib | |
| ;; | |
| zed-extension) | |
| cargo +1.86.0 fmt --check | |
| cargo +1.86.0 check --target wasm32-wasip1 | |
| ;; | |
| *) | |
| echo "Unknown SDK: ${{ matrix.sdk }}" | |
| exit 1 | |
| ;; | |
| esac | |
| - name: Verify CLI Darwin binaries include valid embedded code signatures | |
| if: matrix.sdk == 'cli' | |
| working-directory: examples/cli | |
| run: python3 ../../scripts/verify-darwin-signatures.py | |
| - name: Test compiled CLI binary | |
| if: matrix.sdk == 'cli' | |
| working-directory: examples/cli | |
| run: | | |
| BIN=build/appwrite-cli-linux-x64 | |
| "./$BIN" --version | |
| # The root screen is grouped by intent, so assert a section renders | |
| # rather than just that some help was produced. | |
| "./$BIN" --help | grep -q "USAGE" | |
| "./$BIN" --help | grep -q "GET STARTED" | |
| SHELL=bash "./$BIN" completion bash >/dev/null | |
| - name: Test compiled Go CLI binary | |
| if: matrix.sdk == 'go-cli' | |
| working-directory: examples/go-cli | |
| run: | | |
| ./appwrite --version | |
| ./appwrite --help | grep -q "USAGE" | |
| ./appwrite --help | grep -q "GET STARTED" | |
| SHELL=bash ./appwrite completion bash >/dev/null | |
| - name: Verify npm package entry points | |
| if: matrix.sdk == 'web' || matrix.sdk == 'node' || matrix.sdk == 'react-native' || matrix.sdk == 'cli' | |
| working-directory: examples/${{ matrix.sdk }} | |
| run: | | |
| echo "::group::Checking package.json has files field" | |
| if ! node -e "const pkg = require('./package.json'); if (!pkg.files) { console.error('ERROR: package.json is missing \"files\" field — dist/ will be excluded from npm publish'); process.exit(1); }"; then | |
| exit 1 | |
| fi | |
| echo "files field present" | |
| echo "::endgroup::" | |
| echo "::group::Running npm pack --dry-run" | |
| PACK_OUTPUT=$(npm pack --dry-run 2>&1) | |
| echo "$PACK_OUTPUT" | |
| echo "::endgroup::" | |
| echo "::group::Verifying declared entry points are in the tarball" | |
| # Collect all entry point paths from package.json exports, main, and module | |
| ENTRY_POINTS=$(node -e " | |
| const pkg = require('./package.json'); | |
| const paths = new Set(); | |
| if (pkg.main) paths.add(pkg.main); | |
| if (pkg.module) paths.add(pkg.module); | |
| function walk(obj) { | |
| if (typeof obj === 'string') { | |
| if (obj.endsWith('.js') || obj.endsWith('.cjs') || obj.endsWith('.mjs')) { | |
| paths.add(obj.replace(/^\.\//, '')); | |
| } | |
| } else if (obj && typeof obj === 'object') { | |
| Object.values(obj).forEach(walk); | |
| } | |
| } | |
| if (pkg.exports) walk(pkg.exports); | |
| if (pkg.bin) walk(pkg.bin); | |
| paths.forEach(p => console.log(p)); | |
| ") | |
| MISSING=0 | |
| for entry in $ENTRY_POINTS; do | |
| if echo "$PACK_OUTPUT" | grep -qF "$entry"; then | |
| echo "✓ $entry is included" | |
| else | |
| echo "✗ $entry is MISSING from package — will break consumers" | |
| MISSING=1 | |
| fi | |
| done | |
| if [ "$MISSING" -eq 1 ]; then | |
| echo "::error::One or more entry points declared in package.json are not included in the npm package. Check the \"files\" field." | |
| exit 1 | |
| fi | |
| echo "All entry points verified." | |
| echo "::endgroup::" | |
| validate-tooling-plugins: | |
| name: Tooling plugins | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 | |
| with: | |
| php-version: '8.5' | |
| extensions: curl | |
| - name: Setup Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Composer Dependencies | |
| run: composer install | |
| - name: Regenerate tooling plugins | |
| run: | | |
| php example.php cursor-plugin | |
| php example.php claude-plugin | |
| php example.php codex-plugin | |
| - name: Verify tooling plugin generation is idempotent | |
| run: | | |
| find examples/cursor-plugin examples/claude-plugin examples/codex-plugin -type f -print0 \ | |
| | sort -z \ | |
| | xargs -0 sha256sum > /tmp/tooling-plugin-checksums | |
| php example.php cursor-plugin | |
| php example.php claude-plugin | |
| php example.php codex-plugin | |
| find examples/cursor-plugin examples/claude-plugin examples/codex-plugin -type f -print0 \ | |
| | sort -z \ | |
| | xargs -0 sha256sum \ | |
| | diff -u /tmp/tooling-plugin-checksums - | |
| - name: Validate hosted MCP configurations | |
| run: | | |
| python3 <<'PY' | |
| import json | |
| import pathlib | |
| import tomllib | |
| root = pathlib.Path("examples") | |
| expected_url = "https://mcp.appwrite.io/" | |
| cursor = json.loads((root / "cursor-plugin/.mcp.json").read_text()) | |
| assert cursor["mcpServers"] == { | |
| "appwrite": {"url": expected_url}, | |
| } | |
| claude = json.loads((root / "claude-plugin/.mcp.json").read_text()) | |
| assert claude["mcpServers"] == { | |
| "appwrite": {"type": "http", "url": expected_url}, | |
| } | |
| claude_manifest = json.loads( | |
| (root / "claude-plugin/.claude-plugin/plugin.json").read_text() | |
| ) | |
| assert "userConfig" not in claude_manifest | |
| codex = json.loads( | |
| (root / "codex-plugin/plugins/appwrite/.mcp.json").read_text() | |
| ) | |
| assert codex["mcpServers"] == { | |
| "appwrite": {"type": "http", "url": expected_url}, | |
| } | |
| codex_config = tomllib.loads( | |
| (root / "codex-plugin/plugins/appwrite/config.toml").read_text() | |
| ) | |
| assert codex_config["mcp_servers"] == { | |
| "appwrite": {"url": expected_url}, | |
| } | |
| PY |