Skip to content

[3.x] New publish command#2500

Draft
emmadesilva wants to merge 40 commits into
v3-devfrom
v3/new-publish-command
Draft

[3.x] New publish command#2500
emmadesilva wants to merge 40 commits into
v3-devfrom
v3/new-publish-command

Conversation

@emmadesilva

Copy link
Copy Markdown
Member

No description provided.

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.79550% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 99.97%. Comparing base (078d7c9) to head (4675cd6).

Files with missing lines Patch % Lines
...amework/src/Framework/Services/OverwritePolicy.php 92.85% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##              v3-dev    #2500      +/-   ##
=============================================
- Coverage     100.00%   99.97%   -0.03%     
- Complexity      1608     1757     +149     
=============================================
  Files            168      169       +1     
  Lines           4039     4337     +298     
=============================================
+ Hits            4039     4336     +297     
- Misses             0        1       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@emmadesilva emmadesilva force-pushed the v3/new-publish-command branch from 1b5066d to 6f48418 Compare July 4, 2026 17:58
emmadesilva and others added 15 commits July 4, 2026 19:59
Introduces the shared, pure decision primitive for the new publish command:
given a source and destination path, it returns copy (missing), skip
(unchanged), or blocked (user-modified). Comparison is EOL-agnostic via
unixsum so line-ending-only differences (e.g. CRLF checkouts) count as
unchanged rather than modified. No console/view/page knowledge; no checksum
manifest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Introduces the data model for starter pages used by the new publish command:
an immutable PublishablePage value object (key, label, description, source,
defaultTarget, alternativeTargets, allowCustomTarget) and the PublishablePages
registry (all/get/register) seeded with the default catalog — welcome, posts,
blank, and 404. Pages get a value object + registry (unlike the fixed view
file-maps) because a page can have multiple valid destinations, carries display
metadata, and the registry is an extension point for Hyde Cloud and plugins.

Source paths are stored framework-relative for resolution via Hyde::vendorPath()
at publish time; destination resolution and publishing land in a later step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Per review decision: `blank` is a general-purpose empty starter you drop
anywhere rather than a third homepage variant, so it declares no default
target. Makes PublishablePage::$defaultTarget nullable and sets blank's to
null; destination resolution (Step 5) will always prompt interactively and
require --to non-interactively. This also removes the welcome/blank collision
on _pages/index.blade.php that the default catalog would otherwise create.

Updates spec 5.1/5.2/5.4 to match so the acceptance sweep stays consistent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Introduces the flag-driven publish command shell that routes to the views and
pages flows (both stubbed here, filled in by Steps 4-5). It owns the full flag
surface (--layouts --components --all --page[=NAME] --to=PATH --force) and all
guardrails:

- Raw tag/provider/config publishing is redirected to vendor:publish. These are
  deliberately not declared as options (which would advertise them in --help,
  the exact raw-publishing surface this command exists to hide); instead run()
  intercepts the raw input before Symfony's strict bind. Only those three tokens
  are short-circuited, so a genuine typo like --layout still hits Symfony's
  native "unknown option" error rather than being swallowed.
- --layouts and --components are mutually exclusive.
- --to is only valid alongside --page.
- Non-interactive with no actionable flag fails with a usage hint before any
  prompt is attempted; the interactive wizard (Views / A starter page / Cancel)
  routes to the stub handlers, with Cancel exiting cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Replace the publishViews() stub with a real ViewsPublisher (§4, §7):

- Reads the two declared groups (layouts, components) via ViewPublishGroup;
  --layouts/--components prefilter the offered set, --all skips the picker,
  and a non-interactive scoped run behaves exactly like adding --all.
- Adds a minimal shared InteractiveMultiselect helper: a grouped multi-select
  with an "All views" sentinel (selecting it means everything regardless of
  other checkbox state) and group-prefixed labels (layouts/app.blade.php).
- Decides every file's outcome first (OverwritePolicy: copy/skip/blocked),
  resolves modified-file conflicts second (interactive Skip/Overwrite/Cancel
  or --force; non-interactive without --force is a hard §7 error), and writes
  last, so Cancel never leaves a half-published tree.
- Cardinality-aware output that reports the real breakdown: "Published all N"
  prints only when the entire offered set was genuinely copied; mixed runs
  report copied / already-current / left-modified (with a --force hint).

Re-points the four Step 3 views-routing tests off the removed stub string to
assert real views behavior, adds a dedicated views-flow test suite, and notes
the mixed-run reporting rule in §4 of the spec.

Additional commits:

- Make the publish multiselect "All" sentinel row optional

Add a nullable $allLabel to InteractiveMultiselect::select() so callers can omit the
"select all" row entirely. The views picker keeps its "All views" affordance; the pages
picker (Step 5) passes no label, since "publish all starter pages at once" is never a
sensible selection and would only trip destination resolution and conflict detection.

This is a backward-compatible parameter on the shared helper, not a fork — both callers
depend on the same picker.

- Simplify publish multiselect all sentinel

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Replace the publishPage() stub with a real PagesPublisher backed by the PublishablePages
registry (§5):

- Selection: --page=NAME publishes one page directly; a bare --page or the wizard opens the
  interactive multi-select picker.
- Destination resolution (§5.4): --to → non-interactive default → interactive prompt
  (default / alternative / custom path) → default. A page with no default (blank) fails
  helpfully non-interactively, pointing to --to.
- --to validation: must resolve under _pages/ and end in .blade.php.
- --to is only valid for a single named page; a bare --page (multi-select) with --to is
  rejected. Documented as a new line in spec §5.4.
- Conflict detection (§5.6): two pages resolving to the same target fail before any write.
- Interactive confirm (§5.5): select → resolve → "Ready to publish… Proceed?".
- Overwrite policy (§7): reuses OverwritePolicy exactly as the views flow does
  (missing→copy, identical→skip, modified→confirm-or-force).
- Optional rebuild (§5.7): offered interactively only, defaulting to NO. Implemented inline
  rather than via AsksToRebuildSite, which defaults to YES — a why-comment guards against a
  future consolidation reintroducing the yes-default.

Registry lookups compare ->key (never array access) so the string '404' key survives PHP's
numeric-key coercion. The Step 3 routing tests that asserted the old stub are updated to
assert the real, non-destructive routing behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Destination resolution UX (§5.4): only prompt interactively when a page is genuinely
ambiguous — it has alternative targets or no default. A page whose default is its one
sensible destination (welcome, 404) publishes in a single frictionless step instead of
asking "Where should this go?" for a near-certain answer. allowCustomTarget now governs
only whether a "Custom path…" entry is offered and whether --to is accepted, not whether
the prompt appears; custom placement of an otherwise-unambiguous page is reached via --to.
Consequently --to is rejected for a page that disallows custom targets (404).

Polish:
- The overwrite-conflict "Cancel" choice now prints "Cancelled. No pages were published."
  instead of exiting silently, matching the views flow and the "Proceed? no" path.
- The destination-conflict message uses "both target" for a pair and "all target" for
  three or more colliding pages.
- Added tests: --to rejected for 404, the picker round-tripping the numeric '404' key
  (the coerced-int-key path the named lookup can't reach), and the pages picker omitting
  the "All" row that the views picker offers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Add a test proving the two --to rejections cannot disagree. Bare --page + --to is caught by
the top-level "one path can't serve several pages" guard (message A) before selectPages()
runs, so the per-page allowCustomTarget rejection (message B) can never pre-empt it. The
test runs interactively — it would hang on an unanswered picker prompt if the picker were
reached — and asserts message A wins and message B is absent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Add a single `hyde-config` publish group on ConfigurationServiceProvider
that publishes exactly the six Hyde-owned config files (hyde, docs,
markdown, view, cache, commands) and not torchlight.php, per spec §6.

The legacy `configs` / `hyde-configs` / `support-configs` tags are left
in place; the deprecated publish:configs command still relies on them
until Step 7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Per spec §8, publish:views, publish:configs, and publish:homepage become
thin delegators that print a one-line deprecation notice and forward to the
new surface:

- publish:views [group] -> publish --layouts / --components (no group -> --all,
  keeping legacy non-interactive scripts non-interactive)
- publish:configs -> vendor:publish --tag=hyde-config
- publish:homepage [template] -> publish --page=[template], forwarding --force

The old command classes stay registered and keep working through v3; target
removal in v4. Their tests are rewritten to assert the notice and delegation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Rewrite the publishing docs around php hyde publish (views + --page) and
php hyde vendor:publish --tag=hyde-config for config. Fix the nonexistent
publish:components reference in advanced-markdown.md, and update remaining
publish:views/publish:configs/publish:homepage references throughout the
primary docs. The deprecated aliases now appear only in a migration note
in the console commands reference (historical release notes left as-is).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gemini <218195315+gemini-cli@users.noreply.github.com>
Co-Authored-By: Codex <codex@openai.com>
Fill the coverage gaps flagged on PagesPublisher and ViewsPublisher by
testing the feature paths they exercise, not lines for their own sake:

Pages (§5, §7):
- interactive overwrite-conflict prompt: overwrite / skip / cancel
  (the §7 interactive flow was tested for views but not for pages)
- mixed run reporting published pages alongside already-current ones,
  with pluralized cardinality
- three-or-more pages colliding on one target ("all target" wording)
- accepting the §5.7 rebuild offer actually runs the build
- --to path traversal (..) rejected

Views (§4):
- mixed run reporting copied views alongside already-current ones

The only lines left uncovered are unreachable defensive guards behind
`required` multi-selects (empty-selection is rejected by the prompt
before the guard) and joinLabels' <2 branch — not feature paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@emmadesilva emmadesilva force-pushed the v3/new-publish-command branch from 6f48418 to c4c8a66 Compare July 4, 2026 18:21
emmadesilva and others added 9 commits July 4, 2026 23:15
Delete publish:views, publish:configs, and publish:homepage entirely —
classes, ConsoleServiceProvider registrations, and their test files. The
behavior they covered lives in the new command's suites. Add one pin test
asserting publish:views now raises the native CommandNotFoundException,
proving the command is gone and no shim intercepts it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified each candidate repo-wide before deleting. Removed with zero
remaining consumers:
- InteractivePublishCommandHelper (+ its unit test)
- ViewDiffService / checksumMatchesAny (+ its unit test); unixsum helpers
  stay (OverwritePolicy and GenerateBuildManifest still use unixsum_file)
- AsksToRebuildSite trait (no use/method consumer; reworded the PagesPublisher
  comment that named it)
- The hyde-welcome-page / hyde-posts-page / hyde-blank-page publish tags
  (the new PublishablePages resolves homepages by direct source path)

Kept: hyde-page-404 (general 404 publish surface, not a homepage-command tag).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove the configs, hyde-configs, and support-configs tag registrations from
ConfigurationServiceProvider. With the alias command gone they are dead public
surface, and v3 is the moment to drop them rather than carry them another major
cycle. Keep hyde-config (the exact six-file set, torchlight excluded) and its
two tests unchanged. Add a test pinning that the removed tags publish nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add a "Removed Publishing Commands" section to the v3 upgrade guide (and
  UPGRADE.md) with the full replacement table, including the posts/blank --to
  mappings that don't map 1:1, the config-tag consolidation, and the
  never-overwrite-without-force behavioral note.
- Fix the config-update instructions in updating-hyde.md and troubleshooting.md
  to use --force, noting existing files are skipped without it.
- Replace the "Deprecated publishing commands" table in console-commands.md
  with a single line linking to the upgrade guide's removed-commands section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Rewrite spec §8 from "Deprecated aliases (kept for v3)" to "Removed commands
  (v3)": removal, native command-not-found behavior, and pointer to the upgrade
  guide. Drop the notice-printing requirement.
- Update §11 criterion 16 to the removal + native-error + documented-replacement
  wording (including the posts/blank --to mappings).
- Update §6 to note the legacy config tags are removed, hyde-config is the only
  tag, and updating existing files requires --force. Fix the incidental stale
  alias references in §10 and §12.
- Repair the StyleCI-mangled multi-line @return docblocks in ViewsPublisher and
  PagesPublisher by putting each description back on the tag line. No behavior
  change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The final sweep of packages/ surfaced three shipped, user-facing references to
the now-removed commands (criterion 1 requires only historical notes to remain):

- The default welcome homepage told every new user to run `publish:homepage`;
  now points to `publish --page` (and the text-representation meta test's
  expected string tracks the blade file).
- ValidationService's missing-404 and missing-index tips pointed to
  `publish:views` / `publish:homepage`; now `publish --page=404` / `publish --page`.

packages/ is clean of live references (only CHANGELOG history and the pin test
remain); `hyde list` shows only publish and vendor:publish; suite green (the sole
failure is the pre-existing, unrelated FeaturedImageUnitTest PHP 8.5 issue).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
emmadesilva and others added 8 commits July 5, 2026 00:29
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
@emmadesilva emmadesilva force-pushed the v3/new-publish-command branch from c287970 to 62d568b Compare July 4, 2026 22:36
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.

2 participants