天问

[Gogs1s]基于Gogs平台实现VSCode web预览代码功能开发教程

前言

标题有点绕口哈。作为开发者,VSCode这款IDE应该无人不知,无人不晓吧。但是很多人并不知道VSCode还有一个Web版本,也就是在浏览器中在线编码。要实现在线编码就需要无数强大的服务器集群来支撑,而构建一个VSCode for web来预览代码,则像普通web网站一样,所有用户公用一个服务器资源--------------------引

Gogs1s

在github上已经有前辈开源了一个非常火的项目Github1s,用户在检索github优秀项目的时候,通常做法是把代码clone到本地,然后打开VSCode浏览代码。而有了Github1s,只需要把github域名改成github1s即可在线预览github代码。举例:

github项目:https://github.com/microsoft/vscode/

预览代码:https://github1s.com/microsoft/vscode/

基于此项目,我便提出了ISSUE给作者,希望支持Gogs系统:

此外,近期我自己抽空实现了基于Gogs平台的 VSCode for web 的集成开发,为了表示对Github1s项目的尊敬,本项目命名为 Gogs1s,并记录一下踩的坑。

对gogs平台的改造

修改 Gogs 的模板文件 templates/repo/home.tmpl,嵌入一个 Gogs1s 的链接:

<a class="ui basic icon button poping up clipboard" href="http://code.git.yoqi.me{{.RepoLink}}" target="_blank">
							<i class="octicon octicon-file-submodule"></i>
							<img src="/img/hot.svg" style="position: absolute;height: 10px;width: 10px;top: 0px;right: 3px;">
						</a>

效果如下:

当点击这个按钮,即可打开 Gogs1s 预览当前项目的源码:

Gogs1s开发

要想开发 Gogs1s,首先需要了解 VSCode 的启动流程,以及 VSCode 虚拟文件系统。当然还需要了解 VSCode 的编译,运行。这些可以查看我以往的文章。

项目结构主要有一下三部分:

(1)src/vs 里面是对 VSCode 源码的更改:

workbench.ts 添加一个 IHomeIndicator ,这样可以跳转到Gogs项目,在Gogs项目点击预览按钮又可以回到 Gogs1s,这样体验就很好了:

添加上面代码后,就可以实现左上角的跳转到Gogs项目的按钮:

(2)修改自定义通知

当然,这个功能可以不需要。

(3)显示版本名称

这里我并没有基于最新版的VSCode来开发,而是:

git clone --depth 1 -b 1.52.1 https://github.com/microsoft/vscode.git vscode

在执行 yarn 安装依赖的时候,需要执行postinstall.sh, 这个时候 clone VSCode 源码,而我这边基于VSCode 1.52.1 官方版本开发:

这样当我们查看版本的时候就可以如下显示:

(4)设置 gogs1s 文件协议

当我们在浏览器访问 gogs1s://owner/repo 的时候,Gogs1s检测到这个协议的URL则会正确解析。

(5)gogs1s插件的开发

和 github1s 插件类似,通过复制 vscode.proposed.d.ts 实现 VSCode实验性的 API。这里主要是对文件的读,搜索等功能的实现。

具体实现在 gogs1sfs.ts中,定义了 File 和 Directory 两个类:

而 file 和 directory不再是本地文件,而是需要网络请求,通过 Gogs API 获取,为此定义一个统一的fetch方法:

fetch方法中,统一在 header 中设置 token,Gogs API 必须携带token,否则统一返回401无权限。

接下来在 api.ts中实现 readGitHubDirectoryreadGitHubFile 两个方法,从而可以从gogs平台获取文件和目录:

题外话-填坑指南

1、Gogs 是一个小型的代码管理系统,API很不完善。诸如分支,Tag,验证Token等都没有相应的API,要体验更多功能,只能使用 Gitea了。

2、目前 readGitHubDirectory 存在一个问题,只能获取根目录,而这个 API 问题一直存在:

3、为了消除黑人歧视,默认master分支改为main分支。这个在gogs上也有问题,当HEAD不是master分支的时候 API 获取结果为空。

4、gogs可能还有诸多bug。。。

项目全新架构

最近不断赶紧更新,Gogs1s迎来了新的版本。有要学习 VSCode开发的小伙伴可以在这篇博文中学点知识,以防掉坑。

(1)发布@jianboy/vscode-web 依赖

之前项目打包每次都需要编译 vscode,而这个十分耗时(十多分钟),但是大部分开发工作都是基于扩展开发,所以全新架构项目,抽离 vscode 编译部分代码为依赖,发布到 npm 托管:@jianboy/vscode-web - npm (npmjs.com)

之后每次构建gogs1s项目只花费1分钟,自动打包:

为了打包尽量小,这里排除了*.map文件:

      - name: build
        run: |
          yarn build
      - name: tar dist
        run: |
          tar -zcf dist.tar.gz --exclude='*.map' dist

      - name: artifact
        uses: actions/upload-artifact@v2
        with:
          name: ${{runner.OS}}-artifact
          path: |
            dist.tar.gz

构建的时候需要注意分步骤构建,原来是一个项目构建所有步骤,而现在是分为 gogs1s 项目和 @jianboy/vscode-web 两个项目:

gogs1s项目中,只需要构建 gogs1s 插件,并整合@jianboy/vscode-web依赖,这个在第二部分介绍。

@jianboy/vscode-web项目,只需要构建 vscode 和内置插件:

function main() {
	cd ${APP_ROOT}
	rsync -a resources/gulp-gogs1s.js lib/vscode
	cd lib/vscode

	yarn gulp compile-build
	yarn gulp optimize --gulpfile ./gulp-gogs1s.js
	yarn gulp minify --gulpfile ./gulp-gogs1s.js

	echo "build vscode done!"
}

