Files
devstar/models/devcontainer/devcontainer.go
xinitx 9071a754f4
Some checks failed
DevStar Studio Auto Test Pipeline / unit-frontend-test (push) Has been cancelled
DevStar Studio Auto Test Pipeline / unit-backend-test (push) Has been cancelled
DevStar Studio CI/CD Pipeline / build-and-push-x86-64-docker-image (push) Failing after 21s
!108 给devcontainer增加变量和脚本功能
给devcontainer增加变量和脚本功能

- 能从devstar.cn上获取预定义的DEVSTAR_开头的变量或脚本
- 添加到脚本管理中的变量名,在devcontainer启动时会自动执行,然后才执行devcontainer.json中用户自定义脚本,其中可以调用设置的变量或脚本
- 变量或脚本在用户设置、项目设置和后台管理中都可以添加,如有重名优先级为:用户设置 > 项目设置 > 后台管理
2025-10-18 08:53:50 +00:00

157 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package devcontainer
import (
"context"
"encoding/json"
"io"
"net/http"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
)
type Devcontainer struct {
Id int64 `xorm:"BIGINT pk NOT NULL autoincr 'id' comment('主键devContainerId')"`
Name string `xorm:"VARCHAR(64) charset=utf8mb4 collate=utf8mb4_bin UNIQUE NOT NULL 'name' comment('devContainer名称自动生成')"`
DevcontainerHost string `xorm:"VARCHAR(256) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_host' comment('SSH Host')"`
DevcontainerPort uint16 `xorm:"SMALLINT UNSIGNED NOT NULL 'devcontainer_port' comment('SSH Port')"`
DevcontainerStatus uint16 `xorm:"SMALLINT UNSIGNED NOT NULL 'devcontainer_status' comment('SSH Status')"`
DevcontainerUsername string `xorm:"VARCHAR(32) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_username' comment('SSH Username')"`
DevcontainerWorkDir string `xorm:"VARCHAR(256) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'devcontainer_work_dir' comment('SSH 工作路径,典型值 ~/${project_name}256字节以内')"`
RepoId int64 `xorm:"BIGINT NOT NULL FK('repo_id') REFERENCES repository(id) ON DELETE CASCADE 'repo_id' comment('repository表主键')"`
UserId int64 `xorm:"BIGINT NOT NULL FK('user_id') REFERENCES user(id) ON DELETE CASCADE 'user_id' comment('user表主键')"`
CreatedUnix int64 `xorm:"BIGINT 'created_unix' comment('创建时间戳')"`
UpdatedUnix int64 `xorm:"BIGINT 'updated_unix' comment('更新时间戳')"`
}
type DevcontainerOutput struct {
Id int64 `xorm:"BIGINT pk NOT NULL autoincr 'id' comment('主键devContainerId')"`
RepoId int64 `xorm:"BIGINT NOT NULL unique(uniquename) 'repo_id' comment('repository表主键')"`
UserId int64 `xorm:"BIGINT NOT NULL unique(uniquename) 'user_id' comment('user表主键')"`
Status string `xorm:"VARCHAR(255) charset=utf8mb4 collate=utf8mb4_bin NOT NULL 'status' comment('status')"`
Output string `xorm:"TEXT 'output' comment('output')"`
Command string `xorm:"TEXT 'command' comment('command')"`
ListId int64 `xorm:"BIGINT NOT NULL unique(uniquename) 'list_id' comment('list_id')"`
DevcontainerId int64 `xorm:"BIGINT NOT NULL FK('devcontainer_id') REFERENCES devcontainer(id) ON DELETE CASCADE 'devcontainer_id' comment('devcontainer表主键')"`
}
type DevcontainerScript struct {
Id int64 `xorm:"BIGINT pk NOT NULL autoincr 'id' comment('主键devContainerId')"`
RepoId int64 `xorm:"BIGINT NOT NULL 'repo_id' comment('repository表主键')"`
UserId int64 `xorm:"BIGINT NOT NULL 'user_id' comment('user表主键')"`
VariableName string `xorm:"NOT NULL 'variable_name' comment('user表主键')"`
}
func init() {
db.RegisterModel(new(Devcontainer))
db.RegisterModel(new(DevcontainerScript))
db.RegisterModel(new(DevcontainerOutput))
}
func GetScript(ctx context.Context, userId, repoID int64) (map[string]string, error) {
variables := make(map[string]string)
var devstarVariables []*DevcontainerVariable
var name []string
// Devstar level
// 从远程获取Devstar变量
client := &http.Client{}
req, err := http.NewRequest("GET", "http://devstar.cn/variables/export", nil)
if err != nil {
log.Error("Failed to create request for devstar variables: %v", err)
} else {
resp, err := client.Do(req)
if err != nil {
log.Error("Failed to fetch devstar variables: %v", err)
} else {
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Error("Failed to read devstar variables response: %v", err)
} else {
err = json.Unmarshal(body, &devstarVariables)
if err != nil {
log.Error("Failed to unmarshal devstar variables: %v", err)
}
}
}
}
// Global
err = db.GetEngine(ctx).
Select("variable_name").
Table("devcontainer_script").
Where("user_id = ? AND repo_id = ?", 0, 0).
Find(&name)
globalVariables, err := db.Find[DevcontainerVariable](ctx, FindVariablesOpts{})
if err != nil {
log.Error("find global variables: %v", err)
return nil, err
}
// 过滤出name在variableNames中的变量
globalVariables = append(devstarVariables, globalVariables...)
var filteredGlobalVars []*DevcontainerVariable
for _, v := range globalVariables {
if contains(name, v.Name) {
filteredGlobalVars = append(filteredGlobalVars, v)
}
}
// Org / User level
err = db.GetEngine(ctx).
Select("variable_name").
Table("devcontainer_script").
Where("user_id = ? AND repo_id = ?", userId, 0).
Find(&name)
ownerVariables, err := db.Find[DevcontainerVariable](ctx, FindVariablesOpts{OwnerID: userId})
if err != nil {
log.Error("find variables of org: %d, error: %v", userId, err)
return nil, err
}
// 过滤出name在variableNames中的变量
ownerVariables = append(devstarVariables, ownerVariables...)
var filteredOwnerVars []*DevcontainerVariable
for _, v := range ownerVariables {
if contains(name, v.Name) {
filteredOwnerVars = append(filteredOwnerVars, v)
}
}
// Repo level
err = db.GetEngine(ctx).
Select("variable_name").
Table("devcontainer_script").
Where("repo_id = ?", repoID).
Find(&name)
repoVariables, err := db.Find[DevcontainerVariable](ctx, FindVariablesOpts{RepoID: repoID})
if err != nil {
log.Error("find variables of repo: %d, error: %v", repoID, err)
return nil, err
}
// 过滤出name在variableNames中的变量
repoVariables = append(devstarVariables, repoVariables...)
var filteredRepoVars []*DevcontainerVariable
for _, v := range repoVariables {
if contains(name, v.Name) {
filteredRepoVars = append(filteredRepoVars, v)
}
}
// Level precedence: Org / User > Repo > Global
for _, v := range append(filteredGlobalVars, append(filteredRepoVars, filteredOwnerVars...)...) {
variables[v.Name] = v.Data
}
return variables, nil
}
// contains 检查字符串切片中是否包含指定的字符串
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}