@nimbuslab/cli 0.1.4 → 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/index.js +337 -37
- package/package.json +1 -1
- package/src/commands/create.ts +418 -27
package/dist/index.js
CHANGED
|
@@ -764,27 +764,55 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
|
764
764
|
l2 = R2(m2 ?? l2);
|
|
765
765
|
} };
|
|
766
766
|
};
|
|
767
|
-
var Ce = async (t, n) => {
|
|
768
|
-
const r2 = {}, i = Object.keys(t);
|
|
769
|
-
for (const s of i) {
|
|
770
|
-
const c = t[s], a = await c({ results: r2 })?.catch((l2) => {
|
|
771
|
-
throw l2;
|
|
772
|
-
});
|
|
773
|
-
if (typeof n?.onCancel == "function" && pD(a)) {
|
|
774
|
-
r2[s] = "canceled", n.onCancel({ results: r2 });
|
|
775
|
-
continue;
|
|
776
|
-
}
|
|
777
|
-
r2[s] = a;
|
|
778
|
-
}
|
|
779
|
-
return r2;
|
|
780
|
-
};
|
|
781
767
|
|
|
782
768
|
// src/commands/create.ts
|
|
783
769
|
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
784
770
|
var {$: $2 } = globalThis.Bun;
|
|
785
771
|
import { rm } from "fs/promises";
|
|
786
772
|
import { join } from "path";
|
|
787
|
-
var
|
|
773
|
+
var TEMPLATES = {
|
|
774
|
+
fast: "nimbuslab-templates/fast-template",
|
|
775
|
+
"fast+": "nimbuslab-templates/fastplus-template"
|
|
776
|
+
};
|
|
777
|
+
async function ensureRailwayCli() {
|
|
778
|
+
const checkCmd = process.platform === "win32" ? "where" : "which";
|
|
779
|
+
const hasRailway = await $2`${checkCmd} railway`.quiet().then(() => true).catch(() => false);
|
|
780
|
+
if (hasRailway)
|
|
781
|
+
return true;
|
|
782
|
+
console.log(import_picocolors3.default.yellow("Railway CLI nao encontrado. Instalando..."));
|
|
783
|
+
console.log();
|
|
784
|
+
try {
|
|
785
|
+
if (process.platform === "win32") {
|
|
786
|
+
await $2`powershell -c "iwr https://railway.app/install.ps1 -useb | iex"`.quiet();
|
|
787
|
+
} else {
|
|
788
|
+
await $2`curl -fsSL https://railway.app/install.sh | sh`.quiet();
|
|
789
|
+
}
|
|
790
|
+
console.log(import_picocolors3.default.green("Railway CLI instalado com sucesso!"));
|
|
791
|
+
return true;
|
|
792
|
+
} catch (error) {
|
|
793
|
+
console.log(import_picocolors3.default.red("Erro ao instalar Railway CLI."));
|
|
794
|
+
console.log(import_picocolors3.default.dim("Instale manualmente: https://docs.railway.app/guides/cli"));
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
async function listRailwayProjects() {
|
|
799
|
+
try {
|
|
800
|
+
const result = await $2`railway list`.text();
|
|
801
|
+
const lines = result.trim().split(`
|
|
802
|
+
`).filter((l2) => l2.trim());
|
|
803
|
+
return lines.slice(1).map((l2) => l2.trim());
|
|
804
|
+
} catch {
|
|
805
|
+
return [];
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
async function isRailwayAuthenticated() {
|
|
809
|
+
try {
|
|
810
|
+
await $2`railway whoami`.quiet();
|
|
811
|
+
return true;
|
|
812
|
+
} catch {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
788
816
|
async function create(args) {
|
|
789
817
|
const checkCmd = process.platform === "win32" ? "where" : "which";
|
|
790
818
|
const hasBun = await $2`${checkCmd} bun`.quiet().then(() => true).catch(() => false);
|
|
@@ -825,6 +853,15 @@ async function create(args) {
|
|
|
825
853
|
console.log(import_picocolors3.default.dim("Execute: gh auth login"));
|
|
826
854
|
process.exit(1);
|
|
827
855
|
}
|
|
856
|
+
const hasRailway = await ensureRailwayCli();
|
|
857
|
+
if (hasRailway) {
|
|
858
|
+
const railwayAuth = await isRailwayAuthenticated();
|
|
859
|
+
if (!railwayAuth) {
|
|
860
|
+
console.log(import_picocolors3.default.yellow("Railway CLI nao autenticado."));
|
|
861
|
+
console.log(import_picocolors3.default.dim("Execute: railway login"));
|
|
862
|
+
console.log();
|
|
863
|
+
}
|
|
864
|
+
}
|
|
828
865
|
const hasYes = args.includes("-y") || args.includes("--yes");
|
|
829
866
|
const projectName = args.find((a) => !a.startsWith("-"));
|
|
830
867
|
Ie(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" Novo Projeto nimbuslab ")));
|
|
@@ -834,11 +871,24 @@ async function create(args) {
|
|
|
834
871
|
name: projectName,
|
|
835
872
|
type: "fast",
|
|
836
873
|
git: true,
|
|
837
|
-
install: true
|
|
874
|
+
install: true,
|
|
875
|
+
github: false,
|
|
876
|
+
githubOrg: null,
|
|
877
|
+
githubDescription: "",
|
|
878
|
+
contractNumber: "",
|
|
879
|
+
resendApiKey: "",
|
|
880
|
+
resendFromEmail: "",
|
|
881
|
+
contactEmail: "",
|
|
882
|
+
railwayProject: "",
|
|
883
|
+
railwayToken: "",
|
|
884
|
+
stagingUrl: "",
|
|
885
|
+
productionUrl: ""
|
|
838
886
|
};
|
|
839
887
|
console.log(import_picocolors3.default.dim(` Projeto: ${projectName}`));
|
|
840
888
|
console.log(import_picocolors3.default.dim(` Tipo: fast`));
|
|
841
889
|
console.log(import_picocolors3.default.dim(` Git: sim`));
|
|
890
|
+
console.log(import_picocolors3.default.dim(` GitHub: nao`));
|
|
891
|
+
console.log(import_picocolors3.default.dim(` Infra: configurar depois`));
|
|
842
892
|
console.log(import_picocolors3.default.dim(` Instalar: sim`));
|
|
843
893
|
console.log();
|
|
844
894
|
} else {
|
|
@@ -885,35 +935,193 @@ async function promptConfig(initialName) {
|
|
|
885
935
|
});
|
|
886
936
|
if (pD(type))
|
|
887
937
|
return type;
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
initialValue: true
|
|
892
|
-
}),
|
|
893
|
-
install: () => ye({
|
|
894
|
-
message: "Instalar dependencias?",
|
|
895
|
-
initialValue: true
|
|
896
|
-
})
|
|
938
|
+
const git = await ye({
|
|
939
|
+
message: "Inicializar repositorio Git?",
|
|
940
|
+
initialValue: true
|
|
897
941
|
});
|
|
898
|
-
if (pD(
|
|
899
|
-
return
|
|
942
|
+
if (pD(git))
|
|
943
|
+
return git;
|
|
944
|
+
let github = false;
|
|
945
|
+
let githubOrg = null;
|
|
946
|
+
let githubDescription = "";
|
|
947
|
+
if (git) {
|
|
948
|
+
const createGithub = await ye({
|
|
949
|
+
message: "Criar repositorio no GitHub?",
|
|
950
|
+
initialValue: false
|
|
951
|
+
});
|
|
952
|
+
if (pD(createGithub))
|
|
953
|
+
return createGithub;
|
|
954
|
+
github = createGithub;
|
|
955
|
+
if (github) {
|
|
956
|
+
const org = await ve({
|
|
957
|
+
message: "Organizacao GitHub:",
|
|
958
|
+
options: [
|
|
959
|
+
{ value: "nimbuslab", label: "nimbuslab", hint: "Org principal" },
|
|
960
|
+
{ value: "fast-by-nimbuslab", label: "fast-by-nimbuslab", hint: "Projetos de clientes" },
|
|
961
|
+
{ value: "nimbuslab-templates", label: "nimbuslab-templates", hint: "Templates" },
|
|
962
|
+
{ value: null, label: "Pessoal", hint: "Sem organizacao" }
|
|
963
|
+
]
|
|
964
|
+
});
|
|
965
|
+
if (pD(org))
|
|
966
|
+
return org;
|
|
967
|
+
githubOrg = org;
|
|
968
|
+
const description = await he({
|
|
969
|
+
message: "Descricao do repositorio:",
|
|
970
|
+
placeholder: "Landing page para cliente X",
|
|
971
|
+
initialValue: type === "fast" ? "Landing page fast by nimbuslab" : "SaaS fast+ by nimbuslab"
|
|
972
|
+
});
|
|
973
|
+
if (pD(description))
|
|
974
|
+
return description;
|
|
975
|
+
githubDescription = description;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
let contractNumber = "";
|
|
979
|
+
if (type === "fast") {
|
|
980
|
+
const contract = await he({
|
|
981
|
+
message: "Numero do contrato (ex: 001):",
|
|
982
|
+
placeholder: "001",
|
|
983
|
+
validate: (v2) => v2 ? undefined : "Numero do contrato e obrigatorio para fast"
|
|
984
|
+
});
|
|
985
|
+
if (pD(contract))
|
|
986
|
+
return contract;
|
|
987
|
+
contractNumber = contract;
|
|
988
|
+
}
|
|
989
|
+
const configureInfra = await ye({
|
|
990
|
+
message: "Configurar infra agora? (Resend, URLs)",
|
|
991
|
+
initialValue: true
|
|
992
|
+
});
|
|
993
|
+
if (pD(configureInfra))
|
|
994
|
+
return configureInfra;
|
|
995
|
+
let resendApiKey = "";
|
|
996
|
+
let resendFromEmail = "";
|
|
997
|
+
let contactEmail = "";
|
|
998
|
+
let railwayProject = "";
|
|
999
|
+
let railwayToken = "";
|
|
1000
|
+
let stagingUrl = "";
|
|
1001
|
+
let productionUrl = "";
|
|
1002
|
+
if (configureInfra) {
|
|
1003
|
+
const currentYear = new Date().getFullYear().toString().slice(-2);
|
|
1004
|
+
const defaultStagingUrl = type === "fast" ? `https://fast-${contractNumber}-${currentYear}.nimbuslab.net.br` : `https://${name}.nimbuslab.net.br`;
|
|
1005
|
+
const defaultFromEmail = "no-reply@nimbuslab.com.br";
|
|
1006
|
+
const defaultContactEmail = type === "fast" ? "fast@nimbuslab.com.br" : "suporte@nimbuslab.com.br";
|
|
1007
|
+
console.log();
|
|
1008
|
+
console.log(import_picocolors3.default.dim(" Resend (Email)"));
|
|
1009
|
+
const resendKey = await he({
|
|
1010
|
+
message: "RESEND_API_KEY:",
|
|
1011
|
+
placeholder: "re_xxxxxxxxxxxx"
|
|
1012
|
+
});
|
|
1013
|
+
if (pD(resendKey))
|
|
1014
|
+
return resendKey;
|
|
1015
|
+
resendApiKey = resendKey;
|
|
1016
|
+
const fromEmail = await he({
|
|
1017
|
+
message: "Email de envio (from):",
|
|
1018
|
+
placeholder: defaultFromEmail,
|
|
1019
|
+
initialValue: defaultFromEmail
|
|
1020
|
+
});
|
|
1021
|
+
if (pD(fromEmail))
|
|
1022
|
+
return fromEmail;
|
|
1023
|
+
resendFromEmail = fromEmail;
|
|
1024
|
+
const contact = await he({
|
|
1025
|
+
message: "Email de contato (recebe formularios):",
|
|
1026
|
+
placeholder: defaultContactEmail,
|
|
1027
|
+
initialValue: defaultContactEmail
|
|
1028
|
+
});
|
|
1029
|
+
if (pD(contact))
|
|
1030
|
+
return contact;
|
|
1031
|
+
contactEmail = contact;
|
|
1032
|
+
console.log();
|
|
1033
|
+
console.log(import_picocolors3.default.dim(" URLs do projeto"));
|
|
1034
|
+
const staging = await he({
|
|
1035
|
+
message: "URL de staging:",
|
|
1036
|
+
placeholder: defaultStagingUrl,
|
|
1037
|
+
initialValue: defaultStagingUrl
|
|
1038
|
+
});
|
|
1039
|
+
if (pD(staging))
|
|
1040
|
+
return staging;
|
|
1041
|
+
stagingUrl = staging;
|
|
1042
|
+
const production = await he({
|
|
1043
|
+
message: "URL de producao:",
|
|
1044
|
+
placeholder: defaultStagingUrl.replace(".nimbuslab.net.br", ".com.br"),
|
|
1045
|
+
initialValue: ""
|
|
1046
|
+
});
|
|
1047
|
+
if (pD(production))
|
|
1048
|
+
return production;
|
|
1049
|
+
productionUrl = production;
|
|
1050
|
+
const railwayAuthenticated = await isRailwayAuthenticated();
|
|
1051
|
+
if (railwayAuthenticated) {
|
|
1052
|
+
console.log();
|
|
1053
|
+
console.log(import_picocolors3.default.dim(" Railway"));
|
|
1054
|
+
const projects = await listRailwayProjects();
|
|
1055
|
+
if (type === "fast") {
|
|
1056
|
+
const fastProject = projects.find((p2) => p2.toLowerCase().includes("fast by nimbuslab"));
|
|
1057
|
+
if (fastProject) {
|
|
1058
|
+
railwayProject = fastProject;
|
|
1059
|
+
console.log(import_picocolors3.default.green(` Projeto: ${fastProject} (automatico)`));
|
|
1060
|
+
} else {
|
|
1061
|
+
console.log(import_picocolors3.default.yellow(" Projeto 'Fast by nimbuslab' nao encontrado."));
|
|
1062
|
+
console.log(import_picocolors3.default.dim(" Configure RAILWAY_TOKEN manualmente no .env"));
|
|
1063
|
+
}
|
|
1064
|
+
} else {
|
|
1065
|
+
const projectOptions = [
|
|
1066
|
+
...projects.map((proj) => ({ value: proj, label: proj })),
|
|
1067
|
+
{ value: "__new__", label: "Criar novo projeto", hint: "Abrir railway.app/new" },
|
|
1068
|
+
{ value: "__skip__", label: "Pular", hint: "Configurar depois" }
|
|
1069
|
+
];
|
|
1070
|
+
const selectedProject = await ve({
|
|
1071
|
+
message: "Projeto Railway para este SaaS:",
|
|
1072
|
+
options: projectOptions
|
|
1073
|
+
});
|
|
1074
|
+
if (pD(selectedProject))
|
|
1075
|
+
return selectedProject;
|
|
1076
|
+
if (selectedProject === "__new__") {
|
|
1077
|
+
console.log(import_picocolors3.default.yellow(" Crie o projeto em: https://railway.app/new"));
|
|
1078
|
+
console.log(import_picocolors3.default.dim(" Configure RAILWAY_TOKEN depois no .env"));
|
|
1079
|
+
} else if (selectedProject !== "__skip__") {
|
|
1080
|
+
railwayProject = selectedProject;
|
|
1081
|
+
console.log(import_picocolors3.default.green(` Projeto selecionado: ${railwayProject}`));
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
} else {
|
|
1085
|
+
console.log();
|
|
1086
|
+
console.log(import_picocolors3.default.yellow(" Railway: nao autenticado (railway login)"));
|
|
1087
|
+
console.log(import_picocolors3.default.dim(" Configure RAILWAY_TOKEN manualmente no .env"));
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const install = await ye({
|
|
1091
|
+
message: "Instalar dependencias?",
|
|
1092
|
+
initialValue: true
|
|
1093
|
+
});
|
|
1094
|
+
if (pD(install))
|
|
1095
|
+
return install;
|
|
900
1096
|
return {
|
|
901
1097
|
name,
|
|
902
1098
|
type,
|
|
903
|
-
git
|
|
904
|
-
install
|
|
1099
|
+
git,
|
|
1100
|
+
install,
|
|
1101
|
+
github,
|
|
1102
|
+
githubOrg,
|
|
1103
|
+
githubDescription,
|
|
1104
|
+
contractNumber,
|
|
1105
|
+
resendApiKey,
|
|
1106
|
+
resendFromEmail,
|
|
1107
|
+
contactEmail,
|
|
1108
|
+
railwayProject,
|
|
1109
|
+
railwayToken,
|
|
1110
|
+
stagingUrl,
|
|
1111
|
+
productionUrl
|
|
905
1112
|
};
|
|
906
1113
|
}
|
|
907
1114
|
async function createProject(config) {
|
|
908
1115
|
const s = Y2();
|
|
909
|
-
|
|
1116
|
+
const templateRepo = TEMPLATES[config.type];
|
|
1117
|
+
s.start(`Clonando template ${config.type}...`);
|
|
910
1118
|
try {
|
|
911
|
-
await $2`gh repo clone ${
|
|
1119
|
+
await $2`gh repo clone ${templateRepo} ${config.name} -- --depth 1`.quiet();
|
|
912
1120
|
await rm(join(config.name, ".git"), { recursive: true, force: true });
|
|
913
|
-
s.stop(
|
|
1121
|
+
s.stop(`Template ${config.type} clonado`);
|
|
914
1122
|
} catch (error) {
|
|
915
1123
|
s.stop("Erro ao clonar template");
|
|
916
|
-
throw new Error(
|
|
1124
|
+
throw new Error(`Falha ao clonar template ${templateRepo}. Verifique se tem acesso ao repositorio.`);
|
|
917
1125
|
}
|
|
918
1126
|
s.start("Configurando projeto...");
|
|
919
1127
|
try {
|
|
@@ -933,13 +1141,44 @@ async function createProject(config) {
|
|
|
933
1141
|
s.start("Inicializando Git...");
|
|
934
1142
|
try {
|
|
935
1143
|
const cwd = config.name;
|
|
936
|
-
await $2`git init`.cwd(cwd).quiet();
|
|
1144
|
+
await $2`git init -b main`.cwd(cwd).quiet();
|
|
937
1145
|
await $2`git add -A`.cwd(cwd).quiet();
|
|
938
1146
|
await $2`git commit -m "chore: setup inicial via nimbus create"`.cwd(cwd).quiet();
|
|
939
|
-
|
|
1147
|
+
await $2`git checkout -b staging`.cwd(cwd).quiet();
|
|
1148
|
+
await $2`git checkout -b develop`.cwd(cwd).quiet();
|
|
1149
|
+
s.stop("Git inicializado (main -> staging -> develop)");
|
|
940
1150
|
} catch (error) {
|
|
941
1151
|
s.stop("Erro ao inicializar Git");
|
|
942
1152
|
}
|
|
1153
|
+
if (config.github) {
|
|
1154
|
+
s.start("Criando repositorio no GitHub...");
|
|
1155
|
+
try {
|
|
1156
|
+
const cwd = config.name;
|
|
1157
|
+
const repoName = config.githubOrg ? `${config.githubOrg}/${config.name}` : config.name;
|
|
1158
|
+
const visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public";
|
|
1159
|
+
await $2`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin`.cwd(cwd).quiet();
|
|
1160
|
+
await $2`git checkout main`.cwd(cwd).quiet();
|
|
1161
|
+
await $2`git push -u origin main`.cwd(cwd).quiet();
|
|
1162
|
+
await $2`git checkout staging`.cwd(cwd).quiet();
|
|
1163
|
+
await $2`git push -u origin staging`.cwd(cwd).quiet();
|
|
1164
|
+
await $2`git checkout develop`.cwd(cwd).quiet();
|
|
1165
|
+
await $2`git push -u origin develop`.cwd(cwd).quiet();
|
|
1166
|
+
s.stop(`GitHub: ${repoName} criado`);
|
|
1167
|
+
} catch (error) {
|
|
1168
|
+
s.stop("Erro ao criar repositorio GitHub");
|
|
1169
|
+
console.log(import_picocolors3.default.dim(" Voce pode criar manualmente com: gh repo create"));
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
if (config.resendApiKey || config.stagingUrl) {
|
|
1174
|
+
s.start("Gerando arquivo .env...");
|
|
1175
|
+
try {
|
|
1176
|
+
const envContent = generateEnvFile(config);
|
|
1177
|
+
await Bun.write(`${config.name}/.env`, envContent);
|
|
1178
|
+
s.stop("Arquivo .env criado");
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
s.stop("Erro ao criar .env");
|
|
1181
|
+
}
|
|
943
1182
|
}
|
|
944
1183
|
if (config.install) {
|
|
945
1184
|
s.start("Instalando dependencias (pode demorar)...");
|
|
@@ -951,6 +1190,45 @@ async function createProject(config) {
|
|
|
951
1190
|
}
|
|
952
1191
|
}
|
|
953
1192
|
}
|
|
1193
|
+
function generateEnvFile(config) {
|
|
1194
|
+
const lines = [
|
|
1195
|
+
"# Gerado automaticamente pelo nimbus-cli",
|
|
1196
|
+
"# Nao commitar este arquivo!",
|
|
1197
|
+
"",
|
|
1198
|
+
"# App",
|
|
1199
|
+
`NODE_ENV=development`,
|
|
1200
|
+
"",
|
|
1201
|
+
"# URLs",
|
|
1202
|
+
`NEXT_PUBLIC_APP_URL=${config.productionUrl || "http://localhost:3000"}`,
|
|
1203
|
+
`STAGING_URL=${config.stagingUrl || ""}`,
|
|
1204
|
+
`PRODUCTION_URL=${config.productionUrl || ""}`,
|
|
1205
|
+
"",
|
|
1206
|
+
"# Resend (Email)",
|
|
1207
|
+
`RESEND_API_KEY=${config.resendApiKey || ""}`,
|
|
1208
|
+
`RESEND_FROM_EMAIL=${config.resendFromEmail || ""}`,
|
|
1209
|
+
`CONTACT_EMAIL=${config.contactEmail || ""}`
|
|
1210
|
+
];
|
|
1211
|
+
if (config.railwayProject || config.railwayToken) {
|
|
1212
|
+
lines.push("");
|
|
1213
|
+
lines.push("# Railway");
|
|
1214
|
+
if (config.railwayProject) {
|
|
1215
|
+
lines.push(`# Projeto: ${config.railwayProject}`);
|
|
1216
|
+
}
|
|
1217
|
+
lines.push(`RAILWAY_TOKEN=${config.railwayToken || "# Configure com: railway link"}`);
|
|
1218
|
+
}
|
|
1219
|
+
if (config.type === "fast+") {
|
|
1220
|
+
lines.push("");
|
|
1221
|
+
lines.push("# Database (fast+)");
|
|
1222
|
+
lines.push("DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app");
|
|
1223
|
+
lines.push("");
|
|
1224
|
+
lines.push("# Auth (fast+)");
|
|
1225
|
+
lines.push("BETTER_AUTH_SECRET=");
|
|
1226
|
+
lines.push("BETTER_AUTH_URL=http://localhost:3000");
|
|
1227
|
+
}
|
|
1228
|
+
return lines.join(`
|
|
1229
|
+
`) + `
|
|
1230
|
+
`;
|
|
1231
|
+
}
|
|
954
1232
|
function showNextSteps(config) {
|
|
955
1233
|
console.log();
|
|
956
1234
|
console.log(import_picocolors3.default.bold("Proximos passos:"));
|
|
@@ -962,11 +1240,33 @@ function showNextSteps(config) {
|
|
|
962
1240
|
console.log(` ${import_picocolors3.default.cyan("bun")} setup`);
|
|
963
1241
|
console.log(` ${import_picocolors3.default.cyan("bun")} dev`);
|
|
964
1242
|
console.log();
|
|
1243
|
+
if (config.git) {
|
|
1244
|
+
console.log(import_picocolors3.default.dim(" Git flow: main -> staging -> develop (branch atual)"));
|
|
1245
|
+
console.log();
|
|
1246
|
+
if (config.github) {
|
|
1247
|
+
const repoUrl = config.githubOrg ? `https://github.com/${config.githubOrg}/${config.name}` : `https://github.com/${config.name}`;
|
|
1248
|
+
console.log(import_picocolors3.default.green(` GitHub: ${repoUrl}`));
|
|
1249
|
+
console.log();
|
|
1250
|
+
} else {
|
|
1251
|
+
console.log(import_picocolors3.default.yellow(" Dica: Para criar repo GitHub, use 'gh repo create' ou 'bun setup'."));
|
|
1252
|
+
console.log();
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
965
1255
|
if (config.type === "fast+") {
|
|
966
|
-
console.log(import_picocolors3.default.dim(" Dica:
|
|
1256
|
+
console.log(import_picocolors3.default.dim(" Dica: Para fast+, configure DATABASE_URL e BETTER_AUTH_SECRET no .env"));
|
|
1257
|
+
if (!config.railwayToken) {
|
|
1258
|
+
console.log(import_picocolors3.default.dim(" Railway: Crie um projeto em https://railway.app/new"));
|
|
1259
|
+
}
|
|
1260
|
+
console.log();
|
|
1261
|
+
}
|
|
1262
|
+
if (config.resendApiKey || config.stagingUrl) {
|
|
1263
|
+
console.log(import_picocolors3.default.green(" .env configurado com sucesso!"));
|
|
1264
|
+
console.log();
|
|
1265
|
+
} else {
|
|
1266
|
+
console.log(import_picocolors3.default.yellow(" Dica: Configure .env manualmente ou use 'bun setup'."));
|
|
967
1267
|
console.log();
|
|
968
1268
|
}
|
|
969
|
-
console.log(import_picocolors3.default.dim(" Documentacao: https://github.com/nimbuslab
|
|
1269
|
+
console.log(import_picocolors3.default.dim(" Documentacao: https://github.com/nimbuslab-templates"));
|
|
970
1270
|
console.log();
|
|
971
1271
|
}
|
|
972
1272
|
|
package/package.json
CHANGED
package/src/commands/create.ts
CHANGED
|
@@ -4,13 +4,79 @@ import { $} from "bun"
|
|
|
4
4
|
import { rm } from "node:fs/promises"
|
|
5
5
|
import { join } from "node:path"
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Templates separados por tipo
|
|
8
|
+
const TEMPLATES = {
|
|
9
|
+
"fast": "nimbuslab-templates/fast-template",
|
|
10
|
+
"fast+": "nimbuslab-templates/fastplus-template",
|
|
11
|
+
}
|
|
8
12
|
|
|
9
13
|
interface ProjectConfig {
|
|
10
14
|
name: string
|
|
11
15
|
type: "fast" | "fast+"
|
|
12
16
|
git: boolean
|
|
13
17
|
install: boolean
|
|
18
|
+
github: boolean
|
|
19
|
+
githubOrg: string | null
|
|
20
|
+
githubDescription: string
|
|
21
|
+
// M26: Número do contrato (fast only)
|
|
22
|
+
contractNumber: string
|
|
23
|
+
// M21-M23: Configs de infra
|
|
24
|
+
resendApiKey: string
|
|
25
|
+
resendFromEmail: string
|
|
26
|
+
contactEmail: string
|
|
27
|
+
// M30: Railway via CLI
|
|
28
|
+
railwayProject: string
|
|
29
|
+
railwayToken: string
|
|
30
|
+
stagingUrl: string
|
|
31
|
+
productionUrl: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// M30: Verificar e instalar Railway CLI
|
|
35
|
+
async function ensureRailwayCli(): Promise<boolean> {
|
|
36
|
+
const checkCmd = process.platform === "win32" ? "where" : "which"
|
|
37
|
+
const hasRailway = await $`${checkCmd} railway`.quiet().then(() => true).catch(() => false)
|
|
38
|
+
|
|
39
|
+
if (hasRailway) return true
|
|
40
|
+
|
|
41
|
+
console.log(pc.yellow("Railway CLI nao encontrado. Instalando..."))
|
|
42
|
+
console.log()
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
if (process.platform === "win32") {
|
|
46
|
+
await $`powershell -c "iwr https://railway.app/install.ps1 -useb | iex"`.quiet()
|
|
47
|
+
} else {
|
|
48
|
+
await $`curl -fsSL https://railway.app/install.sh | sh`.quiet()
|
|
49
|
+
}
|
|
50
|
+
console.log(pc.green("Railway CLI instalado com sucesso!"))
|
|
51
|
+
return true
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.log(pc.red("Erro ao instalar Railway CLI."))
|
|
54
|
+
console.log(pc.dim("Instale manualmente: https://docs.railway.app/guides/cli"))
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// M30: Listar projetos Railway via CLI
|
|
60
|
+
async function listRailwayProjects(): Promise<string[]> {
|
|
61
|
+
try {
|
|
62
|
+
const result = await $`railway list`.text()
|
|
63
|
+
// Parse output: primeira linha é o team, demais são projetos
|
|
64
|
+
const lines = result.trim().split("\n").filter(l => l.trim())
|
|
65
|
+
// Remove primeira linha (team name) e extrai nomes dos projetos
|
|
66
|
+
return lines.slice(1).map(l => l.trim())
|
|
67
|
+
} catch {
|
|
68
|
+
return []
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// M30: Verificar autenticacao Railway
|
|
73
|
+
async function isRailwayAuthenticated(): Promise<boolean> {
|
|
74
|
+
try {
|
|
75
|
+
await $`railway whoami`.quiet()
|
|
76
|
+
return true
|
|
77
|
+
} catch {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
14
80
|
}
|
|
15
81
|
|
|
16
82
|
export async function create(args: string[]) {
|
|
@@ -60,6 +126,17 @@ export async function create(args: string[]) {
|
|
|
60
126
|
process.exit(1)
|
|
61
127
|
}
|
|
62
128
|
|
|
129
|
+
// M30: Verificar/instalar Railway CLI
|
|
130
|
+
const hasRailway = await ensureRailwayCli()
|
|
131
|
+
if (hasRailway) {
|
|
132
|
+
const railwayAuth = await isRailwayAuthenticated()
|
|
133
|
+
if (!railwayAuth) {
|
|
134
|
+
console.log(pc.yellow("Railway CLI nao autenticado."))
|
|
135
|
+
console.log(pc.dim("Execute: railway login"))
|
|
136
|
+
console.log()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
63
140
|
const hasYes = args.includes("-y") || args.includes("--yes")
|
|
64
141
|
const projectName = args.find(a => !a.startsWith("-"))
|
|
65
142
|
|
|
@@ -68,16 +145,29 @@ export async function create(args: string[]) {
|
|
|
68
145
|
let config: ProjectConfig | symbol
|
|
69
146
|
|
|
70
147
|
if (hasYes && projectName) {
|
|
71
|
-
// Modo automatico com defaults
|
|
148
|
+
// Modo automatico com defaults (sem configs de infra)
|
|
72
149
|
config = {
|
|
73
150
|
name: projectName,
|
|
74
151
|
type: "fast",
|
|
75
152
|
git: true,
|
|
76
153
|
install: true,
|
|
154
|
+
github: false,
|
|
155
|
+
githubOrg: null,
|
|
156
|
+
githubDescription: "",
|
|
157
|
+
contractNumber: "",
|
|
158
|
+
resendApiKey: "",
|
|
159
|
+
resendFromEmail: "",
|
|
160
|
+
contactEmail: "",
|
|
161
|
+
railwayProject: "",
|
|
162
|
+
railwayToken: "",
|
|
163
|
+
stagingUrl: "",
|
|
164
|
+
productionUrl: "",
|
|
77
165
|
}
|
|
78
166
|
console.log(pc.dim(` Projeto: ${projectName}`))
|
|
79
167
|
console.log(pc.dim(` Tipo: fast`))
|
|
80
168
|
console.log(pc.dim(` Git: sim`))
|
|
169
|
+
console.log(pc.dim(` GitHub: nao`))
|
|
170
|
+
console.log(pc.dim(` Infra: configurar depois`))
|
|
81
171
|
console.log(pc.dim(` Instalar: sim`))
|
|
82
172
|
console.log()
|
|
83
173
|
} else {
|
|
@@ -130,41 +220,237 @@ async function promptConfig(initialName?: string): Promise<ProjectConfig | symbo
|
|
|
130
220
|
|
|
131
221
|
if (p.isCancel(type)) return type
|
|
132
222
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
223
|
+
const git = await p.confirm({
|
|
224
|
+
message: "Inicializar repositorio Git?",
|
|
225
|
+
initialValue: true,
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
if (p.isCancel(git)) return git
|
|
229
|
+
|
|
230
|
+
// GitHub options (only if git is enabled)
|
|
231
|
+
let github = false
|
|
232
|
+
let githubOrg: string | null = null
|
|
233
|
+
let githubDescription = ""
|
|
234
|
+
|
|
235
|
+
if (git) {
|
|
236
|
+
const createGithub = await p.confirm({
|
|
237
|
+
message: "Criar repositorio no GitHub?",
|
|
238
|
+
initialValue: false,
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
if (p.isCancel(createGithub)) return createGithub
|
|
242
|
+
|
|
243
|
+
github = createGithub as boolean
|
|
244
|
+
|
|
245
|
+
if (github) {
|
|
246
|
+
// M13: Selecao de organizacao GitHub
|
|
247
|
+
const org = await p.select({
|
|
248
|
+
message: "Organizacao GitHub:",
|
|
249
|
+
options: [
|
|
250
|
+
{ value: "nimbuslab", label: "nimbuslab", hint: "Org principal" },
|
|
251
|
+
{ value: "fast-by-nimbuslab", label: "fast-by-nimbuslab", hint: "Projetos de clientes" },
|
|
252
|
+
{ value: "nimbuslab-templates", label: "nimbuslab-templates", hint: "Templates" },
|
|
253
|
+
{ value: null, label: "Pessoal", hint: "Sem organizacao" },
|
|
254
|
+
],
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
if (p.isCancel(org)) return org
|
|
258
|
+
githubOrg = org as string | null
|
|
259
|
+
|
|
260
|
+
// M14: Descricao do repo
|
|
261
|
+
const description = await p.text({
|
|
262
|
+
message: "Descricao do repositorio:",
|
|
263
|
+
placeholder: "Landing page para cliente X",
|
|
264
|
+
initialValue: type === "fast" ? "Landing page fast by nimbuslab" : "SaaS fast+ by nimbuslab",
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
if (p.isCancel(description)) return description
|
|
268
|
+
githubDescription = description as string
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// M26: Número do contrato (apenas para fast)
|
|
273
|
+
let contractNumber = ""
|
|
274
|
+
if (type === "fast") {
|
|
275
|
+
const contract = await p.text({
|
|
276
|
+
message: "Numero do contrato (ex: 001):",
|
|
277
|
+
placeholder: "001",
|
|
278
|
+
validate: (v) => v ? undefined : "Numero do contrato e obrigatorio para fast",
|
|
279
|
+
})
|
|
280
|
+
if (p.isCancel(contract)) return contract
|
|
281
|
+
contractNumber = contract as string
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// M21-M23: Configuracoes de infra (opcional)
|
|
285
|
+
const configureInfra = await p.confirm({
|
|
286
|
+
message: "Configurar infra agora? (Resend, URLs)",
|
|
287
|
+
initialValue: true,
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
if (p.isCancel(configureInfra)) return configureInfra
|
|
291
|
+
|
|
292
|
+
let resendApiKey = ""
|
|
293
|
+
let resendFromEmail = ""
|
|
294
|
+
let contactEmail = ""
|
|
295
|
+
let railwayProject = ""
|
|
296
|
+
let railwayToken = ""
|
|
297
|
+
let stagingUrl = ""
|
|
298
|
+
let productionUrl = ""
|
|
299
|
+
|
|
300
|
+
if (configureInfra) {
|
|
301
|
+
const currentYear = new Date().getFullYear().toString().slice(-2) // 25, 26, etc
|
|
302
|
+
|
|
303
|
+
// M26: URLs padrão baseadas no tipo
|
|
304
|
+
// fast: fast-{contrato}-{ano}.nimbuslab.net.br
|
|
305
|
+
// fast+: {nome}.nimbuslab.net.br
|
|
306
|
+
const defaultStagingUrl = type === "fast"
|
|
307
|
+
? `https://fast-${contractNumber}-${currentYear}.nimbuslab.net.br`
|
|
308
|
+
: `https://${name}.nimbuslab.net.br`
|
|
309
|
+
|
|
310
|
+
// M27: Emails padrão
|
|
311
|
+
// from: no-reply@nimbuslab.com.br
|
|
312
|
+
// to fast: fast@nimbuslab.com.br
|
|
313
|
+
// to fast+: suporte@nimbuslab.com.br
|
|
314
|
+
const defaultFromEmail = "no-reply@nimbuslab.com.br"
|
|
315
|
+
const defaultContactEmail = type === "fast"
|
|
316
|
+
? "fast@nimbuslab.com.br"
|
|
317
|
+
: "suporte@nimbuslab.com.br"
|
|
318
|
+
|
|
319
|
+
// M27: Resend config
|
|
320
|
+
console.log()
|
|
321
|
+
console.log(pc.dim(" Resend (Email)"))
|
|
322
|
+
|
|
323
|
+
const resendKey = await p.text({
|
|
324
|
+
message: "RESEND_API_KEY:",
|
|
325
|
+
placeholder: "re_xxxxxxxxxxxx",
|
|
326
|
+
})
|
|
327
|
+
if (p.isCancel(resendKey)) return resendKey
|
|
328
|
+
resendApiKey = resendKey as string
|
|
329
|
+
|
|
330
|
+
const fromEmail = await p.text({
|
|
331
|
+
message: "Email de envio (from):",
|
|
332
|
+
placeholder: defaultFromEmail,
|
|
333
|
+
initialValue: defaultFromEmail,
|
|
334
|
+
})
|
|
335
|
+
if (p.isCancel(fromEmail)) return fromEmail
|
|
336
|
+
resendFromEmail = fromEmail as string
|
|
337
|
+
|
|
338
|
+
const contact = await p.text({
|
|
339
|
+
message: "Email de contato (recebe formularios):",
|
|
340
|
+
placeholder: defaultContactEmail,
|
|
341
|
+
initialValue: defaultContactEmail,
|
|
342
|
+
})
|
|
343
|
+
if (p.isCancel(contact)) return contact
|
|
344
|
+
contactEmail = contact as string
|
|
345
|
+
|
|
346
|
+
// M26: URLs
|
|
347
|
+
console.log()
|
|
348
|
+
console.log(pc.dim(" URLs do projeto"))
|
|
349
|
+
|
|
350
|
+
const staging = await p.text({
|
|
351
|
+
message: "URL de staging:",
|
|
352
|
+
placeholder: defaultStagingUrl,
|
|
353
|
+
initialValue: defaultStagingUrl,
|
|
354
|
+
})
|
|
355
|
+
if (p.isCancel(staging)) return staging
|
|
356
|
+
stagingUrl = staging as string
|
|
357
|
+
|
|
358
|
+
const production = await p.text({
|
|
359
|
+
message: "URL de producao:",
|
|
360
|
+
placeholder: defaultStagingUrl.replace('.nimbuslab.net.br', '.com.br'),
|
|
361
|
+
initialValue: "",
|
|
362
|
+
})
|
|
363
|
+
if (p.isCancel(production)) return production
|
|
364
|
+
productionUrl = production as string
|
|
365
|
+
|
|
366
|
+
// M30: Railway via CLI
|
|
367
|
+
const railwayAuthenticated = await isRailwayAuthenticated()
|
|
368
|
+
|
|
369
|
+
if (railwayAuthenticated) {
|
|
370
|
+
console.log()
|
|
371
|
+
console.log(pc.dim(" Railway"))
|
|
372
|
+
|
|
373
|
+
const projects = await listRailwayProjects()
|
|
374
|
+
|
|
375
|
+
if (type === "fast") {
|
|
376
|
+
// Fast usa projeto compartilhado "Fast by nimbuslab"
|
|
377
|
+
const fastProject = projects.find(p => p.toLowerCase().includes("fast by nimbuslab"))
|
|
378
|
+
if (fastProject) {
|
|
379
|
+
railwayProject = fastProject
|
|
380
|
+
console.log(pc.green(` Projeto: ${fastProject} (automatico)`))
|
|
381
|
+
} else {
|
|
382
|
+
console.log(pc.yellow(" Projeto 'Fast by nimbuslab' nao encontrado."))
|
|
383
|
+
console.log(pc.dim(" Configure RAILWAY_TOKEN manualmente no .env"))
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
// Fast+ pode escolher projeto existente ou criar novo
|
|
387
|
+
const projectOptions = [
|
|
388
|
+
...projects.map(proj => ({ value: proj, label: proj })),
|
|
389
|
+
{ value: "__new__", label: "Criar novo projeto", hint: "Abrir railway.app/new" },
|
|
390
|
+
{ value: "__skip__", label: "Pular", hint: "Configurar depois" },
|
|
391
|
+
]
|
|
392
|
+
|
|
393
|
+
const selectedProject = await p.select({
|
|
394
|
+
message: "Projeto Railway para este SaaS:",
|
|
395
|
+
options: projectOptions,
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
if (p.isCancel(selectedProject)) return selectedProject
|
|
399
|
+
|
|
400
|
+
if (selectedProject === "__new__") {
|
|
401
|
+
console.log(pc.yellow(" Crie o projeto em: https://railway.app/new"))
|
|
402
|
+
console.log(pc.dim(" Configure RAILWAY_TOKEN depois no .env"))
|
|
403
|
+
} else if (selectedProject !== "__skip__") {
|
|
404
|
+
railwayProject = selectedProject as string
|
|
405
|
+
console.log(pc.green(` Projeto selecionado: ${railwayProject}`))
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
} else {
|
|
409
|
+
console.log()
|
|
410
|
+
console.log(pc.yellow(" Railway: nao autenticado (railway login)"))
|
|
411
|
+
console.log(pc.dim(" Configure RAILWAY_TOKEN manualmente no .env"))
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const install = await p.confirm({
|
|
416
|
+
message: "Instalar dependencias?",
|
|
417
|
+
initialValue: true,
|
|
144
418
|
})
|
|
145
419
|
|
|
146
|
-
if (p.isCancel(
|
|
420
|
+
if (p.isCancel(install)) return install
|
|
147
421
|
|
|
148
422
|
return {
|
|
149
423
|
name: name as string,
|
|
150
424
|
type: type as "fast" | "fast+",
|
|
151
|
-
git:
|
|
152
|
-
install:
|
|
425
|
+
git: git as boolean,
|
|
426
|
+
install: install as boolean,
|
|
427
|
+
github,
|
|
428
|
+
githubOrg,
|
|
429
|
+
githubDescription,
|
|
430
|
+
contractNumber,
|
|
431
|
+
resendApiKey,
|
|
432
|
+
resendFromEmail,
|
|
433
|
+
contactEmail,
|
|
434
|
+
railwayProject,
|
|
435
|
+
railwayToken,
|
|
436
|
+
stagingUrl,
|
|
437
|
+
productionUrl,
|
|
153
438
|
}
|
|
154
439
|
}
|
|
155
440
|
|
|
156
441
|
async function createProject(config: ProjectConfig) {
|
|
157
442
|
const s = p.spinner()
|
|
158
443
|
|
|
159
|
-
// Clone template
|
|
160
|
-
|
|
444
|
+
// Clone template baseado no tipo (fast ou fast+)
|
|
445
|
+
const templateRepo = TEMPLATES[config.type]
|
|
446
|
+
s.start(`Clonando template ${config.type}...`)
|
|
161
447
|
try {
|
|
162
|
-
await $`gh repo clone ${
|
|
448
|
+
await $`gh repo clone ${templateRepo} ${config.name} -- --depth 1`.quiet()
|
|
163
449
|
await rm(join(config.name, ".git"), { recursive: true, force: true })
|
|
164
|
-
s.stop(
|
|
450
|
+
s.stop(`Template ${config.type} clonado`)
|
|
165
451
|
} catch (error) {
|
|
166
452
|
s.stop("Erro ao clonar template")
|
|
167
|
-
throw new Error(
|
|
453
|
+
throw new Error(`Falha ao clonar template ${templateRepo}. Verifique se tem acesso ao repositorio.`)
|
|
168
454
|
}
|
|
169
455
|
|
|
170
456
|
// Update package.json
|
|
@@ -205,6 +491,48 @@ async function createProject(config: ProjectConfig) {
|
|
|
205
491
|
} catch (error) {
|
|
206
492
|
s.stop("Erro ao inicializar Git")
|
|
207
493
|
}
|
|
494
|
+
|
|
495
|
+
// Create GitHub repo if requested (M13 + M14)
|
|
496
|
+
if (config.github) {
|
|
497
|
+
s.start("Criando repositorio no GitHub...")
|
|
498
|
+
try {
|
|
499
|
+
const cwd = config.name
|
|
500
|
+
const repoName = config.githubOrg
|
|
501
|
+
? `${config.githubOrg}/${config.name}`
|
|
502
|
+
: config.name
|
|
503
|
+
|
|
504
|
+
// Create repo with description (private by default for client projects)
|
|
505
|
+
const visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public"
|
|
506
|
+
|
|
507
|
+
// Criar repo sem push automático
|
|
508
|
+
await $`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin`.cwd(cwd).quiet()
|
|
509
|
+
|
|
510
|
+
// Push todas as branches na ordem correta: main -> staging -> develop
|
|
511
|
+
await $`git checkout main`.cwd(cwd).quiet()
|
|
512
|
+
await $`git push -u origin main`.cwd(cwd).quiet()
|
|
513
|
+
await $`git checkout staging`.cwd(cwd).quiet()
|
|
514
|
+
await $`git push -u origin staging`.cwd(cwd).quiet()
|
|
515
|
+
await $`git checkout develop`.cwd(cwd).quiet()
|
|
516
|
+
await $`git push -u origin develop`.cwd(cwd).quiet()
|
|
517
|
+
|
|
518
|
+
s.stop(`GitHub: ${repoName} criado`)
|
|
519
|
+
} catch (error) {
|
|
520
|
+
s.stop("Erro ao criar repositorio GitHub")
|
|
521
|
+
console.log(pc.dim(" Voce pode criar manualmente com: gh repo create"))
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// M23: Gerar .env se configs foram fornecidas
|
|
527
|
+
if (config.resendApiKey || config.stagingUrl) {
|
|
528
|
+
s.start("Gerando arquivo .env...")
|
|
529
|
+
try {
|
|
530
|
+
const envContent = generateEnvFile(config)
|
|
531
|
+
await Bun.write(`${config.name}/.env`, envContent)
|
|
532
|
+
s.stop("Arquivo .env criado")
|
|
533
|
+
} catch (error) {
|
|
534
|
+
s.stop("Erro ao criar .env")
|
|
535
|
+
}
|
|
208
536
|
}
|
|
209
537
|
|
|
210
538
|
// Install deps
|
|
@@ -219,6 +547,50 @@ async function createProject(config: ProjectConfig) {
|
|
|
219
547
|
}
|
|
220
548
|
}
|
|
221
549
|
|
|
550
|
+
// M23: Gerar conteudo do .env
|
|
551
|
+
function generateEnvFile(config: ProjectConfig): string {
|
|
552
|
+
const lines = [
|
|
553
|
+
"# Gerado automaticamente pelo nimbus-cli",
|
|
554
|
+
"# Nao commitar este arquivo!",
|
|
555
|
+
"",
|
|
556
|
+
"# App",
|
|
557
|
+
`NODE_ENV=development`,
|
|
558
|
+
"",
|
|
559
|
+
"# URLs",
|
|
560
|
+
`NEXT_PUBLIC_APP_URL=${config.productionUrl || "http://localhost:3000"}`,
|
|
561
|
+
`STAGING_URL=${config.stagingUrl || ""}`,
|
|
562
|
+
`PRODUCTION_URL=${config.productionUrl || ""}`,
|
|
563
|
+
"",
|
|
564
|
+
"# Resend (Email)",
|
|
565
|
+
`RESEND_API_KEY=${config.resendApiKey || ""}`,
|
|
566
|
+
`RESEND_FROM_EMAIL=${config.resendFromEmail || ""}`,
|
|
567
|
+
`CONTACT_EMAIL=${config.contactEmail || ""}`,
|
|
568
|
+
]
|
|
569
|
+
|
|
570
|
+
// Railway (projeto e token)
|
|
571
|
+
if (config.railwayProject || config.railwayToken) {
|
|
572
|
+
lines.push("")
|
|
573
|
+
lines.push("# Railway")
|
|
574
|
+
if (config.railwayProject) {
|
|
575
|
+
lines.push(`# Projeto: ${config.railwayProject}`)
|
|
576
|
+
}
|
|
577
|
+
lines.push(`RAILWAY_TOKEN=${config.railwayToken || "# Configure com: railway link"}`)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Fast+ specific vars
|
|
581
|
+
if (config.type === "fast+") {
|
|
582
|
+
lines.push("")
|
|
583
|
+
lines.push("# Database (fast+)")
|
|
584
|
+
lines.push("DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app")
|
|
585
|
+
lines.push("")
|
|
586
|
+
lines.push("# Auth (fast+)")
|
|
587
|
+
lines.push("BETTER_AUTH_SECRET=")
|
|
588
|
+
lines.push("BETTER_AUTH_URL=http://localhost:3000")
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return lines.join("\n") + "\n"
|
|
592
|
+
}
|
|
593
|
+
|
|
222
594
|
function showNextSteps(config: ProjectConfig) {
|
|
223
595
|
console.log()
|
|
224
596
|
console.log(pc.bold("Proximos passos:"))
|
|
@@ -237,18 +609,37 @@ function showNextSteps(config: ProjectConfig) {
|
|
|
237
609
|
if (config.git) {
|
|
238
610
|
console.log(pc.dim(" Git flow: main -> staging -> develop (branch atual)"))
|
|
239
611
|
console.log()
|
|
612
|
+
|
|
613
|
+
// GitHub info
|
|
614
|
+
if (config.github) {
|
|
615
|
+
const repoUrl = config.githubOrg
|
|
616
|
+
? `https://github.com/${config.githubOrg}/${config.name}`
|
|
617
|
+
: `https://github.com/${config.name}`
|
|
618
|
+
console.log(pc.green(` GitHub: ${repoUrl}`))
|
|
619
|
+
console.log()
|
|
620
|
+
} else {
|
|
621
|
+
console.log(pc.yellow(" Dica: Para criar repo GitHub, use 'gh repo create' ou 'bun setup'."))
|
|
622
|
+
console.log()
|
|
623
|
+
}
|
|
240
624
|
}
|
|
241
625
|
|
|
242
626
|
if (config.type === "fast+") {
|
|
243
|
-
console.log(pc.dim(" Dica:
|
|
627
|
+
console.log(pc.dim(" Dica: Para fast+, configure DATABASE_URL e BETTER_AUTH_SECRET no .env"))
|
|
628
|
+
if (!config.railwayToken) {
|
|
629
|
+
console.log(pc.dim(" Railway: Crie um projeto em https://railway.app/new"))
|
|
630
|
+
}
|
|
244
631
|
console.log()
|
|
245
632
|
}
|
|
246
633
|
|
|
247
|
-
//
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
634
|
+
// Info sobre .env
|
|
635
|
+
if (config.resendApiKey || config.stagingUrl) {
|
|
636
|
+
console.log(pc.green(" .env configurado com sucesso!"))
|
|
637
|
+
console.log()
|
|
638
|
+
} else {
|
|
639
|
+
console.log(pc.yellow(" Dica: Configure .env manualmente ou use 'bun setup'."))
|
|
640
|
+
console.log()
|
|
641
|
+
}
|
|
251
642
|
|
|
252
|
-
console.log(pc.dim(" Documentacao: https://github.com/nimbuslab
|
|
643
|
+
console.log(pc.dim(" Documentacao: https://github.com/nimbuslab-templates"))
|
|
253
644
|
console.log()
|
|
254
645
|
}
|