@nimbuslab/cli 0.2.0 → 0.2.5
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 +148 -27
- package/package.json +1 -1
- package/src/commands/create.ts +194 -32
package/dist/index.js
CHANGED
|
@@ -774,6 +774,45 @@ var TEMPLATES = {
|
|
|
774
774
|
fast: "nimbuslab-templates/fast-template",
|
|
775
775
|
"fast+": "nimbuslab-templates/fastplus-template"
|
|
776
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
|
+
}
|
|
777
816
|
async function create(args) {
|
|
778
817
|
const checkCmd = process.platform === "win32" ? "where" : "which";
|
|
779
818
|
const hasBun = await $2`${checkCmd} bun`.quiet().then(() => true).catch(() => false);
|
|
@@ -814,6 +853,15 @@ async function create(args) {
|
|
|
814
853
|
console.log(import_picocolors3.default.dim("Execute: gh auth login"));
|
|
815
854
|
process.exit(1);
|
|
816
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
|
+
}
|
|
817
865
|
const hasYes = args.includes("-y") || args.includes("--yes");
|
|
818
866
|
const projectName = args.find((a) => !a.startsWith("-"));
|
|
819
867
|
Ie(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" Novo Projeto nimbuslab ")));
|
|
@@ -827,9 +875,11 @@ async function create(args) {
|
|
|
827
875
|
github: false,
|
|
828
876
|
githubOrg: null,
|
|
829
877
|
githubDescription: "",
|
|
878
|
+
contractNumber: "",
|
|
830
879
|
resendApiKey: "",
|
|
831
880
|
resendFromEmail: "",
|
|
832
881
|
contactEmail: "",
|
|
882
|
+
railwayProject: "",
|
|
833
883
|
railwayToken: "",
|
|
834
884
|
stagingUrl: "",
|
|
835
885
|
productionUrl: ""
|
|
@@ -838,7 +888,7 @@ async function create(args) {
|
|
|
838
888
|
console.log(import_picocolors3.default.dim(` Tipo: fast`));
|
|
839
889
|
console.log(import_picocolors3.default.dim(` Git: sim`));
|
|
840
890
|
console.log(import_picocolors3.default.dim(` GitHub: nao`));
|
|
841
|
-
console.log(import_picocolors3.default.dim(` Infra: configurar depois
|
|
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 {
|
|
@@ -925,6 +975,17 @@ async function promptConfig(initialName) {
|
|
|
925
975
|
githubDescription = description;
|
|
926
976
|
}
|
|
927
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
|
+
}
|
|
928
989
|
const configureInfra = await ye({
|
|
929
990
|
message: "Configurar infra agora? (Resend, URLs)",
|
|
930
991
|
initialValue: true
|
|
@@ -934,11 +995,15 @@ async function promptConfig(initialName) {
|
|
|
934
995
|
let resendApiKey = "";
|
|
935
996
|
let resendFromEmail = "";
|
|
936
997
|
let contactEmail = "";
|
|
998
|
+
let railwayProject = "";
|
|
937
999
|
let railwayToken = "";
|
|
938
1000
|
let stagingUrl = "";
|
|
939
1001
|
let productionUrl = "";
|
|
940
1002
|
if (configureInfra) {
|
|
941
|
-
const
|
|
1003
|
+
const currentYear = new Date().getFullYear().toString().slice(-3);
|
|
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";
|
|
942
1007
|
console.log();
|
|
943
1008
|
console.log(import_picocolors3.default.dim(" Resend (Email)"));
|
|
944
1009
|
const resendKey = await he({
|
|
@@ -950,16 +1015,16 @@ async function promptConfig(initialName) {
|
|
|
950
1015
|
resendApiKey = resendKey;
|
|
951
1016
|
const fromEmail = await he({
|
|
952
1017
|
message: "Email de envio (from):",
|
|
953
|
-
placeholder:
|
|
954
|
-
initialValue:
|
|
1018
|
+
placeholder: defaultFromEmail,
|
|
1019
|
+
initialValue: defaultFromEmail
|
|
955
1020
|
});
|
|
956
1021
|
if (pD(fromEmail))
|
|
957
1022
|
return fromEmail;
|
|
958
1023
|
resendFromEmail = fromEmail;
|
|
959
1024
|
const contact = await he({
|
|
960
1025
|
message: "Email de contato (recebe formularios):",
|
|
961
|
-
placeholder:
|
|
962
|
-
initialValue:
|
|
1026
|
+
placeholder: defaultContactEmail,
|
|
1027
|
+
initialValue: defaultContactEmail
|
|
963
1028
|
});
|
|
964
1029
|
if (pD(contact))
|
|
965
1030
|
return contact;
|
|
@@ -968,34 +1033,68 @@ async function promptConfig(initialName) {
|
|
|
968
1033
|
console.log(import_picocolors3.default.dim(" URLs do projeto"));
|
|
969
1034
|
const staging = await he({
|
|
970
1035
|
message: "URL de staging:",
|
|
971
|
-
placeholder:
|
|
972
|
-
initialValue:
|
|
1036
|
+
placeholder: defaultStagingUrl,
|
|
1037
|
+
initialValue: defaultStagingUrl
|
|
973
1038
|
});
|
|
974
1039
|
if (pD(staging))
|
|
975
1040
|
return staging;
|
|
976
1041
|
stagingUrl = staging;
|
|
977
1042
|
const production = await he({
|
|
978
1043
|
message: "URL de producao:",
|
|
979
|
-
placeholder:
|
|
980
|
-
initialValue:
|
|
1044
|
+
placeholder: defaultStagingUrl.replace(".nimbuslab.net.br", ".com.br"),
|
|
1045
|
+
initialValue: ""
|
|
981
1046
|
});
|
|
982
1047
|
if (pD(production))
|
|
983
1048
|
return production;
|
|
984
1049
|
productionUrl = production;
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
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: "via railway init" },
|
|
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
|
+
const projectNameForRailway = name;
|
|
1078
|
+
console.log(import_picocolors3.default.dim(` Criando projeto "${projectNameForRailway}" no Railway...`));
|
|
1079
|
+
try {
|
|
1080
|
+
const result = await $2`railway init -n ${projectNameForRailway} --json`.text();
|
|
1081
|
+
const newProject = JSON.parse(result);
|
|
1082
|
+
railwayProject = newProject.name || projectNameForRailway;
|
|
1083
|
+
console.log(import_picocolors3.default.green(` Projeto "${railwayProject}" criado com sucesso!`));
|
|
1084
|
+
console.log(import_picocolors3.default.dim(` ID: ${newProject.id || "N/A"}`));
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
console.log(import_picocolors3.default.yellow(" Erro ao criar projeto via CLI."));
|
|
1087
|
+
console.log(import_picocolors3.default.dim(" Crie manualmente em: https://railway.app/new"));
|
|
1088
|
+
}
|
|
1089
|
+
} else if (selectedProject !== "__skip__") {
|
|
1090
|
+
railwayProject = selectedProject;
|
|
1091
|
+
console.log(import_picocolors3.default.green(` Projeto selecionado: ${railwayProject}`));
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
} else {
|
|
1095
|
+
console.log();
|
|
1096
|
+
console.log(import_picocolors3.default.yellow(" Railway: nao autenticado (railway login)"));
|
|
1097
|
+
console.log(import_picocolors3.default.dim(" Configure RAILWAY_TOKEN manualmente no .env"));
|
|
999
1098
|
}
|
|
1000
1099
|
}
|
|
1001
1100
|
const install = await ye({
|
|
@@ -1012,9 +1111,11 @@ async function promptConfig(initialName) {
|
|
|
1012
1111
|
github,
|
|
1013
1112
|
githubOrg,
|
|
1014
1113
|
githubDescription,
|
|
1114
|
+
contractNumber,
|
|
1015
1115
|
resendApiKey,
|
|
1016
1116
|
resendFromEmail,
|
|
1017
1117
|
contactEmail,
|
|
1118
|
+
railwayProject,
|
|
1018
1119
|
railwayToken,
|
|
1019
1120
|
stagingUrl,
|
|
1020
1121
|
productionUrl
|
|
@@ -1065,8 +1166,12 @@ async function createProject(config) {
|
|
|
1065
1166
|
const cwd = config.name;
|
|
1066
1167
|
const repoName = config.githubOrg ? `${config.githubOrg}/${config.name}` : config.name;
|
|
1067
1168
|
const visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public";
|
|
1068
|
-
await $2`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin
|
|
1169
|
+
await $2`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin`.cwd(cwd).quiet();
|
|
1170
|
+
await $2`git checkout main`.cwd(cwd).quiet();
|
|
1171
|
+
await $2`git push -u origin main`.cwd(cwd).quiet();
|
|
1172
|
+
await $2`git checkout staging`.cwd(cwd).quiet();
|
|
1069
1173
|
await $2`git push -u origin staging`.cwd(cwd).quiet();
|
|
1174
|
+
await $2`git checkout develop`.cwd(cwd).quiet();
|
|
1070
1175
|
await $2`git push -u origin develop`.cwd(cwd).quiet();
|
|
1071
1176
|
s.stop(`GitHub: ${repoName} criado`);
|
|
1072
1177
|
} catch (error) {
|
|
@@ -1075,6 +1180,16 @@ async function createProject(config) {
|
|
|
1075
1180
|
}
|
|
1076
1181
|
}
|
|
1077
1182
|
}
|
|
1183
|
+
if (config.railwayProject) {
|
|
1184
|
+
s.start(`Linkando Railway: ${config.railwayProject}...`);
|
|
1185
|
+
try {
|
|
1186
|
+
await $2`railway link -p ${config.railwayProject}`.cwd(config.name).quiet();
|
|
1187
|
+
s.stop(`Railway linkado: ${config.railwayProject}`);
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
s.stop("Erro ao linkar Railway");
|
|
1190
|
+
console.log(import_picocolors3.default.dim(" Execute manualmente: railway link"));
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1078
1193
|
if (config.resendApiKey || config.stagingUrl) {
|
|
1079
1194
|
s.start("Gerando arquivo .env...");
|
|
1080
1195
|
try {
|
|
@@ -1113,10 +1228,13 @@ function generateEnvFile(config) {
|
|
|
1113
1228
|
`RESEND_FROM_EMAIL=${config.resendFromEmail || ""}`,
|
|
1114
1229
|
`CONTACT_EMAIL=${config.contactEmail || ""}`
|
|
1115
1230
|
];
|
|
1116
|
-
if (config.railwayToken) {
|
|
1231
|
+
if (config.railwayProject || config.railwayToken) {
|
|
1117
1232
|
lines.push("");
|
|
1118
1233
|
lines.push("# Railway");
|
|
1119
|
-
|
|
1234
|
+
if (config.railwayProject) {
|
|
1235
|
+
lines.push(`# Projeto: ${config.railwayProject}`);
|
|
1236
|
+
}
|
|
1237
|
+
lines.push(`RAILWAY_TOKEN=${config.railwayToken || "# Configure com: railway link"}`);
|
|
1120
1238
|
}
|
|
1121
1239
|
if (config.type === "fast+") {
|
|
1122
1240
|
lines.push("");
|
|
@@ -1156,6 +1274,9 @@ function showNextSteps(config) {
|
|
|
1156
1274
|
}
|
|
1157
1275
|
if (config.type === "fast+") {
|
|
1158
1276
|
console.log(import_picocolors3.default.dim(" Dica: Para fast+, configure DATABASE_URL e BETTER_AUTH_SECRET no .env"));
|
|
1277
|
+
if (!config.railwayToken) {
|
|
1278
|
+
console.log(import_picocolors3.default.dim(" Railway: Crie um projeto em https://railway.app/new"));
|
|
1279
|
+
}
|
|
1159
1280
|
console.log();
|
|
1160
1281
|
}
|
|
1161
1282
|
if (config.resendApiKey || config.stagingUrl) {
|
package/package.json
CHANGED
package/src/commands/create.ts
CHANGED
|
@@ -18,15 +18,67 @@ interface ProjectConfig {
|
|
|
18
18
|
github: boolean
|
|
19
19
|
githubOrg: string | null
|
|
20
20
|
githubDescription: string
|
|
21
|
+
// M26: Número do contrato (fast only)
|
|
22
|
+
contractNumber: string
|
|
21
23
|
// M21-M23: Configs de infra
|
|
22
24
|
resendApiKey: string
|
|
23
25
|
resendFromEmail: string
|
|
24
26
|
contactEmail: string
|
|
27
|
+
// M30: Railway via CLI
|
|
28
|
+
railwayProject: string
|
|
25
29
|
railwayToken: string
|
|
26
30
|
stagingUrl: string
|
|
27
31
|
productionUrl: string
|
|
28
32
|
}
|
|
29
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
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
30
82
|
export async function create(args: string[]) {
|
|
31
83
|
// Verifica dependencias (cross-platform)
|
|
32
84
|
const checkCmd = process.platform === "win32" ? "where" : "which"
|
|
@@ -74,6 +126,17 @@ export async function create(args: string[]) {
|
|
|
74
126
|
process.exit(1)
|
|
75
127
|
}
|
|
76
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
|
+
|
|
77
140
|
const hasYes = args.includes("-y") || args.includes("--yes")
|
|
78
141
|
const projectName = args.find(a => !a.startsWith("-"))
|
|
79
142
|
|
|
@@ -91,9 +154,11 @@ export async function create(args: string[]) {
|
|
|
91
154
|
github: false,
|
|
92
155
|
githubOrg: null,
|
|
93
156
|
githubDescription: "",
|
|
157
|
+
contractNumber: "",
|
|
94
158
|
resendApiKey: "",
|
|
95
159
|
resendFromEmail: "",
|
|
96
160
|
contactEmail: "",
|
|
161
|
+
railwayProject: "",
|
|
97
162
|
railwayToken: "",
|
|
98
163
|
stagingUrl: "",
|
|
99
164
|
productionUrl: "",
|
|
@@ -102,7 +167,7 @@ export async function create(args: string[]) {
|
|
|
102
167
|
console.log(pc.dim(` Tipo: fast`))
|
|
103
168
|
console.log(pc.dim(` Git: sim`))
|
|
104
169
|
console.log(pc.dim(` GitHub: nao`))
|
|
105
|
-
console.log(pc.dim(` Infra: configurar depois
|
|
170
|
+
console.log(pc.dim(` Infra: configurar depois`))
|
|
106
171
|
console.log(pc.dim(` Instalar: sim`))
|
|
107
172
|
console.log()
|
|
108
173
|
} else {
|
|
@@ -204,6 +269,18 @@ async function promptConfig(initialName?: string): Promise<ProjectConfig | symbo
|
|
|
204
269
|
}
|
|
205
270
|
}
|
|
206
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
|
+
|
|
207
284
|
// M21-M23: Configuracoes de infra (opcional)
|
|
208
285
|
const configureInfra = await p.confirm({
|
|
209
286
|
message: "Configurar infra agora? (Resend, URLs)",
|
|
@@ -215,15 +292,31 @@ async function promptConfig(initialName?: string): Promise<ProjectConfig | symbo
|
|
|
215
292
|
let resendApiKey = ""
|
|
216
293
|
let resendFromEmail = ""
|
|
217
294
|
let contactEmail = ""
|
|
295
|
+
let railwayProject = ""
|
|
218
296
|
let railwayToken = ""
|
|
219
297
|
let stagingUrl = ""
|
|
220
298
|
let productionUrl = ""
|
|
221
299
|
|
|
222
300
|
if (configureInfra) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
//
|
|
301
|
+
const currentYear = new Date().getFullYear().toString().slice(-3) // 025, 026, 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
|
|
227
320
|
console.log()
|
|
228
321
|
console.log(pc.dim(" Resend (Email)"))
|
|
229
322
|
|
|
@@ -236,54 +329,97 @@ async function promptConfig(initialName?: string): Promise<ProjectConfig | symbo
|
|
|
236
329
|
|
|
237
330
|
const fromEmail = await p.text({
|
|
238
331
|
message: "Email de envio (from):",
|
|
239
|
-
placeholder:
|
|
240
|
-
initialValue:
|
|
332
|
+
placeholder: defaultFromEmail,
|
|
333
|
+
initialValue: defaultFromEmail,
|
|
241
334
|
})
|
|
242
335
|
if (p.isCancel(fromEmail)) return fromEmail
|
|
243
336
|
resendFromEmail = fromEmail as string
|
|
244
337
|
|
|
245
338
|
const contact = await p.text({
|
|
246
339
|
message: "Email de contato (recebe formularios):",
|
|
247
|
-
placeholder:
|
|
248
|
-
initialValue:
|
|
340
|
+
placeholder: defaultContactEmail,
|
|
341
|
+
initialValue: defaultContactEmail,
|
|
249
342
|
})
|
|
250
343
|
if (p.isCancel(contact)) return contact
|
|
251
344
|
contactEmail = contact as string
|
|
252
345
|
|
|
253
|
-
// URLs
|
|
346
|
+
// M26: URLs
|
|
254
347
|
console.log()
|
|
255
348
|
console.log(pc.dim(" URLs do projeto"))
|
|
256
349
|
|
|
257
350
|
const staging = await p.text({
|
|
258
351
|
message: "URL de staging:",
|
|
259
|
-
placeholder:
|
|
260
|
-
initialValue:
|
|
352
|
+
placeholder: defaultStagingUrl,
|
|
353
|
+
initialValue: defaultStagingUrl,
|
|
261
354
|
})
|
|
262
355
|
if (p.isCancel(staging)) return staging
|
|
263
356
|
stagingUrl = staging as string
|
|
264
357
|
|
|
265
358
|
const production = await p.text({
|
|
266
359
|
message: "URL de producao:",
|
|
267
|
-
placeholder:
|
|
268
|
-
initialValue:
|
|
360
|
+
placeholder: defaultStagingUrl.replace('.nimbuslab.net.br', '.com.br'),
|
|
361
|
+
initialValue: "",
|
|
269
362
|
})
|
|
270
363
|
if (p.isCancel(production)) return production
|
|
271
364
|
productionUrl = production as string
|
|
272
365
|
|
|
273
|
-
// Railway
|
|
274
|
-
const
|
|
275
|
-
message: "Configurar Railway token?",
|
|
276
|
-
initialValue: false,
|
|
277
|
-
})
|
|
278
|
-
if (p.isCancel(configRailway)) return configRailway
|
|
366
|
+
// M30: Railway via CLI
|
|
367
|
+
const railwayAuthenticated = await isRailwayAuthenticated()
|
|
279
368
|
|
|
280
|
-
if (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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: "via railway init" },
|
|
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
|
+
// Criar projeto via Railway CLI
|
|
402
|
+
const projectNameForRailway = name as string
|
|
403
|
+
console.log(pc.dim(` Criando projeto "${projectNameForRailway}" no Railway...`))
|
|
404
|
+
try {
|
|
405
|
+
const result = await $`railway init -n ${projectNameForRailway} --json`.text()
|
|
406
|
+
const newProject = JSON.parse(result)
|
|
407
|
+
railwayProject = newProject.name || projectNameForRailway
|
|
408
|
+
console.log(pc.green(` Projeto "${railwayProject}" criado com sucesso!`))
|
|
409
|
+
console.log(pc.dim(` ID: ${newProject.id || "N/A"}`))
|
|
410
|
+
} catch (error) {
|
|
411
|
+
console.log(pc.yellow(" Erro ao criar projeto via CLI."))
|
|
412
|
+
console.log(pc.dim(" Crie manualmente em: https://railway.app/new"))
|
|
413
|
+
}
|
|
414
|
+
} else if (selectedProject !== "__skip__") {
|
|
415
|
+
railwayProject = selectedProject as string
|
|
416
|
+
console.log(pc.green(` Projeto selecionado: ${railwayProject}`))
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
console.log()
|
|
421
|
+
console.log(pc.yellow(" Railway: nao autenticado (railway login)"))
|
|
422
|
+
console.log(pc.dim(" Configure RAILWAY_TOKEN manualmente no .env"))
|
|
287
423
|
}
|
|
288
424
|
}
|
|
289
425
|
|
|
@@ -302,9 +438,11 @@ async function promptConfig(initialName?: string): Promise<ProjectConfig | symbo
|
|
|
302
438
|
github,
|
|
303
439
|
githubOrg,
|
|
304
440
|
githubDescription,
|
|
441
|
+
contractNumber,
|
|
305
442
|
resendApiKey,
|
|
306
443
|
resendFromEmail,
|
|
307
444
|
contactEmail,
|
|
445
|
+
railwayProject,
|
|
308
446
|
railwayToken,
|
|
309
447
|
stagingUrl,
|
|
310
448
|
productionUrl,
|
|
@@ -376,10 +514,16 @@ async function createProject(config: ProjectConfig) {
|
|
|
376
514
|
|
|
377
515
|
// Create repo with description (private by default for client projects)
|
|
378
516
|
const visibility = config.githubOrg === "fast-by-nimbuslab" ? "--private" : "--public"
|
|
379
|
-
await $`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin --push`.cwd(cwd).quiet()
|
|
380
517
|
|
|
381
|
-
//
|
|
518
|
+
// Criar repo sem push automático
|
|
519
|
+
await $`gh repo create ${repoName} ${visibility} --description ${config.githubDescription} --source . --remote origin`.cwd(cwd).quiet()
|
|
520
|
+
|
|
521
|
+
// Push todas as branches na ordem correta: main -> staging -> develop
|
|
522
|
+
await $`git checkout main`.cwd(cwd).quiet()
|
|
523
|
+
await $`git push -u origin main`.cwd(cwd).quiet()
|
|
524
|
+
await $`git checkout staging`.cwd(cwd).quiet()
|
|
382
525
|
await $`git push -u origin staging`.cwd(cwd).quiet()
|
|
526
|
+
await $`git checkout develop`.cwd(cwd).quiet()
|
|
383
527
|
await $`git push -u origin develop`.cwd(cwd).quiet()
|
|
384
528
|
|
|
385
529
|
s.stop(`GitHub: ${repoName} criado`)
|
|
@@ -390,6 +534,18 @@ async function createProject(config: ProjectConfig) {
|
|
|
390
534
|
}
|
|
391
535
|
}
|
|
392
536
|
|
|
537
|
+
// Railway link (se projeto foi selecionado/criado)
|
|
538
|
+
if (config.railwayProject) {
|
|
539
|
+
s.start(`Linkando Railway: ${config.railwayProject}...`)
|
|
540
|
+
try {
|
|
541
|
+
await $`railway link -p ${config.railwayProject}`.cwd(config.name).quiet()
|
|
542
|
+
s.stop(`Railway linkado: ${config.railwayProject}`)
|
|
543
|
+
} catch (error) {
|
|
544
|
+
s.stop("Erro ao linkar Railway")
|
|
545
|
+
console.log(pc.dim(" Execute manualmente: railway link"))
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
393
549
|
// M23: Gerar .env se configs foram fornecidas
|
|
394
550
|
if (config.resendApiKey || config.stagingUrl) {
|
|
395
551
|
s.start("Gerando arquivo .env...")
|
|
@@ -434,11 +590,14 @@ function generateEnvFile(config: ProjectConfig): string {
|
|
|
434
590
|
`CONTACT_EMAIL=${config.contactEmail || ""}`,
|
|
435
591
|
]
|
|
436
592
|
|
|
437
|
-
// Railway
|
|
438
|
-
if (config.railwayToken) {
|
|
593
|
+
// Railway (projeto e token)
|
|
594
|
+
if (config.railwayProject || config.railwayToken) {
|
|
439
595
|
lines.push("")
|
|
440
596
|
lines.push("# Railway")
|
|
441
|
-
|
|
597
|
+
if (config.railwayProject) {
|
|
598
|
+
lines.push(`# Projeto: ${config.railwayProject}`)
|
|
599
|
+
}
|
|
600
|
+
lines.push(`RAILWAY_TOKEN=${config.railwayToken || "# Configure com: railway link"}`)
|
|
442
601
|
}
|
|
443
602
|
|
|
444
603
|
// Fast+ specific vars
|
|
@@ -489,6 +648,9 @@ function showNextSteps(config: ProjectConfig) {
|
|
|
489
648
|
|
|
490
649
|
if (config.type === "fast+") {
|
|
491
650
|
console.log(pc.dim(" Dica: Para fast+, configure DATABASE_URL e BETTER_AUTH_SECRET no .env"))
|
|
651
|
+
if (!config.railwayToken) {
|
|
652
|
+
console.log(pc.dim(" Railway: Crie um projeto em https://railway.app/new"))
|
|
653
|
+
}
|
|
492
654
|
console.log()
|
|
493
655
|
}
|
|
494
656
|
|