Repository navigation
Back off compat to 1.2.29 and re-introduce dropped unique keys - #90
Merged
Merged
Conversation
- INFO: lower the minimum Cacti compat from 1.2.32 to 1.2.29. - Add webseer_reintroduce_unique_keys(), called after db_update_table() in webseer_upgrade_tables(). Cacti 1.2.29-1.2.31 shipped a db_update_table() that rebuilds indexes only from 'keys' and silently drops UNIQUE keys declared via 'unique_keys' (here plugin_webseer_contacts.user_id_type). On those releases only, re-add the key when it is missing; 1.2.32+ preserves it, so the helper is a no-op there. - Test the in-range re-add, the out-of-range no-ops, and the already-exists guard; make the db_index_exists bootstrap stub mockable and add a cacti_version_compare stub.
TheWitness
requested review from
bmfmancini,
browniebraun,
cigamit and
xmacan
and
a balanced review from Copilot
September 30, 2026 14:48
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Index-creation failures are not propagated, and required operator-facing release notes are missing.
Review effort: Balanced
Findings: 1
Open (4)
What changed in this PR
Lowers Cacti compatibility to 1.2.29 and repairs unique indexes dropped by affected Cacti releases.
Changes:
- Restores the contacts unique index on Cacti 1.2.29–1.2.31.
- Adds version-boundary and existing-index tests.
- Extends database test mocks.
| File | Description |
|---|---|
INFO |
Lowers minimum Cacti version. |
includes/database.php |
Adds unique-index repair logic. |
tests/bootstrap-unit.php |
Adds required test stubs. |
tests/Unit/ReintroduceUniqueKeysTest.php |
Tests repair behavior and version gates. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
added 7 commits
September 30, 2026 16:09
phpunit.xml ships in the repo for CI but is a dev-only test artifact, so it is removed from manifest.json 'expected', pruned from installs on upgrade (like tests/), and excluded from the manifest drift check. The retired markdown-lint configs (.mdlrc, .md_style.rb) are deleted, and .md* is now ignored like .git*: protected from pruning and excluded from drift if it reappears.
The upgrade-time prune now rejects any tombstone containing '.'/'..' segments (which could escape the plugin directory or resolve to its root) and treats ancestors of whitelist entries as protected, so a tombstone on a parent directory can no longer delete a whitelisted file beneath it.
The trailing '# ...' comments in the Project Structure block drifted further right down the tree; align them all to a single column.
- Use the full license header (from the plugin's own setup.php) in tests/Unit/PruneFilesTest.php instead of the abbreviated copyright banner. - Document that the manifest drift check and the upgrade-time prune also handle phpunit.xml and .md* files, matching the implemented behavior.
The repository naming contract reserves the plugin_<name>_ prefix for plugin lifecycle / hook-registration functions; all other functions use the plain <name>_ prefix. Rename the internal upgrade helpers accordingly: plugin_<name>_prune_files() -> <name>_prune_files() plugin_<name>_rmtree() -> <name>_rmtree() The call site, unit tests, and the copilot-instructions.md references are updated to match. No behavioral change.
cigamit
approved these changes
Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Three fleet-wide consistency changes for this plugin:
webseer_reintroduce_unique_keys(), called after thedb_update_table()loop inwebseer_upgrade_tables().manifest.json+ prune mechanism.Why the unique-key fix is needed
Cacti 1.2.29–1.2.31 shipped a
db_update_table()that rebuilds a table's indexes only from thekeysdefinition (honoring uniqueness via a per-key'unique'flag). It does not read a separateunique_keysarray, so an existing UNIQUE index declared that way isDROP INDEX'd and never re-added — a silent loss of uniqueness. webseer declaresplugin_webseer_contacts.user_id_type(user_id,type) viaunique_keys, so any upgrade run on those releases drops it. 1.2.32+ preserves it, so the helper is gated to>= 1.2.29 && < 1.2.32and is a no-op elsewhere. It also checksdb_index_exists()first, so it never double-adds.Show-stopper assessment (unique key)
(user_id, type)contact rows were subsequently inserted,ADD UNIQUE KEYwill fail on those databases. The helper is best-effort (guarded bydb_index_exists), but an operator with pre-existing duplicates will need to de-duplicate first.primary/non-uniquekeysonly.File manifest + upgrade pruning
manifest.jsonwithtombstones(empty — no relocations),expected(the current top-level tree; directories carry a trailing/), andwhitelist(empty — check results are stored in the database; the bundledca-bundle.crtships as stock code).webseer_prune_files()(insetup.php, called from the version-change branch ofplugin_webseer_upgrade()after theplugin_configupdate) removes the dev-onlytests/tree on upgrade, refuses any path resolving outside the plugin directory (a tamperedmanifest.json), warns in the Cacti log on any file/directory it cannot remove, leaveswhitelist/.git*alone, and logs — without removing — any unaccounted top-level entry.tests/bin/validate-manifest.php(wired intoplugin-ci-workflow.yml) fails on drift betweenexpectedand the real top-level tree.tests/Unit/PruneFilesTest.php.SetupHelpersGapsTestand the IntegrationPluginLifecycleTestsandboxbase_path(a temp tree with a copy ofINFO, an emptyincludes/database.php, and no manifest) for their upgrade-path cases so the prune is a no-op there while the new call line stays covered.Docs
README.md: added an Upgrading note —poller_webseer.phpis a long-lived background process, so operators should restart it after a file upgrade (Cacti's poller relaunches it) to pick up the new code; also notes the upgrade-time prune of bundled dev-only files.Validation
Full Pest suite green (61 passed, 315 assertions), patch-coverage 100% (79/79 changed lines) against
develop, manifest drift-check passes, and the translation template is unchanged.Revision: hardening & fleet cleanup
Since the initial description, this PR also:
phpunit.xmlon upgrade (alongside the dev-onlytests/tree) and leaves.md*lint configs in place — the drift check now ignorestests/,phpunit.xml,.git*,.md*, and whitelisted paths../..traversal segment or resolves outside the plugin directory (including via a symlinked directory), and protects a directory when a whitelisted entry lives beneath it — each with added unit coverage.webseer_prune_files()/webseer_rmtree()(theplugin_webseer_prefix is reserved for lifecycle/hook-registration functions).tests/Unit/PruneFilesTest.phpthe full standard GPL v2 header and aligns the Project Structure block in.github/copilot-instructions.md.