diff --git a/app/controllers/action/node_inputs_controller.rb b/app/controllers/action/node_inputs_controller.rb index 65227c657..cf108f324 100644 --- a/app/controllers/action/node_inputs_controller.rb +++ b/app/controllers/action/node_inputs_controller.rb @@ -6,7 +6,7 @@ class Action::NodeInputsController < ApplicationController @node_inputs = @node.action_node_inputs respond_to do |format| format.html - format.json + format.json{ render_ok(data: @node_inputs.as_json) } end end diff --git a/app/controllers/action/node_types_controller.rb b/app/controllers/action/node_types_controller.rb index 32508d942..e90bb665c 100644 --- a/app/controllers/action/node_types_controller.rb +++ b/app/controllers/action/node_types_controller.rb @@ -4,6 +4,10 @@ class Action::NodeTypesController < ApplicationController def index @node_types = Action::NodeType.all + respond_to do |format| + format.html + format.json { render_ok(data: @node_types.as_json) } + end end def create @@ -20,7 +24,10 @@ class Action::NodeTypesController < ApplicationController end def show - + respond_to do |format| + format.html + format.json { render_ok(data: @node_type.as_json) } + end end def new @@ -45,7 +52,10 @@ class Action::NodeTypesController < ApplicationController else flash[:danger] = '删除失败' end - redirect_to action_node_types_path + respond_to do |format| + format.html { redirect_to action_node_types_path } + format.json { render_ok } + end end private diff --git a/app/controllers/action/nodes_controller.rb b/app/controllers/action/nodes_controller.rb index d85a6094a..67eb70c2f 100644 --- a/app/controllers/action/nodes_controller.rb +++ b/app/controllers/action/nodes_controller.rb @@ -1,18 +1,25 @@ class Action::NodesController < ApplicationController - before_action :require_admin, except: [:index] + # before_action :require_admin, except: [:index] + before_action :require_login before_action :find_action_node, except: [:index, :create, :new] def index @node_types = Action::NodeType.all + no_node_type = Action::NodeType.find_by(name: "未分类") @no_type_nodes = Action::Node.where(action_node_types_id: nil) + @no_type_nodes = Action::Node.where(action_node_types_id: nil).or(Action::Node.where(action_node_types_id: no_node_type.id)) if no_node_type.present? respond_to do |format| - format.html { @nodes = Action::Node.all } + format.html { @nodes = Action::Node.where("name LIKE :search OR full_name LIKE :search", :search => "%#{params[:search]}%") } format.json end end def create @node = Action::Node.new(node_params) + if params.require(:node).present? && params.require(:node)[:link_type_array].present? + @node.link_type = (params.require(:node)[:link_type_array] - [""]).join(",") + end + @node.user_id = current_user.id respond_to do |format| if @node.save format.html { redirect_to action_nodes_path, notice: '创建成功.' } @@ -33,10 +40,16 @@ class Action::NodesController < ApplicationController end def edit - + if @node.link_type.present? + @node.link_type_array = @node.link_type.to_s.split(",") + end end def update + if params.require(:node).present? && params.require(:node)[:link_type_array].present? + @node.link_type = (params.require(:node)[:link_type_array] - [""]).join(",") + end + @node.user_id = current_user.id if @node.user_id.blank? @node.update(node_params) respond_to do |format| format.html { redirect_to action_nodes_path, notice: '更新成功.' } @@ -50,7 +63,10 @@ class Action::NodesController < ApplicationController else flash[:danger] = '删除失败' end - redirect_to action_nodes_path + respond_to do |format| + format.html { redirect_to action_nodes_path } + format.json { render_ok() } + end end private @@ -61,9 +77,11 @@ class Action::NodesController < ApplicationController def node_params if params.require(:action_node) - params.require(:action_node).permit(:name, :label, :full_name, :description, :icon, :action_node_types_id, :is_local, :local_url, :yaml, :sort_no) + params.require(:action_node).permit(:name, :label, :full_name, :description, :icon, :action_node_types_id, + :is_local, :local_url, :yaml, :sort_no, :node_type, :is_mutil_link, :link_type, :link_type_array) else - params.permit(:name, :label, :full_name, :description, :icon, :action_node_types_id, :is_local, :local_url, :yaml, :sort_no) + params.permit(:name, :label, :full_name, :description, :icon, :action_node_types_id, :is_local, :local_url, + :yaml, :sort_no, :node_type, :is_mutil_link, :link_type, :link_type_array) end end end diff --git a/app/controllers/action/templates_controller.rb b/app/controllers/action/templates_controller.rb index 092d38d64..f7f99b7d7 100644 --- a/app/controllers/action/templates_controller.rb +++ b/app/controllers/action/templates_controller.rb @@ -49,7 +49,10 @@ class Action::TemplatesController < ApplicationController else flash[:danger] = '删除失败' end - redirect_to action_templates_path + respond_to do |format| + format.html { redirect_to action_templates_path } + format.json { render_ok } + end end private diff --git a/app/controllers/admins/nps_controller.rb b/app/controllers/admins/nps_controller.rb index bfb72f730..2817cd0c6 100644 --- a/app/controllers/admins/nps_controller.rb +++ b/app/controllers/admins/nps_controller.rb @@ -2,7 +2,7 @@ class Admins::NpsController < Admins::BaseController before_action :require_business def index @on_off_switch = EduSetting.get("nps-on-off-switch").to_s == 'true' - @user_nps = UserNp.joins(:user).order(created_at: :desc) + @user_nps = UserNp.order(created_at: :desc) keyword = params[:keyword].to_s.strip.presence if keyword sql = 'CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword OR users.login LIKE :keyword OR users.mail LIKE :keyword OR users.phone LIKE :keyword' diff --git a/app/controllers/api/pm/dashboards_controller.rb b/app/controllers/api/pm/dashboards_controller.rb new file mode 100644 index 000000000..2798c3c52 --- /dev/null +++ b/app/controllers/api/pm/dashboards_controller.rb @@ -0,0 +1,137 @@ +class Api::Pm::DashboardsController < Api::Pm::BaseController + before_action :require_login + def index + end + + def todo + return render_error('请输入正确的pm_project_ids.') if params[:pm_project_ids].blank? + pm_project_ids = params[:pm_project_ids].split(",") rescue [] + date = params[:date].present? ? params[:date].to_date : Date.today rescue Date.today + @issues = Issue.where("start_date <= ? and due_date >= ?", date, date) + @issues = @issues.where(pm_project_id: pm_project_ids).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}) + @issues = @issues.where.not(status_id: 5) + @issues = kaminari_paginate(@issues.distinct.pm_includes) + end + + def my_issues + return render_error('请输入正确的pm_project_ids.') if params[:pm_project_ids].blank? + return render_error('请输入正确的pm_issue_types.') if params[:pm_issue_types].blank? + pm_project_ids = params[:pm_project_ids].split(",") rescue [] + pm_issue_types = params[:pm_issue_types].split(",") rescue [] + @all_issues = Issue.where(pm_project_id: pm_project_ids, pm_issue_type: pm_issue_types) + @issues = @all_issues.joins(:issue_participants).where(issue_participants: {participant_id: current_user.id}) + + @issues = kaminari_paginate(@issues.distinct.pm_includes) + @my_assign_requirements_count = @all_issues.where(pm_issue_type: 1).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}).size + @my_assign_tasks_count = @all_issues.where(pm_issue_type: 2).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}).size + @my_assign_bugs_count = @all_issues.where(pm_issue_type: 3).joins(:issue_participants).where(issue_participants: {participant_id: current_user.id, participant_type: 'assigned'}).size + end + + def my_pm_projects + return render_error('请输入正确的pm_project_id.') if params[:pm_project_id].blank? + @all_issues = Issue.where(pm_project_id: params[:pm_project_id]) + time_now = Time.now + @last_week_create_issues_count = @all_issues.where("created_on > ? and created_on < ?", time_now - 7.days, time_now).size + @before_last_week_create_issue_count = @all_issues.where("created_on > ? and created_on < ?", time_now - 14.days, time_now - 7.days).size + @compare_last_week_create_issues = @before_last_week_create_issue_count.zero? ? 0 :(@last_week_create_issues_count - @before_last_week_create_issue_count).to_f / @before_last_week_create_issue_count rescue 0 + @last_week_close_issues_count = @all_issues.where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 7.days, time_now).size + @before_last_week_close_issue_count = @all_issues.where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 14.days, time_now - 7.days).size + @compare_last_week_close_issues = @before_last_week_close_issue_count.zero? ? 0 :(@last_week_close_issues_count - @before_last_week_close_issue_count).to_f / @before_last_week_close_issue_count rescue 0 + @all_requirement_issues_count = @all_issues.where(pm_issue_type: 1).size + @open_requirement_issues_count = @all_issues.where(pm_issue_type: 1).where.not(status_id: 5).size + @last_week_close_requirement_issues_count = @all_issues.where(pm_issue_type: 1).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 7.days, time_now).size + @last_month_close_requirement_issues_count = @all_issues.where(pm_issue_type: 1).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 30.days, time_now).size + @all_task_issues_count = @all_issues.where(pm_issue_type: 2).size + @open_task_issues_count = @all_issues.where(pm_issue_type: 2).where.not(status_id: 5).size + @last_week_close_tast_issues_count = @all_issues.where(pm_issue_type: 2).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 7.days, time_now).size + @last_month_close_task_issues_count = @all_issues.where(pm_issue_type: 2).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 30.days, time_now).size + @all_bug_issues_count = @all_issues.where(pm_issue_type: 3).size + @open_bug_issues_count = @all_issues.where(pm_issue_type: 3).where.not(status_id: 5).size + @last_week_close_bug_issues_count = @all_issues.where(pm_issue_type: 3).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 7.days, time_now).size + @last_month_close_bug_issues_count = @all_issues.where(pm_issue_type: 3).where(status_id: 5).where("updated_on > ? and updated_on < ?", time_now - 30.days, time_now).size + + + @requirement_close_trend = [[],[]] + @task_close_trend = [[],[]] + @bug_close_trend = [[],[]] + ((time_now-29.days).to_date..time_now.to_date).to_a.each do |i| + @requirement_close_trend[0] << i.strftime("%Y.%m.%d") + @task_close_trend[0] << i.strftime("%Y.%m.%d") + @bug_close_trend[0] << i.strftime("%Y.%m.%d") + @requirement_close_trend[1] << @all_issues.where(pm_issue_type: 1, status_id: 5).where("DATE(updated_on) = ?", i).size + @task_close_trend[1] << @all_issues.where(pm_issue_type: 2, status_id: 5).where("DATE(updated_on) = ?", i).size + @bug_close_trend[1] << @all_issues.where(pm_issue_type: 3, status_id: 5).where("DATE(updated_on) = ?", i).size + end + @close_trend = {requirement: @requirement_close_trend, task: @task_close_trend, bug: @bug_close_trend} + + render_ok(data: { + last_week_close_issues_count: @last_week_close_issues_count, + before_last_week_close_issue_count: @before_last_week_close_issue_count, + compare_last_week_close_issues: @compare_last_week_close_issues, + last_week_create_issues_count: @last_week_create_issues_count, + before_last_week_create_issue_count: @before_last_week_create_issue_count, + compare_last_week_create_issues: @compare_last_week_create_issues, + all_requirement_issues_count: @all_requirement_issues_count, + open_requirement_issues_count: @open_requirement_issues_count, + last_week_close_requirement_issues_count: @last_week_close_requirement_issues_count, + last_month_close_requirement_issues_count: @last_month_close_requirement_issues_count, + all_task_issues_count: @all_task_issues_count, + open_task_issues_count: @open_task_issues_count, + last_week_close_task_issues_count: @last_week_close_tast_issues_count, + last_month_close_task_issues_count: @last_month_close_task_issues_count, + all_bug_issues_count: @all_bug_issues_count, + open_bug_issues_count: @open_bug_issues_count, + last_week_close_bug_issues_count: @last_week_close_bug_issues_count, + last_month_close_bug_issues_count: @last_month_close_bug_issues_count, + close_trend: @close_trend + }) + end + + def my_projects + return render_error('请输入正确的project_id.') if params[:project_id].blank? + + @project = Project.find_by_id params[:project_id] + return render_error('请输入正确的project_id.') unless @project.present? + time_now = Time.now + + branch_tag_result = $gitea_hat_client.get_repos_branch_tag_count_by_owner_repo(@project&.owner&.login, @project&.identifier) rescue {} + languages_result = $gitea_client.get_repos_languages_by_owner_repo(@project&.owner&.login, @project&.identifier) rescue {} + + @open_pull_requests_count = @project.pull_requests.opening.size + @last_week_close_pull_requests_count = @project.pull_requests.where(status: 1).where("updated_at > ? and updated_at < ?", time_now - 7.days, time_now).size + @last_month_close_pull_requets_count = @project.pull_requests.where(status: 1).where("updated_at > ? and updated_at < ?", time_now - 30.days, time_now).size + + @commits_count = @project.commit_logs.size + @last_week_commits_count = @project.commit_logs.where("created_at > ? and created_at < ?", time_now - 7.days, time_now).size + @last_month_commits_count = @project.commit_logs.where("created_at > ? and created_at < ?", time_now - 30.days, time_now).size + + render_ok(data: { + branch_count: branch_tag_result["branch_count"].to_i, + tag_count: branch_tag_result["tag_count"].to_i, + license_name: @project.license&.name, + open_pull_requests_count: @open_pull_requests_count, + last_week_close_pull_requests_count: @last_week_close_pull_requests_count, + last_month_close_pull_requets_count: @last_month_close_pull_requets_count, + commits_count: @commits_count, + last_week_commits_count: @last_week_commits_count, + last_month_commits_count: @last_month_commits_count, + language: hash_transform_precentagable(languages_result), + }) + end + + def my_operate_journals + return render_error('请输入正确的pm_project_id.') if params[:pm_project_id].blank? + @journals = Journal.operate_journals.joins(:issue).where(issues: {pm_project_id: params[:pm_project_id], pm_issue_type: [1,2,3]}) + + @journals = kaminari_paginate(@journals.order(updated_on: :desc)) + end + + private + def hash_transform_precentagable(hash) + total_byte_size = hash.values.sum + hash.transform_values { |v| + ActionController::Base.helpers + .number_to_percentage((v * 100.0 / total_byte_size), precision: 1) + }.select{|k,v| v != "0.0%"} + end +end \ No newline at end of file diff --git a/app/controllers/api/pm/issues_controller.rb b/app/controllers/api/pm/issues_controller.rb index a10a70bce..49cb5295c 100644 --- a/app/controllers/api/pm/issues_controller.rb +++ b/app/controllers/api/pm/issues_controller.rb @@ -237,7 +237,47 @@ class Api::Pm::IssuesController < Api::Pm::BaseController end end + def link_issues + children_issues = @issue.pm_issue_type == 1 ? @issue.child_count > 0 ? Issue.where(id: @issue.id) : Issue.none : Issue.where(root_id: @issue.id) + linkable_issues = Issue.where(id: PmLink.where(linkable_type: "Issue", linkable_id: @issue.id).pluck(:be_linkable_id)) + belinkable_issues = Issue.where(id: PmLink.where(be_linkable_type: "Issue", be_linkable_id: @issue.id).pluck(:linkable_id)) + + full_link_issues_ids = children_issues.pluck(:id) | linkable_issues.pluck(:id) | belinkable_issues.pluck(:id) + compare_link_issues_ids = children_issues.pluck(:id) | linkable_issues.pluck(:id) | belinkable_issues.pluck(:id) + i = compare_link_issues_ids.count + while i > 0 do + children_issues = Issue.where(root_id: compare_link_issues_ids) + linkable_issues = Issue.where(id: PmLink.where(linkable_type: "Issue", linkable_id: compare_link_issues_ids).pluck(:be_linkable_id)) + belinkable_issues = Issue.where(id: PmLink.where(be_linkable_type: "Issue", be_linkable_id: compare_link_issues_ids).pluck(:linkable_id)) + + compare_link_issues_ids = (children_issues.pluck(:id) | linkable_issues.pluck(:id) | belinkable_issues.pluck(:id)) - full_link_issues_ids + full_link_issues_ids = full_link_issues_ids | compare_link_issues_ids + i = compare_link_issues_ids.count + end + exclude_issues_ids = [] + exclude_issues = Issue.where(id: full_link_issues_ids).where.not(root_id: nil) + exclude_issues.each do |i| + exclude_issues_ids << i.id if i.pm_issue_type == 1 && full_link_issues_ids.include?(i.root_id) + end + full_link_issues_ids = full_link_issues_ids - exclude_issues_ids + @requirement_issues = Issue.where(id:full_link_issues_ids, pm_issue_type:1, root_id: nil).pm_includes + @task_issues = Issue.where(id:full_link_issues_ids, pm_issue_type:2).pm_includes + @bug_issues = Issue.where(id:full_link_issues_ids, pm_issue_type:3).pm_includes + end + private + def circle_link_issues(issue_ids) + if issue_ids.present? + children_issues = Issue.joins(:parent_issue).where(issues: {id: issue_ids}) + linkable_issues = Issue.where(id: PmLink.where(linkable_type: "Issue", linkable_id: issue_ids)) + belinkable_issues = Issue.where(id: PmLink.where(be_linkable_type: "Issue", be_linkable_id: issue_ids)) + + return circle_link_issues(children_issues.pluck(:id)) + else + return [] + end + end + def check_issue_operate_permission return if params[:project_id].to_i.zero? render_forbidden('您没有操作权限!') unless @project.member?(current_user) || current_user.admin? || @issue.user == current_user diff --git a/app/controllers/api/pm/projects_controller.rb b/app/controllers/api/pm/projects_controller.rb index 8d808bf99..3cece6400 100644 --- a/app/controllers/api/pm/projects_controller.rb +++ b/app/controllers/api/pm/projects_controller.rb @@ -56,12 +56,15 @@ class Api::Pm::ProjectsController < Api::Pm::BaseController def statistics return tip_exception '参数错误' if params[:pm_project_id].blank? @issues = Issue.where(pm_project_id: params[:pm_project_id], pm_issue_type:[1, 2, 3]) + @last_week_close_issues = @issues.where(status_id: 5).where("updated_on > ? and updated_on < ?", Time.now - 7.days, Time.now) + last_week_close_type_count_data = @last_week_close_issues.group(:pm_issue_type).count type_count_data = @issues.group(:pm_issue_type).count type_status = @issues.group(:pm_issue_type,:status_id).count type_status_data = {} IssueStatus.all.map do |e| # next if e.id == 5 [1,2,3].map{ |type| + next if type == 1 && [1, 6].include?(e.id) type_status_data[type] = {} if type_status_data[type].nil? if type_status[[type,e.id]].nil? type_status_data[type][e.id] = 0 @@ -71,9 +74,9 @@ class Api::Pm::ProjectsController < Api::Pm::BaseController } end open_data = { - "1": type_status_data[1][1].to_i + type_status_data[1][2].to_i, - "2": type_status_data[2][1].to_i + type_status_data[2][2].to_i, - "3": type_status_data[3][1].to_i + type_status_data[3][2].to_i, + "1": type_status_data[1][1].to_i + type_status_data[1][2].to_i + type_status_data[1][3].to_i + type_status_data[1][6].to_i, + "2": type_status_data[2][1].to_i + type_status_data[2][2].to_i + type_status_data[2][3].to_i + type_status_data[2][6].to_i, + "3": type_status_data[3][1].to_i + type_status_data[3][2].to_i + type_status_data[3][3].to_i + type_status_data[3][6].to_i, } if type_count_data.keys.size < 3 nedd_add = [1,2,3] - type_count_data.keys @@ -81,17 +84,24 @@ class Api::Pm::ProjectsController < Api::Pm::BaseController type_count_data[e] = 0 } end + if last_week_close_type_count_data.keys.size < 3 + nedd_add = [1,2,3] - last_week_close_type_count_data.keys + nedd_add.map{ |e| + last_week_close_type_count_data[e] = 0 + } + end data = { pie_chart: type_count_data, bar_chart: type_status_data, - open_data: open_data + open_data: open_data, + last_week_close_data: last_week_close_type_count_data, } render_ok(data: data) end def polyline return tip_exception '参数错误' if params[:pm_project_id].blank? - time_line = (Time.current.beginning_of_day - 6.day) .. Time.current + time_line = (Time.current.beginning_of_day - 29.day) .. Time.current @create_issues = Issue.where(pm_project_id: params[:pm_project_id],created_on: time_line) @due_issues = Issue.where(pm_project_id: params[:pm_project_id],status_id:[3,5],due_date: time_line) @create_issues_count = @create_issues.group(:pm_issue_type,"DATE(created_on)").count @@ -100,7 +110,7 @@ class Api::Pm::ProjectsController < Api::Pm::BaseController create_issues: {}, due_issues: {} } - 7.times do |time| + 30.times do |time| current_time = Date.current - time.day if @create_issues_count.present? data[:create_issues][current_time] = { diff --git a/app/controllers/api/v1/projects/actions/actions_controller.rb b/app/controllers/api/v1/projects/actions/actions_controller.rb index 3825b4685..5a34847e2 100644 --- a/app/controllers/api/v1/projects/actions/actions_controller.rb +++ b/app/controllers/api/v1/projects/actions/actions_controller.rb @@ -1,5 +1,49 @@ class Api::V1::Projects::Actions::ActionsController < Api::V1::Projects::Actions::BaseController + def new_index + @files = $gitea_client.get_repos_contents_by_owner_repo_filepath(@project&.owner&.login, @project&.identifier, ".gitea/workflows") + puts @files + @action_runs = Gitea::ActionRun.where(repo_id: @project.gpid, status: [1,2]) + group_data = @action_runs.group(:workflow_id, :status).count + @result = [] + @files.map{|i|i['name']}.each do |file| + 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] == 1 + end + + @result << { + name: file, + last_action_run: last_action_run_json, + history: { + total: total, + success: success, + failure: failure, + } + } + end + + render :json => @result + end + def index begin gitea_result = $gitea_hat_client.get_repos_actions_by_owner_repo(@project&.owner&.login, @project&.identifier) diff --git a/app/controllers/api/v1/projects/pipelines_controller.rb b/app/controllers/api/v1/projects/pipelines_controller.rb index 888e68960..68305a40b 100644 --- a/app/controllers/api/v1/projects/pipelines_controller.rb +++ b/app/controllers/api/v1/projects/pipelines_controller.rb @@ -1,11 +1,21 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController - before_action :require_operate_above + before_action :require_operate_above, except: [:upload_results, :run_results] def index @pipelines = Action::Pipeline.where(project_id: @project.id).order(updated_at: :desc) @pipelines = 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 size = Action::Pipeline.where(pipeline_name: params[:pipeline_name], project_id: @project.id).size tip_exception("已经存在#{params[:pipeline_name]}流水线!") if size > 0 @@ -29,7 +39,7 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController @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]) + pipeline_yaml = build_pipeline_yaml_new(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/, '')}" @@ -37,7 +47,7 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController 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'] }) + 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 @@ -77,62 +87,56 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController 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]) @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"]) } + 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) - @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"]) + @job_nodes = [] + job_nodes.each do |job| + node = Action::Node.find_by(name: job["data"]["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) + 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 - 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 "=========================" @@ -159,7 +163,7 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController branch: @pipeline.branch, new_branch: @pipeline.branch, content: opt == "create" ? Base64.encode64(@pipeline.yaml).gsub(/\n/, '') : @pipeline.yaml, - message: "#{opt} pipeline", + message: opt == "create" ? "创建流水线:#{@pipeline.pipeline_name}" : "修改流水线:#{@pipeline.pipeline_name}", committer: { email: current_user.mail, name: current_user.login @@ -171,20 +175,18 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController def build_nodes(params_nodes) steps_nodes = [] params_nodes.each do |input_node| - node = Action::Node.find_by(name: input_node["name"]) + node = Action::Node.find_by(name: input_node["data"]["name"]) next if node.blank? - node.label = input_node["label"] if input_node["label"].present? + 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["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"]}" }) + 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"]}" }) + input_values = input_values.merge({ "#{input["name"].gsub("--", "")}": "#{input["value"]}" }) end end node.run_values = run_values @@ -195,269 +197,1872 @@ class Api::V1::Projects::PipelinesController < Api::V1::BaseController steps_nodes end - def demo + 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": [{ - "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" - } - }, + "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": "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" - } - }, + "stroke": "#85A5FF" + } + } + }, + "bottom": { + "position": { + "name": "bottom", + "args": { + "dx": 0 + } + }, + "attrs": { + "circle": { + "r": 4, + "magnet": true, + "strokeWidth": 1, "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": [] + "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 diff --git a/app/controllers/organizations/projects_controller.rb b/app/controllers/organizations/projects_controller.rb index e0525eaff..380f32e48 100644 --- a/app/controllers/organizations/projects_controller.rb +++ b/app/controllers/organizations/projects_controller.rb @@ -12,7 +12,7 @@ class Organizations::ProjectsController < Organizations::BaseController keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('') @projects = @projects.where(id: params[:pm_project_repository_ids].split(',')) if params[:pm_project_repository_ids].present? @projects = @projects.where.not(id: params[:exclude_ids].to_s.split(",")) if params[:exclude_ids].present? - @projects = @projects.where("gpid is not null") if params[:actived].present? + @projects = @projects.where(project_type: ['mirror', 'common']).where("gpid is not null") if params[:actived].present? @projects = @projects.ransack(name_or_identifier_cont: keywords).result if params[:search].present? @projects = @projects.includes(:owner).order("projects.#{sort} #{sort_direction}") @projects = paginate(@projects) diff --git a/app/models/action/node.rb b/app/models/action/node.rb index 79c54293d..0dce4cd68 100644 --- a/app/models/action/node.rb +++ b/app/models/action/node.rb @@ -17,11 +17,15 @@ # created_at :datetime not null # updated_at :datetime not null # label :string(255) +# node_type :string(255) +# is_mutil_link :boolean +# link_type :string(255) # # Indexes # -# index_action_nodes_on_action_node_types_id (action_node_types_id) -# index_action_nodes_on_user_id (user_id) +# by_name (name) +# index_action_nodes_on_action_types_id (action_node_types_id) +# index_action_nodes_on_user_id (user_id) # class Action::Node < ApplicationRecord @@ -34,7 +38,7 @@ class Action::Node < ApplicationRecord belongs_to :user, optional: true - attr_accessor :cust_name, :run_values, :input_values + attr_accessor :cust_name, :run_values, :input_values, :parent_node_id, :sub_nodes, :link_type_array, :node_id validates :name, presence: { message: "不能为空" } validates :full_name, length: { maximum: 200, too_long: "不能超过200个字符" } @@ -42,6 +46,7 @@ class Action::Node < ApplicationRecord validates :description, length: { maximum: 65535, too_long: "不能超过65535个字符"} + def content_yaml "foo".to_yaml <<~YAML diff --git a/app/models/action/pipeline_result.rb b/app/models/action/pipeline_result.rb new file mode 100644 index 000000000..e4edc024b --- /dev/null +++ b/app/models/action/pipeline_result.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: action_pipeline_results +# +# id :integer not null, primary key +# project_id :integer +# run_id :integer +# step_id :string(255) +# job_name :string(255) +# job_show_type :string(255) +# job_result :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_action_pipeline_results_on_project_id (project_id) +# index_action_pipeline_results_on_run_id (run_id) +# + +class Action::PipelineResult < ApplicationRecord + self.table_name = 'action_pipeline_results' + belongs_to :project +end + diff --git a/app/models/gitea/action_run.rb b/app/models/gitea/action_run.rb new file mode 100644 index 000000000..b6c8693fc --- /dev/null +++ b/app/models/gitea/action_run.rb @@ -0,0 +1,9 @@ +class Gitea::ActionRun < Gitea::Base + self.inheritance_column = nil # FIX The single-table inheritance mechanism failed + # establish_connection :gitea_db + + self.table_name = "action_run" + + # belongs_to :user, class_name: '::User', primary_key: :gitea_uid, foreign_key: :owner_id, optional: true + +end diff --git a/app/models/issue.rb b/app/models/issue.rb index b7b748514..d1f164d6c 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -102,6 +102,7 @@ class Issue < ApplicationRecord scope :issue_issue, ->{where(issue_classify: [nil, 'issue'])} scope :issue_pull_request, ->{where(issue_classify: 'pull_request')} scope :issue_index_includes, ->{includes(:tracker, :priority, :version, :issue_status, :journals,:issue_tags,user: :user_extension)} + scope :pm_includes, -> {includes(:project, :show_issue_tags, :issue_status, :priority, :version, :user, :show_assigners, :comment_journals, :operate_journals)} scope :closed, ->{where(status_id: 5)} scope :opened, ->{where.not(status_id: 5)} after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic diff --git a/app/models/journal.rb b/app/models/journal.rb index 77c806ca8..196a76165 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -52,6 +52,7 @@ class Journal < ApplicationRecord scope :journal_includes, ->{includes(:user, :journal_details, :attachments)} scope :parent_journals, ->{where(parent_id: nil)} scope :children_journals, lambda{|journal_id| where(parent_id: journal_id)} + scope :operate_journals, ->{where(notes: nil)} enum state: {opened: 0, resolved: 1, disabled: 2} @@ -103,6 +104,21 @@ class Journal < ApplicationRecord end end + def pm_dashboard_operate_content + content = self.pm_operate_content + if content.start_with?('创建了') + content += "#{self.issue.subject}" + else + prefix = '将' + prefix += "计划" if self.issue.pm_issue_type == 1 + prefix += "任务" if self.issue.pm_issue_type == 2 + prefix += "缺陷" if self.issue.pm_issue_type == 3 + prefix += "#{self.issue.subject}" + content = prefix + content.sub('将', '的') + end + content + end + def pm_operate_content content = "#{operate_by_content}" detail = self.journal_details.take diff --git a/app/models/user.rb b/app/models/user.rb index 1fee098fb..a65775225 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -692,7 +692,11 @@ class User < Owner # Returns the user who matches the given autologin +key+ or nil def self.try_to_autologin(key) user = Token.find_active_user('autologin', key) - user.update(last_login_on: Time.now) if user + if user + Rails.cache.fetch("user::update::last_login_on::#{user.id}",:expires_in => 5.minutes) do + user.update(last_login_on: Time.now) + end + end user end diff --git a/app/services/cache/v2/user_date_rank_service.rb b/app/services/cache/v2/user_date_rank_service.rb index b669e4d67..d390d1f5f 100644 --- a/app/services/cache/v2/user_date_rank_service.rb +++ b/app/services/cache/v2/user_date_rank_service.rb @@ -87,14 +87,22 @@ class Cache::V2::UserDateRankService < ApplicationService def set_user_rank set_user_statistic follow_count = $redis_cache.hget(user_date_statistic_key, "follow-count") || 0 + follow_count = follow_count.to_i < 0 ? 0 : follow_count pullrequest_count = $redis_cache.hget(user_date_statistic_key, "pullrequest-count") || 0 + pullrequest_count = pullrequest_count.to_i < 0 ? 0 : pullrequest_count issues_count = $redis_cache.hget(user_date_statistic_key, "issue-count") || 0 + issues_count = issues_count.to_i < 0 ? 0 : issues_count project_count = $redis_cache.hget(user_date_statistic_key, "project-count") || 0 + project_count = project_count.to_i < 0 ? 0 : project_count fork_count = $redis_cache.hget(user_date_statistic_key, "fork-count") || 0 + fork_count = fork_count.to_i < 0 ? 0 : fork_count project_watchers_count = $redis_cache.hget(user_date_statistic_key, "project-watcher-count") || 0 + project_watchers_count = project_watchers_count.to_i < 0 ? 0 : project_watchers_count project_praises_count = $redis_cache.hget(user_date_statistic_key, "project-praise-count") || 0 + project_praises_count = project_praises_count.to_i < 0 ? 0 : project_praises_count project_language = $redis_cache.hget(user_date_statistic_key, "project-language") project_languages_count = project_language.nil? || project_language == "{}" ? 0 : JSON.parse(project_language).length + project_languages_count = project_languages_count.to_i < 0 ? 0 : project_languages_count # 影响力 influence = (60.0 + follow_count.to_i / (follow_count.to_i + 20.0) * 40.0).to_i diff --git a/app/views/action/nodes/_form.html.erb b/app/views/action/nodes/_form.html.erb index 0ecee56ed..7b21bb20f 100644 --- a/app/views/action/nodes/_form.html.erb +++ b/app/views/action/nodes/_form.html.erb @@ -11,7 +11,7 @@ <%# end %>
- + <%= form.select :action_node_types_id, options_for_select(Action::NodeType.all.map { |key| [key.name, key.id]}, node.action_node_types_id), {}, class: "form-control" %>
@@ -26,6 +26,22 @@ <%= form.label :full_name, "节点全名" %> <%= form.text_field :full_name %>
+
+ + <% node_type_options = [['action节点', 'step' ], ['启动', 'start' ], ['任务', 'job']] %> + <%= form.select :node_type, options_for_select(node_type_options, node.node_type), {}, class: "form-control" %> +
+
+ + <% is_mutil_link_options = [['否', false ], ['是', true]] %> + <%= form.select :is_mutil_link, options_for_select(is_mutil_link_options, node.is_mutil_link), {}, class: "form-control" %> +
+
+ + <% link_type_options = [Action::Node.new(name: "job", label: "任务"), Action::Node.new(name: "step", label: "action节点")] %> + <%= collection_check_boxes(:node, :link_type_array, link_type_options, :name, :label) %> +
+
<%= form.label :description, "描述" %> diff --git a/app/views/action/nodes/index.html.erb b/app/views/action/nodes/index.html.erb index ce603590b..60ad9489a 100644 --- a/app/views/action/nodes/index.html.erb +++ b/app/views/action/nodes/index.html.erb @@ -5,6 +5,15 @@

>>前往模板配置

说明:该界面适用于action 节点配置参数配置

+
+ <%= form_tag(action_nodes_path, method: :get, class: 'form-inline search-form flex-1') do %> + <%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '关键字检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + + <% end %> + <%= link_to "新增", new_action_node_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> +
+ diff --git a/app/views/action/nodes/index.json.jbuilder b/app/views/action/nodes/index.json.jbuilder index fc8a38505..835c7f145 100644 --- a/app/views/action/nodes/index.json.jbuilder +++ b/app/views/action/nodes/index.json.jbuilder @@ -2,7 +2,7 @@ json.types @node_types.each do |node_type| if node_type.name.to_s == "未分类" json.extract! node_type, :id, :name json.nodes @no_type_nodes do |node| - json.extract! node, :id, :label, :name, :full_name, :description, :icon, :action_node_types_id, :yaml, :sort_no, :use_count + json.extract! node, :id, :label, :name, :full_name, :description, :icon, :action_node_types_id, :yaml, :sort_no, :use_count, :node_type, :is_mutil_link, :link_type json.inputs node.action_node_inputs do |node_input| json.partial! "node_input", locals: { node_input: node_input, node: node } end @@ -10,7 +10,7 @@ json.types @node_types.each do |node_type| else json.extract! node_type, :id, :name json.nodes node_type.action_nodes do |node| - json.extract! node, :id, :label, :name, :full_name, :description, :icon, :action_node_types_id, :yaml, :sort_no, :use_count + json.extract! node, :id, :label, :name, :full_name, :description, :icon, :action_node_types_id, :yaml, :sort_no, :use_count, :node_type, :is_mutil_link, :link_type json.inputs node.action_node_inputs do |node_input| json.partial! "node_input", locals: { node_input: node_input, node: node } end diff --git a/app/views/action/nodes/show.json.jbuilder b/app/views/action/nodes/show.json.jbuilder index 64548544d..e731dbd0b 100644 --- a/app/views/action/nodes/show.json.jbuilder +++ b/app/views/action/nodes/show.json.jbuilder @@ -1,7 +1,7 @@ json.status 0 json.message "success" -json.extract! @node, :id, :name, :full_name, :description, :action_node_types_id, :is_local, :local_url, :yaml, :sort_no, :use_count +json.extract! @node, :id, :name, :full_name, :description, :icon, :action_node_types_id, :is_local, :local_url, :yaml, :sort_no, :use_count, :label, :node_type, :is_mutil_link, :link_type json.inputs @node.action_node_inputs do |node_input| json.partial! "node_input", locals: { node_input: node_input, node: @node } end \ No newline at end of file diff --git a/app/views/admins/nps/_user_np_list.html.erb b/app/views/admins/nps/_user_np_list.html.erb index f2d5196e9..474fe8b86 100644 --- a/app/views/admins/nps/_user_np_list.html.erb +++ b/app/views/admins/nps/_user_np_list.html.erb @@ -12,15 +12,15 @@ <% if user_nps.present? %> <% user_nps.each_with_index do |nps, index| %> - + "> - + diff --git a/app/views/admins/nps/index.html.erb b/app/views/admins/nps/index.html.erb index 81e11dd2f..4a6e95168 100644 --- a/app/views/admins/nps/index.html.erb +++ b/app/views/admins/nps/index.html.erb @@ -37,6 +37,21 @@ 提升用户体验:<%= UserNp.where("memo like '%用户体验需进一步提升%'").count %>, 其他:<%= UserNp.where("action_type !='close'").where("LENGTH(memo) >0").where.not(id: UserNp.where("memo like '%期待更加丰富的功能%' or memo like '%希望有新手引导%' or memo like '%用户体验需进一步提升%' ").ids).count %>

