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
82 changes: 0 additions & 82 deletions .github/workflows/compat-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -426,88 +426,6 @@ jobs:
exit 1
fi

- name: Run per-package tests
if: success()
run: |
PORT_VAR="PORT_${{ matrix.cfengine }}"
PORT="${!PORT_VAR}"

PKG_DB="sqlite"

# Find packages with test directories
if [ ! -d "packages" ]; then
echo "No packages/ directory found, skipping"
exit 0
fi

PKG_FAIL=0
for pkg_dir in packages/*/; do
pkg_name=$(basename "$pkg_dir")
test_dir="${pkg_dir}tests"

if [ ! -d "$test_dir" ]; then
echo "Package '${pkg_name}' has no tests/ directory, skipping"
continue
fi

echo ""
echo "=============================================="
echo "Testing package: ${pkg_name} (${{ matrix.cfengine }} + ${PKG_DB})"
echo "=============================================="

# Activate: copy package to vendor/
cp -r "${pkg_dir}" "vendor/${pkg_name}"

# Restart engine for clean app state with the package loaded
docker restart wheels-${{ matrix.cfengine }}-1
WAIT=0
while [ "$WAIT" -lt 30 ]; do
WAIT=$((WAIT + 1))
if curl -s -o /dev/null --connect-timeout 2 --max-time 5 -w "%{http_code}" "http://localhost:${PORT}/" | grep -q "200\|404\|302"; then
break
fi
sleep 5
done
# Warm-up
curl -s -o /dev/null --max-time 60 "http://localhost:${PORT}/?reload=true" || true
sleep 2

# Run the package's tests
TEST_URL="http://localhost:${PORT}/wheels/core/tests?db=${PKG_DB}&format=json&directory=vendor.${pkg_name}.tests"
RESULT_FILE="/tmp/test-results/${{ matrix.cfengine }}-pkg-${pkg_name}.txt"

HTTP_CODE=$(curl -sL -o "$RESULT_FILE" \
--max-time 300 \
--write-out "%{http_code}" \
"$TEST_URL" || echo "000")

if [ "$HTTP_CODE" = "200" ]; then
PASS=$(python3 -c "import json; d=json.load(open('$RESULT_FILE')); print(d.get('totalPass',0))" 2>/dev/null || echo "?")
echo "PASSED: package '${pkg_name}' (${PASS} specs)"
else
echo "::warning::Package '${pkg_name}' tests returned HTTP ${HTTP_CODE}"
PKG_FAIL=1
fi

# Deactivate: remove from vendor/
rm -rf "vendor/${pkg_name}"
done

# Restart engine to restore clean state (no packages)
docker restart wheels-${{ matrix.cfengine }}-1
WAIT=0
while [ "$WAIT" -lt 30 ]; do
WAIT=$((WAIT + 1))
if curl -s -o /dev/null --connect-timeout 2 --max-time 5 -w "%{http_code}" "http://localhost:${PORT}/" | grep -q "200\|404\|302"; then
break
fi
sleep 5
done

if [ "$PKG_FAIL" -ne 0 ]; then
echo "::warning::One or more package test suites had issues (non-blocking)"
fi

- name: Generate per-engine summary
if: always()
run: |
Expand Down
31 changes: 18 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ app/migrator/migrations/ app/db/seeds.cfm app/db/seeds/
app/events/ app/global/ app/lib/
app/mailers/ app/jobs/ app/plugins/ app/snippets/
config/settings.cfm config/routes.cfm config/environment.cfm
packages/ plugins/ public/ tests/ vendor/ .env (never commit)
plugins/ public/ tests/ vendor/ .env (never commit)
```

## Development Tools
Expand Down Expand Up @@ -334,16 +334,19 @@ Strategies: `fixedWindow` (default), `slidingWindow`, `tokenBucket`. Storage: `m

## Package System

Optional first-party modules ship in `packages/` and are activated by copying to `vendor/`. The framework auto-discovers `vendor/*/package.json` on startup via `PackageLoader.cfc` with per-package error isolation.
Optional first-party modules are distributed as standalone repositories and installed into `vendor/<name>/`. The framework auto-discovers `vendor/*/package.json` on startup via `PackageLoader.cfc` with per-package error isolation.

Four first-party packages live in standalone repos under `wheels-dev/`, indexed by the `wheels-dev/wheels-packages` registry:

- `wheels-dev/wheels-sentry` — error tracking
- `wheels-dev/wheels-hotwire` — Turbo/Stimulus
- `wheels-dev/wheels-basecoat` — UI components
- `wheels-dev/wheels-legacy-adapter` — 3.x → 4.x compatibility shims

```
packages/ # Source/staging (NOT auto-loaded)
sentry/ # wheels-sentry — error tracking
hotwire/ # wheels-hotwire — Turbo/Stimulus
basecoat/ # wheels-basecoat — UI components
vendor/ # Runtime: framework core + activated packages
vendor/ # Runtime: framework core + installed packages
wheels/ # Framework core (excluded from package discovery)
sentry/ # Activated package (copied from packages/)
wheels-sentry/ # Installed package
plugins/ # DEPRECATED: legacy plugins still work with warning
```

Expand All @@ -367,14 +370,16 @@ plugins/ # DEPRECATED: legacy plugins still work with warning

**`provides.mixins`**: Comma-delimited targets from the allowlist `application,dispatch,controller,mapper,model,base,sqlserver,mysql,postgresql,h2,test`, plus the special values `global` (inject into all targets) and `none` (explicit opt-out). Determines which framework components receive the package's public methods. Default: `none` (explicit opt-in, unlike legacy plugins which default to `global`). Unknown targets (typos, `view`, `service`, etc.) are rejected with a clear error — view helpers belong in `controller` mixins since Wheels views execute in the controller's variables scope.

### Activating a Package
### Installing a Package

The Wheels 4.1 CLI will ship `wheels packages install <name>` which resolves names against the `wheels-dev/wheels-packages` registry. Until then, interim install is a manual clone:

```bash
cp -r packages/sentry vendor/sentry # activate
rm -rf vendor/sentry # deactivate
gh repo clone wheels-dev/wheels-sentry vendor/wheels-sentry # install
rm -rf vendor/wheels-sentry # remove
```

Restart or reload the app after activation. Symlinks also work: `ln -s ../../packages/sentry vendor/sentry`.
Restart or reload the app after install.

### Error Isolation

Expand All @@ -384,7 +389,7 @@ Each package loads in its own try/catch. A broken package is logged and skipped

```bash
# Run a specific package's tests (package must be in vendor/)
curl "http://localhost:60007/wheels/core/tests?db=sqlite&format=json&directory=vendor.sentry.tests"
curl "http://localhost:60007/wheels/core/tests?db=sqlite&format=json&directory=vendor.wheels-sentry.tests"
```

## Routing Quick Reference
Expand Down
6 changes: 4 additions & 2 deletions cli/lucli/templates/app/app/plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ Wheels 3.x used this directory for the plugin system. For v4.0 and beyond, plugi

## What to use instead

Copy a package from `packages/<name>` into `vendor/<name>` to activate it. Example:
Install a package into `vendor/<name>/`. First-party packages live under `wheels-dev/` on GitHub and are indexed by the [`wheels-dev/wheels-packages`](https://github.com/wheels-dev/wheels-packages) registry.

Wheels 4.1 will ship `wheels packages install wheels-hotwire`. Until then, the interim install is a manual clone:

```bash
cp -r packages/hotwire vendor/hotwire
gh repo clone wheels-dev/wheels-hotwire vendor/wheels-hotwire
wheels reload
```

Expand Down
Loading
Loading