From 314543dcecd090249dbe6fa0a50deb08d7b2adbb Mon Sep 17 00:00:00 2001 From: yinxue <2643126914@qq.com> Date: Fri, 5 Dec 2025 09:43:17 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/remote-container.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/remote-container.ts b/src/remote-container.ts index 290f4b5..01553ef 100644 --- a/src/remote-container.ts +++ b/src/remote-container.ts @@ -252,6 +252,11 @@ export default class RemoteContainer { */ private async createSSHPortForward(hostname: string, sshPort: number, containerPort: number, localPort: number): Promise { return new Promise((resolve, reject) => { + // 添加调试信息 + console.log(`[DEBUG] 当前环境: ${vscode.env.remoteName || '本地'}`); + console.log(`[DEBUG] 当前工作目录: ${process.cwd()}`); + console.log(`[DEBUG] SSH 命令: ssh -N -L ${localPort}:localhost:${containerPort} -p ${sshPort} root@${hostname}`); + const sshArgs = [ '-N', '-L', @@ -266,13 +271,21 @@ export default class RemoteContainer { const sshProcess = spawn('ssh', sshArgs); sshProcess.on('error', (error: Error) => { + console.error(`[ERROR] SSH 进程启动失败:`, error); reject(error); }); - sshProcess.stdout.on('data', (_data: Buffer) => { + sshProcess.stdout.on('data', (data: Buffer) => { + console.log(`[SSH stdout] ${data.toString()}`); }); - sshProcess.stderr.on('data', (_data: Buffer) => { + sshProcess.stderr.on('data', (data: Buffer) => { + console.error(`[SSH stderr] ${data.toString()}`); + }); + + // 监听进程退出 + sshProcess.on('exit', (code: number) => { + console.log(`[DEBUG] SSH 进程退出,代码: ${code}`); }); if (!this.sshProcesses) { @@ -282,6 +295,7 @@ export default class RemoteContainer { this.sshProcesses.set(key, sshProcess); setTimeout(() => { + console.log(`[DEBUG] 端口映射完成: ${containerPort} -> ${localPort}`); resolve(); }, 2000); }); -- 2.49.1 From 3b4c3f15f0fee07fc8387108414dde17d5647852 Mon Sep 17 00:00:00 2001 From: yinxue <2643126914@qq.com> Date: Fri, 5 Dec 2025 09:53:13 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=8E=B7=E5=8F=96forward?= =?UTF-8?q?Ports=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/main.ts b/src/main.ts index 00373ca..4d0c822 100644 --- a/src/main.ts +++ b/src/main.ts @@ -66,31 +66,17 @@ export class DevStarExtension { const path = params.get('path'); const accessToken = params.get('access_token'); const devstarUsername = params.get('devstar_username'); - const rawDevstarDomain = params.get('devstar_domain'); - let devstarDomain = rawDevstarDomain; - if (rawDevstarDomain) { - try { - const url = new URL(rawDevstarDomain); - devstarDomain = `${url.protocol}//${url.hostname}`; + const devstarDomain = params.get('devstar_domain'); + const forwardPortsParam = params.get('forwardPorts'); - // 从 rawDevstarDomain 的查询参数中提取 forwardPorts - const forwardPortsParam = url.searchParams.get('forwardPorts'); - if (forwardPortsParam) { - const ports = forwardPortsParam.split(',').map(port => parseInt(port, 10)).filter(port => !isNaN(port)); - console.log('解析到的 forwardPorts 参数:', ports); - context.globalState.update('forwardPorts', ports); - } else { - // 如果没有 forwardPorts 参数,清除 globalState 中的旧值 - console.log('未找到 forwardPorts 参数,清除旧的 forwardPorts 配置'); - context.globalState.update('forwardPorts', undefined); - } - } catch (error) { - console.error('Invalid devstar_domain URL:', error); - } + // 处理 forwardPorts 参数 + if (forwardPortsParam) { + const ports = forwardPortsParam.split(',').map(port => parseInt(port, 10)).filter(port => !isNaN(port)); + context.globalState.update('forwardPorts', ports); + } else { + // 如果没有 forwardPorts 参数,清除 globalState 中的旧值 + context.globalState.update('forwardPorts', undefined); } - console.log('sanitized_devstar_domain:', devstarDomain); - - // 使用修正后的 devstar_domain if (devstarDomain) { this.user.setDevstarDomain(devstarDomain); this.remoteContainer.setUser(this.user); -- 2.49.1 From 3db04f2f0d3325340a616b7a7977fe929a6bd7c1 Mon Sep 17 00:00:00 2001 From: yinxue <2643126914@qq.com> Date: Fri, 5 Dec 2025 11:16:32 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=8E=89=E6=89=93?= =?UTF-8?q?=E5=BC=80=E8=BF=9C=E7=A8=8B=E6=96=87=E4=BB=B6=E5=A4=B9=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=86=85=E5=AE=B9=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/remote-container.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/remote-container.ts b/src/remote-container.ts index 01553ef..f6377a9 100644 --- a/src/remote-container.ts +++ b/src/remote-container.ts @@ -379,13 +379,19 @@ export default class RemoteContainer { } } + // 添加延迟确保 SSH 隧道完全建立 + await new Promise(resolve => setTimeout(resolve, 3000)); + + // 使用 --new-window 而不是 --reuse-window,避免当前窗口被切换 let terminal = vscode.window.activeTerminal || vscode.window.createTerminal(`Ext Terminal`); terminal.show(true); - const command = `code --remote ssh-remote+root@${host}:${port} ${path} --reuse-window`; + const command = `code --remote ssh-remote+root@${host}:${port} ${path} --new-window`; terminal.sendText(command); + vscode.window.showInformationMessage('端口映射已在本地窗口建立,新窗口将打开远程环境'); + } catch (error) { const errorMessage = error instanceof Error ? error.message : '未知错误'; vscode.window.showErrorMessage(`打开远程文件夹失败: ${errorMessage}`); -- 2.49.1 From b33ca163a17edcefb1c1723b0041d30fffc645b7 Mon Sep 17 00:00:00 2001 From: yinxue <2643126914@qq.com> Date: Fri, 5 Dec 2025 14:09:19 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E9=98=B2=E6=AD=A2=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=88=B0=E8=BF=9C=E7=A8=8B=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=90=8Essh=E8=BF=9B=E7=A8=8B=E8=A2=AB=E6=9D=80=E6=AD=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/remote-container.ts | 65 +++++++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 6a09a0c..865a752 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "devstar", "displayName": "%displayName%", "description": "%description%", - "version": "0.4.1", + "version": "0.4.3", "keywords": [], "publisher": "mengning", "engines": { diff --git a/src/remote-container.ts b/src/remote-container.ts index f6377a9..11de8cf 100644 --- a/src/remote-container.ts +++ b/src/remote-container.ts @@ -268,34 +268,46 @@ export default class RemoteContainer { `root@${hostname}` ]; - const sshProcess = spawn('ssh', sshArgs); + // 使用 detached 选项让 SSH 进程独立运行,不随父进程退出 + const sshProcess = spawn('ssh', sshArgs, { + detached: true, // 让进程在后台独立运行 + stdio: 'ignore' // 忽略输入输出,避免进程挂起 + }); + + // 解除父进程对子进程的引用,使其真正独立 + sshProcess.unref(); sshProcess.on('error', (error: Error) => { console.error(`[ERROR] SSH 进程启动失败:`, error); reject(error); }); - sshProcess.stdout.on('data', (data: Buffer) => { - console.log(`[SSH stdout] ${data.toString()}`); - }); + // 由于使用了 stdio: 'ignore',这些事件监听器不再需要 + // sshProcess.stdout.on('data', (data: Buffer) => { + // console.log(`[SSH stdout] ${data.toString()}`); + // }); - sshProcess.stderr.on('data', (data: Buffer) => { - console.error(`[SSH stderr] ${data.toString()}`); - }); + // sshProcess.stderr.on('data', (data: Buffer) => { + // console.error(`[SSH stderr] ${data.toString()}`); + // }); - // 监听进程退出 - sshProcess.on('exit', (code: number) => { + // 监听进程退出(detached 进程可能不会触发此事件) + sshProcess.on('exit', (code: number | null) => { console.log(`[DEBUG] SSH 进程退出,代码: ${code}`); }); - if (!this.sshProcesses) { - this.sshProcesses = new Map(); - } - const key = `${hostname}:${sshPort}:${containerPort}`; - this.sshProcesses.set(key, sshProcess); + // 注意:由于进程已 detached 和 unref,不再需要保存到 sshProcesses Map + // 因为我们无法也不需要控制这些独立进程的生命周期 + // if (!this.sshProcesses) { + // this.sshProcesses = new Map(); + // } + // const key = `${hostname}:${sshPort}:${containerPort}`; + // this.sshProcesses.set(key, sshProcess); + // 等待 SSH 连接建立 setTimeout(() => { - console.log(`[DEBUG] 端口映射完成: ${containerPort} -> ${localPort}`); + console.log(`[DEBUG] 端口映射完成: ${containerPort} -> ${localPort} (进程已独立运行)`); + console.log(`[DEBUG] SSH 进程 PID: ${sshProcess.pid}`); resolve(); }, 2000); }); @@ -367,33 +379,42 @@ export default class RemoteContainer { * local env */ async openRemoteFolder(host: string, port: number, _username: string, path: string, context: vscode.ExtensionContext): Promise { + console.log(`[openRemoteFolder] 开始执行`); + console.log(`[openRemoteFolder] 当前环境: ${vscode.env.remoteName || '本地'}`); + console.log(`[openRemoteFolder] 参数: host=${host}, port=${port}, path=${path}`); + try { const sshConfig = await this.getSSHConfig(host); + console.log(`[openRemoteFolder] SSH配置:`, sshConfig); + if (sshConfig) { try { + console.log(`[openRemoteFolder] 准备建立端口映射...`); // 调用 setupPortForwardingFromGlobalState 方法 await this.setupPortForwardingFromGlobalState(sshConfig.hostname, port, context); + console.log(`[openRemoteFolder] 端口映射设置完成`); } catch (portError) { + console.error(`[openRemoteFolder] 端口映射失败:`, portError); vscode.window.showWarningMessage('端口映射设置失败,但容器连接已建立'); } + } else { + console.log(`[openRemoteFolder] 未找到SSH配置,跳过端口映射`); } - // 添加延迟确保 SSH 隧道完全建立 - await new Promise(resolve => setTimeout(resolve, 3000)); - - // 使用 --new-window 而不是 --reuse-window,避免当前窗口被切换 + console.log(`[openRemoteFolder] 准备打开远程文件夹...`); let terminal = vscode.window.activeTerminal || vscode.window.createTerminal(`Ext Terminal`); terminal.show(true); - const command = `code --remote ssh-remote+root@${host}:${port} ${path} --new-window`; + const command = `code --remote ssh-remote+root@${host}:${port} ${path} --reuse-window`; + console.log(`[openRemoteFolder] 执行命令: ${command}`); terminal.sendText(command); - - vscode.window.showInformationMessage('端口映射已在本地窗口建立,新窗口将打开远程环境'); + console.log(`[openRemoteFolder] 命令已发送到终端`); } catch (error) { const errorMessage = error instanceof Error ? error.message : '未知错误'; + console.error(`[openRemoteFolder] 发生错误:`, error); vscode.window.showErrorMessage(`打开远程文件夹失败: ${errorMessage}`); } } -- 2.49.1 From 2f4d2cb27a7bbf1112745b80c0f163b8da577991 Mon Sep 17 00:00:00 2001 From: yinxue <2643126914@qq.com> Date: Fri, 5 Dec 2025 14:11:46 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=8E=89remote-container?= =?UTF-8?q?.ts=E4=B8=AD=E7=9A=84=E8=B0=83=E8=AF=95=E8=AF=AD=E5=8F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/remote-container.ts | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/src/remote-container.ts b/src/remote-container.ts index 11de8cf..1357edf 100644 --- a/src/remote-container.ts +++ b/src/remote-container.ts @@ -252,11 +252,6 @@ export default class RemoteContainer { */ private async createSSHPortForward(hostname: string, sshPort: number, containerPort: number, localPort: number): Promise { return new Promise((resolve, reject) => { - // 添加调试信息 - console.log(`[DEBUG] 当前环境: ${vscode.env.remoteName || '本地'}`); - console.log(`[DEBUG] 当前工作目录: ${process.cwd()}`); - console.log(`[DEBUG] SSH 命令: ssh -N -L ${localPort}:localhost:${containerPort} -p ${sshPort} root@${hostname}`); - const sshArgs = [ '-N', '-L', @@ -278,7 +273,6 @@ export default class RemoteContainer { sshProcess.unref(); sshProcess.on('error', (error: Error) => { - console.error(`[ERROR] SSH 进程启动失败:`, error); reject(error); }); @@ -291,23 +285,11 @@ export default class RemoteContainer { // console.error(`[SSH stderr] ${data.toString()}`); // }); - // 监听进程退出(detached 进程可能不会触发此事件) - sshProcess.on('exit', (code: number | null) => { - console.log(`[DEBUG] SSH 进程退出,代码: ${code}`); - }); - // 注意:由于进程已 detached 和 unref,不再需要保存到 sshProcesses Map // 因为我们无法也不需要控制这些独立进程的生命周期 - // if (!this.sshProcesses) { - // this.sshProcesses = new Map(); - // } - // const key = `${hostname}:${sshPort}:${containerPort}`; - // this.sshProcesses.set(key, sshProcess); // 等待 SSH 连接建立 setTimeout(() => { - console.log(`[DEBUG] 端口映射完成: ${containerPort} -> ${localPort} (进程已独立运行)`); - console.log(`[DEBUG] SSH 进程 PID: ${sshProcess.pid}`); resolve(); }, 2000); }); @@ -379,42 +361,28 @@ export default class RemoteContainer { * local env */ async openRemoteFolder(host: string, port: number, _username: string, path: string, context: vscode.ExtensionContext): Promise { - console.log(`[openRemoteFolder] 开始执行`); - console.log(`[openRemoteFolder] 当前环境: ${vscode.env.remoteName || '本地'}`); - console.log(`[openRemoteFolder] 参数: host=${host}, port=${port}, path=${path}`); - try { const sshConfig = await this.getSSHConfig(host); - console.log(`[openRemoteFolder] SSH配置:`, sshConfig); if (sshConfig) { try { - console.log(`[openRemoteFolder] 准备建立端口映射...`); // 调用 setupPortForwardingFromGlobalState 方法 await this.setupPortForwardingFromGlobalState(sshConfig.hostname, port, context); - console.log(`[openRemoteFolder] 端口映射设置完成`); } catch (portError) { - console.error(`[openRemoteFolder] 端口映射失败:`, portError); vscode.window.showWarningMessage('端口映射设置失败,但容器连接已建立'); } - } else { - console.log(`[openRemoteFolder] 未找到SSH配置,跳过端口映射`); } - console.log(`[openRemoteFolder] 准备打开远程文件夹...`); let terminal = vscode.window.activeTerminal || vscode.window.createTerminal(`Ext Terminal`); terminal.show(true); const command = `code --remote ssh-remote+root@${host}:${port} ${path} --reuse-window`; - console.log(`[openRemoteFolder] 执行命令: ${command}`); terminal.sendText(command); - console.log(`[openRemoteFolder] 命令已发送到终端`); } catch (error) { const errorMessage = error instanceof Error ? error.message : '未知错误'; - console.error(`[openRemoteFolder] 发生错误:`, error); vscode.window.showErrorMessage(`打开远程文件夹失败: ${errorMessage}`); } } -- 2.49.1 From bc954a8202337529a65a91f43c887d9095530846 Mon Sep 17 00:00:00 2001 From: yinxue <2643126914@qq.com> Date: Fri, 5 Dec 2025 15:35:58 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=89=93=E5=BC=80=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9=E5=90=8Ehome=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E8=83=BD=E6=AD=A3=E5=B8=B8=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/home.ts | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/home.ts b/src/home.ts index 4132ade..08c27e7 100644 --- a/src/home.ts +++ b/src/home.ts @@ -79,7 +79,33 @@ export default class DSHome { async (message) => { const data = message.data; const need_return = message.need_return; - if (!need_return) { + + if (need_return) { + // 处理需要返回结果的消息 + switch (message.command) { + case 'getUserToken': + panel.webview.postMessage({ + command: 'getUserToken', + data: { userToken: this.user.getUserTokenFromLocal() } + }); + break; + + case 'getUsername': + panel.webview.postMessage({ + command: 'getUsername', + data: { username: this.user.getUsernameFromLocal() } + }); + break; + + case 'getDevstarDomain': + panel.webview.postMessage({ + command: 'getDevstarDomain', + data: { devstarDomain: this.devstarDomain } + }); + break; + } + } else { + // 处理不需要返回结果的消息 switch (message.command) { case 'openExternalUrl': const url = message.url || (data && data.url); @@ -96,6 +122,18 @@ export default class DSHome { vscode.window.showErrorMessage('打开链接失败: 链接地址无效'); } break; + + case 'showInformationNotification': + if (data && data.message) { + vscode.window.showInformationMessage(data.message); + } + break; + + case 'showErrorNotification': + if (data && data.message) { + vscode.window.showErrorMessage(data.message); + } + break; } } }, -- 2.49.1 From d67cb8b6a083d77cb5dc22f1e636be3c0d6cb554 Mon Sep 17 00:00:00 2001 From: yinxue <2643126914@qq.com> Date: Sat, 6 Dec 2025 10:50:01 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81=E6=8B=89=E5=8F=96=E4=BB=A3=E7=A0=81=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=B9=B6=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=8A=A8=E9=80=92=E5=A2=9E?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/devstar-vscode-release.yaml | 46 +++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/devstar-vscode-release.yaml b/.gitea/workflows/devstar-vscode-release.yaml index 9ea29b8..ce4f729 100644 --- a/.gitea/workflows/devstar-vscode-release.yaml +++ b/.gitea/workflows/devstar-vscode-release.yaml @@ -7,17 +7,59 @@ on: branches: - main +permissions: + contents: write + jobs: build: runs-on: ubuntu-latest container: image: node:20-alpine steps: + - name: 安装 Git + run: | + apk add --no-cache git + - name: 拉取代码 - uses: https://devstar.cn/actions/checkout@v4 + uses: actions/checkout@v4 with: + token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 + - name: 配置 Git + run: | + # 添加工作目录为安全目录 + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git config --global --add safe.directory /github/workspace + + # 配置用户信息 + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: 自动递增版本号 + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: | + apk add --no-cache jq + CURRENT_VERSION=$(jq -r '.version' package.json) + echo "当前版本: $CURRENT_VERSION" + + # 分解版本号 + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" + + # 递增补丁版本号 + NEW_PATCH=$((PATCH + 1)) + NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH" + echo "新版本: $NEW_VERSION" + + # 更新 package.json + jq --arg version "$NEW_VERSION" '.version = $version' package.json > package.json.tmp + mv package.json.tmp package.json + + # 提交版本变更 + git add package.json + git commit -m "chore: bump version to $NEW_VERSION [skip ci]" + git push + - name: 安装依赖 run: | npm install @@ -27,7 +69,7 @@ jobs: npm run package - name: 发布插件 - if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main' + if: github.event_name == 'push' && github.ref == 'refs/heads/main' run: | npm run publish env: -- 2.49.1