From 27f2ca94181880861269a7ddc07ae0d50a656d35 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 29 Jan 2017 13:38:00 -0600 Subject: [PATCH] Add 'View on [env]' link to blobs and individual files in diffs --- app/controllers/projects/blob_controller.rb | 3 ++ app/controllers/projects/commit_controller.rb | 3 ++ .../projects/compare_controller.rb | 4 ++ .../projects/merge_requests_controller.rb | 8 +++- app/helpers/commits_helper.rb | 11 +++++ app/models/ci/pipeline.rb | 8 +--- app/models/environment.rb | 18 +++++++ app/models/merge_request.rb | 6 +++ app/models/project.rb | 47 +++++++++++++++++-- app/models/repository.rb | 17 +++++++ app/views/projects/blob/_actions.html.haml | 3 ++ app/views/projects/commit/show.html.haml | 2 +- app/views/projects/compare/show.html.haml | 2 +- app/views/projects/diffs/_diffs.html.haml | 3 +- app/views/projects/diffs/_file.html.haml | 2 + .../merge_requests/_new_diffs.html.haml | 2 +- .../merge_requests/show/_diffs.html.haml | 2 +- changelogs/unreleased/route-map.yml | 4 ++ 18 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 changelogs/unreleased/route-map.yml diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 9940263ae24..1bdb8d45984 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -30,6 +30,9 @@ class Projects::BlobController < Projects::ApplicationController end def show + branch_name = @ref if @repository.branch_exists?(@ref) + @environment = @project.latest_environment_for(@commit, ref: branch_name) + @environment = nil unless can?(current_user, :read_environment, @environment) end def edit diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index b5a7078a3a1..08817afa1e9 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -95,6 +95,9 @@ class Projects::CommitController < Projects::ApplicationController @diffs = commit.diffs(opts) @notes_count = commit.notes.count + + @environment = @project.latest_environment_for(@commit) + @environment = nil unless can?(current_user, :read_environment, @environment) end def define_note_vars diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 321cde255c3..6c94a79f842 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -57,6 +57,10 @@ class Projects::CompareController < Projects::ApplicationController @diffs = @compare.diffs(diff_options) + branch_name = @head_ref if @repository.branch_exists?(@head_ref) + @environment = @project.latest_environment_for(@commit, ref: branch_name) + @environment = nil unless can?(current_user, :read_environment, @environment) + @diff_notes_disabled = true @grouped_diff_discussions = {} end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 6eb542e4bd8..6a6d24db35d 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -103,6 +103,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end + @environment = @merge_request.latest_environment + @environment = nil unless can?(current_user, :read_environment, @environment) + respond_to do |format| format.html { define_discussion_vars } format.json do @@ -245,7 +248,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController end @diff_notes_disabled = true - render json: { html: view_to_html_string('projects/merge_requests/_new_diffs', diffs: @diffs) } + @environment = @merge_request.latest_environment + @environment = nil unless can?(current_user, :read_environment, @environment) + + render json: { html: view_to_html_string('projects/merge_requests/_new_diffs', diffs: @diffs, environment: @environment) } end end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 6dcb624c4da..a5cf4ddb6a3 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -205,6 +205,17 @@ module CommitsHelper end end + def view_on_environment_btn(commit_sha, diff_new_path, environment) + return unless environment && commit_sha + + external_url = environment.external_url_for(diff_new_path, commit_sha) + return unless external_url + + link_to(external_url, class: 'btn btn-file-option has-tooltip', target: '_blank', title: "View on #{environment.formatted_external_url}", data: { container: 'body' }) do + icon('external-link') + end + end + def truncate_sha(sha) Commit.truncate_sha(sha) end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index fab8497ec7d..8db53ea56dd 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -283,13 +283,7 @@ module Ci def ci_yaml_file return @ci_yaml_file if defined?(@ci_yaml_file) - @ci_yaml_file ||= begin - blob = project.repository.blob_at(sha, '.gitlab-ci.yml') - blob.load_all_data!(project.repository) - blob.data - rescue - nil - end + @ci_yaml_file ||= project.repository.ci_yaml_file(sha) end def has_yaml_errors? diff --git a/app/models/environment.rb b/app/models/environment.rb index 577367f1eed..909249dacca 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -51,6 +51,14 @@ class Environment < ActiveRecord::Base state :stopped end + def self.latest_for_commit(environments, commit) + environments.sort_by do |environment| + deployment = environment.first_deployment_for(commit) + + deployment.try(:created_at) || DateTime.parse('1970-01-01') + end.last + end + def predefined_variables [ { key: 'CI_ENVIRONMENT_NAME', value: name, public: true }, @@ -171,6 +179,16 @@ class Environment < ActiveRecord::Base self.slug = slugified end + def external_url_for(path, commit_sha) + return unless self.external_url + + public_path = project.public_path_for_source_path(path, commit_sha) + return unless public_path + + # TODO: Verify this can't be used for XSS + URI.join(external_url, public_path).to_s + end + private # Slugifying a name may remove the uniqueness guarantee afforded by it being diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 082adcafcc8..0155073a1c9 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -729,6 +729,12 @@ class MergeRequest < ActiveRecord::Base end end + def latest_environment + return @latest_environment if defined?(@latest_environment) + + @latest_environment = Environment.latest_for_commit(environments, diff_head_commit) + end + def state_human_name if merged? "Merged" diff --git a/app/models/project.rb b/app/models/project.rb index 7c5fdad5122..ad22ab7577e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1307,10 +1307,17 @@ class Project < ActiveRecord::Base end def environments_for(ref, commit: nil, with_tags: false) - deployments_query = with_tags ? 'ref = ? OR tag IS TRUE' : 'ref = ?' + deps = + if ref + deployments_query = with_tags ? 'ref = ? OR tag IS TRUE' : 'ref = ?' + deployments.where(deployments_query, ref.to_s) + elsif commit + deps = deployments.where(sha: commit.sha) + else + Deployment.none + end - environment_ids = deployments - .where(deployments_query, ref.to_s) + environment_ids = deps .group(:environment_id) .select(:environment_id) @@ -1324,12 +1331,46 @@ class Project < ActiveRecord::Base end end + def latest_environment_for(commit, ref: nil) + environments = environments_for(ref, commit: commit) + Environment.latest_for_commit(environments, commit) + end + def environments_recently_updated_on_branch(branch) environments_for(branch).select do |environment| environment.recently_updated_on_branch?(branch) end end + def route_map_for_commit(commit_sha) + @route_maps_by_commit ||= Hash.new do |h, sha| + h[sha] = begin + data = repository.route_map_file(sha) + next unless data + + # TODO: Validate + YAML.safe_load(data).map do |mapping| + { + source: Regexp.new("^#{mapping['source'][1...-1]}$"), + public: mapping['public'] + } + end + end + end + + @route_maps_by_commit[commit_sha] + end + + def public_path_for_source_path(path, commit_sha) + map = route_map_for_commit(commit_sha) + return unless map + + mapping = map.find { |mapping| path =~ mapping[:source] } + return unless mapping + + path.sub(mapping[:source], mapping[:public]) + end + private def cross_namespace_reference?(from) diff --git a/app/models/repository.rb b/app/models/repository.rb index 7cf09c52bf4..9aa0cc250f0 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -35,6 +35,9 @@ class Repository avatar: :avatar } + ROUTE_MAP_PATH = '.gitlab/route-map.yml' + GITLAB_CI_YML_PATH = '.gitlab-ci.yml' + # Wraps around the given method and caches its output in Redis and an instance # variable. # @@ -1184,6 +1187,20 @@ class Repository end end + def route_map_file(sha) + blob = blob_at(sha, ROUTE_MAP_PATH) + return unless blob + blob.load_all_data!(self) + blob.data + end + + def ci_yaml_file(sha) + blob = blob_at(sha, GITLAB_CI_YML_PATH) + return unless blob + blob.load_all_data!(self) + blob.data + end + private def git_action(index, action) diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index ff893ea74e1..fbe74495e1c 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -1,3 +1,6 @@ +.btn-group + = view_on_environment_btn(@commit.sha, @path, @environment) if @environment + .btn-group.tree-btn-group = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id), class: 'btn btn-sm', target: '_blank' diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 7afd3d80ef5..d5fc283aa8d 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -9,7 +9,7 @@ = render "ci_menu" - else .block-connector - = render "projects/diffs/diffs", diffs: @diffs + = render "projects/diffs/diffs", diffs: @diffs, environment: @environment = render "projects/notes/notes_with_form" - if can_collaborate_with_project? - %w(revert cherry-pick).each do |type| diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index 9c8f58d4aea..0dfc9fe20ed 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -8,7 +8,7 @@ - if @commits.present? = render "projects/commits/commit_list" - = render "projects/diffs/diffs", diffs: @diffs + = render "projects/diffs/diffs", diffs: @diffs, environment: @environment - else .light-well .center diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 58c20e225c6..4b49bed835f 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -1,3 +1,4 @@ +- environment = local_assigns.fetch(:environment, nil) - show_whitespace_toggle = local_assigns.fetch(:show_whitespace_toggle, true) - can_create_note = !@diff_notes_disabled && can?(current_user, :create_note, diffs.project) - diff_files = diffs.diff_files @@ -30,4 +31,4 @@ - file_hash = hexdigest(diff_file.file_path) = render 'projects/diffs/file', file_hash: file_hash, project: diffs.project, - diff_file: diff_file, diff_commit: diff_commit, blob: blob + diff_file: diff_file, diff_commit: diff_commit, blob: blob, environment: environment diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index fc478ccc995..f7aa4fb90f1 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,3 +1,4 @@ +- environment = local_assigns.fetch(:environment, nil) .diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) } .file-title = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}" @@ -14,5 +15,6 @@ blob: blob, link_opts: link_opts) = view_file_btn(diff_commit.id, diff_file.new_path, project) + = view_on_environment_btn(diff_commit.id, diff_file.new_path, environment) if environment = render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project diff --git a/app/views/projects/merge_requests/_new_diffs.html.haml b/app/views/projects/merge_requests/_new_diffs.html.haml index 74367ab9b7b..627fc4e9671 100644 --- a/app/views/projects/merge_requests/_new_diffs.html.haml +++ b/app/views/projects/merge_requests/_new_diffs.html.haml @@ -1 +1 @@ -= render "projects/diffs/diffs", diffs: @diffs, show_whitespace_toggle: false += render "projects/diffs/diffs", diffs: @diffs, environment: @environment, show_whitespace_toggle: false diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml index 5f048d04b27..7f0913ea516 100644 --- a/app/views/projects/merge_requests/show/_diffs.html.haml +++ b/app/views/projects/merge_requests/show/_diffs.html.haml @@ -1,5 +1,5 @@ - if @merge_request_diff.collected? || @merge_request_diff.overflow? = render 'projects/merge_requests/show/versions' - = render "projects/diffs/diffs", diffs: @diffs + = render "projects/diffs/diffs", diffs: @diffs, environment: @environment - elsif @merge_request_diff.empty? .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} diff --git a/changelogs/unreleased/route-map.yml b/changelogs/unreleased/route-map.yml new file mode 100644 index 00000000000..71595745f9b --- /dev/null +++ b/changelogs/unreleased/route-map.yml @@ -0,0 +1,4 @@ +--- +title: Add 'View on [env]' link to blobs and individual files in diffs +merge_request: +author: