Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
67d0cdf
Add design spec for extensible edgezero-cli library
aram356 May 19, 2026
a78284c
Expand CLI extensions spec to cover all 7 sub-projects
aram356 May 19, 2026
f1fdbfe
Apply review feedback and add secret annotation to CLI extensions spec
aram356 May 19, 2026
2e6904b
Expand spec for multi-store manifest + finalize naming and validate s…
aram356 May 20, 2026
0c1e118
Apply second-pass review: runtime API completeness, Cloudflare KV, se…
aram356 May 20, 2026
197b4f9
Third-pass review: async ConfigStore, env overlay, extractor refactor
aram356 May 20, 2026
1b41ad7
Fourth-pass review: manifest discrimination, Hooks split, env coercio…
aram356 May 20, 2026
f0aed20
Fifth-pass review: hard cutoff, Spin as first-class store adapter, on…
aram356 May 20, 2026
27a6169
Sixth-pass review: close Spin integration design holes
aram356 May 20, 2026
fd2ef43
Seventh-pass review + dev→demo rename + documentation step
aram356 May 20, 2026
1533464
Eighth-pass review: three minor fixes (no blockers remain)
aram356 May 20, 2026
d7eff52
Ninth-pass review: three minor notes (reviewer sign-off, no blockers)
aram356 May 20, 2026
fe5dce1
Add implementation plan for CLI extensions (8-commit PR)
aram356 May 20, 2026
a542f28
Fix six plan-review findings (plan + spec)
aram356 May 20, 2026
1e6f3f3
Tighten plan: four review findings before execution
aram356 May 20, 2026
23b76ca
Plan: wire new commands into the default binary, upgrade scaffold, al…
aram356 May 20, 2026
c63dad0
Plan: fix three crate-dependency gaps for typed-config wiring
aram356 May 20, 2026
df7751e
Plan: generator context for config type name + validator workspace seed
aram356 May 20, 2026
82be804
Plan: fix generated-CLI import path + guarantee valid NameUpperCamel …
aram356 May 20, 2026
166c2df
Fixed formatting
aram356 May 20, 2026
1d582dd
Commit 1: extensible edgezero-cli library + generator + app-demo-cli
aram356 May 20, 2026
06f4b72
Make `demo` example-only; `serve --adapter axum` runs the axum adapter
aram356 May 20, 2026
3f6151d
Plan: mark Commit 1 done, fix stale branch/path, expand Fastly in Com…
aram356 May 20, 2026
e5a9a4f
Spec: document the namespaced args API (edgezero_cli::args::*)
aram356 May 20, 2026
f6acd93
Merge remote-tracking branch 'origin/chore/strict-clippy' into featur…
aram356 May 21, 2026
247cb74
demo: run app-demo via run_app for full manifest setup
aram356 May 21, 2026
0b0c914
Plan/spec: rename "Commit N" to "Stage N"
aram356 May 21, 2026
4565b30
Formatting
aram356 May 21, 2026
6463ba1
Make demo a contributor-only command; rename feature to demo-example
aram356 May 21, 2026
8d16ba9
Fix binary name, stale dev docs, and scaffold drift
aram356 May 21, 2026
e29b749
Generate path dependencies to the local edgezero checkout
aram356 May 21, 2026
cc7ff45
Verify the full generated workspace compiles, not just the CLI crate
aram356 May 21, 2026
ca357c4
Fix generated-README serve command; align plan/spec with 4-command CLI
aram356 May 21, 2026
5fe5834
Merge remote-tracking branch 'origin/chore/strict-clippy' into featur…
aram356 May 21, 2026
2277072
Wire generated-project compile check into CI; fix stale plan lines
aram356 May 21, 2026
1d95345
Fix generated wasm adapters and project-name sanitisation
aram356 May 21, 2026
72ec5ee
Plan: clear stale PR #253 gating and five-built-ins references
aram356 May 21, 2026
58c8e91
Spec/plan: revise Stage 2 to the portable-manifest + EDGEZERO__ design
aram356 May 21, 2026
f5bd432
Stage 2 Task 2.1: portable manifest store schema
aram356 May 22, 2026
46248f9
Stage 2 Task 2.2: EDGEZERO__* environment-config layer
aram356 May 22, 2026
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
2 changes: 1 addition & 1 deletion .claude/agents/build-validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ cargo check -p edgezero-core --all-features
cargo check -p edgezero-adapter-fastly --features cli
cargo check -p edgezero-adapter-cloudflare --features cli
cargo check -p edgezero-adapter-axum --features axum
cargo check -p edgezero-cli --features dev-example
cargo check -p edgezero-cli --features demo-example
```

## Demo apps
Expand Down
2 changes: 1 addition & 1 deletion .claude/agents/verify-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Demo adapters must build for their respective WASM targets.
## 6. Dev server smoke test

```
cargo run -p edgezero-cli --features dev-example -- dev &
cargo run -p edgezero-cli --features demo-example -- demo &
pid=$!
trap 'kill "$pid" 2>/dev/null || true; wait "$pid" 2>/dev/null || true' EXIT
sleep 3
Expand Down
36 changes: 17 additions & 19 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
{
"permissions": {
"allow": [
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(head:*)",
"Bash(tail:*)",
"Bash(wc:*)",
"Bash(tree:*)",
"Bash(which:*)",

"Bash(cargo build:*)",
"Bash(cargo test:*)",
"Bash(cargo check:*)",
"Bash(cargo clippy:*)",
"Bash(cargo fmt:*)",
"Bash(cargo metadata:*)",
"Bash(cargo run -p edgezero-cli:*)",

"Bash(cargo fmt:*)",
"Bash(cargo clippy:*)",

"Bash(cargo test:*)",
"Bash(cat:*)",
"Bash(git branch:*)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git status:*)",
"Bash(head:*)",
"Bash(ls:*)",
"Bash(npm ci:*)",
"Bash(npm run:*)",

"Bash(rustup target:*)",

"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git branch:*)"
"Bash(tail:*)",
"Bash(tree:*)",
"Bash(wc:*)",
"Bash(which:*)"
]
},
"enabledPlugins": {
"superpowers@claude-plugins-official": true
}
}
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ jobs:
- name: Check feature compilation
run: cargo check --workspace --all-targets --features "fastly cloudflare spin"

- name: Verify a generated project compiles
run: cargo test -p edgezero-cli --test generated_project_builds -- --ignored

adapter-wasm-tests:
name: ${{ matrix.adapter }} wasm tests
runs-on: ubuntu-latest
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ target/
# Worktrees
.worktrees/

# Superpowers plans
docs/superpowers/

# Editors
.claude/*
!.claude/settings.json
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ crates/
edgezero-adapter-cloudflare/# Cloudflare Workers bridge (wasm32-unknown-unknown)
edgezero-adapter-spin/ # Fermyon Spin bridge (wasm32-wasip1)
edgezero-adapter-axum/ # Axum/Tokio bridge (native, dev server)
edgezero-cli/ # CLI: new, build, deploy, dev, serve
edgezero-cli/ # CLI lib + bin: new, build, deploy, demo, serve
examples/app-demo/ # Reference app with all 4 adapters (excluded from workspace)
docs/ # VitePress documentation site (Node.js)
scripts/ # Build/deploy/test helper scripts
Expand Down Expand Up @@ -55,8 +55,8 @@ cargo check --workspace --all-targets --features "fastly cloudflare spin"
# Spin wasm32 compilation check
cargo check -p edgezero-adapter-spin --target wasm32-wasip1 --features spin

# Run the demo dev server
cargo run -p edgezero-cli --features dev-example -- dev
# Run the demo server
cargo run -p edgezero-cli --features demo-example -- demo

# Docs site
cd docs && npm ci && npm run dev
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ Production-ready toolkit for portable edge HTTP workloads. Write once, deploy to
cargo install --path crates/edgezero-cli

# Create a new project
edgezero-cli new my-app
edgezero new my-app
cd my-app

# Start the dev server
edgezero-cli dev
# Run it locally on the Axum adapter
edgezero serve --adapter axum

# Test it
curl http://127.0.0.1:8787/
Expand Down
4 changes: 2 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ High-level backlog and decisions to drive the next milestones.
- [ ] Adapters: assert error-path mapping for Fastly/Cloudflare request conversion and re-enable the ignored Cloudflare response header test.
- [ ] CLI: add integration tests for `edgezero new` scaffolding, feature-flag builds, and `dev` fallback app.
- [ ] CLI: cover `dev_server`, generator, and template scaffolding flows with tempdir-based integration tests to guard manual HTTP parsing and shell commands.
- [ ] CI: verify feature combinations (without `dev-example`, `json`, `form`) compile and run basic smoke tests.
- [ ] CI: verify feature combinations (without `demo-example`, `json`, `form`) compile and run basic smoke tests.
- [ ] Macros: add trybuild coverage for `app!` manifest expansion (route/middleware generation and error surfacing).
- [x] Core: unit-test `App::build_app`/`Hooks` wiring and `PathParams::deserialize` edge cases beyond indirect coverage. _(Added targeted unit tests in `crates/edgezero-core/src/app.rs` and `crates/edgezero-core/src/params.rs`.)_
- [x] Coverage hygiene: consolidate duplicate router/extractor request-parsing tests and share adapter contract fixtures to reduce redundant maintenance. _(Router duplicates trimmed; extractor suite now owns request parsing checks.)_
Expand Down Expand Up @@ -158,7 +158,7 @@ High-level backlog and decisions to drive the next milestones.
## Review (2025-09-18 03:08 UTC)

- Implemented `edgezero build|deploy --adapter fastly` by wiring cargo wasm32 builds and Fastly CLI invocation in the CLI.
- Documented optional `dev-example` dependency in `edgezero-cli/README.md` and added error handling for unsupported adapters.
- Documented optional `demo-example` dependency in `edgezero-cli/README.md` and added error handling for unsupported adapters.
- Verified builds with `cargo test -p edgezero-cli`.

## Review (2025-09-18 03:27 UTC)
Expand Down
2 changes: 1 addition & 1 deletion crates/edgezero-adapter-axum/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ static AXUM_BLUEPRINT: AdapterBlueprint = AdapterBlueprint {
dev_heading: "{display} (local)",
dev_steps: &[
"`cd {crate_dir}`",
"`cargo run` or `edgezero-cli serve --adapter axum`",
"`cargo run` or `edgezero serve --adapter axum`",
],
},
run_module: "edgezero_adapter_axum",
Expand Down
6 changes: 3 additions & 3 deletions crates/edgezero-adapter-axum/src/config_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ use std::env;

use edgezero_core::config_store::{ConfigStore, ConfigStoreError};

/// Config store for local dev / Axum. Reads from env vars with manifest
/// Config store for local dev / Axum. Reads from env vars with in-memory
/// defaults as fallback. Env vars take precedence over defaults.
///
/// # Note on `from_env`
///
/// [`AxumConfigStore::from_env`] only reads environment variables for keys
/// declared in `[stores.config.defaults]`. Use an empty-string default when a
/// key should be overrideable from env without carrying a real default value.
/// present in the supplied defaults map. The portable manifest no longer
/// carries config-store defaults, so the dev server passes an empty map.
pub struct AxumConfigStore {
defaults: HashMap<String, String>,
env: HashMap<String, String>,
Expand Down
11 changes: 6 additions & 5 deletions crates/edgezero-adapter-axum/src/dev_server.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::env;
use std::fs;
use std::iter;
use std::net::{SocketAddr, TcpListener as StdTcpListener};
use std::path::{Path, PathBuf};
use std::sync::Arc;
Expand Down Expand Up @@ -355,9 +356,10 @@ pub fn run_app<A: Hooks>(manifest_src: &str) -> anyhow::Result<()> {
if A::config_store().is_some() && manifest_data.stores.config.is_none() {
log::warn!("A::config_store() is set but [stores.config] is missing in the manifest. This override is ignored on Axum.");
}
let config_store_handle = manifest_data.stores.config.as_ref().map(|cfg| {
let defaults = cfg.config_store_defaults().clone();
let store = AxumConfigStore::from_env(defaults);
let config_store_handle = manifest_data.stores.config.as_ref().map(|_cfg| {
// The portable manifest no longer carries `[stores.config.defaults]`;
// the axum config store starts empty and reads from the environment.
let store = AxumConfigStore::from_env(iter::empty());
ConfigStoreHandle::new(Arc::new(store))
});
let secret = has_secret_store.then(|| { log::info!("Secret store: reading from environment variables"); SecretHandle::new(Arc::new(
Expand Down Expand Up @@ -479,7 +481,7 @@ mod tests {
let manifest = ManifestLoader::load_from_str(
r#"
[stores.kv]
name = "EDGEZERO_KV"
ids = ["EDGEZERO_KV"]
"#,
);
assert_eq!(
Expand Down Expand Up @@ -603,7 +605,6 @@ mod integration_tests {
use edgezero_core::extractor::Secrets;
use edgezero_core::router::RouterService;
use edgezero_core::secret_store::SecretHandle as CoreSecretHandle;
use std::iter;
use std::time::{Duration, Instant};
use tokio::task::{spawn_blocking, JoinHandle};
use tokio::time::sleep;
Expand Down
2 changes: 1 addition & 1 deletion crates/edgezero-adapter-cloudflare/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static CLOUDFLARE_BLUEPRINT: AdapterBlueprint = AdapterBlueprint {
readme: ReadmeInfo {
description: "{display} entrypoint.",
dev_heading: "{display} (local)",
dev_steps: &["`edgezero-cli serve --adapter cloudflare`"],
dev_steps: &["`edgezero serve --adapter cloudflare`"],
},
run_module: "edgezero_adapter_cloudflare",
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,11 @@ use worker::*;
#[cfg(target_arch = "wasm32")]
#[event(fetch)]
pub async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
edgezero_adapter_cloudflare::run_app::<{{proj_core_mod}}::App>(req, env, ctx).await
edgezero_adapter_cloudflare::run_app::<{{proj_core_mod}}::App>(
include_str!("../../../edgezero.toml"),
req,
env,
ctx,
)
.await
}
2 changes: 1 addition & 1 deletion crates/edgezero-adapter-fastly/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static FASTLY_BLUEPRINT: AdapterBlueprint = AdapterBlueprint {
readme: ReadmeInfo {
description: "{display} entrypoint.",
dev_heading: "{display} (local)",
dev_steps: &["`cd {crate_dir}`", "`edgezero-cli serve --adapter fastly`"],
dev_steps: &["`cd {crate_dir}`", "`edgezero serve --adapter fastly`"],
},
run_module: "edgezero_adapter_fastly",
};
Expand Down
2 changes: 1 addition & 1 deletion crates/edgezero-adapter-spin/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static SPIN_BLUEPRINT: AdapterBlueprint = AdapterBlueprint {
readme: ReadmeInfo {
description: "{display} entrypoint.",
dev_heading: "{display} (local)",
dev_steps: &["`edgezero-cli serve --adapter spin`"],
dev_steps: &["`edgezero serve --adapter spin`"],
},
run_module: "edgezero_adapter_spin",
};
Expand Down
14 changes: 5 additions & 9 deletions crates/edgezero-adapter-spin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,22 +155,18 @@ mod tests {
}

#[test]
fn store_settings_resolve_spin_manifest_overrides() {
fn store_settings_resolve_spin_manifest_declarations() {
let settings = resolve_settings(
r#"
[stores.kv]
name = "GLOBAL_KV"

[stores.kv.adapters.spin]
name = "SPIN_KV"
ids = ["SPIN_KV", "cache"]
default = "SPIN_KV"

[stores.config]
ids = ["app_config"]

[stores.secrets]
enabled = false

[stores.secrets.adapters.spin]
enabled = true
ids = ["default"]
"#,
false,
);
Expand Down
8 changes: 8 additions & 0 deletions crates/edgezero-adapter-spin/src/templates/src/lib.rs.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
#![cfg_attr(
target_arch = "wasm32",
allow(
unsafe_code,
reason = "spin's #[http_component] macro generates the unsafe wasm export"
)
)]

#[cfg(target_arch = "wasm32")]
use spin_sdk::http::{IncomingRequest, IntoResponse};
#[cfg(target_arch = "wasm32")]
Expand Down
6 changes: 5 additions & 1 deletion crates/edgezero-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ description = "EdgeZero CLI: build and deploy to multiple edge adapters"
[lints]
workspace = true

[[bin]]
name = "edgezero"
path = "src/main.rs"

[dependencies]
edgezero-core = { workspace = true }
edgezero-adapter = { path = "../edgezero-adapter" }
Expand Down Expand Up @@ -41,4 +45,4 @@ default = [
"edgezero-adapter-spin",
]
cli = ["dep:clap"]
dev-example = ["dep:app-demo-core"]
demo-example = ["dep:app-demo-core", "edgezero-adapter-axum"]
9 changes: 5 additions & 4 deletions crates/edgezero-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,27 @@ The crate exposes two cargo features:
| Feature | Description | Enabled by default |
|----------------|----------------------------------------------------------|--------------------|
| `cli` | Builds the command-line interface (`edgezero` binary). | ✅ |
| `dev-example` | Pulls in `examples/app-demo/app-demo-core` so `edgezero dev` can boot the bundled demo app. Enable only when you want the sample router available. | ❌ |
| `demo-example` | Pulls in `examples/app-demo/app-demo-core` so `edgezero demo` can boot the bundled example app. Contributor-only; enable when working on the in-repo example. | ❌ |

When you just need the CLI functionality (e.g. packaging for distribution), build without the demo feature:

```bash
cargo build -p edgezero-cli --no-default-features --features cli
```

For contributors working on the demo, enable the extra feature:
For contributors working on the bundled example, enable the extra feature:

```bash
cargo run -p edgezero-cli --features "cli,dev-example" -- dev
cargo run -p edgezero-cli --features "cli,demo-example" -- demo
```

## Commands

_(summaries only; see `edgezero --help` for details)_

- `edgezero new <name>` – Scaffold a new EdgeZero project (templates still evolving).
- `edgezero dev` – Serve the current project locally (add `--features dev-example` to run the bundled demo).
- `edgezero serve --adapter <name>` – Run the current project locally on the named adapter.
- `edgezero demo` – Run the bundled `app-demo` example locally (contributor-only; requires `--features demo-example`).
- `edgezero build --adapter fastly` – Compile the Fastly crate to `wasm32-wasip1` and drop the artifact in `pkg/`.
- `edgezero deploy --adapter fastly` – Invoke the Fastly CLI (`fastly compute deploy`) from the detected Fastly crate.
- `edgezero serve --adapter fastly` – Run `fastly compute serve` in the Fastly crate directory for local testing (requires Fastly CLI).
Expand Down
Loading