Skip to content
Open
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
104 changes: 90 additions & 14 deletions .github/workflows/qa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,19 @@ jobs:
run: php scripts/build-release.php
- name: Test ZIP integrity
run: |
unzip -t dist/codegenie-pulse-connector-1.2.1.zip
unzip -t dist/codegenie-pulse-connector-wordpress-1.2.1-source.zip
unzip -t dist/codegenie-pulse-connector-1.2.2.zip
unzip -t dist/codegenie-pulse-connector-wordpress-1.2.2-source.zip
- name: Verify reproducibility
run: |
first_install="$(sha256sum dist/codegenie-pulse-connector-1.2.1.zip | cut -d ' ' -f 1)"
first_source="$(sha256sum dist/codegenie-pulse-connector-wordpress-1.2.1-source.zip | cut -d ' ' -f 1)"
first_install="$(sha256sum dist/codegenie-pulse-connector-1.2.2.zip | cut -d ' ' -f 1)"
first_source="$(sha256sum dist/codegenie-pulse-connector-wordpress-1.2.2-source.zip | cut -d ' ' -f 1)"
php scripts/build-release.php
second_install="$(sha256sum dist/codegenie-pulse-connector-1.2.1.zip | cut -d ' ' -f 1)"
second_source="$(sha256sum dist/codegenie-pulse-connector-wordpress-1.2.1-source.zip | cut -d ' ' -f 1)"
second_install="$(sha256sum dist/codegenie-pulse-connector-1.2.2.zip | cut -d ' ' -f 1)"
second_source="$(sha256sum dist/codegenie-pulse-connector-wordpress-1.2.2-source.zip | cut -d ' ' -f 1)"
test "$first_install" = "$second_install"
test "$first_source" = "$second_source"
- name: Extract built package for Plugin Check
run: unzip -q dist/codegenie-pulse-connector-1.2.1.zip -d plugin-build
run: unzip -q dist/codegenie-pulse-connector-1.2.2.zip -d plugin-build
- name: Run official Plugin Check against built package
uses: wordpress/plugin-check-action@v1
with:
Expand All @@ -118,13 +118,89 @@ jobs:
- name: Upload reviewed installation artifact
uses: actions/upload-artifact@v4
with:
name: codegenie-pulse-connector-1.2.1-release-candidate
name: codegenie-pulse-connector-1.2.2-release-candidate
path: |
dist/codegenie-pulse-connector-1.2.1.zip
dist/codegenie-pulse-connector-1.2.1.files.txt
dist/codegenie-pulse-connector-1.2.1.sha256
dist/codegenie-pulse-connector-wordpress-1.2.1-source.zip
dist/codegenie-pulse-connector-wordpress-1.2.1-source.files.txt
dist/codegenie-pulse-connector-wordpress-1.2.1-source.sha256
dist/codegenie-pulse-connector-1.2.2.zip
dist/codegenie-pulse-connector-1.2.2.files.txt
dist/codegenie-pulse-connector-1.2.2.sha256
dist/codegenie-pulse-connector-wordpress-1.2.2-source.zip
dist/codegenie-pulse-connector-wordpress-1.2.2-source.files.txt
dist/codegenie-pulse-connector-wordpress-1.2.2-source.sha256
if-no-files-found: error
retention-days: 14

release:
name: Publish GitHub release
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: package
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Check out source
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: zip
coverage: none
tools: none
- name: Resolve release identity
id: release
env:
VERSION: '1.2.2'
run: |
set -euo pipefail
php scripts/check-release-version.php "$VERSION"
tag="v$VERSION"
git fetch --tags --force

if git rev-parse "$tag" >/dev/null 2>&1; then
tagged_commit="$(git rev-list --max-count=1 "$tag")"

if [[ "$tagged_commit" != "$GITHUB_SHA" ]]; then
echo "Version $tag is already released at $tagged_commit; no new release is required for this commit."
echo "publish=false" >> "$GITHUB_OUTPUT"
exit 0
fi
else
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$tag" "$GITHUB_SHA" -m "$tag"
git push origin "$tag"
fi

echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "publish=true" >> "$GITHUB_OUTPUT"
- name: Rebuild reviewed release artifacts
if: steps.release.outputs.publish == 'true'
run: php scripts/build-release.php
- name: Create or repair GitHub release
if: steps.release.outputs.publish == 'true'
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.release.outputs.tag }}
run: |
set -euo pipefail
assets=(
dist/codegenie-pulse-connector-1.2.2.zip
dist/codegenie-pulse-connector-1.2.2.files.txt
dist/codegenie-pulse-connector-1.2.2.sha256
dist/codegenie-pulse-connector-wordpress-1.2.2-source.zip
dist/codegenie-pulse-connector-wordpress-1.2.2-source.files.txt
dist/codegenie-pulse-connector-wordpress-1.2.2-source.sha256
)

if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release upload "$TAG" "${assets[@]}" --repo "$GITHUB_REPOSITORY" --clobber
exit 0
fi

gh release create "$TAG" "${assets[@]}" \
--repo "$GITHUB_REPOSITORY" \
--target "$TAG" \
--title "$TAG" \
--notes-file docs/releases/1.2.2.md
Comment on lines +202 to +206

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require explicit authorization before publishing the release

On any push of this commit to main, this job creates and pushes v1.2.2 and then executes gh release create without --draft; gh release create --help states that this form publishes the release after uploading its assets. This turns the generic QA/build workflow into an automatic publishing path instead of leaving tagging and publication as separately authorized maintainer actions.

AGENTS.md reference: AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

2 changes: 1 addition & 1 deletion .github/workflows/release-prepare.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
version:
description: Exact release version without the v prefix
required: true
default: '1.2.1'
default: '1.2.2'
type: string

permissions:
Expand Down
4 changes: 2 additions & 2 deletions codegenie-pulse-connector.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* Plugin Name: Codegenie Pulse Connector
* Description: Verbind WordPress veilig met Codegenie Pulse voor foutmonitoring, websiteverificatie en deployment tracking.
* Version: 1.2.1
* Version: 1.2.2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update README.md with the new public version contract

The runtime now declares version 1.2.2, but README.md still identifies the plugin, runtime overview, artifact filenames, release notes, workflow input, and tag as 1.2.1. Since the source archive includes that README, consumers and maintainers receive a contradictory public release contract unless it is updated with the rest of the version bump.

AGENTS.md reference: AGENTS.md:L5-L5

Useful? React with 👍 / 👎.

* Requires at least: 6.2
* Requires PHP: 7.4
* Author: Codegenie
Expand All @@ -16,7 +16,7 @@
exit;
}

define( 'CODEGENIE_PULSE_CONNECTOR_VERSION', '1.2.1' );
define( 'CODEGENIE_PULSE_CONNECTOR_VERSION', '1.2.2' );
define( 'CODEGENIE_PULSE_CONNECTOR_FILE', __FILE__ );
define( 'CODEGENIE_PULSE_CONNECTOR_DIR', plugin_dir_path( __FILE__ ) );

Expand Down
53 changes: 53 additions & 0 deletions docs/releases/1.2.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Codegenie Pulse Connector 1.2.2

## Summary

Codegenie Pulse Connector 1.2.2 is a focused delivery-reliability maintenance release. It treats HTTP 408 Request Timeout and HTTP 425 Too Early as temporary responses and applies the connector's existing bounded one-minute local backoff before another automatic delivery attempt.

## Changes

- Added local backoff after HTTP 408 responses.
- Added local backoff after HTTP 425 responses.
- Added specific privacy-safe administrator messages for both temporary response classes.
- Added regression coverage proving that the next automatic event is suppressed during the backoff window.

## Compatibility

- WordPress 6.2 or newer.
- Tested by the repository matrix with WordPress 6.2.0 and 7.0.2.
- PHP 7.4 or newer, including the repository's PHP 7.4, 8.3 and 8.5 quality matrix.
- Requires PHP OpenSSL support for AES-256-GCM.
- Plugin directory, installation slug and text domain: `codegenie-pulse-connector`.
- Connector ID: `codegenie-pulse-connector-wordpress`; protocol version 1.

## Upgrade behaviour

There is no database-schema migration and there are no option-name changes. Existing encrypted DSN, verification, plan capability and capture-mode settings remain unchanged. The release only extends the existing local delivery backoff classification.

## Security and privacy

- No additional data is collected, persisted or transmitted.
- No payload format, endpoint or authentication contract changes.
- Backoff state remains local, short-lived and separated by event kind.
- Result messages remain bounded and do not expose remote response bodies, DSNs, tokens or event content.