(2)gogs1s插件更新

首先需要在 package.json 中添加依赖:

  "devDependencies": {
    "@jianboy/vscode-web": "1.1.10",
}

接下来,只需要构建gogs1s插件即可:

function main() {
	rm -rf "${APP_ROOT}/dist"
	cd "${APP_ROOT}/scripts"
	./build/build-gogs1s-extensions.sh
	./package.sh

	echo "all build done!"
}

注意package.sh,这个命令,打包需要把 vscode 内置插件和gogs1s插件整合:

function main() {
	cd "${APP_ROOT}/scripts"
	./package/copy-extensions.sh
	./package/copy-node_modules.sh
	./package/copy-resources.sh
	node ./package/generate-config.js

	echo "all copy done!"
}

执行 node ./package/generate-config.js 生成 extensions.json 文件。

(3)readGitHubDirectory 功能更新

之前介绍到 gogs 项目的api不完善。GET /repos/:owner/:repo/git/trees/:sha 这个接口一直只能获取根目录的几个文件夹,这个Bug一直存在。

多次查看API文档之后,我决定采用 GET /repos/:username/:reponame/contents/:path 这个接口来替代上述接口的功能。

git/trees 接口可以一次性返回整个项目的结构,有些项目比较大可能会拖垮系统(这也可能是作者不愿更新这个功能的原因吧)。

contents/:path 接口比较强大(乱),:path参数可以是一个目录,也可以是一个文件,目录则返回当前目录的所有文件;文件则返回文件的内容。

 //目录和文件同一个接口,目录返回list,接口返回object
export const readGitHubDirectory = (owner: string, repo: string, ref: string, path: string) => {
	let url = "";
	if (path == null || path == "/") {
		url = `https://git.yoqi.me/api/v1/repos/${owner}/${repo}/contents?ref=${ref}`;
	} else {
		url = `https://git.yoqi.me/api/v1/repos/${owner}/${repo}/contents/${path}?ref=${ref}`;
	}
	return fetch(url).catch(handleRequestError);
	//  return fetch(`https://git.yoqi.me/api/v1/repos/${owner}/${repo}/git/trees/${ref}`).catch(handleRequestError);
 };
 
 export const readGitHubFile = (owner: string, repo: string, ref: string, path: string) => {
	 let url = "";
	 if (path == null || path == "/") {
		 url = `https://git.yoqi.me/api/v1/repos/${owner}/${repo}/contents?ref=${ref}`;
	 } else {
		 url = `https://git.yoqi.me/api/v1/repos/${owner}/${repo}/contents/${path}?ref=${ref}`;
	 }
	 return fetch(url).catch(handleRequestError);
 };

所以有意思的就来了, readGitHubDirectory 和 readGitHubFile 两个方法调用同一个接口,方法体也一样,获取内容就返回即可。

当读取 readGitHubDirectory 结果后,需要保存 insertGitHubRESTEntryToDirectory:

const insertGitHubRESTEntryToDirectory = (githubEntry: GithubRESTEntry, baseDirectory: Directory) => {
	const pathParts = githubEntry.path.split('/').filter(Boolean);
	const fileName = pathParts.pop();
	let current = baseDirectory;
	if (!(current.entries || (current.entries = new Map<string, Entry>())).get(fileName)) {
		const entryUri = Uri.joinPath(current.uri, current.name);
		current.entries.set(fileName, (githubEntry.type === 'tree' || githubEntry.type === 'dir') ? new Directory(entryUri, fileName) : new File(entryUri, fileName));
	}
	const entry: Entry = current.entries.get(fileName);
	entry.sha = githubEntry.sha;
	(entry.type === FileType.File) && (entry.size = githubEntry.size!);
};

由于调用 contents/:path 这个接口返回的是一个数组,且返回的每个数组元素(目录中的文件)的type类型不再是”tree“,而是dir和file,所以为了兼容原始接口,需要扩充 GithubRESTEntry 中type的类型:

interface GithubRESTEntry {
	path: string;
	type: 'tree' | 'blob'  |'dir' | 'file';
	sha: string;
	size?: number;
}

(4)CDN资源加速

VSCode web 还是很大的,编译后项目有60M,2000多个文件:

考虑到服务器带宽比较小,初始打开的时候会很慢。为此将部分较大的js等资源发布到cdn上:

比如 static\vscode\vs\workbench\workbench.web.api.js 这个js压缩后有7.7M,每次都打开项目需要请求。而其他小文件可能加载很少,所以就没必要CDN托管。我这里只选取大于100K的文件,有68个。那么只需要把这个68个文件CDN加速,整个Gogs1s项目就体验很好了。

那么这里有一个细节,怎么按照原始目录抽离指定的68个文件呢?

第一步:先把大于100k的文件路径复制出来:

第二步:在所有目录前面增加 git add:

第三步:应该知道我要做什么了吧,首先对当前目录进行版本管理。复制上面的作为一个批处理命令,在cmd命令行中执行。

第四步:执行完之后,就可以使用小乌龟导出当前提交的文件,小乌龟会按照原始目录结构导出:

第五步:这里我以ftp的形式启动 阿里 OSS:

接下来使用 filezilla 等ftp工具上传即可:

由于我在已经把 OSS 结合 CDN了,所以上传后,就可以使用 CDN 请求了,这些就不是本文的内容了。

博客地址:http://blog.yoqi.me/?p=17522
扫我捐助哦
喜欢 7

这篇文章还没有评论

发表评论