Skip to content
Closed
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
85 changes: 85 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copilot PR Review Instructions — FastEdge-sdk-rust

## Constitution

This repository is `fastedge` (crate) — the Rust SDK for Gcore FastEdge. It provides the `#[fastedge::http]` and `#[wstd::http_server]` handler macros, type conversions, an outbound HTTP client, and ProxyWasm FFI wrappers for CDN apps.

### Principles (enforce during review)

1. **Handler preference** — `#[wstd::http_server]` (async, wasm32-wasip2) is the recommended handler for new HTTP apps. `#[fastedge::http]` is legacy. New examples must use `wstd`.
2. **No over-engineering** — Simple solutions over complex abstractions. Three similar lines > premature abstraction.
3. **Platform constraints** — Only stdout is captured; `eprintln!` output is silently lost. Flag any use of stderr in code or examples.
4. **CDN/HTTP separation** — CDN apps (proxy-wasm filters) and HTTP apps (standalone handlers) are independent application types with different architectures and lifecycles. Never mix their APIs.
5. **WIT submodule integrity** — `wit/` files come from `G-Core/FastEdge-wit` submodule. Never modify them directly.

### Public API contract

The public API surface is defined by:
- `src/lib.rs` — Core types (`Body`, `Error`), type conversions, `send_request`
- `derive/src/lib.rs` — `#[fastedge::http]` proc macro
- `src/proxywasm/` — ProxyWasm FFI wrappers (KV store, secrets, dictionary, utils)
- `src/http_client.rs` — Outbound HTTP client

Changes to these surfaces require updated `docs/`, updated tests, and a semver-appropriate version bump.

## Generated Content — `docs/`

Files in `docs/` are **machine-generated** from source code by `./fastedge-plugin-source/generate-docs.sh`. They must not be edited by hand — manual changes will be silently overwritten on the next generation run.

### When reviewing PRs that touch `docs/`:

- **Never** suggest manual edits to any file in `docs/`
- If docs are stale or incorrect, suggest: **Run `./fastedge-plugin-source/generate-docs.sh`**
- If the generated output itself is wrong (e.g., wrong structure, missing section), the fix belongs in `fastedge-plugin-source/.generation-config.md`, not in `docs/` directly
- If a PR modifies `docs/` files without a corresponding source code change, flag it — the change should come from the generation script, not a hand-edit

### When reviewing PRs that change source code covered by `docs/`:

- Check whether the change affects the public API or user-facing behavior
- If yes, and `docs/` was not regenerated in the same PR, **request changes** with:
> Source code affecting public API was changed but docs/ was not regenerated.
> Run: `./fastedge-plugin-source/generate-docs.sh`

## Documentation Freshness

### Public API changes (must regenerate docs/)
- New, modified, or removed public types/functions in `src/lib.rs`
- Changes to `#[fastedge::http]` macro behavior in `derive/src/lib.rs`
- Changes to ProxyWasm wrapper APIs in `src/proxywasm/`
- Changes to outbound HTTP client in `src/http_client.rs`
- New or modified WIT interfaces in `wit/`
- Changes to `Cargo.toml` (version, features, dependencies)

### Mapping: code location → doc file

| Code path | Doc file |
| --------------------------------------------- | --------------------- |
| `src/lib.rs` (Body, Error, send_request) | `docs/SDK_API.md` |
| `derive/src/lib.rs` (handler macros) | `docs/SDK_API.md` |
| `src/http_client.rs` (outbound HTTP) | `docs/SDK_API.md` |
| `src/proxywasm/key_value.rs` | `docs/HOST_SERVICES.md` |
| `src/proxywasm/secret.rs` | `docs/HOST_SERVICES.md` |
| `src/proxywasm/dictionary.rs` | `docs/HOST_SERVICES.md` |
| `src/proxywasm/utils.rs` | `docs/HOST_SERVICES.md` |
| `src/proxywasm/` (CDN lifecycle, FFI) | `docs/CDN_APPS.md` |
| `Cargo.toml` (version, features) | `docs/INDEX.md` |
| `fastedge-plugin-source/manifest.json` | `.github/copilot-instructions.md` |

### Violation example

> PR changes `send_request` signature in `src/lib.rs` but `docs/SDK_API.md` still shows the old signature → **request changes**. Run `./fastedge-plugin-source/generate-docs.sh` before merge.

### Quickstart protection

If any public API signature or behavior changes, check whether `docs/quickstart.md` examples are still accurate. Request regeneration if examples would no longer work against the updated code.

## Pipeline source contract

If `fastedge-plugin-source/manifest.json` lists source files that overlap with files changed in this PR, request that `docs/` is regenerated (run `./fastedge-plugin-source/generate-docs.sh`) to keep the plugin pipeline's source material current.

## Quality Rules

- All public function signatures in docs must match actual source declarations
- No `eprintln!` or `eprint!` in any code or examples — output is lost on the platform
- New HTTP examples must use `#[wstd::http_server]`, not `#[fastedge::http]`
- No marketing language in documentation — precise, technical prose only
22 changes: 22 additions & 0 deletions .github/workflows/copilot-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Copilot Instructions Sync

on:
pull_request:
paths:
- fastedge-plugin-source/manifest.json
- fastedge-plugin-source/check-copilot-sync.sh
- .github/copilot-instructions.md
- .github/workflows/copilot-sync.yml
- examples/**
- docs/**

jobs:
check-sync:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Check manifest ↔ copilot-instructions sync
run: bash fastedge-plugin-source/check-copilot-sync.sh
5 changes: 3 additions & 2 deletions examples/cdn/cors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ Handles preflight OPTIONS requests and adds CORS response headers
for allowed origins. Supports configurable origin allow-lists,
methods, and exposed headers.

Required configuration:
Configuration:
- Environment variable: ALLOWED_ORIGINS (comma-separated origins or "*")
Optional configuration:
When unset or empty the filter is dormant — requests pass through
without CORS headers, so browsers will block cross-origin access.
- Environment variable: ALLOWED_METHODS (default: "GET, POST, PUT, DELETE, OPTIONS")
- Environment variable: MAX_AGE (default: "86400")
- Environment variable: EXPOSE_HEADERS (response headers to expose)
Expand Down
4 changes: 2 additions & 2 deletions examples/cdn/custom_error_pages/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl HttpContext for HttpBody {
Action::Continue
}

fn on_http_response_body(&mut self, _body_size: usize, end_of_stream: bool) -> Action {
fn on_http_response_body(&mut self, body_size: usize, end_of_stream: bool) -> Action {
// only process 4xx/5xx error responses
let Some(status) = self.get_property(vec!["response.status"]) else {
return Action::Continue;
Expand Down Expand Up @@ -139,7 +139,7 @@ impl HttpContext for HttpBody {

let html_body = handlebars.render("error_template", &page_data).unwrap();
let body = html_body.as_bytes();
self.set_http_response_body(0, body.len(), body);
self.set_http_response_body(0, body_size, body);

Action::Continue
}
Expand Down
2 changes: 1 addition & 1 deletion examples/cdn/md2html/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl HttpContext for HttpBody {
html.push_str("</body></html>");

let body = html.as_bytes();
self.set_http_response_body(0, body.len(), body);
self.set_http_response_body(0, body_size, body);
println!("Converted");

Action::Continue
Expand Down
49 changes: 46 additions & 3 deletions fastedge-plugin-source/check-copilot-sync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
# Validates copilot-instructions.md stays in sync with the codebase:
# 1. All doc files in manifest.json are referenced in the mapping table
# 2. All doc files in the mapping table actually exist on disk
# 3. All example directories on disk are tracked in manifest.json
#
# This script is part of the fastedge-plugin pipeline contract.
# Canonical template: fastedge-plugin/scripts/sync/templates/check-copilot-sync-template.sh
# Each source repo gets a copy at: fastedge-plugin-source/check-copilot-sync.sh
#
# Exits 0 if in sync, 1 if drift detected.
# Exits 0 if in sync (warnings don't affect exit code), 1 if drift detected.

set -euo pipefail

Expand All @@ -23,7 +24,7 @@ fi
# --- Check 1: manifest doc files appear in the mapping table ---

# Extract doc/schema paths that appear in mapping table rows (lines starting with '|')
# Use POSIX-compatible awk instead of grep -P so this works on macOS/BSD grep too
# Uses awk to parse backticked paths — works on macOS/BSD and Linux
mapping_table_docs=$(awk '
/^\|/ {
line = $0
Expand All @@ -49,7 +50,7 @@ if [ -f "$MANIFEST" ]; then

missing=()
for doc in $doc_files; do
if ! echo "$mapping_table_docs" | grep -qF "$doc"; then
if ! printf '%s\n' "$mapping_table_docs" | grep -qxF "$doc"; then
missing+=("$doc")
fi
done
Expand Down Expand Up @@ -88,6 +89,48 @@ else
echo "OK: All doc files in copilot-instructions mapping table exist on disk"
fi

# --- Check 3: example directories on disk are tracked in manifest ---
#
# Detects example projects not listed in manifest.json so the fastedge-plugin
# pipeline doesn't silently miss new examples. Uses ::warning:: annotations
# for visibility in GitHub PR checks.
#
# This check is advisory (does not affect exit code) because repos may have
# known gaps during rollout.

if [ -d "examples" ] && [ -f "$MANIFEST" ]; then
# Get all examples/ file paths from the manifest
manifest_examples=$(jq -r '.sources[].files[]' "$MANIFEST" | grep '^examples/' | sort -u)

# Find example project directories (contain package.json, Cargo.toml, or asconfig.json)
# Handles flat (examples/<name>/) and nested (examples/cdn/<name>/) structures
untracked=()
while IFS= read -r marker_file; do
[ -z "$marker_file" ] && continue
project_dir=$(dirname "$marker_file")
# Check if any manifest file starts with this project directory
if [ -z "$manifest_examples" ] || ! printf '%s\n' "$manifest_examples" | grep -q "^${project_dir}/"; then
untracked+=("$project_dir")
fi
done < <(find examples/ -maxdepth 4 \( -name "package.json" -o -name "Cargo.toml" -o -name "asconfig.json" \) -not -path "*/node_modules/*" 2>/dev/null | sort)
Comment on lines +101 to +115
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Check 3, manifest_examples=$(... | grep '^examples/' | sort -u) can cause the whole script to exit under set -euo pipefail when the manifest currently contains zero examples/ entries (grep exits 1). That would turn this intended advisory check into a hard failure. Consider filtering with jq (e.g., select(startswith("examples/"))) or otherwise neutralizing the non-match case (e.g., || true) so an empty examples list results in warnings rather than an early exit.

Copilot uses AI. Check for mistakes.

if [ ${#untracked[@]} -gt 0 ]; then
echo "WARN: Example directories not tracked in $MANIFEST:"
for dir in "${untracked[@]}"; do
echo " - $dir"
echo "::warning::Example directory '$dir' is not tracked in manifest.json. Add it to fastedge-plugin-source/manifest.json so the fastedge-plugin pipeline can access it."
done
echo ""
echo " To fix: add source entries for the above directories to $MANIFEST"
else
echo "OK: All example directories are tracked in manifest.json"
fi
elif [ ! -d "examples" ]; then
echo "SKIP: No examples/ directory"
else
echo "SKIP: No manifest found at $MANIFEST (cannot check examples coverage)"
fi

# --- Result ---

if [ $errors -ne 0 ]; then
Expand Down
Loading