create-kontrolia-auth 2.2.0 → 2.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/README.md CHANGED
@@ -22,10 +22,11 @@ Eso hace, en orden:
22
22
  4. **Primera aplicación** (opcional): registra tu app y su catálogo de permisos.
23
23
  5. **Despliegue**: genera los `.env.local` de `auth-server` y `admin-panel`. Si
24
24
  eliges Vercel y el repo ya está en GitHub, puede **crear los dos proyectos
25
- por ti** vía la API de Vercel (carpeta y variables de entorno correctas de
26
- una vez, sin tocar el dashboard ni la CLI de Vercel) solo pide un API
27
- token de vercel.com/account/tokens. Para Docker, Railway, Render o
28
- Coolify te da los pasos exactos.
25
+ y desplegarlos por ti** vía la API de Vercel (carpeta y variables de
26
+ entorno correctas, y el primer build disparado automáticamentesin
27
+ tocar el dashboard ni la CLI de Vercel) solo pide un API token de
28
+ vercel.com/account/tokens (el instalador explica cómo generarlo). Para
29
+ Docker, Railway, Render o Coolify te da los pasos exactos.
29
30
 
30
31
  Si ya estás **dentro del repo** (desarrollo), el instalador lo detecta y se
31
32
  salta la descarga:
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { askApplicationStep } from "./steps/application.js";
5
5
  import { askDatabaseStep } from "./steps/database.js";
6
6
  import { askDeploymentStep } from "./steps/deployment.js";
7
7
  import { bringUpAndMigrate } from "./utils/docker.js";
8
+ import { readEnvFile } from "./utils/files.js";
8
9
  import { runPreflight } from "./utils/preflight.js";
9
10
  import { textOrExit } from "./utils/prompts.js";
10
11
  import { ensureRepo, isInsideRepo } from "./utils/scaffold.js";
@@ -93,8 +94,10 @@ async function runUpdateCommand() {
93
94
  * offer) without repeating the database/application questions. For anyone
94
95
  * who already has everything running and just wants to (re)connect a
95
96
  * deploy target — e.g. adding admin-panel after auth-server was already
96
- * deployed. Doesn't persist Supabase credentials anywhere between runs, so
97
- * it still has to ask for them once here.
97
+ * deployed. A previous install/deploy already wrote these same Supabase
98
+ * values into apps/auth-server/.env.local, so this reuses them instead of
99
+ * asking again every time — only falls back to asking when that file is
100
+ * missing or incomplete (e.g. first run, or the file got deleted).
98
101
  */
