# Form Enhancer Pro

A WordPress plugin that extends **Fluent Forms** (and **Fluent SMTP**) with additional fields, integrations, and features — built with PHP 8.1+, PSR-4 autoloading, dependency injection, and scoped vendor dependencies.

> **Note:** The canonical, user-facing readme for the WordPress.org plugin directory is [`readme.txt`](readme.txt) — it holds the description, "Stable tag", and the official changelog, and the release pipeline parses it. This `README.md` is for developers working in the repo. End-user docs live at [formenhancer.com](https://formenhancer.com).

## Architecture

| Path | Purpose |
| --- | --- |
| `src/` | Free version classes (namespace `FormEnhancer\`) |
| `srcPro/` | Pro version classes (namespace `FormEnhancerPro\`) |
| `scoped/` | Manifest + lock for the shipped **free** dependencies (`composer.json`, `composer.lock`) |
| `srcDepsPro/` | Scoper runner for the Pro dependencies, with its manifest in `srcDepsPro/scoped/` (stripped at build) |
| `vendor/deps/` | Scoped free dependencies, built from `scoped/composer.lock` (gitignored) |
| `vendor/depsPro/` | Scoped Pro dependencies, built from `srcDepsPro/scoped/composer.lock` (gitignored) |
| `tests/unit/` | PHPUnit tests (WP_Mock) |

Pro features are conditionally loaded via Freemius `is__premium_only()` checks and the separate namespace/directory split. The DI container is created in `formenhancer_container()`.

See [`CLAUDE.md`](CLAUDE.md) for deeper architectural notes (conditional logic layers, integration conventions, Ecomail API specifics).

## Development

```bash
# Coding standards (WordPress + Eightshift)
composer standards:check
composer standards:fix

# Unit tests
composer test
composer test:report        # text coverage
composer test:report:html   # HTML coverage

# Pro scoped dependencies
composer install-deps       # build vendor/depsPro from srcDepsPro/scoped
composer update-deps        # regenerate HubSpot field list + update Pro deps

# Changelog + dependencies
composer changelog:check    # validate changelog.d/*.txt entry files
composer audit:deps         # security-audit both shipped (scoped) PHP trees

# JavaScript
npm run build               # production build
npm run start               # watch / dev mode
```

Requirements: PHP 8.1+, Node/npm, Composer.

## Changelog entries

**Never edit the `== Changelog ==` section of `readme.txt` in a pull request.**
Add `changelog.d/<slug>.txt` instead — one file per branch, so parallel PRs
never conflict on the changelog. The rules, including how Pro-only bullets are
kept out of the WordPress.org changelog, are in
[`changelog.d/README.md`](changelog.d/README.md).

## Continuous integration

| Workflow | When | What |
| --- | --- | --- |
| `ci.yml` | every push to `main` and every PR | PHP syntax lint and PHPUnit on PHP 8.1 and 8.4; PHPCS, `changelog.d` validation and the shipped-dependency audit on 8.1 |
| `changelog-unreleased.yml` | `changelog.d/*.txt` lands on `main` | folds the entries into `readme.txt` and commits the result |
| `dependabot-auto-merge.yml` | CI finishes | squash-merges green `*-minor` Dependabot group PRs and deletes the branch |
| `security-audit.yml` | Mondays 06:00 UTC, or on demand | `composer audit` on the dev toolchain and on both shipped dependency trees, `npm audit` on the build toolchain |
| `delete-merged-branch.yml` | a PR is merged | deletes the head branch — every merged PR, not just Dependabot's |

Dependabot is scoped to production dependencies, with version bumps for the dev
toolchain switched off and security updates left on. See
[Dependencies & security](docs/agents/dependencies.md) for what each tree gets
and why, and [`.github/dependabot.yml`](.github/dependabot.yml) for the config
itself.

## Versioning

The version lives in **three** places that must stay in sync:

- `formenhancer.php` plugin header (`Version:`)
- `formenhancer.php` constant (`define('FORMENHANCER_VERSION', …)`)
- `readme.txt` (`Stable tag:`)

`make version-bump` updates all three at once (auto-suggests the next patch, accepts a custom value such as `1.4.0-alpha`). Pre-release versions with a `-suffix` intentionally leave `Stable tag` unchanged.

## Release workflow

The release is orchestrated through the `Makefile` and `scripts/`:

### 1. `make version-bump` — `scripts/version-bump.sh`
Bumps and synchronizes the version across the three sources above, then calls
`scripts/changelog-fold-entries.php --release <version>` to rename the
changelog's `= Unreleased =` block to `= <version> (<today>) =`. That step
refuses while `changelog.d/` still holds unfolded entry files.

### 2. `make release` — `scripts/release.sh`
Builds the distributable zip at `build/formenhancer-<version>.zip`:
- runs `npm run build`
- copies the plugin to a temp dir, excluding dev-only files (`.git`, `scripts`, `vendor`, `node_modules`, `tests`, `.claude`, `CLAUDE.md`, `docs`, `Makefile`, `.env`, …)
- runs `composer install` (the dev install is what runs wpify/scoper and builds `vendor/deps`),
  then `composer run install-deps` for `vendor/depsPro`, then `composer install --no-dev` to strip the toolchain
- strips files disallowed by WordPress.org (vendor `bin/`, `tests/`, `examples/`) and removes `srcDepsPro`, `scoped`, `package.json`, `phpcs.xml`, `phpunit.xml`
- zips the result into `build/`

### 3. `make release-deploy` — `scripts/release-deploy.sh`
The full pipeline. Must run on `main` with a clean working tree.
1. Reconciles the three version sources (warns on mismatch).
2. Pushes `main`, then creates and pushes the git tag `v<version>`.
3. Builds the zip (`release.sh`).
4. Generates GitHub release notes by extracting the version's changelog block from `readme.txt` and formatting it via `claude -p`.
5. Creates the GitHub release (`gh release create`) with the zip attached.
6. Prompts for the **Freemius** release mode (beta / production / skip) and uploads via the Freemius PHP SDK, with secrets injected from **1Password** (`op run --env-file .env`).
7. On a **production** release, optionally deploys the free/stripped build to **WordPress.org SVN** (waits ~60s for Freemius to build the free version, then runs `wporg-deploy.sh`).

`scripts/release-deploy.sh --freemius-only` skips git/GitHub and re-uploads an existing zip (useful for testing the Freemius step).

### 4. `make wporg-deploy` — `scripts/wporg-deploy.sh`
Pushes the free version to the WordPress.org SVN repository.

### Prerequisites
- [1Password CLI](https://developer.1password.com/docs/cli/) (`op`) installed and signed in
- `.env` file with `op://` secret references (Freemius dev/public/secret keys, plugin ID)
- [GitHub CLI](https://cli.github.com/) (`gh`) authenticated
- `claude` CLI (for formatting release notes)
- `freemius/php-sdk` dev dependency (`composer require --dev freemius/php-sdk`)

## License

GPLv3 or later.
