464 lines
19 KiB
Ruby
464 lines
19 KiB
Ruby
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
|