## Known limitations

- The plugin has no queue and does not replay an event that was rejected or lost.
- Backoff protects the local site and remote endpoint from repeated delivery attempts, but it does not guarantee eventual delivery.
- WordPress.org publication still requires the approved human-created brand assets and synthetic screenshots documented by the repository asset manifest.

## Artifacts

The tag-triggered release preparation workflow builds deterministic installation and reviewable source ZIP files, verifies both archives, records SHA-256 sidecars and proves reproducibility before uploading the prepared artifacts.

Expected filenames:

- `codegenie-pulse-connector-1.2.2.zip`
- `codegenie-pulse-connector-wordpress-1.2.2-source.zip`

## Rollback and support

For rollback, deactivate 1.2.2 and reinstall the reviewed 1.2.1 package without uninstalling first. Uninstall deliberately removes settings and secrets and is therefore not a rollback mechanism.

Use the repository's normal support channel for non-sensitive issues. Report vulnerabilities through GitHub private vulnerability reporting as described in `SECURITY.md`; never post DSNs, tokens, personal data or private logs publicly.
6 changes: 5 additions & 1 deletion includes/class-codegenie-pulse-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private function send( $endpoint, $payload, $kind, $bypass_backoff ) {
return $this->record_success( $kind, $status );
}

if ( 429 === $status ) {
if ( in_array( $status, array( 408, 425, 429 ), true ) ) {
$this->start_backoff( $kind, MINUTE_IN_SECONDS );
} elseif ( in_array( $status, array( 401, 403, 404, 410 ), true ) ) {
$this->start_backoff( $kind, 15 * MINUTE_IN_SECONDS );
Expand Down Expand Up @@ -236,6 +236,8 @@ private function http_error_message( $status ) {
return __( 'Het account of plan laat deze koppeling niet toe.', 'codegenie-pulse-connector' );
case 404:
return __( 'Het ingestie-endpoint of de token werd niet gevonden.', 'codegenie-pulse-connector' );
case 408:
return __( 'De aanvraag is tijdelijk verlopen. De connector probeert later opnieuw.', 'codegenie-pulse-connector' );
Comment on lines +239 to +240

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid promising an automatic retry that never occurs

After a 408 response, the rejected event is returned to the caller and discarded; start_backoff() only suppresses subsequent events and there is no queue or scheduled retry. Telling administrators that the connector will try again later is therefore inaccurate, as is the same new wording for 425, and should instead describe the temporary delivery pause.

Useful? React with 👍 / 👎.

case 410:
return __( 'De gekoppelde website of foutbron is gearchiveerd.', 'codegenie-pulse-connector' );
case 411:
Expand All @@ -244,6 +246,8 @@ private function http_error_message( $status ) {
return __( 'De gebeurtenis is groter dan de toegestane platformlimiet.', 'codegenie-pulse-connector' );
case 422:
return __( 'Het platform heeft de gebeurtenis inhoudelijk geweigerd.', 'codegenie-pulse-connector' );
case 425:
return __( 'Het platform heeft de aanvraag tijdelijk uitgesteld. De connector probeert later opnieuw.', 'codegenie-pulse-connector' );
case 429:
return __( 'De ingestie- of maandlimiet is bereikt. De connector probeert later opnieuw.', 'codegenie-pulse-connector' );
default:
Expand Down
14 changes: 12 additions & 2 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Tags: monitoring, error monitoring, uptime, fatal errors, deployment tracking
Requires at least: 6.2
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.2.1
Stable tag: 1.2.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Expand Down Expand Up @@ -71,7 +71,7 @@ The DSN can no longer be decrypted. Paste the DSN into the connector settings ag

= Are events retried? =

The plugin has no queue and does not automatically retry events. It applies a short local backoff after network, rate-limit, or token failures to prevent an error storm.
The plugin has no queue and does not automatically retry events. It applies a short local backoff after network, temporary HTTP, rate-limit, or token failures to prevent an error storm.

= Which PHP error capture mode should I use? =

Expand All @@ -95,6 +95,10 @@ The public discovery endpoint returns only connector information and a one-time

== Upgrade Notice ==

= 1.2.2 =

Adds bounded local backoff for temporary HTTP 408 and 425 responses so error bursts do not repeatedly hit an endpoint that has asked the connector to wait.

= 1.2.1 =

Clarifies the actual data transfer and makes one-time provisioning and multisite network activation more fault tolerant without changing the connector protocol.
Expand All @@ -109,6 +113,12 @@ Adds safe optional capture of PHP warnings, notices, and deprecated messages. Ex

== Changelog ==

= 1.2.2 =

* Treats HTTP 408 Request Timeout and HTTP 425 Too Early as temporary delivery failures.
* Applies the existing one-minute local backoff after those responses.
* Adds regression coverage proving that follow-up events are suppressed during the bounded backoff window.

= 1.2.1 =

* Corrected consent copy without a claim about an inventory of installed plugin versions.
Expand Down
2 changes: 1 addition & 1 deletion scripts/build-release.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

$root = dirname( __DIR__ );
$allow_dirty = in_array( '--allow-dirty', $argv, true );
$version = '1.2.1';
$version = '1.2.2';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the package verifier for version 1.2.2

Building this version always fails because scripts/build-release.php passes the generated archive to scripts/verify-package.php, whose lines 81–86 still require the plugin header, constant, and stable tag to be 1.2.1. I confirmed php scripts/build-release.php exits with both version errors, so the package job cannot reach artifact inspection or release.

AGENTS.md reference: AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

$slug = 'codegenie-pulse-connector';
$source_slug = 'codegenie-pulse-connector-wordpress-' . $version . '-source';
$dist = $root . '/dist';
Expand Down
8 changes: 4 additions & 4 deletions scripts/check-versions.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

$checks = array(
'plugin name' => array( $main, '/^ \* Plugin Name:\s+Codegenie Pulse Connector\s*$/m' ),
'plugin version' => array( $main, '/^ \* Version:\s+1\.2\.1\s*$/m' ),
'version constant' => array( $main, "/define\( 'CODEGENIE_PULSE_CONNECTOR_VERSION', '1\.2\.1' \);/" ),
'plugin version' => array( $main, '/^ \* Version:\s+1\.2\.2\s*$/m' ),
'version constant' => array( $main, "/define\( 'CODEGENIE_PULSE_CONNECTOR_VERSION', '1\.2\.2' \);/" ),
'minimum WordPress' => array( $main, '/^ \* Requires at least:\s+6\.2\s*$/m' ),
'minimum PHP' => array( $main, '/^ \* Requires PHP:\s+7\.4\s*$/m' ),
'text domain' => array( $main, '/^ \* Text Domain:\s+codegenie-pulse-connector\s*$/m' ),
'stable tag' => array( $readme, '/^Stable tag:\s+1\.2\.1\s*$/m' ),
'stable tag' => array( $readme, '/^Stable tag:\s+1\.2\.2\s*$/m' ),
'readme WordPress' => array( $readme, '/^Requires at least:\s+6\.2\s*$/m' ),
'readme PHP' => array( $readme, '/^Requires PHP:\s+7\.4\s*$/m' ),
'tested WordPress' => array( $readme, '/^Tested up to:\s+7\.0\s*$/m' ),
Expand All @@ -41,4 +41,4 @@
exit( 1 );
}

echo "Version contracts OK: 1.2.1, WordPress 6.2-7.0.2, PHP 7.4+, connector ID unchanged.\n";
echo "Version contracts OK: 1.2.2, WordPress 6.2-7.0.2, PHP 7.4+, connector ID unchanged.\n";
15 changes: 10 additions & 5 deletions scripts/install-wp-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,19 @@ if [[ ! -d "$WP_CORE_DIR/wp-admin" ]]; then
rm -f "$tmp_archive"
fi

if [[ ! -d "$WP_TESTS_DIR/includes" ]]; then
if [[ ! -d "$WP_TESTS_DIR/includes" || ! -d "$WP_TESTS_DIR/data" || ! -f "$WP_TESTS_DIR/wp-tests-config-sample.php" ]]; then
tmp_develop_archive="$(mktemp)"
tmp_develop_dir="$(mktemp -d)"
download "https://github.com/WordPress/wordpress-develop/archive/refs/tags/${WP_VERSION}.tar.gz" "$tmp_develop_archive"
tar --strip-components=1 -xzf "$tmp_develop_archive" -C "$tmp_develop_dir"
mkdir -p "$WP_TESTS_DIR"
svn export --quiet --force "https://develop.svn.wordpress.org/tags/${WP_VERSION}/tests/phpunit/includes/" "$WP_TESTS_DIR/includes"
svn export --quiet --force "https://develop.svn.wordpress.org/tags/${WP_VERSION}/tests/phpunit/data/" "$WP_TESTS_DIR/data"
rm -rf "$WP_TESTS_DIR/includes" "$WP_TESTS_DIR/data"
cp -R "$tmp_develop_dir/tests/phpunit/includes" "$WP_TESTS_DIR/includes"
cp -R "$tmp_develop_dir/tests/phpunit/data" "$WP_TESTS_DIR/data"
cp "$tmp_develop_dir/wp-tests-config-sample.php" "$WP_TESTS_DIR/wp-tests-config-sample.php"
rm -rf "$tmp_develop_archive" "$tmp_develop_dir"
fi

svn export --quiet --force "https://develop.svn.wordpress.org/tags/${WP_VERSION}/wp-tests-config-sample.php" "$WP_TESTS_DIR/wp-tests-config-sample.php"

sed \
-e "s/youremptytestdbnamehere/${DB_NAME}/" \
-e "s/yourusernamehere/${DB_USER}/" \
Expand Down
8 changes: 7 additions & 1 deletion tests/unit/ClientReporterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,13 @@ public function test_previous_error_handler_exception_is_not_swallowed() {
public function test_removed_strict_level_is_not_registered_on_php_84_or_newer() {
$options = $this->configuredOptions( array( 'error_capture_mode' => Codegenie_Pulse_Options::CAPTURE_DEBUG ) );
$reporter = new Codegenie_Pulse_Reporter( new Codegenie_Pulse_Client( $options ), $options, new Codegenie_Pulse_Redactor() );
$reporter->capture_php_error( 2048, 'legacy strict level', __FILE__, 1 );
$previous_error_reporting = error_reporting( E_ALL );

try {
$reporter->capture_php_error( 2048, 'legacy strict level', __FILE__, 1 );
} finally {
error_reporting( $previous_error_reporting );
}

if ( PHP_VERSION_ID >= 80400 ) {
$this->assertCount( 0, $GLOBALS['codegenie_test']['remote_calls'] );
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/ClientTransientBackoffTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

final class ClientTransientBackoffTest extends Codegenie_Pulse_Test_Case {
public function test_transient_http_responses_start_bounded_backoff() {
foreach ( array( 408, 425 ) as $status ) {
codegenie_test_reset();
$options = $this->configuredOptions();
$client = new Codegenie_Pulse_Client( $options );
$GLOBALS['codegenie_test']['remote_result'] = array(
'response' => array( 'code' => $status ),
'body' => '{}',
);

$result = $client->send_error( array( 'message' => 'synthetic' ) );
$key = Codegenie_Pulse_Options::BACKOFF_KEY . '_error';

$this->assertSame( 'http_' . $status, $result['code'] );
$this->assertArrayHasKey( $key, $GLOBALS['codegenie_test']['transients'] );
$this->assertSame( MINUTE_IN_SECONDS, $GLOBALS['codegenie_test']['transients'][ $key ]['expiration'] );
$this->assertSame( 'backoff', $client->send_error( array( 'message' => 'followup' ) )['code'] );
$this->assertCount( 1, $GLOBALS['codegenie_test']['remote_calls'] );
}
}
}
2 changes: 1 addition & 1 deletion tests/unit/DeploymentUninstallTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public function test_main_plugin_file_loads_and_activation_has_no_fatal_error()
exec( escapeshellarg( PHP_BINARY ) . ' ' . escapeshellarg( dirname( __DIR__ ) . '/fixtures/plugin-activation-runner.php' ) . ' 2>&1', $output, $status );
$this->assertSame( 0, $status, implode( "\n", $output ) );
$result = json_decode( implode( "\n", $output ), true );
$this->assertSame( '1.2.1', $result['version'] );
$this->assertSame( '1.2.2', $result['version'] );
$this->assertSame( 'no', $result['settings_autoload'] );
$this->assertSame( 'no', $result['state_autoload'] );
}
Expand Down
Loading
Loading