From 88a0d4928c1e92aaf6976e41ebb64c5e0ba4a168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?batuhan=20i=C3=A7=C3=B6z?= Date: Fri, 30 Oct 2026 03:23:49 +0000 Subject: [PATCH] Trigger headless builds through the current build API Spacefast replaced POST/GET /builds/wordpress with POST /builds {source: "wordpress"} and GET /builds/preflight, and renamed /uploads/resume to /uploads/refresh. 0.5.8 still called the removed routes, so headless rebuilds never started. Static publishing only kept working through a server-side alias. The behavior suite now pins exact method, URL, query, and body for the preflight, trigger, and upload refresh calls. The old suffix checks let the drift pass. Co-Authored-By: Claude Opus 5.5 --- includes/class-spacefast-client.php | 9 +++-- includes/class-spacefast-static-publisher.php | 4 +- readme.txt | 6 ++- spacefast-wordpress.php | 4 +- tests/acceptance/wordpress.php | 2 +- tests/behavior.php | 37 ++++++++++++------- 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/includes/class-spacefast-client.php b/includes/class-spacefast-client.php index a83295b..e0a74cc 100644 --- a/includes/class-spacefast-client.php +++ b/includes/class-spacefast-client.php @@ -204,7 +204,7 @@ public function repository_health(): array { $source_url = rawurlencode( untrailingslashit( home_url() ) ); return $this->request( 'GET', - "/v1/spaces/{$space_id}/builds/wordpress?sourceUrl={$source_url}" + "/v1/spaces/{$space_id}/builds/preflight?source=wordpress&sourceUrl={$source_url}" ); } @@ -230,8 +230,9 @@ public function trigger_build( string $event_id ): array { $settings = Spacefast_Settings::get(); $result = $this->request( 'POST', - '/v1/spaces/' . rawurlencode( $settings['space_id'] ) . '/builds/wordpress', + '/v1/spaces/' . rawurlencode( $settings['space_id'] ) . '/builds', array( + 'source' => 'wordpress', 'sourceUrl' => untrailingslashit( home_url() ), ), array( 'Idempotency-Key' => $event_id ) @@ -300,12 +301,12 @@ public function create_static_version( array $files, string $event_id, string $p /** * @return array{ok:bool,retryable:bool,code:string,message:string,data:array} */ - public function resume_static_upload( string $version_id ): array { + public function refresh_static_upload( string $version_id ): array { $settings = Spacefast_Settings::get(); return $this->request( 'POST', '/v1/spaces/' . rawurlencode( $settings['space_id'] ) . '/versions/' - . rawurlencode( $version_id ) . '/uploads/resume' + . rawurlencode( $version_id ) . '/uploads/refresh' ); } diff --git a/includes/class-spacefast-static-publisher.php b/includes/class-spacefast-static-publisher.php index 0e24c12..7a73ccd 100644 --- a/includes/class-spacefast-static-publisher.php +++ b/includes/class-spacefast-static-publisher.php @@ -236,7 +236,7 @@ private static function resume( array $state, Spacefast_Client $client ): array if ( $pages > 10000 ) { throw new RuntimeException( 'Spacefast returned too many upload instruction pages.' ); } - $result = $client->resume_static_upload( (string) $state['version_id'] ); + $result = $client->refresh_static_upload( (string) $state['version_id'] ); if ( ! $result['ok'] ) { return self::retry_or_throw( $state, $result, 'uploading' ); } @@ -294,7 +294,7 @@ private static function poll_version( array $state, Spacefast_Client $client ): // refresh runs the same completion the runtime's upload callback runs, // so a lost callback cannot strand the version; when some are, upload // them before waiting. - $refresh = $client->resume_static_upload( (string) $state['version_id'] ); + $refresh = $client->refresh_static_upload( (string) $state['version_id'] ); if ( ! $refresh['ok'] && $refresh['retryable'] ) { return self::retry_or_throw( $state, $refresh, 'finalizing' ); } diff --git a/readme.txt b/readme.txt index a4ceb22..a188e6f 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: spacefast Tags: static site, headless cms, simply static, deployment Requires at least: 6.5 Requires PHP: 8.1 -Stable tag: 0.5.8 +Stable tag: 0.5.9 License: GPLv2 or later Publish a Simply Static export to Spacefast, or rebuild a headless site when WordPress content changes. @@ -31,6 +31,10 @@ OAuth access is limited to the Team you authorize and the mode you choose. WordP == Changelog == += 0.5.9 = +* Trigger headless builds and check the WordPress source through the current Spacefast build API. 0.5.8 called routes Spacefast removed, so headless rebuilds never started. +* Page static upload instructions through the current upload refresh route. + = 0.5.8 = * Retry transient upload and upload-refresh failures in place, with exponential backoff for about four minutes, instead of restarting the whole export as a new version. * Once every file has uploaded, ask Spacefast which files it still lacks, upload any it names, and let that check trigger activation instead of depending on the upload-completion signal alone. diff --git a/spacefast-wordpress.php b/spacefast-wordpress.php index 8b775fd..cb75c7e 100644 --- a/spacefast-wordpress.php +++ b/spacefast-wordpress.php @@ -3,7 +3,7 @@ * Plugin Name: Spacefast * Plugin URI: https://spacefast.com/ * Description: Publishes static WordPress exports or rebuilds a headless Spacefast site. - * Version: 0.5.8 + * Version: 0.5.9 * Update URI: https://github.com/spacefast/wordpress * Requires at least: 6.5 * Requires PHP: 8.1 @@ -14,7 +14,7 @@ defined( 'ABSPATH' ) || exit; -define( 'SPACEFAST_WORDPRESS_VERSION', '0.5.8' ); +define( 'SPACEFAST_WORDPRESS_VERSION', '0.5.9' ); define( 'SPACEFAST_WORDPRESS_FILE', __FILE__ ); require_once __DIR__ . '/includes/class-spacefast-settings.php'; diff --git a/tests/acceptance/wordpress.php b/tests/acceptance/wordpress.php index 4572378..28299fe 100644 --- a/tests/acceptance/wordpress.php +++ b/tests/acceptance/wordpress.php @@ -113,7 +113,7 @@ function spacefast_accept( bool $condition, string $message ): void { ); $plugin = get_plugin_data( WP_PLUGIN_DIR . '/spacefast-wordpress/spacefast-wordpress.php', false, false ); -spacefast_accept( '0.5.8' === $plugin['Version'], 'Unexpected plugin version.' ); +spacefast_accept( '0.5.9' === $plugin['Version'], 'Unexpected plugin version.' ); spacefast_accept( 'https://github.com/spacefast/wordpress' === $plugin['UpdateURI'], 'Update URI is missing.' ); Spacefast_Settings::disconnect(); diff --git a/tests/behavior.php b/tests/behavior.php index 468f448..786d7a3 100644 --- a/tests/behavior.php +++ b/tests/behavior.php @@ -465,7 +465,7 @@ static function ( string $url, array $args ) use ( &$revoke_requests ): array { $connection_requests = array(); $GLOBALS['spacefast_remote_handler'] = static function ( string $url, array $args ) use ( &$connection_requests ): array { $connection_requests[] = array( $url, $args ); - if ( 'POST' === $args['method'] && str_ends_with( $url, '/builds/wordpress' ) ) { + if ( 'POST' === $args['method'] && str_ends_with( $url, '/builds' ) ) { return response( 201, array( 'build' => array( 'id' => 'bld_new_space', 'status' => 'queued' ) ) ); } return response( 200, array( 'ready' => true ) ); @@ -783,6 +783,11 @@ static function ( string $url, array $args ): array { $result = ( new Spacefast_Client( $transport ) )->trigger_build( 'wp-event-123' ); check( true === $result['ok'], 'healthy connection triggers a build' ); check( 2 === count( $requests ), 'verifies the source before the build' ); +check( + Spacefast_Settings::api_url() . '/v1/spaces/spc_demo/builds/preflight?source=wordpress&sourceUrl=https%3A%2F%2Fwp.example.test' === $requests[0][0] + && 'GET' === $requests[0][1]['method'], + 'checks the WordPress source through build preflight' +); $build_request = $requests[1][1]; check( 'wp-event-123' === $build_request['headers']['Idempotency-Key'], @@ -790,11 +795,12 @@ static function ( string $url, array $args ): array { ); $build_body = json_decode( $build_request['body'], true ); check( - 'https://wp.example.test' === $build_body['sourceUrl'], - 'sends only the configured WordPress source' + array( 'source' => 'wordpress', 'sourceUrl' => 'https://wp.example.test' ) === $build_body, + 'names the managed WordPress source and nothing else' ); check( - str_ends_with( $requests[1][0], '/builds/wordpress' ), + Spacefast_Settings::api_url() . '/v1/spaces/spc_demo/builds' === $requests[1][0] + && 'POST' === $build_request['method'], 'uses the constrained server-side trigger' ); check( @@ -932,9 +938,9 @@ static function ( string $url, array $args ) use ( &$static_requests ): array { ) ); } - if ( str_contains( $url, '/uploads/resume' ) ) { + if ( str_contains( $url, '/uploads/refresh' ) ) { // The completion check after the last page finds nothing missing. - if ( count( array_filter( $static_requests, static fn ( array $request ): bool => str_contains( $request[0], '/uploads/resume' ) ) ) > 1 ) { + if ( count( array_filter( $static_requests, static fn ( array $request ): bool => str_contains( $request[0], '/uploads/refresh' ) ) ) > 1 ) { return response( 200, array( 'upload' => null ) ); } return response( @@ -953,7 +959,7 @@ static function ( string $url, array $args ) use ( &$static_requests ): array { 'headers' => array( 'X-Upload' => 'signed-2' ), ), ), - 'links' => array( 'resume' => '/resume', 'finalize' => '/finalize' ), + 'links' => array( 'refresh' => '/refresh', 'finalize' => '/finalize' ), ), ) ); @@ -975,7 +981,7 @@ static function ( string $url, array $args ) use ( &$static_requests ): array { 'headers' => array( 'X-Upload' => 'signed' ), ), ), - 'links' => array( 'resume' => '/resume', 'finalize' => '/finalize' ), + 'links' => array( 'refresh' => '/refresh', 'finalize' => '/finalize' ), ), ) ); @@ -1017,6 +1023,11 @@ static function ( string $url, string $method, array $headers, string $file ) us str_ends_with( $static_requests[3][3], '/assets/app.js' ), 'uploads the next opaque target after resuming' ); +check( + Spacefast_Settings::api_url() . '/v1/spaces/spc_demo/versions/ver_static/uploads/refresh' === $static_requests[2][0] + && 'POST' === $static_requests[2][1]['method'], + 'pages upload instructions through the version upload refresh' +); Spacefast_Sync_State::save( array_merge( Spacefast_Sync_State::defaults(), array( 'desired' => 2, 'active_generation' => 1 ) ) @@ -1109,7 +1120,7 @@ static function ( string $url, array $args ) use ( &$retry_requests, $retry_targ if ( 'GET' === $args['method'] ) { return response( 200, array( 'id' => 'ver_retry', 'status' => 'ready', 'isCurrentProduction' => true ) ); } - if ( str_contains( $url, '/uploads/resume' ) ) { + if ( str_contains( $url, '/uploads/refresh' ) ) { return response( 200, array( 'upload' => null ) ); } return response( @@ -1148,7 +1159,7 @@ static function () use ( &$retry_uploads ): array { ); $retry_confirms = array_filter( $retry_requests, - static fn ( array $request ): bool => str_contains( $request[0], '/versions/ver_retry/uploads/resume' ) + static fn ( array $request ): bool => str_contains( $request[0], '/versions/ver_retry/uploads/refresh' ) ); $retry_finalizes = array_filter( $retry_requests, @@ -1170,7 +1181,7 @@ static function ( string $url, array $args ) use ( &$expired_resumes, $retry_tar 'summary' => array( 'upload' => 1, 'reused' => 0, 'ignored' => 0 ), 'targets' => array( $retry_target ), ); - if ( str_contains( $url, '/uploads/resume' ) ) { + if ( str_contains( $url, '/uploads/refresh' ) ) { $expired_resumes++; if ( 1 === $expired_resumes ) return response( 503 ); return response( 200, array( 'upload' => 2 === $expired_resumes ? $upload : null ) ); @@ -1202,7 +1213,7 @@ static function ( string $url, array $args ) use ( &$missing_resumes, $retry_tar 'summary' => array( 'upload' => 1, 'reused' => 0, 'ignored' => 0 ), 'targets' => array( $retry_target ), ); - if ( str_contains( $url, '/uploads/resume' ) ) { + if ( str_contains( $url, '/uploads/refresh' ) ) { $missing_resumes++; return response( 200, array( 'upload' => 1 === $missing_resumes ? $upload : null ) ); } @@ -1227,7 +1238,7 @@ static function ( string $url, array $args ) use ( $retry_target ): array { 'summary' => array( 'upload' => 1, 'reused' => 0, 'ignored' => 0 ), 'targets' => array( $retry_target ), ); - if ( str_contains( $url, '/uploads/resume' ) ) { + if ( str_contains( $url, '/uploads/refresh' ) ) { return response( 200, array( 'upload' => $upload ) ); } return response( 201, array( 'versionId' => 'ver_stuck', 'upload' => $upload ) );