hatchkit 0.1.47 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adopt.d.ts +61 -1
- package/dist/adopt.d.ts.map +1 -1
- package/dist/adopt.js +90 -86
- package/dist/adopt.js.map +1 -1
- package/dist/assets/env.d.ts +2 -2
- package/dist/assets/env.d.ts.map +1 -1
- package/dist/assets/index.js +11 -11
- package/dist/assets/index.js.map +1 -1
- package/dist/assets/mirror.js +1 -1
- package/dist/completion.d.ts.map +1 -1
- package/dist/completion.js +20 -2
- package/dist/completion.js.map +1 -1
- package/dist/config.d.ts +32 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +364 -1
- package/dist/config.js.map +1 -1
- package/dist/deploy/coolify.d.ts +5 -0
- package/dist/deploy/coolify.d.ts.map +1 -1
- package/dist/deploy/coolify.js +67 -4
- package/dist/deploy/coolify.js.map +1 -1
- package/dist/deploy/ghcr.d.ts +1 -0
- package/dist/deploy/ghcr.d.ts.map +1 -1
- package/dist/deploy/ghcr.js +2 -2
- package/dist/deploy/ghcr.js.map +1 -1
- package/dist/deploy/github.d.ts.map +1 -1
- package/dist/deploy/github.js +3 -2
- package/dist/deploy/github.js.map +1 -1
- package/dist/deploy/rollback.d.ts.map +1 -1
- package/dist/deploy/rollback.js +9 -0
- package/dist/deploy/rollback.js.map +1 -1
- package/dist/dev-setup.d.ts +13 -5
- package/dist/dev-setup.d.ts.map +1 -1
- package/dist/dev-setup.js +268 -59
- package/dist/dev-setup.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +65 -1
- package/dist/doctor.js.map +1 -1
- package/dist/email/index.js +5 -5
- package/dist/email/index.js.map +1 -1
- package/dist/email/setup.d.ts +1 -1
- package/dist/email/setup.d.ts.map +1 -1
- package/dist/email/setup.js +3 -3
- package/dist/email/setup.js.map +1 -1
- package/dist/explain.d.ts.map +1 -1
- package/dist/explain.js +9 -8
- package/dist/explain.js.map +1 -1
- package/dist/index.js +523 -91
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +1 -0
- package/dist/inventory.d.ts.map +1 -1
- package/dist/inventory.js +2 -0
- package/dist/inventory.js.map +1 -1
- package/dist/onboarding/plan.d.ts +54 -0
- package/dist/onboarding/plan.d.ts.map +1 -0
- package/dist/onboarding/plan.js +143 -0
- package/dist/onboarding/plan.js.map +1 -0
- package/dist/onboarding/review.d.ts +27 -0
- package/dist/onboarding/review.d.ts.map +1 -0
- package/dist/onboarding/review.js +55 -0
- package/dist/onboarding/review.js.map +1 -0
- package/dist/prompts.d.ts +13 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +107 -89
- package/dist/prompts.js.map +1 -1
- package/dist/provision/glitchtip.d.ts +1 -0
- package/dist/provision/glitchtip.d.ts.map +1 -1
- package/dist/provision/glitchtip.js +16 -0
- package/dist/provision/glitchtip.js.map +1 -1
- package/dist/provision/index.d.ts +26 -3
- package/dist/provision/index.d.ts.map +1 -1
- package/dist/provision/index.js +215 -11
- package/dist/provision/index.js.map +1 -1
- package/dist/provision/openpanel.d.ts +1 -0
- package/dist/provision/openpanel.d.ts.map +1 -1
- package/dist/provision/openpanel.js +21 -0
- package/dist/provision/openpanel.js.map +1 -1
- package/dist/provision/plausible.d.ts +11 -0
- package/dist/provision/plausible.d.ts.map +1 -0
- package/dist/provision/plausible.js +108 -0
- package/dist/provision/plausible.js.map +1 -0
- package/dist/provision/resend.d.ts +4 -0
- package/dist/provision/resend.d.ts.map +1 -1
- package/dist/provision/resend.js +11 -6
- package/dist/provision/resend.js.map +1 -1
- package/dist/provision/search-console.d.ts +17 -0
- package/dist/provision/search-console.d.ts.map +1 -0
- package/dist/provision/search-console.js +142 -0
- package/dist/provision/search-console.js.map +1 -0
- package/dist/scaffold/app.d.ts +1 -0
- package/dist/scaffold/app.d.ts.map +1 -1
- package/dist/scaffold/app.js +6 -3
- package/dist/scaffold/app.js.map +1 -1
- package/dist/scaffold/infra.js +2 -0
- package/dist/scaffold/infra.js.map +1 -1
- package/dist/scaffold/manifest.d.ts +18 -2
- package/dist/scaffold/manifest.d.ts.map +1 -1
- package/dist/scaffold/manifest.js +7 -1
- package/dist/scaffold/manifest.js.map +1 -1
- package/dist/scaffold/server-add.d.ts +21 -0
- package/dist/scaffold/server-add.d.ts.map +1 -0
- package/dist/scaffold/server-add.js +275 -0
- package/dist/scaffold/server-add.js.map +1 -0
- package/dist/scaffold/starter-files.d.ts +3 -3
- package/dist/scaffold/starter-files.js +3 -3
- package/dist/scaffold/update.d.ts +1 -0
- package/dist/scaffold/update.d.ts.map +1 -1
- package/dist/scaffold/update.js +8 -5
- package/dist/scaffold/update.js.map +1 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +27 -1
- package/dist/status.js.map +1 -1
- package/dist/templates/base/env.example.hbs +3 -0
- package/dist/utils/cloudflare-api.d.ts +5 -0
- package/dist/utils/cloudflare-api.d.ts.map +1 -1
- package/dist/utils/cloudflare-api.js +19 -0
- package/dist/utils/cloudflare-api.js.map +1 -1
- package/dist/utils/coolify-api.d.ts +3 -2
- package/dist/utils/coolify-api.d.ts.map +1 -1
- package/dist/utils/coolify-api.js +19 -5
- package/dist/utils/coolify-api.js.map +1 -1
- package/dist/utils/flags.d.ts.map +1 -1
- package/dist/utils/flags.js +16 -0
- package/dist/utils/flags.js.map +1 -1
- package/dist/utils/run-ledger.d.ts +3 -0
- package/dist/utils/run-ledger.d.ts.map +1 -1
- package/dist/utils/run-ledger.js.map +1 -1
- package/dist/utils/secrets.d.ts +5 -0
- package/dist/utils/secrets.d.ts.map +1 -1
- package/dist/utils/secrets.js +5 -0
- package/dist/utils/secrets.js.map +1 -1
- package/package.json +24 -3
package/dist/adopt.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Feature } from "./prompts.js";
|
|
1
|
+
import type { DeploymentMode, Feature, Surface } from "./prompts.js";
|
|
2
|
+
import { type ProvisionService } from "./provision/index.js";
|
|
2
3
|
import { type ProjectManifest } from "./scaffold/manifest.js";
|
|
3
4
|
export interface DetectedState {
|
|
4
5
|
/** Absolute path to the project root. */
|
|
@@ -74,9 +75,68 @@ export interface DetectedState {
|
|
|
74
75
|
* the user already chose, instead of starting from blank. */
|
|
75
76
|
existingManifest?: ProjectManifest;
|
|
76
77
|
}
|
|
78
|
+
type AdoptSurface = Surface;
|
|
79
|
+
type AdoptDeploymentMode = DeploymentMode;
|
|
80
|
+
export interface AdoptPlan {
|
|
81
|
+
name: string;
|
|
82
|
+
domain: string;
|
|
83
|
+
/** One-liner that ends up on the Coolify project + application
|
|
84
|
+
* pages (instead of the generic "Adopted by hatchkit" blurb).
|
|
85
|
+
* Empty string means "no override" — wireProjectIntoCoolify will
|
|
86
|
+
* use the default on first create and leave any existing
|
|
87
|
+
* description untouched on reconcile. */
|
|
88
|
+
description: string;
|
|
89
|
+
features: Feature[];
|
|
90
|
+
/** What kind of project this is — drives where env files go, which
|
|
91
|
+
* Coolify build-pack we ask for, and which surfaces `hatchkit add`
|
|
92
|
+
* provisions clients into. */
|
|
93
|
+
surfaces: AdoptSurface;
|
|
94
|
+
/** Where this project will run. Coolify is the default for full-
|
|
95
|
+
* stack and server-only adopts; gh-pages is only offered when
|
|
96
|
+
* surfaces is client-only (Pages can't host a backend). Switches
|
|
97
|
+
* the second half of executePlan from Coolify wiring to
|
|
98
|
+
* `runPagesSetupProgrammatic`. */
|
|
99
|
+
deploymentMode: AdoptDeploymentMode;
|
|
100
|
+
/** Required when `surfaces !== "client-only"`. */
|
|
101
|
+
serverDir?: string;
|
|
102
|
+
/** Required when `surfaces !== "server-only"`. */
|
|
103
|
+
clientDir?: string;
|
|
104
|
+
/** Always-on side effect — initialize dotenvx encryption if the
|
|
105
|
+
* server dir doesn't already have an encrypted .env.production. */
|
|
106
|
+
bootstrapDotenvx: boolean;
|
|
107
|
+
/** Initialize git + create a GitHub remote (private repo). Skipped
|
|
108
|
+
* silently when a remote already exists. */
|
|
109
|
+
setupGitHub: boolean;
|
|
110
|
+
/** Wire the repo into the user's existing Coolify + DNS via direct
|
|
111
|
+
* API calls (no Terraform / no submodule). Defaults ON when there's
|
|
112
|
+
* no Coolify app match yet. */
|
|
113
|
+
wireCoolify: boolean;
|
|
114
|
+
/** Whether to treat the GitHub repo as private when wiring Coolify.
|
|
115
|
+
* Private → Coolify clones via the configured GitHub App's SSH
|
|
116
|
+
* deploy key. Public → Coolify clones via HTTPS, no auth needed.
|
|
117
|
+
* Defaulted from `gh repo view --json visibility` when the remote
|
|
118
|
+
* exists; defaults to true when adopt is creating a fresh
|
|
119
|
+
* `--private` repo. Picking the wrong value is the #1 cause of
|
|
120
|
+
* "Permission denied (publickey)" deploy failures, hence its own
|
|
121
|
+
* stepper row instead of being silently inferred from setupGitHub. */
|
|
122
|
+
isPrivate: boolean;
|
|
123
|
+
/** Container port the app exposes — defaults to "3000". */
|
|
124
|
+
appPort: string;
|
|
125
|
+
/** Scaffold the build pipeline (Dockerfile + docker-compose.yml +
|
|
126
|
+
* .github/workflows/deploy.yml) when those files don't exist yet.
|
|
127
|
+
* Always coupled with wireCoolify since the compose file is what
|
|
128
|
+
* Coolify reads. The detection step ensures this is a no-op when
|
|
129
|
+
* the user already has their own. */
|
|
130
|
+
scaffoldBuildPipeline: boolean;
|
|
131
|
+
/** Provisioning to run after manifest write. */
|
|
132
|
+
services: ProvisionService[];
|
|
133
|
+
/** Push dotenvx key to Coolify after everything's written. */
|
|
134
|
+
pushKey: boolean;
|
|
135
|
+
}
|
|
77
136
|
export declare function runAdopt(cwd: string, opts?: {
|
|
78
137
|
resume?: boolean;
|
|
79
138
|
regeneratePipeline?: boolean;
|
|
80
139
|
}): Promise<void>;
|
|
81
140
|
export declare function detectProject(projectDir: string): Promise<DetectedState>;
|
|
141
|
+
export {};
|
|
82
142
|
//# sourceMappingURL=adopt.d.ts.map
|
package/dist/adopt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["../src/adopt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["../src/adopt.ts"],"names":[],"mappings":"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"}
|
package/dist/adopt.js
CHANGED
|
@@ -35,13 +35,15 @@
|
|
|
35
35
|
*/
|
|
36
36
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
37
37
|
import { join, relative } from "node:path";
|
|
38
|
-
import {
|
|
38
|
+
import { confirm, input, select } from "@inquirer/prompts";
|
|
39
39
|
import chalk from "chalk";
|
|
40
40
|
import { ensureGitHub, getCoolifyConfig, getGhcrConfig } from "./config.js";
|
|
41
41
|
import { ghSecretExists, ownerFromRemote, repoSlugFromRemote, setCoolifyDeploySecrets, } from "./deploy/gh-actions-secrets.js";
|
|
42
42
|
import { pushInitialBranch } from "./deploy/github.js";
|
|
43
43
|
import { pushProjectKeyToCoolify, pushProjectKeyToGh } from "./deploy/keys.js";
|
|
44
44
|
import { handleAdoptFailure } from "./deploy/rollback.js";
|
|
45
|
+
import { adoptPlanToOnboardingPlan, onboardingPlanToAdoptPlan, renderOnboardingDeploymentModeSummary, renderOnboardingSurfaceSummary, summarizeOnboardingDomain, summarizeOnboardingFeatures, } from "./onboarding/plan.js";
|
|
46
|
+
import { runProjectOnboardingReview, } from "./onboarding/review.js";
|
|
45
47
|
import { runProvision } from "./provision/index.js";
|
|
46
48
|
import { readEnvKeys } from "./provision/write-env.js";
|
|
47
49
|
import { detectBuildPipeline, scaffoldBuildPipeline } from "./scaffold/build-pipeline.js";
|
|
@@ -152,7 +154,7 @@ export async function runAdopt(cwd, opts = {}) {
|
|
|
152
154
|
// the cursor on the row in this case so the choice is explicit.
|
|
153
155
|
scaffoldBuildPipeline: !state.unknownWorkspaceLayout,
|
|
154
156
|
// Provisioning is opt-in. Each service mints real resources on a
|
|
155
|
-
// third-party (GlitchTip project, OpenPanel project, Resend API
|
|
157
|
+
// third-party (GlitchTip project, OpenPanel project, Plausible site, Resend API
|
|
156
158
|
// key) and cleaning those up after the fact is a chore — better
|
|
157
159
|
// to require an explicit tick than to surprise the user with three
|
|
158
160
|
// new clients they didn't ask for. The user opens the "Provision
|
|
@@ -517,7 +519,9 @@ function detectFeatures(projectDir, serverDir) {
|
|
|
517
519
|
"@sentry/nextjs" in deps ||
|
|
518
520
|
"@openpanel/web" in deps ||
|
|
519
521
|
"@openpanel/sdk" in deps ||
|
|
520
|
-
"@openpanel/nextjs" in deps
|
|
522
|
+
"@openpanel/nextjs" in deps ||
|
|
523
|
+
"plausible-tracker" in deps ||
|
|
524
|
+
"next-plausible" in deps) {
|
|
521
525
|
found.add("analytics");
|
|
522
526
|
}
|
|
523
527
|
if ("@aws-sdk/client-s3" in deps || "minio" in deps)
|
|
@@ -541,7 +545,7 @@ function detectFeatures(projectDir, serverDir) {
|
|
|
541
545
|
found.add("stripe");
|
|
542
546
|
if (/REDIS_URL/.test(text))
|
|
543
547
|
found.add("websocket");
|
|
544
|
-
if (/GLITCHTIP_DSN|SENTRY_DSN|OPENPANEL_/.test(text))
|
|
548
|
+
if (/GLITCHTIP_DSN|SENTRY_DSN|OPENPANEL_|PLAUSIBLE_/.test(text))
|
|
545
549
|
found.add("analytics");
|
|
546
550
|
if (/S3_BUCKET|S3_ENDPOINT/.test(text))
|
|
547
551
|
found.add("s3");
|
|
@@ -610,66 +614,46 @@ function printDetected(state) {
|
|
|
610
614
|
console.log();
|
|
611
615
|
}
|
|
612
616
|
async function reviewLoop(state, initial) {
|
|
613
|
-
let
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
choices.push(new Separator(" "));
|
|
632
|
-
choices.push({
|
|
633
|
-
name: chalk.bold(chalk.green("✓ Adopt — apply changes")),
|
|
634
|
-
value: "__adopt__",
|
|
635
|
-
});
|
|
636
|
-
choices.push({ name: chalk.dim("✗ Cancel"), value: "__cancel__" });
|
|
637
|
-
const picked = await select({
|
|
638
|
-
message: "Next step:",
|
|
639
|
-
default: defaultKey,
|
|
640
|
-
pageSize: Math.min(30, choices.length),
|
|
641
|
-
choices,
|
|
642
|
-
});
|
|
643
|
-
if (picked === "__adopt__")
|
|
644
|
-
return plan;
|
|
645
|
-
if (picked === "__cancel__") {
|
|
646
|
-
console.log(chalk.dim("\n Cancelled. Nothing was changed.\n"));
|
|
647
|
-
throw new Error("Adopt cancelled by user");
|
|
648
|
-
}
|
|
649
|
-
plan = await editAdoptStep(state, plan, picked);
|
|
650
|
-
}
|
|
617
|
+
let latestPlan = initial;
|
|
618
|
+
const reviewedPlan = await runProjectOnboardingReview({
|
|
619
|
+
initial: adoptPlanToOnboardingPlan(initial, state),
|
|
620
|
+
intro: chalk.dim(" Step through each row to confirm or change. Choose 'Adopt' when ready.\n"),
|
|
621
|
+
proceedLabel: "Adopt — apply changes",
|
|
622
|
+
cancelLabel: "Cancel",
|
|
623
|
+
cancelMessage: chalk.dim("\n Cancelled. Nothing was changed.\n"),
|
|
624
|
+
buildGroups: (plan) => buildAdoptGroups(state, plan, onboardingPlanToAdoptPlan(plan, latestPlan, state)),
|
|
625
|
+
editStep: async (plan, picked) => {
|
|
626
|
+
const currentPlan = onboardingPlanToAdoptPlan(plan, latestPlan, state);
|
|
627
|
+
latestPlan = await editAdoptStep(state, currentPlan, picked);
|
|
628
|
+
return adoptPlanToOnboardingPlan(latestPlan, state);
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
return onboardingPlanToAdoptPlan(reviewedPlan, latestPlan, state);
|
|
651
632
|
}
|
|
652
|
-
function buildAdoptGroups(state, plan) {
|
|
633
|
+
function buildAdoptGroups(state, onboarding, plan) {
|
|
653
634
|
return [
|
|
654
635
|
{
|
|
655
636
|
title: "Project",
|
|
656
637
|
steps: [
|
|
657
|
-
{
|
|
638
|
+
{
|
|
639
|
+
key: "name",
|
|
640
|
+
label: "Project name",
|
|
641
|
+
set: !!onboarding.identity.name,
|
|
642
|
+
summary: onboarding.identity.name || "(unset)",
|
|
643
|
+
},
|
|
658
644
|
{
|
|
659
645
|
key: "domain",
|
|
660
646
|
label: "Domain",
|
|
661
|
-
set: !!
|
|
662
|
-
summary:
|
|
663
|
-
? `${plan.domain} ${chalk.dim("→")} https://${plan.domain}`
|
|
664
|
-
: "(unset)",
|
|
647
|
+
set: !!onboarding.identity.domain,
|
|
648
|
+
summary: summarizeOnboardingDomain(onboarding),
|
|
665
649
|
},
|
|
666
650
|
(() => {
|
|
667
651
|
// Description is optional — empty is a valid choice that
|
|
668
652
|
// falls back to the generic "Adopted by hatchkit" blurb.
|
|
669
653
|
// We always render it as `set: true` so the cursor doesn't
|
|
670
654
|
// park on it; the user opens it explicitly when they care.
|
|
671
|
-
const summary =
|
|
672
|
-
? truncate(
|
|
655
|
+
const summary = onboarding.identity.description
|
|
656
|
+
? truncate(onboarding.identity.description, 60)
|
|
673
657
|
: state.packageDescription
|
|
674
658
|
? chalk.dim(`(empty — defaults from package.json: "${truncate(state.packageDescription, 40)}")`)
|
|
675
659
|
: chalk.dim('(empty — defaults to "Adopted by hatchkit")');
|
|
@@ -694,11 +678,7 @@ function buildAdoptGroups(state, plan) {
|
|
|
694
678
|
const hasManifestSurfaces = !!state.existingManifest?.surfaces;
|
|
695
679
|
const detectionWasDefinitive = !!(state.serverDir || state.clientDir);
|
|
696
680
|
const inferred = !hasManifestSurfaces && !detectionWasDefinitive;
|
|
697
|
-
const baseSummary =
|
|
698
|
-
? "server only (backend / API)"
|
|
699
|
-
: plan.surfaces === "client-only"
|
|
700
|
-
? "client only (static / SPA — no backend)"
|
|
701
|
-
: "server + client";
|
|
681
|
+
const baseSummary = renderOnboardingSurfaceSummary(onboarding.layout.surfaces);
|
|
702
682
|
return {
|
|
703
683
|
key: "surfaces",
|
|
704
684
|
label: "Surfaces",
|
|
@@ -710,23 +690,27 @@ function buildAdoptGroups(state, plan) {
|
|
|
710
690
|
// the chosen surface. Hiding instead of greying-out keeps the
|
|
711
691
|
// stepper consistent with the surfaces choice and avoids the
|
|
712
692
|
// "checkmark on a thing I can't unset" UX trap.
|
|
713
|
-
...(
|
|
693
|
+
...(onboarding.layout.surfaces !== "client-only"
|
|
714
694
|
? [
|
|
715
695
|
{
|
|
716
696
|
key: "serverDir",
|
|
717
697
|
label: "Server env dir",
|
|
718
|
-
set: !!
|
|
719
|
-
summary:
|
|
698
|
+
set: !!onboarding.layout.serverDir,
|
|
699
|
+
summary: onboarding.layout.serverDir
|
|
700
|
+
? relativeTo(onboarding.layout.serverDir)
|
|
701
|
+
: "(unset)",
|
|
720
702
|
},
|
|
721
703
|
]
|
|
722
704
|
: []),
|
|
723
|
-
...(
|
|
705
|
+
...(onboarding.layout.surfaces !== "server-only"
|
|
724
706
|
? [
|
|
725
707
|
{
|
|
726
708
|
key: "clientDir",
|
|
727
709
|
label: "Client env dir",
|
|
728
|
-
set: !!
|
|
729
|
-
summary:
|
|
710
|
+
set: !!onboarding.layout.clientDir,
|
|
711
|
+
summary: onboarding.layout.clientDir
|
|
712
|
+
? relativeTo(onboarding.layout.clientDir)
|
|
713
|
+
: "(unset)",
|
|
730
714
|
},
|
|
731
715
|
]
|
|
732
716
|
: []),
|
|
@@ -739,7 +723,7 @@ function buildAdoptGroups(state, plan) {
|
|
|
739
723
|
key: "features",
|
|
740
724
|
label: "Features",
|
|
741
725
|
set: true,
|
|
742
|
-
summary:
|
|
726
|
+
summary: summarizeOnboardingFeatures(onboarding.provisioning.features),
|
|
743
727
|
},
|
|
744
728
|
],
|
|
745
729
|
},
|
|
@@ -750,7 +734,7 @@ function buildAdoptGroups(state, plan) {
|
|
|
750
734
|
key: "bootstrapDotenvx",
|
|
751
735
|
label: "Initialize dotenvx",
|
|
752
736
|
set: true,
|
|
753
|
-
summary:
|
|
737
|
+
summary: onboarding.env.bootstrapDotenvx
|
|
754
738
|
? state.prodEnvIsEncrypted
|
|
755
739
|
? chalk.dim("already encrypted — will skip")
|
|
756
740
|
: "yes — generate keypair + encrypt .env.production"
|
|
@@ -760,7 +744,7 @@ function buildAdoptGroups(state, plan) {
|
|
|
760
744
|
key: "setupGitHub",
|
|
761
745
|
label: "GitHub remote",
|
|
762
746
|
set: true,
|
|
763
|
-
summary:
|
|
747
|
+
summary: onboarding.repo.setupGitHub
|
|
764
748
|
? state.gitRemoteUrl
|
|
765
749
|
? chalk.dim("already set — will skip")
|
|
766
750
|
: "yes — `gh repo create` + push"
|
|
@@ -774,7 +758,7 @@ function buildAdoptGroups(state, plan) {
|
|
|
774
758
|
// configures the redeploy webhook + builds an image). gh-pages
|
|
775
759
|
// ships its own workflow that uploads to Pages instead, so we
|
|
776
760
|
// hide this group when the user picks Pages.
|
|
777
|
-
...(
|
|
761
|
+
...(onboarding.deployment.mode === "coolify"
|
|
778
762
|
? [
|
|
779
763
|
{
|
|
780
764
|
title: "Build pipeline",
|
|
@@ -801,11 +785,11 @@ function buildAdoptGroups(state, plan) {
|
|
|
801
785
|
key: "deploymentMode",
|
|
802
786
|
label: "Deployment mode",
|
|
803
787
|
set: true,
|
|
804
|
-
summary:
|
|
788
|
+
summary: renderOnboardingDeploymentModeSummary(onboarding.deployment.mode, onboarding.layout.surfaces),
|
|
805
789
|
},
|
|
806
790
|
// Coolify-specific rows — only shown when actually deploying
|
|
807
791
|
// to Coolify. gh-pages skips this branch entirely.
|
|
808
|
-
...(
|
|
792
|
+
...(onboarding.deployment.mode === "coolify"
|
|
809
793
|
? [
|
|
810
794
|
(() => {
|
|
811
795
|
// Visibility row. Picking the wrong path here is the #1
|
|
@@ -861,12 +845,14 @@ function buildAdoptGroups(state, plan) {
|
|
|
861
845
|
key: "services",
|
|
862
846
|
label: "Provision clients",
|
|
863
847
|
set: true,
|
|
864
|
-
summary:
|
|
848
|
+
summary: onboarding.provisioning.services.length > 0
|
|
849
|
+
? onboarding.provisioning.services.join(", ")
|
|
850
|
+
: chalk.dim("skip provisioning"),
|
|
865
851
|
},
|
|
866
852
|
// `pushKey` only matters when a Coolify app is the deploy
|
|
867
853
|
// target — Pages reads no secrets from a Coolify env, so the
|
|
868
854
|
// row would just be noise on the gh-pages path.
|
|
869
|
-
...(
|
|
855
|
+
...(onboarding.deployment.mode === "coolify"
|
|
870
856
|
? [
|
|
871
857
|
{
|
|
872
858
|
key: "pushKey",
|
|
@@ -884,18 +870,6 @@ function buildAdoptGroups(state, plan) {
|
|
|
884
870
|
},
|
|
885
871
|
];
|
|
886
872
|
}
|
|
887
|
-
function renderAdoptDeploymentModeSummary(mode, surfaces) {
|
|
888
|
-
switch (mode) {
|
|
889
|
-
case "coolify":
|
|
890
|
-
return "Coolify (full-stack on Hetzner)";
|
|
891
|
-
case "gh-pages":
|
|
892
|
-
return surfaces === "client-only"
|
|
893
|
-
? "GitHub Pages (static)"
|
|
894
|
-
: chalk.yellow("GitHub Pages — needs surfaces=client-only");
|
|
895
|
-
case "scaffold-only":
|
|
896
|
-
return "scaffold only (no deploy)";
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
873
|
async function editAdoptStep(state, plan, step) {
|
|
900
874
|
if (step === "name") {
|
|
901
875
|
const name = (await input({
|
|
@@ -1067,6 +1041,11 @@ async function editAdoptStep(state, plan, step) {
|
|
|
1067
1041
|
value: "openpanel",
|
|
1068
1042
|
checked: plan.services.includes("openpanel"),
|
|
1069
1043
|
},
|
|
1044
|
+
{
|
|
1045
|
+
name: "Plausible (web analytics)",
|
|
1046
|
+
value: "plausible",
|
|
1047
|
+
checked: plan.services.includes("plausible"),
|
|
1048
|
+
},
|
|
1070
1049
|
{
|
|
1071
1050
|
name: "Resend (transactional email)",
|
|
1072
1051
|
value: "resend",
|
|
@@ -1077,6 +1056,11 @@ async function editAdoptStep(state, plan, step) {
|
|
|
1077
1056
|
value: "email",
|
|
1078
1057
|
checked: plan.services.includes("email"),
|
|
1079
1058
|
},
|
|
1059
|
+
{
|
|
1060
|
+
name: "Google Search Console (DNS verification + domain property)",
|
|
1061
|
+
value: "search-console",
|
|
1062
|
+
checked: plan.services.includes("search-console"),
|
|
1063
|
+
},
|
|
1080
1064
|
],
|
|
1081
1065
|
});
|
|
1082
1066
|
return { ...plan, services };
|
|
@@ -1167,9 +1151,11 @@ async function editAdoptStep(state, plan, step) {
|
|
|
1167
1151
|
const RESUME_SERVICE_ENV_KEY = {
|
|
1168
1152
|
glitchtip: { server: "GLITCHTIP_DSN", client: "PUBLIC_GLITCHTIP_DSN" },
|
|
1169
1153
|
openpanel: { server: "OPENPANEL_CLIENT_ID", client: "PUBLIC_OPENPANEL_CLIENT_ID" },
|
|
1154
|
+
plausible: { client: "NEXT_PUBLIC_PLAUSIBLE_DOMAIN" },
|
|
1170
1155
|
resend: { server: "RESEND_API_KEY" },
|
|
1171
1156
|
s3: { server: "R2_ENDPOINT" },
|
|
1172
1157
|
email: {},
|
|
1158
|
+
"search-console": {},
|
|
1173
1159
|
};
|
|
1174
1160
|
/** Filter the services list for `runProvision` on `--resume`: drop
|
|
1175
1161
|
* every service whose canonical env keys are already in the target
|
|
@@ -1660,12 +1646,14 @@ async function executePlan(state, plan, opts = { resume: false }) {
|
|
|
1660
1646
|
username: ghcrConfig?.username,
|
|
1661
1647
|
});
|
|
1662
1648
|
if (r.kind === "private-registered") {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1649
|
+
if (r.created) {
|
|
1650
|
+
ledger.record({
|
|
1651
|
+
kind: "coolifyPrivateRegistry",
|
|
1652
|
+
uuid: r.registryUuid,
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1667
1655
|
}
|
|
1668
|
-
else if (r.kind
|
|
1656
|
+
else if (r.kind === "skipped" || r.kind === "failed") {
|
|
1669
1657
|
caveats.push({
|
|
1670
1658
|
title: "GHCR pull credentials not configured",
|
|
1671
1659
|
reason: r.reason,
|
|
@@ -1719,9 +1707,11 @@ async function executePlan(state, plan, opts = { resume: false }) {
|
|
|
1719
1707
|
services: resumeServices,
|
|
1720
1708
|
surfaces: {
|
|
1721
1709
|
mode: provisionMode,
|
|
1710
|
+
projectDir: state.projectDir,
|
|
1722
1711
|
serverEnvDir: plan.serverDir,
|
|
1723
1712
|
clientEnvDir: plan.clientDir,
|
|
1724
1713
|
},
|
|
1714
|
+
domain: plan.domain,
|
|
1725
1715
|
// Record per-resource as runProvision creates them. Done via
|
|
1726
1716
|
// callback so a mid-loop failure (e.g. Resend after GlitchTip
|
|
1727
1717
|
// already succeeded) still leaves a complete trail of what
|
|
@@ -1733,9 +1723,23 @@ async function executePlan(state, plan, opts = { resume: false }) {
|
|
|
1733
1723
|
else if (event.service === "openpanel") {
|
|
1734
1724
|
ledger.record({ kind: "openpanel", project: event.project });
|
|
1735
1725
|
}
|
|
1726
|
+
else if (event.service === "plausible") {
|
|
1727
|
+
ledger.record({ kind: "plausible", project: event.project });
|
|
1728
|
+
}
|
|
1736
1729
|
else if (event.service === "resend") {
|
|
1737
1730
|
ledger.record({ kind: "resend", client: event.client });
|
|
1738
1731
|
}
|
|
1732
|
+
else if (event.service === "search-console") {
|
|
1733
|
+
if (event.dnsRecord?.created) {
|
|
1734
|
+
ledger.record({
|
|
1735
|
+
kind: "cloudflareDnsRecord",
|
|
1736
|
+
zoneId: event.dnsRecord.zoneId,
|
|
1737
|
+
recordId: event.dnsRecord.id,
|
|
1738
|
+
name: event.dnsRecord.name,
|
|
1739
|
+
type: event.dnsRecord.type,
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1739
1743
|
else if (event.service === "email") {
|
|
1740
1744
|
// Email setup creates three kinds of mutable state on
|
|
1741
1745
|
// Cloudflare: the destination address (account-scoped), the
|