Skip to content

feat(cli): add channel update command to update channel site url#3001

Open
jorgemoya wants to merge 5 commits into
alphafrom
jorgemoya/ltrac-446-cli-auto-update-bc-channel-site-url-on-deploy
Open

feat(cli): add channel update command to update channel site url#3001
jorgemoya wants to merge 5 commits into
alphafrom
jorgemoya/ltrac-446-cli-auto-update-bc-channel-site-url-on-deploy

Conversation

@jorgemoya
Copy link
Copy Markdown
Contributor

@jorgemoya jorgemoya commented May 2, 2026

Jira: LTRAC-446

What/Why?

Adds a new top-level subcommand, catalyst channel update, and a boolean --update-site-url flag on catalyst deploy that both run the same interactive flow:

  1. Resolve the linked project (or fall back to selectOrCreateInfrastructureProject if none is linked or the linked UUID no longer exists).
  2. Fetch available channels, filter to platform === 'catalyst', and prompt the user to pick one.
  3. Read the project's deployment_hostnames — prompt the user to pick one (when invoked from deploy --update-site-url, the just-deployed hostname is placed first as the default).
  4. PUT the channel's site URL.

This is a more discoverable replacement for the previous design (which exposed --update-site-url <channelId> only on deploy and required the caller to already know the channel id). Standalone, the new command lets merchants point any Catalyst channel at any of the project's deployment hostnames — useful for switching channel→deployment mappings, recovering after a manual control-panel change, or wiring up a channel for the first time without re-deploying.

A few decisions worth flagging for review:

  • Standalone command supports --channel-id / --hostname overrides so it can be scripted. The deploy flag stays minimal (boolean only) because the just-deployed hostname is the obvious default.
  • The shared flow lives in lib/channel-site-flow.ts so both surfaces hit identical code paths. The deploy wrapper soft-fails on errors (bundle is already live, non-zero exit would mislead); the standalone command hard-fails so problems are loud.
  • The channel picker filters to platform === 'catalyst' — Stencil and other non-Catalyst storefronts can't meaningfully be pointed at a Catalyst deployment hostname, so they're omitted. Empty list surfaces "No Catalyst channels found on this store. Create one with catalyst create and try again."
  • OAuth scope store_channel_settings was added to DEVICE_OAUTH_SCOPES in the earlier commit on this branch. Existing logged-in users need to re-run catalyst auth login to mint a token that includes it; the 401/403 path surfaces a re-auth hint instead of a generic error.

Testing

Unit tests:

  • pnpm --filter @bigcommerce/catalyst test — 265/265 passing (11 in channel-site-flow.spec.ts, 6 in channel.spec.ts, plus updates to the existing deploy.spec.ts --update-site-url block).
  • pnpm --filter @bigcommerce/catalyst typecheck and pnpm --filter @bigcommerce/catalyst lint clean.

Manual end-to-end (real store + at least one Catalyst channel + a project with at least one deployment):

  1. catalyst auth login — confirm the new scope is requested.
  2. catalyst channel update — confirm the channel picker shows only Catalyst-platform storefronts and the hostname picker shows the linked project's deployment_hostnames. PUT succeeds; verify in the BC control panel.
  3. catalyst channel update --channel-id <id> --hostname <host> — confirm no prompts fire and PUT succeeds.
  4. catalyst channel update on a store with no Catalyst channels — confirm the "No Catalyst channels found" message with the catalyst create hint.
  5. catalyst channel update with no linked project — confirm it falls back to selectOrCreateInfrastructureProject. Declining a create exits cleanly with the catalyst project create hint.
  6. catalyst deploy --update-site-url — confirm the channel prompt fires, the just-deployed hostname is the default (top of the hostname list), PUT succeeds. Verify in control panel.
  7. catalyst deploy without the flag — confirm no channel API call is made.
  8. With a token missing store_channel_settings, run the standalone command and confirm the re-auth hint surfaces.
Kapture.2026-05-15.at.16.33.24.mp4

Migration

None. The flag is opt-in and the channel command is new. Users who want the auto-update should re-run catalyst auth login (new OAuth scope) before invoking either entry point.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
catalyst Ready Ready Preview, Comment May 15, 2026 8:07pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 2, 2026

⚠️ No Changeset found

Latest commit: d7d7dce

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jorgemoya jorgemoya changed the title LTRAC-446: feat(cli) - Auto-update BC channel site URL on deploy feat(cli): auto-update BC channel site URL on deploy May 2, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 2, 2026

Bundle Size Report

Comparing against baseline from d00beeb (2026-05-15).

Metric Baseline Current Delta
First Load JS 128.8 kB 117.7 kB -11.1 kB (-8.6%)
Total JS 434.4 kB 429.8 kB -4.6 kB (-1.1%)

Per-Route First Load JS

Route Baseline Current Delta
/(default)/(auth)/change-password/page 317.4 kB 301.2 kB -16.2 kB (-5.1%)
/(default)/(auth)/login/forgot-password/page 316.6 kB 300.4 kB -16.2 kB (-5.1%)
/(default)/(auth)/login/page 317 kB 300.8 kB -16.2 kB (-5.1%)
/(default)/(auth)/register/page 353.2 kB 336.9 kB -16.3 kB (-4.6%)
/(default)/(faceted)/brand/[slug]/page 329.1 kB 312.9 kB -16.2 kB (-4.9%)
/(default)/(faceted)/category/[slug]/page 337.5 kB 321.2 kB -16.3 kB (-4.8%)
/(default)/(faceted)/search/page 329.1 kB 312.9 kB -16.2 kB (-4.9%)
/(default)/[...rest]/page 312.1 kB 295.9 kB -16.2 kB (-5.2%)
/(default)/account/addresses/page 356.6 kB 340.4 kB -16.2 kB (-4.5%)
/(default)/account/orders/[id]/page 320.2 kB 304 kB -16.2 kB (-5.1%)
/(default)/account/orders/page 321.2 kB 305 kB -16.2 kB (-5%)
/(default)/account/settings/page 327.8 kB 311.6 kB -16.2 kB (-4.9%)
/(default)/account/wishlists/[id]/page 335.2 kB 319 kB -16.2 kB (-4.8%)
/(default)/account/wishlists/page 330.2 kB 314 kB -16.2 kB (-4.9%)
/(default)/blog/[blogId]/page 312.1 kB 295.9 kB -16.2 kB (-5.2%)
/(default)/blog/page 313.1 kB 296.9 kB -16.2 kB (-5.2%)
/(default)/cart/page 333 kB 316.8 kB -16.2 kB (-4.9%)
/(default)/compare/page 324.3 kB 308.1 kB -16.2 kB (-5%)
/(default)/gift-certificates/balance/page 316.1 kB 299.8 kB -16.3 kB (-5.2%)
/(default)/gift-certificates/page 312.1 kB 295.9 kB -16.2 kB (-5.2%)
/(default)/gift-certificates/purchase/page 355.7 kB 339.5 kB -16.2 kB (-4.6%)
/(default)/page 329.3 kB 313.1 kB -16.2 kB (-4.9%)
/(default)/product/[slug]/page 384.1 kB 367.9 kB -16.2 kB (-4.2%)
/(default)/webpages/[id]/contact/page 354.1 kB 337.9 kB -16.2 kB (-4.6%)
/(default)/webpages/[id]/normal/page 320.2 kB 304 kB -16.2 kB (-5.1%)
/(default)/wishlist/[token]/page 325.1 kB 308.9 kB -16.2 kB (-5%)
/maintenance/page 306.3 kB 289.9 kB -16.4 kB (-5.4%)
/_global-error/page 142.8 kB 125.2 kB -17.6 kB (-12.3%)
/_not-found/page 142.8 kB 125.2 kB -17.6 kB (-12.3%)

Threshold: 5% increase. Routes with ⚠️ exceed the threshold.

Copy link
Copy Markdown
Contributor

@chanceaclark chanceaclark left a comment

Choose a reason for hiding this comment

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

I think the intention behind this change is good, but I think it might be very destructive and we should probably avoid this. Maybe we can go in the direction that you can pass a flag to do this?

