@dataif/cli 0.1.0
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 +16 -0
- package/bin/dataif.js +623 -0
- package/package.json +26 -0
- package/scripts/build-template.mjs +72 -0
- package/templates/dataif/README.md +157 -0
- package/templates/dataif/infra/.env.example +119 -0
- package/templates/dataif/infra/.env.stg.example +119 -0
- package/templates/dataif/infra/airflow/Dockerfile +11 -0
- package/templates/dataif/infra/airflow/Dockerfile.release +17 -0
- package/templates/dataif/infra/airflow/requirements.txt +3 -0
- package/templates/dataif/infra/docker-compose.yml +306 -0
- package/templates/dataif/infra/init-db/01-init-dataif.sh +129 -0
- package/templates/dataif/infra/init-db/pnp-curated-views.sqlinc +444 -0
- package/templates/dataif/infra/init-db/pnp-raw-staging-curated.sqlinc +701 -0
- package/templates/dataif/infra/keycloak/Dockerfile +4 -0
- package/templates/dataif/infra/keycloak/realm-dataif.json +73 -0
- package/templates/dataif/infra/ollama/Dockerfile +9 -0
- package/templates/dataif/infra/ollama/bootstrap-model.sh +100 -0
- package/templates/dataif/infra/ollama/sabia-7b.Modelfile +14 -0
- package/templates/dataif/infra/postgres/Dockerfile +4 -0
- package/templates/dataif/pipelines/airflow/dags/generated/.gitkeep +1 -0
- package/templates/dataif/pipelines/airflow/dags/generated/2020_financeiro_fcc6f1f3_sync.py +9 -0
- package/templates/dataif/pipelines/dataif_pipelines/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/airflow/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/airflow/pnp_pipeline_factory.py +167 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/connector.py +28 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/base/types.py +14 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/config.py +19 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/connector.py +558 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/powerbi_microdados.py +728 -0
- package/templates/dataif/pipelines/dataif_pipelines/connectors/nilo_pecanha/transform.py +296 -0
- package/templates/dataif/pipelines/dataif_pipelines/jobs/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/jobs/nilo_pipeline.py +112 -0
- package/templates/dataif/pipelines/dataif_pipelines/orchestration/__init__.py +21 -0
- package/templates/dataif/pipelines/dataif_pipelines/orchestration/pnp_workflow.py +783 -0
- package/templates/dataif/pipelines/dataif_pipelines/repositories/__init__.py +1 -0
- package/templates/dataif/pipelines/dataif_pipelines/repositories/pnp_raw_repository.py +860 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/__init__.py +19 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_curated_service.py +66 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_download_service.py +534 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_quality_service.py +9 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_raw_ingestion_service.py +124 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/pnp_staging_service.py +271 -0
- package/templates/dataif/pipelines/dataif_pipelines/services/powerbi_catalog_service.py +159 -0
- package/templates/dataif/pipelines/sql/staging/020_pnp_matriculas.sql +112 -0
- package/templates/dataif/pipelines/sql/staging/030_pnp_eficiencia_academica.sql +83 -0
- package/templates/dataif/pipelines/sql/staging/040_pnp_servidores.sql +90 -0
- package/templates/dataif/pipelines/sql/staging/050_pnp_financeiro.sql +72 -0
- package/templates/dataif/pipelines/sql/views_curated/004_mv_pnp_dashboard_fast.sql +204 -0
- package/templates/dataif/pipelines/sql/views_curated/010_vw_pnp_admin_ingestao.sql +51 -0
- package/templates/dataif/pipelines/sql/views_curated/020_vw_pnp_qualidade_dados.sql +114 -0
- package/templates/dataif/pipelines/sql/views_curated/030_vw_pnp_matriculas.sql +67 -0
- package/templates/dataif/pipelines/sql/views_curated/040_vw_pnp_eficiencia.sql +33 -0
- package/templates/dataif/pipelines/sql/views_curated/050_vw_pnp_servidores.sql +30 -0
- package/templates/dataif/pipelines/sql/views_curated/060_vw_pnp_financeiro.sql +22 -0
- package/templates/dataif/pipelines/sql/views_curated/070_vw_pnp_vanna.sql +115 -0
- package/templates/dataif/scripts/configure-env.sh +149 -0
- package/templates/dataif/scripts/create_metabase_pnp_dashboard.py +943 -0
- package/templates/dataif/scripts/create_metabase_pnp_matriculas_dashboard.py +580 -0
- package/templates/dataif/scripts/deploy.sh +79 -0
- package/templates/dataif/scripts/fix_metabase_template_tag_ids.py +91 -0
- package/templates/dataif/scripts/pnp_powerbi_microdados_probe.py +14 -0
- package/templates/dataif/scripts/pnp_validate_raw_run.py +330 -0
- package/templates/dataif/scripts/publish-images.sh +31 -0
- package/templates/dataif/scripts/sync_metabase_dashboard_field_filters.py +241 -0
- package/templates/dataif/scripts/use-vanna-ollama.sh +139 -0
- package/templates/dataif/services/api/.dockerignore +18 -0
- package/templates/dataif/services/api/Dockerfile +12 -0
- package/templates/dataif/services/api/app/__init__.py +1 -0
- package/templates/dataif/services/api/app/auth.py +48 -0
- package/templates/dataif/services/api/app/config.py +59 -0
- package/templates/dataif/services/api/app/keycloak_admin.py +215 -0
- package/templates/dataif/services/api/app/main.py +2432 -0
- package/templates/dataif/services/api/app/metabase_admin.py +191 -0
- package/templates/dataif/services/api/app/metabase_bootstrap.py +44 -0
- package/templates/dataif/services/api/app/metabase_embed.py +15 -0
- package/templates/dataif/services/api/app/pnp_dag_provisioner.py +113 -0
- package/templates/dataif/services/api/app/pnp_instance_repository.py +951 -0
- package/templates/dataif/services/api/app/pnp_powerbi.py +438 -0
- package/templates/dataif/services/api/app/vanna_client.py +32 -0
- package/templates/dataif/services/api/requirements.txt +9 -0
- package/templates/dataif/services/vanna/.dockerignore +18 -0
- package/templates/dataif/services/vanna/Dockerfile +12 -0
- package/templates/dataif/services/vanna/app/config.py +57 -0
- package/templates/dataif/services/vanna/app/main.py +108 -0
- package/templates/dataif/services/vanna/app/runtime_config.py +114 -0
- package/templates/dataif/services/vanna/app/sql_guard.py +123 -0
- package/templates/dataif/services/vanna/app/vanna_engine.py +382 -0
- package/templates/dataif/services/vanna/requirements.txt +8 -0
- package/templates/dataif/services/web/.dockerignore +13 -0
- package/templates/dataif/services/web/Dockerfile +16 -0
- package/templates/dataif/services/web/index.html +12 -0
- package/templates/dataif/services/web/nginx.conf +74 -0
- package/templates/dataif/services/web/package-lock.json +4397 -0
- package/templates/dataif/services/web/package.json +32 -0
- package/templates/dataif/services/web/postcss.config.mjs +5 -0
- package/templates/dataif/services/web/src/App.jsx +2817 -0
- package/templates/dataif/services/web/src/adminAuth.js +245 -0
- package/templates/dataif/services/web/src/assets/avatar_placeholder.png +0 -0
- package/templates/dataif/services/web/src/assets/github_logo_icon_229278.svg +1 -0
- package/templates/dataif/services/web/src/assets/if-logo.png +0 -0
- package/templates/dataif/services/web/src/assets/if.svg +0 -0
- package/templates/dataif/services/web/src/assets/pnp-horizontal.svg +1 -0
- package/templates/dataif/services/web/src/components/AppHeader.jsx +233 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/mobile-header.tsx +56 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-account-card.tsx +209 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-item-button.tsx +67 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-item.tsx +108 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/base-components/nav-list.tsx +83 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/config.ts +23 -0
- package/templates/dataif/services/web/src/components/application/app-navigation/header-navigation.tsx +240 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-base.tsx +376 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-dot.tsx +52 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination-line.tsx +48 -0
- package/templates/dataif/services/web/src/components/application/pagination/pagination.tsx +328 -0
- package/templates/dataif/services/web/src/components/application/tabs/tabs.tsx +223 -0
- package/templates/dataif/services/web/src/components/base/avatar/avatar-label-group.tsx +28 -0
- package/templates/dataif/services/web/src/components/base/avatar/avatar.tsx +129 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-add-button.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-company-icon.tsx +24 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/avatar-online-indicator.tsx +29 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/index.tsx +4 -0
- package/templates/dataif/services/web/src/components/base/avatar/base-components/verified-tick.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/badges/badge-types.ts +264 -0
- package/templates/dataif/services/web/src/components/base/badges/badges.tsx +415 -0
- package/templates/dataif/services/web/src/components/base/button-group/button-group.tsx +104 -0
- package/templates/dataif/services/web/src/components/base/buttons/button.tsx +267 -0
- package/templates/dataif/services/web/src/components/base/input/hint-text.tsx +31 -0
- package/templates/dataif/services/web/src/components/base/input/input.tsx +269 -0
- package/templates/dataif/services/web/src/components/base/input/label.tsx +48 -0
- package/templates/dataif/services/web/src/components/base/radio-buttons/radio-buttons.tsx +127 -0
- package/templates/dataif/services/web/src/components/base/select/combobox.tsx +150 -0
- package/templates/dataif/services/web/src/components/base/select/multi-select.tsx +361 -0
- package/templates/dataif/services/web/src/components/base/select/popover.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/select/select-item.tsx +95 -0
- package/templates/dataif/services/web/src/components/base/select/select-native.tsx +67 -0
- package/templates/dataif/services/web/src/components/base/select/select.tsx +144 -0
- package/templates/dataif/services/web/src/components/base/tags/base-components/tag-close-x.tsx +32 -0
- package/templates/dataif/services/web/src/components/base/tooltip/tooltip.tsx +107 -0
- package/templates/dataif/services/web/src/components/foundations/dot-icon.tsx +22 -0
- package/templates/dataif/services/web/src/components/foundations/logo/untitledui-logo-minimal.tsx +170 -0
- package/templates/dataif/services/web/src/components/foundations/logo/untitledui-logo.tsx +58 -0
- package/templates/dataif/services/web/src/hooks/use-breakpoint.ts +34 -0
- package/templates/dataif/services/web/src/hooks/use-resize-observer.ts +67 -0
- package/templates/dataif/services/web/src/main.jsx +14 -0
- package/templates/dataif/services/web/src/providers/theme-provider.jsx +62 -0
- package/templates/dataif/services/web/src/styles/globals.css +60 -0
- package/templates/dataif/services/web/src/styles/theme.css +1326 -0
- package/templates/dataif/services/web/src/styles/typography.css +430 -0
- package/templates/dataif/services/web/src/styles.css +1287 -0
- package/templates/dataif/services/web/src/utils/cx.ts +24 -0
- package/templates/dataif/services/web/src/utils/is-react-component.ts +33 -0
- package/templates/dataif/services/web/vite.config.js +14 -0
- package/templates/dataif/sql/ddl/001_schemas.sql +6 -0
- package/templates/dataif/sql/ddl/003_pnp_raw_staging_curated.sql +699 -0
- package/templates/dataif/sql/migrations/001_pnp_phase1_backfill.sql +3 -0
- package/templates/dataif/sql/migrations/002_pnp_phase2_admin_config_backfill.sql +184 -0
- package/templates/dataif/sql/migrations/003_pnp_phase3_raw_tabular_backfill.sql +3 -0
- package/templates/dataif/sql/migrations/004_pnp_phase3_raw_backfill_support_index.sql +3 -0
- package/templates/dataif/sql/migrations/005_pnp_phase7_staging_support_indexes.sql +2 -0
- package/templates/dataif/sql/migrations/006_pnp_phase7_staging_autovacuum_tuning.sql +2 -0
- package/templates/dataif/sql/migrations/007_pnp_phase7b_run_packages.sql +20 -0
- package/templates/dataif/sql/migrations/008_pnp_phase7a_pipeline_endpoints.sql +169 -0
- package/templates/dataif/sql/migrations/009_pnp_phase8_curated.sql +35 -0
- package/templates/dataif/sql/migrations/010_pnp_phase10_staging_incremental_upsert.sql +3 -0
- package/templates/dataif/sql/migrations/010_pnp_pipeline_uuid.sql +51 -0
- package/templates/dataif/sql/migrations/011_app_settings.sql +7 -0
- package/templates/dataif/sql/staging/020_pnp_matriculas.sql +112 -0
- package/templates/dataif/sql/staging/030_pnp_eficiencia_academica.sql +83 -0
- package/templates/dataif/sql/staging/040_pnp_servidores.sql +90 -0
- package/templates/dataif/sql/staging/050_pnp_financeiro.sql +72 -0
- package/templates/dataif/sql/views_curated/003_vw_pnp_microdados_admin.sql +160 -0
- package/templates/dataif/sql/views_curated/004_mv_pnp_dashboard_fast.sql +204 -0
- package/templates/dataif/sql/views_curated/010_vw_pnp_admin_ingestao.sql +51 -0
- package/templates/dataif/sql/views_curated/020_vw_pnp_qualidade_dados.sql +114 -0
- package/templates/dataif/sql/views_curated/030_vw_pnp_matriculas.sql +67 -0
- package/templates/dataif/sql/views_curated/040_vw_pnp_eficiencia.sql +33 -0
- package/templates/dataif/sql/views_curated/050_vw_pnp_servidores.sql +30 -0
- package/templates/dataif/sql/views_curated/060_vw_pnp_financeiro.sql +22 -0
- package/templates/dataif/sql/views_curated/070_vw_pnp_vanna.sql +115 -0
package/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# @dataif/cli
|
|
2
|
+
|
|
3
|
+
CLI para instalar e subir o DataIF localmente com Docker Compose.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @dataif/cli install
|
|
7
|
+
npx @dataif/cli deploy
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Comandos:
|
|
11
|
+
|
|
12
|
+
- `dataif install`: prepara uma pasta local com os arquivos da stack.
|
|
13
|
+
- `dataif deploy`: configura credenciais/portas e sobe a stack.
|
|
14
|
+
- `dataif status`: mostra containers e URLs da instalacao.
|
|
15
|
+
|
|
16
|
+
Por padrao, a instalacao fica em `~/.dataif/current`. Use `--dir <path>` para escolher outro destino.
|
package/bin/dataif.js
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import readline from "node:readline/promises";
|
|
8
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
13
|
+
const embeddedTemplateRoot = path.join(packageRoot, "templates", "dataif");
|
|
14
|
+
const defaultInstallDir = path.join(os.homedir(), ".dataif", "current");
|
|
15
|
+
|
|
16
|
+
const requiredProjectFiles = [
|
|
17
|
+
"infra/docker-compose.yml",
|
|
18
|
+
"scripts/deploy.sh",
|
|
19
|
+
"scripts/configure-env.sh"
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const installEntries = [
|
|
23
|
+
"infra",
|
|
24
|
+
"pipelines",
|
|
25
|
+
"scripts",
|
|
26
|
+
"services",
|
|
27
|
+
"sql",
|
|
28
|
+
"README.md"
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const ignoredNames = new Set([
|
|
32
|
+
".env",
|
|
33
|
+
".git",
|
|
34
|
+
".pytest_cache",
|
|
35
|
+
"__pycache__",
|
|
36
|
+
"node_modules",
|
|
37
|
+
"dist",
|
|
38
|
+
"build",
|
|
39
|
+
".DS_Store"
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
main().catch((error) => {
|
|
43
|
+
fail(error.message || String(error));
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
const args = process.argv.slice(2);
|
|
48
|
+
const command = args[0];
|
|
49
|
+
|
|
50
|
+
if (!command || command === "-h" || command === "--help") {
|
|
51
|
+
printMainHelp();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const commandArgs = args.slice(1);
|
|
56
|
+
switch (command) {
|
|
57
|
+
case "install":
|
|
58
|
+
await installCommand(commandArgs);
|
|
59
|
+
break;
|
|
60
|
+
case "deploy":
|
|
61
|
+
await deployCommand(commandArgs);
|
|
62
|
+
break;
|
|
63
|
+
case "status":
|
|
64
|
+
statusCommand(commandArgs);
|
|
65
|
+
break;
|
|
66
|
+
default:
|
|
67
|
+
fail(`Comando desconhecido: ${command}\n\nExecute: dataif --help`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function printMainHelp() {
|
|
72
|
+
console.log(`DataIF CLI
|
|
73
|
+
|
|
74
|
+
Uso:
|
|
75
|
+
dataif install [--dir <path>] [--source <path>] [--force]
|
|
76
|
+
dataif deploy [--mode stg|prod] [--llm] [--dir <path>] [--yes]
|
|
77
|
+
dataif status [--dir <path>]
|
|
78
|
+
|
|
79
|
+
Comandos:
|
|
80
|
+
install Prepara uma instalacao local da stack DataIF.
|
|
81
|
+
deploy Configura credenciais/portas e sobe a stack com Docker Compose.
|
|
82
|
+
status Mostra containers e URLs da instalacao.
|
|
83
|
+
|
|
84
|
+
Padrao:
|
|
85
|
+
Diretorio de instalacao: ${defaultInstallDir}
|
|
86
|
+
|
|
87
|
+
Exemplos:
|
|
88
|
+
dataif install
|
|
89
|
+
dataif deploy --mode prod
|
|
90
|
+
dataif deploy --mode stg --llm
|
|
91
|
+
`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function printInstallHelp() {
|
|
95
|
+
console.log(`Uso: dataif install [--dir <path>] [--source <path>] [--force]
|
|
96
|
+
|
|
97
|
+
Opcoes:
|
|
98
|
+
--dir <path> Pasta de destino. Padrao: ${defaultInstallDir}
|
|
99
|
+
--source <path> Projeto DataIF local para copiar. Padrao: repo atual ou template npm.
|
|
100
|
+
--force Sobrescreve arquivos existentes no destino.
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function printDeployHelp() {
|
|
105
|
+
console.log(`Uso: dataif deploy [--mode stg|prod] [--llm] [--dir <path>] [--yes]
|
|
106
|
+
|
|
107
|
+
Opcoes:
|
|
108
|
+
--mode <mode> stg ou prod. Se omitido, a CLI pergunta.
|
|
109
|
+
--llm Inclui profile llm/Ollama.
|
|
110
|
+
--dir <path> Pasta da instalacao. Padrao: ${defaultInstallDir}
|
|
111
|
+
--yes Nao pergunta confirmacao antes de subir containers.
|
|
112
|
+
--force-env Recria infra/.env a partir do template do modo.
|
|
113
|
+
`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function printStatusHelp() {
|
|
117
|
+
console.log(`Uso: dataif status [--dir <path>]
|
|
118
|
+
|
|
119
|
+
Opcoes:
|
|
120
|
+
--dir <path> Pasta da instalacao. Padrao: ${defaultInstallDir}
|
|
121
|
+
`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function installCommand(args) {
|
|
125
|
+
const options = parseOptions(args);
|
|
126
|
+
if (options.help) {
|
|
127
|
+
printInstallHelp();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const targetDir = expandHome(options.dir || defaultInstallDir);
|
|
132
|
+
const sourceDir = resolveInstallSource(options.source);
|
|
133
|
+
|
|
134
|
+
ensureProjectRoot(sourceDir, "origem");
|
|
135
|
+
await prepareInstallDir(sourceDir, targetDir, Boolean(options.force));
|
|
136
|
+
|
|
137
|
+
console.log("\nInstalacao DataIF pronta.");
|
|
138
|
+
console.log(`Pasta: ${targetDir}`);
|
|
139
|
+
console.log(`Proximo passo: dataif deploy --dir ${quotePath(targetDir)}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function deployCommand(args) {
|
|
143
|
+
const options = parseOptions(args, { booleans: ["llm", "yes", "force-env"] });
|
|
144
|
+
if (options.help) {
|
|
145
|
+
printDeployHelp();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let mode = options.mode;
|
|
150
|
+
if (!mode) {
|
|
151
|
+
mode = await chooseMode();
|
|
152
|
+
}
|
|
153
|
+
if (!["stg", "prod"].includes(mode)) {
|
|
154
|
+
fail(`Modo invalido: ${mode}. Use stg ou prod.`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await validateDocker();
|
|
158
|
+
validateHostCapacity();
|
|
159
|
+
|
|
160
|
+
const projectDir = await resolveProjectDir(options.dir);
|
|
161
|
+
const env = {
|
|
162
|
+
...process.env,
|
|
163
|
+
DATAIF_DEPLOY_CONFIG_ONLY: "true"
|
|
164
|
+
};
|
|
165
|
+
if (options["force-env"]) {
|
|
166
|
+
env.DATAIF_FORCE_ENV = "true";
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log(`\nConfigurando DataIF (${mode}) em ${projectDir}`);
|
|
170
|
+
run(path.join(projectDir, "scripts", "deploy.sh"), buildDeployArgs(mode, options.llm), {
|
|
171
|
+
cwd: projectDir,
|
|
172
|
+
env,
|
|
173
|
+
stdio: "inherit"
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const envPath = path.join(projectDir, "infra", ".env");
|
|
177
|
+
const envValues = readEnvFile(envPath);
|
|
178
|
+
await validatePorts(envValues, Boolean(options.yes));
|
|
179
|
+
|
|
180
|
+
printDeploySummary(projectDir, mode, Boolean(options.llm), envValues);
|
|
181
|
+
if (!options.yes) {
|
|
182
|
+
const confirmed = await confirm("Subir containers agora?", true);
|
|
183
|
+
if (!confirmed) {
|
|
184
|
+
console.log("Deploy cancelado antes de subir containers.");
|
|
185
|
+
console.log(`Configuracao mantida em: ${envPath}`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const composeArgs = [
|
|
191
|
+
"compose",
|
|
192
|
+
"--env-file",
|
|
193
|
+
envPath,
|
|
194
|
+
"-f",
|
|
195
|
+
path.join(projectDir, "infra", "docker-compose.yml")
|
|
196
|
+
];
|
|
197
|
+
if (options.llm) {
|
|
198
|
+
composeArgs.push("--profile", "llm");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log("\nValidando Docker Compose...");
|
|
202
|
+
run("docker", [...composeArgs, "config"], { cwd: path.join(projectDir, "infra"), stdio: "inherit" });
|
|
203
|
+
|
|
204
|
+
console.log("\nSubindo stack DataIF...");
|
|
205
|
+
run("docker", [...composeArgs, "up", "-d", "--build"], {
|
|
206
|
+
cwd: path.join(projectDir, "infra"),
|
|
207
|
+
stdio: "inherit"
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
console.log("\nDataIF ativo.");
|
|
211
|
+
console.log(`Web: http://localhost:${envValues.WEB_PORT || "5173"}`);
|
|
212
|
+
console.log(`Status: dataif status --dir ${quotePath(projectDir)}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function statusCommand(args) {
|
|
216
|
+
const options = parseOptions(args);
|
|
217
|
+
if (options.help) {
|
|
218
|
+
printStatusHelp();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const projectDir = resolveExistingProjectDir(options.dir);
|
|
223
|
+
const envPath = path.join(projectDir, "infra", ".env");
|
|
224
|
+
const composePath = path.join(projectDir, "infra", "docker-compose.yml");
|
|
225
|
+
|
|
226
|
+
if (!fs.existsSync(envPath)) {
|
|
227
|
+
fail(`Nao encontrei ${envPath}.\nExecute: dataif deploy --dir ${quotePath(projectDir)}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const envValues = readEnvFile(envPath);
|
|
231
|
+
console.log(`DataIF: ${projectDir}`);
|
|
232
|
+
console.log(`Env: ${envPath}`);
|
|
233
|
+
console.log(`Web: http://localhost:${envValues.WEB_PORT || "5173"}`);
|
|
234
|
+
console.log(`API: http://localhost:${envValues.API_PORT || "8000"}`);
|
|
235
|
+
console.log(`Airflow: http://localhost:${envValues.WEB_PORT || "5173"}/airflow/`);
|
|
236
|
+
console.log(`Metabase: http://localhost:${envValues.WEB_PORT || "5173"}/metabase/`);
|
|
237
|
+
console.log("");
|
|
238
|
+
|
|
239
|
+
const docker = spawnSync("docker", [
|
|
240
|
+
"compose",
|
|
241
|
+
"--env-file",
|
|
242
|
+
envPath,
|
|
243
|
+
"-f",
|
|
244
|
+
composePath,
|
|
245
|
+
"ps"
|
|
246
|
+
], {
|
|
247
|
+
cwd: path.join(projectDir, "infra"),
|
|
248
|
+
encoding: "utf8"
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
if (docker.error || docker.status !== 0) {
|
|
252
|
+
const detail = docker.stderr || docker.error?.message || "";
|
|
253
|
+
if (detail.includes("permission denied") && detail.includes("docker")) {
|
|
254
|
+
fail([
|
|
255
|
+
"Nao consegui consultar Docker Compose por falta de permissao no Docker.",
|
|
256
|
+
"Abra o Docker Desktop/daemon ou rode com um usuario que tenha acesso ao socket Docker.",
|
|
257
|
+
detail.trim()
|
|
258
|
+
].join("\n"));
|
|
259
|
+
}
|
|
260
|
+
fail(`Nao consegui consultar Docker Compose.\n${detail}`.trim());
|
|
261
|
+
}
|
|
262
|
+
process.stdout.write(docker.stdout);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function parseOptions(args, config = {}) {
|
|
266
|
+
const booleans = new Set(["help", "force", ...(config.booleans || [])]);
|
|
267
|
+
const options = {};
|
|
268
|
+
|
|
269
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
270
|
+
const arg = args[index];
|
|
271
|
+
if (arg === "-h" || arg === "--help") {
|
|
272
|
+
options.help = true;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (!arg.startsWith("--")) {
|
|
276
|
+
fail(`Argumento inesperado: ${arg}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const key = arg.slice(2);
|
|
280
|
+
if (booleans.has(key)) {
|
|
281
|
+
options[key] = true;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const value = args[index + 1];
|
|
286
|
+
if (!value || value.startsWith("--")) {
|
|
287
|
+
fail(`Opcao ${arg} precisa de valor.`);
|
|
288
|
+
}
|
|
289
|
+
options[key] = value;
|
|
290
|
+
index += 1;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return options;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function chooseMode() {
|
|
297
|
+
const rl = readline.createInterface({ input, output });
|
|
298
|
+
try {
|
|
299
|
+
while (true) {
|
|
300
|
+
const answer = (await rl.question("Modo de deploy (stg/prod) [prod]: ")).trim() || "prod";
|
|
301
|
+
if (["stg", "prod"].includes(answer)) {
|
|
302
|
+
return answer;
|
|
303
|
+
}
|
|
304
|
+
console.log("Use stg ou prod.");
|
|
305
|
+
}
|
|
306
|
+
} finally {
|
|
307
|
+
rl.close();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function confirm(question, defaultValue) {
|
|
312
|
+
const suffix = defaultValue ? "[S/n]" : "[s/N]";
|
|
313
|
+
const rl = readline.createInterface({ input, output });
|
|
314
|
+
try {
|
|
315
|
+
const answer = (await rl.question(`${question} ${suffix}: `)).trim().toLowerCase();
|
|
316
|
+
if (!answer) {
|
|
317
|
+
return defaultValue;
|
|
318
|
+
}
|
|
319
|
+
return ["s", "sim", "y", "yes"].includes(answer);
|
|
320
|
+
} finally {
|
|
321
|
+
rl.close();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function prepareInstallDir(sourceDir, targetDir, force) {
|
|
326
|
+
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0 && !force) {
|
|
327
|
+
const overwrite = await confirm(`A pasta ${targetDir} ja existe. Atualizar arquivos?`, false);
|
|
328
|
+
if (!overwrite) {
|
|
329
|
+
console.log("Instalacao mantida sem alteracoes.");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
335
|
+
for (const entry of installEntries) {
|
|
336
|
+
const src = path.join(sourceDir, entry);
|
|
337
|
+
if (!fs.existsSync(src)) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
copyRecursive(src, path.join(targetDir, entry));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
for (const script of ["deploy.sh", "configure-env.sh"]) {
|
|
344
|
+
const scriptPath = path.join(targetDir, "scripts", script);
|
|
345
|
+
if (fs.existsSync(scriptPath)) {
|
|
346
|
+
fs.chmodSync(scriptPath, 0o755);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function resolveProjectDir(dirOption) {
|
|
352
|
+
if (dirOption) {
|
|
353
|
+
const dir = expandHome(dirOption);
|
|
354
|
+
if (!isProjectRoot(dir)) {
|
|
355
|
+
fail(`A pasta informada nao parece uma instalacao DataIF: ${dir}`);
|
|
356
|
+
}
|
|
357
|
+
return dir;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const cwdProject = findProjectRoot(process.cwd());
|
|
361
|
+
if (cwdProject) {
|
|
362
|
+
return cwdProject;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (isProjectRoot(defaultInstallDir)) {
|
|
366
|
+
return defaultInstallDir;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const source = resolveInstallSource();
|
|
370
|
+
console.log(`Instalacao padrao nao encontrada. Preparando ${defaultInstallDir}...`);
|
|
371
|
+
await prepareInstallDir(source, defaultInstallDir, false);
|
|
372
|
+
return defaultInstallDir;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function resolveExistingProjectDir(dirOption) {
|
|
376
|
+
const explicitDir = dirOption ? expandHome(dirOption) : null;
|
|
377
|
+
const dir = explicitDir || findProjectRoot(process.cwd()) || defaultInstallDir;
|
|
378
|
+
if (!isProjectRoot(dir)) {
|
|
379
|
+
fail(`Nao encontrei uma instalacao DataIF em ${dir}.\nExecute: dataif install --dir ${quotePath(dir)}`);
|
|
380
|
+
}
|
|
381
|
+
return dir;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function resolveInstallSource(sourceOption) {
|
|
385
|
+
if (sourceOption) {
|
|
386
|
+
const source = expandHome(sourceOption);
|
|
387
|
+
ensureProjectRoot(source, "origem");
|
|
388
|
+
return source;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const cwdProject = findProjectRoot(process.cwd());
|
|
392
|
+
if (cwdProject) {
|
|
393
|
+
return cwdProject;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (isProjectRoot(embeddedTemplateRoot)) {
|
|
397
|
+
return embeddedTemplateRoot;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
fail([
|
|
401
|
+
"Nao encontrei arquivos do DataIF para instalar.",
|
|
402
|
+
"Execute dentro do repo DataIF ou publique o pacote npm com o template gerado por npm pack."
|
|
403
|
+
].join("\n"));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function findProjectRoot(startDir) {
|
|
407
|
+
let current = path.resolve(startDir);
|
|
408
|
+
while (true) {
|
|
409
|
+
if (isProjectRoot(current)) {
|
|
410
|
+
return current;
|
|
411
|
+
}
|
|
412
|
+
const parent = path.dirname(current);
|
|
413
|
+
if (parent === current) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
current = parent;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function isProjectRoot(dir) {
|
|
421
|
+
return requiredProjectFiles.every((file) => fs.existsSync(path.join(dir, file)));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function ensureProjectRoot(dir, label) {
|
|
425
|
+
if (!isProjectRoot(dir)) {
|
|
426
|
+
fail(`A ${label} nao contem uma stack DataIF valida: ${dir}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function copyRecursive(src, dest) {
|
|
431
|
+
if (!shouldCopy(src)) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const stat = fs.statSync(src);
|
|
436
|
+
if (stat.isDirectory()) {
|
|
437
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
438
|
+
for (const entry of fs.readdirSync(src)) {
|
|
439
|
+
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
445
|
+
fs.copyFileSync(src, dest);
|
|
446
|
+
fs.chmodSync(dest, stat.mode);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function shouldCopy(src) {
|
|
450
|
+
const name = path.basename(src);
|
|
451
|
+
if (ignoredNames.has(name)) {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
if (name.endsWith(".pyc")) {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async function validateDocker() {
|
|
461
|
+
const docker = spawnSync("docker", ["--version"], { encoding: "utf8" });
|
|
462
|
+
if (docker.error || docker.status !== 0) {
|
|
463
|
+
fail("Docker nao encontrado. Instale/inicie Docker Engine antes de rodar o deploy.");
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const compose = spawnSync("docker", ["compose", "version"], { encoding: "utf8" });
|
|
467
|
+
if (compose.error || compose.status !== 0) {
|
|
468
|
+
fail("Docker Compose v2 nao encontrado. Verifique se `docker compose version` funciona.");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const daemon = spawnSync("docker", ["info"], { encoding: "utf8" });
|
|
472
|
+
if (daemon.error || daemon.status !== 0) {
|
|
473
|
+
fail("Docker esta instalado, mas o daemon nao respondeu. Inicie o Docker e tente novamente.");
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function validateHostCapacity() {
|
|
478
|
+
const totalMemGb = os.totalmem() / 1024 / 1024 / 1024;
|
|
479
|
+
if (totalMemGb < 6) {
|
|
480
|
+
console.warn(`Aviso: memoria total detectada abaixo de 6 GB (${totalMemGb.toFixed(1)} GB).`);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const available = spawnSync("df", ["-Pk", os.homedir()], { encoding: "utf8" });
|
|
484
|
+
if (available.status === 0) {
|
|
485
|
+
const lines = available.stdout.trim().split("\n");
|
|
486
|
+
const columns = lines.at(-1)?.trim().split(/\s+/) || [];
|
|
487
|
+
const availableKb = Number(columns[3] || 0);
|
|
488
|
+
const availableGb = availableKb / 1024 / 1024;
|
|
489
|
+
if (availableGb > 0 && availableGb < 10) {
|
|
490
|
+
console.warn(`Aviso: espaco livre abaixo de 10 GB em ${os.homedir()} (${availableGb.toFixed(1)} GB).`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async function validatePorts(envValues, assumeYes) {
|
|
496
|
+
const ports = [
|
|
497
|
+
["Web", envValues.WEB_PORT],
|
|
498
|
+
["API", envValues.API_PORT],
|
|
499
|
+
["Postgres", envValues.POSTGRES_EXPOSE_PORT],
|
|
500
|
+
["Metabase", envValues.METABASE_PORT],
|
|
501
|
+
["Airflow", envValues.AIRFLOW_PORT],
|
|
502
|
+
["Keycloak", envValues.KEYCLOAK_PORT],
|
|
503
|
+
["Vanna", envValues.VANNA_PORT]
|
|
504
|
+
].filter(([, port]) => port);
|
|
505
|
+
|
|
506
|
+
const busy = [];
|
|
507
|
+
for (const [label, port] of ports) {
|
|
508
|
+
if (!(await isPortAvailable(Number(port)))) {
|
|
509
|
+
busy.push(`${label}:${port}`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (busy.length === 0) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const message = `Portas em uso: ${busy.join(", ")}`;
|
|
518
|
+
if (assumeYes) {
|
|
519
|
+
fail(`${message}\nPare o processo atual ou altere as portas no deploy prod.`);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
console.warn(`Aviso: ${message}`);
|
|
523
|
+
const proceed = await confirm("Continuar mesmo assim?", false);
|
|
524
|
+
if (!proceed) {
|
|
525
|
+
fail("Deploy interrompido por conflito de portas.");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function isPortAvailable(port) {
|
|
530
|
+
return new Promise((resolve) => {
|
|
531
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
532
|
+
resolve(true);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const server = net.createServer();
|
|
536
|
+
server.once("error", () => resolve(false));
|
|
537
|
+
server.once("listening", () => {
|
|
538
|
+
server.close(() => resolve(true));
|
|
539
|
+
});
|
|
540
|
+
server.listen(port, "0.0.0.0");
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function buildDeployArgs(mode, includeLlm) {
|
|
545
|
+
const args = [mode];
|
|
546
|
+
if (includeLlm) {
|
|
547
|
+
args.push("--llm");
|
|
548
|
+
}
|
|
549
|
+
return args;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function readEnvFile(envPath) {
|
|
553
|
+
if (!fs.existsSync(envPath)) {
|
|
554
|
+
fail(`Arquivo .env nao encontrado: ${envPath}`);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const values = {};
|
|
558
|
+
const content = fs.readFileSync(envPath, "utf8");
|
|
559
|
+
for (const line of content.split(/\r?\n/)) {
|
|
560
|
+
const trimmed = line.trim();
|
|
561
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
const index = trimmed.indexOf("=");
|
|
565
|
+
if (index === -1) {
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
values[trimmed.slice(0, index)] = trimmed.slice(index + 1);
|
|
569
|
+
}
|
|
570
|
+
return values;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function printDeploySummary(projectDir, mode, includeLlm, envValues) {
|
|
574
|
+
console.log("\nResumo do deploy");
|
|
575
|
+
console.log(`- Pasta: ${projectDir}`);
|
|
576
|
+
console.log(`- Modo: ${mode}`);
|
|
577
|
+
console.log(`- Projeto Compose: ${envValues.COMPOSE_PROJECT_NAME || "dataif"}`);
|
|
578
|
+
console.log(`- Web: http://localhost:${envValues.WEB_PORT || "5173"}`);
|
|
579
|
+
console.log(`- API: http://localhost:${envValues.API_PORT || "8000"}`);
|
|
580
|
+
console.log(`- Metabase: http://localhost:${envValues.WEB_PORT || "5173"}/metabase/`);
|
|
581
|
+
console.log(`- Airflow: http://localhost:${envValues.WEB_PORT || "5173"}/airflow/`);
|
|
582
|
+
console.log(`- LLM local: ${includeLlm ? "sim" : "nao"}`);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function run(command, args, options = {}) {
|
|
586
|
+
const result = spawnSync(command, args, {
|
|
587
|
+
stdio: options.stdio || "pipe",
|
|
588
|
+
cwd: options.cwd,
|
|
589
|
+
env: options.env,
|
|
590
|
+
encoding: options.stdio === "inherit" ? undefined : "utf8"
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
if (result.error) {
|
|
594
|
+
fail(`Falha ao executar ${command}: ${result.error.message}`);
|
|
595
|
+
}
|
|
596
|
+
if (result.status !== 0) {
|
|
597
|
+
const stderr = result.stderr ? `\n${result.stderr}` : "";
|
|
598
|
+
fail(`Comando falhou (${result.status}): ${command} ${args.join(" ")}${stderr}`);
|
|
599
|
+
}
|
|
600
|
+
return result;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function expandHome(value) {
|
|
604
|
+
if (!value) {
|
|
605
|
+
return value;
|
|
606
|
+
}
|
|
607
|
+
if (value === "~") {
|
|
608
|
+
return os.homedir();
|
|
609
|
+
}
|
|
610
|
+
if (value.startsWith("~/")) {
|
|
611
|
+
return path.join(os.homedir(), value.slice(2));
|
|
612
|
+
}
|
|
613
|
+
return path.resolve(value);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function quotePath(value) {
|
|
617
|
+
return value.includes(" ") ? `"${value}"` : value;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function fail(message) {
|
|
621
|
+
console.error(`Erro: ${message}`);
|
|
622
|
+
process.exit(1);
|
|
623
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dataif/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI para instalar e subir o DataIF localmente com Docker Compose.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dataif": "bin/dataif.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"scripts",
|
|
12
|
+
"templates",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"prepack": "node scripts/build-template.mjs",
|
|
17
|
+
"smoke": "node bin/dataif.js --help && node bin/dataif.js install --help && node bin/dataif.js deploy --help && node bin/dataif.js status --help"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"license": "UNLICENSED",
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
}
|
|
26
|
+
}
|