Skip to content

Release 4.5.1: streamline first successful Connect setup - #849

Merged
andypotanin merged 35 commits into
latestfrom
fix/451-connect-onboarding
Nov 12, 2026
Merged

andypotanin merged 35 commits into
latestfrom
fix/451-connect-onboarding

Conversation

@andypotanin

@andypotanin andypotanin commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

Based on latest at 4.5.0; no 4.6 vendor work is included.

Changes

  • Fresh unconfigured activation and the Plugins action lead to Connect; manual credentials move under Advanced.
  • Recommended setup uses a new project, generated bucket, US multi-region location and CDN mode. Existing project, bucket, location and mode controls remain under Advanced.
  • Same-tab, nonce-protected Google start; billing and key-custody expectations are visible.
  • Missing project billing has a Check again path that preserves the storage draft.
  • Storage drafts restore project and bucket selections after failures.
  • Setup runs as a job with progress on the screen. One setup at a time per site or network; a setup that is interrupted can be resumed and reuses what it already created, including its service account key.
  • The result of a setup, including the explanation when it fails, stays on the screen until the next action.
  • Service account keys do not pile up: a replaced key is revoked, a revocation that fails is retried by the next setup or a disconnect, and a disconnect names a key Google refuses to remove.
  • Setup state is kept with WordPress core's lock (WP_Upgrader::create_lock()) and the Options API. The only direct database calls are MySQL's named-lock functions, which are how a worker that died is noticed at once.
  • Privacy-limited click-to-first-upload measurement using the stateless.ci 0.3.0 broker.
  • WordPress.org listing: seven refreshed screenshots with new captions. .wordpress-org/ now holds every listing image, because the deploy mirrors that folder.

Privacy and security

The broker records only event timestamps, a hashed transaction ID, a hashed event token and allowlisted failure reasons that expire after seven days and are removed by the next daily cleanup. It does not record origin, email, project, bucket, OAuth codes, reusable tokens or free-form errors in funnel records. Broker-owned returned/approved/redeemed events cannot be submitted by the plugin.

OAuth callback query exclusions are active on the _Default and errors-to-bigquery log sinks. A synthetic callback was absent from logs while a non-sensitive control request remained visible. Historical logs are unchanged.

Validation

Candidate: 4.5.1-rc15 at 0f8235c. The tag tree is identical to the build that was tested (2,417 files, no differences). After the candidate only the changelog date, the release workflow reference and listing images changed (27f1fe9); no plugin code.

On the www.wpcloud.io develop-stateless test sites, all four running that build (PHP 8.5, WordPress 6.9):

  • 19 setup scenarios against a Google stand-in on a real site, with the worker killed at chosen points: interrupted, resumed, abandoned and failed setups, key bookkeeping, disconnects, a database without lock functions
  • Two real processes: a second setup is refused while one runs; when the first is killed the other notices within seconds and takes over
  • Multisite: a sub-site keeps its records with that site, a network setup in network options
  • Real Google on the connected site, in a browser: a setup Google refuses keeps its explanation across reloads; a setup that succeeds keeps its result; a worker killed after its new key went live is taken over and exactly one key is left at Google; real uploads with all sizes served from the bucket
  • Real Google in Chrome: disconnect, new sign-in, setup with a reload afterwards, upload with all sizes, the recorded first-upload milestone, exactly one service account key
  • Upload and measurement fixtures, including the first-upload milestone and the multisite network case
  • Key-configured site: no redirect to Connect, deactivate and reactivate, media library
  • Ghost Inspector suite, 5 of 5 (two known screenshot baselines differ)

PHP versions, in the official php:8.1 and php:8.5 images:

  • Every PHP file in the package compiles on 8.1 (the declared minimum) and on 8.5
  • The setup test suite passes on both
  • Every function the plugin's own code calls exists on PHP 8.1; PHPCompatibility reports nothing new against 4.5.0
  • Bundled libraries are unchanged from 4.5.0

Release:

  • WordPress.org deploy rehearsed against the live SVN without committing: 11 plugin files change, none added or removed; seven screenshots replaced, banners and icons unchanged
  • publish-release.yml is back on wp-gh-release-ops.yml@master, the same workflow that published 4.5.0

Earlier candidates:

  • Configured CDN upgrade and multisite upgrade with settings unchanged
  • Broker staging regression suite (38/38)

Local:

  • php tests/connect-provision-job.php and node tests/connect-provision-browser.cjs
  • WordPress coding standards, database sniffs: no errors

@andypotanin

Copy link
Copy Markdown
Member Author

Independent review update:

  • Codex reviewed the complete change and follow-up fixes. Final review of 3e3f8972 reports no release-blocking correctness or security defects.
  • Isolated Playwright verification passed for WP admin login, deactivate/reactivate routing to Connect, same-tab start, and desktop/mobile layout. Temporary admin and local proxy were removed.
  • The global-Ephemeral reproduction now selects CDN. Cron/lazy cleanup, case replacement, malformed raw states, replacement races, and transaction handoff tests pass in the installed ZIP.
  • Configured CDN and multisite upgrades passed real upload and public HTTP 200 checks with saved settings unchanged.

