@kroszborg/sugi 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin.js +1 -1
- package/install.js +70 -53
- package/package.json +1 -1
package/README.md
CHANGED
package/bin.js
CHANGED
|
@@ -10,7 +10,7 @@ const binPath = path.join(__dirname, 'bin', binName);
|
|
|
10
10
|
|
|
11
11
|
if (!fs.existsSync(binPath)) {
|
|
12
12
|
console.error('sugi: binary not found at ' + binPath);
|
|
13
|
-
console.error('Try reinstalling: npm install -g sugi');
|
|
13
|
+
console.error('Try reinstalling: npm install -g @kroszborg/sugi');
|
|
14
14
|
process.exit(1);
|
|
15
15
|
}
|
|
16
16
|
|
package/install.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// npm binary wrapper for sugi
|
|
3
3
|
// Downloads the correct pre-built binary from GitHub Releases on postinstall.
|
|
4
|
-
// Falls back to `go
|
|
4
|
+
// Falls back to `go install` if no release binary exists yet.
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ const https = require('https');
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const os = require('os');
|
|
12
|
-
const {
|
|
12
|
+
const { spawnSync } = require('child_process');
|
|
13
13
|
|
|
14
14
|
const VERSION = require('./package.json').version;
|
|
15
15
|
const REPO = 'Kroszborg/sugi';
|
|
@@ -35,72 +35,100 @@ function platformInfo() {
|
|
|
35
35
|
return { osName, cpu, ext, binName };
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// Download url to dest, following up to 10 redirects.
|
|
39
|
+
// Only opens the WriteStream once we have a confirmed 200 response.
|
|
38
40
|
function downloadFile(url, dest) {
|
|
39
41
|
return new Promise((resolve, reject) => {
|
|
40
|
-
|
|
42
|
+
let redirects = 0;
|
|
43
|
+
|
|
41
44
|
const get = (u) => {
|
|
45
|
+
if (redirects > 10) return reject(new Error('Too many redirects'));
|
|
42
46
|
https.get(u, { headers: { 'User-Agent': 'sugi-npm-installer' } }, (res) => {
|
|
43
|
-
if (res.statusCode === 301 || res.statusCode === 302
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
if (res.statusCode === 301 || res.statusCode === 302 ||
|
|
48
|
+
res.statusCode === 307 || res.statusCode === 308) {
|
|
49
|
+
redirects++;
|
|
50
|
+
res.resume(); // drain so socket can be reused
|
|
46
51
|
return get(res.headers.location);
|
|
47
52
|
}
|
|
48
53
|
if (res.statusCode !== 200) {
|
|
49
|
-
|
|
50
|
-
return reject(new Error(`HTTP ${res.statusCode}
|
|
54
|
+
res.resume();
|
|
55
|
+
return reject(new Error(`HTTP ${res.statusCode} downloading ${u}`));
|
|
51
56
|
}
|
|
57
|
+
// Only create the file once we have a real 200 response
|
|
58
|
+
const file = fs.createWriteStream(dest);
|
|
52
59
|
res.pipe(file);
|
|
53
|
-
file.on('finish', () =>
|
|
54
|
-
|
|
60
|
+
file.on('finish', () => file.close(resolve));
|
|
61
|
+
file.on('error', (e) => { fs.unlink(dest, () => {}); reject(e); });
|
|
62
|
+
res.on('error', (e) => { file.close(); fs.unlink(dest, () => {}); reject(e); });
|
|
63
|
+
}).on('error', reject);
|
|
55
64
|
};
|
|
65
|
+
|
|
56
66
|
get(url);
|
|
57
67
|
});
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
function tryGoBuild() {
|
|
61
|
-
// Check if go is available
|
|
62
71
|
const goCheck = spawnSync('go', ['version'], { stdio: 'pipe' });
|
|
63
|
-
if (goCheck.status !== 0)
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
72
|
+
if (goCheck.status !== 0) return false;
|
|
66
73
|
|
|
67
74
|
if (!fs.existsSync(BIN_DIR)) fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
68
75
|
|
|
69
76
|
const binName = process.platform === 'win32' ? 'sugi.exe' : 'sugi';
|
|
70
77
|
const binDest = path.join(BIN_DIR, binName);
|
|
71
78
|
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
console.log('sugi: building from source via `go install`...');
|
|
80
|
+
// Embed the version string via ldflags so `sugi version` shows the right value
|
|
81
|
+
const ldflags = `-X main.version=${VERSION}`;
|
|
74
82
|
const result = spawnSync(
|
|
75
83
|
'go',
|
|
76
|
-
['install', `github.com/${REPO}/cmd/sugi@latest`],
|
|
84
|
+
['install', `-ldflags=${ldflags}`, `github.com/${REPO}/cmd/sugi@latest`],
|
|
77
85
|
{ stdio: 'inherit', env: process.env }
|
|
78
86
|
);
|
|
79
87
|
|
|
80
|
-
if (result.status
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
88
|
+
if (result.status !== 0) return false;
|
|
89
|
+
|
|
90
|
+
// Locate the installed binary in GOBIN or GOPATH/bin
|
|
91
|
+
const goEnv = spawnSync('go', ['env', 'GOBIN', 'GOPATH'], { stdio: 'pipe' });
|
|
92
|
+
if (goEnv.status !== 0) return false;
|
|
93
|
+
|
|
94
|
+
const [gobin, gopath] = goEnv.stdout.toString().trim().split('\n');
|
|
95
|
+
const candidates = [
|
|
96
|
+
gobin && path.join(gobin.trim(), binName),
|
|
97
|
+
gopath && path.join(gopath.trim(), 'bin', binName),
|
|
98
|
+
].filter(Boolean);
|
|
99
|
+
|
|
100
|
+
for (const src of candidates) {
|
|
101
|
+
if (fs.existsSync(src)) {
|
|
102
|
+
fs.copyFileSync(src, binDest);
|
|
103
|
+
fs.chmodSync(binDest, 0o755);
|
|
104
|
+
console.log(`sugi: installed to ${binDest}`);
|
|
105
|
+
return true;
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
return false;
|
|
102
110
|
}
|
|
103
111
|
|
|
112
|
+
function extractArchive(archivePath, ext, binName) {
|
|
113
|
+
if (!fs.existsSync(BIN_DIR)) fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
114
|
+
|
|
115
|
+
if (ext === '.tar.gz') {
|
|
116
|
+
// Use array args to avoid shell injection
|
|
117
|
+
const result = spawnSync('tar', ['-xzf', archivePath, '-C', BIN_DIR, binName], { stdio: 'inherit' });
|
|
118
|
+
if (result.status !== 0) throw new Error('tar extraction failed');
|
|
119
|
+
} else {
|
|
120
|
+
// Windows: use PowerShell Expand-Archive
|
|
121
|
+
const ps = `Expand-Archive -Path '${archivePath}' -DestinationPath '${BIN_DIR}' -Force`;
|
|
122
|
+
const result = spawnSync('powershell', ['-NoProfile', '-Command', ps], { stdio: 'inherit' });
|
|
123
|
+
if (result.status !== 0) throw new Error('Expand-Archive failed');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const binDest = path.join(BIN_DIR, binName);
|
|
127
|
+
if (!fs.existsSync(binDest)) throw new Error(`Binary not found after extraction: ${binDest}`);
|
|
128
|
+
fs.chmodSync(binDest, 0o755);
|
|
129
|
+
return binDest;
|
|
130
|
+
}
|
|
131
|
+
|
|
104
132
|
async function main() {
|
|
105
133
|
const { osName, cpu, ext, binName } = platformInfo();
|
|
106
134
|
|
|
@@ -109,45 +137,34 @@ async function main() {
|
|
|
109
137
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sugi-'));
|
|
110
138
|
const archivePath = path.join(tmpDir, assetName);
|
|
111
139
|
|
|
112
|
-
console.log(`sugi: downloading ${assetName}
|
|
140
|
+
console.log(`sugi: downloading ${assetName}...`);
|
|
113
141
|
|
|
114
142
|
let downloadOk = false;
|
|
115
143
|
try {
|
|
116
144
|
await downloadFile(url, archivePath);
|
|
117
145
|
downloadOk = true;
|
|
118
146
|
} catch (e) {
|
|
119
|
-
console.error(`sugi: download failed
|
|
147
|
+
console.error(`sugi: download failed - ${e.message}`);
|
|
120
148
|
}
|
|
121
149
|
|
|
122
150
|
if (!downloadOk) {
|
|
123
|
-
console.log(
|
|
151
|
+
console.log('sugi: falling back to go install...');
|
|
124
152
|
const ok = tryGoBuild();
|
|
153
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
125
154
|
if (!ok) {
|
|
126
|
-
console.error(
|
|
155
|
+
console.error('sugi: install failed. Install manually:');
|
|
127
156
|
console.error(` go install github.com/${REPO}/cmd/sugi@latest`);
|
|
128
157
|
console.error(` or download from: https://github.com/${REPO}/releases`);
|
|
129
158
|
process.exit(1);
|
|
130
159
|
}
|
|
131
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
132
160
|
return;
|
|
133
161
|
}
|
|
134
162
|
|
|
135
|
-
if (!fs.existsSync(BIN_DIR)) fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
136
|
-
const binDest = path.join(BIN_DIR, binName);
|
|
137
|
-
|
|
138
163
|
try {
|
|
139
|
-
|
|
140
|
-
execSync(`tar -xzf "${archivePath}" -C "${BIN_DIR}" sugi`, { stdio: 'inherit' });
|
|
141
|
-
} else {
|
|
142
|
-
execSync(
|
|
143
|
-
`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${BIN_DIR}' -Force"`,
|
|
144
|
-
{ stdio: 'inherit' }
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
fs.chmodSync(binDest, 0o755);
|
|
164
|
+
const binDest = extractArchive(archivePath, ext, binName);
|
|
148
165
|
console.log(`sugi: installed to ${binDest}`);
|
|
149
166
|
} catch (e) {
|
|
150
|
-
console.error(`sugi: extraction failed
|
|
167
|
+
console.error(`sugi: extraction failed - ${e.message}`);
|
|
151
168
|
process.exit(1);
|
|
152
169
|
} finally {
|
|
153
170
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|