if (current?.url === deploymentUrl) {
consola.info(`Channel ${channelId} site URL already up to date (${deploymentUrl}).`);
} else {
await updateChannelSiteUrl(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This actually might mess up people testing deployments to native hosting and would be very destructive.

For example, I might have channel 123 being deployed via Vercel and I am dual-deploying this to Vercel and Native Hosting, deploying will change the channels site, thus messing up the production deployment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good callout, 100% agree, lets make this work opt in via an explicit flag that needs to be passed when deploying.

Having said that, do we want to remove all logic from project create and project link to add the channel to project.json? Or we keep that and just add the explicit flag?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think hosting should be tied to a channel.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have simplified it considerably by simply having a --update-site-url flag that requires a channelId. Still kept the store_channel_settings oauth scope to allow this for users who are authing via the CLI.

The unknown I have is that once we merge the deployment_hostnames changes, the returned array can contain N urls. How will one select the one they want to update to? Obviously we could default to the first one, but maybe it needs to be user selected?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What if instead of doing this on the deploy command we have a separate CLI command to manage this? We'd need to think through the UX for it, but I think it might be better than just a flag?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I see your point. I would wait to get the deployment_hostnames work merged first then.

What I would do is a new command that updates the site channel id. It will get all hostnames under the defined project, and then allow the user to select which hostname they want to be used for site url.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah something like that.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Unlighthouse Performance Comparison — Vercel

Comparing PR preview deployment Unlighthouse scores vs production Unlighthouse scores.

Summary Score

Aggregate score across all categories as reported by Unlighthouse.

Prod Desktop Prod Mobile Preview Desktop Preview Mobile
Score 90 93 91 95

Category Scores

Category Prod Desktop Prod Mobile Preview Desktop Preview Mobile
Performance 71 89 78 84
Accessibility 95 95 95 92
Best Practices 100 100 95 100
SEO 88 88 100 100

Core Web Vitals

Metric Prod Desktop Prod Mobile Preview Desktop Preview Mobile
LCP 5.0 s 3.8 s 3.4 s 4.4 s
CLS 0.037 0 0.001 0
FCP 1.2 s 1.2 s 1.1 s 1.1 s
TBT 0 ms 0 ms 0 ms 0 ms
Max Potential FID 40 ms 40 ms 30 ms 40 ms
Time to Interactive 5.1 s 3.8 s 3.4 s 4.5 s

Full Unlighthouse report →

Add `--update-site-url <channelId>` (env: CATALYST_UPDATE_SITE_URL) to
`catalyst deploy`. After a successful deployment the CLI PUTs the
resulting deployment URL to the BC channel site endpoint
(v3/channels/:channelId/site). Failures are soft: the deploy itself
still succeeds and the user is told to update manually or re-run
`catalyst auth login` to refresh the access token with the new
`store_channel_settings` scope (added to DEVICE_OAUTH_SCOPES).

Rebased onto the current alpha branch; the 5 original PR commits were
squashed into one because origin/alpha was force-rewritten after the
PR opened (every shared commit had a new SHA), making a per-commit
replay infeasible.

Refs LTRAC-446
Co-Authored-By: Claude <noreply@anthropic.com>
@jorgemoya jorgemoya force-pushed the jorgemoya/ltrac-446-cli-auto-update-bc-channel-site-url-on-deploy branch from fa310a9 to e49f807 Compare May 13, 2026 16:34
jorgemoya and others added 2 commits May 15, 2026 11:27
Add a new `catalyst channel update-site-url` subcommand that PUTs a
channel's site URL to one of the linked project's deployment hostnames.
The flow is interactive by default — pick channel, pick hostname — and
accepts `--channel-id` / `--hostname` for scripted use. When no project
is linked or passed, it falls back to `selectOrCreateInfrastructureProject`
(same picker the deploy command uses) so the user can resolve it inline.

Replace the previous `--update-site-url <channelId>` value-bearing option
on `catalyst deploy` with a boolean flag that triggers the same shared
flow after a successful deploy, defaulting the hostname prompt to the
freshly-deployed hostname returned by the deployment SSE.

The shared `runChannelSiteUrlFlow` helper lives in lib/channel-site-flow.ts
so both commands hit identical code paths; the standalone command
hard-fails on errors while the deploy wrapper soft-fails (deploy already
succeeded, bundle is live).

Refs LTRAC-446
Co-Authored-By: Claude <noreply@anthropic.com>
Add coverage for the shared interactive flow and the two surfaces it
backs:

- lib/channel-site-flow.spec.ts: happy path (channel prompt + hostname
  prompt + PUT), --channel-id and --hostname short-circuits, both
  overrides skip all prompts, preferHostname appears first in the
  hostname options, empty channels throws, project with no hostnames
  throws, fallback to selectOrCreateInfrastructureProject when no
  projectUuid is provided, NoLinkedProjectError when no projects exist
  and user declines to create one, and a warn-and-reprompt path when
  the linked project no longer exists.
- commands/channel.spec.ts: end-to-end via `program.parseAsync` —
  happy path, projectUuid resolved from .bigcommerce/project.json,
  --channel-id + --hostname skip all prompts, no-projects exit path
  via consola.info hint, error propagation from the PUT.
- commands/deploy.spec.ts: replace the old `--update-site-url <id>`
  tests with the new boolean-flag flow: triggers interactive prompts
  after deploy, places the freshly-deployed hostname first in the
  hostname prompt, no API call when the flag is omitted, soft-fail
  with re-auth hint on 401.
- tests/mocks/handlers.ts: add a default `GET /v3/channels` handler
  returning two storefronts so tests don't have to override the
  channel list shape per case.

Refs LTRAC-446
Co-Authored-By: Claude <noreply@anthropic.com>
Only Catalyst-platform channels can meaningfully be pointed at a
Catalyst deployment hostname, so filter the channel picker in
runChannelSiteUrlFlow to drop Stencil / non-Catalyst storefronts. When
the resulting list is empty, surface "No Catalyst channels found" with
a hint to run `catalyst create`.

Refs LTRAC-446
Co-Authored-By: Claude <noreply@anthropic.com>
…date`

The subcommand is shorter and reads more naturally as a verb on the
`channel` namespace. The deploy-side flag (`--update-site-url`) keeps its
descriptive name since flag context is less obvious at a glance.

Refs LTRAC-446
Co-Authored-By: Claude <noreply@anthropic.com>
@jorgemoya jorgemoya changed the title feat(cli): auto-update BC channel site URL on deploy feat(cli): add channel update command to update channel site url May 15, 2026
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