Add REST API endpoints for repo folder listing and page pushing
- Implemented `GET /repo-folders` to list available sub-folders in the configured repository. - Added `POST /push` to push a single page to the repository. - Introduced `POST /push-all` to push all synced pages back to the repository. - Enhanced `oribi_sync_rest_sync` to push local changes after pulling, except during dry runs. - Created `oribi_sync_push_page` and `oribi_sync_push_all` functions to handle page pushing logic. - Updated post meta on successful pushes to track last push time and SHA. - Added logging for push actions and errors. Enhance sync engine to support theme file synchronization - Added functionality to auto-apply changed theme files from the repository's theme directory. - Created `oribi_sync_apply_theme_files` to handle theme file updates during sync. - Ensured the existence of a minimal theme structure in the `ots-theme` directory. Refactor uninstall process to clean up additional post meta - Updated `uninstall.php` to remove new post meta related to push operations. - Ensured comprehensive cleanup of options and metadata upon plugin uninstallation. Introduce push client for handling page pushes to Gitea - Created `push-client.php` to encapsulate logic for pushing pages back to the Git repository. - Implemented conflict resolution by creating branches and opening pull requests when necessary. - Added helper functions for authenticated API requests to Gitea.
This commit is contained in:
530
includes/push-client.php
Normal file
530
includes/push-client.php
Normal file
@@ -0,0 +1,530 @@
|
||||
<?php
|
||||
/**
|
||||
* Oribi Sync — Push client.
|
||||
*
|
||||
* Writes page content back to the Git repository.
|
||||
* On SHA conflict (remote file changed since last sync), creates a branch
|
||||
* named oribi-sync/{slug}-{timestamp} and opens a pull request.
|
||||
*
|
||||
* Currently supports Gitea / Forgejo.
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
|
||||
// ─── Auto-push on page save ──────────────────────────────────────────────────
|
||||
add_action( 'save_post_page', 'oribi_sync_maybe_push_on_save', 20, 3 );
|
||||
|
||||
/**
|
||||
* Push a synced page to the repo whenever it is saved.
|
||||
*
|
||||
* Only fires for pages that were previously pulled (have _oribi_sync_checksum
|
||||
* meta), skips autosaves, revisions, and non-publish statuses.
|
||||
* Uses a static guard to prevent re-entry when we update post meta after push.
|
||||
*/
|
||||
function oribi_sync_maybe_push_on_save( int $post_id, WP_Post $post, bool $update ): void {
|
||||
// Guard: only on genuine content saves
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
|
||||
if ( wp_is_post_revision( $post_id ) ) return;
|
||||
if ( $post->post_status !== 'publish' ) return;
|
||||
|
||||
// Guard: only pages that came from a sync (have checksum meta)
|
||||
$checksum = get_post_meta( $post_id, '_oribi_sync_checksum', true );
|
||||
if ( empty( $checksum ) ) return;
|
||||
|
||||
// Guard: prevent re-entry when push updates meta on the same post
|
||||
static $pushing = [];
|
||||
if ( isset( $pushing[ $post_id ] ) ) return;
|
||||
$pushing[ $post_id ] = true;
|
||||
|
||||
oribi_sync_push_page( $post_id );
|
||||
|
||||
unset( $pushing[ $post_id ] );
|
||||
}
|
||||
|
||||
// ─── Generic authenticated request helpers ────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Perform an authenticated POST request to the Git API.
|
||||
*
|
||||
* @param string $url Full API URL.
|
||||
* @param array $body Body payload (will be JSON-encoded).
|
||||
* @param string $provider Provider key.
|
||||
* @param string $pat Personal access token.
|
||||
*
|
||||
* @return array{code: int, body: array|string}|WP_Error
|
||||
*/
|
||||
function oribi_sync_api_post( string $url, array $body, string $provider, string $pat ) {
|
||||
return oribi_sync_api_request( 'POST', $url, $body, $provider, $pat );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an authenticated PUT request to the Git API.
|
||||
*
|
||||
* @return array{code: int, body: array|string}|WP_Error
|
||||
*/
|
||||
function oribi_sync_api_put( string $url, array $body, string $provider, string $pat ) {
|
||||
return oribi_sync_api_request( 'PUT', $url, $body, $provider, $pat );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an authenticated DELETE request to the Git API.
|
||||
*
|
||||
* @return array{code: int, body: array|string}|WP_Error
|
||||
*/
|
||||
function oribi_sync_api_delete( string $url, array $body, string $provider, string $pat ) {
|
||||
return oribi_sync_api_request( 'DELETE', $url, $body, $provider, $pat );
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal: send an authenticated JSON request.
|
||||
*
|
||||
* @return array{code: int, body: array|string}|WP_Error
|
||||
*/
|
||||
function oribi_sync_api_request( string $method, string $url, array $body, string $provider, string $pat ) {
|
||||
$headers = array_merge(
|
||||
oribi_sync_auth_headers( $provider, $pat ),
|
||||
[
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => 'Oribi-Sync-WP/' . ORIBI_SYNC_VERSION,
|
||||
]
|
||||
);
|
||||
|
||||
$args = [
|
||||
'method' => $method,
|
||||
'timeout' => 30,
|
||||
'headers' => $headers,
|
||||
'body' => wp_json_encode( $body ),
|
||||
];
|
||||
|
||||
$response = wp_remote_request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$code = wp_remote_retrieve_response_code( $response );
|
||||
$raw_body = wp_remote_retrieve_body( $response );
|
||||
$decoded = json_decode( $raw_body, true );
|
||||
|
||||
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
||||
$decoded = $raw_body;
|
||||
}
|
||||
|
||||
return [ 'code' => $code, 'body' => $decoded ];
|
||||
}
|
||||
|
||||
// ─── Gitea file metadata ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Get file metadata (including current SHA) from the Gitea API.
|
||||
*
|
||||
* @param string $api_base Gitea API base (e.g. https://host/api/v1/repos/owner/repo).
|
||||
* @param string $branch Branch name.
|
||||
* @param string $filepath Repo-relative file path.
|
||||
* @param string $pat Personal access token.
|
||||
*
|
||||
* @return array{sha: string, content: string}|null|WP_Error
|
||||
* null if file does not exist (404), WP_Error on failure.
|
||||
*/
|
||||
function oribi_sync_gitea_get_file_meta( string $api_base, string $branch, string $filepath, string $pat ) {
|
||||
$encoded_path = implode( '/', array_map( 'rawurlencode', explode( '/', $filepath ) ) );
|
||||
$url = $api_base . '/contents/' . $encoded_path . '?ref=' . rawurlencode( $branch );
|
||||
|
||||
$result = oribi_sync_api_get( $url, 'gitea', $pat );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$msg = $result->get_error_message();
|
||||
// 404 means file doesn't exist yet — not an error
|
||||
if ( strpos( $msg, 'HTTP 404' ) !== false ) {
|
||||
return null;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return [
|
||||
'sha' => $result['sha'] ?? '',
|
||||
'content' => isset( $result['content'] ) ? base64_decode( $result['content'] ) : '',
|
||||
];
|
||||
}
|
||||
|
||||
// ─── Gitea file create / update ───────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Create or update a file in a Gitea repository.
|
||||
*
|
||||
* @param string $api_base API base URL.
|
||||
* @param string $branch Target branch.
|
||||
* @param string $filepath Repo-relative path.
|
||||
* @param string $content Raw file content (will be base64-encoded).
|
||||
* @param string $pat Personal access token.
|
||||
* @param string|null $sha Current file SHA (required for updates; null for creates).
|
||||
* @param string $message Commit message.
|
||||
*
|
||||
* @return array{code: int, body: array}|WP_Error
|
||||
*/
|
||||
function oribi_sync_gitea_put_file(
|
||||
string $api_base,
|
||||
string $branch,
|
||||
string $filepath,
|
||||
string $content,
|
||||
string $pat,
|
||||
?string $sha = null,
|
||||
string $message = ''
|
||||
) {
|
||||
$encoded_path = implode( '/', array_map( 'rawurlencode', explode( '/', $filepath ) ) );
|
||||
$url = $api_base . '/contents/' . $encoded_path;
|
||||
|
||||
$body = [
|
||||
'content' => base64_encode( $content ),
|
||||
'branch' => $branch,
|
||||
'message' => $message ?: 'Update ' . basename( $filepath ),
|
||||
];
|
||||
|
||||
if ( $sha !== null ) {
|
||||
$body['sha'] = $sha;
|
||||
return oribi_sync_api_put( $url, $body, 'gitea', $pat );
|
||||
}
|
||||
|
||||
return oribi_sync_api_post( $url, $body, 'gitea', $pat );
|
||||
}
|
||||
|
||||
// ─── Gitea branch creation ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Create a new branch in a Gitea repository.
|
||||
*
|
||||
* @return array{code: int, body: array}|WP_Error
|
||||
*/
|
||||
function oribi_sync_gitea_create_branch( string $api_base, string $new_branch, string $base_branch, string $pat ) {
|
||||
$url = $api_base . '/branches';
|
||||
|
||||
return oribi_sync_api_post( $url, [
|
||||
'new_branch_name' => $new_branch,
|
||||
'old_branch_name' => $base_branch,
|
||||
], 'gitea', $pat );
|
||||
}
|
||||
|
||||
// ─── Gitea pull request ──────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Open a pull request in a Gitea repository.
|
||||
*
|
||||
* @return array{code: int, body: array}|WP_Error
|
||||
*/
|
||||
function oribi_sync_gitea_create_pr( string $api_base, string $head, string $base, string $title, string $body_text, string $pat ) {
|
||||
$url = $api_base . '/pulls';
|
||||
|
||||
return oribi_sync_api_post( $url, [
|
||||
'title' => $title,
|
||||
'body' => $body_text,
|
||||
'head' => $head,
|
||||
'base' => $base,
|
||||
], 'gitea', $pat );
|
||||
}
|
||||
|
||||
// ─── PHP page-data wrapper generation ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Generate a PHP page-data wrapper suitable for the repo's page-data convention.
|
||||
*
|
||||
* The wrapper uses a nowdoc return so the file can be `include`d by
|
||||
* oribi_sync_execute_php() and produce the Gutenberg block HTML.
|
||||
*
|
||||
* @param string $content Gutenberg block HTML (from post_content).
|
||||
* @param string $slug Page slug.
|
||||
* @param string $title Page title.
|
||||
*
|
||||
* @return string PHP source code.
|
||||
*/
|
||||
function oribi_sync_generate_php_wrapper( string $content, string $slug, string $title = '' ): string {
|
||||
if ( empty( $title ) ) {
|
||||
$title = oribi_sync_slug_to_title( $slug );
|
||||
}
|
||||
|
||||
$date = current_time( 'Y-m-d H:i:s' );
|
||||
|
||||
// Use a nowdoc so the content is treated as a literal string (no interpolation).
|
||||
// Escape the content if it accidentally contains the heredoc delimiter on its own line.
|
||||
$delimiter = 'ORIBI_SYNC_CONTENT';
|
||||
$safe_content = str_replace( "\n" . $delimiter, "\n " . $delimiter, $content );
|
||||
|
||||
$php = "<?php\n";
|
||||
$php .= "/**\n";
|
||||
$php .= " * Page: {$title}\n";
|
||||
$php .= " * Slug: {$slug}\n";
|
||||
$php .= " * Pushed by Oribi Tech Sync on {$date}.\n";
|
||||
$php .= " */\n\n";
|
||||
$php .= "return <<<'{$delimiter}'\n";
|
||||
$php .= $safe_content . "\n";
|
||||
$php .= $delimiter . ";\n";
|
||||
|
||||
return $php;
|
||||
}
|
||||
|
||||
// ─── Push orchestrator ───────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Push a single WordPress page back to the Git repository.
|
||||
*
|
||||
* Flow:
|
||||
* 1. Derive repo path from post meta (_oribi_sync_source) or settings.
|
||||
* 2. Generate PHP wrapper from page content.
|
||||
* 3. Fetch remote file SHA from the Gitea contents API.
|
||||
* 4. If remote SHA matches stored SHA → direct update (no conflict).
|
||||
* 5. If remote SHA differs (file changed externally) → create branch + PR.
|
||||
* 6. If file doesn't exist → create it on the target branch.
|
||||
* 7. Update post meta on success.
|
||||
*
|
||||
* @param int $post_id WP post ID.
|
||||
* @param array $opts Optional overrides: 'message' (commit message).
|
||||
*
|
||||
* @return array{ok: bool, action: string, message: string, pr_url?: string}
|
||||
*/
|
||||
function oribi_sync_push_page( int $post_id, array $opts = [] ): array {
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( ! $post || $post->post_type !== 'page' ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Post not found or not a page.' ];
|
||||
}
|
||||
|
||||
// ── Settings ──────────────────────────────────────────────────────────
|
||||
$repo_url = get_option( 'oribi_sync_repo', '' );
|
||||
$branch = get_option( 'oribi_sync_branch', 'main' ) ?: 'main';
|
||||
$pat = oribi_sync_get_pat();
|
||||
$provider = oribi_sync_get_provider();
|
||||
|
||||
if ( empty( $repo_url ) || empty( $pat ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Repository URL or PAT not configured.' ];
|
||||
}
|
||||
|
||||
if ( $provider !== 'gitea' ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Push is currently supported for Gitea / Forgejo only.' ];
|
||||
}
|
||||
|
||||
$parsed = oribi_sync_parse_repo_url( $repo_url );
|
||||
if ( is_wp_error( $parsed ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => $parsed->get_error_message() ];
|
||||
}
|
||||
|
||||
$api_base = oribi_sync_api_base( $provider, $parsed );
|
||||
|
||||
// ── Determine repo path ───────────────────────────────────────────────
|
||||
$source_meta = get_post_meta( $post_id, '_oribi_sync_source', true );
|
||||
$repo_path = '';
|
||||
|
||||
if ( ! empty( $source_meta ) ) {
|
||||
// Format: {repo_url}@{branch}:{path}
|
||||
$colon_pos = strpos( $source_meta, ':' );
|
||||
if ( $colon_pos !== false ) {
|
||||
// Find the last occurrence of the pattern @branch:
|
||||
$at_pos = strrpos( substr( $source_meta, 0, $colon_pos ), '@' );
|
||||
if ( $at_pos !== false ) {
|
||||
$repo_path = substr( $source_meta, $colon_pos + 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $repo_path ) ) {
|
||||
// Derive from settings + slug
|
||||
$pages_folder = get_option( 'oribi_sync_pages_folder', '' );
|
||||
if ( empty( $pages_folder ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Cannot determine repo path — no pages folder configured and no source meta on this page.' ];
|
||||
}
|
||||
$repo_path = 'Pages/' . $pages_folder . '/' . $post->post_name . '.php';
|
||||
}
|
||||
|
||||
// ── Generate content ──────────────────────────────────────────────────
|
||||
$slug = $post->post_name;
|
||||
$title = $post->post_title;
|
||||
$wp_content = $post->post_content;
|
||||
$php_source = oribi_sync_generate_php_wrapper( $wp_content, $slug, $title );
|
||||
$new_checksum = hash( 'sha256', $php_source );
|
||||
|
||||
$commit_msg = $opts['message'] ?? "Sync: update {$slug} from WordPress";
|
||||
|
||||
// ── Fetch remote file metadata ────────────────────────────────────────
|
||||
$remote = oribi_sync_gitea_get_file_meta( $api_base, $branch, $repo_path, $pat );
|
||||
|
||||
if ( is_wp_error( $remote ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Failed to check remote file: ' . $remote->get_error_message() ];
|
||||
}
|
||||
|
||||
$stored_sha = get_post_meta( $post_id, '_oribi_sync_git_sha', true );
|
||||
|
||||
// ── Decide strategy ───────────────────────────────────────────────────
|
||||
if ( $remote === null ) {
|
||||
// File doesn't exist in repo → create it directly
|
||||
$result = oribi_sync_gitea_put_file( $api_base, $branch, $repo_path, $php_source, $pat, null, $commit_msg );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Create failed: ' . $result->get_error_message() ];
|
||||
}
|
||||
if ( $result['code'] < 200 || $result['code'] >= 300 ) {
|
||||
$err = is_array( $result['body'] ) ? ( $result['body']['message'] ?? wp_json_encode( $result['body'] ) ) : $result['body'];
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => "Create failed (HTTP {$result['code']}): {$err}" ];
|
||||
}
|
||||
|
||||
// Update post meta
|
||||
$new_sha = $result['body']['content']['sha'] ?? '';
|
||||
oribi_sync_update_push_meta( $post_id, $new_sha, $new_checksum, $repo_url, $branch, $repo_path );
|
||||
oribi_sync_log_push( $slug, 'created', $branch );
|
||||
|
||||
return [ 'ok' => true, 'action' => 'created', 'message' => "Created {$repo_path} on branch {$branch}." ];
|
||||
}
|
||||
|
||||
$remote_sha = $remote['sha'];
|
||||
|
||||
// Check for conflict: remote SHA differs from what we last synced
|
||||
$has_conflict = ! empty( $stored_sha ) && $remote_sha !== $stored_sha;
|
||||
|
||||
if ( $has_conflict ) {
|
||||
// ── Conflict path: branch + PR ────────────────────────────────────
|
||||
$timestamp = gmdate( 'Ymd-His' );
|
||||
$new_branch = 'oribi-sync/' . $slug . '-' . $timestamp;
|
||||
|
||||
// Create branch
|
||||
$branch_result = oribi_sync_gitea_create_branch( $api_base, $new_branch, $branch, $pat );
|
||||
if ( is_wp_error( $branch_result ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Branch creation failed: ' . $branch_result->get_error_message() ];
|
||||
}
|
||||
if ( $branch_result['code'] < 200 || $branch_result['code'] >= 300 ) {
|
||||
$err = is_array( $branch_result['body'] ) ? ( $branch_result['body']['message'] ?? '' ) : $branch_result['body'];
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => "Branch creation failed (HTTP {$branch_result['code']}): {$err}" ];
|
||||
}
|
||||
|
||||
// Fetch file meta on the new branch (same SHA as base branch initially)
|
||||
$branch_remote = oribi_sync_gitea_get_file_meta( $api_base, $new_branch, $repo_path, $pat );
|
||||
$branch_sha = null;
|
||||
if ( ! is_wp_error( $branch_remote ) && $branch_remote !== null ) {
|
||||
$branch_sha = $branch_remote['sha'];
|
||||
}
|
||||
|
||||
// Commit to the new branch
|
||||
$put_result = oribi_sync_gitea_put_file( $api_base, $new_branch, $repo_path, $php_source, $pat, $branch_sha, $commit_msg );
|
||||
if ( is_wp_error( $put_result ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Commit to branch failed: ' . $put_result->get_error_message() ];
|
||||
}
|
||||
if ( $put_result['code'] < 200 || $put_result['code'] >= 300 ) {
|
||||
$err = is_array( $put_result['body'] ) ? ( $put_result['body']['message'] ?? '' ) : $put_result['body'];
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => "Commit to branch failed (HTTP {$put_result['code']}): {$err}" ];
|
||||
}
|
||||
|
||||
// Open PR
|
||||
$pr_title = "Sync: {$slug}";
|
||||
$pr_body = "Automatic push from WordPress (Oribi Tech Sync).\n\n";
|
||||
$pr_body .= "**Page:** {$title} (`{$slug}`)\n";
|
||||
$pr_body .= "**Commit:** {$commit_msg}\n\n";
|
||||
$pr_body .= "The target branch `{$branch}` has been modified since the last sync, ";
|
||||
$pr_body .= "so this change was pushed to `{$new_branch}` for review.\n\n";
|
||||
$pr_body .= "Stored SHA: `{$stored_sha}`\n";
|
||||
$pr_body .= "Remote SHA: `{$remote_sha}`\n";
|
||||
|
||||
$pr_result = oribi_sync_gitea_create_pr( $api_base, $new_branch, $branch, $pr_title, $pr_body, $pat );
|
||||
if ( is_wp_error( $pr_result ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'PR creation failed: ' . $pr_result->get_error_message() ];
|
||||
}
|
||||
|
||||
$pr_url = '';
|
||||
if ( $pr_result['code'] >= 200 && $pr_result['code'] < 300 && is_array( $pr_result['body'] ) ) {
|
||||
$pr_url = $pr_result['body']['html_url'] ?? '';
|
||||
}
|
||||
|
||||
// Save PR URL on the post
|
||||
if ( ! empty( $pr_url ) ) {
|
||||
update_post_meta( $post_id, '_oribi_sync_pr_url', $pr_url );
|
||||
}
|
||||
|
||||
oribi_sync_log_push( $slug, 'pr_created', $new_branch, $pr_url );
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'action' => 'pr_created',
|
||||
'message' => "Conflict detected — created PR on branch {$new_branch}.",
|
||||
'pr_url' => $pr_url,
|
||||
];
|
||||
}
|
||||
|
||||
// ── No conflict: direct update ────────────────────────────────────────
|
||||
$result = oribi_sync_gitea_put_file( $api_base, $branch, $repo_path, $php_source, $pat, $remote_sha, $commit_msg );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => 'Update failed: ' . $result->get_error_message() ];
|
||||
}
|
||||
if ( $result['code'] < 200 || $result['code'] >= 300 ) {
|
||||
$err = is_array( $result['body'] ) ? ( $result['body']['message'] ?? wp_json_encode( $result['body'] ) ) : $result['body'];
|
||||
return [ 'ok' => false, 'action' => 'error', 'message' => "Update failed (HTTP {$result['code']}): {$err}" ];
|
||||
}
|
||||
|
||||
// Update post meta
|
||||
$new_sha = $result['body']['content']['sha'] ?? '';
|
||||
oribi_sync_update_push_meta( $post_id, $new_sha, $new_checksum, $repo_url, $branch, $repo_path );
|
||||
oribi_sync_log_push( $slug, 'updated', $branch );
|
||||
|
||||
return [ 'ok' => true, 'action' => 'updated', 'message' => "Updated {$repo_path} on branch {$branch}." ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk-push all synced pages.
|
||||
*
|
||||
* @return array{ok: bool, results: array}
|
||||
*/
|
||||
function oribi_sync_push_all(): array {
|
||||
$query = new WP_Query( [
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'publish',
|
||||
'meta_key' => '_oribi_sync_checksum',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
] );
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach ( $query->posts as $post_id ) {
|
||||
$page = get_post( $post_id );
|
||||
$slug = $page ? $page->post_name : "#{$post_id}";
|
||||
$result = oribi_sync_push_page( (int) $post_id );
|
||||
$results[] = array_merge( $result, [ 'slug' => $slug, 'post_id' => $post_id ] );
|
||||
}
|
||||
|
||||
$all_ok = ! empty( $results ) && count( array_filter( $results, fn( $r ) => ! $r['ok'] ) ) === 0;
|
||||
|
||||
return [ 'ok' => $all_ok, 'results' => $results ];
|
||||
}
|
||||
|
||||
// ─── Post meta helpers ────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Update post meta after a successful push.
|
||||
*/
|
||||
function oribi_sync_update_push_meta( int $post_id, string $sha, string $checksum, string $repo_url, string $branch, string $path ): void {
|
||||
if ( ! empty( $sha ) ) {
|
||||
update_post_meta( $post_id, '_oribi_sync_git_sha', $sha );
|
||||
}
|
||||
update_post_meta( $post_id, '_oribi_sync_checksum', $checksum );
|
||||
update_post_meta( $post_id, '_oribi_sync_source', $repo_url . '@' . $branch . ':' . $path );
|
||||
update_post_meta( $post_id, '_oribi_sync_last_push', current_time( 'mysql' ) );
|
||||
|
||||
// Clear any stale PR URL on successful direct push
|
||||
delete_post_meta( $post_id, '_oribi_sync_pr_url' );
|
||||
}
|
||||
|
||||
// ─── Push log ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Append an entry to the push log.
|
||||
*/
|
||||
function oribi_sync_log_push( string $slug, string $action, string $branch, string $pr_url = '' ): void {
|
||||
$log = get_option( 'oribi_sync_push_log', [] );
|
||||
if ( ! is_array( $log ) ) $log = [];
|
||||
|
||||
array_unshift( $log, [
|
||||
'time' => current_time( 'mysql' ),
|
||||
'slug' => $slug,
|
||||
'action' => $action,
|
||||
'branch' => $branch,
|
||||
'pr_url' => $pr_url,
|
||||
] );
|
||||
|
||||
// Keep last 50 entries
|
||||
$log = array_slice( $log, 0, 50 );
|
||||
update_option( 'oribi_sync_push_log', $log, 'no' );
|
||||
}
|
||||
Reference in New Issue
Block a user