sogtj 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 +42 -0
- package/bin/sogtj.js +189 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# iSOG via npx
|
|
2
|
+
|
|
3
|
+
Pacote npm pequeno para iniciar a instalacao do iSOG pelo comando:
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
npx -y sogtj
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Na versao `0.1.0`, o bootstrap suporta apenas Windows. O pacote baixa o
|
|
10
|
+
instalador publicado na release do GitHub, valida o SHA256 esperado da versao e
|
|
11
|
+
abre o instalador.
|
|
12
|
+
|
|
13
|
+
O comando `npx -y sogtj` nao substitui o instalador do sistema. Ele apenas
|
|
14
|
+
entrega o usuario ao instalador oficial. Depois da instalacao, o aplicativo
|
|
15
|
+
iSOG/SOG Desktop instalado inicia a stack local e abre o dashboard no navegador
|
|
16
|
+
padrao do Windows, que pode ser o Chrome.
|
|
17
|
+
|
|
18
|
+
Asset padrao da versao `0.1.0`:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
SOG.Desktop.Setup.0.1.0.exe
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Publicacao
|
|
25
|
+
|
|
26
|
+
1. Publique o instalador Windows como asset da release `v0.1.0`.
|
|
27
|
+
2. Calcule o SHA256 do instalador.
|
|
28
|
+
3. Publique este pacote no npm:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
cd packages/sogtj
|
|
32
|
+
npm publish --access public
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Teste com URL direta
|
|
36
|
+
|
|
37
|
+
```powershell
|
|
38
|
+
$env:ISOG_ASSET_URL="https://exemplo.local/SOG%20Desktop%20Setup%200.1.0.exe"
|
|
39
|
+
npx -y sogtj --verify-only
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use `ISOG_SHA256` apenas para testar um asset diferente do oficial da versao.
|
package/bin/sogtj.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const childProcess = require("node:child_process");
|
|
4
|
+
const crypto = require("node:crypto");
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const http = require("node:http");
|
|
7
|
+
const https = require("node:https");
|
|
8
|
+
const os = require("node:os");
|
|
9
|
+
const path = require("node:path");
|
|
10
|
+
const { pipeline } = require("node:stream/promises");
|
|
11
|
+
|
|
12
|
+
const PACKAGE_VERSION = "0.1.0";
|
|
13
|
+
const DISPLAY_NAME = "iSOG";
|
|
14
|
+
const REPO_RELEASE_BASE = "https://github.com/carlosGuimaraes-dev/SOG/releases/download";
|
|
15
|
+
const DEFAULT_ASSET_NAME = `SOG.Desktop.Setup.${PACKAGE_VERSION}.exe`;
|
|
16
|
+
const DEFAULT_SHA256 = "b160e23d8f9114b1026794311e89f29445081f2502ab6059b7268d9c64dbfc7b";
|
|
17
|
+
|
|
18
|
+
function usage() {
|
|
19
|
+
console.log(`
|
|
20
|
+
${DISPLAY_NAME} installer
|
|
21
|
+
|
|
22
|
+
Uso:
|
|
23
|
+
npx -y sogtj
|
|
24
|
+
|
|
25
|
+
Opcoes:
|
|
26
|
+
--help Mostra esta ajuda.
|
|
27
|
+
--dry-run Mostra o instalador que seria baixado/executado.
|
|
28
|
+
--verify-only Baixa e valida o instalador, mas nao executa.
|
|
29
|
+
|
|
30
|
+
Variaveis:
|
|
31
|
+
ISOG_VERSION Versao da release. Padrao: ${PACKAGE_VERSION}
|
|
32
|
+
ISOG_ASSET_URL URL direta do instalador Windows.
|
|
33
|
+
ISOG_ASSET_NAME Nome do asset na release. Padrao: ${DEFAULT_ASSET_NAME}
|
|
34
|
+
ISOG_SHA256 SHA256 esperado do instalador. Padrao: hash de ${PACKAGE_VERSION}
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseArgs(argv) {
|
|
39
|
+
const flags = new Set(argv);
|
|
40
|
+
return {
|
|
41
|
+
help: flags.has("--help") || flags.has("-h"),
|
|
42
|
+
dryRun: flags.has("--dry-run"),
|
|
43
|
+
verifyOnly: flags.has("--verify-only"),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function requireWindows() {
|
|
48
|
+
if (process.platform !== "win32") {
|
|
49
|
+
throw new Error(`${DISPLAY_NAME} ${PACKAGE_VERSION} ainda esta disponivel apenas para Windows.`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getInstallConfig() {
|
|
54
|
+
const version = process.env.ISOG_VERSION || PACKAGE_VERSION;
|
|
55
|
+
const assetName = process.env.ISOG_ASSET_NAME || `SOG.Desktop.Setup.${version}.exe`;
|
|
56
|
+
const releaseBase = `${REPO_RELEASE_BASE}/v${version}`;
|
|
57
|
+
const assetUrl = process.env.ISOG_ASSET_URL || `${releaseBase}/${encodeURIComponent(assetName)}`;
|
|
58
|
+
const cacheRoot = process.env.LOCALAPPDATA || os.tmpdir();
|
|
59
|
+
const cacheDir = path.join(cacheRoot, "iSOG", "installers", version);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
version,
|
|
63
|
+
assetName,
|
|
64
|
+
assetUrl,
|
|
65
|
+
expectedSha256: process.env.ISOG_SHA256 || DEFAULT_SHA256,
|
|
66
|
+
cacheDir,
|
|
67
|
+
installerPath: path.join(cacheDir, assetName),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function ensureDir(dir) {
|
|
72
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function fileExists(filePath) {
|
|
76
|
+
try {
|
|
77
|
+
await fs.promises.access(filePath, fs.constants.R_OK);
|
|
78
|
+
return true;
|
|
79
|
+
} catch (_) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function sha256(filePath) {
|
|
85
|
+
const hash = crypto.createHash("sha256");
|
|
86
|
+
const input = fs.createReadStream(filePath);
|
|
87
|
+
|
|
88
|
+
await new Promise((resolve, reject) => {
|
|
89
|
+
input.on("data", (chunk) => hash.update(chunk));
|
|
90
|
+
input.on("error", reject);
|
|
91
|
+
input.on("end", resolve);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return hash.digest("hex");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function request(url, redirectsLeft = 5) {
|
|
98
|
+
const client = url.startsWith("https:") ? https : http;
|
|
99
|
+
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const req = client.get(url, (res) => {
|
|
102
|
+
const statusCode = res.statusCode || 0;
|
|
103
|
+
const location = res.headers.location;
|
|
104
|
+
|
|
105
|
+
if ([301, 302, 303, 307, 308].includes(statusCode) && location && redirectsLeft > 0) {
|
|
106
|
+
res.resume();
|
|
107
|
+
resolve(request(new URL(location, url).toString(), redirectsLeft - 1));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
112
|
+
res.resume();
|
|
113
|
+
reject(new Error(`Download falhou com HTTP ${statusCode}: ${url}`));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
resolve(res);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
req.on("error", reject);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function download(url, destination) {
|
|
125
|
+
const partial = `${destination}.download`;
|
|
126
|
+
const res = await request(url);
|
|
127
|
+
await pipeline(res, fs.createWriteStream(partial));
|
|
128
|
+
await fs.promises.rename(partial, destination);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function ensureInstaller(config) {
|
|
132
|
+
await ensureDir(config.cacheDir);
|
|
133
|
+
|
|
134
|
+
if (!(await fileExists(config.installerPath))) {
|
|
135
|
+
console.log(`Baixando ${DISPLAY_NAME} ${config.version}...`);
|
|
136
|
+
await download(config.assetUrl, config.installerPath);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (config.expectedSha256) {
|
|
140
|
+
const actual = await sha256(config.installerPath);
|
|
141
|
+
if (actual.toLowerCase() !== config.expectedSha256.toLowerCase()) {
|
|
142
|
+
await fs.promises.rm(config.installerPath, { force: true });
|
|
143
|
+
throw new Error(`SHA256 invalido para ${config.assetName}. Esperado ${config.expectedSha256}, obtido ${actual}.`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function runInstaller(installerPath) {
|
|
149
|
+
console.log(`Abrindo instalador: ${installerPath}`);
|
|
150
|
+
const child = childProcess.spawn(installerPath, [], {
|
|
151
|
+
detached: true,
|
|
152
|
+
stdio: "ignore",
|
|
153
|
+
windowsHide: false,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
child.unref();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function main() {
|
|
160
|
+
const args = parseArgs(process.argv.slice(2));
|
|
161
|
+
|
|
162
|
+
if (args.help) {
|
|
163
|
+
usage();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const config = getInstallConfig();
|
|
168
|
+
|
|
169
|
+
if (args.dryRun) {
|
|
170
|
+
console.log(JSON.stringify(config, null, 2));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
requireWindows();
|
|
175
|
+
|
|
176
|
+
await ensureInstaller(config);
|
|
177
|
+
|
|
178
|
+
if (args.verifyOnly) {
|
|
179
|
+
console.log(`${DISPLAY_NAME} ${config.version} baixado e validado em ${config.installerPath}`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
runInstaller(config.installerPath);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
main().catch((error) => {
|
|
187
|
+
console.error(`Erro: ${error.message}`);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sogtj",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Instalador via npx para o iSOG.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"sogtj": "bin/sogtj.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/sogtj.js",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"isog",
|
|
14
|
+
"sog",
|
|
15
|
+
"custas",
|
|
16
|
+
"tjdf"
|
|
17
|
+
],
|
|
18
|
+
"license": "UNLICENSED",
|
|
19
|
+
"private": false,
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
}
|
|
23
|
+
}
|