| @@ -0,0 +1,3 @@ | |||
| class Ci::BaseController < ApplicationController | |||
| before_action :require_login | |||
| end | |||
| @@ -0,0 +1,58 @@ | |||
| class Ci::BuildsController < Ci::BaseController | |||
| include RepositoriesHelper | |||
| before_action :find_project, :find_cloud_account | |||
| before_action :find_cloud_account, except: :get_trustie_pipeline | |||
| before_action :ci_authorize! | |||
| def index | |||
| result = Ci::Drone::API.new(@cloud_account.drone_token, @cloud_account.drone_url, @project.owner.login, @project.identifier).builds | |||
| render json: result | |||
| end | |||
| def detail | |||
| result = Ci::Drone::API.new(@cloud_account.drone_token, @cloud_account.drone_url, @project.owner.login, @project.identifier, number: params[:number]).build | |||
| render json: result | |||
| end | |||
| def restart | |||
| result = Ci::Drone::API.new(@cloud_account.drone_token, @cloud_account.drone_url, @project.owner.login, @project.identifier, number: params[:number]).restart | |||
| render json: result | |||
| end | |||
| def delete | |||
| result = Ci::Drone::API.new(@cloud_account.drone_token, @cloud_account.drone_url, @project.owner.login, @project.identifier, number: params[:number]).stop | |||
| render json: result | |||
| end | |||
| def logs | |||
| result = Ci::Drone::API.new(@cloud_account.drone_token, @cloud_account.drone_url, @project.owner.login, @project.identifier, build: params[:number], stage: params[:stage], step: params[:step]).logs | |||
| render json: result | |||
| end | |||
| # get .trustie-pipeline.yml file | |||
| def get_trustie_pipeline | |||
| file_path_uri = URI.parse('.trustie-pipeline.yml') | |||
| interactor = Repositories::EntriesInteractor.call(@project.owner, @project.identifier, file_path_uri, ref: params[:ref] || "master") | |||
| if interactor.success? | |||
| file = interactor.result | |||
| return render json: {} if file[:status] | |||
| json = {name: file['name'], path: file['path'], sha: file['sha'], content: render_decode64_content(file['content'])} | |||
| render json: json | |||
| end | |||
| end | |||
| private | |||
| def find_project | |||
| @project = Project.find params[:project_id] | |||
| end | |||
| def find_cloud_account | |||
| @cloud_account = @project.ci_cloud_account | |||
| end | |||
| end | |||
| @@ -1,21 +1,20 @@ | |||
| class DevOps::CloudAccountsController < ApplicationController | |||
| class Ci::CloudAccountsController < Ci::BaseController | |||
| include Devopsable | |||
| before_action :require_login | |||
| before_action :auto_load_project | |||
| before_action :devops_authorize! | |||
| before_action :ci_authorize! | |||
| before_action :find_cloud_account, only: %i[activate] | |||
| def create | |||
| ActiveRecord::Base.transaction do | |||
| DevOps::CreateCloudAccountForm.new(devops_params).validate! | |||
| Ci::CreateCloudAccountForm.new(devops_params).validate! | |||
| # 1. 保存华为云服务器帐号 | |||
| create_params = devops_params.merge(ip_num: IPAddr.new(devops_params[:ip_num]).to_i, secret: DevOps::CloudAccount.encrypted_secret(devops_params[:secret])) | |||
| create_params = devops_params.merge(ip_num: IPAddr.new(devops_params[:ip_num]).to_i, secret: Ci::CloudAccount.encrypted_secret(devops_params[:secret])) | |||
| if cloud_account = @project.dev_ops_cloud_account | |||
| return render_error('该仓库已绑定了云帐号.') | |||
| else | |||
| cloud_account = DevOps::CloudAccount.new(create_params) | |||
| cloud_account = Ci::CloudAccount.new(create_params) | |||
| cloud_account.user = current_user | |||
| cloud_account.save! | |||
| end | |||
| @@ -35,15 +34,15 @@ class DevOps::CloudAccountsController < ApplicationController | |||
| logger.info "######### rpc_secret: #{rpc_secret}" | |||
| # 3. 创建drone server | |||
| drone_server_cmd = DevOps::Drone::Server.new(oauth.client_id, oauth.client_secret, cloud_account.drone_host, rpc_secret).generate_cmd | |||
| drone_server_cmd = Ci::Drone::Server.new(oauth.client_id, oauth.client_secret, cloud_account.drone_host, rpc_secret).generate_cmd | |||
| logger.info "######### drone_server_cmd: #{drone_server_cmd}" | |||
| # 4. 创建drone client | |||
| drone_client_cmd = DevOps::Drone::Client.new(oauth.client_id, cloud_account.drone_ip, rpc_secret).generate_cmd | |||
| drone_client_cmd = Ci::Drone::Client.new(oauth.client_id, cloud_account.drone_ip, rpc_secret).generate_cmd | |||
| logger.info "######### drone_client_cmd: #{drone_client_cmd}" | |||
| # 5. 登录远程服务器,启动drone服务 | |||
| result = DevOps::Drone::Start.new(cloud_account.account, cloud_account.visible_secret, cloud_account.drone_ip, drone_server_cmd, drone_client_cmd).run | |||
| result = Ci::Drone::Start.new(cloud_account.account, cloud_account.visible_secret, cloud_account.drone_ip, drone_server_cmd, drone_client_cmd).run | |||
| logger.info "######### result: #{result}" | |||
| @@ -66,11 +65,11 @@ class DevOps::CloudAccountsController < ApplicationController | |||
| result = | |||
| if current_user.devops_has_token? | |||
| # 已有drone_token的,直接激活项目 | |||
| DevOps::Drone::API.new(@cloud_account.drone_token, @cloud_account.drone_url, @project.owner.login, @project.identifier).activate | |||
| Ci::Drone::API.new(@cloud_account.drone_token, @cloud_account.drone_url, @project.owner.login, @project.identifier).activate | |||
| else | |||
| # 没有token,说明是第一次激活devops, 需要用户填写token值 | |||
| return render_error('请先在CI服务端做用户认证.') if !current_user.devops_verified? | |||
| DevOps::Drone::API.new(params[:drone_token], @cloud_account.drone_url, @project.owner.login, @project.identifier).activate | |||
| Ci::Drone::API.new(params[:drone_token], @cloud_account.drone_url, @project.owner.login, @project.identifier).activate | |||
| end | |||
| if result | |||
| @@ -1,21 +1,20 @@ | |||
| class DevOps::LanguagesController < ApplicationController | |||
| class Ci::LanguagesController < Ci::BaseController | |||
| # TODO 需要开启权限认证,只有该项目devops初始化成功后才能获取语言列表 | |||
| before_action :require_login | |||
| before_action :find_langugae, only: :show | |||
| def index | |||
| @languages = DevOps::Language.by_usage_amount_desc | |||
| @languages = Ci::Language.by_usage_amount_desc | |||
| end | |||
| def show | |||
| end | |||
| def common | |||
| @languages = DevOps::Language.six_common | |||
| @languages = Ci::Language.six_common | |||
| end | |||
| private | |||
| def find_langugae | |||
| @language = DevOps::Language.find params[:id] | |||
| @language = Ci::Language.find params[:id] | |||
| end | |||
| end | |||
| @@ -4,8 +4,8 @@ module Devopsable | |||
| included do | |||
| end | |||
| # devops 权限验证 | |||
| def devops_authorize! | |||
| # ci 权限验证 | |||
| def ci_authorize! | |||
| render_forbidden unless @project.owner?(current_user) | |||
| end | |||
| @@ -21,7 +21,7 @@ module Devopsable | |||
| end | |||
| def find_cloud_account | |||
| @cloud_account = DevOps::CloudAccount.find params[:id] | |||
| @cloud_account = Ci::CloudAccount.find params[:id] | |||
| end | |||
| def set_drone_token!(user, cloud_account, drone_token) | |||
| @@ -1,59 +0,0 @@ | |||
| class DevOps::BuildsController < ApplicationController | |||
| include RepositoriesHelper | |||
| before_action :require_login | |||
| before_action :find_project | |||
| before_action :devops_authorize! | |||
| def index | |||
| cloud_account = @project.dev_ops_cloud_account | |||
| result = DevOps::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @project.owner.login, @project.identifier).builds | |||
| render json: result | |||
| end | |||
| def detail | |||
| cloud_account = @project.dev_ops_cloud_account | |||
| result = DevOps::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @project.owner.login, @project.identifier, number: params[:number]).build | |||
| render json: result | |||
| end | |||
| def restart | |||
| cloud_account = @project.dev_ops_cloud_account | |||
| result = DevOps::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @project.owner.login, @project.identifier, number: params[:number]).restart | |||
| render json: result | |||
| end | |||
| def delete | |||
| cloud_account = @project.dev_ops_cloud_account | |||
| result = DevOps::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @project.owner.login, @project.identifier, number: params[:number]).stop | |||
| render json: result | |||
| end | |||
| def logs | |||
| cloud_account = @project.dev_ops_cloud_account | |||
| result = DevOps::Drone::API.new(cloud_account.drone_token, cloud_account.drone_url, @project.owner.login, @project.identifier, build: params[:number], stage: params[:stage], step: params[:step]).logs | |||
| render json: result | |||
| end | |||
| # get .trustie-pipeline.yml file | |||
| def get_trustie_pipeline | |||
| file_path_uri = URI.parse('.trustie-pipeline.yml') | |||
| interactor = Repositories::EntriesInteractor.call(@project.owner, @project.identifier, file_path_uri, ref: params[:ref] || "master") | |||
| if interactor.success? | |||
| file = interactor.result | |||
| return render json: {} if file[:status] | |||
| json = {name: file['name'], path: file['path'], sha: file['sha'], content: render_decode64_content(file['content'])} | |||
| render json: json | |||
| end | |||
| end | |||
| private | |||
| def find_project | |||
| @project = Project.find params[:project_id] | |||
| end | |||
| end | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::CreateCloudAccountForm | |||
| class Ci::CreateCloudAccountForm | |||
| include ActiveModel::Model | |||
| attr_accessor :project_id, :ip_num, :account, :secret | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::Drone::API < DevOps::Drone::Request | |||
| class Ci::Drone::API < Ci::Drone::Request | |||
| attr_reader :drone_token, :endpoint, :owner, :repo, :options | |||
| # drone_token: | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::Drone::Ci | |||
| class Ci::Drone::Ci | |||
| attr_reader :host, :username, :password, :gitea_username | |||
| # host: drone server's ip | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::Drone::Client | |||
| class Ci::Drone::Client | |||
| attr_reader :client_id, :drone_ip, :rpc_secret | |||
| # client_id: user's client_id from oauth | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::Drone::Error < StandardError | |||
| class Ci::Drone::Error < StandardError | |||
| attr_reader :code | |||
| def initialize(code, message) | |||
| @@ -0,0 +1,108 @@ | |||
| class Ci::Drone::Request | |||
| # Converts the response body to an ObjectifiedHash. | |||
| def self.parse(body) | |||
| body = decode(body) | |||
| if body.is_a? Hash | |||
| ObjectifiedHash.new body | |||
| elsif body.is_a? Array | |||
| body.collect! { |e| ObjectifiedHash.new(e) } | |||
| elsif body == true | |||
| body | |||
| else | |||
| raise Error::Parsing.new "Couldn't parse a response body" | |||
| end | |||
| end | |||
| # Decodes a JSON response into Ruby object. | |||
| def self.decode(response) | |||
| begin | |||
| JSON.load response | |||
| rescue JSON::ParserError | |||
| raise Error::Parsing.new "The response is not a valid JSON" | |||
| end | |||
| end | |||
| def get(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:get, endpoint, path, options) | |||
| end | |||
| def post(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:post, endpoint, path, options) | |||
| end | |||
| def put(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:put, endpoint, path, options) | |||
| end | |||
| def patch(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:patch, endpoint, path, options) | |||
| end | |||
| def delete(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:delete, endpoint, path, options) | |||
| end | |||
| private | |||
| def request(method, endpoint, path, **params) | |||
| Rails.logger.info("[drone] request: #{method} #{path} #{params.except(:drone_token).inspect}") | |||
| client ||= begin | |||
| Faraday.new(url: endpoint) do |req| | |||
| req.request :url_encoded | |||
| req.headers['Content-Type'] = 'application/json' | |||
| req.response :logger # 显示日志 | |||
| req.adapter Faraday.default_adapter | |||
| req.authorization :Bearer, params[:drone_token] | |||
| req.headers['Authorization'] | |||
| end | |||
| end | |||
| response = client.public_send(method, path) do |req| | |||
| req.body = params.except(:drone_token).to_json | |||
| end | |||
| json_response(response) | |||
| end | |||
| # Checks the response code for common errors. | |||
| # Returns parsed response for successful requests. | |||
| def validate(response) | |||
| # case response.code | |||
| # when 400; raise Error::BadRequest.new error_message(response) | |||
| # when 401; raise Error::Unauthorized.new error_message(response) | |||
| # when 403; raise Error::Forbidden.new error_message(response) | |||
| # when 404; raise Error::NotFound.new error_message(response) | |||
| # when 405; raise Error::MethodNotAllowed.new error_message(response) | |||
| # when 406; raise Error::DataNotAccepted.new error_message(response) | |||
| # when 409; raise Error::Conflict.new error_message(response) | |||
| # when 500; raise Error::InternalServerError.new error_message(response) | |||
| # when 502; raise Error::BadGateway.new error_message(response) | |||
| # when 503; raise Error::ServiceUnavailable.new error_message(response) | |||
| # end | |||
| response.parsed_response | |||
| end | |||
| # Checks a base_uri and params for requests. | |||
| def validate_request_params!(endpoint) | |||
| raise "Please set an endpoint to API" unless endpoint | |||
| end | |||
| def error_message(response) | |||
| "Server responded with code #{response.code}, message: #{response.parsed_response.message}. " \ | |||
| "Request URI: #{response.request.base_uri}#{response.request.path}" | |||
| end | |||
| def json_response(response) | |||
| result = JSON.parse(response.body) | |||
| status = response.status | |||
| Rails.logger.info("[drone] response:#{status} #{result.inspect}") | |||
| response.status != 200 ? result.merge!(status: response.status) : result | |||
| end | |||
| end | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::Drone::Server | |||
| class Ci::Drone::Server | |||
| attr_reader :client_id, :client_secret, :drone_host, :rpc_secret | |||
| # client_id: user's client_id from oauth | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::Drone::Start | |||
| class Ci::Drone::Start | |||
| attr_reader :drone_username, :drone_password, :drone_host, :drone_server_cmd, :drone_client_cmd | |||
| # drone_username="XXXX" 云服务器登录用户名 | |||
| @@ -1,108 +0,0 @@ | |||
| class DevOps::Drone::Request | |||
| # Converts the response body to an ObjectifiedHash. | |||
| def self.parse(body) | |||
| body = decode(body) | |||
| if body.is_a? Hash | |||
| ObjectifiedHash.new body | |||
| elsif body.is_a? Array | |||
| body.collect! { |e| ObjectifiedHash.new(e) } | |||
| elsif body == true | |||
| body | |||
| else | |||
| raise Error::Parsing.new "Couldn't parse a response body" | |||
| end | |||
| end | |||
| # Decodes a JSON response into Ruby object. | |||
| def self.decode(response) | |||
| begin | |||
| JSON.load response | |||
| rescue JSON::ParserError | |||
| raise Error::Parsing.new "The response is not a valid JSON" | |||
| end | |||
| end | |||
| def get(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:get, endpoint, path, options) | |||
| end | |||
| def post(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:post, endpoint, path, options) | |||
| end | |||
| def put(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:put, endpoint, path, options) | |||
| end | |||
| def patch(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:patch, endpoint, path, options) | |||
| end | |||
| def delete(endpoint, path, options={}) | |||
| validate_request_params!(endpoint) | |||
| request(:delete, endpoint, path, options) | |||
| end | |||
| private | |||
| def request(method, endpoint, path, **params) | |||
| Rails.logger.info("[drone] request: #{method} #{path} #{params.except(:drone_token).inspect}") | |||
| client ||= begin | |||
| Faraday.new(url: endpoint) do |req| | |||
| req.request :url_encoded | |||
| req.headers['Content-Type'] = 'application/json' | |||
| req.response :logger # 显示日志 | |||
| req.adapter Faraday.default_adapter | |||
| req.authorization :Bearer, params[:drone_token] | |||
| req.headers['Authorization'] | |||
| end | |||
| end | |||
| response = client.public_send(method, path) do |req| | |||
| req.body = params.except(:drone_token).to_json | |||
| end | |||
| json_response(response) | |||
| end | |||
| # Checks the response code for common errors. | |||
| # Returns parsed response for successful requests. | |||
| def validate(response) | |||
| # case response.code | |||
| # when 400; raise Error::BadRequest.new error_message(response) | |||
| # when 401; raise Error::Unauthorized.new error_message(response) | |||
| # when 403; raise Error::Forbidden.new error_message(response) | |||
| # when 404; raise Error::NotFound.new error_message(response) | |||
| # when 405; raise Error::MethodNotAllowed.new error_message(response) | |||
| # when 406; raise Error::DataNotAccepted.new error_message(response) | |||
| # when 409; raise Error::Conflict.new error_message(response) | |||
| # when 500; raise Error::InternalServerError.new error_message(response) | |||
| # when 502; raise Error::BadGateway.new error_message(response) | |||
| # when 503; raise Error::ServiceUnavailable.new error_message(response) | |||
| # end | |||
| response.parsed_response | |||
| end | |||
| # Checks a base_uri and params for requests. | |||
| def validate_request_params!(endpoint) | |||
| raise "Please set an endpoint to API" unless endpoint | |||
| end | |||
| def error_message(response) | |||
| "Server responded with code #{response.code}, message: #{response.parsed_response.message}. " \ | |||
| "Request URI: #{response.request.base_uri}#{response.request.path}" | |||
| end | |||
| def json_response(response) | |||
| result = JSON.parse(response.body) | |||
| status = response.status | |||
| Rails.logger.info("[drone] response:#{status} #{result.inspect}") | |||
| response.status != 200 ? result.merge!(status: response.status) : result | |||
| end | |||
| end | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::CloudAccount < ApplicationRecord | |||
| class Ci::CloudAccount < ApplicationRecord | |||
| belongs_to :project | |||
| belongs_to :user | |||
| belongs_to :repository, foreign_key: :repo_id | |||
| @@ -1,4 +1,4 @@ | |||
| class DevOps::Language < ApplicationRecord | |||
| class Ci::Language < ApplicationRecord | |||
| # before_save :encode_content | |||
| belongs_to :cover, class_name: "Attachment", foreign_key: :cover_id, optional: true | |||
| @@ -20,7 +20,7 @@ class Project < ApplicationRecord | |||
| has_many :fork_users, dependent: :destroy | |||
| # has_many :commits, dependent: :destroy | |||
| has_one :dev_ops_cloud_account, class_name: 'DevOps::CloudAccount', dependent: :destroy | |||
| has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', dependent: :destroy | |||
| has_one :project_score, dependent: :destroy | |||
| has_one :repository, dependent: :destroy | |||
| has_many :pull_requests, dependent: :destroy | |||
| @@ -3,7 +3,7 @@ class Repository < ApplicationRecord | |||
| belongs_to :project, :touch => true | |||
| belongs_to :user | |||
| has_one :mirror, foreign_key: :repo_id | |||
| has_one :dev_ops_cloud_account, class_name: 'DevOps::CloudAccount', foreign_key: :repo_id | |||
| has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', foreign_key: :repo_id | |||
| has_many :version_releases, dependent: :destroy | |||
| validates :identifier, presence: true | |||
| @@ -6,6 +6,7 @@ class User < ApplicationRecord | |||
| include BaseModel | |||
| include ProjectOperable | |||
| include ProjectAbility | |||
| include Droneable | |||
| # include Searchable::Dependents::User | |||
| # devops step | |||
| @@ -80,7 +81,7 @@ class User < ApplicationRecord | |||
| has_many :be_watcher_users, through: :be_watchers, dependent: :destroy # 我关注的用户 | |||
| has_many :watchers, as: :watchable, dependent: :destroy | |||
| has_one :dev_ops_cloud_account, class_name: 'DevOps::CloudAccount', dependent: :destroy | |||
| has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', dependent: :destroy | |||
| # 认证 | |||
| has_many :apply_user_authentication | |||
| @@ -15,7 +15,7 @@ Rails.application.routes.draw do | |||
| get 'auth/cas/callback', to: 'oauth/cas#create' | |||
| resources :edu_settings | |||
| scope '/api' do | |||
| namespace :dev_ops do | |||
| namespace :ci do | |||
| resources :cloud_accounts, only: [:create] do | |||
| member do | |||
| post :activate | |||
| @@ -0,0 +1,5 @@ | |||
| class RenameDevOpsCloudAccountToCiCloudAccount < ActiveRecord::Migration[5.2] | |||
| def change | |||
| rename_table :dev_ops_cloud_accounts, :ci_cloud_accounts | |||
| end | |||
| end | |||
| @@ -0,0 +1,5 @@ | |||
| class RenameDevOpsLanguageToCiLanguage < ActiveRecord::Migration[5.2] | |||
| def change | |||
| rename_table :dev_ops_languages, :ci_languages | |||
| end | |||
| end | |||