vkdeploy-cli 0.0.8

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.
Files changed (165) hide show
  1. package/.env.example +5 -0
  2. package/.eslintrc.json +13 -0
  3. package/.prettierignore +5 -0
  4. package/.prettierrc.json +5 -0
  5. package/.vscode/settings.json +3 -0
  6. package/.yarn/cache/@isaacs-cliui-npm-8.0.2-f4364666d5-4a473b9b32.zip +0 -0
  7. package/.yarn/cache/@isaacs-fs-minipass-npm-4.0.1-677026e841-5d36d28996.zip +0 -0
  8. package/.yarn/cache/@npmcli-agent-npm-3.0.0-169e79294f-e8fc25d536.zip +0 -0
  9. package/.yarn/cache/@npmcli-fs-npm-4.0.0-1d9cc8a27b-68951c589e.zip +0 -0
  10. package/.yarn/cache/@pkgjs-parseargs-npm-0.11.0-cd2a3fe948-6ad6a00fc4.zip +0 -0
  11. package/.yarn/cache/@realm-fetch-npm-0.1.1-76609d4244-4922bd005a.zip +0 -0
  12. package/.yarn/cache/abbrev-npm-3.0.0-946682a7b1-2500075b5e.zip +0 -0
  13. package/.yarn/cache/agent-base-npm-7.1.3-b2c16e72fb-87bb7ee54f.zip +0 -0
  14. package/.yarn/cache/ansi-regex-npm-5.0.1-c963a48615-2aa4bb54ca.zip +0 -0
  15. package/.yarn/cache/ansi-regex-npm-6.1.0-abe011aae4-495834a53b.zip +0 -0
  16. package/.yarn/cache/ansi-styles-npm-4.3.0-245c7d42c7-513b44c3b2.zip +0 -0
  17. package/.yarn/cache/ansi-styles-npm-6.2.1-d43647018c-ef940f2f0c.zip +0 -0
  18. package/.yarn/cache/asynckit-npm-0.4.0-c718858525-7b78c451df.zip +0 -0
  19. package/.yarn/cache/axios-npm-1.7.9-3c98466f87-cb8ce29181.zip +0 -0
  20. package/.yarn/cache/balanced-match-npm-1.0.2-a53c126459-9706c088a2.zip +0 -0
  21. package/.yarn/cache/base-64-npm-0.1.0-41e6da6777-5a42938f82.zip +0 -0
  22. package/.yarn/cache/base64-js-npm-1.5.1-b2f7275641-669632eb37.zip +0 -0
  23. package/.yarn/cache/bl-npm-4.1.0-7f94cdcf3f-9e8521fa7e.zip +0 -0
  24. package/.yarn/cache/brace-expansion-npm-2.0.1-17aa2616f9-a61e7cd2e8.zip +0 -0
  25. package/.yarn/cache/bson-npm-4.7.2-77a08a4d01-f357d12c56.zip +0 -0
  26. package/.yarn/cache/buffer-npm-5.7.1-513ef8259e-e2cf8429e1.zip +0 -0
  27. package/.yarn/cache/cacache-npm-19.0.1-395cba1936-e95684717d.zip +0 -0
  28. package/.yarn/cache/chalk-npm-5.3.0-d181999efb-623922e077.zip +0 -0
  29. package/.yarn/cache/chalk-npm-5.4.1-2f3fe4660a-0c656f30b7.zip +0 -0
  30. package/.yarn/cache/child_process-npm-1.0.2-04e116a38e-bd814d82bc.zip +0 -0
  31. package/.yarn/cache/chownr-npm-1.1.4-5bd400ab08-115648f8eb.zip +0 -0
  32. package/.yarn/cache/chownr-npm-3.0.0-5275e85d25-fd73a4bab4.zip +0 -0
  33. package/.yarn/cache/color-convert-npm-2.0.1-79730e935b-79e6bdb9fd.zip +0 -0
  34. package/.yarn/cache/color-name-npm-1.1.4-025792b0ea-b044585952.zip +0 -0
  35. package/.yarn/cache/combined-stream-npm-1.0.8-dc14d4a63a-49fa4aeb49.zip +0 -0
  36. package/.yarn/cache/cross-spawn-npm-7.0.6-264bddf921-8d306efaca.zip +0 -0
  37. package/.yarn/cache/debug-npm-4.4.0-f6efe76023-fb42df878d.zip +0 -0
  38. package/.yarn/cache/decompress-response-npm-6.0.0-359de2878c-d377cf47e0.zip +0 -0
  39. package/.yarn/cache/deep-extend-npm-0.6.0-e182924219-7be7e5a8d4.zip +0 -0
  40. package/.yarn/cache/delayed-stream-npm-1.0.0-c5a4c4cc02-46fe6e83e2.zip +0 -0
  41. package/.yarn/cache/detect-libc-npm-2.0.3-2ddae34945-2ba6a939ae.zip +0 -0
  42. package/.yarn/cache/eastasianwidth-npm-0.2.0-c37eb16bd1-7d00d7cd8e.zip +0 -0
  43. package/.yarn/cache/emoji-regex-npm-8.0.0-213764015c-d4c5c39d5a.zip +0 -0
  44. package/.yarn/cache/emoji-regex-npm-9.2.2-e6fac8d058-8487182da7.zip +0 -0
  45. package/.yarn/cache/encoding-npm-0.1.13-82a1837d30-bb98632f8f.zip +0 -0
  46. package/.yarn/cache/end-of-stream-npm-1.4.4-497fc6dee1-530a5a5a1e.zip +0 -0
  47. package/.yarn/cache/env-paths-npm-2.2.1-7c7577428c-65b5df55a8.zip +0 -0
  48. package/.yarn/cache/err-code-npm-2.0.3-082e0ff9a7-8b7b1be20d.zip +0 -0
  49. package/.yarn/cache/events-npm-3.3.0-c280bc7e48-f6f487ad21.zip +0 -0
  50. package/.yarn/cache/expand-template-npm-2.0.3-80de959306-588c198472.zip +0 -0
  51. package/.yarn/cache/exponential-backoff-npm-3.1.1-04df458b30-3d21519a4f.zip +0 -0
  52. package/.yarn/cache/follow-redirects-npm-1.15.9-539785d34c-859e2bacc7.zip +0 -0
  53. package/.yarn/cache/foreground-child-npm-3.3.0-b8be745271-1989698488.zip +0 -0
  54. package/.yarn/cache/form-data-npm-4.0.1-f1a27a1c2e-ccee458cd5.zip +0 -0
  55. package/.yarn/cache/fs-constants-npm-1.0.0-59576b2177-18f5b71837.zip +0 -0
  56. package/.yarn/cache/fs-minipass-npm-3.0.3-d148d6ac19-8722a41109.zip +0 -0
  57. package/.yarn/cache/github-from-package-npm-0.0.0-519f80c9a1-14e448192a.zip +0 -0
  58. package/.yarn/cache/glob-npm-10.4.5-8c63175f05-0bc725de5e.zip +0 -0
  59. package/.yarn/cache/graceful-fs-npm-4.2.11-24bb648a68-ac85f94da9.zip +0 -0
  60. package/.yarn/cache/http-cache-semantics-npm-4.1.1-1120131375-83ac0bc60b.zip +0 -0
  61. package/.yarn/cache/http-proxy-agent-npm-7.0.2-643ed7cc33-670858c8f8.zip +0 -0
  62. package/.yarn/cache/https-proxy-agent-npm-7.0.6-27a95c2690-b882377a12.zip +0 -0
  63. package/.yarn/cache/iconv-lite-npm-0.6.3-24b8aae27e-3f60d47a5c.zip +0 -0
  64. package/.yarn/cache/ieee754-npm-1.2.1-fb63b3caeb-5144c0c981.zip +0 -0
  65. package/.yarn/cache/imurmurhash-npm-0.1.4-610c5068a0-7cae75c8cd.zip +0 -0
  66. package/.yarn/cache/inherits-npm-2.0.3-401e64b080-78cb8d7d85.zip +0 -0
  67. package/.yarn/cache/inherits-npm-2.0.4-c66b3957a0-4a48a73384.zip +0 -0
  68. package/.yarn/cache/ini-npm-1.3.8-fb5040b4c0-dfd98b0ca3.zip +0 -0
  69. package/.yarn/cache/ip-address-npm-9.0.5-9fa024d42a-aa15f12cfd.zip +0 -0
  70. package/.yarn/cache/is-fullwidth-code-point-npm-3.0.0-1ecf4ebee5-44a30c2945.zip +0 -0
  71. package/.yarn/cache/isexe-npm-2.0.0-b58870bd2e-26bf6c5480.zip +0 -0
  72. package/.yarn/cache/isexe-npm-3.1.1-9c0061eead-7fe1931ee4.zip +0 -0
  73. package/.yarn/cache/jackspeak-npm-3.4.3-546bfad080-be31027fc7.zip +0 -0
  74. package/.yarn/cache/jsbn-npm-1.1.0-1da0181838-944f924f2b.zip +0 -0
  75. package/.yarn/cache/lru-cache-npm-10.4.3-30c10b861a-6476138d21.zip +0 -0
  76. package/.yarn/cache/make-fetch-happen-npm-14.0.3-23b30e8691-6fb2fee6da.zip +0 -0
  77. package/.yarn/cache/mime-db-npm-1.52.0-b5371d6fd2-0d99a03585.zip +0 -0
  78. package/.yarn/cache/mime-types-npm-2.1.35-dd9ea9f3e2-89a5b7f1de.zip +0 -0
  79. package/.yarn/cache/mimic-response-npm-3.1.0-a4a24b4e96-25739fee32.zip +0 -0
  80. package/.yarn/cache/minimatch-npm-9.0.5-9aa93d97fa-2c035575ed.zip +0 -0
  81. package/.yarn/cache/minimist-npm-1.2.8-d7af7b1dce-75a6d645fb.zip +0 -0
  82. package/.yarn/cache/minipass-collect-npm-2.0.1-73d3907e40-b251bceea6.zip +0 -0
  83. package/.yarn/cache/minipass-fetch-npm-4.0.0-d4e49e0194-7d59a31011.zip +0 -0
  84. package/.yarn/cache/minipass-flush-npm-1.0.5-efe79d9826-56269a0b22.zip +0 -0
  85. package/.yarn/cache/minipass-npm-3.3.6-b8d93a945b-a30d083c80.zip +0 -0
  86. package/.yarn/cache/minipass-npm-7.1.2-3a5327d36d-2bfd325b95.zip +0 -0
  87. package/.yarn/cache/minipass-pipeline-npm-1.2.4-5924cb077f-b14240dac0.zip +0 -0
  88. package/.yarn/cache/minipass-sized-npm-1.0.3-306d86f432-79076749fc.zip +0 -0
  89. package/.yarn/cache/minizlib-npm-3.0.1-4bdabd978f-da0a538992.zip +0 -0
  90. package/.yarn/cache/mkdirp-classic-npm-0.5.3-3b5c991910-3f4e088208.zip +0 -0
  91. package/.yarn/cache/mkdirp-npm-3.0.1-f94bfa769e-972deb188e.zip +0 -0
  92. package/.yarn/cache/ms-npm-2.1.3-81ff3cfac1-aa92de6080.zip +0 -0
  93. package/.yarn/cache/napi-build-utils-npm-2.0.0-95da9c2e4e-532121efd2.zip +0 -0
  94. package/.yarn/cache/negotiator-npm-1.0.0-47d727e27e-20ebfe79b2.zip +0 -0
  95. package/.yarn/cache/node-abi-npm-3.73.0-299b8f8c39-d89bfe7b53.zip +0 -0
  96. package/.yarn/cache/node-gyp-npm-11.0.0-669e34db1b-d7d5055ccc.zip +0 -0
  97. package/.yarn/cache/node-machine-id-npm-1.1.12-ad6d29fa15-e23088a0fb.zip +0 -0
  98. package/.yarn/cache/nopt-npm-8.1.0-5570ef63cd-49cfd3eb6f.zip +0 -0
  99. package/.yarn/cache/once-npm-1.4.0-ccf03ef07a-cd0a885013.zip +0 -0
  100. package/.yarn/cache/p-map-npm-7.0.3-93bbec0d8c-8c92d533ac.zip +0 -0
  101. package/.yarn/cache/package-json-from-dist-npm-1.0.1-4631a88465-58ee9538f2.zip +0 -0
  102. package/.yarn/cache/path-browserify-npm-1.0.1-f975d99a99-c6d7fa3764.zip +0 -0
  103. package/.yarn/cache/path-key-npm-3.1.1-0e66ea8321-55cd7a9dd4.zip +0 -0
  104. package/.yarn/cache/path-npm-0.12.7-bddabe2e86-5dedb71e78.zip +0 -0
  105. package/.yarn/cache/path-scurry-npm-1.11.1-aaf8c339af-890d5abcd5.zip +0 -0
  106. package/.yarn/cache/prebuild-install-npm-7.1.3-8e79c3a0a2-300740ca41.zip +0 -0
  107. package/.yarn/cache/proc-log-npm-5.0.0-405173f9b4-c78b26ecef.zip +0 -0
  108. package/.yarn/cache/process-npm-0.11.10-aeb3b641ae-bfcce49814.zip +0 -0
  109. package/.yarn/cache/promise-retry-npm-2.0.1-871f0b01b7-f96a3f6d90.zip +0 -0
  110. package/.yarn/cache/proxy-from-env-npm-1.1.0-c13d07f26b-ed7fcc2ba0.zip +0 -0
  111. package/.yarn/cache/pump-npm-3.0.2-a8afc6734f-e0c4216874.zip +0 -0
  112. package/.yarn/cache/rc-npm-1.2.8-d6768ac936-2e26e052f8.zip +0 -0
  113. package/.yarn/cache/react-native-device-info-npm-14.0.2-04cf22684d-c1825a3151.zip +0 -0
  114. package/.yarn/cache/react-native-fs-npm-2.20.0-a38fe24051-0be9bb9a5c.zip +0 -0
  115. package/.yarn/cache/readable-stream-npm-3.6.2-d2a6069158-bdcbe6c22e.zip +0 -0
  116. package/.yarn/cache/retry-npm-0.12.0-72ac7fb4cc-623bd7d2e5.zip +0 -0
  117. package/.yarn/cache/rimraf-npm-5.0.10-d0c6647697-50e27388dd.zip +0 -0
  118. package/.yarn/cache/safe-buffer-npm-5.2.1-3481c8aa9b-b99c4b41fd.zip +0 -0
  119. package/.yarn/cache/safer-buffer-npm-2.1.2-8d5c0b705e-cab8f25ae6.zip +0 -0
  120. package/.yarn/cache/sax-npm-1.4.1-503b1923cb-3ad64df16b.zip +0 -0
  121. package/.yarn/cache/semver-npm-7.6.3-57e82c14d5-4110ec5d01.zip +0 -0
  122. package/.yarn/cache/shebang-command-npm-2.0.0-eb2b01921d-6b52fe8727.zip +0 -0
  123. package/.yarn/cache/shebang-regex-npm-3.0.0-899a0cd65e-1a2bcae50d.zip +0 -0
  124. package/.yarn/cache/signal-exit-npm-4.1.0-61fb957687-64c757b498.zip +0 -0
  125. package/.yarn/cache/simple-concat-npm-1.0.1-48df70de29-4d211042cc.zip +0 -0
  126. package/.yarn/cache/simple-get-npm-4.0.1-fa2a97645d-e4132fd27c.zip +0 -0
  127. package/.yarn/cache/smart-buffer-npm-4.2.0-5ac3f668bb-b5167a7142.zip +0 -0
  128. package/.yarn/cache/socks-npm-2.8.3-3532b59899-7a6b7f6eed.zip +0 -0
  129. package/.yarn/cache/socks-proxy-agent-npm-8.0.5-24d77a90dc-b4fbcdb7ad.zip +0 -0
  130. package/.yarn/cache/sprintf-js-npm-1.1.3-b99efd75b2-a3fdac7b49.zip +0 -0
  131. package/.yarn/cache/ssri-npm-12.0.0-97c0e53d2e-ef4b6b0ae4.zip +0 -0
  132. package/.yarn/cache/string-width-npm-4.2.3-2c27177bae-e52c10dc3f.zip +0 -0
  133. package/.yarn/cache/string-width-npm-5.1.2-bf60531341-7369deaa29.zip +0 -0
  134. package/.yarn/cache/string_decoder-npm-1.3.0-2422117fd0-8417646695.zip +0 -0
  135. package/.yarn/cache/strip-ansi-npm-6.0.1-caddc7cb40-f3cd25890a.zip +0 -0
  136. package/.yarn/cache/strip-ansi-npm-7.1.0-7453b80b79-859c73fcf2.zip +0 -0
  137. package/.yarn/cache/strip-json-comments-npm-2.0.1-e7883b2d04-1074ccb632.zip +0 -0
  138. package/.yarn/cache/tar-fs-npm-2.1.2-4231292dd1-6b4fcd38a6.zip +0 -0
  139. package/.yarn/cache/tar-npm-7.4.3-1dbbd1ffc3-8485350c06.zip +0 -0
  140. package/.yarn/cache/tar-stream-npm-2.2.0-884c79b510-699831a8b9.zip +0 -0
  141. package/.yarn/cache/timers-npm-0.1.1-caf37d810c-3f75b95c19.zip +0 -0
  142. package/.yarn/cache/tunnel-agent-npm-0.6.0-64345ab7eb-05f6510358.zip +0 -0
  143. package/.yarn/cache/unique-filename-npm-4.0.0-bfc100c4e3-6a62094fca.zip +0 -0
  144. package/.yarn/cache/unique-slug-npm-5.0.0-11508c0469-222d0322bc.zip +0 -0
  145. package/.yarn/cache/utf8-npm-3.0.0-7c39b5994a-cb89a69ad9.zip +0 -0
  146. package/.yarn/cache/util-deprecate-npm-1.0.2-e3fe1a219c-474acf1146.zip +0 -0
  147. package/.yarn/cache/util-npm-0.10.4-7c577db41a-913f9a90d0.zip +0 -0
  148. package/.yarn/cache/which-npm-2.0.2-320ddf72f7-1a5c563d3c.zip +0 -0
  149. package/.yarn/cache/which-npm-5.0.0-15aa39eb60-6ec99e89ba.zip +0 -0
  150. package/.yarn/cache/wrap-ansi-npm-7.0.0-ad6e1a0554-a790b846fd.zip +0 -0
  151. package/.yarn/cache/wrap-ansi-npm-8.1.0-26a4e6ae28-371733296d.zip +0 -0
  152. package/.yarn/cache/wrappy-npm-1.0.2-916de4d4b3-159da4805f.zip +0 -0
  153. package/.yarn/cache/xml2js-npm-0.6.2-64cd781d74-458a838061.zip +0 -0
  154. package/.yarn/cache/xmlbuilder-npm-11.0.1-b8b04dc929-7152695e16.zip +0 -0
  155. package/.yarn/cache/yallist-npm-4.0.0-b493d9e907-343617202a.zip +0 -0
  156. package/.yarn/cache/yallist-npm-5.0.0-8732dd9f1c-eba5118240.zip +0 -0
  157. package/.yarn/install-state.gz +0 -0
  158. package/README.md +191 -0
  159. package/bin/bundle.js +260 -0
  160. package/bin/cli.js +101 -0
  161. package/index.js +0 -0
  162. package/package.json +43 -0
  163. package/src/Login.js +8 -0
  164. package/src/config/env.js +64 -0
  165. package/utils/functions.js +0 -0
