ClientUtils.Harness.Preview (client_utils v0.1.35)

View Source

Where this working copy's preview answers, asked of the server that can say.

Story 968. A project's preview runs from its main checkout, and the address it answers on has to exist before anybody looks for it — the whole point is that it appears without being found or configured.

Why this asks instead of provisioning

The address is reached over a named Cloudflare tunnel on CodeMySpec's own zone, and creating one needs CodeMySpec's Cloudflare credential. That credential cannot ship here: this library is a dependency of every generated application, so a credential in it is a credential every customer holds, on a zone none of them own.

The server already has it. So this sends the working copy's id and its deploy key and takes back an address plus what ClientUtils.CloudflareTunnel needs to run the tunnel in named mode. The keys come back named exactly as that module's options, deliberately — a translation step between the two is somewhere for them to drift apart.

Every refusal is :none, and that is not laziness

Onboarding does a dozen things and a preview is one of them. A copy that comes back with its identity, its databases, its hooks and its proxy configured but no preview is far more useful than a copy whose onboarding aborted — and the reasons it can be refused are all ordinary:

  • this checkout is not the project's main one (409), which is true of every worktree and most of them by design
  • the server has no Cloudflare account configured (503), which is an operator's problem and not this checkout's
  • the server is not reachable at all, which is the normal state of a laptop onboarding offline

None of those mean the checkout is broken, so none of them stop the run. The caller reports what happened and moves on.

Summary

Functions

This application's preview, wherever it is running.

What a checkout's .cms_harness.json says about its preview, as application config.

The preview named by the environment, for an application running in a container.

Functions

config(root, opts \\ [])

@spec config(
  String.t(),
  keyword()
) :: keyword()

This application's preview, wherever it is running.

Two sources, because there are two ways an app runs and only one of them has a checkout. On a developer's machine the values are in .cms_harness.json, written there by onboarding. In a container there is no checkout at all — the image holds the application and nothing else — so they arrive as environment variables.

The file wins where both are present. A container has no file, and a developer's machine has no reason to carry these in its environment, so they do not compete in practice; the ordering matters only for the case where somebody has exported them by hand, and there the checkout is the thing that was actually provisioned.

Empty when neither says anything, which leaves the tunnel disabled.

ensure(server_url, working_copy_id, deploy_key, opts \\ [])

@spec ensure(String.t() | nil, String.t() | nil, String.t() | nil, keyword()) ::
  {:ok, map()} | :none

Ask for this copy's preview.

{:ok, map} carries preview_url, preview_tunnel_id and preview_tunnel_secret — the keys .cms_harness.json holds — plus account_tag for the tunnel's credentials file.

:none for every refusal, reachable or not. See the moduledoc: none of them is a reason to stop onboarding.

from_checkout(root, opts \\ [])

@spec from_checkout(
  String.t(),
  keyword()
) :: keyword()

What a checkout's .cms_harness.json says about its preview, as application config.

For a generated application's config/runtime.exs, which is where the tunnel's options have to come from: onboarding writes the file, and the app reads it at boot rather than being handed the values by whoever started it.

Empty when there is no preview, no file, or nothing readable — and empty is what leaves the tunnel disabled, so an app with no preview starts normally instead of dialling a tunnel that does not exist.

embedder is whatever the server said when it provisioned the preview — the origin allowed to frame this app, which is the site that issued it, not the preview address itself. Falls back to a default only for a copy provisioned before the server started returning it; that default is right for exactly one deployment, which is why it is last.

from_env(opts \\ [])

@spec from_env(keyword()) :: keyword()

The preview named by the environment, for an application running in a container.

CMS_PREVIEW_TUNNEL_ID is what decides there is one, the same way preview_tunnel_id does in a checkout: an address with no tunnel behind it resolves to an edge holding no connection to this app, and the frame sits blank with nothing saying why.