给devcontainer增加变量和脚本功能 - 能从devstar.cn上获取预定义的DEVSTAR_开头的变量或脚本 - 添加到脚本管理中的变量名,在devcontainer启动时会自动执行,然后才执行devcontainer.json中用户自定义脚本,其中可以调用设置的变量或脚本 - 变量或脚本在用户设置、项目设置和后台管理中都可以添加,如有重名优先级为:用户设置 > 项目设置 > 后台管理
157 lines
6.8 KiB
Go
157 lines
6.8 KiB
Go
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
|
||
}
|