package/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # vkdeploy-cli
2
+
3
+ CLI em Node.js para gerar o bundle de um app React Native, compactar os assets e enviar o arquivo para o backend de versionamento.
4
+
5
+ ## O que esse projeto faz
6
+
7
+ O `vkdeploy` automatiza o fluxo de publicação de bundle:
8
+
9
+ 1. abre o fluxo de login para o usuário obter um token;
10
+ 2. salva o token localmente;
11
+ 3. gera o bundle React Native Android;
12
+ 4. compacta bundle + assets em um `.zip`;
13
+ 5. lê a configuração do projeto React Native;
14
+ 6. envia o arquivo para a API de versionamento.
15
+
16
+ ## Requisitos
17
+
18
+ - Node.js `>=18`
19
+ - `zip` instalado no sistema
20
+ - projeto React Native com pasta `android/`
21
+ - arquivo `android/app/src/main/assets/vkdeploy-config.json`
22
+
23
+ Exemplo mínimo do arquivo de configuração:
24
+
25
+ ```json
26
+ {
27
+ "app_secret": "SEU_APP_SECRET"
28
+ }
29
+ ```
30
+
31
+ ## Instalação
32
+
33
+ ### Desenvolvimento local com link
34
+
35
+ Dentro deste projeto:
36
+
37
+ ```bash
38
+ npm install
39
+ npm link
40
+ ```
41
+
42
+ Depois disso, o comando global `vkdeploy` passa a apontar para o código local. Qualquer alteração no código já entra na próxima execução.
43
+
44
+ ### Instalação global
45
+
46
+ ```bash
47
+ npm install -g .
48
+ ```
49
+
50
+ ## Configuração de ambiente
51
+
52
+ O projeto lê variáveis de ambiente de:
53
+
54
+ - `process.env`
55
+ - arquivo local `.env`
56
+ - fallback definido no código
57
+
58
+ Arquivo de exemplo: [.env.example](/home/vinikir/Projetos/Versionamento/vkdeploy-cli/.env.example:1)
59
+
60
+ Variáveis disponíveis:
61
+
62
+ - `FRONTEND_BASE_URL`: base usada para abrir a página de login
63
+ - `API_BASE_URL`: base usada para enviar o bundle para o backend
64
+
65
+ Exemplo:
66
+
67
+ ```env
68
+ FRONTEND_BASE_URL=http://localhost:3000
69
+ API_BASE_URL=http://localhost:3300
70
+ ```
71
+
72
+ ## Comandos
73
+
74
+ ### `vkdeploy login`
75
+
76
+ Abre a URL de login no navegador e pede o token no terminal.
77
+
78
+ Fluxo atual:
79
+
80
+ 1. abre a URL montada a partir de `FRONTEND_BASE_URL`;
81
+ 2. solicita `Insira o token gerado:`;
82
+ 3. salva o token em `database/loginStatus.json`.
83
+
84
+ ### `vkdeploy release-react <projeto> <os>`
85
+
86
+ Executa o fluxo de geração e upload do bundle.
87
+
88
+ Exemplo:
89
+
90
+ ```bash
91
+ vkdeploy release-react "Teste REAL" android
92
+ ```
93
+
94
+ Fluxo atual:
95
+
96
+ 1. verifica se existe login salvo;
97
+ 2. gera o bundle em `./temp/vkdeploy`;
98
+ 3. compacta os arquivos em `bundle-assets.zip`;
99
+ 4. lê `app_secret` de `android/app/src/main/assets/vkdeploy-config.json`;
100
+ 5. lê `versionName` de `android/app/build.gradle`;
101
+ 6. envia o zip para backend;
102
+ 7. remove arquivos temporários.
103
+
104
+ ## Scripts de qualidade
105
+
106
+ ```bash
107
+ npm run lint
108
+ npm run lint:fix
109
+ npm run format
110
+ npm run format:check
111
+ ```
112
+
113
+ ## Estrutura do projeto
114
+
115
+ ```txt
116
+ bin/
117
+ cli.js
118
+ bundle.js
119
+ src/
120
+ Login.js
121
+ config/
122
+ env.js
123
+ database/
124
+ loginStatus.json
125
+ ```
126
+
127
+ ## Funções principais
128
+
129
+ Responsável por montar os comandos do CLI com `yargs`.
130
+
131
+ Funções:
132
+
133
+ - `ensureDatabaseDirExists()`: cria o diretório de persistência do login, se ele não existir
134
+ - `isLoggedIn()`: verifica se existe status de login salvo no JSON
135
+
136
+ Comandos:
137
+
138
+ - `login`: abre o fluxo de autenticação e persiste o token
139
+ - `release-react`: dispara a geração e o upload do bundle
140
+
141
+ Responsável pela parte operacional do release.
142
+
143
+ Funções:
144
+
145
+ - `deleteFolderRecursive(folderPath)`: remove diretórios e arquivos recursivamente
146
+ - `buscProjetoId()`: lê o `app_secret` do arquivo `vkdeploy-config.json`
147
+ - `generateBundle()`: executa o comando `react-native bundle`
148
+ - `pegaVersionName()`: extrai o `versionName` do `build.gradle`
149
+ - `CompactarArquivos()`: gera o `bundle-assets.zip`
150
+ - `UploadBundle()`: envia o zip para a API
151
+ - `hashFileSha256(filePath)`: calcula o hash SHA-256 do arquivo gerado
152
+ - `toMb(bytes)`: converte bytes para MB para exibição no terminal
153
+
154
+ Responsável por centralizar a configuração de URLs.
155
+
156
+ Funções internas:
157
+
158
+ - `parseEnvFile(fileContent)`: interpreta o conteúdo do `.env`
159
+ - `getEnvValue(key, fallback)`: lê da variável de ambiente, do `.env` local ou do fallback
160
+ - `normalizeBaseUrl(value)`: remove `/` no fim da base URL
161
+ - `buildUrl(baseUrl, pathname)`: monta a URL final
162
+
163
+ Valores exportados:
164
+
165
+ - `FRONTEND_BASE_URL`
166
+ - `API_BASE_URL`
167
+ - `LOGIN_URL`
168
+ - `UPLOAD_BUNDLE_URL`
169
+
170
+ Responsável por abrir no navegador a URL de login calculada em `env.js`.
171
+
172
+ Função:
173
+
174
+ - `login()`: abre `LOGIN_URL`
175
+
176
+ ## Tratamento de erro implementado
177
+
178
+ Hoje o CLI já valida alguns pontos antes do upload:
179
+
180
+ - ausência de `vkdeploy-config.json`
181
+ - JSON inválido em `vkdeploy-config.json`
182
+ - ausência de `app_secret`
183
+ - ausência de `bundle-assets.zip`
184
+ - ausência de `versionName` no `build.gradle`
185
+
186
+ ## Limitações atuais
187
+
188
+ - o token ainda é salvo dentro do projeto, em `database/loginStatus.json`
189
+ - o fluxo de upload ainda não envia o token salvo para a API
190
+ - o código hoje está preparado principalmente para Android
191
+ - há pontos de refatoração pendentes no fluxo assíncrono de `bundle.js`
package/bin/bundle.js ADDED
@@ -0,0 +1,260 @@
1
+ import chalk from "chalk";
2
+ import axios from "axios";
3
+ import FormData from "form-data";
4
+ import { exec } from "child_process";
5
+ import fs from "fs";
6
+ import path from "path";
7
+ import crypto from "crypto";
8
+ import { UPLOAD_BUNDLE_URL } from "../src/config/env.js";
9
+
10
+ const tmpDir = path.resolve("./temp");
11
+ const versionamentoClubeDir = path.resolve(tmpDir, "vkdeploy");
12
+ let jaExistiaPastaTemp = false;
13
+
14
+ const toMb = (bytes) => (bytes / (1024 * 1024)).toFixed(2);
15
+
16
+ const hashFileSha256 = (filePath) =>
17
+ new Promise((resolve, reject) => {
18
+ const hash = crypto.createHash("sha256");
19
+ const stream = fs.createReadStream(filePath);
20
+
21
+ stream.on("error", reject);
22
+ stream.on("data", (chunk) => hash.update(chunk));
23
+ stream.on("end", () => resolve(hash.digest("hex")));
24
+ });
25
+
26
+ export const deleteFolderRecursive = (folderPath) => {
27
+ if (fs.existsSync(folderPath)) {
28
+ fs.readdirSync(folderPath).forEach((file) => {
29
+ const curPath = path.join(folderPath, file);
30
+
31
+ if (fs.lstatSync(curPath).isDirectory()) {
32
+ deleteFolderRecursive(curPath);
33
+ } else {
34
+ fs.unlinkSync(curPath);
35
+ }
36
+ });
37
+
38
+ fs.rmdirSync(folderPath);
39
+ }
40
+ };
41
+
42
+ export const buscProjetoId = async () => {
43
+ const configFilePath = path.resolve(
44
+ "./android/app/src/main/assets/vkdeploy-config.json"
45
+ );
46
+
47
+ if (!fs.existsSync(configFilePath)) {
48
+ throw new Error(
49
+ [
50
+ "Arquivo de configuração não encontrado.",
51
+ `Crie o arquivo em: ${configFilePath}`,
52
+ 'Conteúdo mínimo esperado: {"app_secret":"SEU_APP_SECRET"}',
53
+ ].join("\n")
54
+ );
55
+ }
56
+
57
+ const fileContent = fs.readFileSync(configFilePath, "utf8");
58
+ let jsonData;
59
+
60
+ try {
61
+ jsonData = JSON.parse(fileContent);
62
+ } catch (error) {
63
+ throw new Error(
64
+ `Arquivo de configuração inválido em ${configFilePath}: JSON mal formatado.`
65
+ );
66
+ }
67
+
68
+ if (
69
+ typeof jsonData.app_secret !== "string" ||
70
+ jsonData.app_secret.trim() === ""
71
+ ) {
72
+ throw new Error(`O campo "app_secret" é obrigatório em ${configFilePath}.`);
73
+ }
74
+
75
+ return jsonData.app_secret.trim();
76
+ };
77
+
78
+ export const generateBundle = async () => {
79
+ try {
80
+ console.log(chalk.blue(`📦 Gerando o bundle ...`), "\n");
81
+ //const command = `npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res`;
82
+ const command = `npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output ./temp/vkdeploy/index.android.bundle --assets-dest ./temp/vkdeploy`;
83
+ //const command = `npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output ./temp/vkdeploy/index.android.bundle --assets-dest ./temp/vkdeploy/assets`
84
+ //const command = `ls`;
85
+
86
+ if (!fs.existsSync(tmpDir)) {
87
+ fs.mkdirSync(tmpDir, { recursive: true });
88
+ } else {
89
+ jaExistiaPastaTemp = true;
90
+ }
91
+
92
+ if (!fs.existsSync(versionamentoClubeDir)) {
93
+ fs.mkdirSync(versionamentoClubeDir, { recursive: true });
94
+ }
95
+
96
+ exec(command, (error, stdout, stderr) => {
97
+ if (
98
+ error &&
99
+ error.message !=
100
+ `warn Package react-native-sqlite-storage contains invalid configuration: "dependency.platforms.ios.project" is not allowed. Please verify it's properly linked using "npx react-native config" command and contact the package maintainers about this.`
101
+ ) {
102
+ console.log(
103
+ chalk.red(`❌ Erro: Falha ao gerar o bundle: ${error.message}`)
104
+ );
105
+ return false;
106
+ }
107
+
108
+ if (stderr) {
109
+ console.log(chalk.yellow(`💡 Aviso: ${stderr}`));
110
+ }
111
+
112
+ console.log(chalk.green("✅ O bundle foi gerado com sucesso!"), "\n");
113
+ CompactarArquivos();
114
+ });
115
+ } catch (e) {
116
+ deleteFolderRecursive(versionamentoClubeDir);
117
+ if (!jaExistiaPastaTemp) {
118
+ deleteFolderRecursive("temp");
119
+ }
120
+ console.log(
121
+ chalk.red(`❌ Erro: Falha ao gerar o bundle: ${e.message}`),
122
+ "\n"
123
+ );
124
+ }
125
+ };
126
+ const pegaVersionName = () => {
127
+ try {
128
+ const gradleFilePath = "./android/app/build.gradle";
129
+
130
+ const gradleFileContent = fs.readFileSync(gradleFilePath, "utf-8");
131
+
132
+ const versionNameMatch = gradleFileContent.match(/versionName\s+"([^"]+)"/);
133
+
134
+ if (versionNameMatch && versionNameMatch[1]) {
135
+ const versionName = versionNameMatch[1];
136
+
137
+ return versionName;
138
+ }
139
+
140
+ throw new Error("Atributo versionName não encontrado no arquivo.");
141
+ } catch (e) {
142
+ throw new Error(e.message);
143
+ }
144
+ };
145
+
146
+ const CompactarArquivos = async () => {
147
+ try {
148
+ //index.android.bundle
149
+ //const command2 = `tar -czvf bundle-assets.tar -C android/app/src/main assets/index.android.bundle res`;
150
+ //const command2 = `cd android/app/src/main && zip -r ../../../../bundle-assets.zip assets/index.android.bundle res && cd ../../../../`;
151
+ //const command2 = `zip -r bundle-assets.zip temp/vkdeploy/*`;
152
+ const command2 = `cd temp/vkdeploy && zip -r ../../bundle-assets.zip * && cd ../../`;
153
+
154
+ console.log(chalk.blue(`🗃️ Empacotando o bundle e os assets ...`), "\n");
155
+
156
+ exec(command2, (error, stdout, stderr) => {
157
+ if (error) {
158
+ console.log(
159
+ chalk.red(
160
+ `❌ Erro: Falha ao gerar o empacotamento do bundle e assets : ${error.message}`
161
+ )
162
+ );
163
+ return false;
164
+ }
165
+
166
+ if (stderr) {
167
+ console.log(chalk.yellow(`💡 Aviso: ${stderr}`));
168
+ }
169
+
170
+ console.log(
171
+ chalk.green("✅ O bundle e os assets foram empacotados com sucesso!"),
172
+ "\n"
173
+ );
174
+
175
+ deleteFolderRecursive(versionamentoClubeDir);
176
+ if (!jaExistiaPastaTemp) {
177
+ deleteFolderRecursive("temp");
178
+ }
179
+ UploadBundle();
180
+ });
181
+ // UploadBundle();
182
+ } catch (e) {
183
+ deleteFolderRecursive(versionamentoClubeDir);
184
+ if (!jaExistiaPastaTemp) {
185
+ deleteFolderRecursive("temp");
186
+ }
187
+ deleteFolderRecursive("bundle-assets.zip");
188
+ throw new Error(e.message);
189
+ }
190
+ };
191
+
192
+ const UploadBundle = async () => {
193
+ try {
194
+ console.log(chalk.blue(`📤 Upload do bundle...`), "\n");
195
+
196
+ const projectId = await buscProjetoId();
197
+ //const filePath = "./android/app/src/main/assets/index.android.bundle"
198
+ const filePath = "./bundle-assets.zip";
199
+
200
+ if (!fs.existsSync(filePath)) {
201
+ throw new Error(`Arquivo de upload não encontrado: ${filePath}`);
202
+ }
203
+
204
+ const zipStats = fs.statSync(filePath);
205
+ const zipHash = await hashFileSha256(filePath);
206
+ const zipAbsolutePath = path.resolve(filePath);
207
+
208
+ console.log(chalk.blue("🧾 Validando arquivo de upload..."), "\n");
209
+ console.log(`Arquivo: ${path.basename(filePath)}`);
210
+ console.log(`Caminho: ${zipAbsolutePath}`);
211
+ console.log(`Tamanho: ${zipStats.size} bytes (${toMb(zipStats.size)} MB)`);
212
+ console.log(`SHA256: ${zipHash}`, "\n");
213
+
214
+ const uploadUrl = UPLOAD_BUNDLE_URL;
215
+
216
+ const fileStream = fs.createReadStream(filePath);
217
+
218
+ const versionBundle = pegaVersionName();
219
+
220
+ const formData = new FormData();
221
+
222
+ formData.append("projeto", projectId);
223
+ formData.append("versaoBuild", versionBundle);
224
+
225
+ formData.append("file", fileStream);
226
+ let versaoUltima;
227
+
228
+ await axios
229
+ .post(uploadUrl, formData, {
230
+ headers: {
231
+ ...formData.getHeaders(),
232
+ },
233
+ })
234
+ .then((res) => {
235
+ versaoUltima = res.data.versao;
236
+ })
237
+ .catch((e) => {
238
+ console.log("Errroooo", JSON.stringify(e));
239
+ fs.unlinkSync(filePath);
240
+ throw new Error(e.message);
241
+ });
242
+
243
+ console.log(chalk.green("✅ O upload foi realizado com sucesso!"), "\n");
244
+ console.log(
245
+ chalk.green(
246
+ `Versão publicada: Android ${versionBundle}, bundle ${versaoUltima} (V ${versionBundle}.${versaoUltima})`
247
+ ),
248
+ "\n"
249
+ );
250
+
251
+ fs.unlinkSync(filePath);
252
+ } catch (error) {
253
+ console.log(
254
+ chalk.red(`❌ Erro: Falha ao fazer upload do bundle: ${error.message}`),
255
+ "\n"
256
+ );
257
+ } finally {
258
+ deleteFolderRecursive("bundle-assets.zip");
259
+ }
260
+ };
package/bin/cli.js ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+
3
+ import yargs from "yargs/yargs";
4
+ import { hideBin } from "yargs/helpers";
5
+ import { generateBundle } from "./bundle.js";
6
+ import fs from "fs";
7
+ import path from "path";
8
+ import login from "../src/Login.js";
9
+ import readline from "readline";
10
+
11
+ import { fileURLToPath } from "url";
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ // Ajuste o caminho para salvar o arquivo na pasta 'database' uma pasta atrás
17
+ const loginFilePath = path.resolve(__dirname, "../database/loginStatus.json");
18
+
19
+ const ensureDatabaseDirExists = () => {
20
+ const dir = path.dirname(loginFilePath);
21
+ if (!fs.existsSync(dir)) {
22
+ fs.mkdirSync(dir, { recursive: true });
23
+ }
24
+ };
25
+ // async function login() {
26
+ // // Função para fazer o login
27
+ // console.log("Realizando login...");
28
+ // // Adicione aqui a lógica de login
29
+
30
+ // // Simulando login bem-sucedido
31
+ // const loginStatus = { loggedIn: true };
32
+ // fs.writeFileSync(loginFilePath, JSON.stringify(loginStatus));
33
+ // console.log("Login realizado com sucesso.");
34
+ // }
35
+
36
+ function isLoggedIn() {
37
+ if (fs.existsSync(loginFilePath)) {
38
+ const loginStatus = JSON.parse(fs.readFileSync(loginFilePath, "utf-8"));
39
+ return loginStatus.loggedIn;
40
+ }
41
+ return false;
42
+ }
43
+
44
+ yargs(hideBin(process.argv))
45
+ .command(
46
+ "release-react [projeto]",
47
+ "Gera bundle e faz upload",
48
+ (yargs) => {
49
+ return yargs
50
+ .positional("projeto", {
51
+ describe: "Projeto alvo",
52
+ default: "meu-projeto",
53
+ })
54
+ .positional("os", {
55
+ describe: "OS alvo",
56
+ default: "android",
57
+ });
58
+ },
59
+ async (argv) => {
60
+ console.log(isLoggedIn());
61
+ if (!isLoggedIn()) {
62
+ console.log("Você precisa estar logado para gerar o bundle.");
63
+ return;
64
+ }
65
+
66
+ if (argv.os == "android") {
67
+ console.log(`Gerando bundle para ${argv.projeto} os ${argv.os} `, "\n");
68
+ await generateBundle();
69
+ }
70
+
71
+ if (argv.os == "ios") {
72
+ console.log(`Gerando bundle para ${argv.projeto} os ${argv.os} `, "\n");
73
+ //await generateBundleIOS()
74
+ }
75
+ }
76
+ )
77
+ .command(
78
+ "login",
79
+ "Realiza login",
80
+ (yargs) => {
81
+ return yargs;
82
+ },
83
+ async () => {
84
+ login();
85
+ const rl = readline.createInterface({
86
+ input: process.stdin,
87
+ output: process.stdout,
88
+ });
89
+
90
+ rl.question("Insira o token gerado: ", (token) => {
91
+ // Simulando login bem-sucedido com o token
92
+ const loginStatus = { loggedIn: true, token: token };
93
+ ensureDatabaseDirExists();
94
+ fs.writeFileSync(loginFilePath, JSON.stringify(loginStatus));
95
+ console.log("Login realizado com sucesso.");
96
+ rl.close();
97
+ });
98
+ }
99
+ )
100
+ .help()
101
+ .parse();
package/index.js ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "vkdeploy-cli",
3
+ "version": "0.0.8",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "lint": "eslint .",
7
+ "lint:fix": "eslint . --fix",
8
+ "format": "prettier . --write",
9
+ "format:check": "prettier . --check",
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "author": "",
13
+ "license": "ISC",
14
+ "description": "",
15
+ "peerDependencies": {
16
+ "fs": "^0.0.1-security",
17
+ "react": "18.3.1"
18
+ },
19
+ "bin": {
20
+ "vkdeploy": "./bin/cli.js"
21
+ },
22
+ "dependencies": {
23
+ "axios": "^1.7.9",
24
+ "chalk": "^5.3.0",
25
+ "child_process": "^1.0.2",
26
+ "events": "^3.3.0",
27
+ "form-data": "^4.0.1",
28
+ "open": "^10.1.0",
29
+ "path": "^0.12.7",
30
+ "readline": "^1.3.0",
31
+ "timers": "^0.1.1",
32
+ "xml2js": "^0.6.2",
33
+ "yargs": "^17.7.2"
34
+ },
35
+ "devDependencies": {
36
+ "eslint": "^8.57.1",
37
+ "prettier": "^3.3.3"
38
+ },
39
+ "engines": {
40
+ "node": ">=18"
41
+ },
42
+ "type": "module"
43
+ }
package/src/Login.js ADDED
@@ -0,0 +1,8 @@
1
+ import open from "open";
2
+ import { LOGIN_URL } from "./config/env.js";
3
+
4
+ function login() {
5
+ open(LOGIN_URL);
6
+ }
7
+
8
+ export default login;
@@ -0,0 +1,64 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const rootDir = path.resolve(__dirname, "../..");
8
+ const envFilePath = path.join(rootDir, ".env");
9
+
10
+ const parseEnvFile = (fileContent) => {
11
+ const env = {};
12
+
13
+ for (const rawLine of fileContent.split(/\r?\n/)) {
14
+ const line = rawLine.trim();
15
+
16
+ if (!line || line.startsWith("#")) {
17
+ continue;
18
+ }
19
+
20
+ const separatorIndex = line.indexOf("=");
21
+
22
+ if (separatorIndex === -1) {
23
+ continue;
24
+ }
25
+
26
+ const key = line.slice(0, separatorIndex).trim();
27
+ let value = line.slice(separatorIndex + 1).trim();
28
+
29
+ if (
30
+ (value.startsWith('"') && value.endsWith('"')) ||
31
+ (value.startsWith("'") && value.endsWith("'"))
32
+ ) {
33
+ value = value.slice(1, -1);
34
+ }
35
+
36
+ env[key] = value;
37
+ }
38
+
39
+ return env;
40
+ };
41
+
42
+ const fileEnv = fs.existsSync(envFilePath)
43
+ ? parseEnvFile(fs.readFileSync(envFilePath, "utf-8"))
44
+ : {};
45
+
46
+ const getEnvValue = (key, fallback) =>
47
+ process.env[key] || fileEnv[key] || fallback;
48
+
49
+ const normalizeBaseUrl = (value) => value.replace(/\/+$/, "");
50
+
51
+ const buildUrl = (baseUrl, pathname) =>
52
+ `${normalizeBaseUrl(baseUrl)}${pathname}`;
53
+
54
+ export const FRONTEND_BASE_URL = getEnvValue(
55
+ "FRONTEND_BASE_URL",
56
+ "http://localhost:3000"
57
+ );
58
+ export const API_BASE_URL = getEnvValue(
59
+ "API_BASE_URL",
60
+ "https://versionamento-back-end-388770734965.us-central1.run.app"
61
+ );
62
+
63
+ export const LOGIN_URL = buildUrl(FRONTEND_BASE_URL, "/token");
64
+ export const UPLOAD_BUNDLE_URL = buildUrl(API_BASE_URL, "/bundle/uploadFile");
File without changes