99
102
  async function runDeployCommand() {
100
103
  console.clear();
@@ -105,9 +108,40 @@ async function runDeployCommand() {
105
108
  process.exitCode = 1;
106
109
  return;
107
110
  }
108
- const supabaseUrl = await textOrExit("URL de tu proyecto Supabase", "https://tu-proyecto.supabase.co");
109
- const anonKey = await textOrExit("Anon/public key");
110
- const serviceRoleKey = await textOrExit("Service role key (server-only, nunca la expongas al navegador)");
111
+ const existingEnv = await readEnvFile(`${process.cwd()}/apps/auth-server/.env.local`);
112
+ const found = existingEnv?.SUPABASE_URL && existingEnv?.NEXT_PUBLIC_SUPABASE_ANON_KEY && existingEnv?.SUPABASE_SERVICE_ROLE_KEY
113
+ ? {
114
+ supabaseUrl: existingEnv.SUPABASE_URL,
115
+ anonKey: existingEnv.NEXT_PUBLIC_SUPABASE_ANON_KEY,
116
+ serviceRoleKey: existingEnv.SUPABASE_SERVICE_ROLE_KEY,
117
+ }
118
+ : null;
119
+ let supabaseUrl;
120
+ let anonKey;
121
+ let serviceRoleKey;
122
+ if (found) {
123
+ const reuse = await p.confirm({
124
+ message: `Ya tengo las credenciales de Supabase que usaste antes (${found.supabaseUrl}) — ¿las uso, o quieres capturar otras?`,
125
+ initialValue: true,
126
+ });
127
+ if (p.isCancel(reuse)) {
128
+ p.cancel("Cancelado.");
129
+ process.exit(0);
130
+ }
131
+ if (reuse) {
132
+ ({ supabaseUrl, anonKey, serviceRoleKey } = found);
133
+ }
134
+ else {
135
+ supabaseUrl = await textOrExit("URL de tu proyecto Supabase", "https://tu-proyecto.supabase.co");
136
+ anonKey = await textOrExit("Anon/public key");
137
+ serviceRoleKey = await textOrExit("Service role key (server-only, nunca la expongas al navegador)");
138
+ }
139
+ }
140
+ else {
141
+ supabaseUrl = await textOrExit("URL de tu proyecto Supabase", "https://tu-proyecto.supabase.co");
142
+ anonKey = await textOrExit("Anon/public key");
143
+ serviceRoleKey = await textOrExit("Service role key (server-only, nunca la expongas al navegador)");
144
+ }
111
145
  const db = { mode: "existing", databaseUrl: "", supabaseUrl, anonKey, serviceRoleKey };
112
146
  await askDeploymentStep(process.cwd(), db);
113
147
  p.outro("Listo.");
@@ -1 +1 @@
1
- {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA6GpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAwI3F"}
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA8JpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoJ3F"}
@@ -1,7 +1,7 @@
1
1
  import * as p from "@clack/prompts";
2
- import { updateEnvValue, writeEnvFile } from "../utils/files.js";
2
+ import { readEnvFile, updateEnvValue, writeEnvFile } from "../utils/files.js";
3
3
  import { registerOAuthClient } from "../utils/oauth-client.js";
4
- import { createVercelProject, detectGitHubRepo } from "../utils/vercel-api.js";
4
+ import { createVercelProject, detectCurrentBranch, detectGitHubRepo, triggerVercelDeployment } from "../utils/vercel-api.js";
5
5
  const NEXT_STEPS = {
6
6
  docker: "Ya está: los servicios auth-server y admin-panel viven en el mismo docker/docker-compose.yml. Corre `docker compose -f docker/docker-compose.yml up -d` para tenerlos arriba.",
7
7
  vercel: "Lo más confiable con un monorepo pnpm es conectar el repo desde el dashboard (no desplegar carpetas sueltas por CLI):\n" +
@@ -28,12 +28,48 @@ const NEXT_STEPS = {
28
28
  manual: "Corre `pnpm build && pnpm start` en apps/auth-server y apps/admin-panel en el servidor/K8s de tu elección, usando los .env.local generados.",
29
29
  };
30
30
  /**
31
- * Offers to create both Vercel projects via the REST API instead of walking
32
- * the user through the dashboard/CLI by hand the same three fields that
33
- * are easy to get wrong doing this manually (gitRepository, rootDirectory,
34
- * environmentVariables) are just fields in one request body here. Returns
35
- * true only if both projects were created, so the caller knows whether to
36
- * still show the manual fallback instructions.
31
+ * Creates one Vercel project and immediately triggers its first deployment
32
+ * creating the project alone only connects the GitHub repo, it doesn't
33
+ * build or deploy anything by itself, so skipping this step would leave the
34
+ * user with a project that just sits there until something else (a push, or
35
+ * clicking "Deploy" by hand) triggers a build. Reports both outcomes
36
+ * separately since either can fail independently of the other.
37
+ */
38
+ async function createAndDeployVercelProject(options) {
39
+ const createSpinner = p.spinner();
40
+ createSpinner.start(`Creando proyecto de Vercel para ${options.appLabel}`);
41
+ const created = await createVercelProject({
42
+ token: options.token,
43
+ repo: options.repo,
44
+ name: options.projectName,
45
+ rootDirectory: options.rootDirectory,
46
+ env: options.env,
47
+ });
48
+ createSpinner.stop(created.ok ? `Proyecto ${options.appLabel} creado` : `Falló creando ${options.appLabel}: ${created.error}`);
49
+ if (!created.ok)
50
+ return false;
51
+ const deploySpinner = p.spinner();
52
+ deploySpinner.start(`Desplegando ${options.appLabel} (primer build)`);
53
+ const deployed = await triggerVercelDeployment({
54
+ token: options.token,
55
+ repo: options.repo,
56
+ branch: options.branch,
57
+ projectName: options.projectName,
58
+ });
59
+ deploySpinner.stop(deployed.ok
60
+ ? `${options.appLabel} desplegándose — sigue el progreso en vercel.com/dashboard`
61
+ : `Proyecto creado, pero no se pudo disparar el despliegue: ${deployed.error} — dale "Deploy" a mano en el dashboard.`);
62
+ return true;
63
+ }
64
+ /**
65
+ * Offers to create both Vercel projects (and deploy them) via the REST API
66
+ * instead of walking the user through the dashboard/CLI by hand — the same
67
+ * fields that are easy to get wrong doing this manually (gitRepository,
68
+ * rootDirectory, environmentVariables) are just fields in two request
69
+ * bodies here. Returns true if both projects were at least created, so the
70
+ * caller knows whether to still show the manual fallback instructions (a
71
+ * project that was created but failed to deploy still doesn't need those —
72
+ * the user just clicks "Deploy" once in the dashboard).
37
73
  */
38
74
  async function tryAutoCreateVercelProjects(repoRoot, authServerEnv, adminPanelEnv) {
39
75
  const repo = await detectGitHubRepo(repoRoot);
@@ -44,41 +80,43 @@ async function tryAutoCreateVercelProjects(repoRoot, authServerEnv, adminPanelEn
44
80
  return false;
45
81
  }
46
82
  const auto = await p.confirm({
47
- message: `¿Quieres que cree los dos proyectos de Vercel automáticamente (conectados a ${repo}, con sus variables ya puestas)? Solo necesito un API token de Vercel.`,
83
+ message: `¿Quieres que cree y despliegue los dos proyectos de Vercel automáticamente (conectados a ${repo}, con sus variables ya puestas)? Solo necesito un API token de Vercel.`,
48
84
  initialValue: true,
49
85
  });
50
86
  if (p.isCancel(auto) || !auto)
51
87
  return false;
88
+ p.note("1. Entra a vercel.com/account/tokens (inicia sesión con tu cuenta de Vercel si te lo pide)\n" +
89
+ '2. Dale click a "Create Token"\n' +
90
+ '3. Ponle un nombre (por ejemplo "kontrolia-auth"), deja lo demás por defecto y confirma\n' +
91
+ "4. Copia el token que te muestra (solo se ve una vez) y pégalo aquí abajo", "Cómo conseguir el API token de Vercel");
52
92
  const token = await p.password({
53
- message: "API token de Vercel (vercel.com/account/tokens — no se guarda, solo se usa ahora)",
93
+ message: "API token de Vercel (no se guarda, solo se usa ahora)",
54
94
  validate: (value) => (value.trim() ? undefined : "Requerido"),
55
95
  });
56
96
  if (p.isCancel(token))
57
97
  return false;
98
+ const branch = await detectCurrentBranch(repoRoot);
58
99
  const namePrefix = repo.split("/")[1] ?? "kontrolia-auth";
59
- const authSpinner = p.spinner();
60
- authSpinner.start("Creando proyecto de Vercel para auth-server");
61
- const authResult = await createVercelProject({
100
+ const authOk = await createAndDeployVercelProject({
62
101
  token,
63
102
  repo,
64
- name: `${namePrefix}-auth-server`,
103
+ branch,
104
+ appLabel: "auth-server",
105
+ projectName: `${namePrefix}-auth-server`,
65
106
  rootDirectory: "apps/auth-server",
66
107
  env: authServerEnv,
67
108
  });
68
- authSpinner.stop(authResult.ok ? "Proyecto auth-server creado" : `Falló auth-server: ${authResult.error}`);
69
- const adminSpinner = p.spinner();
70
- adminSpinner.start("Creando proyecto de Vercel para admin-panel");
71
- const adminResult = await createVercelProject({
109
+ const adminOk = await createAndDeployVercelProject({
72
110
  token,
73
111
  repo,
74
- name: `${namePrefix}-admin-panel`,
112
+ branch,
113
+ appLabel: "admin-panel",
114
+ projectName: `${namePrefix}-admin-panel`,
75
115
  rootDirectory: "apps/admin-panel",
76
116
  env: adminPanelEnv,
77
117
  });
78
- adminSpinner.stop(adminResult.ok ? "Proyecto admin-panel creado" : `Falló admin-panel: ${adminResult.error}`);
79
- if (authResult.ok && adminResult.ok) {
80
- p.note("Los dos proyectos ya están conectados a tu repo, con la carpeta y las variables correctas. Entra a " +
81
- "vercel.com/dashboard y dale \"Deploy\" a cada uno (o empuja un commit a tu rama principal) para el primer despliegue.", "Listo");
118
+ if (authOk && adminOk) {
119
+ p.note("Revisa el progreso del build en vercel.com/dashboard cada uno toma un par de minutos.", "Listo");
82
120
  return true;
83
121
  }
84
122
  p.note("Algo falló creando los proyectos — usa los pasos manuales de abajo para lo que no se haya creado.", "Revisa el error de arriba");
@@ -90,6 +128,14 @@ async function tryAutoCreateVercelProjects(repoRoot, authServerEnv, adminPanelEn
90
128
  * ever generates env files, it never touches the database connection.
91
129
  */
92
130
  export async function askDeploymentStep(repoRoot, db) {
131
+ // A previous install/deploy already wrote these into .env.local — reuse
132
+ // them as editable defaults instead of making the user retype the same
133
+ // URLs every time they run `deploy` again.
134
+ const prevAuthServerEnv = await readEnvFile(`${repoRoot}/apps/auth-server/.env.local`);
135
+ const prevAdminPanelEnv = await readEnvFile(`${repoRoot}/apps/admin-panel/.env.local`);
136
+ const prevAuthServerUrl = prevAdminPanelEnv?.NEXT_PUBLIC_AUTH_SERVER_URL;
137
+ const prevAdminPanelUrl = prevAuthServerEnv?.NEXT_PUBLIC_ADMIN_PANEL_URL;
138
+ const prevCookieDomain = prevAuthServerEnv?.NEXT_PUBLIC_COOKIE_DOMAIN ?? prevAdminPanelEnv?.NEXT_PUBLIC_COOKIE_DOMAIN;
93
139
  const target = await p.select({
94
140
  message: "¿Dónde vas a desplegar auth-server / admin-panel?",
95
141
  options: [
@@ -109,6 +155,7 @@ export async function askDeploymentStep(repoRoot, db) {
109
155
  message: "¿En qué URL va a vivir auth-server? (admin-panel la usa para enviar ahí a quien no tenga sesión)",
110
156
  placeholder: "http://localhost:3000",
111
157
  defaultValue: "http://localhost:3000",
158
+ initialValue: prevAuthServerUrl,
112
159
  });
113
160
  if (p.isCancel(authServerUrl)) {
114
161
  p.cancel("Instalación cancelada.");
@@ -118,6 +165,7 @@ export async function askDeploymentStep(repoRoot, db) {
118
165
  message: "¿Y en qué URL va a vivir admin-panel? (auth-server la usa para regresar ahí después de iniciar sesión, en vez de mandarte a su propia pantalla de inicio)",
119
166
  placeholder: "http://localhost:3001",
120
167
  defaultValue: "http://localhost:3001",
168
+ initialValue: prevAdminPanelUrl,
121
169
  });
122
170
  if (p.isCancel(adminPanelUrl)) {
123
171
  p.cancel("Instalación cancelada.");
@@ -128,6 +176,7 @@ export async function askDeploymentStep(repoRoot, db) {
128
176
  "Si es así, escribe el dominio compartido empezando con un punto (ej. .tuempresa.com). Si comparten el mismo host, o no lo sabes todavía, déjalo vacío.",
129
177
  placeholder: "",
130
178
  defaultValue: "",
179
+ initialValue: prevCookieDomain,
131
180
  });
132
181
  if (p.isCancel(cookieDomain)) {
133
182
  p.cancel("Instalación cancelada.");
@@ -9,4 +9,6 @@ export declare function writeEnvFile(path: string, values: EnvValues): Promise<v
9
9
  */
10
10
  export declare function updateEnvValue(path: string, key: string, value: string): Promise<void>;
11
11
  export declare function fileExists(path: string): boolean;
12
+ /** Parses a simple `KEY=value` .env file (no quoting/escaping) — null if it doesn't exist. */
13
+ export declare function readEnvFile(path: string): Promise<EnvValues | null>;
12
14
  //# sourceMappingURL=files.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/utils/files.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjF;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5F;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD"}
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/utils/files.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjF;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5F;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,8FAA8F;AAC9F,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAYzE"}
@@ -23,3 +23,18 @@ export async function updateEnvValue(path, key, value) {
23
23
  export function fileExists(path) {
24
24
  return existsSync(path);
25
25
  }
26
+ /** Parses a simple `KEY=value` .env file (no quoting/escaping) — null if it doesn't exist. */
27
+ export async function readEnvFile(path) {
28
+ if (!existsSync(path))
29
+ return null;
30
+ const contents = await readFile(path, "utf8");
31
+ const values = {};
32
+ for (const line of contents.split("\n")) {
33
+ const match = line.match(/^([A-Z0-9_]+)=(.*)$/);
34
+ if (match) {
35
+ const [, key, value] = match;
36
+ values[key] = value.trim();
37
+ }
38
+ }
39
+ return values;
40
+ }
@@ -7,6 +7,8 @@ import type { EnvValues } from "./files.js";
7
7
  * but this product's docs and CLI only ever assume GitHub.
8
8
  */
9
9
  export declare function detectGitHubRepo(repoRoot: string): Promise<string | null>;
10
+ /** The branch actually checked out — what should get deployed, not an assumed "main". */
11
+ export declare function detectCurrentBranch(repoRoot: string): Promise<string>;
10
12
  export interface CreateVercelProjectOptions {
11
13
  token: string;
12
14
  /** "owner/repo" on GitHub. */
@@ -32,4 +34,23 @@ export interface CreateVercelProjectResult {
32
34
  * https://vercel.com/docs/rest-api/reference/endpoints/projects/create-a-new-project
33
35
  */
34
36
  export declare function createVercelProject(options: CreateVercelProjectOptions): Promise<CreateVercelProjectResult>;
37
+ export interface TriggerDeploymentOptions {
38
+ token: string;
39
+ /** "owner/repo" on GitHub. */
40
+ repo: string;
41
+ branch: string;
42
+ /** Must match the `name` used in createVercelProject — that's how this targets the right project. */
43
+ projectName: string;
44
+ }
45
+ /**
46
+ * Creating a project via the API (createVercelProject above) connects the
47
+ * GitHub repo but does NOT build or deploy anything by itself — that's a
48
+ * separate resource. Without this, a freshly created project just sits
49
+ * there until something else (a push, or clicking "Deploy" in the
50
+ * dashboard) triggers its first build, which defeats the point of
51
+ * automating this in the first place.
52
+ *
53
+ * https://vercel.com/docs/rest-api/reference/endpoints/deployments/create-a-new-deployment
54
+ */
55
+ export declare function triggerVercelDeployment(options: TriggerDeploymentOptions): Promise<CreateVercelProjectResult>;
35
56
  //# sourceMappingURL=vercel-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vercel-api.d.ts","sourceRoot":"","sources":["../../src/utils/vercel-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ/E;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,OAAO,CAAC;IACZ,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAgCjH"}
1
+ {"version":3,"file":"vercel-api.d.ts","sourceRoot":"","sources":["../../src/utils/vercel-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ/E;AAED,yFAAyF;AACzF,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO3E;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,OAAO,CAAC;IACZ,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAgCjH;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,qGAAqG;IACrG,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAyBnH"}
@@ -16,6 +16,16 @@ export async function detectGitHubRepo(repoRoot) {
16
16
  return null;
17
17
  }
18
18
  }
19
+ /** The branch actually checked out — what should get deployed, not an assumed "main". */
20
+ export async function detectCurrentBranch(repoRoot) {
21
+ try {
22
+ const branch = (await run("git", ["branch", "--show-current"], { cwd: repoRoot })).trim();
23
+ return branch || "main";
24
+ }
25
+ catch {
26
+ return "main";
27
+ }
28
+ }
19
29
  /**
20
30
  * Creates a Vercel project via the REST API — connected to the GitHub repo,
21
31
  * scoped to one app's folder in this monorepo, with its environment
@@ -59,3 +69,38 @@ export async function createVercelProject(options) {
59
69
  const body = (await response.json().catch(() => null));
60
70
  return { ok: false, error: body?.error?.message ?? `HTTP ${response.status}` };
61
71
  }
72
+ /**
73
+ * Creating a project via the API (createVercelProject above) connects the
74
+ * GitHub repo but does NOT build or deploy anything by itself — that's a
75
+ * separate resource. Without this, a freshly created project just sits
76
+ * there until something else (a push, or clicking "Deploy" in the
77
+ * dashboard) triggers its first build, which defeats the point of
78
+ * automating this in the first place.
79
+ *
80
+ * https://vercel.com/docs/rest-api/reference/endpoints/deployments/create-a-new-deployment
81
+ */
82
+ export async function triggerVercelDeployment(options) {
83
+ const [org, repo] = options.repo.split("/");
84
+ let response;
85
+ try {
86
+ response = await fetch("https://api.vercel.com/v13/deployments", {
87
+ method: "POST",
88
+ headers: {
89
+ Authorization: `Bearer ${options.token}`,
90
+ "Content-Type": "application/json",
91
+ },
92
+ body: JSON.stringify({
93
+ name: options.projectName,
94
+ target: "production",
95
+ gitSource: { type: "github", ref: options.branch, org, repo },
96
+ }),
97
+ });
98
+ }
99
+ catch (error) {
100
+ return { ok: false, error: error.message };
101
+ }
102
+ if (response.ok)
103
+ return { ok: true };
104
+ const body = (await response.json().catch(() => null));
105
+ return { ok: false, error: body?.error?.message ?? `HTTP ${response.status}` };
106
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kontrolia-auth",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "license": "MIT",
5
5
  "description": "Turnkey installer for KontrolIA Auth. `npx create-kontrolia-auth mi-app` checks requirements, downloads the repo, installs deps, and runs a wizard: (1) connect to an existing Supabase project or create a new self-hosted one via Docker; (2) where to deploy auth-server/admin-panel (Docker, Vercel, Railway, Render, Coolify). Also exposes `update`, `migrate` and `doctor` subcommands.",
6
6
  "keywords": [