Skip to content

Commit 4573dd8

Browse files
committed
extension release bootstrapping and updating
1 parent 8fab7bd commit 4573dd8

File tree

8 files changed

+209
-49
lines changed

8 files changed

+209
-49
lines changed

client/package.json

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
{
22
"name": "vscode-mc-shader-client",
3-
"description": "A Visual Studio Code extension for linting/etc Minecraft GLSL Shaders",
4-
"version": "0.0.1",
5-
"publisher": "Noah Santschi-Cooney (Strum355)",
6-
"author": "Noah Santschi-Cooney (Strum355)",
7-
"license": "MIT",
8-
"repository": {
9-
"type": "git",
10-
"url": "https://github.com/Strum355/vscode-mc-shader"
11-
},
12-
"engines": {
13-
"vscode": "^1.43.0"
14-
},
153
"scripts": {
164
"compile": "tsc -p ./"
175
},

client/src/extension.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1+
import { mkdirSync, promises as fs } from 'fs'
12
import * as vscode from 'vscode'
23
import * as lsp from 'vscode-languageclient'
34
import * as commands from './commands'
45
import { log } from './log'
56
import { LanguageClient } from './lspClient'
7+
import { download, getReleaseInfo } from './net'
8+
import { PersistentState } from './persistent_state'
9+
import * as path from 'path'
10+
11+
const platforms: { [key: string]: string } = {
12+
'x64 win32': 'x86_64-pc-windows-gnu',
13+
'x64 linux': 'x86_64-unknown-linux-gnu',
14+
'x64 darwin': 'x86_64-apple-darwin',
15+
}
616

717
export class Extension {
818
private statusBarItem: vscode.StatusBarItem | null = null
919
private extensionContext: vscode.ExtensionContext | null = null
1020
private client: lsp.LanguageClient
21+
private state: PersistentState
22+
23+
readonly extensionID = 'strum355.vscode-mc-shader'
24+
25+
readonly package: {
26+
version: string
27+
} = vscode.extensions.getExtension(this.extensionID)!.packageJSON;
1128

1229
public get context(): vscode.ExtensionContext {
1330
return this.extensionContext
@@ -19,6 +36,9 @@ export class Extension {
1936

2037
public activate = async (context: vscode.ExtensionContext) => {
2138
this.extensionContext = context
39+
this.state = new PersistentState(context.globalState)
40+
41+
await this.bootstrap()
2242

2343
this.registerCommand('graphDot', commands.generateGraphDot)
2444
this.registerCommand('restart', commands.restartExtension)
@@ -54,6 +74,40 @@ export class Extension {
5474
public clearStatus = () => {
5575
this.statusBarItem?.dispose()
5676
}
77+
78+
private bootstrap = async () => {
79+
mkdirSync(this.extensionContext.globalStoragePath, { recursive: true })
80+
81+
const dest = path.join(this.extensionContext.globalStoragePath, 'mcshader-lsp' + (process.platform === 'win32' ? '.exe' : ''))
82+
const exists = await fs.stat(dest).then(() => true, () => false)
83+
if (!exists) await this.state.updateServerVersion(undefined)
84+
85+
this.state.updateServerVersion('borger')
86+
87+
const release = await getReleaseInfo(this.package.version)
88+
89+
const platform = platforms[`${process.arch} ${process.platform}`]
90+
if (platform === undefined) {
91+
vscode.window.showErrorMessage('Unfortunately we don\'t ship binaries for your platform yet.')
92+
return
93+
}
94+
95+
if (release.tag_name === this.state.serverVersion) return
96+
97+
const artifact = release.assets.find(artifact => artifact.name === `mcshader-lsp-${platform}`)
98+
99+
const userResponse = await vscode.window.showInformationMessage(
100+
this.state.serverVersion == undefined ?
101+
`Language server version ${this.package.version} is not installed.` :
102+
`An update is available. Upgrade from ${this.state.serverVersion} to ${release.tag_name}?`,
103+
'Download now'
104+
)
105+
if (userResponse !== 'Download now') return
106+
107+
await download(artifact.browser_download_url, dest)
108+
109+
this.state.updateServerVersion(release.tag_name)
110+
}
57111
}
58112

59113
export const activate = new Extension().activate

client/src/lspClient.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ export class LanguageClient extends lsp.LanguageClient {
1010

1111
constructor(ext: Extension) {
1212
super('vscode-mc-shader', 'VSCode MC Shader', {
13-
command: ext.context.asAbsolutePath(path.join('server', 'target', 'debug', 'vscode-mc-shader')),
13+
command: process.env['MCSHADER_DEBUG'] ?
14+
ext.context.asAbsolutePath(path.join('server', 'target', 'debug', 'vscode-mc-shader')) :
15+
path.join(ext.context.globalStoragePath, 'mcshader-lsp')
1416
}, {
1517
documentSelector: [{scheme: 'file', language: 'glsl'}],
1618
outputChannel: lspOutputChannel,
1719
synchronize: {
1820
configurationSection: 'mcglsl',
19-
fileEvents: workspace.createFileSystemWatcher('**/*.{fsh,gsh,vsh,glsl}')
21+
fileEvents: workspace.createFileSystemWatcher('**/*.{fsh,gsh,vsh,glsl,inc}')
2022
},
2123
})
2224
this.extension = ext

client/src/net.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { log } from './log'
2+
import fetch from 'node-fetch'
3+
import * as vscode from 'vscode'
4+
import * as stream from 'stream'
5+
import * as fs from 'fs'
6+
import * as util from 'util'
7+
8+
const pipeline = util.promisify(stream.pipeline)
9+
10+
interface GithubRelease {
11+
tag_name: string;
12+
assets: Array<{
13+
name: string;
14+
browser_download_url: string;
15+
}>;
16+
}
17+
18+
export async function getReleaseInfo(releaseTag: string): Promise<GithubRelease> {
19+
const response = await fetch(`https://api.github.com/repos/strum355/mcshader-lsp/releases/tags/${releaseTag}`, {
20+
headers: {Accept: 'application/vnd.github.v3+json'}
21+
})
22+
23+
const isRelease = (obj: unknown): obj is GithubRelease => {
24+
return obj != null && typeof obj === 'object'
25+
&& typeof (obj as GithubRelease).tag_name === 'string'
26+
&& Array.isArray((obj as GithubRelease).assets)
27+
&& (obj as GithubRelease).assets.every((a) => typeof a.name === 'string' && typeof a.browser_download_url === 'string')
28+
}
29+
30+
const json = await response.json()
31+
if(!isRelease(json)) {
32+
throw new TypeError('Received malformed request from Github Release API')
33+
}
34+
return json
35+
}
36+
37+
export async function download(url: string, downloadDest: string) {
38+
await vscode.window.withProgress(
39+
{
40+
location: vscode.ProgressLocation.Notification,
41+
cancellable: false,
42+
title: `Downloading ${url}`
43+
},
44+
async (progress, _) => {
45+
let lastPercentage = 0
46+
await downloadFile(url, downloadDest, (readBytes, totalBytes) => {
47+
const newPercentage = Math.round((readBytes / totalBytes) * 100)
48+
if (newPercentage !== lastPercentage) {
49+
progress.report({
50+
message: `${newPercentage.toFixed(0)}%`,
51+
increment: newPercentage - lastPercentage
52+
})
53+
54+
lastPercentage = newPercentage
55+
}
56+
})
57+
}
58+
)
59+
}
60+
61+
async function downloadFile(
62+
url: string,
63+
destFilePath: fs.PathLike,
64+
onProgress: (readBytes: number, totalBytes: number) => void
65+
): Promise<void> {
66+
const res = await fetch(url)
67+
if (!res.ok) {
68+
log.error(res.status, 'while downloading file from', url)
69+
log.error({ body: await res.text(), headers: res.headers })
70+
throw new Error(`Got response ${res.status} when trying to download ${url}.`)
71+
}
72+
73+
const totalBytes = Number(res.headers.get('content-length'))
74+
75+
log.debug('downloading file of', totalBytes, 'bytes size from', url, 'to', destFilePath)
76+
77+
let readBytes = 0
78+
res.body.on('data', (chunk: Buffer) => {
79+
readBytes += chunk.length
80+
onProgress(readBytes, totalBytes)
81+
})
82+
83+
const destFileStream = fs.createWriteStream(destFilePath, { mode: 0o755 })
84+
85+
await pipeline(res.body, destFileStream)
86+
87+
// Don't apply the workaround in fixed versions of nodejs, since the process
88+
// freezes on them, the process waits for no-longer emitted `close` event.
89+
// The fix was applied in commit 7eed9d6bcc in v13.11.0
90+
// See the nodejs changelog:
91+
// https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V13.md
92+
const [, major, minor] = /v(\d+)\.(\d+)\.(\d+)/.exec(process.version)!
93+
if (+major > 13 || (+major === 13 && +minor >= 11)) return
94+
95+
await new Promise<void>(resolve => {
96+
destFileStream.on('close', resolve)
97+
destFileStream.destroy()
98+
// This workaround is awaiting to be removed when vscode moves to newer nodejs version:
99+
// https://github.com/rust-analyzer/rust-analyzer/issues/3167
100+
})
101+
}

client/src/persistent_state.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Memento } from 'vscode'
2+
import { log } from './log'
3+
4+
export class PersistentState {
5+
constructor(private readonly state: Memento) {
6+
const { serverVersion } = this
7+
log.info('working with state', { serverVersion })
8+
}
9+
10+
get serverVersion(): string | undefined {
11+
return this.state.get('serverVersion')
12+
}
13+
async updateServerVersion(value: string | undefined) {
14+
await this.state.update('serverVersion', value)
15+
}
16+
}

package.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
"onLanguage:glsl",
2222
"workspaceContains:**/*.fsh",
2323
"workspaceContains:**/*.vsh",
24-
"workspaceContains:**/*.gsh",
25-
"workspaceContains:**/*.glsl"
24+
"workspaceContains:**/*.gsh"
2625
],
2726
"extensionDependencies": [
2827
"slevesque.shader"
@@ -31,17 +30,17 @@
3130
"contributes": {
3231
"commands": [
3332
{
34-
"command": "mcshader.graphDot",
33+
"command": "mcglsl.graphDot",
3534
"title": "Generate Graphviz DOT dependency graph",
3635
"category": "Minecraft Shader"
3736
},
3837
{
39-
"command": "mcshader.restart",
38+
"command": "mcglsl.restart",
4039
"title": "Restart Language Server",
4140
"category": "Minecraft Shader"
4241
},
4342
{
44-
"command": "mcshader.virtualMerge",
43+
"command": "mcglsl.virtualMerge",
4544
"title": "Show flattened file",
4645
"category": "Minecraft Shader"
4746
}
@@ -69,7 +68,7 @@
6968
"vscode:prepublish": "npm run compile && cd client && rollup -c",
7069
"compile": "tsc -b",
7170
"package": "vsce package -o vscode-mc-shader.vsix",
72-
"watch": "concurrently \"tsc -b -w\" \"cd server && cargo watch -x build\"",
71+
"watch": "concurrently \"tsc -b -w\" \"cd server && MCSHADER_DEBUG=true cargo watch -x build\"",
7372
"postinstall": "cd client && npm install",
7473
"lint": "eslint 'client/**/*.ts' --max-warnings 1",
7574
"fix": "eslint 'client/**/*.ts' --fix"

server/Cargo.lock

Lines changed: 28 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "vscode-mc-shader"
2+
name = "mcshader-lsp"
33
version = "0.1.0"
44
authors = ["Noah Santschi-Cooney <noah@santschi-cooney.ch>"]
55
edition = "2018"

0 commit comments

Comments
 (0)