main
rb 120 lines 5.26 KB
Raw
1 class PullRequestsController < ApplicationController
2 helper_method :render_markdown
3
4 before_action :load_owner
5 before_action :load_repository
6 before_action :ensure_can_read!
7 before_action :require_sign_in!, only: %i[new create close reopen merge add_comment]
8 before_action :load_pull_request, only: %i[show close reopen merge add_comment]
9
10 def index
11 @state = %w[open closed merged all].include?(params[:state]) ? params[:state] : "open"
12 scope = @repository.pull_requests
13 scope = scope.where(state: @state) unless @state == "all"
14 @pull_requests = scope.order(number: :desc).includes(:user)
15
16 @open_count = @repository.pull_requests.open.count
17 @closed_count = @repository.pull_requests.closed.count
18 @merged_count = @repository.pull_requests.merged.count
19 end
20
21 def new
22 @branches = GitRepositoryService.branches(@repository.disk_path)
23 @pull_request = @repository.pull_requests.new(
24 head_ref: params[:head], base_ref: params[:base].presence || @repository.default_branch
25 )
26 end
27
28 def create
29 pr_params = params.require(:pull_request).permit(:title, :body, :head_ref, :base_ref)
30 @pull_request = PullRequestService.create(
31 repository: @repository, author: current_user,
32 title: pr_params[:title], head: pr_params[:head_ref], base: pr_params[:base_ref], body: pr_params[:body]
33 )
34 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number),
35 notice: "Pull request ##{@pull_request.number} opened."
36 rescue PullRequestService::NotAuthorized => e
37 redirect_to repository_path(@owner.username, @repository.name), alert: e.message
38 rescue PullRequestService::Error, ActiveRecord::RecordInvalid => e
39 @branches = GitRepositoryService.branches(@repository.disk_path)
40 @pull_request = @repository.pull_requests.new(pr_params)
41 @pull_request.errors.add(:base,
42 e.is_a?(ActiveRecord::RecordInvalid) ? e.record.errors.full_messages.to_sentence : e.message)
43 render :new, status: :unprocessable_entity
44 end
45
46 def show
47 @comments = @pull_request.comments.includes(:user).order(:created_at)
48 @commits = GitRepositoryService.commits_between(@repository.disk_path, @pull_request.base_ref, @pull_request.head_ref)
49 @diff = @repository.diff_between(@pull_request.base_ref, @pull_request.head_ref)
50 @mergeable = @pull_request.open? &&
51 GitRepositoryService.mergeable?(@repository.disk_path, @pull_request.base_ref, @pull_request.head_ref)
52 end
53
54 def add_comment
55 CommentService.create(
56 repository: @repository, author: current_user, number: @pull_request.number, body: params[:body]
57 )
58 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number)
59 rescue ActiveRecord::RecordInvalid => e
60 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number),
61 alert: e.record.errors.full_messages.to_sentence
62 end
63
64 def close
65 PullRequestService.close(pull_request: @pull_request, actor: current_user)
66 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number),
67 notice: "Pull request closed."
68 rescue PullRequestService::Error => e
69 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number), alert: e.message
70 end
71
72 def reopen
73 PullRequestService.reopen(pull_request: @pull_request, actor: current_user)
74 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number),
75 notice: "Pull request reopened."
76 rescue PullRequestService::Error => e
77 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number), alert: e.message
78 end
79
80 def merge
81 PullRequestService.merge(pull_request: @pull_request, merger: current_user)
82 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number),
83 notice: "Pull request merged."
84 rescue PullRequestService::Error => e
85 redirect_to repository_pull_request_path(@owner.username, @repository.name, @pull_request.number), alert: e.message
86 end
87
88 private
89
90 # PR/comment bodies are free-standing prose, not tied to a file path, so
91 # they render without the repo/ref/dir context RepositoriesController's
92 # README rendering resolves relative links against.
93 def render_markdown(text)
94 MarkdownRenderer.render(text.to_s).html_safe
95 end
96
97 def load_pull_request
98 @pull_request = @repository.pull_requests.find_by!(number: params[:number])
99 rescue ActiveRecord::RecordNotFound
100 render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
101 end
102
103 def load_owner
104 @owner = User.find_by!(username: params[:username])
105 rescue ActiveRecord::RecordNotFound
106 render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
107 end
108
109 def load_repository
110 @repository = @owner.repositories.find_by!(name: params[:repository])
111 rescue ActiveRecord::RecordNotFound
112 render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
113 end
114
115 def ensure_can_read!
116 unless !@repository.is_private || (signed_in? && current_user == @owner)
117 render file: Rails.public_path.join("404.html"), status: :not_found, layout: false
118 end
119 end
120 end