Skip to content

Commit 592702b

Browse files
author
winjo
committed
feat: 优化 git 文件写系统
1 parent 8a792e4 commit 592702b

16 files changed

Lines changed: 272 additions & 141 deletions

File tree

packages/alex/src/api/createApp.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
IAppOpts,
88
STORAGE_NAME,
99
} from '@alipay/alex-core';
10-
import { SlotRenderer, SlotLocation, IAppRenderer } from '@ali/ide-core-browser';
10+
import { SlotRenderer, SlotLocation, IAppRenderer, FILES_DEFAULTS } from '@ali/ide-core-browser';
1111
import { BoxPanel, SplitPanel } from '@ali/ide-core-browser/lib/components';
1212
import { IThemeService } from '@ali/ide-theme/lib/common';
1313
import '@ali/ide-core-browser/lib/style/index.less';
@@ -40,6 +40,11 @@ const getDefaultAppConfig = (): IAppOpts => ({
4040
'editor.quickSuggestionsDelay': 10,
4141
'editor.quickSuggestionsMaxCount': 50,
4242
'settings.userBeforeWorkspace': true,
43+
'files.exclude': {
44+
...FILES_DEFAULTS.filesExclude,
45+
// browserfs OverlayFS 用来记录删除的文件
46+
'**/.deletedFiles.log': true,
47+
},
4348
},
4449
layoutConfig,
4550
layoutComponent: LayoutComponent,

packages/alex/src/integration/startup/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom';
3-
import { AppRenderer, renderApp, IAppInstance } from '../..';
43
import { GitFileSchemeModule } from '@alipay/alex-git';
4+
import { IAppInstance, AppRenderer } from '../..';
5+
import * as Alex from '../..';
56
import { StartupModule } from './startup.module';
67
import './languages';
78
import SarifViewer from '../../../extensions/cloud-ide-ext.sarif-viewer';
@@ -11,6 +12,8 @@ import json from '../../../extensions/alex.json-language-features-worker';
1112
import markdown from '../../../extensions/alex.markdown-language-features-worker';
1213
import typescript from '../../../extensions/alex.typescript-language-features-worker';
1314

15+
(window as any).alex = Alex;
16+
1417
const query = location.search
1518
.slice(1)
1619
.split('&')
@@ -34,6 +37,7 @@ ReactDOM.render(
3437
}}
3538
runtimeConfig={{
3639
git: {
40+
platform: 'antcode',
3741
baseURL: '/code-service',
3842
project,
3943
branch: query.branch,

packages/core/src/common/constant.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export const WORKSPACE_ROOT = '/workspace';
1212

1313
export const GIT_ROOT = '/git';
1414

15+
export const IDB_ROOT = '/idb';
16+
1517
// 全局数据存储目录
1618
export const STORAGE_NAME = '.cloudide';
1719

packages/core/src/common/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import type * as vscode from 'vscode';
2-
import { Event } from '@ali/ide-core-common';
31
import { BrowserFS, FileSystemConfiguration } from '../server/node';
42

53
export { AppConfig } from '@ali/ide-core-browser';
@@ -20,9 +18,12 @@ export interface FileIndex {
2018
}
2119

2220
interface GitConfig {
21+
// 平台
22+
platform: string;
23+
// host
2324
baseURL: string;
2425
// 项目 IDE
25-
projectId?: number;
26+
projectId?: number | string;
2627
// 项目名称 group/repository
2728
project: string;
2829
// 分支

packages/core/src/server/core/util.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ const { createFileSystem, FileSystem, initialize } = BrowserFS;
66

77
export const initializeRootFileSystem = async () => {
88
// TODO: /home 采用 localStorage 作为文件系统是不是更好点,理论数据量不大的话没必要用 IndexedDB
9-
const [tmp, workspace, home] = await Promise.all([
10-
createFileSystem(FileSystem.InMemory, {}),
9+
const [tmp, home] = await Promise.all([
1110
createFileSystem(FileSystem.InMemory, {}),
1211
createFileSystem(FileSystem.IndexedDB, {
1312
storeName: HOME_IDB_NAME,
@@ -16,7 +15,6 @@ export const initializeRootFileSystem = async () => {
1615
const mountfs = await createFileSystem(FileSystem.MountableFileSystem, {
1716
[TMP_ROOT]: tmp,
1817
[HOME_ROOT]: home,
19-
[WORKSPACE_ROOT]: workspace,
2018
});
2119
initialize(mountfs);
2220
return mountfs as RootFS;

packages/core/src/server/node/bfs/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import './patch';
12
import fs from 'browserfs/dist/node/core/node_fs';
23
import { checkOptions } from 'browserfs/dist/node/core/util';
34
import {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// @ts-nocheck
2+
// patch
3+
// 修复 stat 错误
4+
5+
import { BFSCallback } from 'browserfs/dist/node/core/file_system';
6+
import { ApiError, ErrorCode } from 'browserfs/dist/node/core/api_error';
7+
import Stats from 'browserfs/dist/node/core/node_fs_stats';
8+
import { UnlockedOverlayFS } from 'browserfs/dist/node/backend/OverlayFS';
9+
10+
function makeModeWritable(mode: number): number {
11+
return 0o222 | mode;
12+
}
13+
14+
UnlockedOverlayFS.prototype.stat = function (p: string, isLstat: boolean, cb: BFSCallback<Stats>) {
15+
if (!this.checkInitAsync(cb)) {
16+
return;
17+
}
18+
this._writable.stat(p, isLstat, (err: ApiError, stat?: Stats) => {
19+
if (err && err.errno === ErrorCode.ENOENT) {
20+
if (this._deletedFiles[p]) {
21+
cb(ApiError.ENOENT(p));
22+
return;
23+
}
24+
this._readable.stat(p, isLstat, (err: ApiError, stat?: Stats) => {
25+
if (stat) {
26+
stat = stat.clone();
27+
stat.mode = makeModeWritable(stat.mode);
28+
}
29+
cb(err, stat);
30+
});
31+
} else {
32+
cb(err, stat);
33+
}
34+
});
35+
};

packages/git/src/filesystem/configure.ts

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BrowserFS, WORKSPACE_IDB_NAME } from '@alipay/alex-core';
22
import { IGitAPIService } from '../types';
3+
import { GitModelService } from '../git-model.service';
34

45
const stripSlash = (p: string) => {
56
if (p[0] === '/') {
@@ -10,29 +11,50 @@ const stripSlash = (p: string) => {
1011

1112
type Callback<T = any> = (err: Error | null, rv?: T) => void;
1213

13-
const configureFileSystem = async (apiService: IGitAPIService) => {
14+
const configureFileSystem = async (model: GitModelService, api: IGitAPIService) => {
1415
const requestByMethod = (name: 'getTreeEntry' | 'getTree' | 'getBlob' | 'getBlobSize') => (
1516
p: string,
1617
cb: Callback<any>
1718
) => {
1819
p = stripSlash(p);
19-
(apiService[name](p) as Promise<any>).then((res) => cb(null, res)).catch((err) => cb(err));
20+
(api[name](p) as Promise<any>).then((res) => cb(null, res)).catch((err) => cb(err));
2021
};
2122

22-
const gitFileSystem = await BrowserFS.createFileSystem(BrowserFS.FileSystem.CodeHost, {
23-
requestStat: requestByMethod('getTreeEntry'),
24-
requestDir: requestByMethod('getTree'),
25-
requestFile: requestByMethod('getBlob'),
26-
requestFileSize: requestByMethod('getBlobSize'),
23+
const {
24+
createFileSystem,
25+
FileSystem: { CodeHost, OverlayFS, FolderAdapter, IndexedDB },
26+
} = BrowserFS;
27+
28+
const [gitFileSystem, idbFileSystem] = await Promise.all([
29+
createFileSystem(CodeHost, {
30+
requestStat: requestByMethod('getTreeEntry'),
31+
requestDir: requestByMethod('getTree'),
32+
requestFile: requestByMethod('getBlob'),
33+
requestFileSize: requestByMethod('getBlobSize'),
34+
}),
35+
createFileSystem(IndexedDB, { storeName: WORKSPACE_IDB_NAME }),
36+
]);
37+
const folderSystem = await createFileSystem(FolderAdapter, {
38+
wrapped: idbFileSystem,
39+
folder: `/${model.platform}-${model.projectId}-${model.commit}`,
2740
});
28-
const overlayFileSystem = await BrowserFS.createFileSystem(BrowserFS.FileSystem.OverlayFS, {
41+
await new Promise<void>((resolve, reject) => {
42+
(folderSystem as InstanceType<typeof FolderAdapter>).initialize((err) => {
43+
if (err) {
44+
reject(err);
45+
} else {
46+
resolve();
47+
}
48+
});
49+
});
50+
const overlayFileSystem = await createFileSystem(OverlayFS, {
2951
readable: gitFileSystem,
30-
writable: await BrowserFS.createFileSystem(BrowserFS.FileSystem.IndexedDB, {
31-
storeName: WORKSPACE_IDB_NAME,
32-
}),
52+
writable: folderSystem,
3353
});
3454
return {
3555
gitFileSystem,
56+
idbFileSystem,
57+
folderSystem,
3658
overlayFileSystem,
3759
};
3860
};

packages/git/src/git-api.service.ts

Lines changed: 26 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,58 @@
1-
import { Injectable } from '@ali/common-di';
2-
import { Deferred } from '@ali/ide-core-common';
1+
import { Injectable, Autowired } from '@ali/common-di';
2+
import { GitModelService } from './git-model.service';
33
import { request, API } from './request';
44
import { IGitAPIService } from './types';
55

6-
// TODO: 适配 gitlab 接口
7-
// TODO: 放到集成侧去做?AntCode 上如果也用 IDE,那么也会造成重复
86
@Injectable()
97
export class GitAPIService implements IGitAPIService {
10-
public platform = 'antcode';
11-
private initialized = new Deferred();
12-
public project: string;
13-
public projectId: number;
14-
public commit: string;
15-
public branch: string;
16-
17-
async initProject(config: {
18-
project: string;
19-
projectId?: number;
20-
commit?: string;
21-
branch?: string;
22-
}) {
23-
this.project = config.project;
24-
if (config.projectId) {
25-
this.projectId = config.projectId;
26-
}
27-
if (config.commit) {
28-
this.commit = config.commit;
29-
}
30-
if (config.branch) {
31-
this.branch = config.branch;
32-
}
33-
// 唯一确定一个项目
34-
if (this.projectId && this.commit) {
35-
return;
36-
}
37-
if (!this.projectId || !this.branch) {
38-
const projectInfo = await this.getProjectInfo();
39-
this.projectId = projectInfo.id;
40-
if (!this.branch) {
41-
this.branch = projectInfo.default_branch;
42-
}
43-
}
44-
if (!this.commit) {
45-
const commitInfo = await this.getCommit(this.branch);
46-
this.commit = commitInfo.id;
47-
}
48-
this.initialized.resolve();
49-
}
50-
51-
get ready() {
52-
return this.initialized.promise;
53-
}
8+
@Autowired()
9+
gitModel: GitModelService;
5410

5511
getProjectInfo() {
5612
return request<API.ResponseGetProjectById>(
57-
`/api/v3/projects/${encodeURIComponent(this.project)}`
13+
`/api/v3/projects/${encodeURIComponent(this.gitModel.project)}`
5814
);
5915
}
6016

61-
// 根据分支获取最新的 commit
6217
async getCommit(ref: string) {
6318
return request<API.ResponseGetCommit>(
64-
`/api/v3/projects/${this.projectId}/repository/commits/${ref}`
19+
`/api/v3/projects/${this.gitModel.projectId}/repository/commits/${ref}`
6520
);
6621
}
6722

68-
// 获取文件节点
6923
async getTreeEntry(path: string) {
70-
await this.ready;
24+
await this.gitModel.ready;
7125
return request<API.ResponseGetTreeEntry>(
72-
`/api/v3/projects/${this.projectId}/repository/tree_entry`,
26+
`/api/v3/projects/${this.gitModel.projectId}/repository/tree_entry`,
7327
{
7428
params: {
75-
ref_name: this.commit,
29+
ref_name: this.gitModel.commit,
7630
path,
7731
},
7832
}
7933
);
8034
}
8135

82-
// 根据 commit 和 path 获取 tree
36+
/**
37+
* 根据 commit 和 path 获取 tree
38+
*/
8339
async getTree(path: string = '') {
84-
await this.ready;
85-
return request<API.ResponseGetTree>(`/api/v3/projects/${this.projectId}/repository/tree`, {
86-
params: {
87-
ref_name: this.commit,
88-
path,
89-
},
90-
});
40+
await this.gitModel.ready;
41+
return request<API.ResponseGetTree>(
42+
`/api/v3/projects/${this.gitModel.projectId}/repository/tree`,
43+
{
44+
params: {
45+
ref_name: this.gitModel.commit,
46+
path,
47+
},
48+
}
49+
);
9150
}
9251

9352
async getBlobSize(path: string) {
94-
await this.ready;
53+
await this.gitModel.ready;
9554
const res: Response = await request(
96-
`/api/v3/projects/${this.projectId}/repository/blobs/${this.commit}`,
55+
`/api/v3/projects/${this.gitModel.projectId}/repository/blobs/${this.gitModel.commit}`,
9756
{
9857
params: {
9958
filepath: path,
@@ -105,11 +64,10 @@ export class GitAPIService implements IGitAPIService {
10564
return parseInt(res.headers.get('Content-Length') || '-1', 10);
10665
}
10766

108-
// 根据 commit 和 path 获取 blob
10967
async getBlob(path: string = '') {
110-
await this.ready;
68+
await this.gitModel.ready;
11169
const buf = await request<ArrayBuffer>(
112-
`/api/v3/projects/${this.projectId}/repository/blobs/${this.commit}`,
70+
`/api/v3/projects/${this.gitModel.projectId}/repository/blobs/${this.gitModel.commit}`,
11371
{
11472
params: {
11573
filepath: path,

0 commit comments

Comments
 (0)