Repository: netbox-community/netbox

Issue:
Webhooks fire erroneously when custom scripts fail causing a transaction rolback
### NetBox version

v3.2.4

### Python version

3.9

### Steps to Reproduce

* Register a webhook handler for the Contact object type.
* Run the following custom script:

```
from extras.scripts import Script
from tanancy.models import Contact

class MyScript(Script):
    def run(self, data, commit):
        contact = Contact(name="test")
        contact.full_clean()
        contact.save()
        raise Exception("Exception")
```

### Expected Behavior

No webhooks are sent as the script failed and the database transaction is rolled back.

### Observed Behavior

A weebhook is fired for the contact object.


Discussion:

Resolving patch:
diff --git a/netbox/extras/management/commands/runscript.py b/netbox/extras/management/commands/runscript.py
--- a/netbox/extras/management/commands/runscript.py
+++ b/netbox/extras/management/commands/runscript.py
@@ -14,6 +14,7 @@
 from extras.context_managers import change_logging
 from extras.models import JobResult
 from extras.scripts import get_script
+from extras.signals import clear_webhooks
 from utilities.exceptions import AbortTransaction
 from utilities.utils import NetBoxFakeRequest
 
@@ -49,7 +50,7 @@ def _run_script():
 
             except AbortTransaction:
                 script.log_info("Database changes have been reverted automatically.")
-
+                clear_webhooks.send(request)
             except Exception as e:
                 stacktrace = traceback.format_exc()
                 script.log_failure(
@@ -58,7 +59,7 @@ def _run_script():
                 script.log_info("Database changes have been reverted due to error.")
                 logger.error(f"Exception raised during script execution: {e}")
                 job_result.set_status(JobResultStatusChoices.STATUS_ERRORED)
-
+                clear_webhooks.send(request)
             finally:
                 job_result.data = ScriptOutputSerializer(script).data
                 job_result.save()
diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py
--- a/netbox/extras/scripts.py
+++ b/netbox/extras/scripts.py
@@ -17,6 +17,7 @@
 
 from extras.api.serializers import ScriptOutputSerializer
 from extras.choices import JobResultStatusChoices, LogLevelChoices
+from extras.signals import clear_webhooks
 from ipam.formfields import IPAddressFormField, IPNetworkFormField
 from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator, prefix_validator
 from utilities.exceptions import AbortTransaction
@@ -458,7 +459,7 @@ def _run_script():
 
         except AbortTransaction:
             script.log_info("Database changes have been reverted automatically.")
-
+            clear_webhooks.send(request)
         except Exception as e:
             stacktrace = traceback.format_exc()
             script.log_failure(
@@ -467,7 +468,7 @@ def _run_script():
             script.log_info("Database changes have been reverted due to error.")
             logger.error(f"Exception raised during script execution: {e}")
             job_result.set_status(JobResultStatusChoices.STATUS_ERRORED)
-
+            clear_webhooks.send(request)
         finally:
             job_result.data = ScriptOutputSerializer(script).data
             job_result.save()


Test patch: