class Api::V1::Projects::PipelinesController < Api::V1::BaseController include RepositoriesHelper before_action :require_operate_above, except: [:upload_results, :run_results] def index pipelines = Action::Pipeline.where(project_id: @project.id).order(updated_at: :desc) @files = $gitea_client.get_repos_contents_by_owner_repo_filepath(@project&.owner&.login, @project&.identifier, ".gitea/workflows") rescue [] @action_runs = Gitea::ActionRun.where(repo_id: @project.gpid) group_data = @action_runs.where(status: [1,2]).group(:workflow_id, :status).count db_files = pipelines.pluck(:file_name) @run_result = [] @files.map { |i| i['name'] }.each do |file| unless db_files.include?(".gitea/workflows/#{file}") pipeline = Action::Pipeline.find_or_initialize_by(pipeline_name: file.to_s.gsub(".yml", "").gsub(".yaml", ""), file_name: ".gitea/workflows/#{file}", branch: @project.default_branch, is_graphic_design: false, disable: false, project_id: @project.id) interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, ".gitea/workflows/#{file}", ref: @project.default_branch) if interactor.success? pipeline.yaml = decode64_content(interactor.result, @owner, @project.repository, @project.default_branch, nil) end pipeline.user_id = current_user.id pipeline.save # 导入的流水线统一先禁用 $gitea_hat_client.post_repos_actions_disable(@project&.owner&.login, @project&.identifier, {query: {workflow: file}}) rescue nil end last_action_run = @action_runs.where(workflow_id: file).order(updated: :desc).first last_action_run_json = last_action_run.present? ? { id: last_action_run.id, schedule: last_action_run.schedule_id > 0, title: last_action_run.title, index: last_action_run.index, status: last_action_run.status, started: last_action_run.started, stopped: last_action_run.stopped, length: last_action_run.stopped-last_action_run.started, created: last_action_run.created, updated: last_action_run.updated, } : {} total = 0 success = 0 failure = 0 group_data.each do |k,v| total += v if k[0] == file success += v if k[0] == file && k[1] == 1 failure += v if k[0] == file && k[1] == 2 end @run_result << { filename: ".gitea/workflows/#{file}", total: total, success: success, failure: failure }.merge(last_action_run_json) end # Rails.logger.info("@run_result======#{@run_result}") disabled_config = Gitea::RepoUnit.where(repo_id: @project.gpid, type: 10)&.first&.config @disabled_workflows = disabled_config.present? ? JSON.parse(disabled_config)["DisabledWorkflows"] : [] @pipelines = Action::Pipeline.where(project_id: @project.id).order(updated_at: :desc) @pipelines = kaminari_paginate(@pipelines) end def test_yaml pipeline_yaml = build_pipeline_yaml_new("test", JSON.parse(demo2.to_json)) respond_to do |format| format.html { @nodes = Action::Node.all } # format.yaml { @nodes = Action::Node.all } format.yaml { render template: "api/v1/projects/pipelines/test_yaml", content_type: "text/html" } format.json { render_ok({ pipeline_yaml: pipeline_yaml }) } end end def create @pipeline = params[:id].present? ? Action::Pipeline.find(params[:id]) : Action::Pipeline.find_or_initialize_by(pipeline_name: params[:pipeline_name], project_id: @project.id) if @pipeline.pipeline_name != params[:pipeline_name] has_pipeline = Action::Pipeline.where(pipeline_name: params[:pipeline_name], project_id: @project.id).where.not(id: @pipeline.id) tip_exception("已经存在#{params[:pipeline_name]}流水线!") if has_pipeline.present? if @pipeline.yaml.present? sha = get_pipeline_file_sha(@pipeline.file_name, @pipeline.branch) if sha.present? interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("update", params[:pipeline_name]).merge(sha: sha, from_path: "#{@pipeline.pipeline_name}")) tip_exception(interactor.error) unless interactor.success? end end end @pipeline.pipeline_name = params[:pipeline_name] @pipeline.file_name = ".gitea/workflows/#{@pipeline.pipeline_name}.yml" @pipeline.branch = params[:branch] || @project.default_branch @pipeline.is_graphic_design = params[:pipeline_type] == 2 ? true : false @pipeline.pipeline_type = params[:pipeline_type] if params[:pipeline_type].present? @pipeline.save! end def save_yaml @pipeline = params[:id].present? ? Action::Pipeline.find(params[:id]) : Action::Pipeline.find_or_initialize_by(pipeline_name: params[:pipeline_name], project_id: @project.id) @pipeline.file_name = ".gitea/workflows/#{@pipeline.pipeline_name}.yml" @pipeline.branch = params[:branch].to_json if params[:branch].present? @pipeline.json = params[:pipeline_json].to_json if params[:pipeline_json].present? @pipeline.pipeline_name = params[:pipeline_name] if params[:pipeline_name].present? pipeline_yaml = params[:pipeline_yaml].present? ? params[:pipeline_yaml] : build_pipeline_yaml_new(@pipeline.pipeline_name, params[:pipeline_json]) tip_exception("流水线yaml内空不能为空") if pipeline_yaml.blank? @pipeline.yaml = pipeline_yaml #Rails.logger.info "pipeline_yaml base64=========================#{Base64.encode64(@pipeline.yaml).gsub(/\n/, '')}" sha = get_pipeline_file_sha(@pipeline.file_name, @pipeline.branch) #Rails.logger.info "content_params=========#{content_params("create")}" interactor = sha.present? ? Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("update").merge(sha: sha)) : Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("create")) tip_exception(interactor.error) unless interactor.success? file = interactor.result @pipeline.pipeline_type = @pipeline.json.present? ? 2 : 1 @pipeline.save render_ok({ pipeline_yaml: pipeline_yaml, pipeline_name: @pipeline.pipeline_name, file_name: @pipeline.file_name, sha: sha.present? ? sha : file['content']['sha'] }) end def build_yaml if params[:pipeline_json].present? pipeline_yaml = build_pipeline_yaml(params[:pipeline_name], params[:pipeline_json]) else pipeline_yaml = build_test_yaml end # render plain: pipeline_yaml render_ok({ pipeline_yaml: pipeline_yaml }) end def update @pipeline = Action::Pipeline.find(params[:id]) @pipeline.pipeline_name = params[:pipeline_name] @pipeline.file_name = ".gitea/workflows/#{@pipeline.pipeline_name}.yml" @pipeline.branch = params[:branch] || @project.default_branch @pipeline.json = params[:pipeline_json].to_json pipeline_yaml = build_pipeline_yaml(params[:pipeline_name], params[:pipeline_json]) tip_exception("流水线yaml内空不能为空") if pipeline_yaml.blank? @pipeline.yaml = pipeline_yaml @pipeline.save sha = get_pipeline_file_sha(@pipeline.file_name, @pipeline.branch) interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("create").merge(sha: sha)) tip_exception(interactor.error) unless interactor.success? file = interactor.result render_ok({ pipeline_yaml: pipeline_yaml, pipeline_name: params[:pipeline_name], file_name: @pipeline.file_name, sha: file['content']['sha'] }) end def destroy @pipeline = Action::Pipeline.find(params[:id]) if @pipeline sha = get_pipeline_file_sha(@pipeline.file_name, @pipeline.branch) if sha.present? interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, del_content_params(sha).merge(identifier: @project.identifier)) tip_exception(interactor.error) unless interactor.success? end Gitea::ActionRun.where(repo_id: @pipeline.project.gpid).destroy_all @pipeline.destroy! end render_ok end def upload_results tip_exception("参数错误") if params[:owner].blank? || params[:repo].blank? || params[:run_id].blank? @project, @owner = Project.find_with_namespace(params[:owner], params[:repo]) tip_exception("项目不存在") if @project.blank? result = Action::PipelineResult.find_or_initialize_by(run_id: params[:run_id], step_id: params[:step_id], project_id: @project.id) result.step_id = params[:step_id] result.job_name = params[:job_name] result.job_show_type = params[:job_show_type] || "html" result.job_result = params[:job_result] if result.save! render_ok else render_error("保存失败") end end def run_results tip_exception("参数错误") if params[:owner].blank? || params[:repo].blank? || params[:run_id].blank? @project, @owner = Project.find_with_namespace(params[:owner], params[:repo]) tip_exception("项目不存在") if @project.blank? results = Action::PipelineResult.where(run_id: params[:run_id], project_id: @project.id) render_ok(run_results: results.as_json(only: %i[id run_id job_name job_show_type job_result])) end def show @pipeline = Action::Pipeline.find_by(id: params[:id]) disabled_config = Gitea::RepoUnit.where(repo_id: @project.gpid, type: 10)&.first&.config @disabled_workflows = disabled_config.present? ? JSON.parse(disabled_config)["DisabledWorkflows"] : [] # @pipeline = Action::Pipeline.new(id: 0, pipeline_name: "test-ss", yaml: build_test_yaml) if @pipeline.blank? end def build_pipeline_yaml_new(pipeline_name, pipeline_json) @pipeline_name = pipeline_name job_nodes = pipeline_json["nodes"].select { |node| node["data"]["name"].to_s.include?("job") } on_nodes = pipeline_json["nodes"].select { |node| ["on-push", "on-schedule", "on-pull_request", "on-fork"].include?(node["data"]["name"]) } @on_nodes = build_nodes(on_nodes) @job_nodes = [] job_nodes.each do |job| node = Action::Node.find_by(name: job["data"]["name"]) next if node.blank? node.label = job["data"]["label"] if job["data"]["label"].present? node.node_id = job["id"] if job["id"].present? next_edge_nodes = pipeline_json["edges"].select { |edge| edge["source"]["cell"].include?(job["id"]) } node_ids = next_edge_nodes.map { |t| t["target"]["cell"] } next_step_node = pipeline_json["nodes"].select { |node| node_ids.include?(node["id"]) && !node["data"]["name"].to_s.include?("job") }&.first parent_node = pipeline_json["edges"].select { |edge| edge["target"]["cell"].include?(job["id"]) }.first node.parent_node_id = parent_node.present? ? parent_node["source"]["cell"] : "" node.sub_nodes = [] get_all_child_nodes(pipeline_json, node, next_step_node) if next_step_node.present? @job_nodes.push(node) end yaml = ERB.new(File.read(File.join(Rails.root, "app/views/api/v1/projects/pipelines", "build_pipeline.yaml.erb"))).result(binding) pipeline_yaml = yaml.gsub(/^\s*\n/, "") Rails.logger.info "=========================" Rails.logger.info pipeline_yaml pipeline_yaml end private def get_pipeline_file_sha(file_name, branch) file_path_uri = URI.parse(URI.encode(file_name)) interactor = Repositories::EntriesInteractor.call(@project.owner, @project.identifier, file_path_uri, ref: branch || @project.default_branch) if interactor.success? file = interactor.result file['sha'] else nil end end def content_params(opt, rename_file=nil) { filepath: ".gitea/workflows/#{rename_file.present? ? rename_file : @pipeline.pipeline_name}.yml", branch: @pipeline.branch, new_branch: @pipeline.branch, content: opt == "create" ? Base64.encode64(@pipeline.yaml).gsub(/\n/, '') : @pipeline.yaml, message: opt == "create" ? "创建流水线:#{@pipeline.pipeline_name}" : "修改流水线:#{@pipeline.pipeline_name}", committer: { email: current_user.mail, name: current_user.login }, identifier: @project.identifier } end def del_content_params(sha) { filepath: ".gitea/workflows/#{@pipeline.pipeline_name}.yml", base64_filepath: Base64.encode64(".gitea/workflows/#{@pipeline.pipeline_name}.yml").gsub(/\n/, ''), branch: @pipeline.branch, sha: sha } end def build_nodes(params_nodes) steps_nodes = [] params_nodes.each do |input_node| node = Action::Node.find_by(name: input_node["data"]["name"]) next if node.blank? node.label = input_node["data"]["label"] if input_node["data"]["label"].present? node.node_id = input_node["id"] if input_node["id"].present? run_values = {} input_values = {} if input_node["data"]["inputs"].present? input_node["data"]["inputs"].each do |input| if input["name"].to_s.gsub("--", "") == "run" run_values = run_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" }) else input_values = input_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" }) end end node.run_values = run_values node.input_values = input_values end steps_nodes.push(node) end steps_nodes end def get_all_child_nodes(pipeline_json, job_node, target_node) job_node.sub_nodes.push(get_node_info(target_node)) target_edge = pipeline_json["edges"].select { |node| node["source"]["cell"].to_s.include?(target_node["id"]) }&.first # 判断是否有子节点 if target_edge.present? next_node = pipeline_json["nodes"].select { |node| node["id"].include?(target_edge["target"]["cell"]) }.first get_all_child_nodes(pipeline_json, job_node, next_node) end end def get_node_info(input_node) node = Action::Node.find_by(name: input_node["data"]["name"]) return nil if node.blank? node.label = input_node["data"]["label"] if input_node["data"]["label"].present? node.node_id = input_node["id"] if input_node["id"].present? run_values = {} input_values = {} if input_node["data"]["inputs"].present? input_node["data"]["inputs"].each do |input| if input["name"].to_s.gsub("--", "") == "run" run_values = run_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" }) else input_values = input_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" }) end end node.run_values = run_values node.input_values = input_values end node end def demo2 { "nodes": [ { "position": { "x": 290, "y": 190 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "on-push-b2c871f-left", "group": "left" }, { "id": "on-push-b2c871f-right", "group": "right" }, { "id": "on-push-b2c871f-top", "group": "top" }, { "id": "on-push-b2c871f-bottom", "group": "bottom" } ] }, "id": "on-push-b2c871f", "data": { "id": "on-push-b2c871f", "label": "代码push事件启动", "name": "on-push", "full_name": "on-push", "description": "GitLink仓库push事件", "icon": "http://172.20.32.201:4000/api/attachments/c2f255e7-f359-4085-b5ce-1f05850ad74c", "action_node_types_id": 3, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "start", "is_mutil_link": true, "link_type": "job", "inputs": [ { "id": 6, "name": "branches", "input_type": "input", "description": "分支名称,多个分支英文逗号隔开,如'master,dev'", "is_required": true, "value": "master" }, { "id": 7, "name": "paths-ignore", "input_type": "input", "description": "忽略文件,多个文件英文逗号隔开,如'**.md,**.yaml'", "is_required": false, "value": "'*.md'" } ], "x": 673, "y": 495, "img": "http://172.20.32.201:4000/api/attachments/c2f255e7-f359-4085-b5ce-1f05850ad74c", "branches": "master", "paths-ignore": "'*.md'" }, "zIndex": 1, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 680, "y": 190 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "job-ed9ed25-left", "group": "left" }, { "id": "job-ed9ed25-right", "group": "right" }, { "id": "job-ed9ed25-top", "group": "top" }, { "id": "job-ed9ed25-bottom", "group": "bottom" } ] }, "id": "job-ed9ed25", "data": { "id": "job-ed9ed25", "label": "单元测试", "name": "job", "full_name": "", "description": "允许并行多个任务", "icon": "", "action_node_types_id": 7, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "job", "is_mutil_link": true, "link_type": "job,step", "inputs": [], "x": 1093, "y": 513, "img": "" }, "zIndex": 2, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 1090, "y": 190 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "job-3359fd2c-left", "group": "left" }, { "id": "job-3359fd2c-right", "group": "right" }, { "id": "job-3359fd2c-top", "group": "top" }, { "id": "job-3359fd2c-bottom", "group": "bottom" } ] }, "id": "job-3359fd2c", "data": { "id": "job-3359fd2c", "label": "压力测试", "name": "job", "full_name": "", "description": "允许并行多个任务", "icon": "", "action_node_types_id": 7, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "job", "is_mutil_link": true, "link_type": "job,step", "inputs": [], "x": 1493, "y": 540, "img": "" }, "zIndex": 3, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 680, "y": 380 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "shell-79d5ba5d-left", "group": "left" }, { "id": "shell-79d5ba5d-right", "group": "right" }, { "id": "shell-79d5ba5d-top", "group": "top" }, { "id": "shell-79d5ba5d-bottom", "group": "bottom" } ] }, "id": "shell-79d5ba5d", "data": { "id": "shell-79d5ba5d", "label": "运行Shell脚本", "name": "shell", "full_name": "shell", "description": "", "icon": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "action_node_types_id": 4, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "step", "is_mutil_link": false, "link_type": "step", "inputs": [ { "id": 3, "name": "run", "input_type": "input", "is_required": false, "value": "echo 111" } ], "x": 1190, "y": 662, "img": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "run": "echo 111" }, "zIndex": 5, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 1470, "y": 190 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "job-7760b89e-left", "group": "left" }, { "id": "job-7760b89e-right", "group": "right" }, { "id": "job-7760b89e-top", "group": "top" }, { "id": "job-7760b89e-bottom", "group": "bottom" } ] }, "id": "job-7760b89e", "data": { "id": "job-7760b89e", "label": "功能测试", "name": "job", "full_name": "", "description": "允许并行多个任务", "icon": "", "action_node_types_id": 7, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "job", "is_mutil_link": true, "link_type": "job,step", "inputs": [], "x": 1909, "y": 552, "img": "" }, "zIndex": 4, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 1090, "y": 370 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "shell-dd97c50-left", "group": "left" }, { "id": "shell-dd97c50-right", "group": "right" }, { "id": "shell-dd97c50-top", "group": "top" }, { "id": "shell-dd97c50-bottom", "group": "bottom" } ] }, "id": "shell-dd97c50", "data": { "id": "shell-dd97c50", "label": "运行Shell脚本", "name": "shell", "full_name": "shell", "description": "", "icon": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "action_node_types_id": 4, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "step", "is_mutil_link": false, "link_type": "step", "inputs": [ { "id": 3, "name": "run", "input_type": "input", "is_required": false, "value": "echo 444" } ], "x": 1572, "y": 725, "img": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "run": "echo 444" }, "zIndex": 7, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 680, "y": 560 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "shell-799aadee-left", "group": "left" }, { "id": "shell-799aadee-right", "group": "right" }, { "id": "shell-799aadee-top", "group": "top" }, { "id": "shell-799aadee-bottom", "group": "bottom" } ] }, "id": "shell-799aadee", "data": { "id": "shell-799aadee", "label": "唱首歌", "name": "shell", "full_name": "shell", "description": "", "icon": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "action_node_types_id": 4, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "step", "is_mutil_link": false, "link_type": "step", "inputs": [ { "id": 3, "name": "run", "input_type": "input", "is_required": false, "value": "echo 222" } ], "x": 1190, "y": 922, "img": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "run": "echo 222" }, "zIndex": 6, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 1470, "y": 370 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "shell-1766a765-left", "group": "left" }, { "id": "shell-1766a765-right", "group": "right" }, { "id": "shell-1766a765-top", "group": "top" }, { "id": "shell-1766a765-bottom", "group": "bottom" } ] }, "id": "shell-1766a765", "data": { "id": "shell-1766a765", "label": "运行Shell脚本", "name": "shell", "full_name": "shell", "description": "", "icon": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "action_node_types_id": 4, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "step", "is_mutil_link": false, "link_type": "step", "inputs": [ { "id": 3, "name": "run", "input_type": "input", "is_required": false, "value": "echo 666" } ], "x": 1986, "y": 702, "img": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "run": "echo 666" }, "zIndex": 9, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 1090, "y": 550 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "shell-4e09400-left", "group": "left" }, { "id": "shell-4e09400-right", "group": "right" }, { "id": "shell-4e09400-top", "group": "top" }, { "id": "shell-4e09400-bottom", "group": "bottom" } ] }, "id": "shell-4e09400", "data": { "id": "shell-4e09400", "label": "测试报告浏览", "name": "shell", "full_name": "shell", "description": "", "icon": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "action_node_types_id": 4, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "step", "is_mutil_link": false, "link_type": "step", "inputs": [ { "id": 3, "name": "run", "input_type": "input", "is_required": false, "value": "echo 555" } ], "x": 1577, "y": 904, "img": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "run": "echo 555" }, "zIndex": 8, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 680, "y": 760 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "shell-5cec9ec-left", "group": "left" }, { "id": "shell-5cec9ec-right", "group": "right" }, { "id": "shell-5cec9ec-top", "group": "top" }, { "id": "shell-5cec9ec-bottom", "group": "bottom" } ] }, "id": "shell-5cec9ec", "data": { "id": "shell-5cec9ec", "label": "运行Shell脚本", "name": "shell", "full_name": "shell", "description": "", "icon": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "action_node_types_id": 4, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "step", "is_mutil_link": false, "link_type": "step", "inputs": [ { "id": 3, "name": "run", "input_type": "input", "is_required": false, "value": "echo 333" } ], "x": 1189, "y": 1082, "img": "http://172.20.32.201:4000/api/attachments/1d739f94-4e5e-41b8-be7b-13482a099c76", "run": "echo 333" }, "zIndex": 10, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } }, { "position": { "x": 1470, "y": 560 }, "size": { "width": 212, "height": 48 }, "view": "react-shape-view", "shape": "data-processing-dag-node", "ports": { "groups": { "top": { "position": { "name": "top", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "bottom": { "position": { "name": "bottom", "args": { "dx": 0 } }, "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "left": { "position": "left", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } }, "right": { "position": "right", "attrs": { "circle": { "r": 4, "magnet": true, "strokeWidth": 1, "fill": "#fff", "stroke": "#85A5FF" } } } }, "items": [ { "id": "checkout-gitlink-f6e67fe-left", "group": "left" }, { "id": "checkout-gitlink-f6e67fe-right", "group": "right" }, { "id": "checkout-gitlink-f6e67fe-top", "group": "top" }, { "id": "checkout-gitlink-f6e67fe-bottom", "group": "bottom" } ] }, "id": "checkout-gitlink-f6e67fe", "data": { "id": "checkout-gitlink-f6e67fe", "label": "加速版-克隆代码", "name": "checkout-gitlink", "full_name": "https://gitlink.org.cn/actions/checkout@v4", "description": "", "icon": "http://172.20.32.201:4000/api/attachments/c2f255e7-f359-4085-b5ce-1f05850ad74c", "action_node_types_id": 6, "yaml": "", "sort_no": 0, "use_count": 0, "node_type": "step", "is_mutil_link": false, "link_type": "step", "inputs": [], "x": 2017, "y": 887, "img": "http://172.20.32.201:4000/api/attachments/c2f255e7-f359-4085-b5ce-1f05850ad74c" }, "zIndex": 11, "isDelete": true, "tools": { "items": [ { "name": "button-remove", "args": { "x": "100%", "y": 0 } } ] } } ], "edges": [ { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "9cd9fab0-875a-4ddc-9d32-06fb5abcd986", "zIndex": -1, "source": { "cell": "on-push-b2c871f", "port": "on-push-b2c871f-right" }, "target": { "cell": "job-ed9ed25", "port": "job-ed9ed25-left" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "32fee66b-377e-49a6-9f5c-b6a7cb78b4f0", "zIndex": -1, "source": { "cell": "job-ed9ed25", "port": "job-ed9ed25-right" }, "target": { "cell": "job-3359fd2c", "port": "job-3359fd2c-left" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "84b37c54-b347-4ecb-8e7d-f338bb83c503", "zIndex": -1, "source": { "cell": "job-3359fd2c", "port": "job-3359fd2c-right" }, "target": { "cell": "job-7760b89e", "port": "job-7760b89e-left" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "1703563e-2233-4d23-89ec-ed61f4243791", "zIndex": -1, "source": { "cell": "job-ed9ed25", "port": "job-ed9ed25-bottom" }, "target": { "cell": "shell-79d5ba5d", "port": "shell-79d5ba5d-top" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "0be52bf9-8be4-43a9-aaca-1632eaf450ce", "zIndex": -1, "source": { "cell": "shell-79d5ba5d", "port": "shell-79d5ba5d-bottom" }, "target": { "cell": "shell-799aadee", "port": "shell-799aadee-top" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "5b9800bd-5486-4b30-9142-49e6c5a2f78b", "zIndex": -1, "source": { "cell": "job-3359fd2c", "port": "job-3359fd2c-bottom" }, "target": { "cell": "shell-dd97c50", "port": "shell-dd97c50-top" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "0b01ccf2-0ac9-48d5-a5d1-34a4ccc3f1f7", "zIndex": -1, "source": { "cell": "shell-dd97c50", "port": "shell-dd97c50-bottom" }, "target": { "cell": "shell-4e09400", "port": "shell-4e09400-top" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "c10ada7b-ada5-4970-b05d-ea31b97c1408", "zIndex": -1, "source": { "cell": "job-7760b89e", "port": "job-7760b89e-bottom" }, "target": { "cell": "shell-1766a765", "port": "shell-1766a765-top" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "78909862-8e2a-4750-8b5c-c621a7c327ba", "zIndex": -1, "source": { "cell": "shell-799aadee", "port": "shell-799aadee-bottom" }, "target": { "cell": "shell-5cec9ec", "port": "shell-5cec9ec-top" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } }, { "shape": "data-processing-curve", "inherit": "edge", "connector": { "name": "rounded", "args": { "radius": 10 } }, "router": { "name": "manhattan" }, "attrs": { "line": { "strokeDasharray": "0" } }, "id": "cf60eaaf-3503-4c6e-96d8-ca8d273b107b", "zIndex": -1, "source": { "cell": "shell-1766a765", "port": "shell-1766a765-bottom" }, "target": { "cell": "checkout-gitlink-f6e67fe", "port": "checkout-gitlink-f6e67fe-top" }, "tools": { "items": [ { "name": "button-remove", "args": { "distance": -40 } } ] } } ] } end end