The real Google OAuth/provisioning journey is still pending and is not being claimed by these checks.

@andypotanin
andypotanin marked this pull request as ready for review November 10, 2026 21:31
Copilot AI balanced review requested due to automatic review settings November 10, 2026 21:31
@andypotanin

Copy link
Copy Markdown
Member Author

Additional browser verification: an isolated Playwright session clicked Sign in with Google and landed on accounts.google.com in the same tab, with no second tab opened. No Google credentials were entered and no OAuth approval is being claimed. Activation routing and desktop/mobile layout also passed.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The Connect flow can strand users behind hidden billing requirements, inaccessible setup links, or same-tab own-client OAuth, while shared funnel state can misattribute administrators’ events.

4 open issues
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file
What changed in this PR

Release 4.5.1 simplifies WP-Stateless’s first-time Google Cloud setup through Connect while retaining advanced and manual configuration options.

Changes:

  • Routes unconfigured sites to Connect and recommends a new project, US multi-region bucket, and CDN mode.
  • Adds nonce-protected sign-in, billing recovery, and restored storage selections after failures.
  • Adds setup-to-first-upload measurement and updates release documentation and publishing configuration.
File Description
wp-stateless-media.php Bumps the plugin version to 4.5.1.
static/​views/​settings-sections/​google-cloud-storage.php Promotes Connect and groups manual credentials under Advanced.
static/​views/​connect.php Adds recommended setup, billing recovery, and revised sign-in controls.
static/​styles/​stateless-connect.css Styles recommended setup and recovery controls.
static/​scripts/​stateless-connect.js Updates the storage creation button label.
readme.txt Updates installation instructions, stable tag, and changelog.
readme.md Updates the installation walkthrough.
lib/​classes/​class-settings.php Directs unconfigured sites’ Plugins action to Connect.
lib/​classes/​class-google-connect.php Adds setup events, billing rechecks, and draft restoration.
lib/​classes/​class-bootstrap.php Redirects unconfigured activations to Connect.
changes.md Documents the 4.5.1 release.
changelog.txt Adds the 4.5.1 changelog entry.
.github/​workflows/​publish-release.yml Switches publishing to the prerelease asset-fix workflow branch.

Review effort: Balanced

Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-settings.php Resolved
Comment thread static/views/connect.php Resolved
Comment thread static/views/connect.php Resolved
Copilot AI balanced review requested due to automatic review settings November 10, 2026 21:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The change hides billing recovery instructions, directs administrators to an inaccessible Connect page when setup is hidden, and can attribute OAuth events to another administrator’s attempt.

4 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Hide the setup banner when site-scope Connect is unavailable

static/​views/​settings-sections/​google-cloud-storage.php:6

This banner also appears on unconfigured sites where Setup Assistant is explicitly hidden. In that case GoogleConnect::render_page() refuses the linked page, so an installation intentionally using manual credentials is shown a primary setup action that cannot work. Suppress this notice when GoogleConnect::hidden_for_site() is true outside network admin.

1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 10, 2026 23:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Scheduled cleanup drops the user ID, leaving abandoned sign-in records past their intended expiry, while an overly restrictive connection check can prevent network-storage uploads from recording the first-upload milestone.

2 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Site connection blocks the network first-upload milestone

lib/​classes/​class-google-connect.php:608

A site-level Google connection does not mean uploads use site-level storage: Settings::refresh() applies the network bucket and credentials even when the site retains its own OAuth connection. If the site's funnel is absent or expired, this extra connection check prevents an upload using newly provisioned network storage from recording the network's first-upload milestone. Allow the fallback when no site funnel exists; the bucket comparison below already checks whether the upload uses the network bucket.

3 resolved since last review
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

Comment thread lib/classes/class-google-connect.php Resolved
Copilot AI balanced review requested due to automatic review settings November 11, 2026 00:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The first-upload milestone can be recorded before all image sizes finish syncing or during a no-op sync, prematurely consuming funnel state, hiding later thumbnail failures, and skewing upload measurements.

3 open issues
1 resolved since last review
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
The upload milestone is now judged after the last metadata save of a request, so a size that fails after the original succeeded is reported as a failure. Media that was already in the bucket before setup started no longer counts.
Copilot AI balanced review requested due to automatic review settings November 11, 2026 07:50
Copilot AI balanced review requested due to automatic review settings November 12, 2026 00:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical credential-lifecycle flaws allow disconnect/provisioning races, leave unused keys active without retryable cleanup, and overwrite working credentials with unverified keys during checkpoint resume.

