Plugin Directory

Changeset 3490948


Ignore:
Timestamp:
03/25/2026 01:06:13 PM (4 days ago)
Author:
jeromesteunenberg
Message:

Release version 0.0.4

Location:
pushpull
Files:
143 added
10 edited

Legend:

Unmodified
Added
Removed
  • pushpull/trunk/README.md

    r3490393 r3490948  
    66Tested up to: 6.9
    77Requires PHP: 8.1
    8 Stable tag: 0.0.3
     8Stable tag: 0.0.4
    99License: GPLv2
    1010License URI: [http://www.gnu.org/licenses/gpl-2.0.html](http://www.gnu.org/licenses/gpl-2.0.html)
    1111
    1212Git-backed content workflows for selected WordPress content domains.
     13
     14> This is a beta plugin. It is still under active development, has limited functionality, and currently supports only a narrow subset of the intended PushPull feature set.
    1315
    1416## Description
     
    97997. Save the settings
    98100
     101### Empty repositories
     102
     103If the configured GitHub repository exists but has no commits yet, `Test connection` will report that the repository is reachable but empty.
     104
     105In that case, click `Initialize remote repository`. PushPull will:
     106
     1071. create the first commit on the configured branch
     1082. fetch that initial commit into the local remote-tracking ref
     1093. make the repository ready for normal commit, fetch, merge, apply, and push workflows
     110
     111You no longer need to create the first commit manually on GitHub before using PushPull.
     112
    99113## Workflow
    100114
  • pushpull/trunk/composer.json

    r3490393 r3490948  
    2929        "phpstan": "phpstan analyse --configuration=phpstan.neon.dist --no-progress --debug --memory-limit=1G",
    3030        "bump-version": "sh bin/bump-version.sh",
     31        "sync-public-github": "sh bin/sync-public-github.sh",
    3132        "pcp": "sh bin/run-pcp.sh",
    3233        "package": "sh bin/package-plugin.sh",
  • pushpull/trunk/pushpull.php

    r3490393 r3490948  
    55 * Plugin URI: https://github.com/creativemoods/pushpull
    66 * Description: Git-backed content workflows for selected WordPress content domains.
    7  * Version: 0.0.3
     7 * Version: 0.0.4
    88 * Requires at least: 6.0
    99 * Requires PHP: 8.1
     
    2323define('PUSHPULL_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2424define('PUSHPULL_PLUGIN_URL', plugin_dir_url(__FILE__));
    25 define('PUSHPULL_VERSION', '0.0.3');
     25define('PUSHPULL_VERSION', '0.0.4');
    2626
    2727if (is_readable(PUSHPULL_PLUGIN_DIR . 'vendor/autoload.php')) {
  • pushpull/trunk/readme.txt

    r3490393 r3490948  
    55Tested up to: 6.9
    66Requires PHP: 8.1
    7 Stable tag: 0.0.3
     7Stable tag: 0.0.4
    88License: GPLv2
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
    1010
    1111Git-backed content workflows for selected WordPress content domains.
     12
     13= Beta notice =
     14
     15This is a beta plugin. It is still under active development, has limited functionality, and currently supports only a narrow subset of the intended PushPull feature set.
    1216
    1317== Description ==
     
    81857. Save the settings
    8286
     87= Empty repositories =
     88
     89If the configured GitHub repository exists but has no commits yet, `Test connection` will report that the repository is reachable but empty.
     90
     91In that case, click `Initialize remote repository`. PushPull will:
     92
     931. create the first commit on the configured branch
     942. fetch that initial commit into the local remote-tracking ref
     953. make the repository ready for normal commit, fetch, merge, apply, and push workflows
     96
     97You do not need to create the first commit manually on GitHub before using PushPull.
     98
    8399== External services ==
    84100
  • pushpull/trunk/src/Admin/SettingsPage.php

    r3490393 r3490948  
    77use PushPull\Persistence\LocalRepositoryResetService;
    88use PushPull\Persistence\Migrations\SchemaMigrator;
     9use PushPull\Domain\Sync\RemoteRepositoryInitializer;
    910use PushPull\Provider\Exception\ProviderException;
    1011use PushPull\Provider\Exception\UnsupportedProviderException;
     
    2223    private const TEST_CONNECTION_ACTION = 'pushpull_test_connection';
    2324    private const RESET_LOCAL_REPOSITORY_ACTION = 'pushpull_reset_local_repository';
     25    private const INITIALIZE_REMOTE_REPOSITORY_ACTION = 'pushpull_initialize_remote_repository';
    2426
    2527    public function __construct(
     
    2729        private readonly GitProviderFactoryInterface $providerFactory,
    2830        private readonly LocalRepositoryResetService $localRepositoryResetService,
     31        private readonly RemoteRepositoryInitializer $remoteRepositoryInitializer,
    2932        private readonly OperationExecutor $operationExecutor
    3033    ) {
     
    105108        echo '<div class="pushpull-button-grid">';
    106109        $this->renderTestConnectionButton();
     110        if ($this->shouldOfferRemoteInitialization()) {
     111            $this->renderInitializeRemoteButton();
     112        }
    107113        echo '</div>';
    108114        echo '</div>';
     
    188194        }
    189195
    190         $message = sprintf(
    191             'Connection successful for %s. Default branch: %s. Resolved branch: %s.',
    192             $result->repositoryPath,
    193             $result->defaultBranch ?? 'n/a',
    194             $result->resolvedBranch ?? 'n/a'
    195         );
    196 
    197         $this->redirectWithNotice('success', $message);
     196        $message = $result->emptyRepository
     197            ? sprintf(
     198                'Connection successful for %s, but the repository is empty. Initialize branch %s to create the first commit and enable fetch and push.',
     199                $result->repositoryPath,
     200                $result->resolvedBranch ?? $settings->branch
     201            )
     202            : sprintf(
     203                'Connection successful for %s. Default branch: %s. Resolved branch: %s.',
     204                $result->repositoryPath,
     205                $result->defaultBranch ?? 'n/a',
     206                $result->resolvedBranch ?? 'n/a'
     207            );
     208
     209        $this->redirectWithNotice($result->emptyRepository ? 'warning' : 'success', $message, $result->emptyRepository);
    198210    }
    199211
     
    220232    }
    221233
     234    public function handleInitializeRemoteRepository(): void
     235    {
     236        if (! current_user_can(Capabilities::MANAGE_PLUGIN)) {
     237            wp_die(esc_html__('You do not have permission to manage PushPull.', 'pushpull'));
     238        }
     239
     240        check_admin_referer(self::INITIALIZE_REMOTE_REPOSITORY_ACTION);
     241
     242        $settings = $this->settingsRepository->get();
     243
     244        try {
     245            $result = $this->operationExecutor->run(
     246                'generateblocks_global_styles',
     247                'initialize_remote_repository',
     248                ['branch' => $settings->branch],
     249                fn () => $this->remoteRepositoryInitializer->initialize('generateblocks_global_styles', $settings)
     250            );
     251        } catch (\RuntimeException | ProviderException $exception) {
     252            $message = $exception instanceof ProviderException ? $exception->debugSummary() : $exception->getMessage();
     253            $this->redirectWithNotice('error', $message);
     254        }
     255
     256        $this->redirectWithNotice(
     257            'success',
     258            sprintf(
     259                'Initialized remote branch %s with first commit %s and fetched it into %s.',
     260                $result->branch,
     261                $result->remoteCommitHash,
     262                $result->remoteRefName
     263            )
     264        );
     265    }
     266
    222267    private function renderTestConnectionButton(): void
    223268    {
     
    229274    }
    230275
     276    private function renderInitializeRemoteButton(): void
     277    {
     278        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" onsubmit="return window.confirm(\'Initialize the configured remote repository? PushPull will create the first commit on the configured branch so fetch and push can start working.\');">';
     279        echo '<input type="hidden" name="action" value="pushpull_initialize_remote_repository" />';
     280        wp_nonce_field(self::INITIALIZE_REMOTE_REPOSITORY_ACTION);
     281        submit_button(__('Initialize remote repository', 'pushpull'), 'primary', 'submit', false);
     282        echo '</form>';
     283    }
     284
    231285    /**
    232286     * @return array{type: string, message: string}|null
     
    244298
    245299        return [
    246             'type' => $status === 'success' ? 'success' : 'error',
     300            'type' => in_array($status, ['success', 'warning'], true) ? $status : 'error',
    247301            'message' => $message,
    248302        ];
    249303    }
    250304
    251     private function redirectWithNotice(string $status, string $message): never
    252     {
    253         $url = add_query_arg(
    254             [
    255                 'page' => self::MENU_SLUG,
    256                 'pushpull_settings_status' => $status,
    257                 'pushpull_settings_message' => $message,
    258             ],
    259             admin_url('admin.php')
    260         );
     305    private function redirectWithNotice(string $status, string $message, bool $offerRemoteInitialization = false): never
     306    {
     307        $queryArgs = [
     308            'page' => self::MENU_SLUG,
     309            'pushpull_settings_status' => $status,
     310            'pushpull_settings_message' => $message,
     311        ];
     312
     313        if ($offerRemoteInitialization) {
     314            $queryArgs['pushpull_offer_remote_initialization'] = '1';
     315        }
     316
     317        $url = add_query_arg($queryArgs, admin_url('admin.php'));
    261318
    262319        wp_safe_redirect($url);
    263320        exit;
    264321    }
     322
     323    private function shouldOfferRemoteInitialization(): bool
     324    {
     325        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only notice parameters from the redirect target.
     326        return isset($_GET['pushpull_offer_remote_initialization']) && sanitize_key((string) $_GET['pushpull_offer_remote_initialization']) === '1';
     327    }
    265328}
  • pushpull/trunk/src/Plugin/Plugin.php

    r3490393 r3490948  
    2626use PushPull\Domain\Repository\DatabaseLocalRepository;
    2727use PushPull\Domain\Sync\GenerateBlocksRepositoryCommitter;
     28use PushPull\Domain\Sync\RemoteRepositoryInitializer;
    2829use PushPull\Domain\Sync\LocalSyncService;
    2930use PushPull\Persistence\Migrations\SchemaMigrator;
     
    5253        $localRepository = new DatabaseLocalRepository($wpdb);
    5354        $localRepositoryResetService = new LocalRepositoryResetService($wpdb);
     55        $remoteRepositoryInitializer = new RemoteRepositoryInitializer($providerFactory, $localRepository);
    5456        $operationLogRepository = new OperationLogRepository($wpdb);
    5557        $operationExecutor = new OperationExecutor($operationLogRepository, new OperationLockService());
     
    9395        );
    9496        $settingsRegistrar = new SettingsRegistrar($settingsRepository);
    95         $settingsPage = new SettingsPage($settingsRepository, $providerFactory, $localRepositoryResetService, $operationExecutor);
     97        $settingsPage = new SettingsPage($settingsRepository, $providerFactory, $localRepositoryResetService, $remoteRepositoryInitializer, $operationExecutor);
    9698        $operationsPage = new OperationsPage($operationLogRepository);
    9799        $managedContentPage = new ManagedContentPage(
     
    111113        add_action('admin_post_pushpull_test_connection', [$settingsPage, 'handleTestConnection']);
    112114        add_action('admin_post_pushpull_reset_local_repository', [$settingsPage, 'handleResetLocalRepository']);
     115        add_action('admin_post_pushpull_initialize_remote_repository', [$settingsPage, 'handleInitializeRemoteRepository']);
    113116        add_action('admin_post_pushpull_commit_generateblocks', [$managedContentPage, 'handleCommit']);
    114117        add_action('admin_post_pushpull_fetch_generateblocks', [$managedContentPage, 'handleFetch']);
  • pushpull/trunk/src/Provider/GitHub/GitHubProvider.php

    r3490393 r3490948  
    9090        }
    9191
    92         $this->getRef($config, 'refs/heads/' . $resolvedBranch);
     92        try {
     93            $this->getRef($config, 'refs/heads/' . $resolvedBranch);
     94        } catch (ProviderException $exception) {
     95            if ($exception->category !== ProviderException::EMPTY_REPOSITORY) {
     96                throw $exception;
     97            }
     98
     99            return new ProviderConnectionResult(
     100                true,
     101                $this->repositoryPath($config),
     102                $defaultBranch,
     103                $resolvedBranch,
     104                true,
     105                ['GitHub repository is reachable but empty and needs an initial commit before fetch or push can run.']
     106            );
     107        }
    93108
    94109        return new ProviderConnectionResult(
     
    97112            $defaultBranch,
    98113            $resolvedBranch,
     114            false,
    99115            ['GitHub repository metadata and target branch are accessible.']
    100116        );
     
    272288            (string) ($payload['object']['sha'] ?? $request->newCommitHash)
    273289        );
     290    }
     291
     292    public function initializeEmptyRepository(GitRemoteConfig $config, string $commitMessage): RemoteRef
     293    {
     294        $path = '.pushpull-initialized';
     295        $payload = $this->requestJson(
     296            'PUT',
     297            $config,
     298            '/repos/' . $this->repositoryPath($config) . '/contents/' . rawurlencode($path),
     299            'initialize_empty_repository',
     300            [],
     301            [
     302                'message' => $commitMessage,
     303                'content' => base64_encode("Initialized by PushPull.\n"),
     304                'branch' => $config->branch,
     305            ]
     306        );
     307
     308        $commitHash = (string) ($payload['commit']['sha'] ?? '');
     309
     310        if ($commitHash === '') {
     311            throw new ProviderException(
     312                ProviderException::UNSUPPORTED_RESPONSE,
     313                'GitHub did not return a commit hash for repository initialization.'
     314            );
     315        }
     316
     317        return new RemoteRef('refs/heads/' . $config->branch, $commitHash);
    274318    }
    275319
  • pushpull/trunk/src/Provider/GitProviderInterface.php

    r3490393 r3490948  
    3434
    3535    public function updateRef(GitRemoteConfig $config, UpdateRemoteRefRequest $request): UpdateRefResult;
     36
     37    public function initializeEmptyRepository(GitRemoteConfig $config, string $commitMessage): RemoteRef;
    3638}
  • pushpull/trunk/src/Provider/ProviderConnectionResult.php

    r3490393 r3490948  
    1515        public readonly ?string $defaultBranch,
    1616        public readonly ?string $resolvedBranch,
     17        public readonly bool $emptyRepository = false,
    1718        public readonly array $messages = []
    1819    ) {
  • pushpull/trunk/vendor/composer/installed.php

    r3490393 r3490948  
    22    'root' => array(
    33        'name' => 'creativemoods/pushpull',
    4         'pretty_version' => 'v0.0.3',
    5         'version' => '0.0.3.0',
    6         'reference' => 'a63605c9cd69dc3a520969ac80a611125fea414f',
     4        'pretty_version' => 'v0.0.4',
     5        'version' => '0.0.4.0',
     6        'reference' => 'cc2d17267d81de536a0c47cf5c7a661bfce6a958',
    77        'type' => 'wordpress-plugin',
    88        'install_path' => __DIR__ . '/../../',
     
    1212    'versions' => array(
    1313        'creativemoods/pushpull' => array(
    14             'pretty_version' => 'v0.0.3',
    15             'version' => '0.0.3.0',
    16             'reference' => 'a63605c9cd69dc3a520969ac80a611125fea414f',
     14            'pretty_version' => 'v0.0.4',
     15            'version' => '0.0.4.0',
     16            'reference' => 'cc2d17267d81de536a0c47cf5c7a661bfce6a958',
    1717            'type' => 'wordpress-plugin',
    1818            'install_path' => __DIR__ . '/../../',
Note: See TracChangeset for help on using the changeset viewer.