+

+ 代码库基本功能:<%= UserNp.where("memo like '%代码库基本功能%'").count %>, + 疑修:<%= UserNp.where("memo like '%疑修%'").count %>, + 合并请求:<%= UserNp.where("memo like '%合并请求%'").count %>, + 流水线引擎:<%= UserNp.where("memo like '%流水线引擎%'").count %>, + 维基Wiki:<%= UserNp.where("memo like '%维基Wiki%'").count %>, + 数据集:<%= UserNp.where("memo like '%数据集%'").count %>, + 特色专区:<%= UserNp.where("memo like '%特色专区%'").count %>, + BOT功能:<%= UserNp.where("memo like '%BOT功能%'").count %>, + 跨平台同步服务:<%= UserNp.where("memo like '%跨平台同步服务%'").count %>, + 代码溯源及扫描服务:<%= UserNp.where("memo like '%代码溯源及扫描服务%'").count %>, + 开源软件健康度量服务:<%= UserNp.where("memo like '%开源软件健康度量服务%'").count %>, + HiAgent:<%= UserNp.where("memo like '%HiAgent%'").count %>, + 非常满意,没有需要吐槽的功能!:<%= UserNp.where("memo like '%非常满意,没有需要吐槽的功能%'").count %> +

<%= render partial: 'admins/nps/user_np_list', locals: { user_nps: @user_nps } %> diff --git a/app/views/api/pm/dashboards/my_issues.json.jbuilder b/app/views/api/pm/dashboards/my_issues.json.jbuilder new file mode 100644 index 000000000..dfce9c664 --- /dev/null +++ b/app/views/api/pm/dashboards/my_issues.json.jbuilder @@ -0,0 +1,11 @@ +json.status 0 +json.message "success" +json.data do + json.total_count @issues.total_count + json.my_assign_requirements_count @my_assign_requirements_count + json.my_assign_tasks_count @my_assign_tasks_count + json.my_assign_bugs_count @my_assign_bugs_count + json.issues @issues.each do |issue| + json.partial! "api/v1/issues/simple_detail", locals: {issue: issue} + end +end \ No newline at end of file diff --git a/app/views/api/pm/dashboards/my_operate_journals.json.jbuilder b/app/views/api/pm/dashboards/my_operate_journals.json.jbuilder new file mode 100644 index 000000000..0d0aeb18f --- /dev/null +++ b/app/views/api/pm/dashboards/my_operate_journals.json.jbuilder @@ -0,0 +1,25 @@ +json.status 0 +json.message "success" +json.data do + json.total_count @journals.total_count + json.journals @journals do |journal| + journal.associate_attachment_container + json.id journal.id + json.is_journal_detail journal.is_journal_detail? + json.created_at journal.created_on.strftime("%Y-%m-%d %H:%M") + json.updated_at journal.updated_on.strftime("%Y-%m-%d %H:%M") + json.created_time journal.created_on.to_i + json.updated_time journal.updated_on.to_i + json.user do + if journal.user.present? + json.partial! "api/v1/users/simple_user", user: journal.user + else + json.nil! + end + end + detail = journal.journal_details.take + json.operate_category journal.pm_operate_category + json.operate_content journal.is_journal_detail? ? journal.pm_dashboard_operate_content : nil + + end +end diff --git a/app/views/api/pm/dashboards/todo.json.jbuilder b/app/views/api/pm/dashboards/todo.json.jbuilder new file mode 100644 index 000000000..59f331531 --- /dev/null +++ b/app/views/api/pm/dashboards/todo.json.jbuilder @@ -0,0 +1,10 @@ +json.status 0 +json.message "success" +json.data do + json.total_count @issues.total_count + + json.issues @issues.each do |issue| + json.partial! "api/v1/issues/simple_detail", locals: {issue: issue} + end + +end \ No newline at end of file diff --git a/app/views/api/pm/issues/link_issues.json.jbuilder b/app/views/api/pm/issues/link_issues.json.jbuilder new file mode 100644 index 000000000..f20208bf6 --- /dev/null +++ b/app/views/api/pm/issues/link_issues.json.jbuilder @@ -0,0 +1,11 @@ +json.requirement_issues @requirement_issues.each do |issue| + json.partial! "api/v1/issues/simple_detail", locals: {issue: issue} +end + +json.task_issues @task_issues.each do |issue| + json.partial! "api/v1/issues/simple_detail", locals: {issue: issue} +end + +json.bug_issues @bug_issues.each do |issue| + json.partial! "api/v1/issues/simple_detail", locals: {issue: issue} +end \ No newline at end of file diff --git a/app/views/api/v1/issues/_simple_detail.json.jbuilder b/app/views/api/v1/issues/_simple_detail.json.jbuilder index f3a4a12fd..2c05acf7f 100644 --- a/app/views/api/v1/issues/_simple_detail.json.jbuilder +++ b/app/views/api/v1/issues/_simple_detail.json.jbuilder @@ -38,4 +38,6 @@ end json.assigners issue.show_assigners.each do |assigner| json.partial! "api/v1/users/simple_user", locals: {user: assigner} end -json.comment_journals_count issue.comment_journals.size \ No newline at end of file +json.comment_journals_count issue.comment_journals.size +json.start_date issue.start_date +json.due_date issue.due_date \ No newline at end of file diff --git a/app/views/api/v1/projects/pipelines/_jobs.yaml.erb b/app/views/api/v1/projects/pipelines/_jobs.yaml.erb new file mode 100644 index 000000000..4f54cdc0a --- /dev/null +++ b/app/views/api/v1/projects/pipelines/_jobs.yaml.erb @@ -0,0 +1,29 @@ +jobs: +<%@job_nodes.each_with_index do |job,index| %> + job<%=index + 1 %>: + name: "<%=job.label || job.name %>" + # 运行环境,这里就是上面定义的多个 os + runs-on: 'ubuntu-latest' + <% if index >0 %> + needs: job<%=index %> + <% end %> + steps: + <%job.sub_nodes.each do |node| %> + - name: <%=node.label || node.name %> + <% if node.name !="shell" %> + uses: <%=node.full_name %> + <% end %> + <%if node.input_values.present? %> + with: + <% node.input_values.each_key do |key| %> + <%=key %>: '<%=node.input_values[key] %>' + <%end %> + <%end %> + <%if node.run_values.present? %> + <% node.run_values.each_key do |key| %> + <%=key %>: | + <%=node.run_values[key] %> + <%end %> + <%end %> + <% end %> +<%end %> \ No newline at end of file diff --git a/app/views/api/v1/projects/pipelines/_test_job.yaml.erb b/app/views/api/v1/projects/pipelines/_test_job.yaml.erb new file mode 100644 index 000000000..f9b905e9e --- /dev/null +++ b/app/views/api/v1/projects/pipelines/_test_job.yaml.erb @@ -0,0 +1,51 @@ +# steps: + # 将job的工作目录指向$GITHUB_WORSPACES checkout@v2比较旧不推荐使用 + - name: Checkout codes + uses: actions/checkout@v3 + - name: Install Java and Maven + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + # 设置jdk环境 + - name: download latest temurin JDK + id: download_latest_jdk + env: + HTTPS_PROXY: http://172.20.32.253:3128 + HTTP_PROXY: http://172.20.32.253:3128 + #run: curl -o https://testgitea2.trustie.net/xxq250/licensee-identify-api-sss/raw/branch/master/openlogic-openjdk-11.0.22+7-linux-x64.tar.gz + run: wget -O $RUNNER_TEMP/java_package.tar.gz "http://172.20.32.202:10082/xxq250/licensee-identify-api-sss/raw/branch/master/OpenJDK11U-jdk_x64_linux_hotspot_11.0.22_7.tar.gz" + - uses: actions/setup-java@v4 + with: + distribution: 'jdkfile' + jdkFile: ${{ runner.temp }}/java_package.tar.gz + java-version: '11.0.0' + architecture: x64 + mvn-toolchain-vendor: 'Oracle' + # 设置maven 仓库缓存 避免每次构建时都重新下载依赖 + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Test java version + run: java -version + - name: Set up Maven + uses: stCarolas/setup-maven@v5 + with: + maven-version: 3.8.2 + - name: Setup Maven mirrors + uses: s4u/maven-settings-action@v3.0.0 + with: + mirrors: '[{"id": "alimaven", "name": "aliyun maven", "mirrorOf": "central", "url": "http://172.20.32.181:30005/repository/aliyun-maven/"}]' + - name: env show + run: env + - name: cat maven-settings.xml + run: cat /root/.m2/settings.xml + - name: Test with Maven + run: mvn clean test -B -U + env: + https_proxy: http://172.20.32.253:3128 + http_proxy: http://172.20.32.253:3128 diff --git a/app/views/api/v1/projects/pipelines/build_pipeline.yaml.erb b/app/views/api/v1/projects/pipelines/build_pipeline.yaml.erb index cbe7a8333..4d3bf7738 100644 --- a/app/views/api/v1/projects/pipelines/build_pipeline.yaml.erb +++ b/app/views/api/v1/projects/pipelines/build_pipeline.yaml.erb @@ -1,15 +1,12 @@ -# action name -name: <%=@pipeline_name %> - -# 什么时候触发这个workflow +name: "<%=@pipeline_name %>" on: <%@on_nodes.each do |node| %> - <%if node.name.to_s.include?("on-push") %> + <%if node.name.to_s.include?("on-push") && node.input_values.present? %> push: <% node.input_values.each_key do |key| %> <%=key %>: <% if node.input_values[key].blank? %> - - * + - '*' <% else %> <% node.input_values[key].to_s.split(",").each do |val| %> - <%=val %> @@ -17,20 +14,20 @@ on: <% end %> <% end %> <% end %> - <%if node.name.to_s.include?("on-pull_request") %> + <%if node.name.to_s.include?("on-pull_request") && node.input_values.present? %> pull_request: <% node.input_values.each_key do |key| %> <%=key %>: <% if node.input_values[key].blank? %> - - * + - '*' <% else %> <% node.input_values[key].to_s.split(",").each do |val| %> - - <%=val %> + - <%=val %> <% end %> <% end %> <% end %> <% end %> - <%if node.name.to_s.include?("on-schedule") %> + <%if node.name.to_s.include?("on-schedule") && node.input_values.present? %> schedule: <% node.input_values.each_key do |key| %> - <%=key %>: "<%=node.input_values[key] %>" @@ -39,16 +36,21 @@ on: <% end %> jobs: - job1: - # 运行环境 +<%@job_nodes.each_with_index do |job,index| %> + <%=job.node_id %>: + name: "<%=job.label || job.name %>[<%=job.node_id %>]" + # 运行环境,这里就是上面定义的多个 os runs-on: 'ubuntu-latest' + <% if index >0 %> + needs: <%=job.parent_node_id %> + <% end %> steps: - <%@steps_nodes.each do |node| %> - - name: <%=node.label || node.name %> - <% if node.name !="shell" %> + <%job.sub_nodes.each do |node| %> + - name: "<%=node.label || node.name %>[<%=node.node_id %>]" + <% if node.name !="shell" %> uses: <%=node.full_name %> - <% end %> - <%if node.input_values.present? %> + <% end %> + <%if node.input_values.present? %> with: <% node.input_values.each_key do |key| %> <%=key %>: '<%=node.input_values[key] %>' @@ -56,7 +58,9 @@ jobs: <%end %> <%if node.run_values.present? %> <% node.run_values.each_key do |key| %> - <%=key %>: '<%=node.run_values[key] %>' + <%=key %>: | + <%=node.run_values[key] %> <%end %> <%end %> <% end %> +<%end %> diff --git a/app/views/api/v1/projects/pipelines/test.yaml.erb b/app/views/api/v1/projects/pipelines/test.yaml.erb index 3ec890230..0411ba6cd 100644 --- a/app/views/api/v1/projects/pipelines/test.yaml.erb +++ b/app/views/api/v1/projects/pipelines/test.yaml.erb @@ -1,22 +1,20 @@ -name: Check dist<%= @name %> - # action name name: Test with Junit # 什么时候触发这个workflow on: # push 到master分之的时候 这里可以指定多个 - #push: - # branches: - # - master -# paths-ignore: -# - '**.md' + push: + branches: + - master + paths-ignore: + - '**.md' # pull request 到master分之的时候, 这里可以指定多个 - #pull_request: - # branches: - # - master -# paths-ignore: -# - '**.md' + pull_request: + branches: + - master + paths-ignore: + - '**.md' # 定时调度执行 schedule: - cron: '26 10,11 * * *' @@ -35,16 +33,15 @@ jobs: os: [ 'ubuntu-latest' ] # 运行环境,这里就是上面定义的多个 os runs-on: 'ubuntu-latest' - steps: # 将job的工作目录指向$GITHUB_WORSPACES checkout@v2比较旧不推荐使用 - name: Checkout codes uses: actions/checkout@v3 - #- name: Install Java and Maven - # uses: actions/setup-java@v3 - # with: - # java-version: '11' - # distribution: 'temurin' + - name: Install Java and Maven + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' # 设置jdk环境 - name: download latest temurin JDK id: download_latest_jdk diff --git a/app/views/api/v1/projects/pipelines/test_yaml.yaml.erb b/app/views/api/v1/projects/pipelines/test_yaml.yaml.erb new file mode 100644 index 000000000..14ac79a4d --- /dev/null +++ b/app/views/api/v1/projects/pipelines/test_yaml.yaml.erb @@ -0,0 +1,26 @@ +# action name +name: Test with Junit + +# 什么时候触发这个workflow +on: + # push 到master分之的时候 这里可以指定多个 + push: + branches: + - master + paths-ignore: + - '**.md' + # pull request 到master分之的时候, 这里可以指定多个 + pull_request: + branches: + - master + paths-ignore: + - '**.md' + # 定时调度执行 + schedule: + - cron: '26 10,11 * * *' +env: + https_proxy: http://172.20.32.253:3128 + http_proxy: http://172.20.32.253:3128 + +# 一个workflow可以由多个job组成,多个job可以并行运行 +<%= render(partial: 'api/v1/projects/pipelines/jobs') %> diff --git a/config/routes/api.rb b/config/routes/api.rb index 4aaa4beab..ec8031fd8 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -1,6 +1,15 @@ defaults format: :json do namespace :api do namespace :pm do + resources :dashboards,only: [:index] do + collection do + get :todo + get :my_issues + get :my_pm_projects + get :my_projects + get :my_operate_journals + end + end resources :issues do collection do patch :batch_update @@ -14,6 +23,7 @@ defaults format: :json do member do get :link_index get :parent_issues + get :link_issues end resources :issue_links @@ -152,6 +162,7 @@ defaults format: :json do resource :dataset, only: [:create, :update, :show] resources :actions, module: 'actions' do collection do + get :new_index post :disable post :enable resources :runs, only: [:index, :create] do @@ -165,6 +176,9 @@ defaults format: :json do resources :pipelines do post :build_yaml, on: :collection post :save_yaml, on: :collection + post :upload_results, on: :collection + get :run_results, on: :collection + get :test_yaml, on: :collection end resources :pulls, module: 'pulls' do member do diff --git a/db/migrate/202412030203041_add_action_nodes_type.rb b/db/migrate/202412030203041_add_action_nodes_type.rb new file mode 100644 index 000000000..bc7d77f4f --- /dev/null +++ b/db/migrate/202412030203041_add_action_nodes_type.rb @@ -0,0 +1,7 @@ +class AddActionNodesType < ActiveRecord::Migration[5.2] + def change + add_column :action_nodes, :node_type, :string + add_column :action_nodes, :is_mutil_link, :boolean + add_column :action_nodes, :link_type, :string + end +end diff --git a/db/migrate/20241214121789_create_action_pipeline_results.rb b/db/migrate/20241214121789_create_action_pipeline_results.rb new file mode 100644 index 000000000..4470f5cc1 --- /dev/null +++ b/db/migrate/20241214121789_create_action_pipeline_results.rb @@ -0,0 +1,15 @@ +class CreateActionPipelineResults < ActiveRecord::Migration[5.2] + def change + create_table :action_pipeline_results do |t| + t.references :project + t.integer :run_id + t.string :step_id + t.string :job_name + t.string :job_show_type + t.text :job_result + t.timestamps + end + + add_index :action_pipeline_results, :run_id + end +end
<%= list_index_no((params[:page] || 1).to_i, index) %> - <%= link_to "/#{nps.user.login}", target: '_blank' do %> - <%= overflow_hidden_span nps.user.real_name, width: 100 %> + <%= link_to "/#{nps.user.nil? ? "用户已注销" : nps.user.login}", target: '_blank' do %> + <%= overflow_hidden_span (nps.user.nil? ? "用户已注销" : nps.user.real_name), width: 100 %> <% end %> <%= display_text(nps.created_at&.strftime('%Y-%m-%d %H:%M')) %><%= display_text(nps.user.last_login_on&.strftime('%Y-%m-%d %H:%M')) %><%= display_text(nps.user.nil? ? "用户已注销" : nps.user.last_login_on&.strftime('%Y-%m-%d %H:%M')) %> <%= nps.action_type == 'close' ? '--' : nps.score %> <%= nps.memo %>