3 open issues
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
The setup lock, the job record and the sign-in attempt on record were read
and written with SQL against the options tables. They are ordinary options
now, and the claim on a setup scope is core's own lock
(WP_Upgrader::create_lock). The only direct database calls left are the
three named-lock functions, which touch no table: they are how a worker
that died is noticed at once.

Also:
- a resumed setup treats a configuration entered by hand after it stopped
  like any working configuration, and leaves it alone when its saved key
  cannot be verified
- a disconnect first removes the keys this site still has on record at
  Google; it stops with the connection kept while Google is failing, and
  names a key that Google refuses to remove
- a disconnect takes the setup scope on every database, so no setup can
  start while it runs
Copilot AI balanced review requested due to automatic review settings November 12, 2026 01:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved race conditions and stale cache or checkpoint state can erase newer sign-in tracking, overwrite completed jobs, and leave previously configured keys active after rotation.

4 open issues
2 resolved since last review
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
WordPress answers repeated option reads from the copy a request already
has. The job record and the sign-in attempt on record are how requests
learn what another one did in the meantime, so they are re-read from the
database where that matters. A status check that arrives just as a setup
finishes now reports it as finished instead of interrupted, and a
long-running sync no longer removes a sign-in attempt that was saved after
it started.

Also:
- a request that takes over the scope of a worker that died records that
  worker's job as interrupted
- a resumed setup puts the key it is about to replace on record, so a key
  rotated by hand in between is still revoked if the resume stops as well
Copilot AI balanced review requested due to automatic review settings November 12, 2026 01:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical ownership-check gaps allow stale workers to delete a successor’s claim or disconnect cleanup to continue after lock loss, permitting overlapping setup and disconnect operations.

3 open issues
5 resolved since last review
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
The check that stops a request once it no longer holds the setup scope
only applied to setup workers. It now covers every request that took the
scope, so a disconnect whose database connection was replaced stops before
it revokes or removes anything else.

A request also gives up a claim only while it is still its own: with the
database lock that is checked against the lock, and without it a claim
cannot have been taken over before it ran out, so only a younger one is
released.
Copilot AI balanced review requested due to automatic review settings November 12, 2026 01:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Human assessment is needed because cross-tab billing rechecks can restore stale setup state, while delayed request replays can repeat project creation and billing changes.

1 open issue
2 resolved since last review
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

@andypotanin

Copy link
Copy Markdown
Member Author

Update at 0f8235c, published as 4.5.1-rc15. The tag tree is identical to the build that was tested.

Since 4.5.1-rc14:

  • Setup state no longer queries the options tables. The claim on a setup is WordPress core's lock (WP_Upgrader::create_lock()), and the job record and the sign-in attempt on record are ordinary options. The remaining direct database calls are the three MySQL named-lock functions, which touch no table.
  • A resumed setup leaves a configuration that was entered by hand in the meantime alone when its saved key cannot be verified, and still revokes a key that was rotated by hand.
  • A disconnect takes the same hold on the setup as a setup does, on every database, and is held to the same ownership check. Before it gives up access it removes the keys still on record at Google; it stops while Google is failing and names a key Google refuses to remove.
  • A status check that arrives just as a setup finishes reports it as finished.
  • WordPress.org listing images are in .wordpress-org/; screenshot 5 was re-rendered after the project count wording was corrected.

Verification on the four develop-stateless test sites running this build is listed in the description. All review threads are resolved; the one declined suggestion (a single-statement compare-and-delete for the measurement record) is answered in its thread.

Copilot AI balanced review requested due to automatic review settings November 12, 2026 05:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical locking and key-lifecycle flaws can permit overlapping operations, revoke active credentials, reuse revoked keys, and leave newly issued keys untracked.

4 open issues
5 resolved since last review
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
Comment thread lib/classes/class-google-connect.php Resolved
Same fixture and framing as the previous capture. The pointer was sitting on the Edit Media heading.
The pin to udx/reusable-workflows#66 was only needed to cut release candidates from this branch.
Copilot AI balanced review requested due to automatic review settings November 12, 2026 05:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical credential-lifecycle gaps can leave issued Google keys untracked or revoke the site’s live key, while expired nonces can trap setup in endless polling.

2 open issues
4 resolved since last review
1 file not reviewed
  • static/styles/stateless-connect.css: Generated file

Review effort: Balanced

Comment thread lib/classes/class-google-connect.php Resolved
Comment thread static/scripts/stateless-connect.js Resolved
@andypotanin
andypotanin merged commit 52f0b82 into latest Nov 12, 2026
8 checks passed
@andypotanin

Copy link
Copy Markdown
Member Author

Released as 4.5.1.

  • GitHub: tag 4.5.1 at 5b109e7. Its tree is the tested candidate (4.5.1-rc15) plus the changelog date and the listing images; no plugin code differs.
  • WordPress.org: r3797565. 11 plugin files changed, none added or removed, and seven screenshots replaced. The zip WordPress.org serves is identical to the tag's plugin files.

The points from the last two review rounds are answered in their threads and planned for the next patch release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants