节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
7.06 KiB
Stardust
using System.Text.Json;
using NewLife;
using Stardust.Data.Deployment;
using Stardust.Web.Services;

using Stardust.Data.Platform;

namespace Stardust.Web.Mcp.Actions.Deploy;

/// <summary>触发流水线运行。通过PipelineService.Trigger创建运行记录并异步执行编译→部署</summary>
public class PipelineTriggerAction : McpActionBase
{
    private readonly PipelineService _pipelineService;

    /// <summary>构造函数注入PipelineService</summary>
    public PipelineTriggerAction(PipelineService pipelineService) => _pipelineService = pipelineService;

    public override String Name => "pipeline_trigger";
    public override String Description => "触发指定流水线的运行(创建运行记录,异步执行编译→上传→部署编排)。需要Token已授权该流水线所属项目。";
    public override McpModuleType Module => McpModuleType.Deploy;

    public override ResourceRequirement? RequiredResource => new()
    {
        Type = McpResourceType.Project.ToWireName(),
        Field = "pipeline_id",
        Indirect = true,
        IndirectEntity = McpResourceType.Pipeline.ToIndirectEntityName()!,
    };

    public override JsonElement InputSchema
    {
        get
        {
            var json = """
            {
              "type": "object",
              "properties": {
                "pipeline_id": {"type": "integer", "description": "流水线ID"}
              },
              "required": ["pipeline_id"]
            }
            """;
            using var doc = JsonDocument.Parse(json);
            return doc.RootElement.Clone();
        }
    }

    public override async Task<Object> InvokeAsync(JsonElement @params, McpContext context)
    {
        var pipelineId = GetInt32(@params, "pipeline_id");
        if (pipelineId <= 0) throw new McpException(-32602, "Invalid params: pipeline_id must be a positive integer");

        var run = await _pipelineService.Trigger(pipelineId, context.CallerIp);

        return new
        {
            run_id = run.Id,
            pipeline_id = run.PipelineId,
            status = run.Status.ToString(),
            trigger_source = run.TriggerSource,
            branch = run.Branch,
            create_time = run.CreateTime,
        };
    }
}

/// <summary>查询流水线运行详情。返回运行状态、提交信息、各阶段时间戳及步骤列表</summary>
public class PipelineGetRunAction : McpActionBase
{
    public override String Name => "pipeline_get_run";
    public override String Description => "按运行ID查询流水线运行详情(状态、提交信息、阶段时间戳、步骤列表)。需要Token已授权该运行所属流水线的项目。";
    public override McpModuleType Module => McpModuleType.Deploy;

    public override ResourceRequirement? RequiredResource => new()
    {
        Type = McpResourceType.Project.ToWireName(),
        Field = "run_id",
        Indirect = true,
        IndirectEntity = "AppPipelineRun",
    };

    public override JsonElement InputSchema
    {
        get
        {
            var json = """
            {
              "type": "object",
              "properties": {
                "run_id": {"type": "integer", "description": "运行记录ID(Int64雪花ID)"}
              },
              "required": ["run_id"]
            }
            """;
            using var doc = JsonDocument.Parse(json);
            return doc.RootElement.Clone();
        }
    }

    public override Task<Object> InvokeAsync(JsonElement @params, McpContext context)
    {
        var runId = GetInt32(@params, "run_id");
        if (runId <= 0) throw new McpException(-32602, "Invalid params: run_id must be a positive integer");

        var run = AppPipelineRun.FindById(runId);
        if (run == null) throw new McpException(-32601, $"PipelineRun not found: id={runId}");

        // 查询该运行的所有步骤
        var steps = AppPipelineStep.FindAll(AppPipelineStep._.RunId == runId);
        var stepRecords = steps.OrderBy(s => s.StepIndex).Select(s => new
        {
            id = s.Id,
            step_type = s.StepType,
            step_index = s.StepIndex,
            node_id = s.NodeId,
            status = s.Status,
            message = s.Message,
            started_time = s.StartedTime,
            finished_time = s.FinishedTime,
        }).ToList();

        return Task.FromResult<Object>(new
        {
            run_id = run.Id,
            pipeline_id = run.PipelineId,
            status = run.Status.ToString(),
            trigger_source = run.TriggerSource,
            commit_id = run.CommitId,
            commit_message = run.CommitMessage,
            commit_author = run.CommitAuthor,
            commit_time = run.CommitTime,
            branch = run.Branch,
            build_node_id = run.BuildNodeId,
            app_version_id = run.AppVersionId,
            build_started_time = run.BuildStartedTime,
            build_finished_time = run.BuildFinishedTime,
            deploy_started_time = run.DeployStartedTime,
            deploy_finished_time = run.DeployFinishedTime,
            trace_id = run.TraceId,
            remark = run.Remark,
            create_time = run.CreateTime,
            steps = stepRecords,
        });
    }
}

/// <summary>取消运行中的流水线。校验状态后置为Cancelled,并中断后续步骤</summary>
public class PipelineCancelAction : McpActionBase
{
    private readonly PipelineService _pipelineService;

    /// <summary>构造函数注入PipelineService</summary>
    public PipelineCancelAction(PipelineService pipelineService) => _pipelineService = pipelineService;

    public override String Name => "pipeline_cancel";
    public override String Description => "取消运行中的流水线(Pending/Building/UploadSucceeded/Deploying 状态可取消,终态不可取消)。需要Token已授权该运行所属流水线的项目。";
    public override McpModuleType Module => McpModuleType.Deploy;

    public override ResourceRequirement? RequiredResource => new()
    {
        Type = McpResourceType.Project.ToWireName(),
        Field = "run_id",
        Indirect = true,
        IndirectEntity = "AppPipelineRun",
    };

    public override JsonElement InputSchema
    {
        get
        {
            var json = """
            {
              "type": "object",
              "properties": {
                "run_id": {"type": "integer", "description": "运行记录ID"}
              },
              "required": ["run_id"]
            }
            """;
            using var doc = JsonDocument.Parse(json);
            return doc.RootElement.Clone();
        }
    }

    public override Task<Object> InvokeAsync(JsonElement @params, McpContext context)
    {
        var runId = GetInt32(@params, "run_id");
        if (runId <= 0) throw new McpException(-32602, "Invalid params: run_id must be a positive integer");

        var success = _pipelineService.Cancel(runId, context.CallerIp);

        return Task.FromResult<Object>(new
        {
            run_id = runId,
            cancelled = success,
            message = success ? "运行已取消" : "运行已处于终态,无法取消",
        });
    }
}