From bb9a714f571f5faa5205cc6ec2347bf16a5933a5 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 17 Aug 2026 10:00:57 +0530 Subject: [PATCH 1/3] chore: bound template dependency updates --- .github/scripts/check-dependency-ranges.py | 131 ++++++++++ .github/workflows/build.yml | 244 ++++++++++++++++++ CONTRIBUTING.md | 2 +- dart/starter/pubspec.yaml | 2 +- deno/starter/src/main.ts | 4 +- deno/whatsapp-with-vonage/src/main.ts | 4 +- dotnet/starter/StarterTemplate.csproj | 4 +- kotlin/sync-with-meilisearch/src/Main.kt | 2 +- .../discord-command-bot/src/commands/hello.ts | 2 +- .../discord-command-bot/src/main.ts | 9 +- .../discord-command-bot/tsconfig.json | 14 + .../sync-with-meilisearch/package-lock.json | 2 +- .../sync-with-meilisearch/package.json | 2 +- .../sync-with-qdrant/package-lock.json | 2 +- node-typescript/sync-with-qdrant/package.json | 2 +- .../sync-with-qdrant/src/appwrite.ts | 2 +- .../generate_with_tensorflow/requirements.txt | 4 +- python-ml/starter/requirements.txt | 4 +- python/censor_with_redact/requirements.txt | 2 +- python/discord_command_bot/requirements.txt | 4 +- python/email_contact_form/requirements.txt | 1 + python/mcp-server/requirements.txt | 2 +- python/prompt_chatgpt/requirements.txt | 2 +- python/starter/requirements.txt | 2 +- python/storage-cleaner/requirements.txt | 2 +- python/sync_with_algolia/requirements.txt | 4 +- python/sync_with_meilisearch/requirements.txt | 4 +- python/sync_with_qdrant/requirements.txt | 6 +- python/whatsapp_with_vonage/requirements.txt | 4 +- ruby/starter/Gemfile | 2 +- ruby/sync_with_meilisearch/Gemfile | 4 +- ruby/whatsapp-with-vonage/Gemfile | 10 +- swift/starter/Package.swift | 2 +- swift/starter/README.md | 2 +- 34 files changed, 443 insertions(+), 46 deletions(-) create mode 100644 .github/scripts/check-dependency-ranges.py create mode 100644 .github/workflows/build.yml create mode 100644 node-typescript/discord-command-bot/tsconfig.json create mode 100644 python/email_contact_form/requirements.txt diff --git a/.github/scripts/check-dependency-ranges.py b/.github/scripts/check-dependency-ranges.py new file mode 100644 index 00000000..fdc75992 --- /dev/null +++ b/.github/scripts/check-dependency-ranges.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +"""Reject template dependencies that can silently cross a major-version boundary.""" + +from __future__ import annotations + +import json +import re +import sys +import tomllib +import xml.etree.ElementTree as ET +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +SEMVER = r"\d+(?:\.\d+){1,2}(?:[-+][0-9A-Za-z.-]+)?" +errors: list[str] = [] + + +def fail(path: Path, dependency: str, constraint: str) -> None: + errors.append(f"{path.relative_to(ROOT)}: {dependency} has an unsafe constraint: {constraint}") + + +def version_tuple(version: str) -> tuple[int, int, int]: + parts = version.split("-", 1)[0].split("+", 1)[0].split(".") + return tuple(int(part) for part in (parts + ["0", "0"])[:3]) + + +def caret_upper_bound(version: str) -> tuple[int, int, int]: + major, minor, patch = version_tuple(version) + if major: + return major + 1, 0, 0 + if minor: + return 0, minor + 1, 0 + return 0, 0, patch + 1 + + +# npm and Bun use caret ranges. Their committed lockfiles keep current installs reproducible. +for path in sorted([*ROOT.glob("node*/*/package.json"), *ROOT.glob("bun/*/package.json")]): + manifest = json.loads(path.read_text()) + lock = path.with_name("bun.lock" if path.parts[-3] == "bun" else "package-lock.json") + if not lock.exists(): + errors.append(f"{path.relative_to(ROOT)}: missing {lock.name}") + for section in ("dependencies", "devDependencies", "optionalDependencies"): + for name, constraint in manifest.get(section, {}).items(): + if not re.fullmatch(rf"\^{SEMVER}", constraint): + fail(path, name, constraint) + +# Pub caret constraints stay below the next breaking version according to Dart semver rules. +for path in sorted(ROOT.glob("dart/*/pubspec.yaml")): + section = None + for line in path.read_text().splitlines(): + if line in ("dependencies:", "dev_dependencies:"): + section = line[:-1] + continue + if line and not line.startswith(" "): + section = None + if section and (match := re.fullmatch(rf" ([\w-]+): (\^{SEMVER})", line)) is None and line.strip(): + fail(path, line.strip().split(":", 1)[0], line.strip()) + +# Python has no caret operator, so require both a lower bound and an exclusive upper bound. +for path in sorted([*ROOT.glob("python/*/requirements.txt"), *ROOT.glob("python-ml/*/requirements.txt")]): + for line in path.read_text().splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + match = re.fullmatch(rf"([A-Za-z0-9_.-]+)>=({SEMVER}),<({SEMVER})", line) + if not match or version_tuple(match.group(3)) != caret_upper_bound(match.group(2)): + fail(path, line.split("=", 1)[0], line) + +# Composer's caret and Ruby's explicit upper bounds permit compatible updates only. +for path in sorted(ROOT.glob("php/*/composer.json")): + for name, constraint in json.loads(path.read_text()).get("require", {}).items(): + if name == "php" or name.startswith("ext-"): + continue + if not re.fullmatch(rf"\^{SEMVER}", constraint): + fail(path, name, constraint) + +for path in sorted(ROOT.glob("ruby/*/Gemfile")): + for line in path.read_text().splitlines(): + if not line.strip().startswith("gem "): + continue + match = re.match(r"gem ['\"]([^'\"]+)['\"](.*)", line.strip()) + versions = re.findall(SEMVER, match.group(2)) if match else [] + if not match or len(versions) != 2 or version_tuple(versions[1]) != caret_upper_bound(versions[0]): + fail(path, match.group(1) if match else "gem", line.strip()) + +# Cargo's plain versions are caret requirements. Reject wildcards and unbounded inequalities. +for path in sorted(ROOT.glob("rust/*/Cargo.toml")): + dependencies = tomllib.loads(path.read_text()).get("dependencies", {}) + for name, value in dependencies.items(): + constraint = value if isinstance(value, str) else value.get("version", "") + if not re.fullmatch(SEMVER, constraint): + fail(path, name, constraint) + +# NuGet ranges must have an upper bound; an exact version is also safe. +for path in sorted(ROOT.glob("dotnet/*/*.csproj")): + for package in ET.parse(path).iterfind(".//PackageReference"): + constraint = package.get("Version", "") + exact = re.fullmatch(SEMVER, constraint) + bounded = re.fullmatch(rf"[\[(]({SEMVER}),({SEMVER})[\])]", constraint) + if not exact and (not bounded or version_tuple(bounded.group(2)) != caret_upper_bound(bounded.group(1))): + fail(path, package.get("Include", "package"), constraint) + +# Maven coordinates, Go modules, and URL imports have no portable caret syntax here. Require a +# concrete version; Go major versions are additionally encoded in v2+ module paths. +for path in sorted(ROOT.glob("*/*/deps.gradle")): + for group, artifact, version in re.findall(r"['\"]([^:'\"]+):([^:'\"]+):([^'\"]+)['\"]", path.read_text()): + if not re.fullmatch(SEMVER, version): + fail(path, f"{group}:{artifact}", version) + +for path in sorted(ROOT.glob("go/*/go.mod")): + for module, version in re.findall(r"^\s*([^\s]+)\s+(v[^\s]+)$", path.read_text(), re.MULTILINE): + if not re.fullmatch(rf"v{SEMVER}", version): + fail(path, module, version) + +for path in sorted(ROOT.glob("deno/*/src/*")): + if path.suffix != ".ts": + continue + for url in re.findall(r"from\s+[\"'](https://[^\"']+)", path.read_text()): + if "@" not in url: + fail(path, url, "missing URL version") + +for path in sorted(ROOT.glob("swift/*/Package.swift")): + for declaration in re.findall(r"\.package\([^\n]+", path.read_text()): + if ".upToNextMajor(" not in declaration and ".exact(" not in declaration and "revision:" not in declaration: + fail(path, "Swift package", declaration) + +if errors: + print("\n".join(errors), file=sys.stderr) + sys.exit(1) + +print("All template dependencies are bounded below the next breaking version.") diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..d0fdafd3 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,244 @@ +name: Template builds + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + pull_request: + schedule: + # Compatible dependency updates are intentional; verify them weekly. + - cron: "0 7 * * 1" + workflow_dispatch: + +permissions: + contents: read + +jobs: + dependency-ranges: + name: dependency ranges + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.11" + - run: python .github/scripts/check-dependency-ranges.py + + node: + name: Node.js and TypeScript + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: "22" + - name: Build templates + run: | + set -euo pipefail + for dir in node/*/ node-typescript/*/; do + [ -f "${dir}package.json" ] || continue + echo "::group::${dir%/}" + npm ci --prefix "$dir" --ignore-scripts --no-audit --no-fund + if [ -f "${dir}tsconfig.json" ]; then + npm run --prefix "$dir" build + else + find "${dir}src" -type f -name '*.js' -exec node --check {} \; + fi + echo "::endgroup::" + done + + bun: + name: Bun + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: latest + - name: Build templates + run: | + set -euo pipefail + for dir in bun/*/; do + [ -f "${dir}package.json" ] || continue + echo "::group::${dir%/}" + (cd "$dir" && bun install --frozen-lockfile --ignore-scripts && bun -e "await import('./src/main.ts')") + echo "::endgroup::" + done + + python: + name: Python ${{ matrix.name }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: "3.9" + version: "3.9" + templates: "python" + - name: "3.12 MCP" + version: "3.12" + templates: "mcp" + - name: "3.11 ML" + version: "3.11" + templates: "python-ml" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: ${{ matrix.version }} + - name: Build templates + env: + TEMPLATE_SET: ${{ matrix.templates }} + run: | + set -euo pipefail + if [ "$TEMPLATE_SET" = mcp ]; then + dirs=(python/mcp-server/) + elif [ "$TEMPLATE_SET" = python-ml ]; then + dirs=(python-ml/*/) + else + dirs=(python/*/) + fi + for dir in "${dirs[@]}"; do + [ -f "${dir}requirements.txt" ] || continue + if [ "$TEMPLATE_SET" = python ] && [ "$dir" = python/mcp-server/ ]; then continue; fi + echo "::group::${dir%/}" + rm -rf /tmp/template-venv + python -m venv /tmp/template-venv + /tmp/template-venv/bin/pip install --disable-pip-version-check -r "${dir}requirements.txt" + /tmp/template-venv/bin/pip check + /tmp/template-venv/bin/python -m compileall -q "${dir}src" + echo "::endgroup::" + done + + php: + name: PHP + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: "8.3" + tools: composer + - name: Build templates + run: | + set -euo pipefail + for dir in php/*/; do + [ -f "${dir}composer.json" ] || continue + echo "::group::${dir%/}" + composer install --working-dir="$dir" --no-interaction --no-progress + find "${dir}src" -type f -name '*.php' -exec php -l {} \; + echo "::endgroup::" + done + + ruby: + name: Ruby + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: ruby/setup-ruby@a30dfa457ad68707b8b910ac3a244714b61c0626 # v1.320.0 + with: + ruby-version: "3.1" + bundler: latest + - name: Build templates + run: | + set -euo pipefail + for dir in ruby/*/; do + [ -f "${dir}Gemfile" ] || continue + echo "::group::${dir%/}" + (cd "$dir" && bundle install && find lib -type f -name '*.rb' -exec ruby -c {} \;) + echo "::endgroup::" + done + + dart: + name: Dart + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2 + with: + sdk: stable + - name: Build templates + run: | + set -euo pipefail + for dir in dart/*/; do + [ -f "${dir}pubspec.yaml" ] || continue + echo "::group::${dir%/}" + (cd "$dir" && dart pub get && dart analyze) + echo "::endgroup::" + done + + deno: + name: Deno + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2 + with: + deno-version: v2.x + - name: Build templates + run: | + set -euo pipefail + for dir in deno/*/; do + echo "::group::${dir%/}" + (cd "$dir" && deno check src/main.ts) + echo "::endgroup::" + done + + go: + name: Go + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: "1.26.6" + cache: false + - run: for dir in go/*/; do [ ! -f "${dir}go.mod" ] || (cd "$dir" && go build ./...); done + + rust: + name: Rust + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@bd41891a8e7f4b8649f6d684415e1a6155fe4e22 # 1.83.0 + - run: for dir in rust/*/; do [ ! -f "${dir}Cargo.toml" ] || (cd "$dir" && cargo build --locked); done + + runtime-builds: + name: ${{ matrix.template }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - template: cpp/starter + image: openruntimes/cpp:v4-17 + entrypoint: src/main.cc + - template: dotnet/starter + image: openruntimes/dotnet:v4-6.0 + entrypoint: src/Index.cs + - template: java/starter + image: openruntimes/java:v4-17.0 + entrypoint: src/Main.java + - template: kotlin/starter + image: openruntimes/kotlin:v4-1.8 + entrypoint: src/Main.kt + - template: kotlin/sync-with-meilisearch + image: openruntimes/kotlin:v4-1.8 + entrypoint: src/Main.kt + - template: swift/starter + image: openruntimes/swift:v5-6.2 + entrypoint: Sources/index.swift + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Build in Open Runtimes + env: + IMAGE: ${{ matrix.image }} + ENTRYPOINT: ${{ matrix.entrypoint }} + TEMPLATE: ${{ matrix.template }} + run: | + docker run --rm \ + -e OPEN_RUNTIMES_ENTRYPOINT="$ENTRYPOINT" \ + -v "$PWD/$TEMPLATE:/mnt/code" \ + "$IMAGE" sh helpers/build.sh + rm -f "$TEMPLATE/code.tar.gz" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf866f2a..f31735ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,7 +86,7 @@ Security and privacy are extremely important to Appwrite, developers, and users ## Dependencies -Usage of dependencies is welcomed for purpose of simplifying template code. Please only use libraries that are well-known, and popular. +Usage of dependencies is welcomed for purpose of simplifying template code. Please only use libraries that are well-known and popular. Dependency constraints must allow compatible patch and minor updates while excluding the next breaking version (for example, use caret ranges where the package manager supports them). Keep any committed lockfile in sync with its manifest. ## Introducing New Templates diff --git a/dart/starter/pubspec.yaml b/dart/starter/pubspec.yaml index a8d9a1b0..1d83d7e9 100644 --- a/dart/starter/pubspec.yaml +++ b/dart/starter/pubspec.yaml @@ -5,7 +5,7 @@ environment: sdk: ^2.17.0 dependencies: - dart_appwrite: 19.2.1 + dart_appwrite: ^19.2.1 dev_dependencies: lints: ^2.0.0 diff --git a/deno/starter/src/main.ts b/deno/starter/src/main.ts index 420d8b32..14917011 100644 --- a/deno/starter/src/main.ts +++ b/deno/starter/src/main.ts @@ -15,8 +15,8 @@ export default async ({ req, res, log, error }: any) => { // Log messages and errors to the Appwrite Console // These logs won't be seen by your end users log(`Total users: ${response.total}`); - } catch(err) { - error("Could not list users: " + err.message); + } catch (err) { + error("Could not list users: " + String(err)); } // The req object contains the request data diff --git a/deno/whatsapp-with-vonage/src/main.ts b/deno/whatsapp-with-vonage/src/main.ts index 0302884a..f4f077d2 100644 --- a/deno/whatsapp-with-vonage/src/main.ts +++ b/deno/whatsapp-with-vonage/src/main.ts @@ -35,7 +35,7 @@ export default async ({ req, res, log, error }: Context) => { try { throwIfMissing(payload, ["payload_hash"]); } catch (err) { - return res.json({ ok: false, error: err.message }, 400); + return res.json({ ok: false, error: String(err) }, 400); } const hash = crypto.subtle.digestSync( @@ -50,7 +50,7 @@ export default async ({ req, res, log, error }: Context) => { try { throwIfMissing(req.bodyJson, ["from", "text"]); } catch (err) { - return res.json({ ok: false, error: err.message }, 400); + return res.json({ ok: false, error: String(err) }, 400); } const basicAuthToken: string = btoa( diff --git a/dotnet/starter/StarterTemplate.csproj b/dotnet/starter/StarterTemplate.csproj index 7137895f..dc0a8e7e 100644 --- a/dotnet/starter/StarterTemplate.csproj +++ b/dotnet/starter/StarterTemplate.csproj @@ -6,6 +6,6 @@ enable - + - \ No newline at end of file + diff --git a/kotlin/sync-with-meilisearch/src/Main.kt b/kotlin/sync-with-meilisearch/src/Main.kt index 486656aa..b6521b76 100644 --- a/kotlin/sync-with-meilisearch/src/Main.kt +++ b/kotlin/sync-with-meilisearch/src/Main.kt @@ -36,7 +36,7 @@ class Main { val client = AppwriteClient().apply { setEndpoint(System.getenv("APPWRITE_FUNCTION_API_ENDPOINT")) setProject(System.getenv("APPWRITE_FUNCTION_PROJECT_ID")) - setKey(context.res.headers["x-appwrite-key"]) + setKey(context.req.headers["x-appwrite-key"] ?: "") } val databases = Databases(client) diff --git a/node-typescript/discord-command-bot/src/commands/hello.ts b/node-typescript/discord-command-bot/src/commands/hello.ts index 4c852415..d8a21441 100644 --- a/node-typescript/discord-command-bot/src/commands/hello.ts +++ b/node-typescript/discord-command-bot/src/commands/hello.ts @@ -1,6 +1,6 @@ import { InteractionResponseType } from 'discord-interactions'; -export default function HelloCommand(res) { +export default function HelloCommand(res: any) { return res.json( { type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, diff --git a/node-typescript/discord-command-bot/src/main.ts b/node-typescript/discord-command-bot/src/main.ts index 1cced355..e9082445 100644 --- a/node-typescript/discord-command-bot/src/main.ts +++ b/node-typescript/discord-command-bot/src/main.ts @@ -7,7 +7,14 @@ import { import { throwIfMissing } from './utils.js'; import commands from './commands/index.js'; -export default async ({ req, res, error, log }) => { +type Context = { + req: any; + res: any; + log: (message: string) => void; + error: (message: string) => void; +}; + +export default async ({ req, res, error, log }: Context) => { throwIfMissing(process.env, [ 'DISCORD_PUBLIC_KEY', 'DISCORD_APPLICATION_ID', diff --git a/node-typescript/discord-command-bot/tsconfig.json b/node-typescript/discord-command-bot/tsconfig.json new file mode 100644 index 00000000..c4f98f32 --- /dev/null +++ b/node-typescript/discord-command-bot/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "rootDir": "src", + "resolveJsonModule": true, + "outDir": "dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitAny": true, + "skipLibCheck": true + } +} diff --git a/node-typescript/sync-with-meilisearch/package-lock.json b/node-typescript/sync-with-meilisearch/package-lock.json index 68a1742e..00f64b8e 100644 --- a/node-typescript/sync-with-meilisearch/package-lock.json +++ b/node-typescript/sync-with-meilisearch/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "meilisearch": "^0.40.0", "node-appwrite": "^14.1.0", - "typescript": "5.4.5" + "typescript": "^5.4.5" }, "devDependencies": { "@types/node": "^20.12.12", diff --git a/node-typescript/sync-with-meilisearch/package.json b/node-typescript/sync-with-meilisearch/package.json index 9e6f3d05..15d9e4fd 100644 --- a/node-typescript/sync-with-meilisearch/package.json +++ b/node-typescript/sync-with-meilisearch/package.json @@ -12,7 +12,7 @@ "dependencies": { "meilisearch": "^0.40.0", "node-appwrite": "^14.1.0", - "typescript": "5.4.5" + "typescript": "^5.4.5" }, "devDependencies": { "prettier": "^3.2.5", diff --git a/node-typescript/sync-with-qdrant/package-lock.json b/node-typescript/sync-with-qdrant/package-lock.json index 4deccf7c..b6fadf17 100644 --- a/node-typescript/sync-with-qdrant/package-lock.json +++ b/node-typescript/sync-with-qdrant/package-lock.json @@ -11,7 +11,7 @@ "@qdrant/js-client-rest": "^1.9.0", "node-appwrite": "^14.1.0", "openai": "^4.47.1", - "typescript": "5.4.5" + "typescript": "^5.4.5" }, "devDependencies": { "@types/node": "^20.12.10", diff --git a/node-typescript/sync-with-qdrant/package.json b/node-typescript/sync-with-qdrant/package.json index 018b94e0..ed4c9bf2 100644 --- a/node-typescript/sync-with-qdrant/package.json +++ b/node-typescript/sync-with-qdrant/package.json @@ -13,7 +13,7 @@ "@qdrant/js-client-rest": "^1.9.0", "node-appwrite": "^14.1.0", "openai": "^4.47.1", - "typescript": "5.4.5" + "typescript": "^5.4.5" }, "devDependencies": { "@types/node": "^20.12.10", diff --git a/node-typescript/sync-with-qdrant/src/appwrite.ts b/node-typescript/sync-with-qdrant/src/appwrite.ts index d113caf2..0a1e0557 100644 --- a/node-typescript/sync-with-qdrant/src/appwrite.ts +++ b/node-typescript/sync-with-qdrant/src/appwrite.ts @@ -3,7 +3,7 @@ import { Client, Databases, Query } from 'node-appwrite'; class AppwriteService { databases: Databases; - constructor(apiKey) { + constructor(apiKey: string) { const client = new Client(); client .setEndpoint(process.env.APPWRITE_FUNCTION_API_ENDPOINT) diff --git a/python-ml/generate_with_tensorflow/requirements.txt b/python-ml/generate_with_tensorflow/requirements.txt index 95d00589..e5ca81ef 100644 --- a/python-ml/generate_with_tensorflow/requirements.txt +++ b/python-ml/generate_with_tensorflow/requirements.txt @@ -1,2 +1,2 @@ -tensorflow -numpy \ No newline at end of file +tensorflow>=2.15.0,<3.0.0 +numpy>=2.0.0,<3.0.0 diff --git a/python-ml/starter/requirements.txt b/python-ml/starter/requirements.txt index 37e088f6..3cc006dc 100644 --- a/python-ml/starter/requirements.txt +++ b/python-ml/starter/requirements.txt @@ -1,2 +1,2 @@ -appwrite==23.0.0 -numpy \ No newline at end of file +appwrite>=23.0.0,<24.0.0 +numpy>=2.0.0,<3.0.0 diff --git a/python/censor_with_redact/requirements.txt b/python/censor_with_redact/requirements.txt index 663bd1f6..d51edac5 100644 --- a/python/censor_with_redact/requirements.txt +++ b/python/censor_with_redact/requirements.txt @@ -1 +1 @@ -requests \ No newline at end of file +requests>=2.31.0,<3.0.0 diff --git a/python/discord_command_bot/requirements.txt b/python/discord_command_bot/requirements.txt index 319860cf..4000c0c0 100644 --- a/python/discord_command_bot/requirements.txt +++ b/python/discord_command_bot/requirements.txt @@ -1,2 +1,2 @@ -requests -discord-interactions \ No newline at end of file +requests>=2.31.0,<3.0.0 +discord-interactions>=0.4.0,<0.5.0 diff --git a/python/email_contact_form/requirements.txt b/python/email_contact_form/requirements.txt new file mode 100644 index 00000000..3e1593f0 --- /dev/null +++ b/python/email_contact_form/requirements.txt @@ -0,0 +1 @@ +# No third-party dependencies. diff --git a/python/mcp-server/requirements.txt b/python/mcp-server/requirements.txt index 321fd1e3..6e6ac958 100644 --- a/python/mcp-server/requirements.txt +++ b/python/mcp-server/requirements.txt @@ -1 +1 @@ -mcp==2.0.0 +mcp>=2.0.0,<3.0.0 diff --git a/python/prompt_chatgpt/requirements.txt b/python/prompt_chatgpt/requirements.txt index f0dd0aec..b16349fc 100644 --- a/python/prompt_chatgpt/requirements.txt +++ b/python/prompt_chatgpt/requirements.txt @@ -1 +1 @@ -openai \ No newline at end of file +openai>=0.27.8,<0.28.0 diff --git a/python/starter/requirements.txt b/python/starter/requirements.txt index 0d370b0b..e6a70027 100644 --- a/python/starter/requirements.txt +++ b/python/starter/requirements.txt @@ -1 +1 @@ -appwrite==23.0.0 +appwrite>=23.0.0,<24.0.0 diff --git a/python/storage-cleaner/requirements.txt b/python/storage-cleaner/requirements.txt index b0d9f6db..e6a70027 100644 --- a/python/storage-cleaner/requirements.txt +++ b/python/storage-cleaner/requirements.txt @@ -1 +1 @@ -appwrite==23.0.0 \ No newline at end of file +appwrite>=23.0.0,<24.0.0 diff --git a/python/sync_with_algolia/requirements.txt b/python/sync_with_algolia/requirements.txt index ad66ca26..c48f316a 100644 --- a/python/sync_with_algolia/requirements.txt +++ b/python/sync_with_algolia/requirements.txt @@ -1,2 +1,2 @@ -appwrite==23.0.0 -algoliasearch \ No newline at end of file +appwrite>=23.0.0,<24.0.0 +algoliasearch>=3.0.0,<4.0.0 diff --git a/python/sync_with_meilisearch/requirements.txt b/python/sync_with_meilisearch/requirements.txt index 2ba2bf3b..68c14de2 100644 --- a/python/sync_with_meilisearch/requirements.txt +++ b/python/sync_with_meilisearch/requirements.txt @@ -1,2 +1,2 @@ -appwrite==23.0.0 -meilisearch \ No newline at end of file +appwrite>=23.0.0,<24.0.0 +meilisearch>=0.30.0,<0.31.0 diff --git a/python/sync_with_qdrant/requirements.txt b/python/sync_with_qdrant/requirements.txt index 3fb23c0b..39604c17 100644 --- a/python/sync_with_qdrant/requirements.txt +++ b/python/sync_with_qdrant/requirements.txt @@ -1,3 +1,3 @@ -appwrite==23.0.0 -qdrant-client -openai \ No newline at end of file +appwrite>=23.0.0,<24.0.0 +qdrant-client>=1.9.0,<2.0.0 +openai>=1.30.0,<2.0.0 diff --git a/python/whatsapp_with_vonage/requirements.txt b/python/whatsapp_with_vonage/requirements.txt index b126a469..0f287e0a 100644 --- a/python/whatsapp_with_vonage/requirements.txt +++ b/python/whatsapp_with_vonage/requirements.txt @@ -1,2 +1,2 @@ -requests -pyjwt +requests>=2.31.0,<3.0.0 +pyjwt>=2.8.0,<3.0.0 diff --git a/ruby/starter/Gemfile b/ruby/starter/Gemfile index a25455c8..5b22aedf 100644 --- a/ruby/starter/Gemfile +++ b/ruby/starter/Gemfile @@ -1,3 +1,3 @@ source "https://rubygems.org" -gem 'appwrite' \ No newline at end of file +gem 'appwrite', '>= 27.0.0', '< 28.0.0' diff --git a/ruby/sync_with_meilisearch/Gemfile b/ruby/sync_with_meilisearch/Gemfile index cb866cfd..64ea9e56 100644 --- a/ruby/sync_with_meilisearch/Gemfile +++ b/ruby/sync_with_meilisearch/Gemfile @@ -1,4 +1,4 @@ source "https://rubygems.org" -gem 'appwrite' -gem 'meilisearch' +gem 'appwrite', '>= 27.0.0', '< 28.0.0' +gem 'meilisearch', '>= 0.26.0', '< 0.27.0' diff --git a/ruby/whatsapp-with-vonage/Gemfile b/ruby/whatsapp-with-vonage/Gemfile index 233ba77a..01f641de 100644 --- a/ruby/whatsapp-with-vonage/Gemfile +++ b/ruby/whatsapp-with-vonage/Gemfile @@ -1,7 +1,7 @@ source "https://rubygems.org" -gem 'json' -gem 'dotenv' -gem 'digest' -gem 'jwt' -gem 'httparty' \ No newline at end of file +gem 'json', '>= 2.7.6', '< 3.0.0' +gem 'dotenv', '>= 2.8.1', '< 3.0.0' +gem 'digest', '>= 3.2.1', '< 4.0.0' +gem 'jwt', '>= 3.2.0', '< 4.0.0' +gem 'httparty', '>= 0.21.0', '< 0.22.0' \ No newline at end of file diff --git a/swift/starter/Package.swift b/swift/starter/Package.swift index 21826853..5de357c9 100644 --- a/swift/starter/Package.swift +++ b/swift/starter/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.5 +// swift-tools-version:6.2 import PackageDescription let package = Package( diff --git a/swift/starter/README.md b/swift/starter/README.md index b682efba..5991eb04 100644 --- a/swift/starter/README.md +++ b/swift/starter/README.md @@ -37,7 +37,7 @@ Sample `200` Response: | Setting | Value | | ----------------- | --------------------- | -| Runtime | Swift (5.5) | +| Runtime | Swift (6.2) | | Entrypoint | `Sources/index.swift` | | Permissions | `any` | | Timeout (Seconds) | 15 | From 204615d80d496875dc64f95a32bb0bdf465e7b03 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 17 Aug 2026 10:08:00 +0530 Subject: [PATCH 2/3] fix: validate Go dependency declarations --- .github/scripts/check-dependency-ranges.py | 10 +++++++++- ruby/sync_with_meilisearch/Gemfile | 2 +- ruby/whatsapp-with-vonage/Gemfile | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/scripts/check-dependency-ranges.py b/.github/scripts/check-dependency-ranges.py index fdc75992..3f9d3ffd 100644 --- a/.github/scripts/check-dependency-ranges.py +++ b/.github/scripts/check-dependency-ranges.py @@ -108,7 +108,15 @@ def caret_upper_bound(version: str) -> tuple[int, int, int]: fail(path, f"{group}:{artifact}", version) for path in sorted(ROOT.glob("go/*/go.mod")): - for module, version in re.findall(r"^\s*([^\s]+)\s+(v[^\s]+)$", path.read_text(), re.MULTILINE): + text = path.read_text() + dependencies = re.findall( + r"^\s*(?:require\s+)?([^\s]+)\s+(v[^\s]+)(?:\s+//.*)?$", + text, + re.MULTILINE, + ) + if "require " in text and not dependencies: + errors.append(f"{path.relative_to(ROOT)}: no Go dependencies could be parsed") + for module, version in dependencies: if not re.fullmatch(rf"v{SEMVER}", version): fail(path, module, version) diff --git a/ruby/sync_with_meilisearch/Gemfile b/ruby/sync_with_meilisearch/Gemfile index 64ea9e56..4fd5641e 100644 --- a/ruby/sync_with_meilisearch/Gemfile +++ b/ruby/sync_with_meilisearch/Gemfile @@ -1,4 +1,4 @@ source "https://rubygems.org" gem 'appwrite', '>= 27.0.0', '< 28.0.0' -gem 'meilisearch', '>= 0.26.0', '< 0.27.0' +gem 'meilisearch', '>= 0.33.0', '< 0.34.0' diff --git a/ruby/whatsapp-with-vonage/Gemfile b/ruby/whatsapp-with-vonage/Gemfile index 01f641de..6e87d751 100644 --- a/ruby/whatsapp-with-vonage/Gemfile +++ b/ruby/whatsapp-with-vonage/Gemfile @@ -4,4 +4,4 @@ gem 'json', '>= 2.7.6', '< 3.0.0' gem 'dotenv', '>= 2.8.1', '< 3.0.0' gem 'digest', '>= 3.2.1', '< 4.0.0' gem 'jwt', '>= 3.2.0', '< 4.0.0' -gem 'httparty', '>= 0.21.0', '< 0.22.0' \ No newline at end of file +gem 'httparty', '>= 0.24.0', '< 0.25.0' From fd7150b60d136ed1f6f6b78621b7131eb35b6a29 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 17 Aug 2026 13:22:02 +0530 Subject: [PATCH 3/3] chore: document dependency range guidance --- .github/scripts/check-dependency-ranges.py | 139 ------------ .github/workflows/build.yml | 244 --------------------- AGENTS.md | 3 + 3 files changed, 3 insertions(+), 383 deletions(-) delete mode 100644 .github/scripts/check-dependency-ranges.py delete mode 100644 .github/workflows/build.yml create mode 100644 AGENTS.md diff --git a/.github/scripts/check-dependency-ranges.py b/.github/scripts/check-dependency-ranges.py deleted file mode 100644 index 3f9d3ffd..00000000 --- a/.github/scripts/check-dependency-ranges.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python3 -"""Reject template dependencies that can silently cross a major-version boundary.""" - -from __future__ import annotations - -import json -import re -import sys -import tomllib -import xml.etree.ElementTree as ET -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[2] -SEMVER = r"\d+(?:\.\d+){1,2}(?:[-+][0-9A-Za-z.-]+)?" -errors: list[str] = [] - - -def fail(path: Path, dependency: str, constraint: str) -> None: - errors.append(f"{path.relative_to(ROOT)}: {dependency} has an unsafe constraint: {constraint}") - - -def version_tuple(version: str) -> tuple[int, int, int]: - parts = version.split("-", 1)[0].split("+", 1)[0].split(".") - return tuple(int(part) for part in (parts + ["0", "0"])[:3]) - - -def caret_upper_bound(version: str) -> tuple[int, int, int]: - major, minor, patch = version_tuple(version) - if major: - return major + 1, 0, 0 - if minor: - return 0, minor + 1, 0 - return 0, 0, patch + 1 - - -# npm and Bun use caret ranges. Their committed lockfiles keep current installs reproducible. -for path in sorted([*ROOT.glob("node*/*/package.json"), *ROOT.glob("bun/*/package.json")]): - manifest = json.loads(path.read_text()) - lock = path.with_name("bun.lock" if path.parts[-3] == "bun" else "package-lock.json") - if not lock.exists(): - errors.append(f"{path.relative_to(ROOT)}: missing {lock.name}") - for section in ("dependencies", "devDependencies", "optionalDependencies"): - for name, constraint in manifest.get(section, {}).items(): - if not re.fullmatch(rf"\^{SEMVER}", constraint): - fail(path, name, constraint) - -# Pub caret constraints stay below the next breaking version according to Dart semver rules. -for path in sorted(ROOT.glob("dart/*/pubspec.yaml")): - section = None - for line in path.read_text().splitlines(): - if line in ("dependencies:", "dev_dependencies:"): - section = line[:-1] - continue - if line and not line.startswith(" "): - section = None - if section and (match := re.fullmatch(rf" ([\w-]+): (\^{SEMVER})", line)) is None and line.strip(): - fail(path, line.strip().split(":", 1)[0], line.strip()) - -# Python has no caret operator, so require both a lower bound and an exclusive upper bound. -for path in sorted([*ROOT.glob("python/*/requirements.txt"), *ROOT.glob("python-ml/*/requirements.txt")]): - for line in path.read_text().splitlines(): - line = line.strip() - if not line or line.startswith("#"): - continue - match = re.fullmatch(rf"([A-Za-z0-9_.-]+)>=({SEMVER}),<({SEMVER})", line) - if not match or version_tuple(match.group(3)) != caret_upper_bound(match.group(2)): - fail(path, line.split("=", 1)[0], line) - -# Composer's caret and Ruby's explicit upper bounds permit compatible updates only. -for path in sorted(ROOT.glob("php/*/composer.json")): - for name, constraint in json.loads(path.read_text()).get("require", {}).items(): - if name == "php" or name.startswith("ext-"): - continue - if not re.fullmatch(rf"\^{SEMVER}", constraint): - fail(path, name, constraint) - -for path in sorted(ROOT.glob("ruby/*/Gemfile")): - for line in path.read_text().splitlines(): - if not line.strip().startswith("gem "): - continue - match = re.match(r"gem ['\"]([^'\"]+)['\"](.*)", line.strip()) - versions = re.findall(SEMVER, match.group(2)) if match else [] - if not match or len(versions) != 2 or version_tuple(versions[1]) != caret_upper_bound(versions[0]): - fail(path, match.group(1) if match else "gem", line.strip()) - -# Cargo's plain versions are caret requirements. Reject wildcards and unbounded inequalities. -for path in sorted(ROOT.glob("rust/*/Cargo.toml")): - dependencies = tomllib.loads(path.read_text()).get("dependencies", {}) - for name, value in dependencies.items(): - constraint = value if isinstance(value, str) else value.get("version", "") - if not re.fullmatch(SEMVER, constraint): - fail(path, name, constraint) - -# NuGet ranges must have an upper bound; an exact version is also safe. -for path in sorted(ROOT.glob("dotnet/*/*.csproj")): - for package in ET.parse(path).iterfind(".//PackageReference"): - constraint = package.get("Version", "") - exact = re.fullmatch(SEMVER, constraint) - bounded = re.fullmatch(rf"[\[(]({SEMVER}),({SEMVER})[\])]", constraint) - if not exact and (not bounded or version_tuple(bounded.group(2)) != caret_upper_bound(bounded.group(1))): - fail(path, package.get("Include", "package"), constraint) - -# Maven coordinates, Go modules, and URL imports have no portable caret syntax here. Require a -# concrete version; Go major versions are additionally encoded in v2+ module paths. -for path in sorted(ROOT.glob("*/*/deps.gradle")): - for group, artifact, version in re.findall(r"['\"]([^:'\"]+):([^:'\"]+):([^'\"]+)['\"]", path.read_text()): - if not re.fullmatch(SEMVER, version): - fail(path, f"{group}:{artifact}", version) - -for path in sorted(ROOT.glob("go/*/go.mod")): - text = path.read_text() - dependencies = re.findall( - r"^\s*(?:require\s+)?([^\s]+)\s+(v[^\s]+)(?:\s+//.*)?$", - text, - re.MULTILINE, - ) - if "require " in text and not dependencies: - errors.append(f"{path.relative_to(ROOT)}: no Go dependencies could be parsed") - for module, version in dependencies: - if not re.fullmatch(rf"v{SEMVER}", version): - fail(path, module, version) - -for path in sorted(ROOT.glob("deno/*/src/*")): - if path.suffix != ".ts": - continue - for url in re.findall(r"from\s+[\"'](https://[^\"']+)", path.read_text()): - if "@" not in url: - fail(path, url, "missing URL version") - -for path in sorted(ROOT.glob("swift/*/Package.swift")): - for declaration in re.findall(r"\.package\([^\n]+", path.read_text()): - if ".upToNextMajor(" not in declaration and ".exact(" not in declaration and "revision:" not in declaration: - fail(path, "Swift package", declaration) - -if errors: - print("\n".join(errors), file=sys.stderr) - sys.exit(1) - -print("All template dependencies are bounded below the next breaking version.") diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index d0fdafd3..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,244 +0,0 @@ -name: Template builds - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -on: - pull_request: - schedule: - # Compatible dependency updates are intentional; verify them weekly. - - cron: "0 7 * * 1" - workflow_dispatch: - -permissions: - contents: read - -jobs: - dependency-ranges: - name: dependency ranges - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: "3.11" - - run: python .github/scripts/check-dependency-ranges.py - - node: - name: Node.js and TypeScript - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: "22" - - name: Build templates - run: | - set -euo pipefail - for dir in node/*/ node-typescript/*/; do - [ -f "${dir}package.json" ] || continue - echo "::group::${dir%/}" - npm ci --prefix "$dir" --ignore-scripts --no-audit --no-fund - if [ -f "${dir}tsconfig.json" ]; then - npm run --prefix "$dir" build - else - find "${dir}src" -type f -name '*.js' -exec node --check {} \; - fi - echo "::endgroup::" - done - - bun: - name: Bun - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: latest - - name: Build templates - run: | - set -euo pipefail - for dir in bun/*/; do - [ -f "${dir}package.json" ] || continue - echo "::group::${dir%/}" - (cd "$dir" && bun install --frozen-lockfile --ignore-scripts && bun -e "await import('./src/main.ts')") - echo "::endgroup::" - done - - python: - name: Python ${{ matrix.name }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - name: "3.9" - version: "3.9" - templates: "python" - - name: "3.12 MCP" - version: "3.12" - templates: "mcp" - - name: "3.11 ML" - version: "3.11" - templates: "python-ml" - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: ${{ matrix.version }} - - name: Build templates - env: - TEMPLATE_SET: ${{ matrix.templates }} - run: | - set -euo pipefail - if [ "$TEMPLATE_SET" = mcp ]; then - dirs=(python/mcp-server/) - elif [ "$TEMPLATE_SET" = python-ml ]; then - dirs=(python-ml/*/) - else - dirs=(python/*/) - fi - for dir in "${dirs[@]}"; do - [ -f "${dir}requirements.txt" ] || continue - if [ "$TEMPLATE_SET" = python ] && [ "$dir" = python/mcp-server/ ]; then continue; fi - echo "::group::${dir%/}" - rm -rf /tmp/template-venv - python -m venv /tmp/template-venv - /tmp/template-venv/bin/pip install --disable-pip-version-check -r "${dir}requirements.txt" - /tmp/template-venv/bin/pip check - /tmp/template-venv/bin/python -m compileall -q "${dir}src" - echo "::endgroup::" - done - - php: - name: PHP - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: "8.3" - tools: composer - - name: Build templates - run: | - set -euo pipefail - for dir in php/*/; do - [ -f "${dir}composer.json" ] || continue - echo "::group::${dir%/}" - composer install --working-dir="$dir" --no-interaction --no-progress - find "${dir}src" -type f -name '*.php' -exec php -l {} \; - echo "::endgroup::" - done - - ruby: - name: Ruby - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: ruby/setup-ruby@a30dfa457ad68707b8b910ac3a244714b61c0626 # v1.320.0 - with: - ruby-version: "3.1" - bundler: latest - - name: Build templates - run: | - set -euo pipefail - for dir in ruby/*/; do - [ -f "${dir}Gemfile" ] || continue - echo "::group::${dir%/}" - (cd "$dir" && bundle install && find lib -type f -name '*.rb' -exec ruby -c {} \;) - echo "::endgroup::" - done - - dart: - name: Dart - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2 - with: - sdk: stable - - name: Build templates - run: | - set -euo pipefail - for dir in dart/*/; do - [ -f "${dir}pubspec.yaml" ] || continue - echo "::group::${dir%/}" - (cd "$dir" && dart pub get && dart analyze) - echo "::endgroup::" - done - - deno: - name: Deno - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2 - with: - deno-version: v2.x - - name: Build templates - run: | - set -euo pipefail - for dir in deno/*/; do - echo "::group::${dir%/}" - (cd "$dir" && deno check src/main.ts) - echo "::endgroup::" - done - - go: - name: Go - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version: "1.26.6" - cache: false - - run: for dir in go/*/; do [ ! -f "${dir}go.mod" ] || (cd "$dir" && go build ./...); done - - rust: - name: Rust - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: dtolnay/rust-toolchain@bd41891a8e7f4b8649f6d684415e1a6155fe4e22 # 1.83.0 - - run: for dir in rust/*/; do [ ! -f "${dir}Cargo.toml" ] || (cd "$dir" && cargo build --locked); done - - runtime-builds: - name: ${{ matrix.template }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - template: cpp/starter - image: openruntimes/cpp:v4-17 - entrypoint: src/main.cc - - template: dotnet/starter - image: openruntimes/dotnet:v4-6.0 - entrypoint: src/Index.cs - - template: java/starter - image: openruntimes/java:v4-17.0 - entrypoint: src/Main.java - - template: kotlin/starter - image: openruntimes/kotlin:v4-1.8 - entrypoint: src/Main.kt - - template: kotlin/sync-with-meilisearch - image: openruntimes/kotlin:v4-1.8 - entrypoint: src/Main.kt - - template: swift/starter - image: openruntimes/swift:v5-6.2 - entrypoint: Sources/index.swift - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Build in Open Runtimes - env: - IMAGE: ${{ matrix.image }} - ENTRYPOINT: ${{ matrix.entrypoint }} - TEMPLATE: ${{ matrix.template }} - run: | - docker run --rm \ - -e OPEN_RUNTIMES_ENTRYPOINT="$ENTRYPOINT" \ - -v "$PWD/$TEMPLATE:/mnt/code" \ - "$IMAGE" sh helpers/build.sh - rm -f "$TEMPLATE/code.tar.gz" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..6b7eec23 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,3 @@ +# Repository instructions + +When adding or updating template dependencies, use constraints that allow compatible patch and minor updates while excluding the next breaking version. Prefer caret ranges where the package manager supports them, avoid unbounded or wildcard constraints, and keep committed lockfiles in sync with their manifests.