|
- class Api::V1::Projects::PipelinesController < Api::V1::BaseController
- before_action :require_operate_above
-
- def index
- @pipelines = Action::Pipeline.where(project_id: @project.id).order(updated_at: :desc)
- @pipelines = paginate @pipelines
- end
-
- def create
- size = Action::Pipeline.where(pipeline_name: params[:pipeline_name], project_id: @project.id).size
- tip_exception("已经存在#{params[:pipeline_name]}流水线!") if size > 0
- @pipeline = Action::Pipeline.new(pipeline_name: params[:pipeline_name], project_id: @project.id)
- @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)
- tip_exception("#{@pipeline.file_name}已存在") if sha
- interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params("create"))
- tip_exception(interactor.error) unless interactor.success?
- render_ok({ id: @pipeline.id })
- end
-
- def save_yaml
- @pipeline = Action::Pipeline.new(pipeline_name: params[:pipeline_name], project_id: @project.id)
- @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
- Rails.logger.info "pipeline_yaml base64=========================#{Base64.encode64(@pipeline.yaml).gsub(/\n/, '')}"
- sha = get_pipeline_file_sha(@pipeline.file_name, @pipeline.branch)
- 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
- render_ok({ pipeline_yaml: pipeline_yaml, pipeline_name: params[: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
- interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, content_params("update"))
- tip_exception(interactor.error) unless interactor.success?
- @pipeline.destroy!
- end
- render_ok
- end
-
- def show
- @pipeline = Action::Pipeline.find_by(id: params[:id])
- @pipeline = Action::Pipeline.new(id: 0, pipeline_name: "test-ss", yaml: build_test_yaml) if @pipeline.blank?
- end
-
- def build_pipeline_yaml(pipeline_name, pipeline_json)
- if pipeline_json.present? && pipeline_json.present?
- @pipeline_name = pipeline_name
- params_nodes = pipeline_json["nodes"].select { |node| !["on-push", "on-schedule"].include?(node["name"]) }
- on_nodes = pipeline_json["nodes"].select { |node| ["on-push", "on-schedule"].include?(node["name"]) }
- @on_nodes = build_nodes(on_nodes)
- @steps_nodes = build_nodes(params_nodes)
- 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/, "")
- else
- pipeline_yaml = params[:pipeline_yaml]
- end
- Rails.logger.info "pipeline_yaml========================="
- Rails.logger.info pipeline_yaml
- pipeline_yaml
- end
-
- def build_test_yaml
- @pipeline_name = "I like it"
- params_nodes = JSON.parse(demo.to_json)["nodes"].select { |node| !["on-push", "on-schedule"].include?(node["name"]) }
- on_nodes = JSON.parse(demo.to_json)["nodes"].select { |node| ["on-push", "on-schedule"].include?(node["name"]) }
- @on_nodes = build_nodes(on_nodes)
- @steps_nodes = []
- params_nodes.each do |input_node|
- # Rails.logger.info "input_node=====0===#{input_node["name"]}======#{input_node["inputs"]}"
- node = Action::Node.find_by(name: input_node["name"])
- next if node.blank?
- node.label = input_node["label"] if input_node["label"].present?
- run_values = {}
- input_values = {}
- if input_node["inputs"].present?
- Rails.logger.info "@inputs=====11===#{input_node["name"]}======#{input_node["inputs"]}"
- input_node["inputs"].each do |input|
- # Rails.logger.info "@inputs.input_name===#{input[:name]}"
- # Rails.logger.info "@inputs.input_value===#{input["value"]}"
- 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
- # Rails.logger.info "@input_values run_values===#{node.run_values.to_json}"
- # Rails.logger.info "@input_values input_values===#{node.input_values.to_json}"
- end
- @steps_nodes.push(node)
- end
- Rails.logger.info "@@on_nodes===#{@on_nodes.to_json}"
- Rails.logger.info "@steps_nodes===#{@steps_nodes.to_json}"
- 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)
- {
- filepath: ".gitea/workflows/#{@pipeline.pipeline_name}.yml",
- branch: @pipeline.branch,
- new_branch: @pipeline.branch,
- content: opt == "create" ? Base64.encode64(@pipeline.yaml).gsub(/\n/, '') : @pipeline.yaml,
- message: "#{opt} pipeline",
- committer: {
- email: current_user.mail,
- name: current_user.login
- },
- identifier: @project.identifier
- }
- end
-
- def build_nodes(params_nodes)
- steps_nodes = []
- params_nodes.each do |input_node|
- node = Action::Node.find_by(name: input_node["name"])
- next if node.blank?
- node.label = input_node["label"] if input_node["label"].present?
- run_values = {}
- input_values = {}
- if input_node["inputs"].present?
- Rails.logger.info "@inputs=====11===#{input_node["name"]}======#{input_node["inputs"]}"
- input_node["inputs"].each do |input|
- # Rails.logger.info "@inputs.input_name===#{input[:name]}"
- # Rails.logger.info "@inputs.input_value===#{input["value"]}"
- 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 demo
- {
- "nodes": [{
- "id": "on-schedule-2fcf505",
- "name": "on-schedule",
- "full_name": "on-schedule",
- "description": " 定时器计划器",
- "icon": "https://testforgeplus.trustie.net/api/attachments/0445403c-5d9e-4495-8414-339f87981ca1",
- "action_node_types_id": 3,
- "yaml": "",
- "sort_no": 0,
- "use_count": 0,
- "inputs": [{
- "id": 8,
- "name": "cron",
- "input_type": "input",
- "description": "示例:\r\n- cron: '20 8 * * *'",
- "is_required": true,
- "value": "- corn: '0 10 * * *'"
- }],
- "x": 586,
- "y": 165.328125,
- "label": "on-schedule",
- "img": "https://testforgeplus.trustie.net/api/attachments/0445403c-5d9e-4495-8414-339f87981ca1",
- "isCluster": false,
- "type": "rect-node",
- "size": [110, 36],
- "labelCfg": {
- "style": {
- "fill": "transparent",
- "fontSize": 0,
- "boxShadow": "0px 0px 12px rgba(75, 84, 137, 0.05)",
- "overflow": "hidden",
- "x": -20,
- "y": 0,
- "textAlign": "left",
- "textBaseline": "middle"
- }
- },
- "style": {
- "active": {
- "fill": "rgb(247, 250, 255)",
- "stroke": "rgb(95, 149, 255)",
- "lineWidth": 2,
- "shadowColor": "rgb(95, 149, 255)",
- "shadowBlur": 10
- },
- "selected": {
- "fill": "rgb(255, 255, 255)",
- "stroke": "rgb(95, 149, 255)",
- "lineWidth": 4,
- "shadowColor": "rgb(95, 149, 255)",
- "shadowBlur": 10,
- "text-shape": {
- "fontWeight": 500
- }
- },
- "highlight": {
- "fill": "rgb(223, 234, 255)",
- "stroke": "#4572d9",
- "lineWidth": 2,
- "text-shape": {
- "fontWeight": 500
- }
- },
- "inactive": {
- "fill": "rgb(247, 250, 255)",
- "stroke": "rgb(191, 213, 255)",
- "lineWidth": 1
- },
- "disable": {
- "fill": "rgb(250, 250, 250)",
- "stroke": "rgb(224, 224, 224)",
- "lineWidth": 1
- },
- "nodeSelected": {
- "fill": "red",
- "shadowColor": "red",
- "stroke": "red",
- "text-shape": {
- "fill": "red",
- "stroke": "red"
- }
- },
- "fill": "#fff",
- "stroke": "transparent",
- "cursor": "pointer",
- "radius": 10,
- "overflow": "hidden",
- "lineWidth": 0.5,
- "shadowColor": "rgba(75,84,137,0.05)",
- "shadowBlur": 12
- },
- "cron": "- corn: '0 10 * * *'",
- "depth": 0
- }, {
- "id": "actions/setup-node@v3-257f29d",
- "name": "node",
- "full_name": "actions/setup-node@v3",
- "description": "",
- "icon": "https://testforgeplus.trustie.net/api/attachments/c4774fc1-ecd9-47fd-9878-1847bdaf98f6",
- "action_node_types_id": 1,
- "yaml": "",
- "sort_no": 0,
- "use_count": 0,
- "inputs": [{
- "id": 2,
- "name": "node-version",
- "input_type": "select",
- "is_required": false,
- "value": 55
- }],
- "x": 608,
- "y": 357.328125,
- "label": "node",
- "img": "https://testforgeplus.trustie.net/api/attachments/c4774fc1-ecd9-47fd-9878-1847bdaf98f6",
- "isCluster": false,
- "type": "rect-node",
- "size": [110, 36],
- "labelCfg": {
- "style": {
- "fill": "transparent",
- "fontSize": 0,
- "boxShadow": "0px 0px 12px rgba(75, 84, 137, 0.05)",
- "overflow": "hidden",
- "x": -20,
- "y": 0,
- "textAlign": "left",
- "textBaseline": "middle"
- }
- },
- "style": {
- "active": {
- "fill": "rgb(247, 250, 255)",
- "stroke": "rgb(95, 149, 255)",
- "lineWidth": 2,
- "shadowColor": "rgb(95, 149, 255)",
- "shadowBlur": 10
- },
- "selected": {
- "fill": "rgb(255, 255, 255)",
- "stroke": "rgb(95, 149, 255)",
- "lineWidth": 4,
- "shadowColor": "rgb(95, 149, 255)",
- "shadowBlur": 10,
- "text-shape": {
- "fontWeight": 500
- }
- },
- "highlight": {
- "fill": "rgb(223, 234, 255)",
- "stroke": "#4572d9",
- "lineWidth": 2,
- "text-shape": {
- "fontWeight": 500
- }
- },
- "inactive": {
- "fill": "rgb(247, 250, 255)",
- "stroke": "rgb(191, 213, 255)",
- "lineWidth": 1
- },
- "disable": {
- "fill": "rgb(250, 250, 250)",
- "stroke": "rgb(224, 224, 224)",
- "lineWidth": 1
- },
- "nodeSelected": {
- "fill": "red",
- "shadowColor": "red",
- "stroke": "red",
- "text-shape": {
- "fill": "red",
- "stroke": "red"
- }
- },
- "fill": "#fff",
- "stroke": "transparent",
- "cursor": "pointer",
- "radius": 10,
- "overflow": "hidden",
- "lineWidth": 0.5,
- "shadowColor": "rgba(75,84,137,0.05)",
- "shadowBlur": 12
- },
- "depth": 0,
- "node-version": 55
- }],
- "edges": [{
- "source": "on-schedule-2fcf505",
- "target": "actions/setup-node@v3-257f29d",
- "style": {
- "active": {
- "stroke": "rgb(95, 149, 255)",
- "lineWidth": 1
- },
- "selected": {
- "stroke": "rgb(95, 149, 255)",
- "lineWidth": 2,
- "shadowColor": "rgb(95, 149, 255)",
- "shadowBlur": 10,
- "text-shape": {
- "fontWeight": 500
- }
- },
- "highlight": {
- "stroke": "rgb(95, 149, 255)",
- "lineWidth": 2,
- "text-shape": {
- "fontWeight": 500
- }
- },
- "inactive": {
- "stroke": "rgb(234, 234, 234)",
- "lineWidth": 1
- },
- "disable": {
- "stroke": "rgb(245, 245, 245)",
- "lineWidth": 1
- },
- "endArrow": {
- "path": "M 6,0 L 9,-1.5 L 9,1.5 Z",
- "d": 4.5,
- "fill": "#CDD0DC"
- },
- "cursor": "pointer",
- "lineWidth": 1,
- "opacity": 1,
- "stroke": "#CDD0DC",
- "radius": 1
- },
- "nodeStateStyle": {
- "hover": {
- "opacity": 1,
- "stroke": "#8fe8ff"
- }
- },
- "labelCfg": {
- "autoRotate": true,
- "style": {
- "fontSize": 10,
- "fill": "#FFF"
- }
- },
- "id": "edge-0.96904321945951241716516719464",
- "startPoint": {
- "x": 586,
- "y": 183.578125,
- "anchorIndex": 1
- },
- "endPoint": {
- "x": 608,
- "y": 339.078125,
- "anchorIndex": 0
- },
- "sourceAnchor": 1,
- "targetAnchor": 0,
- "type": "cubic-vertical",
- "curveOffset": [0, 0],
- "curvePosition": [0.5, 0.5],
- "minCurveOffset": [0, 0]
- }],
- "combos": []
- }
- end
- end
|