Skip to content

harper preview: deploy, drop and list PR previews from any CI #3054

Description

@dawsontoth

Proposal

CLI subcommands that wrap the preview lifecycle, so GitHub Actions, GitLab or any other CI can use it.

  • harper preview deploy app=<component> label=<dns-label> pr=<n> branch=<name>
    • Computes the slug.
    • Deploys the current directory as <component>-pr-<n>, with isolated, host, branchedDatabases and preview metadata.
    • Streams progress and prints the URL.
  • harper preview drop pr=<n> and harper preview list.
  • Authentication uses the existing OIDC exchange (feat: OIDC trusted publishing — deploy from CI with no stored credential #2173), or any configured credential.

Slug rules. The slug is one lowercase DNS label:

  • Anything outside [a-z0-9] becomes -, runs collapse, and leading and trailing - are trimmed.
  • At most 40 characters, with a 6-character hash suffix when cut.
  • Reserved names (www, dev, stage, api, …) fall back to pr-<n>.

For example, david/1778-custom-regions becomes david-1778-custom-regions.

export function previewSlug(branch, prNumber, max = 40) {
	let slug = branch.normalize('NFKD').replace(/[̀-ͯ]/g, '').toLowerCase()
		.replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
	if (!slug || RESERVED.has(slug)) slug = `pr-${prNumber}`;
	if (slug.length > max) {
		const hash = createHash('sha256').update(branch).digest('hex').slice(0, 6);
		slug = `${slug.slice(0, max - 7).replace(/-+$/, '')}-${hash}`;
	}
	return slug;
}

The component is named by PR number, not by slug. That keeps socket paths short, and renaming a branch moves the host but not the data branch. If another open PR already holds the slug, append -pr<n>.

Depends on

The payload-deploy (#3043), no-restart and scoped-permission sub-issues of #3042.

Update, 2026-10-07

The recipe in HarperFast/documentation#715 is the behavior this command should absorb, so that a workflow shrinks to one call:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    Priority

    P2

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions