Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitec4aa92

Browse files
fix: keep binary in same place, update if needed (#33)
Co-authored-by: Dean Sheather <dean@deansheather.com>
1 parentc2a2d8c commitec4aa92

File tree

2 files changed

+196
-107
lines changed

2 files changed

+196
-107
lines changed

‎package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
"build":"webpack",
8686
"watch":"webpack --watch",
8787
"package":"webpack --mode production --devtool hidden-source-map",
88+
"package:prerelease":"npx vsce package --pre-release",
8889
"lint":"eslint . --ext ts,md",
8990
"tsc:compile":"tsc",
9091
"tsc:watch":"tsc -w",

‎src/storage.ts

Lines changed: 195 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
importaxiosfrom"axios"
22
import{execFile}from"child_process"
33
import{getBuildInfo}from"coder/site/src/api/api"
4-
import{createWriteStream}from"fs"
4+
import*ascryptofrom"crypto"
5+
import{createWriteStream,createReadStream}from"fs"
56
import{ensureDir}from"fs-extra"
67
importfsfrom"fs/promises"
78
import{IncomingMessage}from"http"
@@ -73,39 +74,16 @@ export class Storage {
7374
// fetchBinary returns the path to a Coder binary.
7475
// The binary will be cached if a matching server version already exists.
7576
publicasyncfetchBinary():Promise<string|undefined>{
77+
awaitthis.cleanUpOldBinaries()
7678
constbaseURL=this.getURL()
7779
if(!baseURL){
7880
thrownewError("Must be logged in!")
7981
}
8082
constbaseURI=vscode.Uri.parse(baseURL)
8183

8284
constbuildInfo=awaitgetBuildInfo()
83-
constbinPath=this.binaryPath(buildInfo.version)
84-
constexists=awaitfs
85-
.stat(binPath)
86-
.then(()=>true)
87-
.catch(()=>false)
88-
if(exists){
89-
// Even if the file exists, it could be corrupted.
90-
// We run `coder version` to ensure the binary can be executed.
91-
this.output.appendLine(`Using cached binary:${binPath}`)
92-
constvalid=awaitnewPromise<boolean>((resolve)=>{
93-
try{
94-
execFile(binPath,["version"],(err)=>{
95-
if(err){
96-
this.output.appendLine("Check for binary corruption: "+err)
97-
}
98-
resolve(err===null)
99-
})
100-
}catch(ex){
101-
this.output.appendLine("The cached binary cannot be executed: "+ex)
102-
resolve(false)
103-
}
104-
})
105-
if(valid){
106-
returnbinPath
107-
}
108-
}
85+
constbinPath=this.binaryPath()
86+
constexists=awaitthis.checkBinaryExists(binPath)
10987
constos=goos()
11088
constarch=goarch()
11189
letbinName=`coder-${os}-${arch}`
@@ -114,106 +92,153 @@ export class Storage {
11492
binName+=".exe"
11593
}
11694
constcontroller=newAbortController()
95+
96+
if(exists){
97+
this.output.appendLine(`Found existing binary:${binPath}`)
98+
constvalid=awaitthis.checkBinaryValid(binPath)
99+
if(!valid){
100+
constremoved=awaitthis.rmBinary(binPath)
101+
if(!removed){
102+
vscode.window.showErrorMessage("Failed to remove existing binary!")
103+
returnundefined
104+
}
105+
}
106+
}
107+
constetag=awaitthis.getBinaryETag()
108+
this.output.appendLine(`Using binName:${binName}`)
109+
this.output.appendLine(`Using binPath:${binPath}`)
110+
this.output.appendLine(`Using ETag:${etag}`)
111+
117112
constresp=awaitaxios.get("/bin/"+binName,{
118113
signal:controller.signal,
119114
baseURL:baseURL,
120115
responseType:"stream",
121116
headers:{
122117
"Accept-Encoding":"gzip",
118+
"If-None-Match":`"${etag}"`,
123119
},
124120
decompress:true,
125121
// Ignore all errors so we can catch a 404!
126122
validateStatus:()=>true,
127123
})
128-
if(resp.status===404){
129-
vscode.window
130-
.showErrorMessage(
131-
"Coder isn't supported for your platform. Please open an issue, we'd love to support it!",
132-
"Open an Issue",
133-
)
134-
.then((value)=>{
135-
if(!value){
136-
return
137-
}
138-
constparams=newURLSearchParams({
139-
title:`Support the \`${os}-${arch}\` platform`,
140-
body:`I'd like to use the \`${os}-${arch}\` architecture with the VS Code extension.`,
141-
})
142-
consturi=vscode.Uri.parse(`https://github.com/coder/vscode-coder/issues/new?`+params.toString())
143-
vscode.env.openExternal(uri)
144-
})
145-
return
146-
}
147-
if(resp.status!==200){
148-
vscode.window.showErrorMessage("Failed to fetch the Coder binary: "+resp.statusText)
149-
return
150-
}
124+
this.output.appendLine("Response status code: "+resp.status)
151125

152-
constcontentLength=Number.parseInt(resp.headers["content-length"])
126+
switch(resp.status){
127+
case200:{
128+
constcontentLength=Number.parseInt(resp.headers["content-length"])
153129

154-
// Ensure the binary directory exists!
155-
awaitfs.mkdir(path.dirname(binPath),{recursive:true})
130+
// Ensure the binary directory exists!
131+
awaitfs.mkdir(path.dirname(binPath),{recursive:true})
132+
consttempFile=binPath+".temp-"+Math.random().toString(36).substring(8)
156133

157-
constcompleted=awaitvscode.window.withProgress<boolean>(
158-
{
159-
location:vscode.ProgressLocation.Notification,
160-
title:`Downloading the latest binary (${buildInfo.version} from${baseURI.authority})`,
161-
cancellable:true,
162-
},
163-
async(progress,token)=>{
164-
constreadStream=resp.dataasIncomingMessage
165-
letcancelled=false
166-
token.onCancellationRequested(()=>{
167-
controller.abort()
168-
readStream.destroy()
169-
cancelled=true
170-
})
134+
constcompleted=awaitvscode.window.withProgress<boolean>(
135+
{
136+
location:vscode.ProgressLocation.Notification,
137+
title:`Downloading the latest binary (${buildInfo.version} from${baseURI.authority})`,
138+
cancellable:true,
139+
},
140+
async(progress,token)=>{
141+
constreadStream=resp.dataasIncomingMessage
142+
letcancelled=false
143+
token.onCancellationRequested(()=>{
144+
controller.abort()
145+
readStream.destroy()
146+
cancelled=true
147+
})
171148

172-
letcontentLengthPretty=""
173-
// Reverse proxies might not always send a content length!
174-
if(!Number.isNaN(contentLength)){
175-
contentLengthPretty=" / "+prettyBytes(contentLength)
176-
}
149+
letcontentLengthPretty=""
150+
// Reverse proxies might not always send a content length!
151+
if(!Number.isNaN(contentLength)){
152+
contentLengthPretty=" / "+prettyBytes(contentLength)
153+
}
177154

178-
constwriteStream=createWriteStream(binPath,{
179-
autoClose:true,
180-
mode:0o755,
181-
})
182-
letwritten=0
183-
readStream.on("data",(buffer:Buffer)=>{
184-
writeStream.write(buffer,()=>{
185-
written+=buffer.byteLength
186-
progress.report({
187-
message:`${prettyBytes(written)}${contentLengthPretty}`,
188-
increment:(buffer.byteLength/contentLength)*100,
155+
constwriteStream=createWriteStream(tempFile,{
156+
autoClose:true,
157+
mode:0o755,
189158
})
190-
})
159+
letwritten=0
160+
readStream.on("data",(buffer:Buffer)=>{
161+
writeStream.write(buffer,()=>{
162+
written+=buffer.byteLength
163+
progress.report({
164+
message:`${prettyBytes(written)}${contentLengthPretty}`,
165+
increment:(buffer.byteLength/contentLength)*100,
166+
})
167+
})
168+
})
169+
try{
170+
awaitnewPromise<void>((resolve,reject)=>{
171+
readStream.on("error",(err)=>{
172+
reject(err)
173+
})
174+
readStream.on("close",()=>{
175+
if(cancelled){
176+
returnreject()
177+
}
178+
writeStream.close()
179+
resolve()
180+
})
181+
})
182+
returntrue
183+
}catch(ex){
184+
returnfalse
185+
}
186+
},
187+
)
188+
if(!completed){
189+
return
190+
}
191+
this.output.appendLine(`Downloaded binary:${binPath}`)
192+
constoldBinPath=binPath+".old-"+Math.random().toString(36).substring(8)
193+
awaitfs.rename(binPath,oldBinPath).catch(()=>{
194+
this.output.appendLine(`Warning: failed to rename${binPath} to${oldBinPath}`)
191195
})
192-
try{
193-
awaitnewPromise<void>((resolve,reject)=>{
194-
readStream.on("error",(err)=>{
195-
reject(err)
196+
awaitfs.rename(tempFile,binPath)
197+
awaitfs.rm(oldBinPath,{force:true}).catch((error)=>{
198+
this.output.appendLine(`Warning: failed to remove old binary:${error}`)
199+
})
200+
returnbinPath
201+
}
202+
case304:{
203+
this.output.appendLine(`Using cached binary:${binPath}`)
204+
returnbinPath
205+
}
206+
case404:{
207+
vscode.window
208+
.showErrorMessage(
209+
"Coder isn't supported for your platform. Please open an issue, we'd love to support it!",
210+
"Open an Issue",
211+
)
212+
.then((value)=>{
213+
if(!value){
214+
return
215+
}
216+
constparams=newURLSearchParams({
217+
title:`Support the \`${os}-${arch}\` platform`,
218+
body:`I'd like to use the \`${os}-${arch}\` architecture with the VS Code extension.`,
196219
})
197-
readStream.on("close",()=>{
198-
if(cancelled){
199-
returnreject()
200-
}
201-
writeStream.close()
202-
resolve()
220+
consturi=vscode.Uri.parse(`https://github.com/coder/vscode-coder/issues/new?`+params.toString())
221+
vscode.env.openExternal(uri)
222+
})
223+
returnundefined
224+
}
225+
default:{
226+
vscode.window
227+
.showErrorMessage("Failed to download binary. Please open an issue.","Open an Issue")
228+
.then((value)=>{
229+
if(!value){
230+
return
231+
}
232+
constparams=newURLSearchParams({
233+
title:`Failed to download binary on \`${os}-${arch}\``,
234+
body:`Received status code \`${resp.status}\` when downloading the binary.`,
203235
})
236+
consturi=vscode.Uri.parse(`https://github.com/coder/vscode-coder/issues/new?`+params.toString())
237+
vscode.env.openExternal(uri)
204238
})
205-
returntrue
206-
}catch(ex){
207-
returnfalse
208-
}
209-
},
210-
)
211-
if(!completed){
212-
return
239+
returnundefined
240+
}
213241
}
214-
215-
this.output.appendLine(`Downloaded binary:${binPath}`)
216-
returnbinPath
217242
}
218243

219244
// getBinaryCachePath returns the path where binaries are cached.
@@ -240,6 +265,23 @@ export class Storage {
240265
returnpath.join(this.globalStorageUri.fsPath,"url")
241266
}
242267

268+
publicgetBinaryETag():Promise<string>{
269+
consthash=crypto.createHash("sha1")
270+
conststream=createReadStream(this.binaryPath())
271+
returnnewPromise((resolve,reject)=>{
272+
stream.on("end",()=>{
273+
hash.end()
274+
resolve(hash.digest("hex"))
275+
})
276+
stream.on("error",(err)=>{
277+
reject(err)
278+
})
279+
stream.on("data",(chunk)=>{
280+
hash.update(chunk)
281+
})
282+
})
283+
}
284+
243285
privateappDataDir():string{
244286
switch(process.platform){
245287
case"darwin":
@@ -264,16 +306,62 @@ export class Storage {
264306
}
265307
}
266308

267-
privatebinaryPath(version:string):string{
309+
privateasynccleanUpOldBinaries():Promise<void>{
310+
constbinPath=this.binaryPath()
311+
constbinDir=path.dirname(binPath)
312+
constfiles=awaitfs.readdir(binDir)
313+
for(constfileoffiles){
314+
constfileName=path.basename(file)
315+
if(fileName.includes(".old-")){
316+
try{
317+
awaitfs.rm(path.join(binDir,file),{force:true})
318+
}catch(error){
319+
this.output.appendLine(`Warning: failed to remove${fileName}. Error:${error}`)
320+
}
321+
}
322+
}
323+
}
324+
325+
privatebinaryPath():string{
268326
constos=goos()
269327
constarch=goarch()
270-
letbinPath=path.join(this.getBinaryCachePath(),`coder-${os}-${arch}-${version}`)
328+
letbinPath=path.join(this.getBinaryCachePath(),`coder-${os}-${arch}`)
271329
if(os==="windows"){
272330
binPath+=".exe"
273331
}
274332
returnbinPath
275333
}
276334

335+
privateasynccheckBinaryExists(binPath:string):Promise<boolean>{
336+
returnawaitfs
337+
.stat(binPath)
338+
.then(()=>true)
339+
.catch(()=>false)
340+
}
341+
342+
privateasyncrmBinary(binPath:string):Promise<boolean>{
343+
returnawaitfs
344+
.rm(binPath,{force:true})
345+
.then(()=>true)
346+
.catch(()=>false)
347+
}
348+
349+
privateasynccheckBinaryValid(binPath:string):Promise<boolean>{
350+
returnawaitnewPromise<boolean>((resolve)=>{
351+
try{
352+
execFile(binPath,["version"],(err)=>{
353+
if(err){
354+
this.output.appendLine("Check for binary corruption: "+err)
355+
}
356+
resolve(err===null)
357+
})
358+
}catch(ex){
359+
this.output.appendLine("The cached binary cannot be executed: "+ex)
360+
resolve(false)
361+
}
362+
})
363+
}
364+
277365
privateasyncupdateSessionToken(){
278366
consttoken=awaitthis.getSessionToken()
279367
if(token){

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp