@gw-tools/gw 0.12.18 → 0.12.20
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/install.js +74 -28
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
* Downloads the appropriate binary for the current platform
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
chmodSync,
|
|
12
|
+
openSync,
|
|
13
|
+
fsyncSync,
|
|
14
|
+
closeSync,
|
|
15
|
+
} = require('fs');
|
|
9
16
|
const { join } = require('path');
|
|
10
17
|
const { arch, platform } = require('os');
|
|
11
18
|
const https = require('https');
|
|
@@ -39,7 +46,7 @@ function getBinaryName() {
|
|
|
39
46
|
if (!os || !cpu) {
|
|
40
47
|
console.error(
|
|
41
48
|
`Unsupported platform: ${platform()}-${arch()}\n` +
|
|
42
|
-
'Supported platforms: macOS (x64, arm64), Linux (x64, arm64), Windows (x64, arm64)'
|
|
49
|
+
'Supported platforms: macOS (x64, arm64), Linux (x64, arm64), Windows (x64, arm64)',
|
|
43
50
|
);
|
|
44
51
|
process.exit(1);
|
|
45
52
|
}
|
|
@@ -55,26 +62,43 @@ function download(url, dest) {
|
|
|
55
62
|
return new Promise((resolve, reject) => {
|
|
56
63
|
const file = createWriteStream(dest);
|
|
57
64
|
|
|
58
|
-
https
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
https
|
|
66
|
+
.get(
|
|
67
|
+
url,
|
|
68
|
+
{ headers: { 'User-Agent': 'gw-npm-installer' } },
|
|
69
|
+
(response) => {
|
|
70
|
+
// Handle redirects
|
|
71
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
72
|
+
return download(response.headers.location, dest)
|
|
73
|
+
.then(resolve)
|
|
74
|
+
.catch(reject);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (response.statusCode !== 200) {
|
|
78
|
+
reject(
|
|
79
|
+
new Error(
|
|
80
|
+
`Failed to download: ${response.statusCode} ${response.statusMessage}`,
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
response.pipe(file);
|
|
87
|
+
|
|
88
|
+
file.on('finish', () => {
|
|
89
|
+
file.close((err) => {
|
|
90
|
+
if (err) {
|
|
91
|
+
reject(err);
|
|
92
|
+
} else {
|
|
93
|
+
resolve();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
.on('error', (err) => {
|
|
100
|
+
reject(err);
|
|
74
101
|
});
|
|
75
|
-
}).on('error', (err) => {
|
|
76
|
-
reject(err);
|
|
77
|
-
});
|
|
78
102
|
|
|
79
103
|
file.on('error', (err) => {
|
|
80
104
|
reject(err);
|
|
@@ -106,9 +130,20 @@ async function install() {
|
|
|
106
130
|
console.log(`Downloading from: ${downloadUrl}`);
|
|
107
131
|
await download(downloadUrl, binaryPath);
|
|
108
132
|
|
|
109
|
-
//
|
|
133
|
+
// Ensure file is fully written to disk before chmod
|
|
134
|
+
// This prevents ETXTBSY errors on Linux systems
|
|
110
135
|
if (platform() !== 'win32') {
|
|
136
|
+
const fd = openSync(binaryPath, 'r+');
|
|
137
|
+
fsyncSync(fd);
|
|
138
|
+
closeSync(fd);
|
|
139
|
+
|
|
140
|
+
// Small delay after fsync before chmod
|
|
141
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
142
|
+
|
|
111
143
|
chmodSync(binaryPath, 0o755);
|
|
144
|
+
|
|
145
|
+
// Additional delay after chmod to prevent ETXTBSY
|
|
146
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
112
147
|
}
|
|
113
148
|
|
|
114
149
|
console.log('✓ Installation complete!');
|
|
@@ -118,7 +153,9 @@ async function install() {
|
|
|
118
153
|
await installShellIntegration(binaryPath);
|
|
119
154
|
|
|
120
155
|
console.log('\nRun "gw --help" to get started.');
|
|
121
|
-
console.log(
|
|
156
|
+
console.log(
|
|
157
|
+
'Tip: Restart your terminal or run "source ~/.zshrc" (or ~/.bashrc) to use "gw cd"',
|
|
158
|
+
);
|
|
122
159
|
} catch (error) {
|
|
123
160
|
console.error('\n✗ Installation failed:', error.message);
|
|
124
161
|
console.error('\nYou can manually download the binary from:');
|
|
@@ -134,25 +171,34 @@ async function install() {
|
|
|
134
171
|
/**
|
|
135
172
|
* Install shell integration
|
|
136
173
|
*/
|
|
137
|
-
async function installShellIntegration(binaryPath) {
|
|
174
|
+
async function installShellIntegration(binaryPath, retries = 3) {
|
|
138
175
|
const { spawn } = require('child_process');
|
|
139
176
|
|
|
140
177
|
return new Promise((resolve) => {
|
|
141
178
|
const child = spawn(binaryPath, ['install-shell', '--quiet'], {
|
|
142
|
-
stdio: 'inherit'
|
|
179
|
+
stdio: 'inherit',
|
|
143
180
|
});
|
|
144
181
|
|
|
145
182
|
child.on('close', (code) => {
|
|
146
183
|
if (code === 0) {
|
|
147
184
|
console.log('✓ Shell integration installed!');
|
|
148
185
|
} else {
|
|
149
|
-
console.log(
|
|
186
|
+
console.log(
|
|
187
|
+
' (Shell integration can be installed later with: gw install-shell)',
|
|
188
|
+
);
|
|
150
189
|
}
|
|
151
190
|
resolve();
|
|
152
191
|
});
|
|
153
192
|
|
|
154
|
-
child.on('error', (err) => {
|
|
155
|
-
|
|
193
|
+
child.on('error', async (err) => {
|
|
194
|
+
// Retry on ETXTBSY (text file busy) error
|
|
195
|
+
if (err.code === 'ETXTBSY' && retries > 0) {
|
|
196
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
197
|
+
return installShellIntegration(binaryPath, retries - 1).then(resolve);
|
|
198
|
+
}
|
|
199
|
+
console.log(
|
|
200
|
+
' (Shell integration can be installed later with: gw install-shell)',
|
|
201
|
+
);
|
|
156
202
|
resolve();
|
|
157
203
|
});
|
|
158
204
|
});
|