From 6686a4431645f50ae15bd678c521792ecb133408 Mon Sep 17 00:00:00 2001 From: init Date: Thu, 4 Sep 2025 10:48:46 +0800 Subject: [PATCH] add webterminal dockerfile --- Makefile | 1 + docker/Dockerfile.devstar | 2 +- docker/Dockerfile.webTerminal | 40 +++++++++++ routers/web/devcontainer/devstar_home.go | 4 +- services/devcontainer/devcontainer.go | 54 +++++++++++++- services/devcontainer/devcontainer_utils.go | 17 +++++ services/devcontainer/docker_agent.go | 24 +++++-- ...ome-vscode-js.tmpl => vscode-home-js.tmpl} | 0 ...star-home-vscode.tmpl => vscode-home.tmpl} | 2 +- webTerminal.sh | 70 +++++++++++++++++++ 10 files changed, 201 insertions(+), 13 deletions(-) create mode 100644 docker/Dockerfile.webTerminal rename templates/repo/devcontainer/{devstar-home-vscode-js.tmpl => vscode-home-js.tmpl} (100%) rename templates/repo/devcontainer/{devstar-home-vscode.tmpl => vscode-home.tmpl} (99%) create mode 100644 webTerminal.sh diff --git a/Makefile b/Makefile index 65cc683213..3ad79bf560 100644 --- a/Makefile +++ b/Makefile @@ -918,6 +918,7 @@ generate-manpage: ## generate manpage .PHONY: devstar devstar: docker build -t devstar-studio:latest -f docker/Dockerfile.devstar . + docker build -t devstar.cn/devstar/webterminal:latest -f docker/Dockerfile.webTerminal . .PHONY: docker docker: diff --git a/docker/Dockerfile.devstar b/docker/Dockerfile.devstar index 6c374dd9c2..962bcfa055 100644 --- a/docker/Dockerfile.devstar +++ b/docker/Dockerfile.devstar @@ -73,7 +73,7 @@ RUN chown git:git /var/lib/gitea /etc/gitea COPY --from=build-env /tmp/local / COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini - +COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/webTerminal.sh /app/gitea/webTerminal.sh # git:git USER 1000:1000 ENV GITEA_WORK_DIR=/var/lib/gitea diff --git a/docker/Dockerfile.webTerminal b/docker/Dockerfile.webTerminal new file mode 100644 index 0000000000..4fb65b05de --- /dev/null +++ b/docker/Dockerfile.webTerminal @@ -0,0 +1,40 @@ +FROM docker.io/library/ubuntu:24.04 AS build-env + + +RUN apt-get update && \ + apt-get install -y \ + git \ + build-essential \ + cmake \ + libjson-c-dev \ + libwebsockets-dev + +RUN git clone https://devstar.cn/devstar/webTerminal.git /home/webTerminal +# 设置工作目录并构建 +WORKDIR /home/webTerminal/build +RUN cmake .. +RUN make && make install + + +FROM ubuntu:24.04 + +# 从构建阶段复制编译好的程序 +COPY --from=build-env /home/webTerminal/build/ttyd /home/webTerminal/build/ttyd + +# 只安装运行时需要的库 +RUN apt-get update && \ + apt-get install -y \ + curl && \ + curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc && \ + apt-get install -y tini \ + libjson-c-dev \ + libwebsockets-dev && \ + echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/ \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + tee /etc/apt/sources.list.d/docker.list > /dev/null && \ + apt-get update && apt-get install -y docker-ce-cli && \ + apt remove --purge curl -y && apt autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +ENTRYPOINT ["/usr/bin/tini", "--"] +CMD ["/home/webTerminal/build/ttyd", "-W", "bash"] \ No newline at end of file diff --git a/routers/web/devcontainer/devstar_home.go b/routers/web/devcontainer/devstar_home.go index d885fbf365..4fc8c8935e 100644 --- a/routers/web/devcontainer/devstar_home.go +++ b/routers/web/devcontainer/devstar_home.go @@ -8,8 +8,8 @@ import ( ) const ( - // TplDevstarHome 显示 DevStar Home 页面 templates/devstar-home-vscode.tmpl - TplDevstarHome templates.TplName = "repo/devcontainer/devstar-home-vscode" + // TplDevstarHome 显示 DevStar Home 页面 templates/vscode-home.tmpl + TplDevstarHome templates.TplName = "repo/devcontainer/vscode-home" ) // DevstarHome 渲染适配于 VSCode 插件的 DevStar Home 页面 diff --git a/services/devcontainer/devcontainer.go b/services/devcontainer/devcontainer.go index 57c9961906..591dcd06a9 100644 --- a/services/devcontainer/devcontainer.go +++ b/services/devcontainer/devcontainer.go @@ -1,12 +1,14 @@ package devcontainer import ( + "archive/tar" "bytes" "context" "fmt" "math" "net" "net/url" + "os" "time" "code.gitea.io/gitea/models/db" @@ -19,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/setting" gitea_context "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" + "github.com/docker/docker/api/types" "xorm.io/builder" ) @@ -501,15 +504,62 @@ func GetTerminalCommand(ctx context.Context, userID string, repo *repo.Repositor return "", "", err } if status == "running" { + //添加脚本文件 + if setting.K8sConfig.Enable { + + } else { + + var scriptContent []byte + _, err = os.Stat("webTerminal.sh") + if os.IsNotExist(err) { + _, err = os.Stat("/app/gitea/webTerminal.sh") + if os.IsNotExist(err) { + return "", "", err + } else { + scriptContent, err = os.ReadFile("/app/gitea/webTerminal.sh") + if err != nil { + return "", "", err + } + } + } else { + scriptContent, err = os.ReadFile("webTerminal.sh") + if err != nil { + return "", "", err + } + } + // 创建 tar 归档文件 + var buf bytes.Buffer + tw := tar.NewWriter(&buf) + defer tw.Close() + + // 添加文件到 tar 归档 + AddFileToTar(tw, "webTerminal.sh", string(scriptContent), 0777) + // 创建 Docker 客户端 + cli, err := docker_module.CreateDockerClient(ctx) + if err != nil { + return "", "", err + } + // 获取容器 ID + containerID, err := docker_module.GetContainerID(cli, devContainerInfo.Name) + if err != nil { + return "", "", err + } + err = cli.CopyToContainer(ctx, containerID, "/home", bytes.NewReader(buf.Bytes()), types.CopyToContainerOptions{}) + if err != nil { + log.Info("%v", err) + return "", "", err + } + } realTimeStatus = 3 } } break case 3: - //正在创建容器,创建容器成功,则状态转移 + //正在初始化容器,初始化容器成功,则状态转移 if setting.K8sConfig.Enable { //k8s的逻辑 } else { + status, err := CheckDirExistsFromDocker(ctx, devContainerInfo.Name, devContainerInfo.DevcontainerWorkDir) if err != nil { return "", "", err @@ -520,7 +570,7 @@ func GetTerminalCommand(ctx context.Context, userID string, repo *repo.Repositor } break case 4: - //正在创建容器,创建容器成功,则状态转移 + //正在连接容器 if setting.K8sConfig.Enable { //k8s的逻辑 } else { diff --git a/services/devcontainer/devcontainer_utils.go b/services/devcontainer/devcontainer_utils.go index aa4fa7772a..b3643d6679 100644 --- a/services/devcontainer/devcontainer_utils.go +++ b/services/devcontainer/devcontainer_utils.go @@ -1,6 +1,7 @@ package devcontainer import ( + "archive/tar" "context" "fmt" "io" @@ -210,3 +211,19 @@ func ReplacePortOfUrl(originalURL, targetPort string) (string, error) { newURL := parsedURL.String() return newURL, nil } + +// addFileToTar 将文件添加到 tar 归档 +func AddFileToTar(tw *tar.Writer, filename string, content string, mode int64) error { + hdr := &tar.Header{ + Name: filename, + Mode: mode, + Size: int64(len(content)), + } + if err := tw.WriteHeader(hdr); err != nil { + return err + } + if _, err := tw.Write([]byte(content)); err != nil { + return err + } + return nil +} diff --git a/services/devcontainer/docker_agent.go b/services/devcontainer/docker_agent.go index 7d8036d938..772dade896 100644 --- a/services/devcontainer/docker_agent.go +++ b/services/devcontainer/docker_agent.go @@ -239,13 +239,23 @@ func CreateDevContainerByDockerCommand(ctx context.Context, newDevcontainer *dev log.Info("Failed to insert record: %v", err) return err } + cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf) + if err != nil { + return err + } //创建并运行容器的命令 if _, err := dbEngine.Table("devcontainer_output").Insert(&devcontainer_models.DevcontainerOutput{ - Output: "", - Status: "waitting", - UserId: newDevcontainer.UserId, - RepoId: newDevcontainer.RepoId, - Command: `docker -H ` + dockerSocket + ` run -d --name ` + newDevcontainer.Name + ` -p 22 ` + imageName + ` sh -c "tail -f /dev/null"` + "\n", + Output: "", + Status: "waitting", + UserId: newDevcontainer.UserId, + RepoId: newDevcontainer.RepoId, + Command: `docker -H ` + dockerSocket + ` run -d --name ` + newDevcontainer.Name + + ` -p 22 ` + + ` -e RepoLink="` + strings.TrimSuffix(cfg.Section("server").Key("ROOT_URL").Value(), `/`) + repo.Link() + `" ` + + ` -e WorkSpace="` + newDevcontainer.DevcontainerWorkDir + `/` + repo.Name + `"` + + ` -e PublicKeyList="` + strings.Join(publicKeyList, "\n") + `" ` + + imageName + + ` sh -c "tail -f /dev/null"` + "\n", ListId: 2, DevcontainerId: newDevcontainer.Id, }); err != nil { @@ -258,7 +268,7 @@ func CreateDevContainerByDockerCommand(ctx context.Context, newDevcontainer *dev Status: "waitting", UserId: newDevcontainer.UserId, RepoId: newDevcontainer.RepoId, - Command: `docker -H ` + dockerSocket + ` exec ` + newDevcontainer.Name + ` sh -c "echo \"` + newDevcontainer.DevcontainerHost + ` host.docker.internal\" | tee -a /etc/hosts;apt update;apt install -y git ;git clone ` + strings.TrimSuffix(setting.AppURL, "/") + repo.Link() + " /data/workspace/" + repo.Name + `; apt install -y ssh;echo -e "PubkeyAuthentication yes\nPermitRootLogin yes\n" | tee -a /etc/ssh/sshd_config;rm -f /etc/ssh/ssh_host_*; ssh-keygen -A; service ssh restart;mkdir -p ~/.ssh;chmod 700 ~/.ssh;echo "` + strings.Join(publicKeyList, "\n") + `" > ~/.ssh/authorized_keys;chmod 600 ~/.ssh/authorized_keys"` + "\n", + Command: `docker -H ` + dockerSocket + ` exec ` + newDevcontainer.Name + ` /home/webTerminal.sh start` + "\n", ListId: 3, DevcontainerId: newDevcontainer.Id, }); err != nil { @@ -552,7 +562,7 @@ func RegistWebTerminal(ctx context.Context) error { containerName := "webterminal-" + timestamp //创建并启动WebTerminal容器 err = docker_module.CreateAndStartContainer(ctx, cli, setting.DevContainerConfig.Web_Terminal_Image, - []string{"sh", "-c", "/home/webTerminal/build/ttyd -W bash\n"}, + nil, nil, binds, nat.PortSet{ "7681/tcp": struct{}{}, diff --git a/templates/repo/devcontainer/devstar-home-vscode-js.tmpl b/templates/repo/devcontainer/vscode-home-js.tmpl similarity index 100% rename from templates/repo/devcontainer/devstar-home-vscode-js.tmpl rename to templates/repo/devcontainer/vscode-home-js.tmpl diff --git a/templates/repo/devcontainer/devstar-home-vscode.tmpl b/templates/repo/devcontainer/vscode-home.tmpl similarity index 99% rename from templates/repo/devcontainer/devstar-home-vscode.tmpl rename to templates/repo/devcontainer/vscode-home.tmpl index c811c4ea48..8dbaaf4c14 100644 --- a/templates/repo/devcontainer/devstar-home-vscode.tmpl +++ b/templates/repo/devcontainer/vscode-home.tmpl @@ -431,4 +431,4 @@ -{{template "repo/devcontainer/devstar-home-vscode-js" .}} \ No newline at end of file +{{template "repo/devcontainer/vscode-home-js" .}} \ No newline at end of file diff --git a/webTerminal.sh b/webTerminal.sh new file mode 100644 index 0000000000..768ffb0933 --- /dev/null +++ b/webTerminal.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# 获取参数 +ACTION=$1 +OS_ID=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"') + + + +# 根据参数执行不同命令 +case $ACTION in + "start") + echo "Starting service..." + # 启动服务的命令 + echo "newDevcontainer.DevcontainerHost host.docker.internal" | tee -a /etc/hosts; + case $OS_ID in + ubuntu|debian) + apt-get update -y + apt-get install ssh git -y + ;; + centos) + # sudo yum update -y + # sudo yum install -y epel-release + # sudo yum groupinstall -y "Development Tools" + # sudo yum install -y yaml-cpp yaml-cpp-devel + ;; + fedora) + # sudo dnf update -y + # sudo dnf group install -y "Development Tools" + # sudo dnf install -y yaml-cpp yaml-cpp-devel + ;; + *) + failure "Unsupported OS: $OS_ID" + exit 1 + ;; + esac + + echo -e "PubkeyAuthentication yes\nPermitRootLogin yes\n" | tee -a /etc/ssh/sshd_config; + rm -f /etc/ssh/ssh_host_*; + ssh-keygen -A; + mkdir -p ~/.ssh; + chmod 700 ~/.ssh; + case $OS_ID in + ubuntu|debian) + service ssh restart; + ;; + centos) + ;; + fedora) + ;; + *) + failure "Unsupported OS: $OS_ID" + exit 1 + ;; + esac + echo "$PublicKeyList" > ~/.ssh/authorized_keys; + chmod 600 ~/.ssh/authorized_keys + git clone $RepoLink $WorkSpace + ;; + "stop") + echo "Stopping service..." + # 停止服务的命令 + ;; + "restart") + echo "Restarting service..." + # 重启服务的命令 + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac \ No newline at end of file